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