diff options
Diffstat (limited to 'drivers/staging/comedi')
-rw-r--r-- | drivers/staging/comedi/comedi_fops.c | 25 |
1 files changed, 15 insertions, 10 deletions
diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index 88491e9..7e42190 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -1078,22 +1078,19 @@ static int do_cmd_ioctl(struct comedi_device *dev, DPRINTK("subdevice busy\n"); return -EBUSY; } - s->busy = file; /* make sure channel/gain list isn't too long */ if (user_cmd.chanlist_len > s->len_chanlist) { DPRINTK("channel/gain list too long %u > %d\n", user_cmd.chanlist_len, s->len_chanlist); - ret = -EINVAL; - goto cleanup; + return -EINVAL; } /* make sure channel/gain list isn't too short */ if (user_cmd.chanlist_len < 1) { DPRINTK("channel/gain list too short %u < 1\n", user_cmd.chanlist_len); - ret = -EINVAL; - goto cleanup; + return -EINVAL; } async->cmd = user_cmd; @@ -1103,8 +1100,7 @@ static int do_cmd_ioctl(struct comedi_device *dev, kmalloc(async->cmd.chanlist_len * sizeof(int), GFP_KERNEL); if (!async->cmd.chanlist) { DPRINTK("allocation failed\n"); - ret = -ENOMEM; - goto cleanup; + return -ENOMEM; } if (copy_from_user(async->cmd.chanlist, user_cmd.chanlist, @@ -1156,6 +1152,9 @@ static int do_cmd_ioctl(struct comedi_device *dev, comedi_set_subdevice_runflags(s, ~0, SRF_USER | SRF_RUNNING); + /* set s->busy _after_ setting SRF_RUNNING flag to avoid race with + * comedi_read() or comedi_write() */ + s->busy = file; ret = s->do_cmd(dev, s); if (ret == 0) return 0; @@ -1658,6 +1657,7 @@ static ssize_t comedi_write(struct file *file, const char __user *buf, if (!(comedi_get_subdevice_runflags(s) & SRF_RUNNING)) { if (count == 0) { + mutex_lock(&dev->mutex); if (comedi_get_subdevice_runflags(s) & SRF_ERROR) { retval = -EPIPE; @@ -1665,6 +1665,7 @@ static ssize_t comedi_write(struct file *file, const char __user *buf, retval = 0; } do_become_nonbusy(dev, s); + mutex_unlock(&dev->mutex); } break; } @@ -1779,6 +1780,7 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes, if (n == 0) { if (!(comedi_get_subdevice_runflags(s) & SRF_RUNNING)) { + mutex_lock(&dev->mutex); do_become_nonbusy(dev, s); if (comedi_get_subdevice_runflags(s) & SRF_ERROR) { @@ -1786,6 +1788,7 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes, } else { retval = 0; } + mutex_unlock(&dev->mutex); break; } if (file->f_flags & O_NONBLOCK) { @@ -1823,9 +1826,11 @@ 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_get_subdevice_runflags(s) & (SRF_ERROR | SRF_RUNNING)) && - async->buf_read_count - async->buf_write_count == 0) { - do_become_nonbusy(dev, s); + if (!(comedi_get_subdevice_runflags(s) & (SRF_ERROR | SRF_RUNNING))) { + mutex_lock(&dev->mutex); + if (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); |