usb: f_fs: avoid race condition with ffs_epfile_io_complete
[deliverable/linux.git] / drivers / usb / gadget / function / f_fs.c
index 63fe6931b17b32297513a964dd77436c2bfb2a50..8cfce105c7eeb2ed516c28507553d038e89f33a6 100644 (file)
@@ -778,6 +778,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
                ret = -EINVAL;
        } else if (!io_data->aio) {
                DECLARE_COMPLETION_ONSTACK(done);
+               bool interrupted = false;
 
                req = ep->req;
                req->buf      = data;
@@ -793,9 +794,14 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
                spin_unlock_irq(&epfile->ffs->eps_lock);
 
                if (unlikely(wait_for_completion_interruptible(&done))) {
-                       ret = -EINTR;
+                       /*
+                        * To avoid race condition with ffs_epfile_io_complete,
+                        * dequeue the request first then check
+                        * status. usb_ep_dequeue API should guarantee no race
+                        * condition with req->complete callback.
+                        */
                        usb_ep_dequeue(ep->ep, req);
-                       goto error_mutex;
+                       interrupted = ep->status < 0;
                }
 
                /*
@@ -804,7 +810,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
                 * rounded up to maxpacketsize), we may end up with more data
                 * then user space has space for.
                 */
-               ret = ep->status;
+               ret = interrupted ? -EINTR : ep->status;
                if (io_data->read && ret > 0) {
                        ret = copy_to_iter(data, ret, &io_data->data);
                        if (!ret)
This page took 0.027329 seconds and 5 git commands to generate.