From: Greg Kroah-Hartman Date: Sat, 19 Nov 2016 08:58:33 +0000 (+0100) Subject: 4.8-stable patches X-Git-Tag: v4.4.34~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cc269c2f1d80635c0432a24015f9a6b1f634ec93;p=thirdparty%2Fkernel%2Fstable-queue.git 4.8-stable patches added patches: usb-gadget-f_fs-edit-epfile-ep-under-lock.patch usb-gadget-f_fs-stop-sleeping-in-ffs_func_eps_disable.patch --- diff --git a/queue-4.8/series b/queue-4.8/series index a604321e93d..7ba0658265d 100644 --- a/queue-4.8/series +++ b/queue-4.8/series @@ -45,3 +45,5 @@ sparc64-convert-ng2copy_-from-to-_user-to-accurate-exception-reporting.patch sparc64-convert-u3copy_-from-to-_user-to-accurate-exception-reporting.patch sparc64-delete-now-unused-user-copy-assembler-helpers.patch sparc64-delete-now-unused-user-copy-fixup-functions.patch +usb-gadget-f_fs-edit-epfile-ep-under-lock.patch +usb-gadget-f_fs-stop-sleeping-in-ffs_func_eps_disable.patch diff --git a/queue-4.8/usb-gadget-f_fs-edit-epfile-ep-under-lock.patch b/queue-4.8/usb-gadget-f_fs-edit-epfile-ep-under-lock.patch new file mode 100644 index 00000000000..5f33a38c988 --- /dev/null +++ b/queue-4.8/usb-gadget-f_fs-edit-epfile-ep-under-lock.patch @@ -0,0 +1,46 @@ +From 454915dde06a51133750c6745f0ba57361ba209d Mon Sep 17 00:00:00 2001 +From: Michal Nazarewicz +Date: Tue, 4 Oct 2016 02:07:33 +0200 +Subject: usb: gadget: f_fs: edit epfile->ep under lock + +From: Michal Nazarewicz + +commit 454915dde06a51133750c6745f0ba57361ba209d upstream. + +epfile->ep is protected by ffs->eps_lock (not epfile->mutex) so clear it +while holding the spin lock. + +Tested-by: John Stultz +Tested-by: Chen Yu +Signed-off-by: Michal Nazarewicz +Signed-off-by: Felipe Balbi +Signed-off-by: Greg Kroah-Hartman + + +--- + drivers/usb/gadget/function/f_fs.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/usb/gadget/function/f_fs.c ++++ b/drivers/usb/gadget/function/f_fs.c +@@ -1722,17 +1722,17 @@ static void ffs_func_eps_disable(struct + unsigned long flags; + + do { +- if (epfile) +- mutex_lock(&epfile->mutex); + 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) { +- epfile->ep = NULL; ++ mutex_lock(&epfile->mutex); + kfree(epfile->read_buffer); + epfile->read_buffer = NULL; + mutex_unlock(&epfile->mutex); diff --git a/queue-4.8/usb-gadget-f_fs-stop-sleeping-in-ffs_func_eps_disable.patch b/queue-4.8/usb-gadget-f_fs-stop-sleeping-in-ffs_func_eps_disable.patch new file mode 100644 index 00000000000..910263e64b0 --- /dev/null +++ b/queue-4.8/usb-gadget-f_fs-stop-sleeping-in-ffs_func_eps_disable.patch @@ -0,0 +1,200 @@ +From a9e6f83c2df199187a5248f824f31b6787ae23ae Mon Sep 17 00:00:00 2001 +From: Michal Nazarewicz +Date: Tue, 4 Oct 2016 02:07:34 +0200 +Subject: 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 + +From: Michal Nazarewicz + +commit a9e6f83c2df199187a5248f824f31b6787ae23ae upstream. + +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 +Signed-off-by: Michał Nazarewicz +Fixes: 9353afbbfa7b ("buffer data from ‘oversized’ OUT requests") +Tested-by: John Stultz +Tested-by: Chen Yu +Signed-off-by: Felipe Balbi +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/usb/gadget/function/f_fs.c | 109 +++++++++++++++++++++++++++++++------ + 1 file changed, 93 insertions(+), 16 deletions(-) + +--- a/drivers/usb/gadget/function/f_fs.c ++++ b/drivers/usb/gadget/function/f_fs.c +@@ -133,8 +133,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]; + +@@ -733,25 +785,47 @@ static void ffs_epfile_async_io_complete + 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; + } + +@@ -780,7 +854,15 @@ static ssize_t __ffs_epfile_read_data(st + 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; + } +@@ -1094,8 +1176,7 @@ ffs_epfile_release(struct inode *inode, + + ENTER(); + +- kfree(epfile->read_buffer); +- epfile->read_buffer = NULL; ++ __ffs_epfile_read_buffer_free(epfile); + ffs_data_closed(epfile->ffs); + + return 0; +@@ -1721,24 +1802,20 @@ static void ffs_func_eps_disable(struct + 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)