diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index 65894fd8b67b724a0637914b8c22a7e84672251d..79b852c6d868c81dbcf454e0a241ec3fce98f012 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -43,6 +43,22 @@ #include "comedi_internal.h" +/** + * struct comedi_file - per-file private data for comedi device + * @dev: comedi_device struct + * @read_subdev: current "read" subdevice + * @write_subdev: current "write" subdevice + * @last_detach_count: last known detach count + * @last_attached: last known attached/detached state + */ +struct comedi_file { + struct comedi_device *dev; + struct comedi_subdevice *read_subdev; + struct comedi_subdevice *write_subdev; + unsigned int last_detach_count; + bool last_attached:1; +}; + #define COMEDI_NUM_MINORS 0x100 #define COMEDI_NUM_SUBDEVICE_MINORS \ (COMEDI_NUM_MINORS - COMEDI_NUM_BOARD_MINORS) @@ -239,6 +255,54 @@ comedi_write_subdevice(const struct comedi_device *dev, unsigned int minor) return dev->write_subdev; } +static void comedi_file_reset(struct file *file) +{ + struct comedi_file *cfp = file->private_data; + struct comedi_device *dev = cfp->dev; + struct comedi_subdevice *s, *read_s, *write_s; + unsigned int minor = iminor(file_inode(file)); + + read_s = dev->read_subdev; + write_s = dev->write_subdev; + if (minor >= COMEDI_NUM_BOARD_MINORS) { + s = comedi_subdevice_from_minor(dev, minor); + if (s == NULL || s->subdev_flags & SDF_CMD_READ) + read_s = s; + if (s == NULL || s->subdev_flags & SDF_CMD_WRITE) + write_s = s; + } + cfp->last_attached = dev->attached; + cfp->last_detach_count = dev->detach_count; + ACCESS_ONCE(cfp->read_subdev) = read_s; + ACCESS_ONCE(cfp->write_subdev) = write_s; +} + +static void comedi_file_check(struct file *file) +{ + struct comedi_file *cfp = file->private_data; + struct comedi_device *dev = cfp->dev; + + if (cfp->last_attached != dev->attached || + cfp->last_detach_count != dev->detach_count) + comedi_file_reset(file); +} + +static struct comedi_subdevice *comedi_file_read_subdevice(struct file *file) +{ + struct comedi_file *cfp = file->private_data; + + comedi_file_check(file); + return ACCESS_ONCE(cfp->read_subdev); +} + +static struct comedi_subdevice *comedi_file_write_subdevice(struct file *file) +{ + struct comedi_file *cfp = file->private_data; + + comedi_file_check(file); + return ACCESS_ONCE(cfp->write_subdev); +} + static int resize_async_buffer(struct comedi_device *dev, struct comedi_subdevice *s, unsigned new_size) { @@ -776,7 +840,6 @@ static int do_devinfo_ioctl(struct comedi_device *dev, struct comedi_devinfo __user *arg, struct file *file) { - const unsigned minor = iminor(file_inode(file)); struct comedi_subdevice *s; struct comedi_devinfo devinfo; @@ -788,13 +851,13 @@ static int do_devinfo_ioctl(struct comedi_device *dev, strlcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN); strlcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN); - s = comedi_read_subdevice(dev, minor); + s = comedi_file_read_subdevice(file); if (s) devinfo.read_subdevice = s->index; else devinfo.read_subdevice = -1; - s = comedi_write_subdevice(dev, minor); + s = comedi_file_write_subdevice(file); if (s) devinfo.write_subdevice = s->index; else @@ -1787,8 +1850,9 @@ static int do_poll_ioctl(struct comedi_device *dev, unsigned long arg, static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - const unsigned minor = iminor(file_inode(file)); - struct comedi_device *dev = file->private_data; + unsigned minor = iminor(file_inode(file)); + struct comedi_file *cfp = file->private_data; + struct comedi_device *dev = cfp->dev; int rc; mutex_lock(&dev->mutex); @@ -1910,8 +1974,8 @@ static struct vm_operations_struct comedi_vm_ops = { static int comedi_mmap(struct file *file, struct vm_area_struct *vma) { - const unsigned minor = iminor(file_inode(file)); - struct comedi_device *dev = file->private_data; + struct comedi_file *cfp = file->private_data; + struct comedi_device *dev = cfp->dev; struct comedi_subdevice *s; struct comedi_async *async; struct comedi_buf_map *bm = NULL; @@ -1937,9 +2001,9 @@ static int comedi_mmap(struct file *file, struct vm_area_struct *vma) } if (vma->vm_flags & VM_WRITE) - s = comedi_write_subdevice(dev, minor); + s = comedi_file_write_subdevice(file); else - s = comedi_read_subdevice(dev, minor); + s = comedi_file_read_subdevice(file); if (!s) { retval = -EINVAL; goto done; @@ -2002,8 +2066,8 @@ static int comedi_mmap(struct file *file, struct vm_area_struct *vma) static unsigned int comedi_poll(struct file *file, poll_table *wait) { unsigned int mask = 0; - const unsigned minor = iminor(file_inode(file)); - struct comedi_device *dev = file->private_data; + struct comedi_file *cfp = file->private_data; + struct comedi_device *dev = cfp->dev; struct comedi_subdevice *s; mutex_lock(&dev->mutex); @@ -2013,7 +2077,7 @@ static unsigned int comedi_poll(struct file *file, poll_table *wait) goto done; } - s = comedi_read_subdevice(dev, minor); + s = comedi_file_read_subdevice(file); if (s && s->async) { poll_wait(file, &s->async->wait_head, wait); if (!s->busy || !comedi_is_subdevice_running(s) || @@ -2022,7 +2086,7 @@ static unsigned int comedi_poll(struct file *file, poll_table *wait) mask |= POLLIN | POLLRDNORM; } - s = comedi_write_subdevice(dev, minor); + s = comedi_file_write_subdevice(file); if (s && s->async) { unsigned int bps = comedi_bytes_per_sample(s); @@ -2046,8 +2110,8 @@ static ssize_t comedi_write(struct file *file, const char __user *buf, struct comedi_async *async; int n, m, count = 0, retval = 0; DECLARE_WAITQUEUE(wait, current); - const unsigned minor = iminor(file_inode(file)); - struct comedi_device *dev = file->private_data; + struct comedi_file *cfp = file->private_data; + struct comedi_device *dev = cfp->dev; bool on_wait_queue = false; bool attach_locked; unsigned int old_detach_count; @@ -2063,7 +2127,7 @@ static ssize_t comedi_write(struct file *file, const char __user *buf, goto out; } - s = comedi_write_subdevice(dev, minor); + s = comedi_file_write_subdevice(file); if (!s || !s->async) { retval = -EIO; goto out; @@ -2115,7 +2179,7 @@ static ssize_t comedi_write(struct file *file, const char __user *buf, * meantime!), but check the subdevice pointer * as well just in case. */ - new_s = comedi_write_subdevice(dev, minor); + new_s = comedi_file_write_subdevice(file); if (dev->attached && old_detach_count == dev->detach_count && s == new_s && new_s->async == async) @@ -2190,8 +2254,8 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes, struct comedi_async *async; int n, m, count = 0, retval = 0; DECLARE_WAITQUEUE(wait, current); - const unsigned minor = iminor(file_inode(file)); - struct comedi_device *dev = file->private_data; + struct comedi_file *cfp = file->private_data; + struct comedi_device *dev = cfp->dev; unsigned int old_detach_count; bool become_nonbusy = false; bool attach_locked; @@ -2207,7 +2271,7 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes, goto out; } - s = comedi_read_subdevice(dev, minor); + s = comedi_file_read_subdevice(file); if (!s || !s->async) { retval = -EIO; goto out; @@ -2304,7 +2368,7 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes, * meantime!), but check the subdevice pointer as well just in * case. */ - new_s = comedi_read_subdevice(dev, minor); + new_s = comedi_file_read_subdevice(file); if (dev->attached && old_detach_count == dev->detach_count && s == new_s && new_s->async == async) { if (become_nonbusy || comedi_buf_n_bytes_ready(s) == 0) @@ -2322,6 +2386,7 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes, static int comedi_open(struct inode *inode, struct file *file) { const unsigned minor = iminor(inode); + struct comedi_file *cfp; struct comedi_device *dev = comedi_dev_get_from_minor(minor); int rc; @@ -2330,6 +2395,12 @@ static int comedi_open(struct inode *inode, struct file *file) return -ENODEV; } + cfp = kzalloc(sizeof(*cfp), GFP_KERNEL); + if (!cfp) + return -ENOMEM; + + cfp->dev = dev; + mutex_lock(&dev->mutex); if (!dev->attached && !capable(CAP_NET_ADMIN)) { dev_dbg(dev->class_dev, "not attached and not CAP_NET_ADMIN\n"); @@ -2351,26 +2422,31 @@ static int comedi_open(struct inode *inode, struct file *file) } dev->use_count++; - file->private_data = dev; + file->private_data = cfp; + comedi_file_reset(file); rc = 0; out: mutex_unlock(&dev->mutex); - if (rc) + if (rc) { comedi_dev_put(dev); + kfree(cfp); + } return rc; } static int comedi_fasync(int fd, struct file *file, int on) { - struct comedi_device *dev = file->private_data; + struct comedi_file *cfp = file->private_data; + struct comedi_device *dev = cfp->dev; return fasync_helper(fd, file, on, &dev->async_queue); } static int comedi_close(struct inode *inode, struct file *file) { - struct comedi_device *dev = file->private_data; + struct comedi_file *cfp = file->private_data; + struct comedi_device *dev = cfp->dev; struct comedi_subdevice *s = NULL; int i; @@ -2396,6 +2472,7 @@ static int comedi_close(struct inode *inode, struct file *file) mutex_unlock(&dev->mutex); comedi_dev_put(dev); + kfree(cfp); return 0; }