diff options
author | Sujeet Kumar <ksujeet@codeaurora.org> | 2016-01-28 10:42:06 -0800 |
---|---|---|
committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 11:10:01 -0700 |
commit | 55a6f12e78ba2ee1eb3be9f12cc840085f1ea9fc (patch) | |
tree | dbc646ae27c517a05a07e6e58606fd31ad6e3920 /drivers/usb/gadget/function | |
parent | 8b5f119fba94e61fdc271fc555a7357e34192704 (diff) |
USB: f_fs: Fail stale read IOs after disconnect
After a USB disconnect, endpoints for adb are disabled.
After this no IO is allowed on the endpoints.
Since, adbd is not aware of this disconnect, it may
still perform read/writes IO. For adb writes, IOs are
failed, but for adb reads kernel waits untill endpoints
are enabled.
When a USB disconnect and adb read still queued
a buffer to kernel, ffs_epfile_io simply waits for
endpoint to be enabled. A next connect happens
and endpoints are enabled after set_alt, the adb
read stale buffer from previous session continues
and queues to endpoint.
All this time, adb did not close the epfile because
it did not get return status on the IOs which it
queued. This is an issue, because a new session
is not established and both userspace and kernel
goes out of sync.
To fix this issue, when endpoints are disbled
set epfile error. This epfile error is only cleared
in epfile open. This will ensure that after a USB
disconnect and connect, new session is established.
Also, return ENODEV if endpoints not enabled rather
than EINTR as EINTR case, and simply retries the
request. Incase usb_ep_queue failed, return -EIO
inspite of depend on return status from usb_ep_queue.
Change-Id: I6e677e98ec28e5462b372ed290acdde251286f48
Signed-off-by: Sujeet Kumar <ksujeet@codeaurora.org>
Signed-off-by: Mayank Rana <mrana@codeaurora.org>
Diffstat (limited to 'drivers/usb/gadget/function')
-rw-r--r-- | drivers/usb/gadget/function/f_fs.c | 22 |
1 files changed, 18 insertions, 4 deletions
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index c7adc17c1b30..a3862a21db85 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -124,6 +124,7 @@ struct ffs_epfile { /* Protects ep->ep and ep->req. */ struct mutex mutex; wait_queue_head_t wait; + atomic_t error; struct ffs_data *ffs; struct ffs_ep *ep; /* P: ffs->eps_lock */ @@ -703,9 +704,19 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) goto error; } - ret = wait_event_interruptible(epfile->wait, (ep = epfile->ep)); - if (ret) { - ret = -EINTR; + /* + * If ep is disabled, this fails all current IOs + * and wait for next epfile open to happen. + */ + if (!atomic_read(&epfile->error)) { + ret = wait_event_interruptible(epfile->wait, + (ep = epfile->ep)); + if (ret < 0) + goto error; + } + + if (!ep) { + ret = -ENODEV; goto error; } } @@ -830,7 +841,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) spin_unlock_irq(&epfile->ffs->eps_lock); if (unlikely(ret < 0)) { - /* nop */ + ret = -EIO; } else if (unlikely( wait_for_completion_interruptible(&done))) { spin_lock_irq(&epfile->ffs->eps_lock); @@ -885,6 +896,7 @@ ffs_epfile_open(struct inode *inode, struct file *file) file->private_data = epfile; ffs_data_opened(epfile->ffs); + atomic_set(&epfile->error, 0); return 0; } @@ -1000,6 +1012,7 @@ ffs_epfile_release(struct inode *inode, struct file *file) ENTER(); + atomic_set(&epfile->error, 1); ffs_data_closed(epfile->ffs); return 0; @@ -1633,6 +1646,7 @@ static void ffs_func_eps_disable(struct ffs_function *func) ++ep; if (epfile) { + atomic_set(&epfile->error, 1); epfile->ep = NULL; ++epfile; } |