diff options
-rw-r--r-- | drivers/usb/gadget/inode.c | 38 |
1 files changed, 29 insertions, 9 deletions
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index dda0dc4a5567..32bcbeba119e 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/sched.h> | 24 | #include <linux/sched.h> |
25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
26 | #include <linux/poll.h> | 26 | #include <linux/poll.h> |
27 | #include <linux/mmu_context.h> | ||
27 | 28 | ||
28 | #include <linux/device.h> | 29 | #include <linux/device.h> |
29 | #include <linux/moduleparam.h> | 30 | #include <linux/moduleparam.h> |
@@ -513,6 +514,9 @@ static long ep_ioctl(struct file *fd, unsigned code, unsigned long value) | |||
513 | struct kiocb_priv { | 514 | struct kiocb_priv { |
514 | struct usb_request *req; | 515 | struct usb_request *req; |
515 | struct ep_data *epdata; | 516 | struct ep_data *epdata; |
517 | struct kiocb *iocb; | ||
518 | struct mm_struct *mm; | ||
519 | struct work_struct work; | ||
516 | void *buf; | 520 | void *buf; |
517 | const struct iovec *iv; | 521 | const struct iovec *iv; |
518 | unsigned long nr_segs; | 522 | unsigned long nr_segs; |
@@ -540,15 +544,12 @@ static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e) | |||
540 | return value; | 544 | return value; |
541 | } | 545 | } |
542 | 546 | ||
543 | static ssize_t ep_aio_read_retry(struct kiocb *iocb) | 547 | static ssize_t ep_copy_to_user(struct kiocb_priv *priv) |
544 | { | 548 | { |
545 | struct kiocb_priv *priv = iocb->private; | ||
546 | ssize_t len, total; | 549 | ssize_t len, total; |
547 | void *to_copy; | 550 | void *to_copy; |
548 | int i; | 551 | int i; |
549 | 552 | ||
550 | /* we "retry" to get the right mm context for this: */ | ||
551 | |||
552 | /* copy stuff into user buffers */ | 553 | /* copy stuff into user buffers */ |
553 | total = priv->actual; | 554 | total = priv->actual; |
554 | len = 0; | 555 | len = 0; |
@@ -568,9 +569,26 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb) | |||
568 | if (total == 0) | 569 | if (total == 0) |
569 | break; | 570 | break; |
570 | } | 571 | } |
572 | |||
573 | return len; | ||
574 | } | ||
575 | |||
576 | static void ep_user_copy_worker(struct work_struct *work) | ||
577 | { | ||
578 | struct kiocb_priv *priv = container_of(work, struct kiocb_priv, work); | ||
579 | struct mm_struct *mm = priv->mm; | ||
580 | struct kiocb *iocb = priv->iocb; | ||
581 | size_t ret; | ||
582 | |||
583 | use_mm(mm); | ||
584 | ret = ep_copy_to_user(priv); | ||
585 | unuse_mm(mm); | ||
586 | |||
587 | /* completing the iocb can drop the ctx and mm, don't touch mm after */ | ||
588 | aio_complete(iocb, ret, ret); | ||
589 | |||
571 | kfree(priv->buf); | 590 | kfree(priv->buf); |
572 | kfree(priv); | 591 | kfree(priv); |
573 | return len; | ||
574 | } | 592 | } |
575 | 593 | ||
576 | static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) | 594 | static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) |
@@ -596,14 +614,14 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) | |||
596 | aio_complete(iocb, req->actual ? req->actual : req->status, | 614 | aio_complete(iocb, req->actual ? req->actual : req->status, |
597 | req->status); | 615 | req->status); |
598 | } else { | 616 | } else { |
599 | /* retry() won't report both; so we hide some faults */ | 617 | /* ep_copy_to_user() won't report both; we hide some faults */ |
600 | if (unlikely(0 != req->status)) | 618 | if (unlikely(0 != req->status)) |
601 | DBG(epdata->dev, "%s fault %d len %d\n", | 619 | DBG(epdata->dev, "%s fault %d len %d\n", |
602 | ep->name, req->status, req->actual); | 620 | ep->name, req->status, req->actual); |
603 | 621 | ||
604 | priv->buf = req->buf; | 622 | priv->buf = req->buf; |
605 | priv->actual = req->actual; | 623 | priv->actual = req->actual; |
606 | kick_iocb(iocb); | 624 | schedule_work(&priv->work); |
607 | } | 625 | } |
608 | spin_unlock(&epdata->dev->lock); | 626 | spin_unlock(&epdata->dev->lock); |
609 | 627 | ||
@@ -633,8 +651,10 @@ fail: | |||
633 | return value; | 651 | return value; |
634 | } | 652 | } |
635 | iocb->private = priv; | 653 | iocb->private = priv; |
654 | priv->iocb = iocb; | ||
636 | priv->iv = iv; | 655 | priv->iv = iv; |
637 | priv->nr_segs = nr_segs; | 656 | priv->nr_segs = nr_segs; |
657 | INIT_WORK(&priv->work, ep_user_copy_worker); | ||
638 | 658 | ||
639 | value = get_ready_ep(iocb->ki_filp->f_flags, epdata); | 659 | value = get_ready_ep(iocb->ki_filp->f_flags, epdata); |
640 | if (unlikely(value < 0)) { | 660 | if (unlikely(value < 0)) { |
@@ -646,6 +666,7 @@ fail: | |||
646 | get_ep(epdata); | 666 | get_ep(epdata); |
647 | priv->epdata = epdata; | 667 | priv->epdata = epdata; |
648 | priv->actual = 0; | 668 | priv->actual = 0; |
669 | priv->mm = current->mm; /* mm teardown waits for iocbs in exit_aio() */ | ||
649 | 670 | ||
650 | /* each kiocb is coupled to one usb_request, but we can't | 671 | /* each kiocb is coupled to one usb_request, but we can't |
651 | * allocate or submit those if the host disconnected. | 672 | * allocate or submit those if the host disconnected. |
@@ -674,7 +695,7 @@ fail: | |||
674 | kfree(priv); | 695 | kfree(priv); |
675 | put_ep(epdata); | 696 | put_ep(epdata); |
676 | } else | 697 | } else |
677 | value = (iv ? -EIOCBRETRY : -EIOCBQUEUED); | 698 | value = -EIOCBQUEUED; |
678 | return value; | 699 | return value; |
679 | } | 700 | } |
680 | 701 | ||
@@ -692,7 +713,6 @@ ep_aio_read(struct kiocb *iocb, const struct iovec *iov, | |||
692 | if (unlikely(!buf)) | 713 | if (unlikely(!buf)) |
693 | return -ENOMEM; | 714 | return -ENOMEM; |
694 | 715 | ||
695 | iocb->ki_retry = ep_aio_read_retry; | ||
696 | return ep_aio_rwtail(iocb, buf, iocb->ki_left, epdata, iov, nr_segs); | 716 | return ep_aio_rwtail(iocb, buf, iocb->ki_left, epdata, iov, nr_segs); |
697 | } | 717 | } |
698 | 718 | ||