Merge tag 'pm-6.12-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[linux-block.git] / kernel / time / posix-clock.c
CommitLineData
35728b82 1// SPDX-License-Identifier: GPL-2.0+
0606f422 2/*
58c5fc2b 3 * Support for dynamic clock devices
0606f422
RC
4 *
5 * Copyright (C) 2010 OMICRON electronics GmbH
0606f422
RC
6 */
7#include <linux/device.h>
6e5fdeed 8#include <linux/export.h>
0606f422 9#include <linux/file.h>
0606f422
RC
10#include <linux/posix-clock.h>
11#include <linux/slab.h>
12#include <linux/syscalls.h>
13#include <linux/uaccess.h>
14
bab0aae9
TG
15#include "posix-timers.h"
16
0606f422
RC
17/*
18 * Returns NULL if the posix_clock instance attached to 'fp' is old and stale.
19 */
20static struct posix_clock *get_posix_clock(struct file *fp)
21{
60c69466
XM
22 struct posix_clock_context *pccontext = fp->private_data;
23 struct posix_clock *clk = pccontext->clk;
0606f422 24
1791f881 25 down_read(&clk->rwsem);
0606f422
RC
26
27 if (!clk->zombie)
28 return clk;
29
1791f881 30 up_read(&clk->rwsem);
0606f422
RC
31
32 return NULL;
33}
34
35static void put_posix_clock(struct posix_clock *clk)
36{
1791f881 37 up_read(&clk->rwsem);
0606f422
RC
38}
39
40static ssize_t posix_clock_read(struct file *fp, char __user *buf,
41 size_t count, loff_t *ppos)
42{
60c69466 43 struct posix_clock_context *pccontext = fp->private_data;
0606f422
RC
44 struct posix_clock *clk = get_posix_clock(fp);
45 int err = -EINVAL;
46
47 if (!clk)
48 return -ENODEV;
49
50 if (clk->ops.read)
60c69466 51 err = clk->ops.read(pccontext, fp->f_flags, buf, count);
0606f422
RC
52
53 put_posix_clock(clk);
54
55 return err;
56}
57
9dd95748 58static __poll_t posix_clock_poll(struct file *fp, poll_table *wait)
0606f422 59{
60c69466 60 struct posix_clock_context *pccontext = fp->private_data;
0606f422 61 struct posix_clock *clk = get_posix_clock(fp);
9dd95748 62 __poll_t result = 0;
0606f422
RC
63
64 if (!clk)
a9a08845 65 return EPOLLERR;
0606f422
RC
66
67 if (clk->ops.poll)
60c69466 68 result = clk->ops.poll(pccontext, fp, wait);
0606f422
RC
69
70 put_posix_clock(clk);
71
72 return result;
73}
74
0606f422
RC
75static long posix_clock_ioctl(struct file *fp,
76 unsigned int cmd, unsigned long arg)
77{
60c69466 78 struct posix_clock_context *pccontext = fp->private_data;
0606f422
RC
79 struct posix_clock *clk = get_posix_clock(fp);
80 int err = -ENOTTY;
81
82 if (!clk)
83 return -ENODEV;
84
85 if (clk->ops.ioctl)
60c69466 86 err = clk->ops.ioctl(pccontext, cmd, arg);
0606f422
RC
87
88 put_posix_clock(clk);
89
90 return err;
91}
92
93#ifdef CONFIG_COMPAT
94static long posix_clock_compat_ioctl(struct file *fp,
95 unsigned int cmd, unsigned long arg)
96{
60c69466 97 struct posix_clock_context *pccontext = fp->private_data;
0606f422
RC
98 struct posix_clock *clk = get_posix_clock(fp);
99 int err = -ENOTTY;
100
101 if (!clk)
102 return -ENODEV;
103
104 if (clk->ops.ioctl)
60c69466 105 err = clk->ops.ioctl(pccontext, cmd, arg);
0606f422
RC
106
107 put_posix_clock(clk);
108
109 return err;
110}
111#endif
112
113static int posix_clock_open(struct inode *inode, struct file *fp)
114{
115 int err;
116 struct posix_clock *clk =
117 container_of(inode->i_cdev, struct posix_clock, cdev);
60c69466 118 struct posix_clock_context *pccontext;
0606f422 119
1791f881 120 down_read(&clk->rwsem);
0606f422
RC
121
122 if (clk->zombie) {
123 err = -ENODEV;
124 goto out;
125 }
60c69466
XM
126 pccontext = kzalloc(sizeof(*pccontext), GFP_KERNEL);
127 if (!pccontext) {
128 err = -ENOMEM;
129 goto out;
130 }
131 pccontext->clk = clk;
5b4cdd9c 132 if (clk->ops.open) {
60c69466 133 err = clk->ops.open(pccontext, fp->f_mode);
5b4cdd9c
LT
134 if (err) {
135 kfree(pccontext);
136 goto out;
137 }
0606f422 138 }
5b4cdd9c
LT
139
140 fp->private_data = pccontext;
141 get_device(clk->dev);
142 err = 0;
0606f422 143out:
1791f881 144 up_read(&clk->rwsem);
0606f422
RC
145 return err;
146}
147
148static int posix_clock_release(struct inode *inode, struct file *fp)
149{
60c69466
XM
150 struct posix_clock_context *pccontext = fp->private_data;
151 struct posix_clock *clk;
0606f422
RC
152 int err = 0;
153
60c69466
XM
154 if (!pccontext)
155 return -ENODEV;
156 clk = pccontext->clk;
157
0606f422 158 if (clk->ops.release)
60c69466 159 err = clk->ops.release(pccontext);
0606f422 160
a33121e5 161 put_device(clk->dev);
0606f422 162
60c69466 163 kfree(pccontext);
0606f422
RC
164 fp->private_data = NULL;
165
166 return err;
167}
168
169static const struct file_operations posix_clock_file_operations = {
170 .owner = THIS_MODULE,
0606f422
RC
171 .read = posix_clock_read,
172 .poll = posix_clock_poll,
173 .unlocked_ioctl = posix_clock_ioctl,
174 .open = posix_clock_open,
175 .release = posix_clock_release,
0606f422
RC
176#ifdef CONFIG_COMPAT
177 .compat_ioctl = posix_clock_compat_ioctl,
178#endif
179};
180
a33121e5 181int posix_clock_register(struct posix_clock *clk, struct device *dev)
0606f422
RC
182{
183 int err;
184
1791f881 185 init_rwsem(&clk->rwsem);
0606f422
RC
186
187 cdev_init(&clk->cdev, &posix_clock_file_operations);
a33121e5
VD
188 err = cdev_device_add(&clk->cdev, dev);
189 if (err) {
190 pr_err("%s unable to add device %d:%d\n",
191 dev_name(dev), MAJOR(dev->devt), MINOR(dev->devt));
192 return err;
193 }
0606f422 194 clk->cdev.owner = clk->ops.owner;
a33121e5 195 clk->dev = dev;
0606f422 196
a33121e5 197 return 0;
0606f422
RC
198}
199EXPORT_SYMBOL_GPL(posix_clock_register);
200
0606f422
RC
201void posix_clock_unregister(struct posix_clock *clk)
202{
a33121e5 203 cdev_device_del(&clk->cdev, clk->dev);
0606f422 204
1791f881 205 down_write(&clk->rwsem);
0606f422 206 clk->zombie = true;
1791f881 207 up_write(&clk->rwsem);
0606f422 208
a33121e5 209 put_device(clk->dev);
0606f422
RC
210}
211EXPORT_SYMBOL_GPL(posix_clock_unregister);
212
213struct posix_clock_desc {
214 struct file *fp;
215 struct posix_clock *clk;
216};
217
218static int get_clock_desc(const clockid_t id, struct posix_clock_desc *cd)
219{
29f1b2b0 220 struct file *fp = fget(clockid_to_fd(id));
0606f422
RC
221 int err = -EINVAL;
222
223 if (!fp)
224 return err;
225
226 if (fp->f_op->open != posix_clock_open || !fp->private_data)
227 goto out;
228
229 cd->fp = fp;
230 cd->clk = get_posix_clock(fp);
231
232 err = cd->clk ? 0 : -ENODEV;
233out:
234 if (err)
235 fput(fp);
236 return err;
237}
238
239static void put_clock_desc(struct posix_clock_desc *cd)
240{
241 put_posix_clock(cd->clk);
242 fput(cd->fp);
243}
244
ead25417 245static int pc_clock_adjtime(clockid_t id, struct __kernel_timex *tx)
0606f422
RC
246{
247 struct posix_clock_desc cd;
248 int err;
249
250 err = get_clock_desc(id, &cd);
251 if (err)
252 return err;
253
6e6823d1
TH
254 if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
255 err = -EACCES;
256 goto out;
257 }
258
0606f422
RC
259 if (cd.clk->ops.clock_adjtime)
260 err = cd.clk->ops.clock_adjtime(cd.clk, tx);
261 else
262 err = -EOPNOTSUPP;
6e6823d1 263out:
0606f422
RC
264 put_clock_desc(&cd);
265
266 return err;
267}
268
3c9c12f4 269static int pc_clock_gettime(clockid_t id, struct timespec64 *ts)
0606f422
RC
270{
271 struct posix_clock_desc cd;
272 int err;
273
274 err = get_clock_desc(id, &cd);
275 if (err)
276 return err;
277
3c9c12f4
DD
278 if (cd.clk->ops.clock_gettime)
279 err = cd.clk->ops.clock_gettime(cd.clk, ts);
0606f422
RC
280 else
281 err = -EOPNOTSUPP;
282
283 put_clock_desc(&cd);
284
285 return err;
286}
287
d2e3e0ca 288static int pc_clock_getres(clockid_t id, struct timespec64 *ts)
0606f422
RC
289{
290 struct posix_clock_desc cd;
291 int err;
292
293 err = get_clock_desc(id, &cd);
294 if (err)
295 return err;
296
d2e3e0ca
DD
297 if (cd.clk->ops.clock_getres)
298 err = cd.clk->ops.clock_getres(cd.clk, ts);
0606f422
RC
299 else
300 err = -EOPNOTSUPP;
301
302 put_clock_desc(&cd);
303
304 return err;
305}
306
0fe6afe3 307static int pc_clock_settime(clockid_t id, const struct timespec64 *ts)
0606f422
RC
308{
309 struct posix_clock_desc cd;
310 int err;
311
312 err = get_clock_desc(id, &cd);
313 if (err)
314 return err;
315
6e6823d1
TH
316 if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
317 err = -EACCES;
318 goto out;
319 }
320
0606f422 321 if (cd.clk->ops.clock_settime)
0fe6afe3 322 err = cd.clk->ops.clock_settime(cd.clk, ts);
0606f422
RC
323 else
324 err = -EOPNOTSUPP;
6e6823d1 325out:
0606f422
RC
326 put_clock_desc(&cd);
327
328 return err;
329}
330
d3ba5a9a 331const struct k_clock clock_posix_dynamic = {
819a95fe
AV
332 .clock_getres = pc_clock_getres,
333 .clock_set = pc_clock_settime,
334 .clock_get_timespec = pc_clock_gettime,
335 .clock_adj = pc_clock_adjtime,
0606f422 336};