On 01/15/2014 06:01 PM, Michal Nazarewicz wrote: > On Tue, Jan 14 2014, Robert Baldyga wrote: >> This patch adds asynchronous I/O support for FunctionFS endpoint >> files. > > Thanks for doing this. I never had enough time to finish this feature. > >> @@ -343,6 +345,25 @@ static char *ffs_prepare_buffer(const char __user *buf, size_t len) >> __attribute__((warn_unused_result, nonnull)); >> >> >> +/* ffs_io_data structure ***************************************************/ >> + >> +struct ffs_io_data { >> + int aio:1; >> + int read:1; > > Just make those bools. > >> + > > Nit: trailing white-space here in in other places. ;) > >> + struct kiocb *kiocb; >> + const struct iovec *iovec; >> + unsigned long nr_segs; >> + char __user *buf; >> + size_t len; >> + >> + struct mm_struct *mm; >> + struct work_struct work; >> + >> + struct usb_ep *ep; >> + struct usb_request *req; >> +}; >> + >> /* Control file aka ep0 *****************************************************/ >> >> static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req) >> @@ -788,8 +809,51 @@ static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req) >> } >> } >> >> -static ssize_t ffs_epfile_io(struct file *file, >> - char __user *buf, size_t len, int read) >> +static void ffs_user_copy_worker(struct work_struct *work) >> +{ >> + size_t len = 0; >> + int i = 0; >> + int ret; >> + >> + struct ffs_io_data *io_data = container_of(work, struct ffs_io_data, work); > > Nit: Over 80 characters. > >> + >> + use_mm(io_data->mm); >> + for (i=0; i < io_data->nr_segs; i++) { >> + ret = copy_to_user(io_data->iovec[i].iov_base, >> + &io_data->buf[len], >> + io_data->iovec[i].iov_len); >> + len += io_data->iovec[i].iov_len; >> + } >> + unuse_mm(io_data->mm); >> + >> + aio_complete(io_data->kiocb, 0, 0); >> + >> + kfree(io_data->iovec); >> + kfree(io_data->buf); >> + kfree(io_data); >> +} > >> +static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) >> { >> struct ffs_epfile *epfile = file->private_data; >> struct ffs_ep *ep; >> @@ -825,25 +889,12 @@ first_try: >> } >> >> /* Do we halt? */ >> - halt = !read == !epfile->in; >> + halt = !io_data->read == !epfile->in; >> if (halt && epfile->isoc) { >> ret = -EINVAL; >> goto error; >> } >> >> - /* Allocate & copy */ >> - if (!halt && !data) { >> - data = kzalloc(len, GFP_KERNEL); >> - if (unlikely(!data)) >> - return -ENOMEM; >> - >> - if (!read && >> - unlikely(__copy_from_user(data, buf, len))) { >> - ret = -EFAULT; >> - goto error; >> - } >> - } >> - >> /* We will be using request */ >> ret = ffs_mutex_lock(&epfile->mutex, >> file->f_flags & O_NONBLOCK); >> @@ -869,33 +920,86 @@ first_try: >> spin_unlock_irq(&epfile->ffs->eps_lock); >> ret = -EBADMSG; >> } else { >> - /* Fire the request */ >> - DECLARE_COMPLETION_ONSTACK(done); >> + struct usb_request *req; >> >> - struct usb_request *req = ep->req; >> - req->context = &done; >> - req->complete = ffs_epfile_io_complete; >> - req->buf = data; >> - req->length = len; >> + data = kzalloc(io_data->len, GFP_KERNEL); >> + if (unlikely(!data)) >> + return -ENOMEM; >> + >> + if(io_data->aio) { >> + size_t len = 0; >> + int i; >> + for (i=0; !io_data->read && i < io_data->nr_segs; i++) { >> + if (unlikely(copy_from_user(&data[len], > > The whole if-else-if-else is under a spin lock so you cannot > copy_from_user. This is why in original code copying was done prior to > this piece of code. > >> + io_data->iovec[i].iov_base, >> + io_data->iovec[i].iov_len) != 0)) { >> + ret = -EFAULT; >> + goto error; >> + } >> + len += io_data->iovec[i].iov_len; >> + } >> >> - ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); >> + req = usb_ep_alloc_request(ep->ep, GFP_KERNEL); >> + if(unlikely(!req)) >> + goto error; >> >> - spin_unlock_irq(&epfile->ffs->eps_lock); >> + req->buf = data; >> + req->length = io_data->len; >> >> - if (unlikely(ret < 0)) { >> - /* nop */ >> - } else if (unlikely(wait_for_completion_interruptible(&done))) { >> - ret = -EINTR; >> - usb_ep_dequeue(ep->ep, req); >> - } else { >> - ret = ep->status; >> - if (read && ret > 0 && >> - unlikely(copy_to_user(buf, data, ret))) >> + io_data->buf = data; >> + io_data->ep = ep->ep; >> + io_data->req = req; >> + >> + req->context = io_data; >> + req->complete = ffs_epfile_async_io_complete; >> + >> + ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); >> + if(unlikely(ret)) { >> + usb_ep_free_request(ep->ep, req); >> + goto error; >> + } >> + ret = -EIOCBQUEUED; >> + >> + spin_unlock_irq(&epfile->ffs->eps_lock); >> + } >> + else { >> + DECLARE_COMPLETION_ONSTACK(done); >> + >> + if (!io_data->read && >> + unlikely(__copy_from_user(data, io_data->buf, >> + io_data->len))) { >> ret = -EFAULT; >> + goto error; >> + } >> + >> + req = ep->req; >> + req->buf = data; >> + req->length = io_data->len; >> + >> + req->context = &done; >> + req->complete = ffs_epfile_io_complete; >> + >> + ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); >> + >> + spin_unlock_irq(&epfile->ffs->eps_lock); >> + >> + if (unlikely(ret < 0)) { >> + /* nop */ >> + } else if (unlikely(wait_for_completion_interruptible(&done))) { >> + ret = -EINTR; >> + usb_ep_dequeue(ep->ep, req); >> + } else { >> + ret = ep->status; >> + if (io_data->read && ret > 0 && >> + unlikely(copy_to_user(io_data->buf, data, ret))) >> + ret = -EFAULT; >> + } >> + kfree(data); >> } >> } >> >> mutex_unlock(&epfile->mutex); >> + return ret; >> error: >> kfree(data); >> return ret; > >> +static ssize_t ffs_epfile_aio_write(struct kiocb *kiocb, const struct iovec *iovec, unsigned long nr_segs, loff_t loff) >> +{ >> + struct ffs_io_data *io_data; >> + >> + ENTER(); >> + >> + io_data = kmalloc(sizeof(struct ffs_io_data), GFP_KERNEL); > > io_data = kmalloc(sizeof(*io_data), GFP_KERNEL); > if (!io_data) { > return -ENOMEM; > } > >> + io_data->aio = 1; >> + io_data->read = 0; >> + io_data->kiocb = kiocb; >> + io_data->iovec = iovec; >> + io_data->nr_segs = nr_segs; >> + io_data->len = kiocb->ki_nbytes; >> + io_data->mm = current->mm; >> + >> + kiocb->private = io_data; >> + >> + kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); >> + >> + return ffs_epfile_io(kiocb->ki_filp, io_data); >> +} >> + >> +static ssize_t ffs_epfile_aio_read(struct kiocb *kiocb, const struct iovec *iovec, unsigned long nr_segs, loff_t loff) >> +{ >> + struct ffs_io_data *io_data; >> + struct iovec *iovec_copy; >> + >> + ENTER(); >> + >> + iovec_copy = kmalloc(sizeof(struct iovec)*nr_segs, GFP_KERNEL); > > iovec_copy = kmalloc_array(nr_regs, sizeof(*iovec_copy), GFP_KERNEL); > if (!iovec_copy) { > return -ENOMEM; > } > > Besides, do we really need to make a copy? I haven't checked AIO API > but that sounds strange to me. > I was also surprised, but this struct is created on stack in AIO module. >> + memcpy(iovec_copy, iovec, sizeof(struct iovec)*nr_segs); >> + >> + io_data = kmalloc(sizeof(struct ffs_io_data), GFP_KERNEL); > > io_data = kmalloc(sizeof(*io_data), GFP_KERNEL); > if (!io_data) { > kfree(iovec_copy); > return -ENOMEM; > } > >> + io_data->aio = 1; >> + io_data->read = 1; >> + io_data->kiocb = kiocb; >> + io_data->iovec = iovec_copy; >> + io_data->nr_segs = nr_segs; >> + io_data->len = kiocb->ki_nbytes; >> + io_data->mm = current->mm; >> + >> + kiocb->private = io_data; >> + >> + kiocb_set_cancel_fn(kiocb, ffs_aio_cancel); >> + >> + return ffs_epfile_io(kiocb->ki_filp, io_data); >> +} >> + >> static int >> ffs_epfile_release(struct inode *inode, struct file *file) >> { > > > Thanks for suggestions. Best regards Robert Baldyga Samsung R&D Institute Poland -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html