All of lore.kernel.org
 help / color / mirror / Atom feed
* FAILED: patch "[PATCH] usb: gadget: f_fs: stop sleeping in ffs_func_eps_disable" failed to apply to 4.8-stable tree
@ 2016-11-17  7:49 gregkh
  2016-11-18 21:44 ` Michal Nazarewicz
  0 siblings, 1 reply; 3+ messages in thread
From: gregkh @ 2016-11-17  7:49 UTC (permalink / raw)
  To: mina86, chenyu56, felipe.balbi, john.stultz; +Cc: stable


The patch below does not apply to the 4.8-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.

thanks,

greg k-h

------------------ original commit in Linus's tree ------------------

>From a9e6f83c2df199187a5248f824f31b6787ae23ae Mon Sep 17 00:00:00 2001
From: Michal Nazarewicz <mina86@mina86.com>
Date: Tue, 4 Oct 2016 02:07:34 +0200
Subject: [PATCH] usb: gadget: f_fs: stop sleeping in ffs_func_eps_disable
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

ffs_func_eps_disable is called from atomic context so it cannot sleep
thus cannot grab a mutex.  Change the handling of epfile->read_buffer
to use non-sleeping synchronisation method.

Reported-by: Chen Yu <chenyu56@huawei.com>
Signed-off-by: Michał Nazarewicz <mina86@mina86.com>
Fixes: 9353afbbfa7b ("buffer data from ‘oversized’ OUT requests")
Tested-by: John Stultz <john.stultz@linaro.org>
Tested-by: Chen Yu <chenyu56@huawei.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>

diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index b31aa9572723..e40d47d47d82 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -136,8 +136,60 @@ struct ffs_epfile {
 	/*
 	 * Buffer for holding data from partial reads which may happen since
 	 * we’re rounding user read requests to a multiple of a max packet size.
+	 *
+	 * The pointer is initialised with NULL value and may be set by
+	 * __ffs_epfile_read_data function to point to a temporary buffer.
+	 *
+	 * In normal operation, calls to __ffs_epfile_read_buffered will consume
+	 * data from said buffer and eventually free it.  Importantly, while the
+	 * function is using the buffer, it sets the pointer to NULL.  This is
+	 * all right since __ffs_epfile_read_data and __ffs_epfile_read_buffered
+	 * can never run concurrently (they are synchronised by epfile->mutex)
+	 * so the latter will not assign a new value to the pointer.
+	 *
+	 * Meanwhile ffs_func_eps_disable frees the buffer (if the pointer is
+	 * valid) and sets the pointer to READ_BUFFER_DROP value.  This special
+	 * value is crux of the synchronisation between ffs_func_eps_disable and
+	 * __ffs_epfile_read_data.
+	 *
+	 * Once __ffs_epfile_read_data is about to finish it will try to set the
+	 * pointer back to its old value (as described above), but seeing as the
+	 * pointer is not-NULL (namely READ_BUFFER_DROP) it will instead free
+	 * the buffer.
+	 *
+	 * == State transitions ==
+	 *
+	 * • ptr == NULL:  (initial state)
+	 *   ◦ __ffs_epfile_read_buffer_free: go to ptr == DROP
+	 *   ◦ __ffs_epfile_read_buffered:    nop
+	 *   ◦ __ffs_epfile_read_data allocates temp buffer: go to ptr == buf
+	 *   ◦ reading finishes:              n/a, not in ‘and reading’ state
+	 * • ptr == DROP:
+	 *   ◦ __ffs_epfile_read_buffer_free: nop
+	 *   ◦ __ffs_epfile_read_buffered:    go to ptr == NULL
+	 *   ◦ __ffs_epfile_read_data allocates temp buffer: free buf, nop
+	 *   ◦ reading finishes:              n/a, not in ‘and reading’ state
+	 * • ptr == buf:
+	 *   ◦ __ffs_epfile_read_buffer_free: free buf, go to ptr == DROP
+	 *   ◦ __ffs_epfile_read_buffered:    go to ptr == NULL and reading
+	 *   ◦ __ffs_epfile_read_data:        n/a, __ffs_epfile_read_buffered
+	 *                                    is always called first
+	 *   ◦ reading finishes:              n/a, not in ‘and reading’ state
+	 * • ptr == NULL and reading:
+	 *   ◦ __ffs_epfile_read_buffer_free: go to ptr == DROP and reading
+	 *   ◦ __ffs_epfile_read_buffered:    n/a, mutex is held
+	 *   ◦ __ffs_epfile_read_data:        n/a, mutex is held
+	 *   ◦ reading finishes and …
+	 *     … all data read:               free buf, go to ptr == NULL
+	 *     … otherwise:                   go to ptr == buf and reading
+	 * • ptr == DROP and reading:
+	 *   ◦ __ffs_epfile_read_buffer_free: nop
+	 *   ◦ __ffs_epfile_read_buffered:    n/a, mutex is held
+	 *   ◦ __ffs_epfile_read_data:        n/a, mutex is held
+	 *   ◦ reading finishes:              free buf, go to ptr == DROP
 	 */
-	struct ffs_buffer		*read_buffer;	/* P: epfile->mutex */
+	struct ffs_buffer		*read_buffer;
+#define READ_BUFFER_DROP ((struct ffs_buffer *)ERR_PTR(-ESHUTDOWN))
 
 	char				name[5];
 
@@ -736,25 +788,47 @@ static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
 	schedule_work(&io_data->work);
 }
 
+static void __ffs_epfile_read_buffer_free(struct ffs_epfile *epfile)
+{
+	/*
+	 * See comment in struct ffs_epfile for full read_buffer pointer
+	 * synchronisation story.
+	 */
+	struct ffs_buffer *buf = xchg(&epfile->read_buffer, READ_BUFFER_DROP);
+	if (buf && buf != READ_BUFFER_DROP)
+		kfree(buf);
+}
+
 /* Assumes epfile->mutex is held. */
 static ssize_t __ffs_epfile_read_buffered(struct ffs_epfile *epfile,
 					  struct iov_iter *iter)
 {
-	struct ffs_buffer *buf = epfile->read_buffer;
+	/*
+	 * Null out epfile->read_buffer so ffs_func_eps_disable does not free
+	 * the buffer while we are using it.  See comment in struct ffs_epfile
+	 * for full read_buffer pointer synchronisation story.
+	 */
+	struct ffs_buffer *buf = xchg(&epfile->read_buffer, NULL);
 	ssize_t ret;
-	if (!buf)
+	if (!buf || buf == READ_BUFFER_DROP)
 		return 0;
 
 	ret = copy_to_iter(buf->data, buf->length, iter);
 	if (buf->length == ret) {
 		kfree(buf);
-		epfile->read_buffer = NULL;
-	} else if (unlikely(iov_iter_count(iter))) {
+		return ret;
+	}
+
+	if (unlikely(iov_iter_count(iter))) {
 		ret = -EFAULT;
 	} else {
 		buf->length -= ret;
 		buf->data += ret;
 	}
+
+	if (cmpxchg(&epfile->read_buffer, NULL, buf))
+		kfree(buf);
+
 	return ret;
 }
 
@@ -783,7 +857,15 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile,
 	buf->length = data_len;
 	buf->data = buf->storage;
 	memcpy(buf->storage, data + ret, data_len);
-	epfile->read_buffer = buf;
+
+	/*
+	 * At this point read_buffer is NULL or READ_BUFFER_DROP (if
+	 * ffs_func_eps_disable has been called in the meanwhile).  See comment
+	 * in struct ffs_epfile for full read_buffer pointer synchronisation
+	 * story.
+	 */
+	if (unlikely(cmpxchg(&epfile->read_buffer, NULL, buf)))
+		kfree(buf);
 
 	return ret;
 }
@@ -1097,8 +1179,7 @@ ffs_epfile_release(struct inode *inode, struct file *file)
 
 	ENTER();
 
-	kfree(epfile->read_buffer);
-	epfile->read_buffer = NULL;
+	__ffs_epfile_read_buffer_free(epfile);
 	ffs_data_closed(epfile->ffs);
 
 	return 0;
@@ -1724,24 +1805,20 @@ static void ffs_func_eps_disable(struct ffs_function *func)
 	unsigned count            = func->ffs->eps_count;
 	unsigned long flags;
 
+	spin_lock_irqsave(&func->ffs->eps_lock, flags);
 	do {
-		spin_lock_irqsave(&func->ffs->eps_lock, flags);
 		/* pending requests get nuked */
 		if (likely(ep->ep))
 			usb_ep_disable(ep->ep);
 		++ep;
-		if (epfile)
-			epfile->ep = NULL;
-		spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
 
 		if (epfile) {
-			mutex_lock(&epfile->mutex);
-			kfree(epfile->read_buffer);
-			epfile->read_buffer = NULL;
-			mutex_unlock(&epfile->mutex);
+			epfile->ep = NULL;
+			__ffs_epfile_read_buffer_free(epfile);
 			++epfile;
 		}
 	} while (--count);
+	spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
 }
 
 static int ffs_func_eps_enable(struct ffs_function *func)


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: FAILED: patch "[PATCH] usb: gadget: f_fs: stop sleeping in ffs_func_eps_disable" failed to apply to 4.8-stable tree
  2016-11-17  7:49 FAILED: patch "[PATCH] usb: gadget: f_fs: stop sleeping in ffs_func_eps_disable" failed to apply to 4.8-stable tree gregkh
@ 2016-11-18 21:44 ` Michal Nazarewicz
  2016-11-19  8:55   ` Greg KH
  0 siblings, 1 reply; 3+ messages in thread
From: Michal Nazarewicz @ 2016-11-18 21:44 UTC (permalink / raw)
  To: gregkh, chenyu56, felipe.balbi, john.stultz; +Cc: stable

On Thu, Nov 17 2016, gregkh wrote:
> The patch below does not apply to the 4.8-stable tree.
> If someone wants it applied there, or to any other stable or longterm
> tree, then please email the backport, including the original git commit
> id to <stable@vger.kernel.org>.

Uh?  Works with no issues for me:

$ cd ~/code/linux
$ git checkout v4.8.9
$ git cherry-pick 454915d
[detached HEAD 24f34df] usb: gadget: f_fs: edit epfile->ep under lock
 Date: Tue Oct 4 02:07:33 2016 +0200
 1 file changed, 3 insertions(+), 3 deletions(-)
$ git cherry-pick a9e6f83
[detached HEAD e575f39] usb: gadget: f_fs: stop sleeping in ffs_func_eps_disable
 Date: Tue Oct 4 02:07:34 2016 +0200
 1 file changed, 93 insertions(+), 16 deletions(-)

I can send those in emails if you want.

> ------------------ original commit in Linus's tree ------------------
>
> From a9e6f83c2df199187a5248f824f31b6787ae23ae Mon Sep 17 00:00:00 2001
> From: Michal Nazarewicz <mina86@mina86.com>
> Date: Tue, 4 Oct 2016 02:07:34 +0200
> Subject: [PATCH] usb: gadget: f_fs: stop sleeping in ffs_func_eps_disable
> MIME-Version: 1.0
> Content-Type: text/plain; charset=UTF-8
> Content-Transfer-Encoding: 8bit
>
> ffs_func_eps_disable is called from atomic context so it cannot sleep
> thus cannot grab a mutex.  Change the handling of epfile->read_buffer
> to use non-sleeping synchronisation method.
>
> Reported-by: Chen Yu <chenyu56@huawei.com>
> Signed-off-by: Michał Nazarewicz <mina86@mina86.com>
> Fixes: 9353afbbfa7b ("buffer data from ‘oversized’ OUT requests")
> Tested-by: John Stultz <john.stultz@linaro.org>
> Tested-by: Chen Yu <chenyu56@huawei.com>
> Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
>
> diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
> index b31aa9572723..e40d47d47d82 100644
> --- a/drivers/usb/gadget/function/f_fs.c
> +++ b/drivers/usb/gadget/function/f_fs.c
> @@ -136,8 +136,60 @@ struct ffs_epfile {
>  	/*
>  	 * Buffer for holding data from partial reads which may happen since
>  	 * we’re rounding user read requests to a multiple of a max packet size.
> +	 *
> +	 * The pointer is initialised with NULL value and may be set by
> +	 * __ffs_epfile_read_data function to point to a temporary buffer.
> +	 *
> +	 * In normal operation, calls to __ffs_epfile_read_buffered will consume
> +	 * data from said buffer and eventually free it.  Importantly, while the
> +	 * function is using the buffer, it sets the pointer to NULL.  This is
> +	 * all right since __ffs_epfile_read_data and __ffs_epfile_read_buffered
> +	 * can never run concurrently (they are synchronised by epfile->mutex)
> +	 * so the latter will not assign a new value to the pointer.
> +	 *
> +	 * Meanwhile ffs_func_eps_disable frees the buffer (if the pointer is
> +	 * valid) and sets the pointer to READ_BUFFER_DROP value.  This special
> +	 * value is crux of the synchronisation between ffs_func_eps_disable and
> +	 * __ffs_epfile_read_data.
> +	 *
> +	 * Once __ffs_epfile_read_data is about to finish it will try to set the
> +	 * pointer back to its old value (as described above), but seeing as the
> +	 * pointer is not-NULL (namely READ_BUFFER_DROP) it will instead free
> +	 * the buffer.
> +	 *
> +	 * == State transitions ==
> +	 *
> +	 * • ptr == NULL:  (initial state)
> +	 *   ◦ __ffs_epfile_read_buffer_free: go to ptr == DROP
> +	 *   ◦ __ffs_epfile_read_buffered:    nop
> +	 *   ◦ __ffs_epfile_read_data allocates temp buffer: go to ptr == buf
> +	 *   ◦ reading finishes:              n/a, not in ‘and reading’ state
> +	 * • ptr == DROP:
> +	 *   ◦ __ffs_epfile_read_buffer_free: nop
> +	 *   ◦ __ffs_epfile_read_buffered:    go to ptr == NULL
> +	 *   ◦ __ffs_epfile_read_data allocates temp buffer: free buf, nop
> +	 *   ◦ reading finishes:              n/a, not in ‘and reading’ state
> +	 * • ptr == buf:
> +	 *   ◦ __ffs_epfile_read_buffer_free: free buf, go to ptr == DROP
> +	 *   ◦ __ffs_epfile_read_buffered:    go to ptr == NULL and reading
> +	 *   ◦ __ffs_epfile_read_data:        n/a, __ffs_epfile_read_buffered
> +	 *                                    is always called first
> +	 *   ◦ reading finishes:              n/a, not in ‘and reading’ state
> +	 * • ptr == NULL and reading:
> +	 *   ◦ __ffs_epfile_read_buffer_free: go to ptr == DROP and reading
> +	 *   ◦ __ffs_epfile_read_buffered:    n/a, mutex is held
> +	 *   ◦ __ffs_epfile_read_data:        n/a, mutex is held
> +	 *   ◦ reading finishes and …
> +	 *     … all data read:               free buf, go to ptr == NULL
> +	 *     … otherwise:                   go to ptr == buf and reading
> +	 * • ptr == DROP and reading:
> +	 *   ◦ __ffs_epfile_read_buffer_free: nop
> +	 *   ◦ __ffs_epfile_read_buffered:    n/a, mutex is held
> +	 *   ◦ __ffs_epfile_read_data:        n/a, mutex is held
> +	 *   ◦ reading finishes:              free buf, go to ptr == DROP
>  	 */
> -	struct ffs_buffer		*read_buffer;	/* P: epfile->mutex */
> +	struct ffs_buffer		*read_buffer;
> +#define READ_BUFFER_DROP ((struct ffs_buffer *)ERR_PTR(-ESHUTDOWN))
>  
>  	char				name[5];
>  
> @@ -736,25 +788,47 @@ static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
>  	schedule_work(&io_data->work);
>  }
>  
> +static void __ffs_epfile_read_buffer_free(struct ffs_epfile *epfile)
> +{
> +	/*
> +	 * See comment in struct ffs_epfile for full read_buffer pointer
> +	 * synchronisation story.
> +	 */
> +	struct ffs_buffer *buf = xchg(&epfile->read_buffer, READ_BUFFER_DROP);
> +	if (buf && buf != READ_BUFFER_DROP)
> +		kfree(buf);
> +}
> +
>  /* Assumes epfile->mutex is held. */
>  static ssize_t __ffs_epfile_read_buffered(struct ffs_epfile *epfile,
>  					  struct iov_iter *iter)
>  {
> -	struct ffs_buffer *buf = epfile->read_buffer;
> +	/*
> +	 * Null out epfile->read_buffer so ffs_func_eps_disable does not free
> +	 * the buffer while we are using it.  See comment in struct ffs_epfile
> +	 * for full read_buffer pointer synchronisation story.
> +	 */
> +	struct ffs_buffer *buf = xchg(&epfile->read_buffer, NULL);
>  	ssize_t ret;
> -	if (!buf)
> +	if (!buf || buf == READ_BUFFER_DROP)
>  		return 0;
>  
>  	ret = copy_to_iter(buf->data, buf->length, iter);
>  	if (buf->length == ret) {
>  		kfree(buf);
> -		epfile->read_buffer = NULL;
> -	} else if (unlikely(iov_iter_count(iter))) {
> +		return ret;
> +	}
> +
> +	if (unlikely(iov_iter_count(iter))) {
>  		ret = -EFAULT;
>  	} else {
>  		buf->length -= ret;
>  		buf->data += ret;
>  	}
> +
> +	if (cmpxchg(&epfile->read_buffer, NULL, buf))
> +		kfree(buf);
> +
>  	return ret;
>  }
>  
> @@ -783,7 +857,15 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile,
>  	buf->length = data_len;
>  	buf->data = buf->storage;
>  	memcpy(buf->storage, data + ret, data_len);
> -	epfile->read_buffer = buf;
> +
> +	/*
> +	 * At this point read_buffer is NULL or READ_BUFFER_DROP (if
> +	 * ffs_func_eps_disable has been called in the meanwhile).  See comment
> +	 * in struct ffs_epfile for full read_buffer pointer synchronisation
> +	 * story.
> +	 */
> +	if (unlikely(cmpxchg(&epfile->read_buffer, NULL, buf)))
> +		kfree(buf);
>  
>  	return ret;
>  }
> @@ -1097,8 +1179,7 @@ ffs_epfile_release(struct inode *inode, struct file *file)
>  
>  	ENTER();
>  
> -	kfree(epfile->read_buffer);
> -	epfile->read_buffer = NULL;
> +	__ffs_epfile_read_buffer_free(epfile);
>  	ffs_data_closed(epfile->ffs);
>  
>  	return 0;
> @@ -1724,24 +1805,20 @@ static void ffs_func_eps_disable(struct ffs_function *func)
>  	unsigned count            = func->ffs->eps_count;
>  	unsigned long flags;
>  
> +	spin_lock_irqsave(&func->ffs->eps_lock, flags);
>  	do {
> -		spin_lock_irqsave(&func->ffs->eps_lock, flags);
>  		/* pending requests get nuked */
>  		if (likely(ep->ep))
>  			usb_ep_disable(ep->ep);
>  		++ep;
> -		if (epfile)
> -			epfile->ep = NULL;
> -		spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
>  
>  		if (epfile) {
> -			mutex_lock(&epfile->mutex);
> -			kfree(epfile->read_buffer);
> -			epfile->read_buffer = NULL;
> -			mutex_unlock(&epfile->mutex);
> +			epfile->ep = NULL;
> +			__ffs_epfile_read_buffer_free(epfile);
>  			++epfile;
>  		}
>  	} while (--count);
> +	spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
>  }
>  
>  static int ffs_func_eps_enable(struct ffs_function *func)
>

-- 
Best regards
ミハウ “𝓶𝓲𝓷𝓪86” ナザレヴイツ
«If at first you don’t succeed, give up skydiving»

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: FAILED: patch "[PATCH] usb: gadget: f_fs: stop sleeping in ffs_func_eps_disable" failed to apply to 4.8-stable tree
  2016-11-18 21:44 ` Michal Nazarewicz
@ 2016-11-19  8:55   ` Greg KH
  0 siblings, 0 replies; 3+ messages in thread
From: Greg KH @ 2016-11-19  8:55 UTC (permalink / raw)
  To: Michal Nazarewicz; +Cc: chenyu56, felipe.balbi, john.stultz, stable

On Fri, Nov 18, 2016 at 10:44:07PM +0100, Michal Nazarewicz wrote:
> On Thu, Nov 17 2016, gregkh wrote:
> > The patch below does not apply to the 4.8-stable tree.
> > If someone wants it applied there, or to any other stable or longterm
> > tree, then please email the backport, including the original git commit
> > id to <stable@vger.kernel.org>.
> 
> Uh?  Works with no issues for me:
> 
> $ cd ~/code/linux
> $ git checkout v4.8.9
> $ git cherry-pick 454915d
> [detached HEAD 24f34df] usb: gadget: f_fs: edit epfile->ep under lock
>  Date: Tue Oct 4 02:07:33 2016 +0200
>  1 file changed, 3 insertions(+), 3 deletions(-)
> $ git cherry-pick a9e6f83
> [detached HEAD e575f39] usb: gadget: f_fs: stop sleeping in ffs_func_eps_disable
>  Date: Tue Oct 4 02:07:34 2016 +0200
>  1 file changed, 93 insertions(+), 16 deletions(-)
> 
> I can send those in emails if you want.

Ok, cherry-pick is smarter than quilt/patch is at times, like now.  I've
done this and all should be good, thanks for letting me know.

gre gk-h

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2016-11-19  8:55 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-11-17  7:49 FAILED: patch "[PATCH] usb: gadget: f_fs: stop sleeping in ffs_func_eps_disable" failed to apply to 4.8-stable tree gregkh
2016-11-18 21:44 ` Michal Nazarewicz
2016-11-19  8:55   ` Greg KH

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.