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>
This commit is contained in:
parent
8b5f119fba
commit
55a6f12e78
1 changed files with 18 additions and 4 deletions
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue