diff options
Diffstat (limited to 'drivers/usb/gadget/function/f_fs.c')
| -rw-r--r-- | drivers/usb/gadget/function/f_fs.c | 40 | 
1 files changed, 25 insertions, 15 deletions
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 4585ee3a444a..e0fa4b186ec6 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -122,8 +122,6 @@ struct ffs_ep {  	struct usb_endpoint_descriptor	*descs[3];  	u8				num; - -	int				status;	/* P: epfile->mutex */  };  struct ffs_epfile { @@ -227,6 +225,9 @@ struct ffs_io_data {  	bool use_sg;  	struct ffs_data *ffs; + +	int status; +	struct completion done;  };  struct ffs_desc_helper { @@ -707,12 +708,15 @@ static const struct file_operations ffs_ep0_operations = {  static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)  { +	struct ffs_io_data *io_data = req->context; +  	ENTER(); -	if (req->context) { -		struct ffs_ep *ep = _ep->driver_data; -		ep->status = req->status ? req->status : req->actual; -		complete(req->context); -	} +	if (req->status) +		io_data->status = req->status; +	else +		io_data->status = req->actual; + +	complete(&io_data->done);  }  static ssize_t ffs_copy_to_iter(void *data, int data_len, struct iov_iter *iter) @@ -1050,7 +1054,6 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)  		WARN(1, "%s: data_len == -EINVAL\n", __func__);  		ret = -EINVAL;  	} else if (!io_data->aio) { -		DECLARE_COMPLETION_ONSTACK(done);  		bool interrupted = false;  		req = ep->req; @@ -1066,7 +1069,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)  		io_data->buf = data; -		req->context  = &done; +		init_completion(&io_data->done); +		req->context  = io_data;  		req->complete = ffs_epfile_io_complete;  		ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC); @@ -1075,7 +1079,12 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)  		spin_unlock_irq(&epfile->ffs->eps_lock); -		if (wait_for_completion_interruptible(&done)) { +		if (wait_for_completion_interruptible(&io_data->done)) { +			spin_lock_irq(&epfile->ffs->eps_lock); +			if (epfile->ep != ep) { +				ret = -ESHUTDOWN; +				goto error_lock; +			}  			/*  			 * To avoid race condition with ffs_epfile_io_complete,  			 * dequeue the request first then check @@ -1083,17 +1092,18 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)  			 * condition with req->complete callback.  			 */  			usb_ep_dequeue(ep->ep, req); -			wait_for_completion(&done); -			interrupted = ep->status < 0; +			spin_unlock_irq(&epfile->ffs->eps_lock); +			wait_for_completion(&io_data->done); +			interrupted = io_data->status < 0;  		}  		if (interrupted)  			ret = -EINTR; -		else if (io_data->read && ep->status > 0) -			ret = __ffs_epfile_read_data(epfile, data, ep->status, +		else if (io_data->read && io_data->status > 0) +			ret = __ffs_epfile_read_data(epfile, data, io_data->status,  						     &io_data->data);  		else -			ret = ep->status; +			ret = io_data->status;  		goto error_mutex;  	} else if (!(req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC))) {  		ret = -ENOMEM;  |