Previous patch got rid of kiocb->ki_users; this was done by having
kiocb_cancel()/aio_complete() explicitly synchronize with each other.

The new rule is that when a driver calls aio_complete(), after
aio_complete() returns ki_cancel cannot be running and it's safe to
dispose of kiocb->priv. But, this means ki_cancel() won't be able to
call aio_complete() itself, or aio_complete() will deadlock.

So, update the driver.

Signed-off-by: Kent Overstreet <koverstr...@google.com>
Cc: Zach Brown <z...@redhat.com>
Cc: Felipe Balbi <ba...@ti.com>
Cc: Greg Kroah-Hartman <gre...@linuxfoundation.org>
Cc: Mark Fasheh <mfas...@suse.com>
Cc: Joel Becker <jl...@evilplan.org>
Cc: Rusty Russell <ru...@rustcorp.com.au>
Cc: Jens Axboe <ax...@kernel.dk>
Cc: Asai Thambi S P <asamymuth...@micron.com>
Cc: Selvan Mani <sm...@micron.com>
Cc: Sam Bradshaw <sbrads...@micron.com>
Cc: Jeff Moyer <jmo...@redhat.com>
Cc: Al Viro <v...@zeniv.linux.org.uk>
Cc: Benjamin LaHaise <b...@kvack.org>
---
 drivers/usb/gadget/inode.c | 61 +++++++++++++++++++++-------------------------
 1 file changed, 28 insertions(+), 33 deletions(-)

diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index f255ad7..69adb87 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -522,6 +522,7 @@ struct kiocb_priv {
        const struct iovec      *iv;
        unsigned long           nr_segs;
        unsigned                actual;
+       int                     status;
 };
 
 static int ep_aio_cancel(struct kiocb *iocb)
@@ -577,14 +578,26 @@ static void ep_user_copy_worker(struct work_struct *work)
        struct kiocb_priv *priv = container_of(work, struct kiocb_priv, work);
        struct mm_struct *mm = priv->mm;
        struct kiocb *iocb = priv->iocb;
-       size_t ret;
 
-       use_mm(mm);
-       ret = ep_copy_to_user(priv);
-       unuse_mm(mm);
+       if (priv->iv && priv->actual) {
+               size_t ret;
+
+               use_mm(mm);
+               ret = ep_copy_to_user(priv);
+               unuse_mm(mm);
+
+               if (!priv->status)
+                       priv->status = ret;
+               /*
+                * completing the iocb can drop the ctx and mm, don't touch mm
+                * after
+                */
+       }
 
-       /* completing the iocb can drop the ctx and mm, don't touch mm after */
-       aio_complete(iocb, ret, ret);
+
+       /* aio_complete() reports bytes-transferred _and_ faults */
+       aio_complete(iocb, priv->actual ? priv->actual : priv->status,
+                    priv->status);
 
        kfree(priv->buf);
        kfree(priv);
@@ -596,36 +609,18 @@ static void ep_aio_complete(struct usb_ep *ep, struct 
usb_request *req)
        struct kiocb_priv       *priv = iocb->private;
        struct ep_data          *epdata = priv->epdata;
 
-       /* lock against disconnect (and ideally, cancel) */
-       spin_lock(&epdata->dev->lock);
-       priv->req = NULL;
-       priv->epdata = NULL;
-
-       /* if this was a write or a read returning no data then we
-        * don't need to copy anything to userspace, so we can
-        * complete the aio request immediately.
-        */
-       if (priv->iv == NULL || unlikely(req->actual == 0)) {
-               kfree(req->buf);
-               kfree(priv);
-               iocb->private = NULL;
-               /* aio_complete() reports bytes-transferred _and_ faults */
-               aio_complete(iocb, req->actual ? req->actual : req->status,
-                               req->status);
-       } else {
-               /* ep_copy_to_user() won't report both; we hide some faults */
-               if (unlikely(0 != req->status))
-                       DBG(epdata->dev, "%s fault %d len %d\n",
-                               ep->name, req->status, req->actual);
-
-               priv->buf = req->buf;
-               priv->actual = req->actual;
-               schedule_work(&priv->work);
-       }
-       spin_unlock(&epdata->dev->lock);
+       priv->buf       = req->buf;
+       priv->actual    = req->actual;
+       priv->status    = req->status;
 
        usb_ep_free_request(ep, req);
        put_ep(epdata);
+
+       if ((priv->iv && priv->actual) ||
+           iocb->ki_cancel == KIOCB_CANCELLING)
+               schedule_work(&priv->work);
+       else
+               ep_user_copy_worker(&priv->work);
 }
 
 static ssize_t
-- 
1.8.2.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to