]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 5.15
authorSasha Levin <sashal@kernel.org>
Mon, 19 Feb 2024 20:58:53 +0000 (15:58 -0500)
committerSasha Levin <sashal@kernel.org>
Mon, 19 Feb 2024 20:58:53 +0000 (15:58 -0500)
Signed-off-by: Sasha Levin <sashal@kernel.org>
32 files changed:
queue-5.15/bus-moxtet-add-spi-device-table.patch [new file with mode: 0644]
queue-5.15/crypto-lib-mpi-fix-unexpected-pointer-access-in-mpi_.patch [new file with mode: 0644]
queue-5.15/dma-buf-add-dma_fence_timestamp-helper.patch [new file with mode: 0644]
queue-5.15/fbdev-defio-early-out-if-page-is-already-enlisted.patch [new file with mode: 0644]
queue-5.15/fbdev-defio-fix-the-pagelist-corruption.patch [new file with mode: 0644]
queue-5.15/fbdev-don-t-sort-deferred-i-o-pages-by-default.patch [new file with mode: 0644]
queue-5.15/fbdev-fix-incorrect-page-mapping-clearance-at-fb_def.patch [new file with mode: 0644]
queue-5.15/fbdev-fix-invalid-page-access-after-closing-deferred.patch [new file with mode: 0644]
queue-5.15/fbdev-flush-deferred-io-before-closing.patch [new file with mode: 0644]
queue-5.15/fbdev-rename-pagelist-to-pagereflist-for-deferred-i-.patch [new file with mode: 0644]
queue-5.15/fbdev-track-deferred-i-o-pages-in-pageref-struct.patch [new file with mode: 0644]
queue-5.15/mwifiex-select-firmware-based-on-strapping.patch [new file with mode: 0644]
queue-5.15/scripts-decode_stacktrace-demangle-rust-symbols.patch [new file with mode: 0644]
queue-5.15/scripts-decode_stacktrace.sh-optionally-use-llvm-uti.patch [new file with mode: 0644]
queue-5.15/scripts-decode_stacktrace.sh-support-old-bash-versio.patch [new file with mode: 0644]
queue-5.15/serial-8250_exar-fill-in-rs485_supported.patch [new file with mode: 0644]
queue-5.15/serial-8250_exar-set-missing-rs485_supported-flag.patch [new file with mode: 0644]
queue-5.15/series
queue-5.15/usb-dwc3-ep0-don-t-prepare-beyond-setup-stage.patch [new file with mode: 0644]
queue-5.15/usb-dwc3-fix-ep0-handling-when-getting-reset-while-d.patch [new file with mode: 0644]
queue-5.15/usb-dwc3-gadget-delay-issuing-end-transfer.patch [new file with mode: 0644]
queue-5.15/usb-dwc3-gadget-force-sending-delayed-status-during-.patch [new file with mode: 0644]
queue-5.15/usb-dwc3-gadget-handle-ep0-request-dequeuing-properl.patch [new file with mode: 0644]
queue-5.15/usb-dwc3-gadget-only-end-transfer-for-ep0-data-phase.patch [new file with mode: 0644]
queue-5.15/usb-dwc3-gadget-queue-pm-runtime-idle-on-disconnect-.patch [new file with mode: 0644]
queue-5.15/usb-dwc3-gadget-refactor-ep0-forced-stall-restart-in.patch [new file with mode: 0644]
queue-5.15/usb-dwc3-gadget-stall-and-restart-ep0-if-host-is-unr.patch [new file with mode: 0644]
queue-5.15/usb-dwc3-gadget-submit-endxfer-command-if-delayed-du.patch [new file with mode: 0644]
queue-5.15/usb-dwc3-gadget-wait-for-ep0-xfers-to-complete-durin.patch [new file with mode: 0644]
queue-5.15/wifi-mwifiex-add-extra-delay-for-firmware-ready.patch [new file with mode: 0644]
queue-5.15/wifi-mwifiex-fix-uninitialized-firmware_stat.patch [new file with mode: 0644]
queue-5.15/wifi-mwifiex-support-sd8978-chipset.patch [new file with mode: 0644]

diff --git a/queue-5.15/bus-moxtet-add-spi-device-table.patch b/queue-5.15/bus-moxtet-add-spi-device-table.patch
new file mode 100644 (file)
index 0000000..5c6fc59
--- /dev/null
@@ -0,0 +1,52 @@
+From 8e9cea52c07682a98800a53b4e5afa93e8289f70 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Nov 2023 22:35:05 +0100
+Subject: bus: moxtet: Add spi device table
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Sjoerd Simons <sjoerd@collabora.com>
+
+[ Upstream commit aaafe88d5500ba18b33be72458439367ef878788 ]
+
+The moxtet module fails to auto-load on. Add a SPI id table to
+allow it to do so.
+
+Signed-off-by: Sjoerd Simons <sjoerd@collabora.com>
+Cc:  <stable@vger.kernel.org>
+Reviewed-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/bus/moxtet.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/bus/moxtet.c b/drivers/bus/moxtet.c
+index fd87a59837fa..fbf0818933be 100644
+--- a/drivers/bus/moxtet.c
++++ b/drivers/bus/moxtet.c
+@@ -832,6 +832,12 @@ static int moxtet_remove(struct spi_device *spi)
+       return 0;
+ }
++static const struct spi_device_id moxtet_spi_ids[] = {
++      { "moxtet" },
++      { },
++};
++MODULE_DEVICE_TABLE(spi, moxtet_spi_ids);
++
+ static const struct of_device_id moxtet_dt_ids[] = {
+       { .compatible = "cznic,moxtet" },
+       {},
+@@ -843,6 +849,7 @@ static struct spi_driver moxtet_spi_driver = {
+               .name           = "moxtet",
+               .of_match_table = moxtet_dt_ids,
+       },
++      .id_table       = moxtet_spi_ids,
+       .probe          = moxtet_probe,
+       .remove         = moxtet_remove,
+ };
+-- 
+2.43.0
+
diff --git a/queue-5.15/crypto-lib-mpi-fix-unexpected-pointer-access-in-mpi_.patch b/queue-5.15/crypto-lib-mpi-fix-unexpected-pointer-access-in-mpi_.patch
new file mode 100644 (file)
index 0000000..0b80b38
--- /dev/null
@@ -0,0 +1,42 @@
+From febe21946d85586d4f9af1925ed41627a6d099dd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 Dec 2023 11:08:34 +0800
+Subject: crypto: lib/mpi - Fix unexpected pointer access in mpi_ec_init
+
+From: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
+
+[ Upstream commit ba3c5574203034781ac4231acf117da917efcd2a ]
+
+When the mpi_ec_ctx structure is initialized, some fields are not
+cleared, causing a crash when referencing the field when the
+structure was released. Initially, this issue was ignored because
+memory for mpi_ec_ctx is allocated with the __GFP_ZERO flag.
+For example, this error will be triggered when calculating the
+Za value for SM2 separately.
+
+Fixes: d58bb7e55a8a ("lib/mpi: Introduce ec implementation to MPI library")
+Cc: stable@vger.kernel.org # v6.5
+Signed-off-by: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ lib/mpi/ec.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/lib/mpi/ec.c b/lib/mpi/ec.c
+index 40f5908e57a4..e16dca1e23d5 100644
+--- a/lib/mpi/ec.c
++++ b/lib/mpi/ec.c
+@@ -584,6 +584,9 @@ void mpi_ec_init(struct mpi_ec_ctx *ctx, enum gcry_mpi_ec_models model,
+       ctx->a = mpi_copy(a);
+       ctx->b = mpi_copy(b);
++      ctx->d = NULL;
++      ctx->t.two_inv_p = NULL;
++
+       ctx->t.p_barrett = use_barrett > 0 ? mpi_barrett_init(ctx->p, 0) : NULL;
+       mpi_ec_get_reset(ctx);
+-- 
+2.43.0
+
diff --git a/queue-5.15/dma-buf-add-dma_fence_timestamp-helper.patch b/queue-5.15/dma-buf-add-dma_fence_timestamp-helper.patch
new file mode 100644 (file)
index 0000000..eeb20be
--- /dev/null
@@ -0,0 +1,292 @@
+From e87be7a3eccf156225b7d942d0d66503cdfb6450 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 Sep 2023 10:27:23 +0200
+Subject: dma-buf: add dma_fence_timestamp helper
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Christian König <christian.koenig@amd.com>
+
+[ Upstream commit b83ce9cb4a465b8f9a3fa45561b721a9551f60e3 ]
+
+When a fence signals there is a very small race window where the timestamp
+isn't updated yet. sync_file solves this by busy waiting for the
+timestamp to appear, but on other ocassions didn't handled this
+correctly.
+
+Provide a dma_fence_timestamp() helper function for this and use it in
+all appropriate cases.
+
+Another alternative would be to grab the spinlock when that happens.
+
+v2 by teddy: add a wait parameter to wait for the timestamp to show up, in case
+   the accurate timestamp is needed and/or the timestamp is not based on
+   ktime (e.g. hw timestamp)
+v3 chk: drop the parameter again for unified handling
+
+Signed-off-by: Yunxiang Li <Yunxiang.Li@amd.com>
+Signed-off-by: Christian König <christian.koenig@amd.com>
+Fixes: 1774baa64f93 ("drm/scheduler: Change scheduled fence track v2")
+Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
+CC: stable@vger.kernel.org
+Link: https://patchwork.freedesktop.org/patch/msgid/20230929104725.2358-1-christian.koenig@amd.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma-buf/dma-fence-unwrap.c     | 176 +++++++++++++++++++++++++
+ drivers/dma-buf/sync_file.c            |   9 +-
+ drivers/gpu/drm/scheduler/sched_main.c |   3 +-
+ include/linux/dma-fence.h              |  19 +++
+ 4 files changed, 199 insertions(+), 8 deletions(-)
+ create mode 100644 drivers/dma-buf/dma-fence-unwrap.c
+
+diff --git a/drivers/dma-buf/dma-fence-unwrap.c b/drivers/dma-buf/dma-fence-unwrap.c
+new file mode 100644
+index 000000000000..628af51c81af
+--- /dev/null
++++ b/drivers/dma-buf/dma-fence-unwrap.c
+@@ -0,0 +1,176 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * dma-fence-util: misc functions for dma_fence objects
++ *
++ * Copyright (C) 2022 Advanced Micro Devices, Inc.
++ * Authors:
++ *    Christian König <christian.koenig@amd.com>
++ */
++
++#include <linux/dma-fence.h>
++#include <linux/dma-fence-array.h>
++#include <linux/dma-fence-chain.h>
++#include <linux/dma-fence-unwrap.h>
++#include <linux/slab.h>
++
++/* Internal helper to start new array iteration, don't use directly */
++static struct dma_fence *
++__dma_fence_unwrap_array(struct dma_fence_unwrap *cursor)
++{
++      cursor->array = dma_fence_chain_contained(cursor->chain);
++      cursor->index = 0;
++      return dma_fence_array_first(cursor->array);
++}
++
++/**
++ * dma_fence_unwrap_first - return the first fence from fence containers
++ * @head: the entrypoint into the containers
++ * @cursor: current position inside the containers
++ *
++ * Unwraps potential dma_fence_chain/dma_fence_array containers and return the
++ * first fence.
++ */
++struct dma_fence *dma_fence_unwrap_first(struct dma_fence *head,
++                                       struct dma_fence_unwrap *cursor)
++{
++      cursor->chain = dma_fence_get(head);
++      return __dma_fence_unwrap_array(cursor);
++}
++EXPORT_SYMBOL_GPL(dma_fence_unwrap_first);
++
++/**
++ * dma_fence_unwrap_next - return the next fence from a fence containers
++ * @cursor: current position inside the containers
++ *
++ * Continue unwrapping the dma_fence_chain/dma_fence_array containers and return
++ * the next fence from them.
++ */
++struct dma_fence *dma_fence_unwrap_next(struct dma_fence_unwrap *cursor)
++{
++      struct dma_fence *tmp;
++
++      ++cursor->index;
++      tmp = dma_fence_array_next(cursor->array, cursor->index);
++      if (tmp)
++              return tmp;
++
++      cursor->chain = dma_fence_chain_walk(cursor->chain);
++      return __dma_fence_unwrap_array(cursor);
++}
++EXPORT_SYMBOL_GPL(dma_fence_unwrap_next);
++
++/* Implementation for the dma_fence_merge() marco, don't use directly */
++struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
++                                         struct dma_fence **fences,
++                                         struct dma_fence_unwrap *iter)
++{
++      struct dma_fence_array *result;
++      struct dma_fence *tmp, **array;
++      ktime_t timestamp;
++      unsigned int i;
++      size_t count;
++
++      count = 0;
++      timestamp = ns_to_ktime(0);
++      for (i = 0; i < num_fences; ++i) {
++              dma_fence_unwrap_for_each(tmp, &iter[i], fences[i]) {
++                      if (!dma_fence_is_signaled(tmp)) {
++                              ++count;
++                      } else {
++                              ktime_t t = dma_fence_timestamp(tmp);
++
++                              if (ktime_after(t, timestamp))
++                                      timestamp = t;
++                      }
++              }
++      }
++
++      /*
++       * If we couldn't find a pending fence just return a private signaled
++       * fence with the timestamp of the last signaled one.
++       */
++      if (count == 0)
++              return dma_fence_allocate_private_stub(timestamp);
++
++      array = kmalloc_array(count, sizeof(*array), GFP_KERNEL);
++      if (!array)
++              return NULL;
++
++      /*
++       * This trashes the input fence array and uses it as position for the
++       * following merge loop. This works because the dma_fence_merge()
++       * wrapper macro is creating this temporary array on the stack together
++       * with the iterators.
++       */
++      for (i = 0; i < num_fences; ++i)
++              fences[i] = dma_fence_unwrap_first(fences[i], &iter[i]);
++
++      count = 0;
++      do {
++              unsigned int sel;
++
++restart:
++              tmp = NULL;
++              for (i = 0; i < num_fences; ++i) {
++                      struct dma_fence *next;
++
++                      while (fences[i] && dma_fence_is_signaled(fences[i]))
++                              fences[i] = dma_fence_unwrap_next(&iter[i]);
++
++                      next = fences[i];
++                      if (!next)
++                              continue;
++
++                      /*
++                       * We can't guarantee that inpute fences are ordered by
++                       * context, but it is still quite likely when this
++                       * function is used multiple times. So attempt to order
++                       * the fences by context as we pass over them and merge
++                       * fences with the same context.
++                       */
++                      if (!tmp || tmp->context > next->context) {
++                              tmp = next;
++                              sel = i;
++
++                      } else if (tmp->context < next->context) {
++                              continue;
++
++                      } else if (dma_fence_is_later(tmp, next)) {
++                              fences[i] = dma_fence_unwrap_next(&iter[i]);
++                              goto restart;
++                      } else {
++                              fences[sel] = dma_fence_unwrap_next(&iter[sel]);
++                              goto restart;
++                      }
++              }
++
++              if (tmp) {
++                      array[count++] = dma_fence_get(tmp);
++                      fences[sel] = dma_fence_unwrap_next(&iter[sel]);
++              }
++      } while (tmp);
++
++      if (count == 0) {
++              tmp = dma_fence_allocate_private_stub(ktime_get());
++              goto return_tmp;
++      }
++
++      if (count == 1) {
++              tmp = array[0];
++              goto return_tmp;
++      }
++
++      result = dma_fence_array_create(count, array,
++                                      dma_fence_context_alloc(1),
++                                      1, false);
++      if (!result) {
++              tmp = NULL;
++              goto return_tmp;
++      }
++      return &result->base;
++
++return_tmp:
++      kfree(array);
++      return tmp;
++}
++EXPORT_SYMBOL_GPL(__dma_fence_unwrap_merge);
+diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c
+index 394e6e1e9686..875ae4b3b047 100644
+--- a/drivers/dma-buf/sync_file.c
++++ b/drivers/dma-buf/sync_file.c
+@@ -384,13 +384,10 @@ static int sync_fill_fence_info(struct dma_fence *fence,
+               sizeof(info->driver_name));
+       info->status = dma_fence_get_status(fence);
+-      while (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags) &&
+-             !test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags))
+-              cpu_relax();
+       info->timestamp_ns =
+-              test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags) ?
+-              ktime_to_ns(fence->timestamp) :
+-              ktime_set(0, 0);
++              dma_fence_is_signaled(fence) ?
++                      ktime_to_ns(dma_fence_timestamp(fence)) :
++                      ktime_set(0, 0);
+       return info->status;
+ }
+diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c
+index 67382621b429..e827e8a83c4e 100644
+--- a/drivers/gpu/drm/scheduler/sched_main.c
++++ b/drivers/gpu/drm/scheduler/sched_main.c
+@@ -698,8 +698,7 @@ drm_sched_get_cleanup_job(struct drm_gpu_scheduler *sched)
+                                               typeof(*next), list);
+               if (next)
+                       next->s_fence->scheduled.timestamp =
+-                              job->s_fence->finished.timestamp;
+-
++                              dma_fence_timestamp(&job->s_fence->finished);
+       } else {
+               job = NULL;
+               /* queue timeout for next job */
+diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h
+index 6ffb4b2c6371..9d276655cc25 100644
+--- a/include/linux/dma-fence.h
++++ b/include/linux/dma-fence.h
+@@ -551,6 +551,25 @@ static inline void dma_fence_set_error(struct dma_fence *fence,
+       fence->error = error;
+ }
++/**
++ * dma_fence_timestamp - helper to get the completion timestamp of a fence
++ * @fence: fence to get the timestamp from.
++ *
++ * After a fence is signaled the timestamp is updated with the signaling time,
++ * but setting the timestamp can race with tasks waiting for the signaling. This
++ * helper busy waits for the correct timestamp to appear.
++ */
++static inline ktime_t dma_fence_timestamp(struct dma_fence *fence)
++{
++      if (WARN_ON(!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)))
++              return ktime_get();
++
++      while (!test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags))
++              cpu_relax();
++
++      return fence->timestamp;
++}
++
+ signed long dma_fence_wait_timeout(struct dma_fence *,
+                                  bool intr, signed long timeout);
+ signed long dma_fence_wait_any_timeout(struct dma_fence **fences,
+-- 
+2.43.0
+
diff --git a/queue-5.15/fbdev-defio-early-out-if-page-is-already-enlisted.patch b/queue-5.15/fbdev-defio-early-out-if-page-is-already-enlisted.patch
new file mode 100644 (file)
index 0000000..afe2a2e
--- /dev/null
@@ -0,0 +1,83 @@
+From 8a33b0569f9410057e7bab59752b8b3714bda753 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 11 Feb 2022 10:46:39 +0100
+Subject: fbdev/defio: Early-out if page is already enlisted
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit 105a940416fc622406653b6fe54732897642dfbc ]
+
+Return early if a page is already in the list of dirty pages for
+deferred I/O. This can be detected if the page's list head is not
+empty. Keep the list head initialized while the page is not enlisted
+to make this work reliably.
+
+v2:
+       * update comment and fix spelling (Sam)
+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Acked-by: Sam Ravnborg <sam@ravnborg.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20220211094640.21632-2-tzimmermann@suse.de
+Stable-dep-of: 33cd6ea9c067 ("fbdev: flush deferred IO before closing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/video/fbdev/core/fb_defio.c | 26 +++++++++++++++++---------
+ 1 file changed, 17 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
+index 0708e214c5a3..95264a621221 100644
+--- a/drivers/video/fbdev/core/fb_defio.c
++++ b/drivers/video/fbdev/core/fb_defio.c
+@@ -59,6 +59,7 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
+               printk(KERN_ERR "no mapping available\n");
+       BUG_ON(!page->mapping);
++      INIT_LIST_HEAD(&page->lru);
+       page->index = vmf->pgoff;
+       vmf->page = page;
+@@ -118,17 +119,24 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+        */
+       lock_page(page);
++      /*
++       * This check is to catch the case where a new process could start
++       * writing to the same page through a new PTE. This new access
++       * can cause a call to .page_mkwrite even if the original process'
++       * PTE is marked writable.
++       *
++       * TODO: The lru field is owned by the page cache; hence the name.
++       *       We dequeue in fb_deferred_io_work() after flushing the
++       *       page's content into video memory. Instead of lru, fbdefio
++       *       should have it's own field.
++       */
++      if (!list_empty(&page->lru))
++              goto page_already_added;
++
+       /* we loop through the pagelist before adding in order
+       to keep the pagelist sorted */
+       list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+-              /* this check is to catch the case where a new
+-              process could start writing to the same page
+-              through a new pte. this new access can cause the
+-              mkwrite even when the original ps's pte is marked
+-              writable */
+-              if (unlikely(cur == page))
+-                      goto page_already_added;
+-              else if (cur->index > page->index)
++              if (cur->index > page->index)
+                       break;
+       }
+@@ -190,7 +198,7 @@ static void fb_deferred_io_work(struct work_struct *work)
+       /* clear the list */
+       list_for_each_safe(node, next, &fbdefio->pagelist) {
+-              list_del(node);
++              list_del_init(node);
+       }
+       mutex_unlock(&fbdefio->lock);
+ }
+-- 
+2.43.0
+
diff --git a/queue-5.15/fbdev-defio-fix-the-pagelist-corruption.patch b/queue-5.15/fbdev-defio-fix-the-pagelist-corruption.patch
new file mode 100644 (file)
index 0000000..02b1a20
--- /dev/null
@@ -0,0 +1,93 @@
+From d74bedcc06ac6927a4c678e4bc57cf6862734297 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 18 Mar 2022 08:50:03 +0800
+Subject: fbdev: defio: fix the pagelist corruption
+
+From: Chuansheng Liu <chuansheng.liu@intel.com>
+
+[ Upstream commit 856082f021a28221db2c32bd0531614a8382be67 ]
+
+Easily hit the below list corruption:
+==
+list_add corruption. prev->next should be next (ffffffffc0ceb090), but
+was ffffec604507edc8. (prev=ffffec604507edc8).
+WARNING: CPU: 65 PID: 3959 at lib/list_debug.c:26
+__list_add_valid+0x53/0x80
+CPU: 65 PID: 3959 Comm: fbdev Tainted: G     U
+RIP: 0010:__list_add_valid+0x53/0x80
+Call Trace:
+ <TASK>
+ fb_deferred_io_mkwrite+0xea/0x150
+ do_page_mkwrite+0x57/0xc0
+ do_wp_page+0x278/0x2f0
+ __handle_mm_fault+0xdc2/0x1590
+ handle_mm_fault+0xdd/0x2c0
+ do_user_addr_fault+0x1d3/0x650
+ exc_page_fault+0x77/0x180
+ ? asm_exc_page_fault+0x8/0x30
+ asm_exc_page_fault+0x1e/0x30
+RIP: 0033:0x7fd98fc8fad1
+==
+
+Figure out the race happens when one process is adding &page->lru into
+the pagelist tail in fb_deferred_io_mkwrite(), another process is
+re-initializing the same &page->lru in fb_deferred_io_fault(), which is
+not protected by the lock.
+
+This fix is to init all the page lists one time during initialization,
+it not only fixes the list corruption, but also avoids INIT_LIST_HEAD()
+redundantly.
+
+V2: change "int i" to "unsigned int i" (Geert Uytterhoeven)
+
+Signed-off-by: Chuansheng Liu <chuansheng.liu@intel.com>
+Fixes: 105a940416fc ("fbdev/defio: Early-out if page is already enlisted")
+Cc: Thomas Zimmermann <tzimmermann@suse.de>
+Cc: Geert Uytterhoeven <geert@linux-m68k.org>
+Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
+Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Link: https://patchwork.freedesktop.org/patch/msgid/20220318005003.51810-1-chuansheng.liu@intel.com
+Stable-dep-of: 33cd6ea9c067 ("fbdev: flush deferred IO before closing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/video/fbdev/core/fb_defio.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
+index bead69f39ffc..0da3dfe35233 100644
+--- a/drivers/video/fbdev/core/fb_defio.c
++++ b/drivers/video/fbdev/core/fb_defio.c
+@@ -59,7 +59,6 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
+               printk(KERN_ERR "no mapping available\n");
+       BUG_ON(!page->mapping);
+-      INIT_LIST_HEAD(&page->lru);
+       page->index = vmf->pgoff;
+       vmf->page = page;
+@@ -216,6 +215,8 @@ static void fb_deferred_io_work(struct work_struct *work)
+ void fb_deferred_io_init(struct fb_info *info)
+ {
+       struct fb_deferred_io *fbdefio = info->fbdefio;
++      struct page *page;
++      unsigned int i;
+       BUG_ON(!fbdefio);
+       mutex_init(&fbdefio->lock);
+@@ -223,6 +224,12 @@ void fb_deferred_io_init(struct fb_info *info)
+       INIT_LIST_HEAD(&fbdefio->pagelist);
+       if (fbdefio->delay == 0) /* set a default of 1 s */
+               fbdefio->delay = HZ;
++
++      /* initialize all the page lists one time */
++      for (i = 0; i < info->fix.smem_len; i += PAGE_SIZE) {
++              page = fb_deferred_io_page(info, i);
++              INIT_LIST_HEAD(&page->lru);
++      }
+ }
+ EXPORT_SYMBOL_GPL(fb_deferred_io_init);
+-- 
+2.43.0
+
diff --git a/queue-5.15/fbdev-don-t-sort-deferred-i-o-pages-by-default.patch b/queue-5.15/fbdev-don-t-sort-deferred-i-o-pages-by-default.patch
new file mode 100644 (file)
index 0000000..25f1dfb
--- /dev/null
@@ -0,0 +1,187 @@
+From 27cb6ff6155d1aee9a23bedcc90905bf10d07f36 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 11 Feb 2022 10:46:40 +0100
+Subject: fbdev: Don't sort deferred-I/O pages by default
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit 8c30e2d81bfddc5ab9f6b04db1c0f7d6ca7bdf46 ]
+
+Fbdev's deferred I/O sorts all dirty pages by default, which incurs a
+significant overhead. Make the sorting step optional and update the few
+drivers that require it. Use a FIFO list by default.
+
+Most fbdev drivers with deferred I/O build a bounding rectangle around
+the dirty pages or simply flush the whole screen. The only two affected
+DRM drivers, generic fbdev and vmwgfx, both use a bounding rectangle.
+In those cases, the exact order of the pages doesn't matter. The other
+drivers look at the page index or handle pages one-by-one. The patch
+sets the sort_pagelist flag for those, even though some of them would
+probably work correctly without sorting. Driver maintainers should update
+their driver accordingly.
+
+Sorting pages by memory offset for deferred I/O performs an implicit
+bubble-sort step on the list of dirty pages. The algorithm goes through
+the list of dirty pages and inserts each new page according to its
+index field. Even worse, list traversal always starts at the first
+entry. As video memory is most likely updated scanline by scanline, the
+algorithm traverses through the complete list for each updated page.
+
+For example, with 1024x768x32bpp each page covers exactly one scanline.
+Writing a single screen update from top to bottom requires updating
+768 pages. With an average list length of 384 entries, a screen update
+creates (768 * 384 =) 294912 compare operation.
+
+Fix this by making the sorting step opt-in and update the few drivers
+that require it. All other drivers work with unsorted page lists. Pages
+are appended to the list. Therefore, in the common case of writing the
+framebuffer top to bottom, pages are still sorted by offset, which may
+have a positive effect on performance.
+
+Playing a video [1] in mplayer's benchmark mode shows the difference
+(i7-4790, FullHD, simpledrm, kernel with debugging).
+
+  mplayer -benchmark -nosound -vo fbdev ./big_buck_bunny_720p_stereo.ogg
+
+With sorted page lists:
+
+  BENCHMARKs: VC:  32.960s VO:  73.068s A:   0.000s Sys:   2.413s =  108.441s
+  BENCHMARK%: VC: 30.3947% VO: 67.3802% A:  0.0000% Sys:  2.2251% = 100.0000%
+
+With unsorted page lists:
+
+  BENCHMARKs: VC:  31.005s VO:  42.889s A:   0.000s Sys:   2.256s =   76.150s
+  BENCHMARK%: VC: 40.7156% VO: 56.3219% A:  0.0000% Sys:  2.9625% = 100.0000%
+
+VC shows the overhead of video decoding, VO shows the overhead of the
+video output. Using unsorted page lists reduces the benchmark's run time
+by ~32s/~25%.
+
+v2:
+       * Make sorted pagelists the special case (Sam)
+       * Comment on drivers' use of pagelist (Sam)
+       * Warn about the overhead in comment
+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Acked-by: Sam Ravnborg <sam@ravnborg.org>
+Acked-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_720p_stereo.ogg # [1]
+Link: https://patchwork.freedesktop.org/patch/msgid/20220211094640.21632-3-tzimmermann@suse.de
+Stable-dep-of: 33cd6ea9c067 ("fbdev: flush deferred IO before closing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/staging/fbtft/fbtft-core.c  |  1 +
+ drivers/video/fbdev/broadsheetfb.c  |  1 +
+ drivers/video/fbdev/core/fb_defio.c | 24 +++++++++++++++++-------
+ drivers/video/fbdev/metronomefb.c   |  1 +
+ drivers/video/fbdev/udlfb.c         |  1 +
+ include/linux/fb.h                  |  1 +
+ 6 files changed, 22 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c
+index 1690358b8f01..52b19ec580a4 100644
+--- a/drivers/staging/fbtft/fbtft-core.c
++++ b/drivers/staging/fbtft/fbtft-core.c
+@@ -654,6 +654,7 @@ struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display,
+       fbops->fb_blank     =      fbtft_fb_blank;
+       fbdefio->delay =           HZ / fps;
++      fbdefio->sort_pagelist =   true;
+       fbdefio->deferred_io =     fbtft_deferred_io;
+       fb_deferred_io_init(info);
+diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c
+index fd66f4d4a621..b9054f658838 100644
+--- a/drivers/video/fbdev/broadsheetfb.c
++++ b/drivers/video/fbdev/broadsheetfb.c
+@@ -1059,6 +1059,7 @@ static const struct fb_ops broadsheetfb_ops = {
+ static struct fb_deferred_io broadsheetfb_defio = {
+       .delay          = HZ/4,
++      .sort_pagelist  = true,
+       .deferred_io    = broadsheetfb_dpy_deferred_io,
+ };
+diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
+index 95264a621221..bead69f39ffc 100644
+--- a/drivers/video/fbdev/core/fb_defio.c
++++ b/drivers/video/fbdev/core/fb_defio.c
+@@ -92,7 +92,7 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+       struct page *page = vmf->page;
+       struct fb_info *info = vmf->vma->vm_private_data;
+       struct fb_deferred_io *fbdefio = info->fbdefio;
+-      struct page *cur;
++      struct list_head *pos = &fbdefio->pagelist;
+       /* this is a callback we get when userspace first tries to
+       write to the page. we schedule a workqueue. that workqueue
+@@ -133,14 +133,24 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+       if (!list_empty(&page->lru))
+               goto page_already_added;
+-      /* we loop through the pagelist before adding in order
+-      to keep the pagelist sorted */
+-      list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+-              if (cur->index > page->index)
+-                      break;
++      if (unlikely(fbdefio->sort_pagelist)) {
++              /*
++               * We loop through the pagelist before adding in order to
++               * keep the pagelist sorted. This has significant overhead
++               * of O(n^2) with n being the number of written pages. If
++               * possible, drivers should try to work with unsorted page
++               * lists instead.
++               */
++              struct page *cur;
++
++              list_for_each_entry(cur, &fbdefio->pagelist, lru) {
++                      if (cur->index > page->index)
++                              break;
++              }
++              pos = &cur->lru;
+       }
+-      list_add_tail(&page->lru, &cur->lru);
++      list_add_tail(&page->lru, pos);
+ page_already_added:
+       mutex_unlock(&fbdefio->lock);
+diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c
+index 952826557a0c..af858dd23ea6 100644
+--- a/drivers/video/fbdev/metronomefb.c
++++ b/drivers/video/fbdev/metronomefb.c
+@@ -568,6 +568,7 @@ static const struct fb_ops metronomefb_ops = {
+ static struct fb_deferred_io metronomefb_defio = {
+       .delay          = HZ,
++      .sort_pagelist  = true,
+       .deferred_io    = metronomefb_dpy_deferred_io,
+ };
+diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c
+index 0de7b867714a..8603898bf37e 100644
+--- a/drivers/video/fbdev/udlfb.c
++++ b/drivers/video/fbdev/udlfb.c
+@@ -982,6 +982,7 @@ static int dlfb_ops_open(struct fb_info *info, int user)
+               if (fbdefio) {
+                       fbdefio->delay = DL_DEFIO_WRITE_DELAY;
++                      fbdefio->sort_pagelist = true;
+                       fbdefio->deferred_io = dlfb_dpy_deferred_io;
+               }
+diff --git a/include/linux/fb.h b/include/linux/fb.h
+index 3d7306c9a706..9a77ab615c36 100644
+--- a/include/linux/fb.h
++++ b/include/linux/fb.h
+@@ -204,6 +204,7 @@ struct fb_pixmap {
+ struct fb_deferred_io {
+       /* delay between mkwrite and deferred handler */
+       unsigned long delay;
++      bool sort_pagelist; /* sort pagelist by offset */
+       struct mutex lock; /* mutex that protects the page list */
+       struct list_head pagelist; /* list of touched pages */
+       /* callback */
+-- 
+2.43.0
+
diff --git a/queue-5.15/fbdev-fix-incorrect-page-mapping-clearance-at-fb_def.patch b/queue-5.15/fbdev-fix-incorrect-page-mapping-clearance-at-fb_def.patch
new file mode 100644 (file)
index 0000000..0227a11
--- /dev/null
@@ -0,0 +1,106 @@
+From a67b59a0fe2dacc469e88dc08601e15ae362002a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Mar 2023 11:50:12 +0100
+Subject: fbdev: Fix incorrect page mapping clearance at
+ fb_deferred_io_release()
+
+From: Takashi Iwai <tiwai@suse.de>
+
+[ Upstream commit fe9ae05cfbe587dda724fcf537c00bc2f287da62 ]
+
+The recent fix for the deferred I/O by the commit
+  3efc61d95259 ("fbdev: Fix invalid page access after closing deferred I/O devices")
+caused a regression when the same fb device is opened/closed while
+it's being used.  It resulted in a frozen screen even if something
+is redrawn there after the close.  The breakage is because the patch
+was made under a wrong assumption of a single open; in the current
+code, fb_deferred_io_release() cleans up the page mapping of the
+pageref list and it calls cancel_delayed_work_sync() unconditionally,
+where both are no correct behavior for multiple opens.
+
+This patch adds a refcount for the opens of the device, and applies
+the cleanup only when all files get closed.
+
+As both fb_deferred_io_open() and _close() are called always in the
+fb_info lock (mutex), it's safe to use the normal int for the
+refcounting.
+
+Also, a useless BUG_ON() is dropped.
+
+Fixes: 3efc61d95259 ("fbdev: Fix invalid page access after closing deferred I/O devices")
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Reviewed-by: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Link: https://patchwork.freedesktop.org/patch/msgid/20230308105012.1845-1-tiwai@suse.de
+Stable-dep-of: 33cd6ea9c067 ("fbdev: flush deferred IO before closing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/video/fbdev/core/fb_defio.c | 17 +++++++++++++----
+ include/linux/fb.h                  |  1 +
+ 2 files changed, 14 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
+index b3f41f432ec2..7e76e53b83d5 100644
+--- a/drivers/video/fbdev/core/fb_defio.c
++++ b/drivers/video/fbdev/core/fb_defio.c
+@@ -288,17 +288,18 @@ void fb_deferred_io_open(struct fb_info *info,
+                        struct inode *inode,
+                        struct file *file)
+ {
++      struct fb_deferred_io *fbdefio = info->fbdefio;
++
+       file->f_mapping->a_ops = &fb_deferred_io_aops;
++      fbdefio->open_count++;
+ }
+ EXPORT_SYMBOL_GPL(fb_deferred_io_open);
+-void fb_deferred_io_release(struct fb_info *info)
++static void fb_deferred_io_lastclose(struct fb_info *info)
+ {
+-      struct fb_deferred_io *fbdefio = info->fbdefio;
+       struct page *page;
+       int i;
+-      BUG_ON(!fbdefio);
+       cancel_delayed_work_sync(&info->deferred_work);
+       /* clear out the mapping that we setup */
+@@ -307,13 +308,21 @@ void fb_deferred_io_release(struct fb_info *info)
+               page->mapping = NULL;
+       }
+ }
++
++void fb_deferred_io_release(struct fb_info *info)
++{
++      struct fb_deferred_io *fbdefio = info->fbdefio;
++
++      if (!--fbdefio->open_count)
++              fb_deferred_io_lastclose(info);
++}
+ EXPORT_SYMBOL_GPL(fb_deferred_io_release);
+ void fb_deferred_io_cleanup(struct fb_info *info)
+ {
+       struct fb_deferred_io *fbdefio = info->fbdefio;
+-      fb_deferred_io_release(info);
++      fb_deferred_io_lastclose(info);
+       kvfree(info->pagerefs);
+       mutex_destroy(&fbdefio->lock);
+diff --git a/include/linux/fb.h b/include/linux/fb.h
+index 433cddf8442b..b79a833524ef 100644
+--- a/include/linux/fb.h
++++ b/include/linux/fb.h
+@@ -212,6 +212,7 @@ struct fb_deferred_io {
+       /* delay between mkwrite and deferred handler */
+       unsigned long delay;
+       bool sort_pagereflist; /* sort pagelist by offset */
++      int open_count; /* number of opened files; protected by fb_info lock */
+       struct mutex lock; /* mutex that protects the pageref list */
+       struct list_head pagereflist; /* list of pagerefs for touched pages */
+       /* callback */
+-- 
+2.43.0
+
diff --git a/queue-5.15/fbdev-fix-invalid-page-access-after-closing-deferred.patch b/queue-5.15/fbdev-fix-invalid-page-access-after-closing-deferred.patch
new file mode 100644 (file)
index 0000000..a75a183
--- /dev/null
@@ -0,0 +1,93 @@
+From 00cfec7001b41fd83fb3736cd71f29d09329bae2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 29 Jan 2023 09:28:56 +0100
+Subject: fbdev: Fix invalid page access after closing deferred I/O devices
+
+From: Takashi Iwai <tiwai@suse.de>
+
+[ Upstream commit 3efc61d95259956db25347e2a9562c3e54546e20 ]
+
+When a fbdev with deferred I/O is once opened and closed, the dirty
+pages still remain queued in the pageref list, and eventually later
+those may be processed in the delayed work.  This may lead to a
+corruption of pages, hitting an Oops.
+
+This patch makes sure to cancel the delayed work and clean up the
+pageref list at closing the device for addressing the bug.  A part of
+the cleanup code is factored out as a new helper function that is
+called from the common fb_release().
+
+Reviewed-by: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Tested-by: Miko Larsson <mikoxyzzz@gmail.com>
+Fixes: 56c134f7f1b5 ("fbdev: Track deferred-I/O pages in pageref struct")
+Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Link: https://patchwork.freedesktop.org/patch/msgid/20230129082856.22113-1-tiwai@suse.de
+Stable-dep-of: 33cd6ea9c067 ("fbdev: flush deferred IO before closing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/video/fbdev/core/fb_defio.c | 10 +++++++++-
+ drivers/video/fbdev/core/fbmem.c    |  4 ++++
+ include/linux/fb.h                  |  1 +
+ 3 files changed, 14 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
+index 5faeca61d3dd..b3f41f432ec2 100644
+--- a/drivers/video/fbdev/core/fb_defio.c
++++ b/drivers/video/fbdev/core/fb_defio.c
+@@ -292,7 +292,7 @@ void fb_deferred_io_open(struct fb_info *info,
+ }
+ EXPORT_SYMBOL_GPL(fb_deferred_io_open);
+-void fb_deferred_io_cleanup(struct fb_info *info)
++void fb_deferred_io_release(struct fb_info *info)
+ {
+       struct fb_deferred_io *fbdefio = info->fbdefio;
+       struct page *page;
+@@ -306,6 +306,14 @@ void fb_deferred_io_cleanup(struct fb_info *info)
+               page = fb_deferred_io_page(info, i);
+               page->mapping = NULL;
+       }
++}
++EXPORT_SYMBOL_GPL(fb_deferred_io_release);
++
++void fb_deferred_io_cleanup(struct fb_info *info)
++{
++      struct fb_deferred_io *fbdefio = info->fbdefio;
++
++      fb_deferred_io_release(info);
+       kvfree(info->pagerefs);
+       mutex_destroy(&fbdefio->lock);
+diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
+index 1b288a613a6e..ec7a883715e3 100644
+--- a/drivers/video/fbdev/core/fbmem.c
++++ b/drivers/video/fbdev/core/fbmem.c
+@@ -1454,6 +1454,10 @@ __releases(&info->lock)
+       struct fb_info * const info = file->private_data;
+       lock_fb_info(info);
++#if IS_ENABLED(CONFIG_FB_DEFERRED_IO)
++      if (info->fbdefio)
++              fb_deferred_io_release(info);
++#endif
+       if (info->fbops->fb_release)
+               info->fbops->fb_release(info,1);
+       module_put(info->fbops->owner);
+diff --git a/include/linux/fb.h b/include/linux/fb.h
+index b322d30f6225..433cddf8442b 100644
+--- a/include/linux/fb.h
++++ b/include/linux/fb.h
+@@ -675,6 +675,7 @@ extern int  fb_deferred_io_init(struct fb_info *info);
+ extern void fb_deferred_io_open(struct fb_info *info,
+                               struct inode *inode,
+                               struct file *file);
++extern void fb_deferred_io_release(struct fb_info *info);
+ extern void fb_deferred_io_cleanup(struct fb_info *info);
+ extern int fb_deferred_io_fsync(struct file *file, loff_t start,
+                               loff_t end, int datasync);
+-- 
+2.43.0
+
diff --git a/queue-5.15/fbdev-flush-deferred-io-before-closing.patch b/queue-5.15/fbdev-flush-deferred-io-before-closing.patch
new file mode 100644 (file)
index 0000000..c217cf0
--- /dev/null
@@ -0,0 +1,51 @@
+From 9b669dc1dd0ed0ff85993f50a0eae94b2605be86 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 Dec 2023 10:57:31 +0100
+Subject: fbdev: flush deferred IO before closing
+
+From: Nam Cao <namcao@linutronix.de>
+
+[ Upstream commit 33cd6ea9c0673517cdb06ad5c915c6f22e9615fc ]
+
+When framebuffer gets closed, the queued deferred IO gets cancelled. This
+can cause some last display data to vanish. This is problematic for users
+who send a still image to the framebuffer, then close the file: the image
+may never appear.
+
+To ensure none of display data get lost, flush the queued deferred IO
+first before closing.
+
+Another possible solution is to delete the cancel_delayed_work_sync()
+instead. The difference is that the display may appear some time after
+closing. However, the clearing of page mapping after this needs to be
+removed too, because the page mapping is used by the deferred work. It is
+not completely obvious whether it is okay to not clear the page mapping.
+For a patch intended for stable trees, go with the simple and obvious
+solution.
+
+Fixes: 60b59beafba8 ("fbdev: mm: Deferred IO support")
+Cc: stable@vger.kernel.org
+Signed-off-by: Nam Cao <namcao@linutronix.de>
+Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Signed-off-by: Helge Deller <deller@gmx.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/video/fbdev/core/fb_defio.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
+index 7e76e53b83d5..1f12c2043603 100644
+--- a/drivers/video/fbdev/core/fb_defio.c
++++ b/drivers/video/fbdev/core/fb_defio.c
+@@ -300,7 +300,7 @@ static void fb_deferred_io_lastclose(struct fb_info *info)
+       struct page *page;
+       int i;
+-      cancel_delayed_work_sync(&info->deferred_work);
++      flush_delayed_work(&info->deferred_work);
+       /* clear out the mapping that we setup */
+       for (i = 0 ; i < info->fix.smem_len; i += PAGE_SIZE) {
+-- 
+2.43.0
+
diff --git a/queue-5.15/fbdev-rename-pagelist-to-pagereflist-for-deferred-i-.patch b/queue-5.15/fbdev-rename-pagelist-to-pagereflist-for-deferred-i-.patch
new file mode 100644 (file)
index 0000000..d13728e
--- /dev/null
@@ -0,0 +1,524 @@
+From 6706769caf4a001397cc1c5dde6b8010fb77c24a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 Apr 2022 12:08:33 +0200
+Subject: fbdev: Rename pagelist to pagereflist for deferred I/O
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit e80eec1b871a2acb8f5c92db4c237e9ae6dd322b ]
+
+Rename various instances of pagelist to pagereflist. The list now
+stores pageref structures, so the new name is more appropriate.
+
+In their write-back helpers, several fbdev drivers refer to the
+pageref list in struct fb_deferred_io instead of using the one
+supplied as argument to the function. Convert them over to the
+supplied one. It's the same instance, so no change of behavior
+occurs.
+
+v4:
+       * fix commit message (Javier)
+
+Suggested-by: Sam Ravnborg <sam@ravnborg.org>
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20220429100834.18898-5-tzimmermann@suse.de
+Stable-dep-of: 33cd6ea9c067 ("fbdev: flush deferred IO before closing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/drm_fb_helper.c        |  7 +++----
+ drivers/gpu/drm/vmwgfx/vmwgfx_fb.c     |  5 ++---
+ drivers/hid/hid-picolcd_fb.c           |  2 +-
+ drivers/staging/fbtft/fbtft-core.c     | 10 +++++-----
+ drivers/video/fbdev/broadsheetfb.c     | 12 +++++-------
+ drivers/video/fbdev/core/fb_defio.c    | 18 +++++++++---------
+ drivers/video/fbdev/hecubafb.c         |  3 +--
+ drivers/video/fbdev/hyperv_fb.c        |  5 ++---
+ drivers/video/fbdev/metronomefb.c      | 12 +++++-------
+ drivers/video/fbdev/sh_mobile_lcdcfb.c | 16 +++++++---------
+ drivers/video/fbdev/smscufx.c          |  8 +++-----
+ drivers/video/fbdev/ssd1307fb.c        |  3 +--
+ drivers/video/fbdev/udlfb.c            |  8 +++-----
+ drivers/video/fbdev/xen-fbfront.c      |  5 ++---
+ include/drm/drm_fb_helper.h            |  3 +--
+ include/linux/fb.h                     |  6 +++---
+ 16 files changed, 53 insertions(+), 70 deletions(-)
+
+diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
+index 888ec6135544..82960d5d4e73 100644
+--- a/drivers/gpu/drm/drm_fb_helper.c
++++ b/drivers/gpu/drm/drm_fb_helper.c
+@@ -683,13 +683,12 @@ static void drm_fb_helper_damage(struct fb_info *info, u32 x, u32 y,
+ /**
+  * drm_fb_helper_deferred_io() - fbdev deferred_io callback function
+  * @info: fb_info struct pointer
+- * @pagelist: list of mmap framebuffer pages that have to be flushed
++ * @pagereflist: list of mmap framebuffer pages that have to be flushed
+  *
+  * This function is used as the &fb_deferred_io.deferred_io
+  * callback function for flushing the fbdev mmap writes.
+  */
+-void drm_fb_helper_deferred_io(struct fb_info *info,
+-                             struct list_head *pagelist)
++void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+       unsigned long start, end, min, max;
+       struct fb_deferred_io_pageref *pageref;
+@@ -697,7 +696,7 @@ void drm_fb_helper_deferred_io(struct fb_info *info,
+       min = ULONG_MAX;
+       max = 0;
+-      list_for_each_entry(pageref, pagelist, list) {
++      list_for_each_entry(pageref, pagereflist, list) {
+               start = pageref->offset;
+               end = start + PAGE_SIZE - 1;
+               min = min(min, start);
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+index 1f20e3c958ef..79b08e927f76 100644
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+@@ -316,8 +316,7 @@ static int vmw_fb_pan_display(struct fb_var_screeninfo *var,
+       return 0;
+ }
+-static void vmw_deferred_io(struct fb_info *info,
+-                          struct list_head *pagelist)
++static void vmw_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+       struct vmw_fb_par *par = info->par;
+       unsigned long start, end, min, max;
+@@ -327,7 +326,7 @@ static void vmw_deferred_io(struct fb_info *info,
+       min = ULONG_MAX;
+       max = 0;
+-      list_for_each_entry(pageref, pagelist, list) {
++      list_for_each_entry(pageref, pagereflist, list) {
+               struct page *page = pageref->page;
+               start = page->index << PAGE_SHIFT;
+               end = start + PAGE_SIZE - 1;
+diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c
+index 33c102a60992..8a0d1365cd72 100644
+--- a/drivers/hid/hid-picolcd_fb.c
++++ b/drivers/hid/hid-picolcd_fb.c
+@@ -432,7 +432,7 @@ static const struct fb_ops picolcdfb_ops = {
+ /* Callback from deferred IO workqueue */
+-static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)
++static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+       picolcd_fb_update(info);
+ }
+diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c
+index 5c52b5ff9f51..7aec35ae5cd5 100644
+--- a/drivers/staging/fbtft/fbtft-core.c
++++ b/drivers/staging/fbtft/fbtft-core.c
+@@ -322,7 +322,7 @@ static void fbtft_mkdirty(struct fb_info *info, int y, int height)
+       schedule_delayed_work(&info->deferred_work, fbdefio->delay);
+ }
+-static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
++static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+       struct fbtft_par *par = info->par;
+       unsigned int dirty_lines_start, dirty_lines_end;
+@@ -340,7 +340,7 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
+       spin_unlock(&par->dirty_lock);
+       /* Mark display lines as dirty */
+-      list_for_each_entry(pageref, pagelist, list) {
++      list_for_each_entry(pageref, pagereflist, list) {
+               struct page *page = pageref->page;
+               count++;
+               index = page->index << PAGE_SHIFT;
+@@ -654,9 +654,9 @@ struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display,
+       fbops->fb_setcolreg =      fbtft_fb_setcolreg;
+       fbops->fb_blank     =      fbtft_fb_blank;
+-      fbdefio->delay =           HZ / fps;
+-      fbdefio->sort_pagelist =   true;
+-      fbdefio->deferred_io =     fbtft_deferred_io;
++      fbdefio->delay =            HZ / fps;
++      fbdefio->sort_pagereflist = true;
++      fbdefio->deferred_io =      fbtft_deferred_io;
+       fb_deferred_io_init(info);
+       snprintf(info->fix.id, sizeof(info->fix.id), "%s", dev->driver->name);
+diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c
+index 2ca753d27f76..8b953d20ccdc 100644
+--- a/drivers/video/fbdev/broadsheetfb.c
++++ b/drivers/video/fbdev/broadsheetfb.c
+@@ -929,13 +929,11 @@ static void broadsheetfb_dpy_update(struct broadsheetfb_par *par)
+ }
+ /* this is called back from the deferred io workqueue */
+-static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
+-                              struct list_head *pagelist)
++static void broadsheetfb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+       u16 y1 = 0, h = 0;
+       int prev_index = -1;
+       struct fb_deferred_io_pageref *pageref;
+-      struct fb_deferred_io *fbdefio = info->fbdefio;
+       int h_inc;
+       u16 yres = info->var.yres;
+       u16 xres = info->var.xres;
+@@ -944,7 +942,7 @@ static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
+       h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
+       /* walk the written page list and swizzle the data */
+-      list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++      list_for_each_entry(pageref, pagereflist, list) {
+               struct page *cur = pageref->page;
+               if (prev_index < 0) {
+                       /* just starting so assign first page */
+@@ -1059,9 +1057,9 @@ static const struct fb_ops broadsheetfb_ops = {
+ };
+ static struct fb_deferred_io broadsheetfb_defio = {
+-      .delay          = HZ/4,
+-      .sort_pagelist  = true,
+-      .deferred_io    = broadsheetfb_dpy_deferred_io,
++      .delay                  = HZ/4,
++      .sort_pagereflist       = true,
++      .deferred_io            = broadsheetfb_dpy_deferred_io,
+ };
+ static int broadsheetfb_probe(struct platform_device *dev)
+diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
+index a79af3b5faf3..5faeca61d3dd 100644
+--- a/drivers/video/fbdev/core/fb_defio.c
++++ b/drivers/video/fbdev/core/fb_defio.c
+@@ -41,7 +41,7 @@ static struct fb_deferred_io_pageref *fb_deferred_io_pageref_get(struct fb_info
+                                                                struct page *page)
+ {
+       struct fb_deferred_io *fbdefio = info->fbdefio;
+-      struct list_head *pos = &fbdefio->pagelist;
++      struct list_head *pos = &fbdefio->pagereflist;
+       unsigned long pgoff = offset >> PAGE_SHIFT;
+       struct fb_deferred_io_pageref *pageref, *cur;
+@@ -63,7 +63,7 @@ static struct fb_deferred_io_pageref *fb_deferred_io_pageref_get(struct fb_info
+       pageref->page = page;
+       pageref->offset = pgoff << PAGE_SHIFT;
+-      if (unlikely(fbdefio->sort_pagelist)) {
++      if (unlikely(fbdefio->sort_pagereflist)) {
+               /*
+                * We loop through the list of pagerefs before adding in
+                * order to keep the pagerefs sorted. This has significant
+@@ -71,7 +71,7 @@ static struct fb_deferred_io_pageref *fb_deferred_io_pageref_get(struct fb_info
+                * pages. If possible, drivers should try to work with
+                * unsorted page lists instead.
+                */
+-              list_for_each_entry(cur, &info->fbdefio->pagelist, list) {
++              list_for_each_entry(cur, &fbdefio->pagereflist, list) {
+                       if (cur->offset > pageref->offset)
+                               break;
+               }
+@@ -163,7 +163,7 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+       mutex_lock(&fbdefio->lock);
+       /* first write in this cycle, notify the driver */
+-      if (fbdefio->first_io && list_empty(&fbdefio->pagelist))
++      if (fbdefio->first_io && list_empty(&fbdefio->pagereflist))
+               fbdefio->first_io(info);
+       pageref = fb_deferred_io_pageref_get(info, offset, page);
+@@ -228,18 +228,18 @@ static void fb_deferred_io_work(struct work_struct *work)
+       /* here we mkclean the pages, then do all deferred IO */
+       mutex_lock(&fbdefio->lock);
+-      list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++      list_for_each_entry(pageref, &fbdefio->pagereflist, list) {
+               struct page *cur = pageref->page;
+               lock_page(cur);
+               page_mkclean(cur);
+               unlock_page(cur);
+       }
+-      /* driver's callback with pagelist */
+-      fbdefio->deferred_io(info, &fbdefio->pagelist);
++      /* driver's callback with pagereflist */
++      fbdefio->deferred_io(info, &fbdefio->pagereflist);
+       /* clear the list */
+-      list_for_each_entry_safe(pageref, next, &fbdefio->pagelist, list)
++      list_for_each_entry_safe(pageref, next, &fbdefio->pagereflist, list)
+               fb_deferred_io_pageref_put(pageref, info);
+       mutex_unlock(&fbdefio->lock);
+@@ -259,7 +259,7 @@ int fb_deferred_io_init(struct fb_info *info)
+       mutex_init(&fbdefio->lock);
+       INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
+-      INIT_LIST_HEAD(&fbdefio->pagelist);
++      INIT_LIST_HEAD(&fbdefio->pagereflist);
+       if (fbdefio->delay == 0) /* set a default of 1 s */
+               fbdefio->delay = HZ;
+diff --git a/drivers/video/fbdev/hecubafb.c b/drivers/video/fbdev/hecubafb.c
+index 00d77105161a..bd6b0dec414b 100644
+--- a/drivers/video/fbdev/hecubafb.c
++++ b/drivers/video/fbdev/hecubafb.c
+@@ -115,8 +115,7 @@ static void hecubafb_dpy_update(struct hecubafb_par *par)
+ }
+ /* this is called back from the deferred io workqueue */
+-static void hecubafb_dpy_deferred_io(struct fb_info *info,
+-                              struct list_head *pagelist)
++static void hecubafb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+       hecubafb_dpy_update(info->par);
+ }
+diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c
+index 704ba7ee13c6..6a881cfd7f5c 100644
+--- a/drivers/video/fbdev/hyperv_fb.c
++++ b/drivers/video/fbdev/hyperv_fb.c
+@@ -420,8 +420,7 @@ static void hvfb_docopy(struct hvfb_par *par,
+ }
+ /* Deferred IO callback */
+-static void synthvid_deferred_io(struct fb_info *p,
+-                               struct list_head *pagelist)
++static void synthvid_deferred_io(struct fb_info *p, struct list_head *pagereflist)
+ {
+       struct hvfb_par *par = p->par;
+       struct fb_deferred_io_pageref *pageref;
+@@ -437,7 +436,7 @@ static void synthvid_deferred_io(struct fb_info *p,
+        * in synthvid_update function by clamping the y2
+        * value to yres.
+        */
+-      list_for_each_entry(pageref, pagelist, list) {
++      list_for_each_entry(pageref, pagereflist, list) {
+               struct page *page = pageref->page;
+               start = page->index << PAGE_SHIFT;
+               end = start + PAGE_SIZE - 1;
+diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c
+index 74672539ee7b..b8df0bb7cabc 100644
+--- a/drivers/video/fbdev/metronomefb.c
++++ b/drivers/video/fbdev/metronomefb.c
+@@ -465,16 +465,14 @@ static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index)
+ }
+ /* this is called back from the deferred io workqueue */
+-static void metronomefb_dpy_deferred_io(struct fb_info *info,
+-                              struct list_head *pagelist)
++static void metronomefb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+       u16 cksum;
+       struct fb_deferred_io_pageref *pageref;
+-      struct fb_deferred_io *fbdefio = info->fbdefio;
+       struct metronomefb_par *par = info->par;
+       /* walk the written page list and swizzle the data */
+-      list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++      list_for_each_entry(pageref, pagereflist, list) {
+               struct page *cur = pageref->page;
+               cksum = metronomefb_dpy_update_page(par,
+                                       (cur->index << PAGE_SHIFT));
+@@ -568,9 +566,9 @@ static const struct fb_ops metronomefb_ops = {
+ };
+ static struct fb_deferred_io metronomefb_defio = {
+-      .delay          = HZ,
+-      .sort_pagelist  = true,
+-      .deferred_io    = metronomefb_dpy_deferred_io,
++      .delay                  = HZ,
++      .sort_pagereflist       = true,
++      .deferred_io            = metronomefb_dpy_deferred_io,
+ };
+ static int metronomefb_probe(struct platform_device *dev)
+diff --git a/drivers/video/fbdev/sh_mobile_lcdcfb.c b/drivers/video/fbdev/sh_mobile_lcdcfb.c
+index b6b5ce3505c0..e33c016c5428 100644
+--- a/drivers/video/fbdev/sh_mobile_lcdcfb.c
++++ b/drivers/video/fbdev/sh_mobile_lcdcfb.c
+@@ -435,8 +435,7 @@ static struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
+       .read_data      = lcdc_sys_read_data,
+ };
+-static int sh_mobile_lcdc_sginit(struct fb_info *info,
+-                                struct list_head *pagelist)
++static int sh_mobile_lcdc_sginit(struct fb_info *info, struct list_head *pagereflist)
+ {
+       struct sh_mobile_lcdc_chan *ch = info->par;
+       unsigned int nr_pages_max = ch->fb_size >> PAGE_SHIFT;
+@@ -445,7 +444,7 @@ static int sh_mobile_lcdc_sginit(struct fb_info *info,
+       sg_init_table(ch->sglist, nr_pages_max);
+-      list_for_each_entry(pageref, pagelist, list) {
++      list_for_each_entry(pageref, pagereflist, list) {
+               struct page *page = pageref->page;
+               sg_set_page(&ch->sglist[nr_pages++], page, PAGE_SIZE, 0);
+       }
+@@ -453,8 +452,7 @@ static int sh_mobile_lcdc_sginit(struct fb_info *info,
+       return nr_pages;
+ }
+-static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
+-                                     struct list_head *pagelist)
++static void sh_mobile_lcdc_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+       struct sh_mobile_lcdc_chan *ch = info->par;
+       const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg;
+@@ -463,7 +461,7 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
+       sh_mobile_lcdc_clk_on(ch->lcdc);
+       /*
+-       * It's possible to get here without anything on the pagelist via
++       * It's possible to get here without anything on the pagereflist via
+        * sh_mobile_lcdc_deferred_io_touch() or via a userspace fsync()
+        * invocation. In the former case, the acceleration routines are
+        * stepped in to when using the framebuffer console causing the
+@@ -473,12 +471,12 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
+        * acceleration routines have their own methods for writing in
+        * that still need to be updated.
+        *
+-       * The fsync() and empty pagelist case could be optimized for,
++       * The fsync() and empty pagereflist case could be optimized for,
+        * but we don't bother, as any application exhibiting such
+        * behaviour is fundamentally broken anyways.
+        */
+-      if (!list_empty(pagelist)) {
+-              unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
++      if (!list_empty(pagereflist)) {
++              unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagereflist);
+               /* trigger panel update */
+               dma_map_sg(ch->lcdc->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c
+index bf3d151abd6f..14e4e106d26b 100644
+--- a/drivers/video/fbdev/smscufx.c
++++ b/drivers/video/fbdev/smscufx.c
+@@ -953,12 +953,10 @@ static void ufx_ops_fillrect(struct fb_info *info,
+  *   Touching ANY framebuffer memory that triggers a page fault
+  *   in fb_defio will cause a deadlock, when it also tries to
+  *   grab the same mutex. */
+-static void ufx_dpy_deferred_io(struct fb_info *info,
+-                              struct list_head *pagelist)
++static void ufx_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+-      struct fb_deferred_io_pageref *pageref;
+-      struct fb_deferred_io *fbdefio = info->fbdefio;
+       struct ufx_data *dev = info->par;
++      struct fb_deferred_io_pageref *pageref;
+       if (!fb_defio)
+               return;
+@@ -967,7 +965,7 @@ static void ufx_dpy_deferred_io(struct fb_info *info,
+               return;
+       /* walk the written page list and render each to device */
+-      list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++      list_for_each_entry(pageref, pagereflist, list) {
+               /* create a rectangle of full screen width that encloses the
+                * entire dirty framebuffer page */
+               struct page *cur = pageref->page;
+diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
+index 1e2f71c2f8a8..7acf7c0b263e 100644
+--- a/drivers/video/fbdev/ssd1307fb.c
++++ b/drivers/video/fbdev/ssd1307fb.c
+@@ -370,8 +370,7 @@ static const struct fb_ops ssd1307fb_ops = {
+       .fb_imageblit   = ssd1307fb_imageblit,
+ };
+-static void ssd1307fb_deferred_io(struct fb_info *info,
+-                              struct list_head *pagelist)
++static void ssd1307fb_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+       ssd1307fb_update_display(info->par);
+ }
+diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c
+index c187163fe580..12bd5a7318e1 100644
+--- a/drivers/video/fbdev/udlfb.c
++++ b/drivers/video/fbdev/udlfb.c
+@@ -780,11 +780,9 @@ static void dlfb_ops_fillrect(struct fb_info *info,
+  *   in fb_defio will cause a deadlock, when it also tries to
+  *   grab the same mutex.
+  */
+-static void dlfb_dpy_deferred_io(struct fb_info *info,
+-                              struct list_head *pagelist)
++static void dlfb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+       struct fb_deferred_io_pageref *pageref;
+-      struct fb_deferred_io *fbdefio = info->fbdefio;
+       struct dlfb_data *dlfb = info->par;
+       struct urb *urb;
+       char *cmd;
+@@ -810,7 +808,7 @@ static void dlfb_dpy_deferred_io(struct fb_info *info,
+       cmd = urb->transfer_buffer;
+       /* walk the written page list and render each to device */
+-      list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++      list_for_each_entry(pageref, pagereflist, list) {
+               struct page *cur = pageref->page;
+               if (dlfb_render_hline(dlfb, &urb, (char *) info->fix.smem_start,
+@@ -983,7 +981,7 @@ static int dlfb_ops_open(struct fb_info *info, int user)
+               if (fbdefio) {
+                       fbdefio->delay = DL_DEFIO_WRITE_DELAY;
+-                      fbdefio->sort_pagelist = true;
++                      fbdefio->sort_pagereflist = true;
+                       fbdefio->deferred_io = dlfb_dpy_deferred_io;
+               }
+diff --git a/drivers/video/fbdev/xen-fbfront.c b/drivers/video/fbdev/xen-fbfront.c
+index 00d9502ee25a..37e3f226f78c 100644
+--- a/drivers/video/fbdev/xen-fbfront.c
++++ b/drivers/video/fbdev/xen-fbfront.c
+@@ -181,8 +181,7 @@ static void xenfb_refresh(struct xenfb_info *info,
+               xenfb_do_update(info, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+ }
+-static void xenfb_deferred_io(struct fb_info *fb_info,
+-                            struct list_head *pagelist)
++static void xenfb_deferred_io(struct fb_info *fb_info, struct list_head *pagereflist)
+ {
+       struct xenfb_info *info = fb_info->par;
+       struct fb_deferred_io_pageref *pageref;
+@@ -191,7 +190,7 @@ static void xenfb_deferred_io(struct fb_info *fb_info,
+       miny = INT_MAX;
+       maxy = 0;
+-      list_for_each_entry(pageref, pagelist, list) {
++      list_for_each_entry(pageref, pagereflist, list) {
+               struct page *page = pageref->page;
+               beg = page->index << PAGE_SHIFT;
+               end = beg + PAGE_SIZE - 1;
+diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
+index 3af4624368d8..329607ca65c0 100644
+--- a/include/drm/drm_fb_helper.h
++++ b/include/drm/drm_fb_helper.h
+@@ -229,8 +229,7 @@ void drm_fb_helper_fill_info(struct fb_info *info,
+                            struct drm_fb_helper *fb_helper,
+                            struct drm_fb_helper_surface_size *sizes);
+-void drm_fb_helper_deferred_io(struct fb_info *info,
+-                             struct list_head *pagelist);
++void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagereflist);
+ ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf,
+                              size_t count, loff_t *ppos);
+diff --git a/include/linux/fb.h b/include/linux/fb.h
+index 768de6534a82..b322d30f6225 100644
+--- a/include/linux/fb.h
++++ b/include/linux/fb.h
+@@ -211,9 +211,9 @@ struct fb_deferred_io_pageref {
+ struct fb_deferred_io {
+       /* delay between mkwrite and deferred handler */
+       unsigned long delay;
+-      bool sort_pagelist; /* sort pagelist by offset */
+-      struct mutex lock; /* mutex that protects the page list */
+-      struct list_head pagelist; /* list of touched pages */
++      bool sort_pagereflist; /* sort pagelist by offset */
++      struct mutex lock; /* mutex that protects the pageref list */
++      struct list_head pagereflist; /* list of pagerefs for touched pages */
+       /* callback */
+       void (*first_io)(struct fb_info *info);
+       void (*deferred_io)(struct fb_info *info, struct list_head *pagelist);
+-- 
+2.43.0
+
diff --git a/queue-5.15/fbdev-track-deferred-i-o-pages-in-pageref-struct.patch b/queue-5.15/fbdev-track-deferred-i-o-pages-in-pageref-struct.patch
new file mode 100644 (file)
index 0000000..acc93da
--- /dev/null
@@ -0,0 +1,565 @@
+From 1177a6fb2dee82cb4c03bc1068dbfb7ff9172408 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 Apr 2022 12:08:31 +0200
+Subject: fbdev: Track deferred-I/O pages in pageref struct
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit 56c134f7f1b58be08bdb0ca8372474a4a5165f31 ]
+
+Store the per-page state for fbdev's deferred I/O in struct
+fb_deferred_io_pageref. Maintain a list of pagerefs for the pages
+that have to be written back to video memory. Update all affected
+drivers.
+
+As with pages before, fbdev acquires a pageref when an mmaped page
+of the framebuffer is being written to. It holds the pageref in a
+list of all currently written pagerefs until it flushes the written
+pages to video memory. Writeback occurs periodically. After writeback
+fbdev releases all pagerefs and builds up a new dirty list until the
+next writeback occurs.
+
+Using pagerefs has a number of benefits.
+
+For pages of the framebuffer, the deferred I/O code used struct
+page.lru as an entry into the list of dirty pages. The lru field is
+owned by the page cache, which makes deferred I/O incompatible with
+some memory pages (e.g., most notably DRM's GEM SHMEM allocator).
+struct fb_deferred_io_pageref now provides an entry into a list of
+dirty framebuffer pages, freeing lru for use with the page cache.
+
+Drivers also assumed that struct page.index is the page offset into
+the framebuffer. This is not true for DRM buffers, which are located
+at various offset within a mapped area. struct fb_deferred_io_pageref
+explicitly stores an offset into the framebuffer. struct page.index
+is now only the page offset into the mapped area.
+
+These changes will allow DRM to use fbdev deferred I/O without an
+intermediate shadow buffer.
+
+v3:
+       * use pageref->offset for sorting
+       * fix grammar in comment
+v2:
+       * minor fixes in commit message
+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20220429100834.18898-3-tzimmermann@suse.de
+Stable-dep-of: 33cd6ea9c067 ("fbdev: flush deferred IO before closing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/drm_fb_helper.c        |   6 +-
+ drivers/gpu/drm/vmwgfx/vmwgfx_fb.c     |   5 +-
+ drivers/staging/fbtft/fbtft-core.c     |   5 +-
+ drivers/video/fbdev/broadsheetfb.c     |   5 +-
+ drivers/video/fbdev/core/fb_defio.c    | 156 ++++++++++++++++---------
+ drivers/video/fbdev/hyperv_fb.c        |   5 +-
+ drivers/video/fbdev/metronomefb.c      |   5 +-
+ drivers/video/fbdev/sh_mobile_lcdcfb.c |   6 +-
+ drivers/video/fbdev/smscufx.c          |   5 +-
+ drivers/video/fbdev/udlfb.c            |   5 +-
+ drivers/video/fbdev/xen-fbfront.c      |   5 +-
+ include/linux/fb.h                     |  11 +-
+ 12 files changed, 145 insertions(+), 74 deletions(-)
+
+diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
+index 3d9c0444df40..888ec6135544 100644
+--- a/drivers/gpu/drm/drm_fb_helper.c
++++ b/drivers/gpu/drm/drm_fb_helper.c
+@@ -692,13 +692,13 @@ void drm_fb_helper_deferred_io(struct fb_info *info,
+                              struct list_head *pagelist)
+ {
+       unsigned long start, end, min, max;
+-      struct page *page;
++      struct fb_deferred_io_pageref *pageref;
+       u32 y1, y2;
+       min = ULONG_MAX;
+       max = 0;
+-      list_for_each_entry(page, pagelist, lru) {
+-              start = page->index << PAGE_SHIFT;
++      list_for_each_entry(pageref, pagelist, list) {
++              start = pageref->offset;
+               end = start + PAGE_SIZE - 1;
+               min = min(min, start);
+               max = max(max, end);
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+index f18ed03a8b2d..1f20e3c958ef 100644
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+@@ -322,12 +322,13 @@ static void vmw_deferred_io(struct fb_info *info,
+       struct vmw_fb_par *par = info->par;
+       unsigned long start, end, min, max;
+       unsigned long flags;
+-      struct page *page;
++      struct fb_deferred_io_pageref *pageref;
+       int y1, y2;
+       min = ULONG_MAX;
+       max = 0;
+-      list_for_each_entry(page, pagelist, lru) {
++      list_for_each_entry(pageref, pagelist, list) {
++              struct page *page = pageref->page;
+               start = page->index << PAGE_SHIFT;
+               end = start + PAGE_SIZE - 1;
+               min = min(min, start);
+diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c
+index 52b19ec580a4..5c52b5ff9f51 100644
+--- a/drivers/staging/fbtft/fbtft-core.c
++++ b/drivers/staging/fbtft/fbtft-core.c
+@@ -326,7 +326,7 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
+ {
+       struct fbtft_par *par = info->par;
+       unsigned int dirty_lines_start, dirty_lines_end;
+-      struct page *page;
++      struct fb_deferred_io_pageref *pageref;
+       unsigned long index;
+       unsigned int y_low = 0, y_high = 0;
+       int count = 0;
+@@ -340,7 +340,8 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
+       spin_unlock(&par->dirty_lock);
+       /* Mark display lines as dirty */
+-      list_for_each_entry(page, pagelist, lru) {
++      list_for_each_entry(pageref, pagelist, list) {
++              struct page *page = pageref->page;
+               count++;
+               index = page->index << PAGE_SHIFT;
+               y_low = index / info->fix.line_length;
+diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c
+index b9054f658838..2ca753d27f76 100644
+--- a/drivers/video/fbdev/broadsheetfb.c
++++ b/drivers/video/fbdev/broadsheetfb.c
+@@ -934,7 +934,7 @@ static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
+ {
+       u16 y1 = 0, h = 0;
+       int prev_index = -1;
+-      struct page *cur;
++      struct fb_deferred_io_pageref *pageref;
+       struct fb_deferred_io *fbdefio = info->fbdefio;
+       int h_inc;
+       u16 yres = info->var.yres;
+@@ -944,7 +944,8 @@ static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
+       h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
+       /* walk the written page list and swizzle the data */
+-      list_for_each_entry(cur, &fbdefio->pagelist, lru) {
++      list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++              struct page *cur = pageref->page;
+               if (prev_index < 0) {
+                       /* just starting so assign first page */
+                       y1 = (cur->index << PAGE_SHIFT) / xres;
+diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
+index 0da3dfe35233..a79af3b5faf3 100644
+--- a/drivers/video/fbdev/core/fb_defio.c
++++ b/drivers/video/fbdev/core/fb_defio.c
+@@ -36,6 +36,60 @@ static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs
+       return page;
+ }
++static struct fb_deferred_io_pageref *fb_deferred_io_pageref_get(struct fb_info *info,
++                                                               unsigned long offset,
++                                                               struct page *page)
++{
++      struct fb_deferred_io *fbdefio = info->fbdefio;
++      struct list_head *pos = &fbdefio->pagelist;
++      unsigned long pgoff = offset >> PAGE_SHIFT;
++      struct fb_deferred_io_pageref *pageref, *cur;
++
++      if (WARN_ON_ONCE(pgoff >= info->npagerefs))
++              return NULL; /* incorrect allocation size */
++
++      /* 1:1 mapping between pageref and page offset */
++      pageref = &info->pagerefs[pgoff];
++
++      /*
++       * This check is to catch the case where a new process could start
++       * writing to the same page through a new PTE. This new access
++       * can cause a call to .page_mkwrite even if the original process'
++       * PTE is marked writable.
++       */
++      if (!list_empty(&pageref->list))
++              goto pageref_already_added;
++
++      pageref->page = page;
++      pageref->offset = pgoff << PAGE_SHIFT;
++
++      if (unlikely(fbdefio->sort_pagelist)) {
++              /*
++               * We loop through the list of pagerefs before adding in
++               * order to keep the pagerefs sorted. This has significant
++               * overhead of O(n^2) with n being the number of written
++               * pages. If possible, drivers should try to work with
++               * unsorted page lists instead.
++               */
++              list_for_each_entry(cur, &info->fbdefio->pagelist, list) {
++                      if (cur->offset > pageref->offset)
++                              break;
++              }
++              pos = &cur->list;
++      }
++
++      list_add_tail(&pageref->list, pos);
++
++pageref_already_added:
++      return pageref;
++}
++
++static void fb_deferred_io_pageref_put(struct fb_deferred_io_pageref *pageref,
++                                     struct fb_info *info)
++{
++      list_del_init(&pageref->list);
++}
++
+ /* this is to find and return the vmalloc-ed fb pages */
+ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
+ {
+@@ -59,7 +113,7 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
+               printk(KERN_ERR "no mapping available\n");
+       BUG_ON(!page->mapping);
+-      page->index = vmf->pgoff;
++      page->index = vmf->pgoff; /* for page_mkclean() */
+       vmf->page = page;
+       return 0;
+@@ -91,7 +145,11 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+       struct page *page = vmf->page;
+       struct fb_info *info = vmf->vma->vm_private_data;
+       struct fb_deferred_io *fbdefio = info->fbdefio;
+-      struct list_head *pos = &fbdefio->pagelist;
++      struct fb_deferred_io_pageref *pageref;
++      unsigned long offset;
++      vm_fault_t ret;
++
++      offset = (vmf->address - vmf->vma->vm_start);
+       /* this is a callback we get when userspace first tries to
+       write to the page. we schedule a workqueue. that workqueue
+@@ -108,6 +166,12 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+       if (fbdefio->first_io && list_empty(&fbdefio->pagelist))
+               fbdefio->first_io(info);
++      pageref = fb_deferred_io_pageref_get(info, offset, page);
++      if (WARN_ON_ONCE(!pageref)) {
++              ret = VM_FAULT_OOM;
++              goto err_mutex_unlock;
++      }
++
+       /*
+        * We want the page to remain locked from ->page_mkwrite until
+        * the PTE is marked dirty to avoid page_mkclean() being called
+@@ -116,47 +180,17 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+        * Do this by locking the page here and informing the caller
+        * about it with VM_FAULT_LOCKED.
+        */
+-      lock_page(page);
+-
+-      /*
+-       * This check is to catch the case where a new process could start
+-       * writing to the same page through a new PTE. This new access
+-       * can cause a call to .page_mkwrite even if the original process'
+-       * PTE is marked writable.
+-       *
+-       * TODO: The lru field is owned by the page cache; hence the name.
+-       *       We dequeue in fb_deferred_io_work() after flushing the
+-       *       page's content into video memory. Instead of lru, fbdefio
+-       *       should have it's own field.
+-       */
+-      if (!list_empty(&page->lru))
+-              goto page_already_added;
+-
+-      if (unlikely(fbdefio->sort_pagelist)) {
+-              /*
+-               * We loop through the pagelist before adding in order to
+-               * keep the pagelist sorted. This has significant overhead
+-               * of O(n^2) with n being the number of written pages. If
+-               * possible, drivers should try to work with unsorted page
+-               * lists instead.
+-               */
+-              struct page *cur;
+-
+-              list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+-                      if (cur->index > page->index)
+-                              break;
+-              }
+-              pos = &cur->lru;
+-      }
+-
+-      list_add_tail(&page->lru, pos);
++      lock_page(pageref->page);
+-page_already_added:
+       mutex_unlock(&fbdefio->lock);
+       /* come back after delay to process the deferred IO */
+       schedule_delayed_work(&info->deferred_work, fbdefio->delay);
+       return VM_FAULT_LOCKED;
++
++err_mutex_unlock:
++      mutex_unlock(&fbdefio->lock);
++      return ret;
+ }
+ static const struct vm_operations_struct fb_deferred_io_vm_ops = {
+@@ -188,15 +222,14 @@ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
+ /* workqueue callback */
+ static void fb_deferred_io_work(struct work_struct *work)
+ {
+-      struct fb_info *info = container_of(work, struct fb_info,
+-                                              deferred_work.work);
+-      struct list_head *node, *next;
+-      struct page *cur;
++      struct fb_info *info = container_of(work, struct fb_info, deferred_work.work);
++      struct fb_deferred_io_pageref *pageref, *next;
+       struct fb_deferred_io *fbdefio = info->fbdefio;
+       /* here we mkclean the pages, then do all deferred IO */
+       mutex_lock(&fbdefio->lock);
+-      list_for_each_entry(cur, &fbdefio->pagelist, lru) {
++      list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++              struct page *cur = pageref->page;
+               lock_page(cur);
+               page_mkclean(cur);
+               unlock_page(cur);
+@@ -206,30 +239,48 @@ static void fb_deferred_io_work(struct work_struct *work)
+       fbdefio->deferred_io(info, &fbdefio->pagelist);
+       /* clear the list */
+-      list_for_each_safe(node, next, &fbdefio->pagelist) {
+-              list_del_init(node);
+-      }
++      list_for_each_entry_safe(pageref, next, &fbdefio->pagelist, list)
++              fb_deferred_io_pageref_put(pageref, info);
++
+       mutex_unlock(&fbdefio->lock);
+ }
+-void fb_deferred_io_init(struct fb_info *info)
++int fb_deferred_io_init(struct fb_info *info)
+ {
+       struct fb_deferred_io *fbdefio = info->fbdefio;
+-      struct page *page;
+-      unsigned int i;
++      struct fb_deferred_io_pageref *pagerefs;
++      unsigned long npagerefs, i;
++      int ret;
+       BUG_ON(!fbdefio);
++
++      if (WARN_ON(!info->fix.smem_len))
++              return -EINVAL;
++
+       mutex_init(&fbdefio->lock);
+       INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
+       INIT_LIST_HEAD(&fbdefio->pagelist);
+       if (fbdefio->delay == 0) /* set a default of 1 s */
+               fbdefio->delay = HZ;
+-      /* initialize all the page lists one time */
+-      for (i = 0; i < info->fix.smem_len; i += PAGE_SIZE) {
+-              page = fb_deferred_io_page(info, i);
+-              INIT_LIST_HEAD(&page->lru);
++      npagerefs = DIV_ROUND_UP(info->fix.smem_len, PAGE_SIZE);
++
++      /* alloc a page ref for each page of the display memory */
++      pagerefs = kvcalloc(npagerefs, sizeof(*pagerefs), GFP_KERNEL);
++      if (!pagerefs) {
++              ret = -ENOMEM;
++              goto err;
+       }
++      for (i = 0; i < npagerefs; ++i)
++              INIT_LIST_HEAD(&pagerefs[i].list);
++      info->npagerefs = npagerefs;
++      info->pagerefs = pagerefs;
++
++      return 0;
++
++err:
++      mutex_destroy(&fbdefio->lock);
++      return ret;
+ }
+ EXPORT_SYMBOL_GPL(fb_deferred_io_init);
+@@ -256,6 +307,7 @@ void fb_deferred_io_cleanup(struct fb_info *info)
+               page->mapping = NULL;
+       }
++      kvfree(info->pagerefs);
+       mutex_destroy(&fbdefio->lock);
+ }
+ EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
+diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c
+index de865e197c8d..704ba7ee13c6 100644
+--- a/drivers/video/fbdev/hyperv_fb.c
++++ b/drivers/video/fbdev/hyperv_fb.c
+@@ -424,7 +424,7 @@ static void synthvid_deferred_io(struct fb_info *p,
+                                struct list_head *pagelist)
+ {
+       struct hvfb_par *par = p->par;
+-      struct page *page;
++      struct fb_deferred_io_pageref *pageref;
+       unsigned long start, end;
+       int y1, y2, miny, maxy;
+@@ -437,7 +437,8 @@ static void synthvid_deferred_io(struct fb_info *p,
+        * in synthvid_update function by clamping the y2
+        * value to yres.
+        */
+-      list_for_each_entry(page, pagelist, lru) {
++      list_for_each_entry(pageref, pagelist, list) {
++              struct page *page = pageref->page;
+               start = page->index << PAGE_SHIFT;
+               end = start + PAGE_SIZE - 1;
+               y1 = start / p->fix.line_length;
+diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c
+index af858dd23ea6..74672539ee7b 100644
+--- a/drivers/video/fbdev/metronomefb.c
++++ b/drivers/video/fbdev/metronomefb.c
+@@ -469,12 +469,13 @@ static void metronomefb_dpy_deferred_io(struct fb_info *info,
+                               struct list_head *pagelist)
+ {
+       u16 cksum;
+-      struct page *cur;
++      struct fb_deferred_io_pageref *pageref;
+       struct fb_deferred_io *fbdefio = info->fbdefio;
+       struct metronomefb_par *par = info->par;
+       /* walk the written page list and swizzle the data */
+-      list_for_each_entry(cur, &fbdefio->pagelist, lru) {
++      list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++              struct page *cur = pageref->page;
+               cksum = metronomefb_dpy_update_page(par,
+                                       (cur->index << PAGE_SHIFT));
+               par->metromem_img_csum -= par->csum_table[cur->index];
+diff --git a/drivers/video/fbdev/sh_mobile_lcdcfb.c b/drivers/video/fbdev/sh_mobile_lcdcfb.c
+index aa4ebe3192ec..b6b5ce3505c0 100644
+--- a/drivers/video/fbdev/sh_mobile_lcdcfb.c
++++ b/drivers/video/fbdev/sh_mobile_lcdcfb.c
+@@ -440,13 +440,15 @@ static int sh_mobile_lcdc_sginit(struct fb_info *info,
+ {
+       struct sh_mobile_lcdc_chan *ch = info->par;
+       unsigned int nr_pages_max = ch->fb_size >> PAGE_SHIFT;
+-      struct page *page;
++      struct fb_deferred_io_pageref *pageref;
+       int nr_pages = 0;
+       sg_init_table(ch->sglist, nr_pages_max);
+-      list_for_each_entry(page, pagelist, lru)
++      list_for_each_entry(pageref, pagelist, list) {
++              struct page *page = pageref->page;
+               sg_set_page(&ch->sglist[nr_pages++], page, PAGE_SIZE, 0);
++      }
+       return nr_pages;
+ }
+diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c
+index b3295cd7fd4f..bf3d151abd6f 100644
+--- a/drivers/video/fbdev/smscufx.c
++++ b/drivers/video/fbdev/smscufx.c
+@@ -956,7 +956,7 @@ static void ufx_ops_fillrect(struct fb_info *info,
+ static void ufx_dpy_deferred_io(struct fb_info *info,
+                               struct list_head *pagelist)
+ {
+-      struct page *cur;
++      struct fb_deferred_io_pageref *pageref;
+       struct fb_deferred_io *fbdefio = info->fbdefio;
+       struct ufx_data *dev = info->par;
+@@ -967,9 +967,10 @@ static void ufx_dpy_deferred_io(struct fb_info *info,
+               return;
+       /* walk the written page list and render each to device */
+-      list_for_each_entry(cur, &fbdefio->pagelist, lru) {
++      list_for_each_entry(pageref, &fbdefio->pagelist, list) {
+               /* create a rectangle of full screen width that encloses the
+                * entire dirty framebuffer page */
++              struct page *cur = pageref->page;
+               const int x = 0;
+               const int width = dev->info->var.xres;
+               const int y = (cur->index << PAGE_SHIFT) / (width * 2);
+diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c
+index 8603898bf37e..c187163fe580 100644
+--- a/drivers/video/fbdev/udlfb.c
++++ b/drivers/video/fbdev/udlfb.c
+@@ -783,7 +783,7 @@ static void dlfb_ops_fillrect(struct fb_info *info,
+ static void dlfb_dpy_deferred_io(struct fb_info *info,
+                               struct list_head *pagelist)
+ {
+-      struct page *cur;
++      struct fb_deferred_io_pageref *pageref;
+       struct fb_deferred_io *fbdefio = info->fbdefio;
+       struct dlfb_data *dlfb = info->par;
+       struct urb *urb;
+@@ -810,7 +810,8 @@ static void dlfb_dpy_deferred_io(struct fb_info *info,
+       cmd = urb->transfer_buffer;
+       /* walk the written page list and render each to device */
+-      list_for_each_entry(cur, &fbdefio->pagelist, lru) {
++      list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++              struct page *cur = pageref->page;
+               if (dlfb_render_hline(dlfb, &urb, (char *) info->fix.smem_start,
+                                 &cmd, cur->index << PAGE_SHIFT,
+diff --git a/drivers/video/fbdev/xen-fbfront.c b/drivers/video/fbdev/xen-fbfront.c
+index 5ec51445bee8..00d9502ee25a 100644
+--- a/drivers/video/fbdev/xen-fbfront.c
++++ b/drivers/video/fbdev/xen-fbfront.c
+@@ -185,13 +185,14 @@ static void xenfb_deferred_io(struct fb_info *fb_info,
+                             struct list_head *pagelist)
+ {
+       struct xenfb_info *info = fb_info->par;
+-      struct page *page;
++      struct fb_deferred_io_pageref *pageref;
+       unsigned long beg, end;
+       int y1, y2, miny, maxy;
+       miny = INT_MAX;
+       maxy = 0;
+-      list_for_each_entry(page, pagelist, lru) {
++      list_for_each_entry(pageref, pagelist, list) {
++              struct page *page = pageref->page;
+               beg = page->index << PAGE_SHIFT;
+               end = beg + PAGE_SIZE - 1;
+               y1 = beg / fb_info->fix.line_length;
+diff --git a/include/linux/fb.h b/include/linux/fb.h
+index 9a77ab615c36..768de6534a82 100644
+--- a/include/linux/fb.h
++++ b/include/linux/fb.h
+@@ -201,6 +201,13 @@ struct fb_pixmap {
+ };
+ #ifdef CONFIG_FB_DEFERRED_IO
++struct fb_deferred_io_pageref {
++      struct page *page;
++      unsigned long offset;
++      /* private */
++      struct list_head list;
++};
++
+ struct fb_deferred_io {
+       /* delay between mkwrite and deferred handler */
+       unsigned long delay;
+@@ -469,6 +476,8 @@ struct fb_info {
+ #endif
+ #ifdef CONFIG_FB_DEFERRED_IO
+       struct delayed_work deferred_work;
++      unsigned long npagerefs;
++      struct fb_deferred_io_pageref *pagerefs;
+       struct fb_deferred_io *fbdefio;
+ #endif
+@@ -662,7 +671,7 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
+ /* drivers/video/fb_defio.c */
+ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma);
+-extern void fb_deferred_io_init(struct fb_info *info);
++extern int  fb_deferred_io_init(struct fb_info *info);
+ extern void fb_deferred_io_open(struct fb_info *info,
+                               struct inode *inode,
+                               struct file *file);
+-- 
+2.43.0
+
diff --git a/queue-5.15/mwifiex-select-firmware-based-on-strapping.patch b/queue-5.15/mwifiex-select-firmware-based-on-strapping.patch
new file mode 100644 (file)
index 0000000..a7fdc1e
--- /dev/null
@@ -0,0 +1,132 @@
+From 7e80290871316bb942dbb58af4881108959ec77f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 Apr 2022 11:03:12 +0200
+Subject: mwifiex: Select firmware based on strapping
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Andrejs Cainikovs <andrejs.cainikovs@toradex.com>
+
+[ Upstream commit 255ca28a659d3cfb069f73c7644853ed93aecdb0 ]
+
+Some WiFi/Bluetooth modules might have different host connection
+options, allowing to either use SDIO for both WiFi and Bluetooth,
+or SDIO for WiFi and UART for Bluetooth. It is possible to detect
+whether a module has SDIO-SDIO or SDIO-UART connection by reading
+its host strap register.
+
+This change introduces a way to automatically select appropriate
+firmware depending of the connection method, and removes a need
+of symlinking or overwriting the original firmware file with a
+required one.
+
+Host strap register used in this commit comes from the NXP driver [1]
+hosted at Code Aurora.
+
+[1] https://source.codeaurora.org/external/imx/linux-imx/tree/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_sdio_mmc.c?h=rel_imx_5.4.70_2.3.2&id=688b67b2c7220b01521ffe560da7eee33042c7bd#n1274
+
+Signed-off-by: Andrejs Cainikovs <andrejs.cainikovs@toradex.com>
+Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
+Signed-off-by: Kalle Valo <kvalo@kernel.org>
+Link: https://lore.kernel.org/r/20220422090313.125857-2-andrejs.cainikovs@toradex.com
+Stable-dep-of: 1c5d463c0770 ("wifi: mwifiex: add extra delay for firmware ready")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/marvell/mwifiex/sdio.c | 21 ++++++++++++++++++++-
+ drivers/net/wireless/marvell/mwifiex/sdio.h |  5 +++++
+ 2 files changed, 25 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
+index b09e60fedeb1..016065a56e6c 100644
+--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
++++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
+@@ -182,6 +182,9 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8997 = {
+       .host_int_rsr_reg = 0x4,
+       .host_int_status_reg = 0x0C,
+       .host_int_mask_reg = 0x08,
++      .host_strap_reg = 0xF4,
++      .host_strap_mask = 0x01,
++      .host_strap_value = 0x00,
+       .status_reg_0 = 0xE8,
+       .status_reg_1 = 0xE9,
+       .sdio_int_mask = 0xff,
+@@ -283,6 +286,9 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8987 = {
+       .host_int_rsr_reg = 0x4,
+       .host_int_status_reg = 0x0C,
+       .host_int_mask_reg = 0x08,
++      .host_strap_reg = 0xF4,
++      .host_strap_mask = 0x01,
++      .host_strap_value = 0x00,
+       .status_reg_0 = 0xE8,
+       .status_reg_1 = 0xE9,
+       .sdio_int_mask = 0xff,
+@@ -537,6 +543,7 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
+               struct mwifiex_sdio_device *data = (void *)id->driver_data;
+               card->firmware = data->firmware;
++              card->firmware_sdiouart = data->firmware_sdiouart;
+               card->reg = data->reg;
+               card->max_ports = data->max_ports;
+               card->mp_agg_pkt_limit = data->mp_agg_pkt_limit;
+@@ -2440,6 +2447,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
+       int ret;
+       struct sdio_mmc_card *card = adapter->card;
+       struct sdio_func *func = card->func;
++      const char *firmware = card->firmware;
+       /* save adapter pointer in card */
+       card->adapter = adapter;
+@@ -2456,7 +2464,18 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
+               return ret;
+       }
+-      strcpy(adapter->fw_name, card->firmware);
++      /* Select correct firmware (sdsd or sdiouart) firmware based on the strapping
++       * option
++       */
++      if (card->firmware_sdiouart) {
++              u8 val;
++
++              mwifiex_read_reg(adapter, card->reg->host_strap_reg, &val);
++              if ((val & card->reg->host_strap_mask) == card->reg->host_strap_value)
++                      firmware = card->firmware_sdiouart;
++      }
++      strcpy(adapter->fw_name, firmware);
++
+       if (card->fw_dump_enh) {
+               adapter->mem_type_mapping_tbl = generic_mem_type_map;
+               adapter->num_mem_types = 1;
+diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.h b/drivers/net/wireless/marvell/mwifiex/sdio.h
+index 5648512c9300..ad2c28cbb630 100644
+--- a/drivers/net/wireless/marvell/mwifiex/sdio.h
++++ b/drivers/net/wireless/marvell/mwifiex/sdio.h
+@@ -196,6 +196,9 @@ struct mwifiex_sdio_card_reg {
+       u8 host_int_rsr_reg;
+       u8 host_int_status_reg;
+       u8 host_int_mask_reg;
++      u8 host_strap_reg;
++      u8 host_strap_mask;
++      u8 host_strap_value;
+       u8 status_reg_0;
+       u8 status_reg_1;
+       u8 sdio_int_mask;
+@@ -241,6 +244,7 @@ struct sdio_mmc_card {
+       struct completion fw_done;
+       const char *firmware;
++      const char *firmware_sdiouart;
+       const struct mwifiex_sdio_card_reg *reg;
+       u8 max_ports;
+       u8 mp_agg_pkt_limit;
+@@ -274,6 +278,7 @@ struct sdio_mmc_card {
+ struct mwifiex_sdio_device {
+       const char *firmware;
++      const char *firmware_sdiouart;
+       const struct mwifiex_sdio_card_reg *reg;
+       u8 max_ports;
+       u8 mp_agg_pkt_limit;
+-- 
+2.43.0
+
diff --git a/queue-5.15/scripts-decode_stacktrace-demangle-rust-symbols.patch b/queue-5.15/scripts-decode_stacktrace-demangle-rust-symbols.patch
new file mode 100644 (file)
index 0000000..c1b6ba1
--- /dev/null
@@ -0,0 +1,60 @@
+From 701fdb2fff3ddaf22147d8d0b2032d4b8322efcf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 5 Dec 2021 19:00:43 +0100
+Subject: scripts: decode_stacktrace: demangle Rust symbols
+
+From: Miguel Ojeda <ojeda@kernel.org>
+
+[ Upstream commit 99115db4ecc87af73415939439ec604ea0531e6f ]
+
+Recent versions of both Binutils (`c++filt`) and LLVM (`llvm-cxxfilt`)
+provide Rust v0 mangling support.
+
+Reviewed-by: Kees Cook <keescook@chromium.org>
+Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Co-developed-by: Alex Gaynor <alex.gaynor@gmail.com>
+Signed-off-by: Alex Gaynor <alex.gaynor@gmail.com>
+Co-developed-by: Wedson Almeida Filho <wedsonaf@google.com>
+Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
+Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
+Stable-dep-of: efbd63983533 ("scripts/decode_stacktrace.sh: optionally use LLVM utilities")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ scripts/decode_stacktrace.sh | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh
+index 7075e26ab2c4..564c5632e1a2 100755
+--- a/scripts/decode_stacktrace.sh
++++ b/scripts/decode_stacktrace.sh
+@@ -8,6 +8,14 @@ usage() {
+       echo "  $0 -r <release> | <vmlinux> [<base path>|auto] [<modules path>]"
+ }
++# Try to find a Rust demangler
++if type llvm-cxxfilt >/dev/null 2>&1 ; then
++      cppfilt=llvm-cxxfilt
++elif type c++filt >/dev/null 2>&1 ; then
++      cppfilt=c++filt
++      cppfilt_opts=-i
++fi
++
+ if [[ $1 == "-r" ]] ; then
+       vmlinux=""
+       basepath="auto"
+@@ -180,6 +188,12 @@ parse_symbol() {
+       # In the case of inlines, move everything to same line
+       code=${code//$'\n'/' '}
++      # Demangle if the name looks like a Rust symbol and if
++      # we got a Rust demangler
++      if [[ $name =~ ^_R && $cppfilt != "" ]] ; then
++              name=$("$cppfilt" "$cppfilt_opts" "$name")
++      fi
++
+       # Replace old address with pretty line numbers
+       symbol="$segment$name ($code)"
+ }
+-- 
+2.43.0
+
diff --git a/queue-5.15/scripts-decode_stacktrace.sh-optionally-use-llvm-uti.patch b/queue-5.15/scripts-decode_stacktrace.sh-optionally-use-llvm-uti.patch
new file mode 100644 (file)
index 0000000..f6db1ef
--- /dev/null
@@ -0,0 +1,102 @@
+From 48d25a8f063530bb98041d6266c3e01040e40e48 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 Sep 2023 03:48:17 +0000
+Subject: scripts/decode_stacktrace.sh: optionally use LLVM utilities
+
+From: Carlos Llamas <cmllamas@google.com>
+
+[ Upstream commit efbd6398353315b7018e6943e41fee9ec35e875f ]
+
+GNU's addr2line can have problems parsing a vmlinux built with LLVM,
+particularly when LTO was used.  In order to decode the traces correctly
+this patch adds the ability to switch to LLVM's utilities readelf and
+addr2line.  The same approach is followed by Will in [1].
+
+Before:
+  $ scripts/decode_stacktrace.sh vmlinux < kernel.log
+  [17716.240635] Call trace:
+  [17716.240646] skb_cow_data (??:?)
+  [17716.240654] esp6_input (ld-temp.o:?)
+  [17716.240666] xfrm_input (ld-temp.o:?)
+  [17716.240674] xfrm6_rcv (??:?)
+  [...]
+
+After:
+  $ LLVM=1 scripts/decode_stacktrace.sh vmlinux < kernel.log
+  [17716.240635] Call trace:
+  [17716.240646] skb_cow_data (include/linux/skbuff.h:2172 net/core/skbuff.c:4503)
+  [17716.240654] esp6_input (net/ipv6/esp6.c:977)
+  [17716.240666] xfrm_input (net/xfrm/xfrm_input.c:659)
+  [17716.240674] xfrm6_rcv (net/ipv6/xfrm6_input.c:172)
+  [...]
+
+Note that one could set CROSS_COMPILE=llvm- instead to hack around this
+issue.  However, doing so can break the decodecode routine as it will
+force the selection of other LLVM utilities down the line e.g.  llvm-as.
+
+[1] https://lore.kernel.org/all/20230914131225.13415-3-will@kernel.org/
+
+Link: https://lkml.kernel.org/r/20230929034836.403735-1-cmllamas@google.com
+Signed-off-by: Carlos Llamas <cmllamas@google.com>
+Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>
+Reviewed-by: Elliot Berman <quic_eberman@quicinc.com>
+Tested-by: Justin Stitt <justinstitt@google.com>
+Cc: Will Deacon <will@kernel.org>
+Cc: John Stultz <jstultz@google.com>
+Cc: Masahiro Yamada <masahiroy@kernel.org>
+Cc: Nathan Chancellor <nathan@kernel.org>
+Cc: Tom Rix <trix@redhat.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ scripts/decode_stacktrace.sh | 19 +++++++++++++++++--
+ 1 file changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh
+index 564c5632e1a2..bfe5a4082d8e 100755
+--- a/scripts/decode_stacktrace.sh
++++ b/scripts/decode_stacktrace.sh
+@@ -16,6 +16,21 @@ elif type c++filt >/dev/null 2>&1 ; then
+       cppfilt_opts=-i
+ fi
++UTIL_SUFFIX=
++if [[ -z ${LLVM:-} ]]; then
++      UTIL_PREFIX=${CROSS_COMPILE:-}
++else
++      UTIL_PREFIX=llvm-
++      if [[ ${LLVM} == */ ]]; then
++              UTIL_PREFIX=${LLVM}${UTIL_PREFIX}
++      elif [[ ${LLVM} == -* ]]; then
++              UTIL_SUFFIX=${LLVM}
++      fi
++fi
++
++READELF=${UTIL_PREFIX}readelf${UTIL_SUFFIX}
++ADDR2LINE=${UTIL_PREFIX}addr2line${UTIL_SUFFIX}
++
+ if [[ $1 == "-r" ]] ; then
+       vmlinux=""
+       basepath="auto"
+@@ -75,7 +90,7 @@ find_module() {
+       if [[ "$modpath" != "" ]] ; then
+               for fn in $(find "$modpath" -name "${module//_/[-_]}.ko*") ; do
+-                      if readelf -WS "$fn" | grep -qwF .debug_line ; then
++                      if ${READELF} -WS "$fn" | grep -qwF .debug_line ; then
+                               echo $fn
+                               return
+                       fi
+@@ -169,7 +184,7 @@ parse_symbol() {
+       if [[ $aarray_support == true && "${cache[$module,$address]+isset}" == "isset" ]]; then
+               local code=${cache[$module,$address]}
+       else
+-              local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address" 2>/dev/null)
++              local code=$(${ADDR2LINE} -i -e "$objfile" "$address" 2>/dev/null)
+               if [[ $aarray_support == true ]]; then
+                       cache[$module,$address]=$code
+               fi
+-- 
+2.43.0
+
diff --git a/queue-5.15/scripts-decode_stacktrace.sh-support-old-bash-versio.patch b/queue-5.15/scripts-decode_stacktrace.sh-support-old-bash-versio.patch
new file mode 100644 (file)
index 0000000..34438d4
--- /dev/null
@@ -0,0 +1,108 @@
+From 073cdb347279ca86cf043c0fe12fd47856b09c9c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 Apr 2022 14:37:57 -0700
+Subject: scripts/decode_stacktrace.sh: support old bash version
+
+From: Schspa Shi <schspa@gmail.com>
+
+[ Upstream commit 3af8acf6aff2a98731522b52927429760f0b8006 ]
+
+Old bash version don't support associative array variables.  Avoid to use
+associative array variables to avoid error.
+
+Without this, old bash version will report error as fellowing
+[   15.954042] Kernel panic - not syncing: sysrq triggered crash
+[   15.955252] CPU: 1 PID: 167 Comm: sh Not tainted 5.18.0-rc1-00208-gb7d075db2fd5 #4
+[   15.956472] Hardware name: Hobot J5 Virtual development board (DT)
+[   15.957856] Call trace:
+./scripts/decode_stacktrace.sh: line 128: ,dump_backtrace: syntax error: operand expected (error token is ",dump_backtrace")
+
+Link: https://lkml.kernel.org/r/20220409180331.24047-1-schspa@gmail.com
+Signed-off-by: Schspa Shi <schspa@gmail.com>
+Cc: Stephen Boyd <swboyd@chromium.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Stable-dep-of: efbd63983533 ("scripts/decode_stacktrace.sh: optionally use LLVM utilities")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ scripts/decode_stacktrace.sh | 27 +++++++++++++++++++--------
+ 1 file changed, 19 insertions(+), 8 deletions(-)
+
+diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh
+index 5fbad61fe490..7075e26ab2c4 100755
+--- a/scripts/decode_stacktrace.sh
++++ b/scripts/decode_stacktrace.sh
+@@ -45,8 +45,13 @@ else
+       fi
+ fi
+-declare -A cache
+-declare -A modcache
++declare aarray_support=true
++declare -A cache 2>/dev/null
++if [[ $? != 0 ]]; then
++      aarray_support=false
++else
++      declare -A modcache
++fi
+ find_module() {
+       if [[ -n $debuginfod ]] ; then
+@@ -97,7 +102,7 @@ parse_symbol() {
+       if [[ $module == "" ]] ; then
+               local objfile=$vmlinux
+-      elif [[ "${modcache[$module]+isset}" == "isset" ]]; then
++      elif [[ $aarray_support == true && "${modcache[$module]+isset}" == "isset" ]]; then
+               local objfile=${modcache[$module]}
+       else
+               local objfile=$(find_module)
+@@ -105,7 +110,9 @@ parse_symbol() {
+                       echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2
+                       return
+               fi
+-              modcache[$module]=$objfile
++              if [[ $aarray_support == true ]]; then
++                      modcache[$module]=$objfile
++              fi
+       fi
+       # Remove the englobing parenthesis
+@@ -125,7 +132,7 @@ parse_symbol() {
+       # Use 'nm vmlinux' to figure out the base address of said symbol.
+       # It's actually faster to call it every time than to load it
+       # all into bash.
+-      if [[ "${cache[$module,$name]+isset}" == "isset" ]]; then
++      if [[ $aarray_support == true && "${cache[$module,$name]+isset}" == "isset" ]]; then
+               local base_addr=${cache[$module,$name]}
+       else
+               local base_addr=$(nm "$objfile" 2>/dev/null | awk '$3 == "'$name'" && ($2 == "t" || $2 == "T") {print $1; exit}')
+@@ -133,7 +140,9 @@ parse_symbol() {
+                       # address not found
+                       return
+               fi
+-              cache[$module,$name]="$base_addr"
++              if [[ $aarray_support == true ]]; then
++                      cache[$module,$name]="$base_addr"
++              fi
+       fi
+       # Let's start doing the math to get the exact address into the
+       # symbol. First, strip out the symbol total length.
+@@ -149,11 +158,13 @@ parse_symbol() {
+       # Pass it to addr2line to get filename and line number
+       # Could get more than one result
+-      if [[ "${cache[$module,$address]+isset}" == "isset" ]]; then
++      if [[ $aarray_support == true && "${cache[$module,$address]+isset}" == "isset" ]]; then
+               local code=${cache[$module,$address]}
+       else
+               local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address" 2>/dev/null)
+-              cache[$module,$address]=$code
++              if [[ $aarray_support == true ]]; then
++                      cache[$module,$address]=$code
++              fi
+       fi
+       # addr2line doesn't return a proper error code if it fails, so
+-- 
+2.43.0
+
diff --git a/queue-5.15/serial-8250_exar-fill-in-rs485_supported.patch b/queue-5.15/serial-8250_exar-fill-in-rs485_supported.patch
new file mode 100644 (file)
index 0000000..4764ce9
--- /dev/null
@@ -0,0 +1,81 @@
+From 56ebed83bab688a588edb8ea276033b3db38ed98 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 6 Jun 2022 13:04:04 +0300
+Subject: serial: 8250_exar: Fill in rs485_supported
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit 59c221f8e1269278161313048c71929c9950b2c4 ]
+
+Add information on supported serial_rs485 features.
+
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Link: https://lore.kernel.org/r/20220606100433.13793-8-ilpo.jarvinen@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 0c2a5f471ce5 ("serial: 8250_exar: Set missing rs485_supported flag")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/tty/serial/8250/8250_exar.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c
+index 0b1976ceb01f..24878d5a6a05 100644
+--- a/drivers/tty/serial/8250/8250_exar.c
++++ b/drivers/tty/serial/8250/8250_exar.c
+@@ -123,6 +123,7 @@ struct exar8250;
+ struct exar8250_platform {
+       int (*rs485_config)(struct uart_port *, struct serial_rs485 *);
++      const struct serial_rs485 *rs485_supported;
+       int (*register_gpio)(struct pci_dev *, struct uart_8250_port *);
+       void (*unregister_gpio)(struct uart_8250_port *);
+ };
+@@ -441,10 +442,15 @@ static int generic_rs485_config(struct uart_port *port,
+       return 0;
+ }
++static const struct serial_rs485 generic_rs485_supported = {
++      .flags = SER_RS485_ENABLED,
++};
++
+ static const struct exar8250_platform exar8250_default_platform = {
+       .register_gpio = xr17v35x_register_gpio,
+       .unregister_gpio = xr17v35x_unregister_gpio,
+       .rs485_config = generic_rs485_config,
++      .rs485_supported = &generic_rs485_supported,
+ };
+ static int iot2040_rs485_config(struct uart_port *port,
+@@ -480,6 +486,10 @@ static int iot2040_rs485_config(struct uart_port *port,
+       return generic_rs485_config(port, rs485);
+ }
++static const struct serial_rs485 iot2040_rs485_supported = {
++      .flags = SER_RS485_ENABLED | SER_RS485_RX_DURING_TX | SER_RS485_TERMINATE_BUS,
++};
++
+ static const struct property_entry iot2040_gpio_properties[] = {
+       PROPERTY_ENTRY_U32("exar,first-pin", 10),
+       PROPERTY_ENTRY_U32("ngpios", 1),
+@@ -508,6 +518,7 @@ static int iot2040_register_gpio(struct pci_dev *pcidev,
+ static const struct exar8250_platform iot2040_platform = {
+       .rs485_config = iot2040_rs485_config,
++      .rs485_supported = &iot2040_rs485_supported,
+       .register_gpio = iot2040_register_gpio,
+       .unregister_gpio = xr17v35x_unregister_gpio,
+ };
+@@ -550,6 +561,7 @@ pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev,
+       port->port.uartclk = baud * 16;
+       port->port.rs485_config = platform->rs485_config;
++      port->port.rs485_supported = platform->rs485_supported;
+       /*
+        * Setup the UART clock for the devices on expansion slot to
+-- 
+2.43.0
+
diff --git a/queue-5.15/serial-8250_exar-set-missing-rs485_supported-flag.patch b/queue-5.15/serial-8250_exar-set-missing-rs485_supported-flag.patch
new file mode 100644 (file)
index 0000000..3dcbfa3
--- /dev/null
@@ -0,0 +1,59 @@
+From fd145d84ec6dbe6802f1ccef24360a2325eb115a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 3 Jan 2024 07:18:18 +0100
+Subject: serial: 8250_exar: Set missing rs485_supported flag
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Lino Sanfilippo <l.sanfilippo@kunbus.com>
+
+[ Upstream commit 0c2a5f471ce58bca8f8ab5fcb911aff91eaaa5eb ]
+
+The UART supports an auto-RTS mode in which the RTS pin is automatically
+activated during transmission. So mark this mode as being supported even
+if RTS is not controlled by the driver but the UART.
+
+Also the serial core expects now at least one of both modes rts-on-send or
+rts-after-send to be supported. This is since during sanitization
+unsupported flags are deleted from a RS485 configuration set by userspace.
+However if the configuration ends up with both flags unset, the core prints
+a warning since it considers such a configuration invalid (see
+uart_sanitize_serial_rs485()).
+
+Cc:  <stable@vger.kernel.org>
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Lino Sanfilippo <l.sanfilippo@kunbus.com>
+Link: https://lore.kernel.org/r/20240103061818.564-8-l.sanfilippo@kunbus.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/tty/serial/8250/8250_exar.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c
+index 24878d5a6a05..ada9666f5988 100644
+--- a/drivers/tty/serial/8250/8250_exar.c
++++ b/drivers/tty/serial/8250/8250_exar.c
+@@ -443,7 +443,7 @@ static int generic_rs485_config(struct uart_port *port,
+ }
+ static const struct serial_rs485 generic_rs485_supported = {
+-      .flags = SER_RS485_ENABLED,
++      .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND,
+ };
+ static const struct exar8250_platform exar8250_default_platform = {
+@@ -487,7 +487,8 @@ static int iot2040_rs485_config(struct uart_port *port,
+ }
+ static const struct serial_rs485 iot2040_rs485_supported = {
+-      .flags = SER_RS485_ENABLED | SER_RS485_RX_DURING_TX | SER_RS485_TERMINATE_BUS,
++      .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |
++               SER_RS485_RX_DURING_TX | SER_RS485_TERMINATE_BUS,
+ };
+ static const struct property_entry iot2040_gpio_properties[] = {
+-- 
+2.43.0
+
index 52ed0540a7eb0a42e1500f7e41b935dcc7119d0c..fd3a61003201ad52d8c731eb58a13ff9b007d4d5 100644 (file)
@@ -434,3 +434,34 @@ can-j1939-fix-uaf-in-j1939_sk_match_filter-during-setsockopt-so_j1939_filter.pat
 pmdomain-core-move-the-unused-cleanup-to-a-_sync-initcall.patch
 tracing-inform-kmemleak-of-saved_cmdlines-allocation.patch
 af_unix-fix-task-hung-while-purging-oob_skb-in-gc.patch
+dma-buf-add-dma_fence_timestamp-helper.patch
+mwifiex-select-firmware-based-on-strapping.patch
+wifi-mwifiex-support-sd8978-chipset.patch
+wifi-mwifiex-add-extra-delay-for-firmware-ready.patch
+bus-moxtet-add-spi-device-table.patch
+wifi-mwifiex-fix-uninitialized-firmware_stat.patch
+crypto-lib-mpi-fix-unexpected-pointer-access-in-mpi_.patch
+usb-dwc3-gadget-wait-for-ep0-xfers-to-complete-durin.patch
+usb-dwc3-ep0-don-t-prepare-beyond-setup-stage.patch
+usb-dwc3-gadget-only-end-transfer-for-ep0-data-phase.patch
+usb-dwc3-gadget-delay-issuing-end-transfer.patch
+usb-dwc3-fix-ep0-handling-when-getting-reset-while-d.patch
+usb-dwc3-gadget-force-sending-delayed-status-during-.patch
+usb-dwc3-gadget-submit-endxfer-command-if-delayed-du.patch
+usb-dwc3-gadget-stall-and-restart-ep0-if-host-is-unr.patch
+usb-dwc3-gadget-refactor-ep0-forced-stall-restart-in.patch
+usb-dwc3-gadget-handle-ep0-request-dequeuing-properl.patch
+usb-dwc3-gadget-queue-pm-runtime-idle-on-disconnect-.patch
+serial-8250_exar-fill-in-rs485_supported.patch
+serial-8250_exar-set-missing-rs485_supported-flag.patch
+fbdev-defio-early-out-if-page-is-already-enlisted.patch
+fbdev-don-t-sort-deferred-i-o-pages-by-default.patch
+fbdev-defio-fix-the-pagelist-corruption.patch
+fbdev-track-deferred-i-o-pages-in-pageref-struct.patch
+fbdev-rename-pagelist-to-pagereflist-for-deferred-i-.patch
+fbdev-fix-invalid-page-access-after-closing-deferred.patch
+fbdev-fix-incorrect-page-mapping-clearance-at-fb_def.patch
+fbdev-flush-deferred-io-before-closing.patch
+scripts-decode_stacktrace.sh-support-old-bash-versio.patch
+scripts-decode_stacktrace-demangle-rust-symbols.patch
+scripts-decode_stacktrace.sh-optionally-use-llvm-uti.patch
diff --git a/queue-5.15/usb-dwc3-ep0-don-t-prepare-beyond-setup-stage.patch b/queue-5.15/usb-dwc3-ep0-don-t-prepare-beyond-setup-stage.patch
new file mode 100644 (file)
index 0000000..871baff
--- /dev/null
@@ -0,0 +1,89 @@
+From b99690d4610a25983636c8d393b94a2fe845fd0c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 Apr 2022 19:22:50 -0700
+Subject: usb: dwc3: ep0: Don't prepare beyond Setup stage
+
+From: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+
+[ Upstream commit c96683798e272366866a5c0ce3073c0b5a256db7 ]
+
+Since we can't guarantee that the host won't send new Setup packet
+before going through the device-initiated disconnect, don't prepare
+beyond the Setup stage and keep the device in EP0_SETUP_PHASE. This
+ensures that the device-initated disconnect sequence can go through
+gracefully. Note that the controller won't service the End Transfer
+command if it can't DMA out the Setup packet.
+
+Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Link: https://lore.kernel.org/r/6bacec56ecabb2c6e49a09cedfcac281fdc97de0.1650593829.git.Thinh.Nguyen@synopsys.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/ep0.c    |  2 +-
+ drivers/usb/dwc3/gadget.c | 29 +++++++++++++++++------------
+ 2 files changed, 18 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
+index 34cb8662e129..624de23782b5 100644
+--- a/drivers/usb/dwc3/ep0.c
++++ b/drivers/usb/dwc3/ep0.c
+@@ -816,7 +816,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
+       int ret = -EINVAL;
+       u32 len;
+-      if (!dwc->gadget_driver)
++      if (!dwc->gadget_driver || !dwc->connected)
+               goto out;
+       trace_dwc3_ctrl_req(ctrl);
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index 3a663d71d791..2afe6784f1df 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -2472,6 +2472,23 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
+       spin_lock_irqsave(&dwc->lock, flags);
+       dwc->connected = false;
++      /*
++       * Per databook, when we want to stop the gadget, if a control transfer
++       * is still in process, complete it and get the core into setup phase.
++       */
++      if (dwc->ep0state != EP0_SETUP_PHASE) {
++              int ret;
++
++              reinit_completion(&dwc->ep0_in_setup);
++
++              spin_unlock_irqrestore(&dwc->lock, flags);
++              ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
++                              msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
++              spin_lock_irqsave(&dwc->lock, flags);
++              if (ret == 0)
++                      dev_warn(dwc->dev, "timed out waiting for SETUP phase\n");
++      }
++
+       /*
+        * In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a
+        * Section 4.1.8 Table 4-7, it states that for a device-initiated
+@@ -2516,18 +2533,6 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
+       is_on = !!is_on;
+       dwc->softconnect = is_on;
+-      /*
+-       * Per databook, when we want to stop the gadget, if a control transfer
+-       * is still in process, complete it and get the core into setup phase.
+-       */
+-      if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) {
+-              reinit_completion(&dwc->ep0_in_setup);
+-
+-              ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
+-                              msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
+-              if (ret == 0)
+-                      dev_warn(dwc->dev, "timed out waiting for SETUP phase\n");
+-      }
+       /*
+        * Avoid issuing a runtime resume if the device is already in the
+-- 
+2.43.0
+
diff --git a/queue-5.15/usb-dwc3-fix-ep0-handling-when-getting-reset-while-d.patch b/queue-5.15/usb-dwc3-fix-ep0-handling-when-getting-reset-while-d.patch
new file mode 100644 (file)
index 0000000..933112e
--- /dev/null
@@ -0,0 +1,138 @@
+From 8f73ee63dc2cc5c4e2dd666f8aaa8451604fdac4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 4 May 2022 12:36:41 -0700
+Subject: usb: dwc3: Fix ep0 handling when getting reset while doing control
+ transfer
+
+From: Mayank Rana <quic_mrana@quicinc.com>
+
+[ Upstream commit 9d778f0c5f95ca5aa2ff628ea281978697e8d89b ]
+
+According to the databook ep0 should be in setup phase during reset.
+If host issues reset between control transfers, ep0 will be  in an
+invalid state. Fix this by issuing stall and restart on ep0 if it
+is not in setup phase.
+
+Also SW needs to complete pending control transfer and setup core for
+next setup stage as per data book. Hence check ep0 state during reset
+interrupt handling and make sure active transfers on ep0 out/in
+endpoint are stopped by queuing ENDXFER command for that endpoint and
+restart ep0 out again to receive next setup packet.
+
+Signed-off-by: Mayank Rana <quic_mrana@quicinc.com>
+Link: https://lore.kernel.org/r/1651693001-29891-1-git-send-email-quic_mrana@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/ep0.c    | 11 ++++++++---
+ drivers/usb/dwc3/gadget.c | 27 +++++++++++++++++++++++++--
+ drivers/usb/dwc3/gadget.h |  2 ++
+ 3 files changed, 35 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
+index 624de23782b5..f402c66039af 100644
+--- a/drivers/usb/dwc3/ep0.c
++++ b/drivers/usb/dwc3/ep0.c
+@@ -218,7 +218,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
+       return ret;
+ }
+-static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
++void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
+ {
+       struct dwc3_ep          *dep;
+@@ -1090,13 +1090,18 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
+       __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
+ }
+-static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
++void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
+ {
+       struct dwc3_gadget_ep_cmd_params params;
+       u32                     cmd;
+       int                     ret;
+-      if (!dep->resource_index)
++      /*
++       * For status/DATA OUT stage, TRB will be queued on ep0 out
++       * endpoint for which resource index is zero. Hence allow
++       * queuing ENDXFER command for ep0 out endpoint.
++       */
++      if (!dep->resource_index && dep->number)
+               return;
+       cmd = DWC3_DEPCMD_ENDTRANSFER;
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index 5cf2fb165a56..b978e42ace75 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -869,12 +869,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
+               reg |= DWC3_DALEPENA_EP(dep->number);
+               dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
++              dep->trb_dequeue = 0;
++              dep->trb_enqueue = 0;
++
+               if (usb_endpoint_xfer_control(desc))
+                       goto out;
+               /* Initialize the TRB ring */
+-              dep->trb_dequeue = 0;
+-              dep->trb_enqueue = 0;
+               memset(dep->trb_pool, 0,
+                      sizeof(struct dwc3_trb) * DWC3_TRB_NUM);
+@@ -2702,6 +2703,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
+       /* begin to receive SETUP packets */
+       dwc->ep0state = EP0_SETUP_PHASE;
++      dwc->ep0_bounced = false;
+       dwc->link_state = DWC3_LINK_STATE_SS_DIS;
+       dwc->delayed_status = false;
+       dwc3_ep0_out_start(dwc);
+@@ -3790,6 +3792,27 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
+       }
+       dwc3_reset_gadget(dwc);
++
++      /*
++       * From SNPS databook section 8.1.2, the EP0 should be in setup
++       * phase. So ensure that EP0 is in setup phase by issuing a stall
++       * and restart if EP0 is not in setup phase.
++       */
++      if (dwc->ep0state != EP0_SETUP_PHASE) {
++              unsigned int    dir;
++
++              dir = !!dwc->ep0_expect_in;
++              if (dwc->ep0state == EP0_DATA_PHASE)
++                      dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
++              else
++                      dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
++
++              dwc->eps[0]->trb_enqueue = 0;
++              dwc->eps[1]->trb_enqueue = 0;
++
++              dwc3_ep0_stall_and_restart(dwc);
++      }
++
+       /*
+        * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
+        * Section 4.1.2 Table 4-2, it states that during a USB reset, the SW
+diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
+index f763380e672e..55a56cf67d73 100644
+--- a/drivers/usb/dwc3/gadget.h
++++ b/drivers/usb/dwc3/gadget.h
+@@ -110,6 +110,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
+ void dwc3_ep0_interrupt(struct dwc3 *dwc,
+               const struct dwc3_event_depevt *event);
+ void dwc3_ep0_out_start(struct dwc3 *dwc);
++void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep);
++void dwc3_ep0_stall_and_restart(struct dwc3 *dwc);
+ int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
+ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
+ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
+-- 
+2.43.0
+
diff --git a/queue-5.15/usb-dwc3-gadget-delay-issuing-end-transfer.patch b/queue-5.15/usb-dwc3-gadget-delay-issuing-end-transfer.patch
new file mode 100644 (file)
index 0000000..ec00d7d
--- /dev/null
@@ -0,0 +1,67 @@
+From adce0573fd1d8834fc982a989489eabca25c066c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 Apr 2022 19:23:03 -0700
+Subject: usb: dwc3: gadget: Delay issuing End Transfer
+
+From: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+
+[ Upstream commit f66eef8fb8989a7193cafc3870f7c7b2b97f16cb ]
+
+If the controller hasn't DMA'ed the Setup data from its fifo, it won't
+process the End Transfer command. Polling for the command completion may
+block the driver from servicing the Setup phase and cause a timeout.
+Previously we only check and delay issuing End Transfer in the case of
+endpoint dequeue. Let's do that for all End Transfer scenarios.
+
+Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Link: https://lore.kernel.org/r/2fcf3b5d90068d549589a57a27a79f76c6769b04.1650593829.git.Thinh.Nguyen@synopsys.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/gadget.c | 22 ++++++++++++----------
+ 1 file changed, 12 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index 4d830ba7d824..5cf2fb165a56 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -2029,16 +2029,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
+               if (r == req) {
+                       struct dwc3_request *t;
+-                      /*
+-                       * If a Setup packet is received but yet to DMA out, the controller will
+-                       * not process the End Transfer command of any endpoint. Polling of its
+-                       * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
+-                       * timeout. Delay issuing the End Transfer command until the Setup TRB is
+-                       * prepared.
+-                       */
+-                      if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status)
+-                              dep->flags |= DWC3_EP_DELAY_STOP;
+-
+                       /* wait until it is processed */
+                       dwc3_stop_active_transfer(dep, true, true);
+@@ -3663,6 +3653,18 @@ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+           (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+               return;
++      /*
++       * If a Setup packet is received but yet to DMA out, the controller will
++       * not process the End Transfer command of any endpoint. Polling of its
++       * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
++       * timeout. Delay issuing the End Transfer command until the Setup TRB is
++       * prepared.
++       */
++      if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) {
++              dep->flags |= DWC3_EP_DELAY_STOP;
++              return;
++      }
++
+       /*
+        * NOTICE: We are violating what the Databook says about the
+        * EndTransfer command. Ideally we would _always_ wait for the
+-- 
+2.43.0
+
diff --git a/queue-5.15/usb-dwc3-gadget-force-sending-delayed-status-during-.patch b/queue-5.15/usb-dwc3-gadget-force-sending-delayed-status-during-.patch
new file mode 100644 (file)
index 0000000..32a23c0
--- /dev/null
@@ -0,0 +1,56 @@
+From c0f4a91e640fa3781409bd37104482ccc3924a88 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 17 Aug 2022 11:23:52 -0700
+Subject: usb: dwc3: gadget: Force sending delayed status during soft
+ disconnect
+
+From: Wesley Cheng <quic_wcheng@quicinc.com>
+
+[ Upstream commit e1ee843488d58099a89979627ef85d5bd6c5cacd ]
+
+If any function drivers request for a delayed status phase, this leads to a
+SETUP transfer timeout error, since the function may take longer to process
+the DATA stage.  This eventually results in end transfer timeouts, as there
+is a pending SETUP transaction.
+
+In addition, allow the DWC3_EP_DELAY_STOP to be set for if there is a
+delayed status requested.  Ocasionally, a host may abort the current SETUP
+transaction, by issuing a subsequent SETUP token.  In those situations, it
+would result in an endxfer timeout as well.
+
+Reviewed-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
+Link: https://lore.kernel.org/r/20220817182359.13550-3-quic_wcheng@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/gadget.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index b978e42ace75..ba1b2e70b351 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -2470,6 +2470,9 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
+       if (dwc->ep0state != EP0_SETUP_PHASE) {
+               int ret;
++              if (dwc->delayed_status)
++                      dwc3_ep0_send_delayed_status(dwc);
++
+               reinit_completion(&dwc->ep0_in_setup);
+               spin_unlock_irqrestore(&dwc->lock, flags);
+@@ -3662,7 +3665,7 @@ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+        * timeout. Delay issuing the End Transfer command until the Setup TRB is
+        * prepared.
+        */
+-      if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) {
++      if (dwc->ep0state != EP0_SETUP_PHASE) {
+               dep->flags |= DWC3_EP_DELAY_STOP;
+               return;
+       }
+-- 
+2.43.0
+
diff --git a/queue-5.15/usb-dwc3-gadget-handle-ep0-request-dequeuing-properl.patch b/queue-5.15/usb-dwc3-gadget-handle-ep0-request-dequeuing-properl.patch
new file mode 100644 (file)
index 0000000..726e64e
--- /dev/null
@@ -0,0 +1,73 @@
+From 0b4e713f7dab29713dfeb420e9db0223b89d849f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 Dec 2023 12:18:14 -0800
+Subject: usb: dwc3: gadget: Handle EP0 request dequeuing properly
+
+From: Wesley Cheng <quic_wcheng@quicinc.com>
+
+[ Upstream commit 730e12fbec53ab59dd807d981a204258a4cfb29a ]
+
+Current EP0 dequeue path will share the same as other EPs.  However, there
+are some special considerations that need to be made for EP0 transfers:
+
+  - EP0 transfers never transition into the started_list
+  - EP0 only has one active request at a time
+
+In case there is a vendor specific control message for a function over USB
+FFS, then there is no guarantee on the timeline which the DATA/STATUS stage
+is responded to.  While this occurs, any attempt to end transfers on
+non-control EPs will end up having the DWC3_EP_DELAY_STOP flag set, and
+defer issuing of the end transfer command.  If the USB FFS application
+decides to timeout the control transfer, or if USB FFS AIO path exits, the
+USB FFS driver will issue a call to usb_ep_dequeue() for the ep0 request.
+
+In case of the AIO exit path, the AIO FS blocks until all pending USB
+requests utilizing the AIO path is completed.  However, since the dequeue
+of ep0 req does not happen properly, all non-control EPs with the
+DWC3_EP_DELAY_STOP flag set will not be handled, and the AIO exit path will
+be stuck waiting for the USB FFS data endpoints to receive a completion
+callback.
+
+Fix is to utilize dwc3_ep0_reset_state() in the dequeue API to ensure EP0
+is brought back to the SETUP state, and ensures that any deferred end
+transfer commands are handled.  This also will end any active transfers
+on EP0, compared to the previous implementation which directly called
+giveback only.
+
+Fixes: fcd2def66392 ("usb: dwc3: gadget: Refactor dwc3_gadget_ep_dequeue")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
+Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Link: https://lore.kernel.org/r/20231206201814.32664-1-quic_wcheng@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/gadget.c | 12 +++++++++++-
+ 1 file changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index 7704e2444b4b..d472dab16889 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -2039,7 +2039,17 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
+       list_for_each_entry(r, &dep->pending_list, list) {
+               if (r == req) {
+-                      dwc3_gadget_giveback(dep, req, -ECONNRESET);
++                      /*
++                       * Explicitly check for EP0/1 as dequeue for those
++                       * EPs need to be handled differently.  Control EP
++                       * only deals with one USB req, and giveback will
++                       * occur during dwc3_ep0_stall_and_restart().  EP0
++                       * requests are never added to started_list.
++                       */
++                      if (dep->number > 1)
++                              dwc3_gadget_giveback(dep, req, -ECONNRESET);
++                      else
++                              dwc3_ep0_reset_state(dwc);
+                       goto out;
+               }
+       }
+-- 
+2.43.0
+
diff --git a/queue-5.15/usb-dwc3-gadget-only-end-transfer-for-ep0-data-phase.patch b/queue-5.15/usb-dwc3-gadget-only-end-transfer-for-ep0-data-phase.patch
new file mode 100644 (file)
index 0000000..02452e7
--- /dev/null
@@ -0,0 +1,53 @@
+From b54260b8b5b4a85bbd1a4bc2846273d02f85522c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 Apr 2022 19:22:57 -0700
+Subject: usb: dwc3: gadget: Only End Transfer for ep0 data phase
+
+From: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+
+[ Upstream commit ace17b6ee4f92ab0375d12a1b42494f8590a96b6 ]
+
+The driver shouldn't be able to issue End Transfer to the control
+endpoint at anytime. Typically we should only do so in error cases such
+as invalid/unexpected direction of Data Phase as described in the
+control transfer flow of the programming guide. It _may_ end started
+data phase during controller deinitialization from soft disconnect or
+driver removal. However, that should not happen because the driver
+should be maintained in EP0_SETUP_PHASE during driver tear-down. On
+soft-connect, the controller should be reset from a soft-reset and there
+should be no issue starting the control endpoint.
+
+Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Link: https://lore.kernel.org/r/3c6643678863a26702e4115e9e19d7d94a30d49c.1650593829.git.Thinh.Nguyen@synopsys.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/gadget.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index 2afe6784f1df..4d830ba7d824 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -3647,6 +3647,17 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
+ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+       bool interrupt)
+ {
++      struct dwc3 *dwc = dep->dwc;
++
++      /*
++       * Only issue End Transfer command to the control endpoint of a started
++       * Data Phase. Typically we should only do so in error cases such as
++       * invalid/unexpected direction as described in the control transfer
++       * flow of the programming guide.
++       */
++      if (dep->number <= 1 && dwc->ep0state != EP0_DATA_PHASE)
++              return;
++
+       if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
+           (dep->flags & DWC3_EP_DELAY_STOP) ||
+           (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+-- 
+2.43.0
+
diff --git a/queue-5.15/usb-dwc3-gadget-queue-pm-runtime-idle-on-disconnect-.patch b/queue-5.15/usb-dwc3-gadget-queue-pm-runtime-idle-on-disconnect-.patch
new file mode 100644 (file)
index 0000000..4854934
--- /dev/null
@@ -0,0 +1,62 @@
+From cd27b42ef09f2d914b0dd9d43f36e03f8681b88c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 3 Jan 2024 13:49:46 -0800
+Subject: usb: dwc3: gadget: Queue PM runtime idle on disconnect event
+
+From: Wesley Cheng <quic_wcheng@quicinc.com>
+
+[ Upstream commit 3c7af52c7616c3aa6dacd2336ec748d4a65df8f4 ]
+
+There is a scenario where DWC3 runtime suspend is blocked due to the
+dwc->connected flag still being true while PM usage_count is zero after
+DWC3 giveback is completed and the USB gadget session is being terminated.
+This leads to a case where nothing schedules a PM runtime idle for the
+device.
+
+The exact condition is seen with the following sequence:
+  1.  USB bus reset is issued by the host
+  2.  Shortly after, or concurrently, a USB PD DR SWAP request is received
+      (sink->source)
+  3.  USB bus reset event handler runs and issues
+      dwc3_stop_active_transfers(), and pending transfer are stopped
+  4.  DWC3 usage_count decremented to 0, and runtime idle occurs while
+      dwc->connected == true, returns -EBUSY
+  5.  DWC3 disconnect event seen, dwc->connected set to false due to DR
+      swap handling
+  6.  No runtime idle after this point
+
+Address this by issuing an asynchronous PM runtime idle call after the
+disconnect event is completed, as it modifies the dwc->connected flag,
+which is what blocks the initial runtime idle.
+
+Fixes: fc8bb91bc83e ("usb: dwc3: implement runtime PM")
+Cc:  <stable@vger.kernel.org>
+Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
+Link: https://lore.kernel.org/r/20240103214946.2596-1-quic_wcheng@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/gadget.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index d472dab16889..8bd063f4eff2 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -3784,6 +3784,13 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
+       usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
+       dwc3_ep0_reset_state(dwc);
++
++      /*
++       * Request PM idle to address condition where usage count is
++       * already decremented to zero, but waiting for the disconnect
++       * interrupt to set dwc->connected to FALSE.
++       */
++      pm_request_idle(dwc->dev);
+ }
+ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
+-- 
+2.43.0
+
diff --git a/queue-5.15/usb-dwc3-gadget-refactor-ep0-forced-stall-restart-in.patch b/queue-5.15/usb-dwc3-gadget-refactor-ep0-forced-stall-restart-in.patch
new file mode 100644 (file)
index 0000000..787aec8
--- /dev/null
@@ -0,0 +1,114 @@
+From 92460099926c88af8a8237322a3e18180a4bf039 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 20 Apr 2023 14:27:59 -0700
+Subject: usb: dwc3: gadget: Refactor EP0 forced stall/restart into a separate
+ API
+
+From: Wesley Cheng <quic_wcheng@quicinc.com>
+
+[ Upstream commit 8f40fc0808137c157dd408d2632e63bfca2aecdb ]
+
+Several sequences utilize the same routine for forcing the control endpoint
+back into the SETUP phase.  This is required, because those operations need
+to ensure that EP0 is back in the default state.
+
+Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
+Link: https://lore.kernel.org/r/20230420212759.29429-3-quic_wcheng@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/gadget.c | 53 ++++++++++++++++-----------------------
+ 1 file changed, 21 insertions(+), 32 deletions(-)
+
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index a74ac44c6cd8..7704e2444b4b 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -139,6 +139,24 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
+       return -ETIMEDOUT;
+ }
++static void dwc3_ep0_reset_state(struct dwc3 *dwc)
++{
++      unsigned int    dir;
++
++      if (dwc->ep0state != EP0_SETUP_PHASE) {
++              dir = !!dwc->ep0_expect_in;
++              if (dwc->ep0state == EP0_DATA_PHASE)
++                      dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
++              else
++                      dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
++
++              dwc->eps[0]->trb_enqueue = 0;
++              dwc->eps[1]->trb_enqueue = 0;
++
++              dwc3_ep0_stall_and_restart(dwc);
++      }
++}
++
+ /**
+  * dwc3_ep_inc_trb - increment a trb index.
+  * @index: Pointer to the TRB index to increment.
+@@ -2495,16 +2513,9 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
+               ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
+                               msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
+               if (ret == 0) {
+-                      unsigned int    dir;
+-
+                       dev_warn(dwc->dev, "wait for SETUP phase timed out\n");
+                       spin_lock_irqsave(&dwc->lock, flags);
+-                      dir = !!dwc->ep0_expect_in;
+-                      if (dwc->ep0state == EP0_DATA_PHASE)
+-                              dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
+-                      else
+-                              dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
+-                      dwc3_ep0_stall_and_restart(dwc);
++                      dwc3_ep0_reset_state(dwc);
+                       spin_unlock_irqrestore(&dwc->lock, flags);
+               }
+       }
+@@ -3762,16 +3773,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
+       dwc->setup_packet_pending = false;
+       usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
+-      if (dwc->ep0state != EP0_SETUP_PHASE) {
+-              unsigned int    dir;
+-
+-              dir = !!dwc->ep0_expect_in;
+-              if (dwc->ep0state == EP0_DATA_PHASE)
+-                      dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
+-              else
+-                      dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
+-              dwc3_ep0_stall_and_restart(dwc);
+-      }
++      dwc3_ep0_reset_state(dwc);
+ }
+ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
+@@ -3827,20 +3829,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
+        * phase. So ensure that EP0 is in setup phase by issuing a stall
+        * and restart if EP0 is not in setup phase.
+        */
+-      if (dwc->ep0state != EP0_SETUP_PHASE) {
+-              unsigned int    dir;
+-
+-              dir = !!dwc->ep0_expect_in;
+-              if (dwc->ep0state == EP0_DATA_PHASE)
+-                      dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
+-              else
+-                      dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
+-
+-              dwc->eps[0]->trb_enqueue = 0;
+-              dwc->eps[1]->trb_enqueue = 0;
+-
+-              dwc3_ep0_stall_and_restart(dwc);
+-      }
++      dwc3_ep0_reset_state(dwc);
+       /*
+        * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
+-- 
+2.43.0
+
diff --git a/queue-5.15/usb-dwc3-gadget-stall-and-restart-ep0-if-host-is-unr.patch b/queue-5.15/usb-dwc3-gadget-stall-and-restart-ep0-if-host-is-unr.patch
new file mode 100644 (file)
index 0000000..15714d5
--- /dev/null
@@ -0,0 +1,128 @@
+From 620813673299aa87ddc65381506f42fb9fe1505b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 13 Apr 2023 12:57:40 -0700
+Subject: usb: dwc3: gadget: Stall and restart EP0 if host is unresponsive
+
+From: Wesley Cheng <quic_wcheng@quicinc.com>
+
+[ Upstream commit 02435a739b81ae24aff5d6e930efef9458e2af3c ]
+
+It was observed that there are hosts that may complete pending SETUP
+transactions before the stop active transfers and controller halt occurs,
+leading to lingering endxfer commands on DEPs on subsequent pullup/gadget
+start iterations.
+
+  dwc3_gadget_ep_disable   name=ep8in flags=0x3009  direction=1
+  dwc3_gadget_ep_disable   name=ep4in flags=1  direction=1
+  dwc3_gadget_ep_disable   name=ep3out flags=1  direction=0
+  usb_gadget_disconnect   deactivated=0  connected=0  ret=0
+
+The sequence shows that the USB gadget disconnect (dwc3_gadget_pullup(0))
+routine completed successfully, allowing for the USB gadget to proceed with
+a USB gadget connect.  However, if this occurs the system runs into an
+issue where:
+
+  BUG: spinlock already unlocked on CPU
+  spin_bug+0x0
+  dwc3_remove_requests+0x278
+  dwc3_ep0_out_start+0xb0
+  __dwc3_gadget_start+0x25c
+
+This is due to the pending endxfers, leading to gadget start (w/o lock
+held) to execute the remove requests, which will unlock the dwc3
+spinlock as part of giveback.
+
+To mitigate this, resolve the pending endxfers on the pullup disable
+path by re-locating the SETUP phase check after stop active transfers, since
+that is where the DWC3_EP_DELAY_STOP is potentially set.  This also allows
+for handling of a host that may be unresponsive by using the completion
+timeout to trigger the stall and restart for EP0.
+
+Fixes: c96683798e27 ("usb: dwc3: ep0: Don't prepare beyond Setup stage")
+Cc: stable@vger.kernel.org
+Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
+Link: https://lore.kernel.org/r/20230413195742.11821-2-quic_wcheng@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/gadget.c | 49 +++++++++++++++++++++++++--------------
+ 1 file changed, 32 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index c61b6f49701a..a74ac44c6cd8 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -2459,29 +2459,17 @@ static int __dwc3_gadget_start(struct dwc3 *dwc);
+ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
+ {
+       unsigned long flags;
++      int ret;
+       spin_lock_irqsave(&dwc->lock, flags);
+       dwc->connected = false;
+       /*
+-       * Per databook, when we want to stop the gadget, if a control transfer
+-       * is still in process, complete it and get the core into setup phase.
++       * Attempt to end pending SETUP status phase, and not wait for the
++       * function to do so.
+        */
+-      if (dwc->ep0state != EP0_SETUP_PHASE) {
+-              int ret;
+-
+-              if (dwc->delayed_status)
+-                      dwc3_ep0_send_delayed_status(dwc);
+-
+-              reinit_completion(&dwc->ep0_in_setup);
+-
+-              spin_unlock_irqrestore(&dwc->lock, flags);
+-              ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
+-                              msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
+-              spin_lock_irqsave(&dwc->lock, flags);
+-              if (ret == 0)
+-                      dev_warn(dwc->dev, "timed out waiting for SETUP phase\n");
+-      }
++      if (dwc->delayed_status)
++              dwc3_ep0_send_delayed_status(dwc);
+       /*
+        * In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a
+@@ -2494,6 +2482,33 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
+       __dwc3_gadget_stop(dwc);
+       spin_unlock_irqrestore(&dwc->lock, flags);
++      /*
++       * Per databook, when we want to stop the gadget, if a control transfer
++       * is still in process, complete it and get the core into setup phase.
++       * In case the host is unresponsive to a SETUP transaction, forcefully
++       * stall the transfer, and move back to the SETUP phase, so that any
++       * pending endxfers can be executed.
++       */
++      if (dwc->ep0state != EP0_SETUP_PHASE) {
++              reinit_completion(&dwc->ep0_in_setup);
++
++              ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
++                              msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
++              if (ret == 0) {
++                      unsigned int    dir;
++
++                      dev_warn(dwc->dev, "wait for SETUP phase timed out\n");
++                      spin_lock_irqsave(&dwc->lock, flags);
++                      dir = !!dwc->ep0_expect_in;
++                      if (dwc->ep0state == EP0_DATA_PHASE)
++                              dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
++                      else
++                              dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
++                      dwc3_ep0_stall_and_restart(dwc);
++                      spin_unlock_irqrestore(&dwc->lock, flags);
++              }
++      }
++
+       /*
+        * Note: if the GEVNTCOUNT indicates events in the event buffer, the
+        * driver needs to acknowledge them before the controller can halt.
+-- 
+2.43.0
+
diff --git a/queue-5.15/usb-dwc3-gadget-submit-endxfer-command-if-delayed-du.patch b/queue-5.15/usb-dwc3-gadget-submit-endxfer-command-if-delayed-du.patch
new file mode 100644 (file)
index 0000000..cee435e
--- /dev/null
@@ -0,0 +1,58 @@
+From bdce6e89daa1411beecbe033786d22487f003c9a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 1 Sep 2022 12:36:25 -0700
+Subject: usb: dwc3: gadget: Submit endxfer command if delayed during
+ disconnect
+
+From: Wesley Cheng <quic_wcheng@quicinc.com>
+
+[ Upstream commit 8422b769fa46bd429dc0f324012629a4691f0dd9 ]
+
+During a cable disconnect sequence, if ep0state is not in the SETUP phase,
+then nothing will trigger any pending end transfer commands.  Force
+stopping of any pending SETUP transaction, and move back to the SETUP
+phase.
+
+Reviewed-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
+Link: https://lore.kernel.org/r/20220901193625.8727-6-quic_wcheng@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/gadget.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index ba1b2e70b351..c61b6f49701a 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -3739,13 +3739,24 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
+       reg &= ~DWC3_DCTL_INITU2ENA;
+       dwc3_gadget_dctl_write_safe(dwc, reg);
++      dwc->connected = false;
++
+       dwc3_disconnect_gadget(dwc);
+       dwc->gadget->speed = USB_SPEED_UNKNOWN;
+       dwc->setup_packet_pending = false;
+       usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
+-      dwc->connected = false;
++      if (dwc->ep0state != EP0_SETUP_PHASE) {
++              unsigned int    dir;
++
++              dir = !!dwc->ep0_expect_in;
++              if (dwc->ep0state == EP0_DATA_PHASE)
++                      dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
++              else
++                      dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
++              dwc3_ep0_stall_and_restart(dwc);
++      }
+ }
+ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
+-- 
+2.43.0
+
diff --git a/queue-5.15/usb-dwc3-gadget-wait-for-ep0-xfers-to-complete-durin.patch b/queue-5.15/usb-dwc3-gadget-wait-for-ep0-xfers-to-complete-durin.patch
new file mode 100644 (file)
index 0000000..7bf46e9
--- /dev/null
@@ -0,0 +1,170 @@
+From d5523dbba024bb8a98d2a06c0553fc92f6e4a90f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 9 Mar 2022 12:54:02 -0800
+Subject: usb: dwc3: gadget: Wait for ep0 xfers to complete during dequeue
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+
+[ Upstream commit e4cf6580ac740f766dae26203bd6311d353dcd42 ]
+
+If a Setup packet is received but yet to DMA out, the controller will
+not process the End Transfer command of any endpoint. Polling of its
+DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
+command timeout.
+
+This may occur if the driver doesn’t service the completion interrupt of
+the control status stage yet due to system latency, then it won’t
+prepare TRB and start the transfer for the next Setup Stage. To the host
+side, the control transfer had completed, and the host can send a new
+Setup packet at this point.
+
+In the meanwhile, if the driver receives an async call to dequeue a
+request (triggering End Transfer) to any endpoint, then the driver will
+service that End transfer first, blocking the control status stage
+completion handler. Since no TRB is available for the Setup stage, the
+Setup packet can’t be DMA’ed out and the End Transfer gets hung.
+
+The driver must not block setting up of the Setup stage. So track and
+only issue the End Transfer command only when there’s Setup TRB prepared
+so that the controller can DMA out the Setup packet. Delay the End
+transfer command if there's no Setup TRB available. This is applicable to
+all DWC_usb3x IPs.
+
+Co-developed-by: Wesley Cheng <quic_wcheng@quicinc.com>
+Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
+Link: https://lore.kernel.org/r/20220309205402.4467-1-quic_wcheng@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/core.h   |  1 +
+ drivers/usb/dwc3/ep0.c    | 14 ++++++++++++++
+ drivers/usb/dwc3/gadget.c | 20 +++++++++++++++-----
+ drivers/usb/dwc3/gadget.h |  1 +
+ 4 files changed, 31 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
+index 3dcb5b744f7c..d64f7edc70c1 100644
+--- a/drivers/usb/dwc3/core.h
++++ b/drivers/usb/dwc3/core.h
+@@ -722,6 +722,7 @@ struct dwc3_ep {
+ #define DWC3_EP_FIRST_STREAM_PRIMED   BIT(10)
+ #define DWC3_EP_PENDING_CLEAR_STALL   BIT(11)
+ #define DWC3_EP_TXFIFO_RESIZED                BIT(12)
++#define DWC3_EP_DELAY_STOP             BIT(13)
+       /* This last one is specific to EP0 */
+ #define DWC3_EP0_DIR_IN               BIT(31)
+diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
+index 52f2bfae46bc..34cb8662e129 100644
+--- a/drivers/usb/dwc3/ep0.c
++++ b/drivers/usb/dwc3/ep0.c
+@@ -274,6 +274,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
+ {
+       struct dwc3_ep                  *dep;
+       int                             ret;
++      int                             i;
+       complete(&dwc->ep0_in_setup);
+@@ -282,6 +283,19 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
+                       DWC3_TRBCTL_CONTROL_SETUP, false);
+       ret = dwc3_ep0_start_trans(dep);
+       WARN_ON(ret < 0);
++      for (i = 2; i < DWC3_ENDPOINTS_NUM; i++) {
++              struct dwc3_ep *dwc3_ep;
++
++              dwc3_ep = dwc->eps[i];
++              if (!dwc3_ep)
++                      continue;
++
++              if (!(dwc3_ep->flags & DWC3_EP_DELAY_STOP))
++                      continue;
++
++              dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP;
++              dwc3_stop_active_transfer(dwc3_ep, true, true);
++      }
+ }
+ static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index 6188193e5ef4..3a663d71d791 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -641,9 +641,6 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
+       return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, &params);
+ }
+-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+-              bool interrupt);
+-
+ /**
+  * dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value
+  * @dwc: pointer to the DWC3 context
+@@ -1891,6 +1888,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
+        */
+       if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) ||
+           (dep->flags & DWC3_EP_WEDGE) ||
++          (dep->flags & DWC3_EP_DELAY_STOP) ||
+           (dep->flags & DWC3_EP_STALL)) {
+               dep->flags |= DWC3_EP_DELAY_START;
+               return 0;
+@@ -2031,6 +2029,16 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
+               if (r == req) {
+                       struct dwc3_request *t;
++                      /*
++                       * If a Setup packet is received but yet to DMA out, the controller will
++                       * not process the End Transfer command of any endpoint. Polling of its
++                       * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
++                       * timeout. Delay issuing the End Transfer command until the Setup TRB is
++                       * prepared.
++                       */
++                      if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status)
++                              dep->flags |= DWC3_EP_DELAY_STOP;
++
+                       /* wait until it is processed */
+                       dwc3_stop_active_transfer(dep, true, true);
+@@ -2114,7 +2122,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
+               list_for_each_entry_safe(req, tmp, &dep->started_list, list)
+                       dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED);
+-              if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
++              if (dep->flags & DWC3_EP_END_TRANSFER_PENDING ||
++                  (dep->flags & DWC3_EP_DELAY_STOP)) {
+                       dep->flags |= DWC3_EP_PENDING_CLEAR_STALL;
+                       return 0;
+               }
+@@ -3630,10 +3639,11 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
+       }
+ }
+-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
++void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+       bool interrupt)
+ {
+       if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
++          (dep->flags & DWC3_EP_DELAY_STOP) ||
+           (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+               return;
+diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
+index 77df4b6d6c13..f763380e672e 100644
+--- a/drivers/usb/dwc3/gadget.h
++++ b/drivers/usb/dwc3/gadget.h
+@@ -116,6 +116,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
+               gfp_t gfp_flags);
+ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
+ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc);
++void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt);
+ /**
+  * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
+-- 
+2.43.0
+
diff --git a/queue-5.15/wifi-mwifiex-add-extra-delay-for-firmware-ready.patch b/queue-5.15/wifi-mwifiex-add-extra-delay-for-firmware-ready.patch
new file mode 100644 (file)
index 0000000..4236177
--- /dev/null
@@ -0,0 +1,163 @@
+From 307f5bd1d45e51f1e0899ef19771b7ecb1b59f9c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 9 Dec 2023 07:40:29 +0800
+Subject: wifi: mwifiex: add extra delay for firmware ready
+
+From: David Lin <yu-hao.lin@nxp.com>
+
+[ Upstream commit 1c5d463c0770c6fa2037511a24fb17966fd07d97 ]
+
+For SDIO IW416, due to a bug, FW may return ready before complete full
+initialization. Command timeout may occur at driver load after reboot.
+Workaround by adding 100ms delay at checking FW status.
+
+Signed-off-by: David Lin <yu-hao.lin@nxp.com>
+Cc: stable@vger.kernel.org
+Reviewed-by: Francesco Dolcini <francesco.dolcini@toradex.com>
+Acked-by: Brian Norris <briannorris@chromium.org>
+Tested-by: Marcel Ziswiler <marcel.ziswiler@toradex.com> # Verdin AM62 (IW416)
+Signed-off-by: Kalle Valo <kvalo@kernel.org>
+Link: https://msgid.link/20231208234029.2197-1-yu-hao.lin@nxp.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/marvell/mwifiex/sdio.c | 19 +++++++++++++++++++
+ drivers/net/wireless/marvell/mwifiex/sdio.h |  2 ++
+ 2 files changed, 21 insertions(+)
+
+diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
+index 919f1bae61dc..dd4bfb7d71ee 100644
+--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
++++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
+@@ -343,6 +343,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = {
+       .can_dump_fw = false,
+       .can_auto_tdls = false,
+       .can_ext_scan = false,
++      .fw_ready_extra_delay = false,
+ };
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
+@@ -358,6 +359,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
+       .can_dump_fw = false,
+       .can_auto_tdls = false,
+       .can_ext_scan = true,
++      .fw_ready_extra_delay = false,
+ };
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
+@@ -373,6 +375,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
+       .can_dump_fw = false,
+       .can_auto_tdls = false,
+       .can_ext_scan = true,
++      .fw_ready_extra_delay = false,
+ };
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
+@@ -388,6 +391,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
+       .can_dump_fw = true,
+       .can_auto_tdls = false,
+       .can_ext_scan = true,
++      .fw_ready_extra_delay = false,
+ };
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8977 = {
+@@ -404,6 +408,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8977 = {
+       .fw_dump_enh = true,
+       .can_auto_tdls = false,
+       .can_ext_scan = true,
++      .fw_ready_extra_delay = false,
+ };
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8978 = {
+@@ -420,6 +425,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8978 = {
+       .fw_dump_enh = true,
+       .can_auto_tdls = false,
+       .can_ext_scan = true,
++      .fw_ready_extra_delay = true,
+ };
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8997 = {
+@@ -436,6 +442,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8997 = {
+       .fw_dump_enh = true,
+       .can_auto_tdls = false,
+       .can_ext_scan = true,
++      .fw_ready_extra_delay = false,
+ };
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = {
+@@ -451,6 +458,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = {
+       .can_dump_fw = false,
+       .can_auto_tdls = true,
+       .can_ext_scan = true,
++      .fw_ready_extra_delay = false,
+ };
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8987 = {
+@@ -467,6 +475,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8987 = {
+       .fw_dump_enh = true,
+       .can_auto_tdls = true,
+       .can_ext_scan = true,
++      .fw_ready_extra_delay = false,
+ };
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8801 = {
+@@ -482,6 +491,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8801 = {
+       .can_dump_fw = false,
+       .can_auto_tdls = false,
+       .can_ext_scan = true,
++      .fw_ready_extra_delay = false,
+ };
+ static struct memory_type_mapping generic_mem_type_map[] = {
+@@ -574,6 +584,7 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
+               card->fw_dump_enh = data->fw_dump_enh;
+               card->can_auto_tdls = data->can_auto_tdls;
+               card->can_ext_scan = data->can_ext_scan;
++              card->fw_ready_extra_delay = data->fw_ready_extra_delay;
+               INIT_WORK(&card->work, mwifiex_sdio_work);
+       }
+@@ -777,6 +788,7 @@ mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat)
+ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
+                                  u32 poll_num)
+ {
++      struct sdio_mmc_card *card = adapter->card;
+       int ret = 0;
+       u16 firmware_stat;
+       u32 tries;
+@@ -794,6 +806,13 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
+               ret = -1;
+       }
++      if (card->fw_ready_extra_delay &&
++          firmware_stat == FIRMWARE_READY_SDIO)
++              /* firmware might pretend to be ready, when it's not.
++               * Wait a little bit more as a workaround.
++               */
++              msleep(100);
++
+       return ret;
+ }
+diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.h b/drivers/net/wireless/marvell/mwifiex/sdio.h
+index e9a9de566cc8..8f11fed8eae9 100644
+--- a/drivers/net/wireless/marvell/mwifiex/sdio.h
++++ b/drivers/net/wireless/marvell/mwifiex/sdio.h
+@@ -269,6 +269,7 @@ struct sdio_mmc_card {
+       bool fw_dump_enh;
+       bool can_auto_tdls;
+       bool can_ext_scan;
++      bool fw_ready_extra_delay;
+       struct mwifiex_sdio_mpa_tx mpa_tx;
+       struct mwifiex_sdio_mpa_rx mpa_rx;
+@@ -292,6 +293,7 @@ struct mwifiex_sdio_device {
+       bool fw_dump_enh;
+       bool can_auto_tdls;
+       bool can_ext_scan;
++      bool fw_ready_extra_delay;
+ };
+ /*
+-- 
+2.43.0
+
diff --git a/queue-5.15/wifi-mwifiex-fix-uninitialized-firmware_stat.patch b/queue-5.15/wifi-mwifiex-fix-uninitialized-firmware_stat.patch
new file mode 100644 (file)
index 0000000..36198b9
--- /dev/null
@@ -0,0 +1,41 @@
+From e8342bec9354a4d2492c1f8dca1c564065906a33 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 Dec 2023 09:55:11 +0800
+Subject: wifi: mwifiex: fix uninitialized firmware_stat
+
+From: David Lin <yu-hao.lin@nxp.com>
+
+[ Upstream commit 3df95e265924ac898c1a38a0c01846dd0bd3b354 ]
+
+Variable firmware_stat is possible to be used without initialization.
+
+Signed-off-by: David Lin <yu-hao.lin@nxp.com>
+Fixes: 1c5d463c0770 ("wifi: mwifiex: add extra delay for firmware ready")
+Cc: stable@vger.kernel.org
+Reported-by: kernel test robot <lkp@intel.com>
+Reported-by: Dan Carpenter <error27@gmail.com>
+Closes: https://lore.kernel.org/r/202312192236.ZflaWYCw-lkp@intel.com/
+Acked-by: Brian Norris <briannorris@chromium.org>
+Signed-off-by: Kalle Valo <kvalo@kernel.org>
+Link: https://msgid.link/20231221015511.1032128-1-yu-hao.lin@nxp.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/marvell/mwifiex/sdio.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
+index dd4bfb7d71ee..45f46a445a6c 100644
+--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
++++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
+@@ -790,7 +790,7 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
+ {
+       struct sdio_mmc_card *card = adapter->card;
+       int ret = 0;
+-      u16 firmware_stat;
++      u16 firmware_stat = 0;
+       u32 tries;
+       for (tries = 0; tries < poll_num; tries++) {
+-- 
+2.43.0
+
diff --git a/queue-5.15/wifi-mwifiex-support-sd8978-chipset.patch b/queue-5.15/wifi-mwifiex-support-sd8978-chipset.patch
new file mode 100644 (file)
index 0000000..eea9eed
--- /dev/null
@@ -0,0 +1,177 @@
+From 444a1f89cee2117c4524c52fb57d2a70f40d89bc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 27 Jan 2023 15:02:00 +0100
+Subject: wifi: mwifiex: Support SD8978 chipset
+
+From: Lukas Wunner <lukas@wunner.de>
+
+[ Upstream commit bba047f15851c8b053221f1b276eb7682d59f755 ]
+
+The Marvell SD8978 (aka NXP IW416) uses identical registers as SD8987,
+so reuse the existing mwifiex_reg_sd8987 definition.
+
+Note that mwifiex_reg_sd8977 and mwifiex_reg_sd8997 are likewise
+identical, save for the fw_dump_ctrl register:  They define it as 0xf0
+whereas mwifiex_reg_sd8987 defines it as 0xf9.  I've verified that
+0xf9 is the correct value on SD8978.  NXP's out-of-tree driver uses
+0xf9 for all of them, so there's a chance that 0xf0 is not correct
+in the mwifiex_reg_sd8977 and mwifiex_reg_sd8997 definitions.  I cannot
+test that for lack of hardware, hence am leaving it as is.
+
+NXP has only released a firmware which runs Bluetooth over UART.
+Perhaps Bluetooth over SDIO is unsupported by this chipset.
+Consequently, only an "sdiouart" firmware image is referenced, not an
+alternative "sdsd" image.
+
+Signed-off-by: Lukas Wunner <lukas@wunner.de>
+Signed-off-by: Kalle Valo <kvalo@kernel.org>
+Link: https://lore.kernel.org/r/536b4f17a72ca460ad1b07045757043fb0778988.1674827105.git.lukas@wunner.de
+Stable-dep-of: 1c5d463c0770 ("wifi: mwifiex: add extra delay for firmware ready")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../bindings/net/wireless/marvell-8xxx.txt    |  4 ++-
+ drivers/net/wireless/marvell/mwifiex/Kconfig  |  5 ++--
+ drivers/net/wireless/marvell/mwifiex/sdio.c   | 25 +++++++++++++++++--
+ drivers/net/wireless/marvell/mwifiex/sdio.h   |  1 +
+ include/linux/mmc/sdio_ids.h                  |  1 +
+ 5 files changed, 31 insertions(+), 5 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/net/wireless/marvell-8xxx.txt b/Documentation/devicetree/bindings/net/wireless/marvell-8xxx.txt
+index 9bf9bbac16e2..cdc303caf5f4 100644
+--- a/Documentation/devicetree/bindings/net/wireless/marvell-8xxx.txt
++++ b/Documentation/devicetree/bindings/net/wireless/marvell-8xxx.txt
+@@ -1,4 +1,4 @@
+-Marvell 8787/8897/8997 (sd8787/sd8897/sd8997/pcie8997) SDIO/PCIE devices
++Marvell 8787/8897/8978/8997 (sd8787/sd8897/sd8978/sd8997/pcie8997) SDIO/PCIE devices
+ ------
+ This node provides properties for controlling the Marvell SDIO/PCIE wireless device.
+@@ -10,7 +10,9 @@ Required properties:
+   - compatible : should be one of the following:
+       * "marvell,sd8787"
+       * "marvell,sd8897"
++      * "marvell,sd8978"
+       * "marvell,sd8997"
++      * "nxp,iw416"
+       * "pci11ab,2b42"
+       * "pci1b4b,2b42"
+diff --git a/drivers/net/wireless/marvell/mwifiex/Kconfig b/drivers/net/wireless/marvell/mwifiex/Kconfig
+index 2b4ff2b78a7e..b182f7155d66 100644
+--- a/drivers/net/wireless/marvell/mwifiex/Kconfig
++++ b/drivers/net/wireless/marvell/mwifiex/Kconfig
+@@ -10,13 +10,14 @@ config MWIFIEX
+         mwifiex.
+ config MWIFIEX_SDIO
+-      tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8887/SD8897/SD8977/SD8987/SD8997"
++      tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8887/SD8897/SD8977/SD8978/SD8987/SD8997"
+       depends on MWIFIEX && MMC
+       select FW_LOADER
+       select WANT_DEV_COREDUMP
+       help
+         This adds support for wireless adapters based on Marvell
+-        8786/8787/8797/8887/8897/8977/8987/8997 chipsets with SDIO interface.
++        8786/8787/8797/8887/8897/8977/8978/8987/8997 chipsets with
++        SDIO interface. SD8978 is also known as NXP IW416.
+         If you choose to build it as a module, it will be called
+         mwifiex_sdio.
+diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
+index 016065a56e6c..919f1bae61dc 100644
+--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
++++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
+@@ -275,7 +275,7 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8887 = {
+                                0x68, 0x69, 0x6a},
+ };
+-static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8987 = {
++static const struct mwifiex_sdio_card_reg mwifiex_reg_sd89xx = {
+       .start_rd_port = 0,
+       .start_wr_port = 0,
+       .base_0_reg = 0xF8,
+@@ -406,6 +406,22 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8977 = {
+       .can_ext_scan = true,
+ };
++static const struct mwifiex_sdio_device mwifiex_sdio_sd8978 = {
++      .firmware_sdiouart = SD8978_SDIOUART_FW_NAME,
++      .reg = &mwifiex_reg_sd89xx,
++      .max_ports = 32,
++      .mp_agg_pkt_limit = 16,
++      .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
++      .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX,
++      .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX,
++      .supports_sdio_new_mode = true,
++      .has_control_mask = false,
++      .can_dump_fw = true,
++      .fw_dump_enh = true,
++      .can_auto_tdls = false,
++      .can_ext_scan = true,
++};
++
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8997 = {
+       .firmware = SD8997_DEFAULT_FW_NAME,
+       .reg = &mwifiex_reg_sd8997,
+@@ -439,7 +455,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = {
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8987 = {
+       .firmware = SD8987_DEFAULT_FW_NAME,
+-      .reg = &mwifiex_reg_sd8987,
++      .reg = &mwifiex_reg_sd89xx,
+       .max_ports = 32,
+       .mp_agg_pkt_limit = 16,
+       .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
+@@ -493,7 +509,9 @@ static struct memory_type_mapping mem_type_mapping_tbl[] = {
+ static const struct of_device_id mwifiex_sdio_of_match_table[] __maybe_unused = {
+       { .compatible = "marvell,sd8787" },
+       { .compatible = "marvell,sd8897" },
++      { .compatible = "marvell,sd8978" },
+       { .compatible = "marvell,sd8997" },
++      { .compatible = "nxp,iw416" },
+       { }
+ };
+@@ -931,6 +949,8 @@ static const struct sdio_device_id mwifiex_ids[] = {
+               .driver_data = (unsigned long)&mwifiex_sdio_sd8801},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8977_WLAN),
+               .driver_data = (unsigned long)&mwifiex_sdio_sd8977},
++      {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8978_WLAN),
++              .driver_data = (unsigned long)&mwifiex_sdio_sd8978},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8987_WLAN),
+               .driver_data = (unsigned long)&mwifiex_sdio_sd8987},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997_WLAN),
+@@ -3175,5 +3195,6 @@ MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME);
+ MODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME);
+ MODULE_FIRMWARE(SD8887_DEFAULT_FW_NAME);
+ MODULE_FIRMWARE(SD8977_DEFAULT_FW_NAME);
++MODULE_FIRMWARE(SD8978_SDIOUART_FW_NAME);
+ MODULE_FIRMWARE(SD8987_DEFAULT_FW_NAME);
+ MODULE_FIRMWARE(SD8997_DEFAULT_FW_NAME);
+diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.h b/drivers/net/wireless/marvell/mwifiex/sdio.h
+index ad2c28cbb630..e9a9de566cc8 100644
+--- a/drivers/net/wireless/marvell/mwifiex/sdio.h
++++ b/drivers/net/wireless/marvell/mwifiex/sdio.h
+@@ -37,6 +37,7 @@
+ #define SD8887_DEFAULT_FW_NAME "mrvl/sd8887_uapsta.bin"
+ #define SD8801_DEFAULT_FW_NAME "mrvl/sd8801_uapsta.bin"
+ #define SD8977_DEFAULT_FW_NAME "mrvl/sdsd8977_combo_v2.bin"
++#define SD8978_SDIOUART_FW_NAME "mrvl/sdiouartiw416_combo_v0.bin"
+ #define SD8987_DEFAULT_FW_NAME "mrvl/sd8987_uapsta.bin"
+ #define SD8997_DEFAULT_FW_NAME "mrvl/sdsd8997_combo_v4.bin"
+diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h
+index a85c9f0bd470..d1524c5e49a1 100644
+--- a/include/linux/mmc/sdio_ids.h
++++ b/include/linux/mmc/sdio_ids.h
+@@ -101,6 +101,7 @@
+ #define SDIO_DEVICE_ID_MARVELL_8977_BT                0x9146
+ #define SDIO_DEVICE_ID_MARVELL_8987_WLAN      0x9149
+ #define SDIO_DEVICE_ID_MARVELL_8987_BT                0x914a
++#define SDIO_DEVICE_ID_MARVELL_8978_WLAN      0x9159
+ #define SDIO_VENDOR_ID_MEDIATEK                       0x037a
+ #define SDIO_DEVICE_ID_MEDIATEK_MT7663                0x7663
+-- 
+2.43.0
+