cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

timerdev.c (6594B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *
      4 * general timer device for using in ISDN stacks
      5 *
      6 * Author	Karsten Keil <kkeil@novell.com>
      7 *
      8 * Copyright 2008  by Karsten Keil <kkeil@novell.com>
      9 */
     10
     11#include <linux/poll.h>
     12#include <linux/vmalloc.h>
     13#include <linux/slab.h>
     14#include <linux/timer.h>
     15#include <linux/miscdevice.h>
     16#include <linux/module.h>
     17#include <linux/mISDNif.h>
     18#include <linux/mutex.h>
     19#include <linux/sched/signal.h>
     20
     21#include "core.h"
     22
     23static DEFINE_MUTEX(mISDN_mutex);
     24static u_int	*debug;
     25
     26
     27struct mISDNtimerdev {
     28	int			next_id;
     29	struct list_head	pending;
     30	struct list_head	expired;
     31	wait_queue_head_t	wait;
     32	u_int			work;
     33	spinlock_t		lock; /* protect lists */
     34};
     35
     36struct mISDNtimer {
     37	struct list_head	list;
     38	struct  mISDNtimerdev	*dev;
     39	struct timer_list	tl;
     40	int			id;
     41};
     42
     43static int
     44mISDN_open(struct inode *ino, struct file *filep)
     45{
     46	struct mISDNtimerdev	*dev;
     47
     48	if (*debug & DEBUG_TIMER)
     49		printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
     50	dev = kmalloc(sizeof(struct mISDNtimerdev) , GFP_KERNEL);
     51	if (!dev)
     52		return -ENOMEM;
     53	dev->next_id = 1;
     54	INIT_LIST_HEAD(&dev->pending);
     55	INIT_LIST_HEAD(&dev->expired);
     56	spin_lock_init(&dev->lock);
     57	dev->work = 0;
     58	init_waitqueue_head(&dev->wait);
     59	filep->private_data = dev;
     60	return nonseekable_open(ino, filep);
     61}
     62
     63static int
     64mISDN_close(struct inode *ino, struct file *filep)
     65{
     66	struct mISDNtimerdev	*dev = filep->private_data;
     67	struct list_head	*list = &dev->pending;
     68	struct mISDNtimer	*timer, *next;
     69
     70	if (*debug & DEBUG_TIMER)
     71		printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
     72
     73	spin_lock_irq(&dev->lock);
     74	while (!list_empty(list)) {
     75		timer = list_first_entry(list, struct mISDNtimer, list);
     76		spin_unlock_irq(&dev->lock);
     77		del_timer_sync(&timer->tl);
     78		spin_lock_irq(&dev->lock);
     79		/* it might have been moved to ->expired */
     80		list_del(&timer->list);
     81		kfree(timer);
     82	}
     83	spin_unlock_irq(&dev->lock);
     84
     85	list_for_each_entry_safe(timer, next, &dev->expired, list) {
     86		kfree(timer);
     87	}
     88	kfree(dev);
     89	return 0;
     90}
     91
     92static ssize_t
     93mISDN_read(struct file *filep, char __user *buf, size_t count, loff_t *off)
     94{
     95	struct mISDNtimerdev	*dev = filep->private_data;
     96	struct list_head *list = &dev->expired;
     97	struct mISDNtimer	*timer;
     98	int	ret = 0;
     99
    100	if (*debug & DEBUG_TIMER)
    101		printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__,
    102		       filep, buf, (int)count, off);
    103
    104	if (count < sizeof(int))
    105		return -ENOSPC;
    106
    107	spin_lock_irq(&dev->lock);
    108	while (list_empty(list) && (dev->work == 0)) {
    109		spin_unlock_irq(&dev->lock);
    110		if (filep->f_flags & O_NONBLOCK)
    111			return -EAGAIN;
    112		wait_event_interruptible(dev->wait, (dev->work ||
    113						     !list_empty(list)));
    114		if (signal_pending(current))
    115			return -ERESTARTSYS;
    116		spin_lock_irq(&dev->lock);
    117	}
    118	if (dev->work)
    119		dev->work = 0;
    120	if (!list_empty(list)) {
    121		timer = list_first_entry(list, struct mISDNtimer, list);
    122		list_del(&timer->list);
    123		spin_unlock_irq(&dev->lock);
    124		if (put_user(timer->id, (int __user *)buf))
    125			ret = -EFAULT;
    126		else
    127			ret = sizeof(int);
    128		kfree(timer);
    129	} else {
    130		spin_unlock_irq(&dev->lock);
    131	}
    132	return ret;
    133}
    134
    135static __poll_t
    136mISDN_poll(struct file *filep, poll_table *wait)
    137{
    138	struct mISDNtimerdev	*dev = filep->private_data;
    139	__poll_t		mask = EPOLLERR;
    140
    141	if (*debug & DEBUG_TIMER)
    142		printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait);
    143	if (dev) {
    144		poll_wait(filep, &dev->wait, wait);
    145		mask = 0;
    146		if (dev->work || !list_empty(&dev->expired))
    147			mask |= (EPOLLIN | EPOLLRDNORM);
    148		if (*debug & DEBUG_TIMER)
    149			printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__,
    150			       dev->work, list_empty(&dev->expired));
    151	}
    152	return mask;
    153}
    154
    155static void
    156dev_expire_timer(struct timer_list *t)
    157{
    158	struct mISDNtimer *timer = from_timer(timer, t, tl);
    159	u_long			flags;
    160
    161	spin_lock_irqsave(&timer->dev->lock, flags);
    162	if (timer->id >= 0)
    163		list_move_tail(&timer->list, &timer->dev->expired);
    164	wake_up_interruptible(&timer->dev->wait);
    165	spin_unlock_irqrestore(&timer->dev->lock, flags);
    166}
    167
    168static int
    169misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
    170{
    171	int			id;
    172	struct mISDNtimer	*timer;
    173
    174	if (!timeout) {
    175		dev->work = 1;
    176		wake_up_interruptible(&dev->wait);
    177		id = 0;
    178	} else {
    179		timer = kzalloc(sizeof(struct mISDNtimer), GFP_KERNEL);
    180		if (!timer)
    181			return -ENOMEM;
    182		timer->dev = dev;
    183		timer_setup(&timer->tl, dev_expire_timer, 0);
    184		spin_lock_irq(&dev->lock);
    185		id = timer->id = dev->next_id++;
    186		if (dev->next_id < 0)
    187			dev->next_id = 1;
    188		list_add_tail(&timer->list, &dev->pending);
    189		timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000);
    190		add_timer(&timer->tl);
    191		spin_unlock_irq(&dev->lock);
    192	}
    193	return id;
    194}
    195
    196static int
    197misdn_del_timer(struct mISDNtimerdev *dev, int id)
    198{
    199	struct mISDNtimer	*timer;
    200
    201	spin_lock_irq(&dev->lock);
    202	list_for_each_entry(timer, &dev->pending, list) {
    203		if (timer->id == id) {
    204			list_del_init(&timer->list);
    205			timer->id = -1;
    206			spin_unlock_irq(&dev->lock);
    207			del_timer_sync(&timer->tl);
    208			kfree(timer);
    209			return id;
    210		}
    211	}
    212	spin_unlock_irq(&dev->lock);
    213	return 0;
    214}
    215
    216static long
    217mISDN_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
    218{
    219	struct mISDNtimerdev	*dev = filep->private_data;
    220	int			id, tout, ret = 0;
    221
    222
    223	if (*debug & DEBUG_TIMER)
    224		printk(KERN_DEBUG "%s(%p, %x, %lx)\n", __func__,
    225		       filep, cmd, arg);
    226	mutex_lock(&mISDN_mutex);
    227	switch (cmd) {
    228	case IMADDTIMER:
    229		if (get_user(tout, (int __user *)arg)) {
    230			ret = -EFAULT;
    231			break;
    232		}
    233		id = misdn_add_timer(dev, tout);
    234		if (*debug & DEBUG_TIMER)
    235			printk(KERN_DEBUG "%s add %d id %d\n", __func__,
    236			       tout, id);
    237		if (id < 0) {
    238			ret = id;
    239			break;
    240		}
    241		if (put_user(id, (int __user *)arg))
    242			ret = -EFAULT;
    243		break;
    244	case IMDELTIMER:
    245		if (get_user(id, (int __user *)arg)) {
    246			ret = -EFAULT;
    247			break;
    248		}
    249		if (*debug & DEBUG_TIMER)
    250			printk(KERN_DEBUG "%s del id %d\n", __func__, id);
    251		id = misdn_del_timer(dev, id);
    252		if (put_user(id, (int __user *)arg))
    253			ret = -EFAULT;
    254		break;
    255	default:
    256		ret = -EINVAL;
    257	}
    258	mutex_unlock(&mISDN_mutex);
    259	return ret;
    260}
    261
    262static const struct file_operations mISDN_fops = {
    263	.owner		= THIS_MODULE,
    264	.read		= mISDN_read,
    265	.poll		= mISDN_poll,
    266	.unlocked_ioctl	= mISDN_ioctl,
    267	.open		= mISDN_open,
    268	.release	= mISDN_close,
    269	.llseek		= no_llseek,
    270};
    271
    272static struct miscdevice mISDNtimer = {
    273	.minor	= MISC_DYNAMIC_MINOR,
    274	.name	= "mISDNtimer",
    275	.fops	= &mISDN_fops,
    276};
    277
    278int
    279mISDN_inittimer(u_int *deb)
    280{
    281	int	err;
    282
    283	debug = deb;
    284	err = misc_register(&mISDNtimer);
    285	if (err)
    286		printk(KERN_WARNING "mISDN: Could not register timer device\n");
    287	return err;
    288}
    289
    290void mISDN_timer_cleanup(void)
    291{
    292	misc_deregister(&mISDNtimer);
    293}