LCOV - code coverage report
Current view: top level - kernel/time - posix-clock.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 108 0.0 %
Date: 2023-04-06 08:38:28 Functions: 0 12 0.0 %

          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             : 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             :         return NULL;
      32             : }
      33             : 
      34             : 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             :                 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             :                 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             :                 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             : 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           0 : static int posix_clock_open(struct inode *inode, struct file *fp)
     109             : {
     110             :         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             :                 err = -ENODEV;
     118             :                 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             : 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           0 : int posix_clock_register(struct posix_clock *clk, struct device *dev)
     163             : {
     164             :         int err;
     165             : 
     166           0 :         init_rwsem(&clk->rwsem);
     167             : 
     168           0 :         cdev_init(&clk->cdev, &posix_clock_file_operations);
     169           0 :         err = cdev_device_add(&clk->cdev, dev);
     170           0 :         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           0 :         clk->cdev.owner = clk->ops.owner;
     176           0 :         clk->dev = dev;
     177             : 
     178           0 :         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           0 : static int get_clock_desc(const clockid_t id, struct posix_clock_desc *cd)
     200             : {
     201           0 :         struct file *fp = fget(clockid_to_fd(id));
     202           0 :         int err = -EINVAL;
     203             : 
     204           0 :         if (!fp)
     205             :                 return err;
     206             : 
     207           0 :         if (fp->f_op->open != posix_clock_open || !fp->private_data)
     208             :                 goto out;
     209             : 
     210           0 :         cd->fp = fp;
     211           0 :         cd->clk = get_posix_clock(fp);
     212             : 
     213           0 :         err = cd->clk ? 0 : -ENODEV;
     214             : out:
     215           0 :         if (err)
     216           0 :                 fput(fp);
     217             :         return err;
     218             : }
     219             : 
     220             : 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             :         struct posix_clock_desc cd;
     229             :         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             :                 err = -EACCES;
     237             :                 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             : 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             :         struct posix_clock_desc cd;
     253             :         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             :         struct posix_clock_desc cd;
     272             :         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             :         struct posix_clock_desc cd;
     291             :         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             :                 err = -EACCES;
     299             :                 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             : 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             : };

Generated by: LCOV version 1.14