staging: comedi: use refcount in sysfs attribute handlers
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / comedi_fops.c
index 1636c7ca57e237fcd516141933c5047ee12131da..37400e85c41709b8642f8c919d36e67e766cd78b 100644 (file)
@@ -89,11 +89,37 @@ static struct cdev comedi_cdev;
 
 static void comedi_device_init(struct comedi_device *dev)
 {
+       kref_init(&dev->refcount);
        spin_lock_init(&dev->spinlock);
        mutex_init(&dev->mutex);
+       init_rwsem(&dev->attach_lock);
        dev->minor = -1;
 }
 
+static void comedi_dev_kref_release(struct kref *kref)
+{
+       struct comedi_device *dev =
+               container_of(kref, struct comedi_device, refcount);
+
+       mutex_destroy(&dev->mutex);
+       kfree(dev);
+}
+
+int comedi_dev_put(struct comedi_device *dev)
+{
+       if (dev)
+               return kref_put(&dev->refcount, comedi_dev_kref_release);
+       return 1;
+}
+EXPORT_SYMBOL_GPL(comedi_dev_put);
+
+static struct comedi_device *comedi_dev_get(struct comedi_device *dev)
+{
+       if (dev)
+               kref_get(&dev->refcount);
+       return dev;
+}
+
 static void comedi_device_cleanup(struct comedi_device *dev)
 {
        struct module *driver_module = NULL;
@@ -111,7 +137,6 @@ static void comedi_device_cleanup(struct comedi_device *dev)
                dev->use_count--;
        }
        mutex_unlock(&dev->mutex);
-       mutex_destroy(&dev->mutex);
 }
 
 static bool comedi_clear_board_dev(struct comedi_device *dev)
@@ -147,7 +172,7 @@ static void comedi_free_board_dev(struct comedi_device *dev)
                                       MKDEV(COMEDI_MAJOR, dev->minor));
                }
                comedi_device_cleanup(dev);
-               kfree(dev);
+               comedi_dev_put(dev);
        }
 }
 
@@ -192,6 +217,40 @@ struct comedi_device *comedi_dev_from_minor(unsigned minor)
 }
 EXPORT_SYMBOL_GPL(comedi_dev_from_minor);
 
+static struct comedi_device *comedi_dev_get_from_board_minor(unsigned minor)
+{
+       struct comedi_device *dev;
+
+       BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
+       mutex_lock(&comedi_board_minor_table_lock);
+       dev = comedi_dev_get(comedi_board_minor_table[minor]);
+       mutex_unlock(&comedi_board_minor_table_lock);
+       return dev;
+}
+
+static struct comedi_device *comedi_dev_get_from_subdevice_minor(unsigned minor)
+{
+       struct comedi_device *dev;
+       struct comedi_subdevice *s;
+       unsigned int i = minor - COMEDI_NUM_BOARD_MINORS;
+
+       BUG_ON(i >= COMEDI_NUM_SUBDEVICE_MINORS);
+       mutex_lock(&comedi_subdevice_minor_table_lock);
+       s = comedi_subdevice_minor_table[i];
+       dev = comedi_dev_get(s ? s->device : NULL);
+       mutex_unlock(&comedi_subdevice_minor_table_lock);
+       return dev;
+}
+
+struct comedi_device *comedi_dev_get_from_minor(unsigned minor)
+{
+       if (minor < COMEDI_NUM_BOARD_MINORS)
+               return comedi_dev_get_from_board_minor(minor);
+       else
+               return comedi_dev_get_from_subdevice_minor(minor);
+}
+EXPORT_SYMBOL_GPL(comedi_dev_get_from_minor);
+
 static struct comedi_subdevice *
 comedi_read_subdevice(const struct comedi_device *dev, unsigned int minor)
 {
@@ -269,7 +328,7 @@ static ssize_t max_read_buffer_kb_show(struct device *csdev,
        struct comedi_subdevice *s;
        unsigned int size = 0;
 
-       dev = comedi_dev_from_minor(minor);
+       dev = comedi_dev_get_from_minor(minor);
        if (!dev)
                return -ENODEV;
 
@@ -279,6 +338,7 @@ static ssize_t max_read_buffer_kb_show(struct device *csdev,
                size = s->async->max_bufsize / 1024;
        mutex_unlock(&dev->mutex);
 
+       comedi_dev_put(dev);
        return snprintf(buf, PAGE_SIZE, "%i\n", size);
 }
 
@@ -299,7 +359,7 @@ static ssize_t max_read_buffer_kb_store(struct device *csdev,
                return -EINVAL;
        size *= 1024;
 
-       dev = comedi_dev_from_minor(minor);
+       dev = comedi_dev_get_from_minor(minor);
        if (!dev)
                return -ENODEV;
 
@@ -311,6 +371,7 @@ static ssize_t max_read_buffer_kb_store(struct device *csdev,
                err = -EINVAL;
        mutex_unlock(&dev->mutex);
 
+       comedi_dev_put(dev);
        return err ? err : count;
 }
 static DEVICE_ATTR_RW(max_read_buffer_kb);
@@ -323,7 +384,7 @@ static ssize_t read_buffer_kb_show(struct device *csdev,
        struct comedi_subdevice *s;
        unsigned int size = 0;
 
-       dev = comedi_dev_from_minor(minor);
+       dev = comedi_dev_get_from_minor(minor);
        if (!dev)
                return -ENODEV;
 
@@ -333,6 +394,7 @@ static ssize_t read_buffer_kb_show(struct device *csdev,
                size = s->async->prealloc_bufsz / 1024;
        mutex_unlock(&dev->mutex);
 
+       comedi_dev_put(dev);
        return snprintf(buf, PAGE_SIZE, "%i\n", size);
 }
 
@@ -353,7 +415,7 @@ static ssize_t read_buffer_kb_store(struct device *csdev,
                return -EINVAL;
        size *= 1024;
 
-       dev = comedi_dev_from_minor(minor);
+       dev = comedi_dev_get_from_minor(minor);
        if (!dev)
                return -ENODEV;
 
@@ -365,6 +427,7 @@ static ssize_t read_buffer_kb_store(struct device *csdev,
                err = -EINVAL;
        mutex_unlock(&dev->mutex);
 
+       comedi_dev_put(dev);
        return err ? err : count;
 }
 static DEVICE_ATTR_RW(read_buffer_kb);
@@ -378,7 +441,7 @@ static ssize_t max_write_buffer_kb_show(struct device *csdev,
        struct comedi_subdevice *s;
        unsigned int size = 0;
 
-       dev = comedi_dev_from_minor(minor);
+       dev = comedi_dev_get_from_minor(minor);
        if (!dev)
                return -ENODEV;
 
@@ -388,6 +451,7 @@ static ssize_t max_write_buffer_kb_show(struct device *csdev,
                size = s->async->max_bufsize / 1024;
        mutex_unlock(&dev->mutex);
 
+       comedi_dev_put(dev);
        return snprintf(buf, PAGE_SIZE, "%i\n", size);
 }
 
@@ -408,7 +472,7 @@ static ssize_t max_write_buffer_kb_store(struct device *csdev,
                return -EINVAL;
        size *= 1024;
 
-       dev = comedi_dev_from_minor(minor);
+       dev = comedi_dev_get_from_minor(minor);
        if (!dev)
                return -ENODEV;
 
@@ -420,6 +484,7 @@ static ssize_t max_write_buffer_kb_store(struct device *csdev,
                err = -EINVAL;
        mutex_unlock(&dev->mutex);
 
+       comedi_dev_put(dev);
        return err ? err : count;
 }
 static DEVICE_ATTR_RW(max_write_buffer_kb);
@@ -432,7 +497,7 @@ static ssize_t write_buffer_kb_show(struct device *csdev,
        struct comedi_subdevice *s;
        unsigned int size = 0;
 
-       dev = comedi_dev_from_minor(minor);
+       dev = comedi_dev_get_from_minor(minor);
        if (!dev)
                return -ENODEV;
 
@@ -442,6 +507,7 @@ static ssize_t write_buffer_kb_show(struct device *csdev,
                size = s->async->prealloc_bufsz / 1024;
        mutex_unlock(&dev->mutex);
 
+       comedi_dev_put(dev);
        return snprintf(buf, PAGE_SIZE, "%i\n", size);
 }
 
@@ -462,7 +528,7 @@ static ssize_t write_buffer_kb_store(struct device *csdev,
                return -EINVAL;
        size *= 1024;
 
-       dev = comedi_dev_from_minor(minor);
+       dev = comedi_dev_get_from_minor(minor);
        if (!dev)
                return -ENODEV;
 
@@ -474,6 +540,7 @@ static ssize_t write_buffer_kb_store(struct device *csdev,
                err = -EINVAL;
        mutex_unlock(&dev->mutex);
 
+       comedi_dev_put(dev);
        return err ? err : count;
 }
 static DEVICE_ATTR_RW(write_buffer_kb);
@@ -543,7 +610,7 @@ void *comedi_alloc_spriv(struct comedi_subdevice *s, size_t size)
 {
        s->private = kzalloc(size, GFP_KERNEL);
        if (s->private)
-               comedi_set_subdevice_runflags(s, ~0, SRF_FREE_SPRIV);
+               s->runflags |= SRF_FREE_SPRIV;
        return s->private;
 }
 EXPORT_SYMBOL_GPL(comedi_alloc_spriv);
@@ -562,12 +629,13 @@ static void do_become_nonbusy(struct comedi_device *dev,
                async->inttrig = NULL;
                kfree(async->cmd.chanlist);
                async->cmd.chanlist = NULL;
+               s->busy = NULL;
+               wake_up_interruptible_all(&s->async->wait_head);
        } else {
                dev_err(dev->class_dev,
                        "BUG: (?) do_become_nonbusy called with async=NULL\n");
+               s->busy = NULL;
        }
-
-       s->busy = NULL;
 }
 
 static int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
@@ -582,6 +650,21 @@ static int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
        return ret;
 }
 
+void comedi_device_cancel_all(struct comedi_device *dev)
+{
+       struct comedi_subdevice *s;
+       int i;
+
+       if (!dev->attached)
+               return;
+
+       for (i = 0; i < dev->n_subdevices; i++) {
+               s = &dev->subdevices[i];
+               if (s->async)
+                       do_cancel(dev, s);
+       }
+}
+
 static int is_device_busy(struct comedi_device *dev)
 {
        struct comedi_subdevice *s;
@@ -806,7 +889,6 @@ static int do_subdinfo_ioctl(struct comedi_device *dev,
                } else {
                        us->range_type = 0;     /* XXX */
                }
-               us->flags = s->flags;
 
                if (s->busy)
                        us->subd_flags |= SDF_BUSY;
@@ -818,8 +900,6 @@ static int do_subdinfo_ioctl(struct comedi_device *dev,
                        us->subd_flags |= SDF_LOCK_OWNER;
                if (!s->maxdata && s->maxdata_list)
                        us->subd_flags |= SDF_MAXDATA;
-               if (s->flaglist)
-                       us->subd_flags |= SDF_FLAGS;
                if (s->range_table_list)
                        us->subd_flags |= SDF_RANGETYPE;
                if (s->do_cmd)
@@ -829,8 +909,6 @@ static int do_subdinfo_ioctl(struct comedi_device *dev,
                        us->insn_bits_support = COMEDI_SUPPORTED;
                else
                        us->insn_bits_support = COMEDI_UNSUPPORTED;
-
-               us->settling_time_0 = s->settling_time_0;
        }
 
        ret = copy_to_user(arg, tmp, dev->n_subdevices * sizeof(*tmp));
@@ -875,13 +953,8 @@ static int do_chaninfo_ioctl(struct comedi_device *dev,
                        return -EFAULT;
        }
 
-       if (it.flaglist) {
-               if (!s->flaglist)
-                       return -EINVAL;
-               if (copy_to_user(it.flaglist, s->flaglist,
-                                s->n_chan * sizeof(unsigned int)))
-                       return -EFAULT;
-       }
+       if (it.flaglist)
+               return -EINVAL; /* flaglist not supported */
 
        if (it.rangelist) {
                int i;
@@ -1431,17 +1504,11 @@ static int do_cmd_ioctl(struct comedi_device *dev,
        async->cmd = cmd;
        async->cmd.data = NULL;
        /* load channel/gain list */
-       async->cmd.chanlist =
-           kmalloc(async->cmd.chanlist_len * sizeof(int), GFP_KERNEL);
-       if (!async->cmd.chanlist) {
-               DPRINTK("allocation failed\n");
-               return -ENOMEM;
-       }
-
-       if (copy_from_user(async->cmd.chanlist, user_chanlist,
-                          async->cmd.chanlist_len * sizeof(int))) {
-               DPRINTK("fault reading chanlist\n");
-               ret = -EFAULT;
+       async->cmd.chanlist = memdup_user(user_chanlist,
+                                         async->cmd.chanlist_len * sizeof(int));
+       if (IS_ERR(async->cmd.chanlist)) {
+               ret = PTR_ERR(async->cmd.chanlist);
+               DPRINTK("memdup_user failed with code %d\n", ret);
                goto cleanup;
        }
 
@@ -1485,7 +1552,7 @@ static int do_cmd_ioctl(struct comedi_device *dev,
        if (async->cmd.flags & TRIG_WAKE_EOS)
                async->cb_mask |= COMEDI_CB_EOS;
 
-       comedi_set_subdevice_runflags(s, ~0, SRF_USER | SRF_RUNNING);
+       comedi_set_subdevice_runflags(s, SRF_ERROR | SRF_RUNNING, SRF_RUNNING);
 
        /* set s->busy _after_ setting SRF_RUNNING flag to avoid race with
         * comedi_read() or comedi_write() */
@@ -1558,18 +1625,11 @@ static int do_cmdtest_ioctl(struct comedi_device *dev,
 
        /* load channel/gain list */
        if (cmd.chanlist) {
-               chanlist =
-                   kmalloc(cmd.chanlist_len * sizeof(int), GFP_KERNEL);
-               if (!chanlist) {
-                       DPRINTK("allocation failed\n");
-                       ret = -ENOMEM;
-                       goto cleanup;
-               }
-
-               if (copy_from_user(chanlist, user_chanlist,
-                                  cmd.chanlist_len * sizeof(int))) {
-                       DPRINTK("fault reading chanlist\n");
-                       ret = -EFAULT;
+               chanlist = memdup_user(user_chanlist,
+                                      cmd.chanlist_len * sizeof(int));
+               if (IS_ERR(chanlist)) {
+                       ret = PTR_ERR(chanlist);
+                       DPRINTK("memdup_user exited with code %d", ret);
                        goto cleanup;
                }
 
@@ -1722,8 +1782,6 @@ static int do_cancel_ioctl(struct comedi_device *dev, unsigned int arg,
                return -EBUSY;
 
        ret = do_cancel(dev, s);
-       if (comedi_get_subdevice_runflags(s) & SRF_USER)
-               wake_up_interruptible(&s->async->wait_head);
 
        return ret;
 }
@@ -2031,38 +2089,77 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
        DECLARE_WAITQUEUE(wait, current);
        const unsigned minor = iminor(file_inode(file));
        struct comedi_device *dev = comedi_dev_from_minor(minor);
+       bool on_wait_queue = false;
+       bool attach_locked;
+       unsigned int old_detach_count;
 
        if (!dev)
                return -ENODEV;
 
+       /* Protect against device detachment during operation. */
+       down_read(&dev->attach_lock);
+       attach_locked = true;
+       old_detach_count = dev->detach_count;
+
        if (!dev->attached) {
                DPRINTK("no driver configured on comedi%i\n", dev->minor);
-               return -ENODEV;
+               retval = -ENODEV;
+               goto out;
        }
 
        s = comedi_write_subdevice(dev, minor);
-       if (!s || !s->async)
-               return -EIO;
+       if (!s || !s->async) {
+               retval = -EIO;
+               goto out;
+       }
 
        async = s->async;
 
        if (!s->busy || !nbytes)
-               return 0;
-       if (s->busy != file)
-               return -EACCES;
+               goto out;
+       if (s->busy != file) {
+               retval = -EACCES;
+               goto out;
+       }
 
        add_wait_queue(&async->wait_head, &wait);
+       on_wait_queue = true;
        while (nbytes > 0 && !retval) {
                set_current_state(TASK_INTERRUPTIBLE);
 
                if (!comedi_is_subdevice_running(s)) {
                        if (count == 0) {
-                               mutex_lock(&dev->mutex);
+                               struct comedi_subdevice *new_s;
+
                                if (comedi_is_subdevice_in_error(s))
                                        retval = -EPIPE;
                                else
                                        retval = 0;
-                               do_become_nonbusy(dev, s);
+                               /*
+                                * To avoid deadlock, cannot acquire dev->mutex
+                                * while dev->attach_lock is held.  Need to
+                                * remove task from the async wait queue before
+                                * releasing dev->attach_lock, as it might not
+                                * be valid afterwards.
+                                */
+                               remove_wait_queue(&async->wait_head, &wait);
+                               on_wait_queue = false;
+                               up_read(&dev->attach_lock);
+                               attach_locked = false;
+                               mutex_lock(&dev->mutex);
+                               /*
+                                * Become non-busy unless things have changed
+                                * behind our back.  Checking dev->detach_count
+                                * is unchanged ought to be sufficient (unless
+                                * there have been 2**32 detaches in the
+                                * meantime!), but check the subdevice pointer
+                                * as well just in case.
+                                */
+                               new_s = comedi_write_subdevice(dev, minor);
+                               if (dev->attached &&
+                                   old_detach_count == dev->detach_count &&
+                                   s == new_s && new_s->async == async)
+                                       do_become_nonbusy(dev, s);
                                mutex_unlock(&dev->mutex);
                        }
                        break;
@@ -2112,8 +2209,12 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
                buf += n;
                break;          /* makes device work like a pipe */
        }
+out:
+       if (on_wait_queue)
+               remove_wait_queue(&async->wait_head, &wait);
        set_current_state(TASK_RUNNING);
-       remove_wait_queue(&async->wait_head, &wait);
+       if (attach_locked)
+               up_read(&dev->attach_lock);
 
        return count ? count : retval;
 }
@@ -2127,24 +2228,37 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes,
        DECLARE_WAITQUEUE(wait, current);
        const unsigned minor = iminor(file_inode(file));
        struct comedi_device *dev = comedi_dev_from_minor(minor);
+       unsigned int old_detach_count;
+       bool become_nonbusy = false;
+       bool attach_locked;
 
        if (!dev)
                return -ENODEV;
 
+       /* Protect against device detachment during operation. */
+       down_read(&dev->attach_lock);
+       attach_locked = true;
+       old_detach_count = dev->detach_count;
+
        if (!dev->attached) {
                DPRINTK("no driver configured on comedi%i\n", dev->minor);
-               return -ENODEV;
+               retval = -ENODEV;
+               goto out;
        }
 
        s = comedi_read_subdevice(dev, minor);
-       if (!s || !s->async)
-               return -EIO;
+       if (!s || !s->async) {
+               retval = -EIO;
+               goto out;
+       }
 
        async = s->async;
        if (!s->busy || !nbytes)
-               return 0;
-       if (s->busy != file)
-               return -EACCES;
+               goto out;
+       if (s->busy != file) {
+               retval = -EACCES;
+               goto out;
+       }
 
        add_wait_queue(&async->wait_head, &wait);
        while (nbytes > 0 && !retval) {
@@ -2162,13 +2276,11 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes,
 
                if (n == 0) {
                        if (!comedi_is_subdevice_running(s)) {
-                               mutex_lock(&dev->mutex);
-                               do_become_nonbusy(dev, s);
                                if (comedi_is_subdevice_in_error(s))
                                        retval = -EPIPE;
                                else
                                        retval = 0;
-                               mutex_unlock(&dev->mutex);
+                               become_nonbusy = true;
                                break;
                        }
                        if (file->f_flags & O_NONBLOCK) {
@@ -2206,14 +2318,37 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes,
                buf += n;
                break;          /* makes device work like a pipe */
        }
-       if (comedi_is_subdevice_idle(s)) {
+       remove_wait_queue(&async->wait_head, &wait);
+       set_current_state(TASK_RUNNING);
+       if (become_nonbusy || comedi_is_subdevice_idle(s)) {
+               struct comedi_subdevice *new_s;
+
+               /*
+                * To avoid deadlock, cannot acquire dev->mutex
+                * while dev->attach_lock is held.
+                */
+               up_read(&dev->attach_lock);
+               attach_locked = false;
                mutex_lock(&dev->mutex);
-               if (async->buf_read_count - async->buf_write_count == 0)
-                       do_become_nonbusy(dev, s);
+               /*
+                * Check device hasn't become detached behind our back.
+                * Checking dev->detach_count is unchanged ought to be
+                * sufficient (unless there have been 2**32 detaches in the
+                * meantime!), but check the subdevice pointer as well just in
+                * case.
+                */
+               new_s = comedi_read_subdevice(dev, minor);
+               if (dev->attached && old_detach_count == dev->detach_count &&
+                   s == new_s && new_s->async == async) {
+                       if (become_nonbusy ||
+                           async->buf_read_count - async->buf_write_count == 0)
+                               do_become_nonbusy(dev, s);
+               }
                mutex_unlock(&dev->mutex);
        }
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(&async->wait_head, &wait);
+out:
+       if (attach_locked)
+               up_read(&dev->attach_lock);
 
        return count ? count : retval;
 }
@@ -2221,7 +2356,8 @@ 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_device *dev = comedi_dev_from_minor(minor);
+       struct comedi_device *dev = comedi_dev_get_from_minor(minor);
+       int rc;
 
        if (!dev) {
                DPRINTK("invalid minor number\n");
@@ -2246,8 +2382,8 @@ static int comedi_open(struct inode *inode, struct file *file)
                goto ok;
        if (!capable(CAP_NET_ADMIN) && dev->in_request_module) {
                DPRINTK("in request module\n");
-               mutex_unlock(&dev->mutex);
-               return -ENODEV;
+               rc = -ENODEV;
+               goto out;
        }
        if (capable(CAP_NET_ADMIN) && dev->in_request_module)
                goto ok;
@@ -2264,8 +2400,8 @@ static int comedi_open(struct inode *inode, struct file *file)
 
        if (!dev->attached && !capable(CAP_NET_ADMIN)) {
                DPRINTK("not attached and not CAP_NET_ADMIN\n");
-               mutex_unlock(&dev->mutex);
-               return -ENODEV;
+               rc = -ENODEV;
+               goto out;
        }
 ok:
        __module_get(THIS_MODULE);
@@ -2273,26 +2409,28 @@ ok:
        if (dev->attached) {
                if (!try_module_get(dev->driver->module)) {
                        module_put(THIS_MODULE);
-                       mutex_unlock(&dev->mutex);
-                       return -ENOSYS;
+                       rc = -ENOSYS;
+                       goto out;
                }
        }
 
        if (dev->attached && dev->use_count == 0 && dev->open) {
-               int rc = dev->open(dev);
+               rc = dev->open(dev);
                if (rc < 0) {
                        module_put(dev->driver->module);
                        module_put(THIS_MODULE);
-                       mutex_unlock(&dev->mutex);
-                       return rc;
+                       goto out;
                }
        }
 
        dev->use_count++;
+       rc = 0;
 
+out:
        mutex_unlock(&dev->mutex);
-
-       return 0;
+       if (rc)
+               comedi_dev_put(dev);
+       return rc;
 }
 
 static int comedi_fasync(int fd, struct file *file, int on)
@@ -2338,6 +2476,7 @@ static int comedi_close(struct inode *inode, struct file *file)
        dev->use_count--;
 
        mutex_unlock(&dev->mutex);
+       comedi_dev_put(dev);
 
        return 0;
 }
@@ -2390,16 +2529,11 @@ void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s)
        }
 
        if (async->cb_mask & s->async->events) {
-               if (comedi_get_subdevice_runflags(s) & SRF_USER) {
-                       wake_up_interruptible(&async->wait_head);
-                       if (s->subdev_flags & SDF_CMD_READ)
-                               kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
-                       if (s->subdev_flags & SDF_CMD_WRITE)
-                               kill_fasync(&dev->async_queue, SIGIO, POLL_OUT);
-               } else {
-                       if (async->cb_func)
-                               async->cb_func(s->async->events, async->cb_arg);
-               }
+               wake_up_interruptible(&async->wait_head);
+               if (s->subdev_flags & SDF_CMD_READ)
+                       kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
+               if (s->subdev_flags & SDF_CMD_WRITE)
+                       kill_fasync(&dev->async_queue, SIGIO, POLL_OUT);
        }
        s->async->events = 0;
 }
@@ -2430,7 +2564,7 @@ struct comedi_device *comedi_alloc_board_minor(struct device *hardware_device)
        if (i == COMEDI_NUM_BOARD_MINORS) {
                mutex_unlock(&dev->mutex);
                comedi_device_cleanup(dev);
-               kfree(dev);
+               comedi_dev_put(dev);
                pr_err("comedi: error: ran out of minor numbers for board device files.\n");
                return ERR_PTR(-EBUSY);
        }