summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <koverstreet@google.com>2013-03-22 12:51:30 -0700
committerKent Overstreet <koverstreet@google.com>2013-06-17 19:39:44 -0700
commit1cc6fe9e12e50fed34d72be24a9919da7be72112 (patch)
tree4d9bd49e1b1704acf3df0fdb2609a54c3d40df8f
parent41a5ea05ee70ebe7ef4fceb5150975845826ddeb (diff)
aio/usb: Update cancellation for new synchonization
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 <koverstreet@google.com> Cc: Zach Brown <zab@redhat.com> Cc: Felipe Balbi <balbi@ti.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Mark Fasheh <mfasheh@suse.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Rusty Russell <rusty@rustcorp.com.au> Cc: Jens Axboe <axboe@kernel.dk> Cc: Asai Thambi S P <asamymuthupa@micron.com> Cc: Selvan Mani <smani@micron.com> Cc: Sam Bradshaw <sbradshaw@micron.com> Cc: Jeff Moyer <jmoyer@redhat.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Benjamin LaHaise <bcrl@kvack.org>
-rw-r--r--drivers/usb/gadget/inode.c61
1 files changed, 28 insertions, 33 deletions
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index f255ad7f4c74..69adb876b380 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