From 3e7273c55ca8ac81b44a76376cb0c9fe188cfabd Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Mon, 19 Feb 2024 20:25:46 -0500 Subject: [PATCH] Fixes for 5.15 Signed-off-by: Sasha Levin --- ...us-moxtet-add-spi-device-table.patch-12211 | 52 ++ ...expected-pointer-access-in-mpi_.patch-5277 | 42 ++ ...add-dma_fence_timestamp-helper.patch-23638 | 292 +++++++++ ...ut-if-page-is-already-enlisted.patch-31235 | 83 +++ ...fio-fix-the-pagelist-corruption.patch-6707 | 93 +++ ...t-deferred-i-o-pages-by-default.patch-6824 | 187 ++++++ ...ge-mapping-clearance-at-fb_def.patch-30601 | 106 ++++ ...-access-after-closing-deferred.patch-12976 | 93 +++ ...ush-deferred-io-before-closing.patch-17074 | 51 ++ ...to-pagereflist-for-deferred-i-.patch-15544 | 524 ++++++++++++++++ ...red-i-o-pages-in-pageref-struct.patch-4580 | 565 ++++++++++++++++++ ...ct-firmware-based-on-strapping.patch-10303 | 132 ++++ ...acktrace-demangle-rust-symbols.patch-30753 | 60 ++ ...ace.sh-optionally-use-llvm-uti.patch-24681 | 102 ++++ ...race.sh-support-old-bash-versio.patch-6443 | 108 ++++ ...0_exar-fill-in-rs485_supported.patch-28198 | 81 +++ ...t-missing-rs485_supported-flag.patch-19915 | 59 ++ queue-5.15/series | 31 + ...n-t-prepare-beyond-setup-stage.patch-27319 | 89 +++ ...ing-when-getting-reset-while-d.patch-19133 | 138 +++++ ...get-delay-issuing-end-transfer.patch-16020 | 67 +++ ...sending-delayed-status-during-.patch-25697 | 56 ++ ...-ep0-request-dequeuing-properl.patch-14688 | 73 +++ ...nd-transfer-for-ep0-data-phase.patch-25635 | 53 ++ ...pm-runtime-idle-on-disconnect-.patch-19688 | 62 ++ ...or-ep0-forced-stall-restart-in.patch-17868 | 114 ++++ ...and-restart-ep0-if-host-is-unr.patch-18649 | 128 ++++ ...-endxfer-command-if-delayed-du.patch-28020 | 58 ++ ...or-ep0-xfers-to-complete-durin.patch-16924 | 170 ++++++ ...extra-delay-for-firmware-ready.patch-15130 | 163 +++++ ...ix-uninitialized-firmware_stat.patch-14283 | 41 ++ ...mwifiex-support-sd8978-chipset.patch-17202 | 177 ++++++ 32 files changed, 4050 insertions(+) create mode 100644 queue-5.15/bus-moxtet-add-spi-device-table.patch-12211 create mode 100644 queue-5.15/crypto-lib-mpi-fix-unexpected-pointer-access-in-mpi_.patch-5277 create mode 100644 queue-5.15/dma-buf-add-dma_fence_timestamp-helper.patch-23638 create mode 100644 queue-5.15/fbdev-defio-early-out-if-page-is-already-enlisted.patch-31235 create mode 100644 queue-5.15/fbdev-defio-fix-the-pagelist-corruption.patch-6707 create mode 100644 queue-5.15/fbdev-don-t-sort-deferred-i-o-pages-by-default.patch-6824 create mode 100644 queue-5.15/fbdev-fix-incorrect-page-mapping-clearance-at-fb_def.patch-30601 create mode 100644 queue-5.15/fbdev-fix-invalid-page-access-after-closing-deferred.patch-12976 create mode 100644 queue-5.15/fbdev-flush-deferred-io-before-closing.patch-17074 create mode 100644 queue-5.15/fbdev-rename-pagelist-to-pagereflist-for-deferred-i-.patch-15544 create mode 100644 queue-5.15/fbdev-track-deferred-i-o-pages-in-pageref-struct.patch-4580 create mode 100644 queue-5.15/mwifiex-select-firmware-based-on-strapping.patch-10303 create mode 100644 queue-5.15/scripts-decode_stacktrace-demangle-rust-symbols.patch-30753 create mode 100644 queue-5.15/scripts-decode_stacktrace.sh-optionally-use-llvm-uti.patch-24681 create mode 100644 queue-5.15/scripts-decode_stacktrace.sh-support-old-bash-versio.patch-6443 create mode 100644 queue-5.15/serial-8250_exar-fill-in-rs485_supported.patch-28198 create mode 100644 queue-5.15/serial-8250_exar-set-missing-rs485_supported-flag.patch-19915 create mode 100644 queue-5.15/usb-dwc3-ep0-don-t-prepare-beyond-setup-stage.patch-27319 create mode 100644 queue-5.15/usb-dwc3-fix-ep0-handling-when-getting-reset-while-d.patch-19133 create mode 100644 queue-5.15/usb-dwc3-gadget-delay-issuing-end-transfer.patch-16020 create mode 100644 queue-5.15/usb-dwc3-gadget-force-sending-delayed-status-during-.patch-25697 create mode 100644 queue-5.15/usb-dwc3-gadget-handle-ep0-request-dequeuing-properl.patch-14688 create mode 100644 queue-5.15/usb-dwc3-gadget-only-end-transfer-for-ep0-data-phase.patch-25635 create mode 100644 queue-5.15/usb-dwc3-gadget-queue-pm-runtime-idle-on-disconnect-.patch-19688 create mode 100644 queue-5.15/usb-dwc3-gadget-refactor-ep0-forced-stall-restart-in.patch-17868 create mode 100644 queue-5.15/usb-dwc3-gadget-stall-and-restart-ep0-if-host-is-unr.patch-18649 create mode 100644 queue-5.15/usb-dwc3-gadget-submit-endxfer-command-if-delayed-du.patch-28020 create mode 100644 queue-5.15/usb-dwc3-gadget-wait-for-ep0-xfers-to-complete-durin.patch-16924 create mode 100644 queue-5.15/wifi-mwifiex-add-extra-delay-for-firmware-ready.patch-15130 create mode 100644 queue-5.15/wifi-mwifiex-fix-uninitialized-firmware_stat.patch-14283 create mode 100644 queue-5.15/wifi-mwifiex-support-sd8978-chipset.patch-17202 diff --git a/queue-5.15/bus-moxtet-add-spi-device-table.patch-12211 b/queue-5.15/bus-moxtet-add-spi-device-table.patch-12211 new file mode 100644 index 00000000000..5c6fc59d363 --- /dev/null +++ b/queue-5.15/bus-moxtet-add-spi-device-table.patch-12211 @@ -0,0 +1,52 @@ +From 8e9cea52c07682a98800a53b4e5afa93e8289f70 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Cc: +Reviewed-by: Marek Behún +Signed-off-by: Gregory CLEMENT +Signed-off-by: Sasha Levin +--- + 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-5277 b/queue-5.15/crypto-lib-mpi-fix-unexpected-pointer-access-in-mpi_.patch-5277 new file mode 100644 index 00000000000..0b80b3824d9 --- /dev/null +++ b/queue-5.15/crypto-lib-mpi-fix-unexpected-pointer-access-in-mpi_.patch-5277 @@ -0,0 +1,42 @@ +From febe21946d85586d4f9af1925ed41627a6d099dd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 Dec 2023 11:08:34 +0800 +Subject: crypto: lib/mpi - Fix unexpected pointer access in mpi_ec_init + +From: Tianjia Zhang + +[ 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 +Signed-off-by: Herbert Xu +Signed-off-by: Sasha Levin +--- + 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-23638 b/queue-5.15/dma-buf-add-dma_fence_timestamp-helper.patch-23638 new file mode 100644 index 00000000000..eeb20bef1e2 --- /dev/null +++ b/queue-5.15/dma-buf-add-dma_fence_timestamp-helper.patch-23638 @@ -0,0 +1,292 @@ +From e87be7a3eccf156225b7d942d0d66503cdfb6450 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Signed-off-by: Christian König +Fixes: 1774baa64f93 ("drm/scheduler: Change scheduled fence track v2") +Reviewed-by: Alex Deucher +CC: stable@vger.kernel.org +Link: https://patchwork.freedesktop.org/patch/msgid/20230929104725.2358-1-christian.koenig@amd.com +Signed-off-by: Sasha Levin +--- + 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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* 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-31235 b/queue-5.15/fbdev-defio-early-out-if-page-is-already-enlisted.patch-31235 new file mode 100644 index 00000000000..afe2a2ec035 --- /dev/null +++ b/queue-5.15/fbdev-defio-early-out-if-page-is-already-enlisted.patch-31235 @@ -0,0 +1,83 @@ +From 8a33b0569f9410057e7bab59752b8b3714bda753 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 11 Feb 2022 10:46:39 +0100 +Subject: fbdev/defio: Early-out if page is already enlisted + +From: Thomas Zimmermann + +[ 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 +Acked-by: Sam Ravnborg +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 +--- + 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-6707 b/queue-5.15/fbdev-defio-fix-the-pagelist-corruption.patch-6707 new file mode 100644 index 00000000000..02b1a2005f8 --- /dev/null +++ b/queue-5.15/fbdev-defio-fix-the-pagelist-corruption.patch-6707 @@ -0,0 +1,93 @@ +From d74bedcc06ac6927a4c678e4bc57cf6862734297 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 18 Mar 2022 08:50:03 +0800 +Subject: fbdev: defio: fix the pagelist corruption + +From: Chuansheng Liu + +[ 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: + + 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 +Fixes: 105a940416fc ("fbdev/defio: Early-out if page is already enlisted") +Cc: Thomas Zimmermann +Cc: Geert Uytterhoeven +Reviewed-by: Javier Martinez Canillas +Reviewed-by: Thomas Zimmermann +Signed-off-by: Thomas Zimmermann +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 +--- + 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-6824 b/queue-5.15/fbdev-don-t-sort-deferred-i-o-pages-by-default.patch-6824 new file mode 100644 index 00000000000..25f1dfb3618 --- /dev/null +++ b/queue-5.15/fbdev-don-t-sort-deferred-i-o-pages-by-default.patch-6824 @@ -0,0 +1,187 @@ +From 27cb6ff6155d1aee9a23bedcc90905bf10d07f36 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 11 Feb 2022 10:46:40 +0100 +Subject: fbdev: Don't sort deferred-I/O pages by default + +From: Thomas Zimmermann + +[ 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 +Acked-by: Sam Ravnborg +Acked-by: Andy Shevchenko +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 +--- + 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-30601 b/queue-5.15/fbdev-fix-incorrect-page-mapping-clearance-at-fb_def.patch-30601 new file mode 100644 index 00000000000..0227a11e400 --- /dev/null +++ b/queue-5.15/fbdev-fix-incorrect-page-mapping-clearance-at-fb_def.patch-30601 @@ -0,0 +1,106 @@ +From a67b59a0fe2dacc469e88dc08601e15ae362002a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 8 Mar 2023 11:50:12 +0100 +Subject: fbdev: Fix incorrect page mapping clearance at + fb_deferred_io_release() + +From: Takashi Iwai + +[ 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: +Signed-off-by: Takashi Iwai +Reviewed-by: Patrik Jakobsson +Signed-off-by: Thomas Zimmermann +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 +--- + 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-12976 b/queue-5.15/fbdev-fix-invalid-page-access-after-closing-deferred.patch-12976 new file mode 100644 index 00000000000..a75a183df74 --- /dev/null +++ b/queue-5.15/fbdev-fix-invalid-page-access-after-closing-deferred.patch-12976 @@ -0,0 +1,93 @@ +From 00cfec7001b41fd83fb3736cd71f29d09329bae2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 29 Jan 2023 09:28:56 +0100 +Subject: fbdev: Fix invalid page access after closing deferred I/O devices + +From: Takashi Iwai + +[ 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 +Cc: +Signed-off-by: Takashi Iwai +Tested-by: Miko Larsson +Fixes: 56c134f7f1b5 ("fbdev: Track deferred-I/O pages in pageref struct") +Reviewed-by: Thomas Zimmermann +Signed-off-by: Thomas Zimmermann +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 +--- + 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-17074 b/queue-5.15/fbdev-flush-deferred-io-before-closing.patch-17074 new file mode 100644 index 00000000000..c217cf00c8c --- /dev/null +++ b/queue-5.15/fbdev-flush-deferred-io-before-closing.patch-17074 @@ -0,0 +1,51 @@ +From 9b669dc1dd0ed0ff85993f50a0eae94b2605be86 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 18 Dec 2023 10:57:31 +0100 +Subject: fbdev: flush deferred IO before closing + +From: Nam Cao + +[ 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 +Reviewed-by: Sebastian Andrzej Siewior +Signed-off-by: Helge Deller +Signed-off-by: Sasha Levin +--- + 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-15544 b/queue-5.15/fbdev-rename-pagelist-to-pagereflist-for-deferred-i-.patch-15544 new file mode 100644 index 00000000000..d13728e1778 --- /dev/null +++ b/queue-5.15/fbdev-rename-pagelist-to-pagereflist-for-deferred-i-.patch-15544 @@ -0,0 +1,524 @@ +From 6706769caf4a001397cc1c5dde6b8010fb77c24a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 Apr 2022 12:08:33 +0200 +Subject: fbdev: Rename pagelist to pagereflist for deferred I/O + +From: Thomas Zimmermann + +[ 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 +Signed-off-by: Thomas Zimmermann +Reviewed-by: Javier Martinez Canillas +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 +--- + 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-4580 b/queue-5.15/fbdev-track-deferred-i-o-pages-in-pageref-struct.patch-4580 new file mode 100644 index 00000000000..acc93da884e --- /dev/null +++ b/queue-5.15/fbdev-track-deferred-i-o-pages-in-pageref-struct.patch-4580 @@ -0,0 +1,565 @@ +From 1177a6fb2dee82cb4c03bc1068dbfb7ff9172408 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 Apr 2022 12:08:31 +0200 +Subject: fbdev: Track deferred-I/O pages in pageref struct + +From: Thomas Zimmermann + +[ 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 +Reviewed-by: Javier Martinez Canillas +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 +--- + 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-10303 b/queue-5.15/mwifiex-select-firmware-based-on-strapping.patch-10303 new file mode 100644 index 00000000000..a7fdc1e2083 --- /dev/null +++ b/queue-5.15/mwifiex-select-firmware-based-on-strapping.patch-10303 @@ -0,0 +1,132 @@ +From 7e80290871316bb942dbb58af4881108959ec77f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Reviewed-by: Alvin Å ipraga +Signed-off-by: Kalle Valo +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 +--- + 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-30753 b/queue-5.15/scripts-decode_stacktrace-demangle-rust-symbols.patch-30753 new file mode 100644 index 00000000000..c1b6ba1282e --- /dev/null +++ b/queue-5.15/scripts-decode_stacktrace-demangle-rust-symbols.patch-30753 @@ -0,0 +1,60 @@ +From 701fdb2fff3ddaf22147d8d0b2032d4b8322efcf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 5 Dec 2021 19:00:43 +0100 +Subject: scripts: decode_stacktrace: demangle Rust symbols + +From: Miguel Ojeda + +[ Upstream commit 99115db4ecc87af73415939439ec604ea0531e6f ] + +Recent versions of both Binutils (`c++filt`) and LLVM (`llvm-cxxfilt`) +provide Rust v0 mangling support. + +Reviewed-by: Kees Cook +Reviewed-by: Greg Kroah-Hartman +Co-developed-by: Alex Gaynor +Signed-off-by: Alex Gaynor +Co-developed-by: Wedson Almeida Filho +Signed-off-by: Wedson Almeida Filho +Signed-off-by: Miguel Ojeda +Stable-dep-of: efbd63983533 ("scripts/decode_stacktrace.sh: optionally use LLVM utilities") +Signed-off-by: Sasha Levin +--- + 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 | [|auto] []" + } + ++# 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-24681 b/queue-5.15/scripts-decode_stacktrace.sh-optionally-use-llvm-uti.patch-24681 new file mode 100644 index 00000000000..f6db1ef5bfb --- /dev/null +++ b/queue-5.15/scripts-decode_stacktrace.sh-optionally-use-llvm-uti.patch-24681 @@ -0,0 +1,102 @@ +From 48d25a8f063530bb98041d6266c3e01040e40e48 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 Sep 2023 03:48:17 +0000 +Subject: scripts/decode_stacktrace.sh: optionally use LLVM utilities + +From: Carlos Llamas + +[ 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 +Reviewed-by: Nick Desaulniers +Reviewed-by: Elliot Berman +Tested-by: Justin Stitt +Cc: Will Deacon +Cc: John Stultz +Cc: Masahiro Yamada +Cc: Nathan Chancellor +Cc: Tom Rix +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Sasha Levin +--- + 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-6443 b/queue-5.15/scripts-decode_stacktrace.sh-support-old-bash-versio.patch-6443 new file mode 100644 index 00000000000..34438d46fc0 --- /dev/null +++ b/queue-5.15/scripts-decode_stacktrace.sh-support-old-bash-versio.patch-6443 @@ -0,0 +1,108 @@ +From 073cdb347279ca86cf043c0fe12fd47856b09c9c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 29 Apr 2022 14:37:57 -0700 +Subject: scripts/decode_stacktrace.sh: support old bash version + +From: Schspa Shi + +[ 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 +Cc: Stephen Boyd +Signed-off-by: Andrew Morton +Stable-dep-of: efbd63983533 ("scripts/decode_stacktrace.sh: optionally use LLVM utilities") +Signed-off-by: Sasha Levin +--- + 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-28198 b/queue-5.15/serial-8250_exar-fill-in-rs485_supported.patch-28198 new file mode 100644 index 00000000000..4764ce9cac6 --- /dev/null +++ b/queue-5.15/serial-8250_exar-fill-in-rs485_supported.patch-28198 @@ -0,0 +1,81 @@ +From 56ebed83bab688a588edb8ea276033b3db38ed98 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ Upstream commit 59c221f8e1269278161313048c71929c9950b2c4 ] + +Add information on supported serial_rs485 features. + +Signed-off-by: Ilpo Järvinen +Link: https://lore.kernel.org/r/20220606100433.13793-8-ilpo.jarvinen@linux.intel.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 0c2a5f471ce5 ("serial: 8250_exar: Set missing rs485_supported flag") +Signed-off-by: Sasha Levin +--- + 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-19915 b/queue-5.15/serial-8250_exar-set-missing-rs485_supported-flag.patch-19915 new file mode 100644 index 00000000000..3dcbfa3a829 --- /dev/null +++ b/queue-5.15/serial-8250_exar-set-missing-rs485_supported-flag.patch-19915 @@ -0,0 +1,59 @@ +From fd145d84ec6dbe6802f1ccef24360a2325eb115a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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: +Reviewed-by: Ilpo Järvinen +Signed-off-by: Lino Sanfilippo +Link: https://lore.kernel.org/r/20240103061818.564-8-l.sanfilippo@kunbus.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-5.15/series b/queue-5.15/series index fd3a6100320..9e95600ebc0 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -465,3 +465,34 @@ 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 +dma-buf-add-dma_fence_timestamp-helper.patch-23638 +mwifiex-select-firmware-based-on-strapping.patch-10303 +wifi-mwifiex-support-sd8978-chipset.patch-17202 +wifi-mwifiex-add-extra-delay-for-firmware-ready.patch-15130 +bus-moxtet-add-spi-device-table.patch-12211 +wifi-mwifiex-fix-uninitialized-firmware_stat.patch-14283 +crypto-lib-mpi-fix-unexpected-pointer-access-in-mpi_.patch-5277 +usb-dwc3-gadget-wait-for-ep0-xfers-to-complete-durin.patch-16924 +usb-dwc3-ep0-don-t-prepare-beyond-setup-stage.patch-27319 +usb-dwc3-gadget-only-end-transfer-for-ep0-data-phase.patch-25635 +usb-dwc3-gadget-delay-issuing-end-transfer.patch-16020 +usb-dwc3-fix-ep0-handling-when-getting-reset-while-d.patch-19133 +usb-dwc3-gadget-force-sending-delayed-status-during-.patch-25697 +usb-dwc3-gadget-submit-endxfer-command-if-delayed-du.patch-28020 +usb-dwc3-gadget-stall-and-restart-ep0-if-host-is-unr.patch-18649 +usb-dwc3-gadget-refactor-ep0-forced-stall-restart-in.patch-17868 +usb-dwc3-gadget-handle-ep0-request-dequeuing-properl.patch-14688 +usb-dwc3-gadget-queue-pm-runtime-idle-on-disconnect-.patch-19688 +serial-8250_exar-fill-in-rs485_supported.patch-28198 +serial-8250_exar-set-missing-rs485_supported-flag.patch-19915 +fbdev-defio-early-out-if-page-is-already-enlisted.patch-31235 +fbdev-don-t-sort-deferred-i-o-pages-by-default.patch-6824 +fbdev-defio-fix-the-pagelist-corruption.patch-6707 +fbdev-track-deferred-i-o-pages-in-pageref-struct.patch-4580 +fbdev-rename-pagelist-to-pagereflist-for-deferred-i-.patch-15544 +fbdev-fix-invalid-page-access-after-closing-deferred.patch-12976 +fbdev-fix-incorrect-page-mapping-clearance-at-fb_def.patch-30601 +fbdev-flush-deferred-io-before-closing.patch-17074 +scripts-decode_stacktrace.sh-support-old-bash-versio.patch-6443 +scripts-decode_stacktrace-demangle-rust-symbols.patch-30753 +scripts-decode_stacktrace.sh-optionally-use-llvm-uti.patch-24681 diff --git a/queue-5.15/usb-dwc3-ep0-don-t-prepare-beyond-setup-stage.patch-27319 b/queue-5.15/usb-dwc3-ep0-don-t-prepare-beyond-setup-stage.patch-27319 new file mode 100644 index 00000000000..871baffb266 --- /dev/null +++ b/queue-5.15/usb-dwc3-ep0-don-t-prepare-beyond-setup-stage.patch-27319 @@ -0,0 +1,89 @@ +From b99690d4610a25983636c8d393b94a2fe845fd0c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 Apr 2022 19:22:50 -0700 +Subject: usb: dwc3: ep0: Don't prepare beyond Setup stage + +From: Thinh Nguyen + +[ 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 +Link: https://lore.kernel.org/r/6bacec56ecabb2c6e49a09cedfcac281fdc97de0.1650593829.git.Thinh.Nguyen@synopsys.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly") +Signed-off-by: Sasha Levin +--- + 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-19133 b/queue-5.15/usb-dwc3-fix-ep0-handling-when-getting-reset-while-d.patch-19133 new file mode 100644 index 00000000000..933112ec355 --- /dev/null +++ b/queue-5.15/usb-dwc3-fix-ep0-handling-when-getting-reset-while-d.patch-19133 @@ -0,0 +1,138 @@ +From 8f73ee63dc2cc5c4e2dd666f8aaa8451604fdac4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Link: https://lore.kernel.org/r/1651693001-29891-1-git-send-email-quic_mrana@quicinc.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly") +Signed-off-by: Sasha Levin +--- + 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-16020 b/queue-5.15/usb-dwc3-gadget-delay-issuing-end-transfer.patch-16020 new file mode 100644 index 00000000000..ec00d7d00b3 --- /dev/null +++ b/queue-5.15/usb-dwc3-gadget-delay-issuing-end-transfer.patch-16020 @@ -0,0 +1,67 @@ +From adce0573fd1d8834fc982a989489eabca25c066c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 Apr 2022 19:23:03 -0700 +Subject: usb: dwc3: gadget: Delay issuing End Transfer + +From: Thinh Nguyen + +[ 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 +Link: https://lore.kernel.org/r/2fcf3b5d90068d549589a57a27a79f76c6769b04.1650593829.git.Thinh.Nguyen@synopsys.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly") +Signed-off-by: Sasha Levin +--- + 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-25697 b/queue-5.15/usb-dwc3-gadget-force-sending-delayed-status-during-.patch-25697 new file mode 100644 index 00000000000..32a23c0b720 --- /dev/null +++ b/queue-5.15/usb-dwc3-gadget-force-sending-delayed-status-during-.patch-25697 @@ -0,0 +1,56 @@ +From c0f4a91e640fa3781409bd37104482ccc3924a88 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 17 Aug 2022 11:23:52 -0700 +Subject: usb: dwc3: gadget: Force sending delayed status during soft + disconnect + +From: Wesley Cheng + +[ 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 +Signed-off-by: Wesley Cheng +Link: https://lore.kernel.org/r/20220817182359.13550-3-quic_wcheng@quicinc.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly") +Signed-off-by: Sasha Levin +--- + 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-14688 b/queue-5.15/usb-dwc3-gadget-handle-ep0-request-dequeuing-properl.patch-14688 new file mode 100644 index 00000000000..726e64e443d --- /dev/null +++ b/queue-5.15/usb-dwc3-gadget-handle-ep0-request-dequeuing-properl.patch-14688 @@ -0,0 +1,73 @@ +From 0b4e713f7dab29713dfeb420e9db0223b89d849f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 Dec 2023 12:18:14 -0800 +Subject: usb: dwc3: gadget: Handle EP0 request dequeuing properly + +From: Wesley Cheng + +[ 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 +Signed-off-by: Wesley Cheng +Acked-by: Thinh Nguyen +Link: https://lore.kernel.org/r/20231206201814.32664-1-quic_wcheng@quicinc.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + 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-25635 b/queue-5.15/usb-dwc3-gadget-only-end-transfer-for-ep0-data-phase.patch-25635 new file mode 100644 index 00000000000..02452e7c0eb --- /dev/null +++ b/queue-5.15/usb-dwc3-gadget-only-end-transfer-for-ep0-data-phase.patch-25635 @@ -0,0 +1,53 @@ +From b54260b8b5b4a85bbd1a4bc2846273d02f85522c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 Apr 2022 19:22:57 -0700 +Subject: usb: dwc3: gadget: Only End Transfer for ep0 data phase + +From: Thinh Nguyen + +[ 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 +Link: https://lore.kernel.org/r/3c6643678863a26702e4115e9e19d7d94a30d49c.1650593829.git.Thinh.Nguyen@synopsys.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly") +Signed-off-by: Sasha Levin +--- + 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-19688 b/queue-5.15/usb-dwc3-gadget-queue-pm-runtime-idle-on-disconnect-.patch-19688 new file mode 100644 index 00000000000..48549340b87 --- /dev/null +++ b/queue-5.15/usb-dwc3-gadget-queue-pm-runtime-idle-on-disconnect-.patch-19688 @@ -0,0 +1,62 @@ +From cd27b42ef09f2d914b0dd9d43f36e03f8681b88c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 3 Jan 2024 13:49:46 -0800 +Subject: usb: dwc3: gadget: Queue PM runtime idle on disconnect event + +From: Wesley Cheng + +[ 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: +Signed-off-by: Wesley Cheng +Link: https://lore.kernel.org/r/20240103214946.2596-1-quic_wcheng@quicinc.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + 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-17868 b/queue-5.15/usb-dwc3-gadget-refactor-ep0-forced-stall-restart-in.patch-17868 new file mode 100644 index 00000000000..787aec8bcc8 --- /dev/null +++ b/queue-5.15/usb-dwc3-gadget-refactor-ep0-forced-stall-restart-in.patch-17868 @@ -0,0 +1,114 @@ +From 92460099926c88af8a8237322a3e18180a4bf039 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Signed-off-by: Wesley Cheng +Link: https://lore.kernel.org/r/20230420212759.29429-3-quic_wcheng@quicinc.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly") +Signed-off-by: Sasha Levin +--- + 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-18649 b/queue-5.15/usb-dwc3-gadget-stall-and-restart-ep0-if-host-is-unr.patch-18649 new file mode 100644 index 00000000000..15714d57160 --- /dev/null +++ b/queue-5.15/usb-dwc3-gadget-stall-and-restart-ep0-if-host-is-unr.patch-18649 @@ -0,0 +1,128 @@ +From 620813673299aa87ddc65381506f42fb9fe1505b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 13 Apr 2023 12:57:40 -0700 +Subject: usb: dwc3: gadget: Stall and restart EP0 if host is unresponsive + +From: Wesley Cheng + +[ 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 +Signed-off-by: Wesley Cheng +Link: https://lore.kernel.org/r/20230413195742.11821-2-quic_wcheng@quicinc.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly") +Signed-off-by: Sasha Levin +--- + 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-28020 b/queue-5.15/usb-dwc3-gadget-submit-endxfer-command-if-delayed-du.patch-28020 new file mode 100644 index 00000000000..cee435e419f --- /dev/null +++ b/queue-5.15/usb-dwc3-gadget-submit-endxfer-command-if-delayed-du.patch-28020 @@ -0,0 +1,58 @@ +From bdce6e89daa1411beecbe033786d22487f003c9a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 1 Sep 2022 12:36:25 -0700 +Subject: usb: dwc3: gadget: Submit endxfer command if delayed during + disconnect + +From: Wesley Cheng + +[ 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 +Signed-off-by: Wesley Cheng +Link: https://lore.kernel.org/r/20220901193625.8727-6-quic_wcheng@quicinc.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly") +Signed-off-by: Sasha Levin +--- + 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-16924 b/queue-5.15/usb-dwc3-gadget-wait-for-ep0-xfers-to-complete-durin.patch-16924 new file mode 100644 index 00000000000..7bf46e955c8 --- /dev/null +++ b/queue-5.15/usb-dwc3-gadget-wait-for-ep0-xfers-to-complete-durin.patch-16924 @@ -0,0 +1,170 @@ +From d5523dbba024bb8a98d2a06c0553fc92f6e4a90f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Signed-off-by: Thinh Nguyen +Signed-off-by: Wesley Cheng +Link: https://lore.kernel.org/r/20220309205402.4467-1-quic_wcheng@quicinc.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly") +Signed-off-by: Sasha Levin +--- + 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, ¶ms); + } + +-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-15130 b/queue-5.15/wifi-mwifiex-add-extra-delay-for-firmware-ready.patch-15130 new file mode 100644 index 00000000000..42361774357 --- /dev/null +++ b/queue-5.15/wifi-mwifiex-add-extra-delay-for-firmware-ready.patch-15130 @@ -0,0 +1,163 @@ +From 307f5bd1d45e51f1e0899ef19771b7ecb1b59f9c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 9 Dec 2023 07:40:29 +0800 +Subject: wifi: mwifiex: add extra delay for firmware ready + +From: David Lin + +[ 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 +Cc: stable@vger.kernel.org +Reviewed-by: Francesco Dolcini +Acked-by: Brian Norris +Tested-by: Marcel Ziswiler # Verdin AM62 (IW416) +Signed-off-by: Kalle Valo +Link: https://msgid.link/20231208234029.2197-1-yu-hao.lin@nxp.com +Signed-off-by: Sasha Levin +--- + 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-14283 b/queue-5.15/wifi-mwifiex-fix-uninitialized-firmware_stat.patch-14283 new file mode 100644 index 00000000000..36198b9cf83 --- /dev/null +++ b/queue-5.15/wifi-mwifiex-fix-uninitialized-firmware_stat.patch-14283 @@ -0,0 +1,41 @@ +From e8342bec9354a4d2492c1f8dca1c564065906a33 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 Dec 2023 09:55:11 +0800 +Subject: wifi: mwifiex: fix uninitialized firmware_stat + +From: David Lin + +[ Upstream commit 3df95e265924ac898c1a38a0c01846dd0bd3b354 ] + +Variable firmware_stat is possible to be used without initialization. + +Signed-off-by: David Lin +Fixes: 1c5d463c0770 ("wifi: mwifiex: add extra delay for firmware ready") +Cc: stable@vger.kernel.org +Reported-by: kernel test robot +Reported-by: Dan Carpenter +Closes: https://lore.kernel.org/r/202312192236.ZflaWYCw-lkp@intel.com/ +Acked-by: Brian Norris +Signed-off-by: Kalle Valo +Link: https://msgid.link/20231221015511.1032128-1-yu-hao.lin@nxp.com +Signed-off-by: Sasha Levin +--- + 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-17202 b/queue-5.15/wifi-mwifiex-support-sd8978-chipset.patch-17202 new file mode 100644 index 00000000000..eea9eed97dc --- /dev/null +++ b/queue-5.15/wifi-mwifiex-support-sd8978-chipset.patch-17202 @@ -0,0 +1,177 @@ +From 444a1f89cee2117c4524c52fb57d2a70f40d89bc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 27 Jan 2023 15:02:00 +0100 +Subject: wifi: mwifiex: Support SD8978 chipset + +From: Lukas Wunner + +[ 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 +Signed-off-by: Kalle Valo +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 +--- + .../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 + -- 2.47.3