diff options
author | Kent Overstreet <koverstreet@google.com> | 2013-03-22 12:51:30 -0700 |
---|---|---|
committer | Kent Overstreet <kmo@daterainc.com> | 2013-10-10 20:50:45 -0700 |
commit | b0c8599eddc0d4c28e22087854cd5f30787e1cf2 (patch) | |
tree | a264f2fc69ff96c2e2860f6b08b317d7b06ba49e | |
parent | 377ea2c12e8ff6a8803fe7ea5f2919277b2c7c24 (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.c | 61 |
1 files changed, 28 insertions, 33 deletions
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index b94c049ab0d0..473498d75a8b 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 |