root/kernel/time/posix-clock.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_posix_clock
  2. put_posix_clock
  3. posix_clock_read
  4. posix_clock_poll
  5. posix_clock_ioctl
  6. posix_clock_compat_ioctl
  7. posix_clock_open
  8. posix_clock_release
  9. posix_clock_register
  10. posix_clock_unregister
  11. get_clock_desc
  12. put_clock_desc
  13. pc_clock_adjtime
  14. pc_clock_gettime
  15. pc_clock_getres
  16. pc_clock_settime

   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 static struct posix_clock *get_posix_clock(struct file *fp)
  21 {
  22         struct posix_clock *clk = fp->private_data;
  23 
  24         down_read(&clk->rwsem);
  25 
  26         if (!clk->zombie)
  27                 return clk;
  28 
  29         up_read(&clk->rwsem);
  30 
  31         return NULL;
  32 }
  33 
  34 static void put_posix_clock(struct posix_clock *clk)
  35 {
  36         up_read(&clk->rwsem);
  37 }
  38 
  39 static ssize_t posix_clock_read(struct file *fp, char __user *buf,
  40                                 size_t count, loff_t *ppos)
  41 {
  42         struct posix_clock *clk = get_posix_clock(fp);
  43         int err = -EINVAL;
  44 
  45         if (!clk)
  46                 return -ENODEV;
  47 
  48         if (clk->ops.read)
  49                 err = clk->ops.read(clk, fp->f_flags, buf, count);
  50 
  51         put_posix_clock(clk);
  52 
  53         return err;
  54 }
  55 
  56 static __poll_t posix_clock_poll(struct file *fp, poll_table *wait)
  57 {
  58         struct posix_clock *clk = get_posix_clock(fp);
  59         __poll_t result = 0;
  60 
  61         if (!clk)
  62                 return EPOLLERR;
  63 
  64         if (clk->ops.poll)
  65                 result = clk->ops.poll(clk, fp, wait);
  66 
  67         put_posix_clock(clk);
  68 
  69         return result;
  70 }
  71 
  72 static long posix_clock_ioctl(struct file *fp,
  73                               unsigned int cmd, unsigned long arg)
  74 {
  75         struct posix_clock *clk = get_posix_clock(fp);
  76         int err = -ENOTTY;
  77 
  78         if (!clk)
  79                 return -ENODEV;
  80 
  81         if (clk->ops.ioctl)
  82                 err = clk->ops.ioctl(clk, cmd, arg);
  83 
  84         put_posix_clock(clk);
  85 
  86         return err;
  87 }
  88 
  89 #ifdef CONFIG_COMPAT
  90 static long posix_clock_compat_ioctl(struct file *fp,
  91                                      unsigned int cmd, unsigned long arg)
  92 {
  93         struct posix_clock *clk = get_posix_clock(fp);
  94         int err = -ENOTTY;
  95 
  96         if (!clk)
  97                 return -ENODEV;
  98 
  99         if (clk->ops.ioctl)
 100                 err = clk->ops.ioctl(clk, cmd, arg);
 101 
 102         put_posix_clock(clk);
 103 
 104         return err;
 105 }
 106 #endif
 107 
 108 static int posix_clock_open(struct inode *inode, struct file *fp)
 109 {
 110         int err;
 111         struct posix_clock *clk =
 112                 container_of(inode->i_cdev, struct posix_clock, cdev);
 113 
 114         down_read(&clk->rwsem);
 115 
 116         if (clk->zombie) {
 117                 err = -ENODEV;
 118                 goto out;
 119         }
 120         if (clk->ops.open)
 121                 err = clk->ops.open(clk, fp->f_mode);
 122         else
 123                 err = 0;
 124 
 125         if (!err) {
 126                 get_device(clk->dev);
 127                 fp->private_data = clk;
 128         }
 129 out:
 130         up_read(&clk->rwsem);
 131         return err;
 132 }
 133 
 134 static int posix_clock_release(struct inode *inode, struct file *fp)
 135 {
 136         struct posix_clock *clk = fp->private_data;
 137         int err = 0;
 138 
 139         if (clk->ops.release)
 140                 err = clk->ops.release(clk);
 141 
 142         put_device(clk->dev);
 143 
 144         fp->private_data = NULL;
 145 
 146         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 int posix_clock_register(struct posix_clock *clk, struct device *dev)
 163 {
 164         int err;
 165 
 166         init_rwsem(&clk->rwsem);
 167 
 168         cdev_init(&clk->cdev, &posix_clock_file_operations);
 169         err = cdev_device_add(&clk->cdev, dev);
 170         if (err) {
 171                 pr_err("%s unable to add device %d:%d\n",
 172                         dev_name(dev), MAJOR(dev->devt), MINOR(dev->devt));
 173                 return err;
 174         }
 175         clk->cdev.owner = clk->ops.owner;
 176         clk->dev = dev;
 177 
 178         return 0;
 179 }
 180 EXPORT_SYMBOL_GPL(posix_clock_register);
 181 
 182 void posix_clock_unregister(struct posix_clock *clk)
 183 {
 184         cdev_device_del(&clk->cdev, clk->dev);
 185 
 186         down_write(&clk->rwsem);
 187         clk->zombie = true;
 188         up_write(&clk->rwsem);
 189 
 190         put_device(clk->dev);
 191 }
 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 static void put_clock_desc(struct posix_clock_desc *cd)
 221 {
 222         put_posix_clock(cd->clk);
 223         fput(cd->fp);
 224 }
 225 
 226 static int pc_clock_adjtime(clockid_t id, struct __kernel_timex *tx)
 227 {
 228         struct posix_clock_desc cd;
 229         int err;
 230 
 231         err = get_clock_desc(id, &cd);
 232         if (err)
 233                 return err;
 234 
 235         if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
 236                 err = -EACCES;
 237                 goto out;
 238         }
 239 
 240         if (cd.clk->ops.clock_adjtime)
 241                 err = cd.clk->ops.clock_adjtime(cd.clk, tx);
 242         else
 243                 err = -EOPNOTSUPP;
 244 out:
 245         put_clock_desc(&cd);
 246 
 247         return err;
 248 }
 249 
 250 static int pc_clock_gettime(clockid_t id, struct timespec64 *ts)
 251 {
 252         struct posix_clock_desc cd;
 253         int err;
 254 
 255         err = get_clock_desc(id, &cd);
 256         if (err)
 257                 return err;
 258 
 259         if (cd.clk->ops.clock_gettime)
 260                 err = cd.clk->ops.clock_gettime(cd.clk, ts);
 261         else
 262                 err = -EOPNOTSUPP;
 263 
 264         put_clock_desc(&cd);
 265 
 266         return err;
 267 }
 268 
 269 static int pc_clock_getres(clockid_t id, struct timespec64 *ts)
 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 
 278         if (cd.clk->ops.clock_getres)
 279                 err = cd.clk->ops.clock_getres(cd.clk, ts);
 280         else
 281                 err = -EOPNOTSUPP;
 282 
 283         put_clock_desc(&cd);
 284 
 285         return err;
 286 }
 287 
 288 static int pc_clock_settime(clockid_t id, const struct timespec64 *ts)
 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 
 297         if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
 298                 err = -EACCES;
 299                 goto out;
 300         }
 301 
 302         if (cd.clk->ops.clock_settime)
 303                 err = cd.clk->ops.clock_settime(cd.clk, ts);
 304         else
 305                 err = -EOPNOTSUPP;
 306 out:
 307         put_clock_desc(&cd);
 308 
 309         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      = pc_clock_gettime,
 316         .clock_adj      = pc_clock_adjtime,
 317 };

/* [<][>][^][v][top][bottom][index][help] */