From: Sasha Levin Date: Mon, 14 Jul 2025 17:18:51 +0000 (-0400) Subject: Fixes for 6.6 X-Git-Tag: v5.4.296~35 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4e9cf124cec9696873fe4573133de6815fd00cf8;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 6.6 Signed-off-by: Sasha Levin --- diff --git a/queue-6.6/alsa-hda-realtek-enable-mute-led-on-hp-pavilion-lapt.patch b/queue-6.6/alsa-hda-realtek-enable-mute-led-on-hp-pavilion-lapt.patch new file mode 100644 index 0000000000..4a4c5f145b --- /dev/null +++ b/queue-6.6/alsa-hda-realtek-enable-mute-led-on-hp-pavilion-lapt.patch @@ -0,0 +1,35 @@ +From ab3dacc491cf0b5213279b9907bb25f1595dce66 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 21 Jun 2025 01:36:14 -0400 +Subject: ALSA: hda/realtek - Enable mute LED on HP Pavilion Laptop 15-eg100 + +From: Yasmin Fitzgerald + +[ Upstream commit 68cc9d3c8e44afe90e43cbbd2960da15c2f31e23 ] + +The HP Pavilion Laptop 15-eg100 has Realtek HDA codec ALC287. +It needs the ALC287_FIXUP_HP_GPIO_LED quirk to enable the mute LED. + +Signed-off-by: Yasmin Fitzgerald +Link: https://patch.msgid.link/20250621053832.52950-1-sunoflife1.git@gmail.com +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/pci/hda/patch_realtek.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c +index 0d367cec03ade..1c2059e37fdab 100644 +--- a/sound/pci/hda/patch_realtek.c ++++ b/sound/pci/hda/patch_realtek.c +@@ -10151,6 +10151,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x8975, "HP EliteBook x360 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x897d, "HP mt440 Mobile Thin Client U74", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8981, "HP Elite Dragonfly G3", ALC245_FIXUP_CS35L41_SPI_4), ++ SND_PCI_QUIRK(0x103c, 0x898a, "HP Pavilion 15-eg100", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x898e, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x898f, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8991, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED), +-- +2.39.5 + diff --git a/queue-6.6/asoc-amd-yc-add-quirk-for-acer-nitro-anv15-41-intern.patch b/queue-6.6/asoc-amd-yc-add-quirk-for-acer-nitro-anv15-41-intern.patch new file mode 100644 index 0000000000..2db3858acc --- /dev/null +++ b/queue-6.6/asoc-amd-yc-add-quirk-for-acer-nitro-anv15-41-intern.patch @@ -0,0 +1,42 @@ +From fe539faf5d6caa122ea372e7914841d0bb312197 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 22 Jun 2025 22:58:00 +0000 +Subject: ASoC: amd: yc: add quirk for Acer Nitro ANV15-41 internal mic + +From: Yuzuru10 + +[ Upstream commit 7186b81807b4a08f8bf834b6bdc72d6ed8ba1587 ] + +This patch adds DMI-based quirk for the Acer Nitro ANV15-41, +allowing the internal microphone to be detected correctly on +machines with "RB" as board vendor. + +Signed-off-by: Yuzuru +Link: https://patch.msgid.link/20250622225754.20856-1-yuzuru_10@proton.me +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/amd/yc/acp6x-mach.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c +index 429e61d47ffbb..66ef8f4fd02cd 100644 +--- a/sound/soc/amd/yc/acp6x-mach.c ++++ b/sound/soc/amd/yc/acp6x-mach.c +@@ -346,6 +346,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "83Q3"), + } + }, ++ { ++ .driver_data = &acp6x_card, ++ .matches = { ++ DMI_MATCH(DMI_BOARD_VENDOR, "RB"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Nitro ANV15-41"), ++ } ++ }, + { + .driver_data = &acp6x_card, + .matches = { +-- +2.39.5 + diff --git a/queue-6.6/atm-idt77252-add-missing-dma_map_error.patch b/queue-6.6/atm-idt77252-add-missing-dma_map_error.patch new file mode 100644 index 0000000000..e95c5c6913 --- /dev/null +++ b/queue-6.6/atm-idt77252-add-missing-dma_map_error.patch @@ -0,0 +1,53 @@ +From 64227d0c8cff6f03369cf54c01773c3952f6215e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 24 Jun 2025 08:41:47 +0200 +Subject: atm: idt77252: Add missing `dma_map_error()` + +From: Thomas Fourier + +[ Upstream commit c4890963350dcf4e9a909bae23665921fba4ad27 ] + +The DMA map functions can fail and should be tested for errors. + +Signed-off-by: Thomas Fourier +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250624064148.12815-3-fourier.thomas@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/atm/idt77252.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c +index a876024d8a05f..63d41320cd5cf 100644 +--- a/drivers/atm/idt77252.c ++++ b/drivers/atm/idt77252.c +@@ -852,6 +852,8 @@ queue_skb(struct idt77252_dev *card, struct vc_map *vc, + + IDT77252_PRV_PADDR(skb) = dma_map_single(&card->pcidev->dev, skb->data, + skb->len, DMA_TO_DEVICE); ++ if (dma_mapping_error(&card->pcidev->dev, IDT77252_PRV_PADDR(skb))) ++ return -ENOMEM; + + error = -EINVAL; + +@@ -1857,6 +1859,8 @@ add_rx_skb(struct idt77252_dev *card, int queue, + paddr = dma_map_single(&card->pcidev->dev, skb->data, + skb_end_pointer(skb) - skb->data, + DMA_FROM_DEVICE); ++ if (dma_mapping_error(&card->pcidev->dev, paddr)) ++ goto outpoolrm; + IDT77252_PRV_PADDR(skb) = paddr; + + if (push_rx_skb(card, skb, queue)) { +@@ -1871,6 +1875,7 @@ add_rx_skb(struct idt77252_dev *card, int queue, + dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), + skb_end_pointer(skb) - skb->data, DMA_FROM_DEVICE); + ++outpoolrm: + handle = IDT77252_PRV_POOL(skb); + card->sbpool[POOL_QUEUE(handle)].skb[POOL_INDEX(handle)] = NULL; + +-- +2.39.5 + diff --git a/queue-6.6/bnxt_en-fix-dcb-ets-validation.patch b/queue-6.6/bnxt_en-fix-dcb-ets-validation.patch new file mode 100644 index 0000000000..13e1a53077 --- /dev/null +++ b/queue-6.6/bnxt_en-fix-dcb-ets-validation.patch @@ -0,0 +1,49 @@ +From 22cfc00bfe14c72a8657b7df4a383d26f575ba6f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 10 Jul 2025 14:39:36 -0700 +Subject: bnxt_en: Fix DCB ETS validation + +From: Shravya KN + +[ Upstream commit b74c2a2e9cc471e847abd87e50a2354c07e02040 ] + +In bnxt_ets_validate(), the code incorrectly loops over all possible +traffic classes to check and add the ETS settings. Fix it to loop +over the configured traffic classes only. + +The unconfigured traffic classes will default to TSA_ETS with 0 +bandwidth. Looping over these unconfigured traffic classes may +cause the validation to fail and trigger this error message: + +"rejecting ETS config starving a TC\n" + +The .ieee_setets() will then fail. + +Fixes: 7df4ae9fe855 ("bnxt_en: Implement DCBNL to support host-based DCBX.") +Reviewed-by: Sreekanth Reddy +Signed-off-by: Shravya KN +Signed-off-by: Michael Chan +Link: https://patch.msgid.link/20250710213938.1959625-2-michael.chan@broadcom.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c +index 63e0670383852..1727e9bb1479d 100644 +--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c ++++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c +@@ -487,7 +487,9 @@ static int bnxt_ets_validate(struct bnxt *bp, struct ieee_ets *ets, u8 *tc) + + if ((ets->tc_tx_bw[i] || ets->tc_tsa[i]) && i > bp->max_tc) + return -EINVAL; ++ } + ++ for (i = 0; i < max_tc; i++) { + switch (ets->tc_tsa[i]) { + case IEEE_8021QAZ_TSA_STRICT: + break; +-- +2.39.5 + diff --git a/queue-6.6/bnxt_en-set-dma-unmap-len-correctly-for-xdp_redirect.patch b/queue-6.6/bnxt_en-set-dma-unmap-len-correctly-for-xdp_redirect.patch new file mode 100644 index 0000000000..54262f3bee --- /dev/null +++ b/queue-6.6/bnxt_en-set-dma-unmap-len-correctly-for-xdp_redirect.patch @@ -0,0 +1,72 @@ +From 0f17919420f5f9d08eddf069e962eb16d97b5dfa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 10 Jul 2025 14:39:38 -0700 +Subject: bnxt_en: Set DMA unmap len correctly for XDP_REDIRECT + +From: Somnath Kotur + +[ Upstream commit 3cdf199d4755d477972ee87110b2aebc88b3cfad ] + +When transmitting an XDP_REDIRECT packet, call dma_unmap_len_set() +with the proper length instead of 0. This bug triggers this warning +on a system with IOMMU enabled: + +WARNING: CPU: 36 PID: 0 at drivers/iommu/dma-iommu.c:842 __iommu_dma_unmap+0x159/0x170 +RIP: 0010:__iommu_dma_unmap+0x159/0x170 +Code: a8 00 00 00 00 48 c7 45 b0 00 00 00 00 48 c7 45 c8 00 00 00 00 48 c7 45 a0 ff ff ff ff 4c 89 45 +b8 4c 89 45 c0 e9 77 ff ff ff <0f> 0b e9 60 ff ff ff e8 8b bf 6a 00 66 66 2e 0f 1f 84 00 00 00 00 +RSP: 0018:ff22d31181150c88 EFLAGS: 00010206 +RAX: 0000000000002000 RBX: 00000000e13a0000 RCX: 0000000000000000 +RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 +RBP: ff22d31181150cf0 R08: ff22d31181150ca8 R09: 0000000000000000 +R10: 0000000000000000 R11: ff22d311d36c9d80 R12: 0000000000001000 +R13: ff13544d10645010 R14: ff22d31181150c90 R15: ff13544d0b2bac00 +FS: 0000000000000000(0000) GS:ff13550908a00000(0000) knlGS:0000000000000000 +CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +CR2: 00005be909dacff8 CR3: 0008000173408003 CR4: 0000000000f71ef0 +PKRU: 55555554 +Call Trace: + +? show_regs+0x6d/0x80 +? __warn+0x89/0x160 +? __iommu_dma_unmap+0x159/0x170 +? report_bug+0x17e/0x1b0 +? handle_bug+0x46/0x90 +? exc_invalid_op+0x18/0x80 +? asm_exc_invalid_op+0x1b/0x20 +? __iommu_dma_unmap+0x159/0x170 +? __iommu_dma_unmap+0xb3/0x170 +iommu_dma_unmap_page+0x4f/0x100 +dma_unmap_page_attrs+0x52/0x220 +? srso_alias_return_thunk+0x5/0xfbef5 +? xdp_return_frame+0x2e/0xd0 +bnxt_tx_int_xdp+0xdf/0x440 [bnxt_en] +__bnxt_poll_work_done+0x81/0x1e0 [bnxt_en] +bnxt_poll+0xd3/0x1e0 [bnxt_en] + +Fixes: f18c2b77b2e4 ("bnxt_en: optimized XDP_REDIRECT support") +Signed-off-by: Somnath Kotur +Signed-off-by: Michael Chan +Link: https://patch.msgid.link/20250710213938.1959625-4-michael.chan@broadcom.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +index 758f51366ef03..07a458ecb7cc9 100644 +--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c ++++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +@@ -115,7 +115,7 @@ static void __bnxt_xmit_xdp_redirect(struct bnxt *bp, + tx_buf->action = XDP_REDIRECT; + tx_buf->xdpf = xdpf; + dma_unmap_addr_set(tx_buf, mapping, mapping); +- dma_unmap_len_set(tx_buf, len, 0); ++ dma_unmap_len_set(tx_buf, len, len); + } + + void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int budget) +-- +2.39.5 + diff --git a/queue-6.6/bpf-adjust-free-target-to-avoid-global-starvation-of.patch b/queue-6.6/bpf-adjust-free-target-to-avoid-global-starvation-of.patch new file mode 100644 index 0000000000..976eb4a2c9 --- /dev/null +++ b/queue-6.6/bpf-adjust-free-target-to-avoid-global-starvation-of.patch @@ -0,0 +1,311 @@ +From ec35890b1ed775b3817b47133db8d8ab17f679a3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 18 Jun 2025 17:57:40 -0400 +Subject: bpf: Adjust free target to avoid global starvation of LRU map + +From: Willem de Bruijn + +[ Upstream commit d4adf1c9ee7722545450608bcb095fb31512f0c6 ] + +BPF_MAP_TYPE_LRU_HASH can recycle most recent elements well before the +map is full, due to percpu reservations and force shrink before +neighbor stealing. Once a CPU is unable to borrow from the global map, +it will once steal one elem from a neighbor and after that each time +flush this one element to the global list and immediately recycle it. + +Batch value LOCAL_FREE_TARGET (128) will exhaust a 10K element map +with 79 CPUs. CPU 79 will observe this behavior even while its +neighbors hold 78 * 127 + 1 * 15 == 9921 free elements (99%). + +CPUs need not be active concurrently. The issue can appear with +affinity migration, e.g., irqbalance. Each CPU can reserve and then +hold onto its 128 elements indefinitely. + +Avoid global list exhaustion by limiting aggregate percpu caches to +half of map size, by adjusting LOCAL_FREE_TARGET based on cpu count. +This change has no effect on sufficiently large tables. + +Similar to LOCAL_NR_SCANS and lru->nr_scans, introduce a map variable +lru->free_target. The extra field fits in a hole in struct bpf_lru. +The cacheline is already warm where read in the hot path. The field is +only accessed with the lru lock held. + +Tested-by: Anton Protopopov +Signed-off-by: Willem de Bruijn +Acked-by: Stanislav Fomichev +Link: https://lore.kernel.org/r/20250618215803.3587312-1-willemdebruijn.kernel@gmail.com +Signed-off-by: Alexei Starovoitov +Signed-off-by: Sasha Levin +--- + Documentation/bpf/map_hash.rst | 8 ++- + Documentation/bpf/map_lru_hash_update.dot | 6 +- + kernel/bpf/bpf_lru_list.c | 9 ++- + kernel/bpf/bpf_lru_list.h | 1 + + tools/testing/selftests/bpf/test_lru_map.c | 72 +++++++++++----------- + 5 files changed, 52 insertions(+), 44 deletions(-) + +diff --git a/Documentation/bpf/map_hash.rst b/Documentation/bpf/map_hash.rst +index d2343952f2cbd..8606bf958a8cf 100644 +--- a/Documentation/bpf/map_hash.rst ++++ b/Documentation/bpf/map_hash.rst +@@ -233,10 +233,16 @@ attempts in order to enforce the LRU property which have increasing impacts on + other CPUs involved in the following operation attempts: + + - Attempt to use CPU-local state to batch operations +-- Attempt to fetch free nodes from global lists ++- Attempt to fetch ``target_free`` free nodes from global lists + - Attempt to pull any node from a global list and remove it from the hashmap + - Attempt to pull any node from any CPU's list and remove it from the hashmap + ++The number of nodes to borrow from the global list in a batch, ``target_free``, ++depends on the size of the map. Larger batch size reduces lock contention, but ++may also exhaust the global structure. The value is computed at map init to ++avoid exhaustion, by limiting aggregate reservation by all CPUs to half the map ++size. With a minimum of a single element and maximum budget of 128 at a time. ++ + This algorithm is described visually in the following diagram. See the + description in commit 3a08c2fd7634 ("bpf: LRU List") for a full explanation of + the corresponding operations: +diff --git a/Documentation/bpf/map_lru_hash_update.dot b/Documentation/bpf/map_lru_hash_update.dot +index a0fee349d29c2..ab10058f5b79f 100644 +--- a/Documentation/bpf/map_lru_hash_update.dot ++++ b/Documentation/bpf/map_lru_hash_update.dot +@@ -35,18 +35,18 @@ digraph { + fn_bpf_lru_list_pop_free_to_local [shape=rectangle,fillcolor=2, + label="Flush local pending, + Rotate Global list, move +- LOCAL_FREE_TARGET ++ target_free + from global -> local"] + // Also corresponds to: + // fn__local_list_flush() + // fn_bpf_lru_list_rotate() + fn___bpf_lru_node_move_to_free[shape=diamond,fillcolor=2, +- label="Able to free\nLOCAL_FREE_TARGET\nnodes?"] ++ label="Able to free\ntarget_free\nnodes?"] + + fn___bpf_lru_list_shrink_inactive [shape=rectangle,fillcolor=3, + label="Shrink inactive list + up to remaining +- LOCAL_FREE_TARGET ++ target_free + (global LRU -> local)"] + fn___bpf_lru_list_shrink [shape=diamond,fillcolor=2, + label="> 0 entries in\nlocal free list?"] +diff --git a/kernel/bpf/bpf_lru_list.c b/kernel/bpf/bpf_lru_list.c +index 3dabdd137d102..2d6e1c98d8adc 100644 +--- a/kernel/bpf/bpf_lru_list.c ++++ b/kernel/bpf/bpf_lru_list.c +@@ -337,12 +337,12 @@ static void bpf_lru_list_pop_free_to_local(struct bpf_lru *lru, + list) { + __bpf_lru_node_move_to_free(l, node, local_free_list(loc_l), + BPF_LRU_LOCAL_LIST_T_FREE); +- if (++nfree == LOCAL_FREE_TARGET) ++ if (++nfree == lru->target_free) + break; + } + +- if (nfree < LOCAL_FREE_TARGET) +- __bpf_lru_list_shrink(lru, l, LOCAL_FREE_TARGET - nfree, ++ if (nfree < lru->target_free) ++ __bpf_lru_list_shrink(lru, l, lru->target_free - nfree, + local_free_list(loc_l), + BPF_LRU_LOCAL_LIST_T_FREE); + +@@ -577,6 +577,9 @@ static void bpf_common_lru_populate(struct bpf_lru *lru, void *buf, + list_add(&node->list, &l->lists[BPF_LRU_LIST_T_FREE]); + buf += elem_size; + } ++ ++ lru->target_free = clamp((nr_elems / num_possible_cpus()) / 2, ++ 1, LOCAL_FREE_TARGET); + } + + static void bpf_percpu_lru_populate(struct bpf_lru *lru, void *buf, +diff --git a/kernel/bpf/bpf_lru_list.h b/kernel/bpf/bpf_lru_list.h +index cbd8d3720c2bb..fe2661a58ea94 100644 +--- a/kernel/bpf/bpf_lru_list.h ++++ b/kernel/bpf/bpf_lru_list.h +@@ -58,6 +58,7 @@ struct bpf_lru { + del_from_htab_func del_from_htab; + void *del_arg; + unsigned int hash_offset; ++ unsigned int target_free; + unsigned int nr_scans; + bool percpu; + }; +diff --git a/tools/testing/selftests/bpf/test_lru_map.c b/tools/testing/selftests/bpf/test_lru_map.c +index fda7589c50236..4ae83f4b7fc7e 100644 +--- a/tools/testing/selftests/bpf/test_lru_map.c ++++ b/tools/testing/selftests/bpf/test_lru_map.c +@@ -138,6 +138,12 @@ static int sched_next_online(int pid, int *next_to_try) + return ret; + } + ++/* Inverse of how bpf_common_lru_populate derives target_free from map_size. */ ++static unsigned int __map_size(unsigned int tgt_free) ++{ ++ return tgt_free * nr_cpus * 2; ++} ++ + /* Size of the LRU map is 2 + * Add key=1 (+1 key) + * Add key=2 (+1 key) +@@ -231,11 +237,11 @@ static void test_lru_sanity0(int map_type, int map_flags) + printf("Pass\n"); + } + +-/* Size of the LRU map is 1.5*tgt_free +- * Insert 1 to tgt_free (+tgt_free keys) +- * Lookup 1 to tgt_free/2 +- * Insert 1+tgt_free to 2*tgt_free (+tgt_free keys) +- * => 1+tgt_free/2 to LOCALFREE_TARGET will be removed by LRU ++/* Verify that unreferenced elements are recycled before referenced ones. ++ * Insert elements. ++ * Reference a subset of these. ++ * Insert more, enough to trigger recycling. ++ * Verify that unreferenced are recycled. + */ + static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free) + { +@@ -257,7 +263,7 @@ static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free) + batch_size = tgt_free / 2; + assert(batch_size * 2 == tgt_free); + +- map_size = tgt_free + batch_size; ++ map_size = __map_size(tgt_free) + batch_size; + lru_map_fd = create_map(map_type, map_flags, map_size); + assert(lru_map_fd != -1); + +@@ -266,13 +272,13 @@ static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free) + + value[0] = 1234; + +- /* Insert 1 to tgt_free (+tgt_free keys) */ +- end_key = 1 + tgt_free; ++ /* Insert map_size - batch_size keys */ ++ end_key = 1 + __map_size(tgt_free); + for (key = 1; key < end_key; key++) + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); + +- /* Lookup 1 to tgt_free/2 */ ++ /* Lookup 1 to batch_size */ + end_key = 1 + batch_size; + for (key = 1; key < end_key; key++) { + assert(!bpf_map_lookup_elem_with_ref_bit(lru_map_fd, key, value)); +@@ -280,12 +286,13 @@ static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free) + BPF_NOEXIST)); + } + +- /* Insert 1+tgt_free to 2*tgt_free +- * => 1+tgt_free/2 to LOCALFREE_TARGET will be ++ /* Insert another map_size - batch_size keys ++ * Map will contain 1 to batch_size plus these latest, i.e., ++ * => previous 1+batch_size to map_size - batch_size will have been + * removed by LRU + */ +- key = 1 + tgt_free; +- end_key = key + tgt_free; ++ key = 1 + __map_size(tgt_free); ++ end_key = key + __map_size(tgt_free); + for (; key < end_key; key++) { + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); +@@ -301,17 +308,8 @@ static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free) + printf("Pass\n"); + } + +-/* Size of the LRU map 1.5 * tgt_free +- * Insert 1 to tgt_free (+tgt_free keys) +- * Update 1 to tgt_free/2 +- * => The original 1 to tgt_free/2 will be removed due to +- * the LRU shrink process +- * Re-insert 1 to tgt_free/2 again and do a lookup immeidately +- * Insert 1+tgt_free to tgt_free*3/2 +- * Insert 1+tgt_free*3/2 to tgt_free*5/2 +- * => Key 1+tgt_free to tgt_free*3/2 +- * will be removed from LRU because it has never +- * been lookup and ref bit is not set ++/* Verify that insertions exceeding map size will recycle the oldest. ++ * Verify that unreferenced elements are recycled before referenced. + */ + static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) + { +@@ -334,7 +332,7 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) + batch_size = tgt_free / 2; + assert(batch_size * 2 == tgt_free); + +- map_size = tgt_free + batch_size; ++ map_size = __map_size(tgt_free) + batch_size; + lru_map_fd = create_map(map_type, map_flags, map_size); + assert(lru_map_fd != -1); + +@@ -343,8 +341,8 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) + + value[0] = 1234; + +- /* Insert 1 to tgt_free (+tgt_free keys) */ +- end_key = 1 + tgt_free; ++ /* Insert map_size - batch_size keys */ ++ end_key = 1 + __map_size(tgt_free); + for (key = 1; key < end_key; key++) + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); +@@ -357,8 +355,7 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) + * shrink the inactive list to get tgt_free + * number of free nodes. + * +- * Hence, the oldest key 1 to tgt_free/2 +- * are removed from the LRU list. ++ * Hence, the oldest key is removed from the LRU list. + */ + key = 1; + if (map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) { +@@ -370,8 +367,7 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) + BPF_EXIST)); + } + +- /* Re-insert 1 to tgt_free/2 again and do a lookup +- * immeidately. ++ /* Re-insert 1 to batch_size again and do a lookup immediately. + */ + end_key = 1 + batch_size; + value[0] = 4321; +@@ -387,17 +383,18 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) + + value[0] = 1234; + +- /* Insert 1+tgt_free to tgt_free*3/2 */ +- end_key = 1 + tgt_free + batch_size; +- for (key = 1 + tgt_free; key < end_key; key++) ++ /* Insert batch_size new elements */ ++ key = 1 + __map_size(tgt_free); ++ end_key = key + batch_size; ++ for (; key < end_key; key++) + /* These newly added but not referenced keys will be + * gone during the next LRU shrink. + */ + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); + +- /* Insert 1+tgt_free*3/2 to tgt_free*5/2 */ +- end_key = key + tgt_free; ++ /* Insert map_size - batch_size elements */ ++ end_key += __map_size(tgt_free); + for (; key < end_key; key++) { + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); +@@ -500,7 +497,8 @@ static void test_lru_sanity4(int map_type, int map_flags, unsigned int tgt_free) + lru_map_fd = create_map(map_type, map_flags, + 3 * tgt_free * nr_cpus); + else +- lru_map_fd = create_map(map_type, map_flags, 3 * tgt_free); ++ lru_map_fd = create_map(map_type, map_flags, ++ 3 * __map_size(tgt_free)); + assert(lru_map_fd != -1); + + expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, +-- +2.39.5 + diff --git a/queue-6.6/btrfs-fix-assertion-when-building-free-space-tree.patch b/queue-6.6/btrfs-fix-assertion-when-building-free-space-tree.patch new file mode 100644 index 0000000000..6c7a7d2545 --- /dev/null +++ b/queue-6.6/btrfs-fix-assertion-when-building-free-space-tree.patch @@ -0,0 +1,126 @@ +From 6c3b7c2d1ac2ebc869c375f40c750c792194f456 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 5 Jun 2025 20:51:03 +0100 +Subject: btrfs: fix assertion when building free space tree + +From: Filipe Manana + +[ Upstream commit 1961d20f6fa8903266ed9bd77c691924c22c8f02 ] + +When building the free space tree with the block group tree feature +enabled, we can hit an assertion failure like this: + + BTRFS info (device loop0 state M): rebuilding free space tree + assertion failed: ret == 0, in fs/btrfs/free-space-tree.c:1102 + ------------[ cut here ]------------ + kernel BUG at fs/btrfs/free-space-tree.c:1102! + Internal error: Oops - BUG: 00000000f2000800 [#1] SMP + Modules linked in: + CPU: 1 UID: 0 PID: 6592 Comm: syz-executor322 Not tainted 6.15.0-rc7-syzkaller-gd7fa1af5b33e #0 PREEMPT + Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/07/2025 + pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) + pc : populate_free_space_tree+0x514/0x518 fs/btrfs/free-space-tree.c:1102 + lr : populate_free_space_tree+0x514/0x518 fs/btrfs/free-space-tree.c:1102 + sp : ffff8000a4ce7600 + x29: ffff8000a4ce76e0 x28: ffff0000c9bc6000 x27: ffff0000ddfff3d8 + x26: ffff0000ddfff378 x25: dfff800000000000 x24: 0000000000000001 + x23: ffff8000a4ce7660 x22: ffff70001499cecc x21: ffff0000e1d8c160 + x20: ffff0000e1cb7800 x19: ffff0000e1d8c0b0 x18: 00000000ffffffff + x17: ffff800092f39000 x16: ffff80008ad27e48 x15: ffff700011e740c0 + x14: 1ffff00011e740c0 x13: 0000000000000004 x12: ffffffffffffffff + x11: ffff700011e740c0 x10: 0000000000ff0100 x9 : 94ef24f55d2dbc00 + x8 : 94ef24f55d2dbc00 x7 : 0000000000000001 x6 : 0000000000000001 + x5 : ffff8000a4ce6f98 x4 : ffff80008f415ba0 x3 : ffff800080548ef0 + x2 : 0000000000000000 x1 : 0000000100000000 x0 : 000000000000003e + Call trace: + populate_free_space_tree+0x514/0x518 fs/btrfs/free-space-tree.c:1102 (P) + btrfs_rebuild_free_space_tree+0x14c/0x54c fs/btrfs/free-space-tree.c:1337 + btrfs_start_pre_rw_mount+0xa78/0xe10 fs/btrfs/disk-io.c:3074 + btrfs_remount_rw fs/btrfs/super.c:1319 [inline] + btrfs_reconfigure+0x828/0x2418 fs/btrfs/super.c:1543 + reconfigure_super+0x1d4/0x6f0 fs/super.c:1083 + do_remount fs/namespace.c:3365 [inline] + path_mount+0xb34/0xde0 fs/namespace.c:4200 + do_mount fs/namespace.c:4221 [inline] + __do_sys_mount fs/namespace.c:4432 [inline] + __se_sys_mount fs/namespace.c:4409 [inline] + __arm64_sys_mount+0x3e8/0x468 fs/namespace.c:4409 + __invoke_syscall arch/arm64/kernel/syscall.c:35 [inline] + invoke_syscall+0x98/0x2b8 arch/arm64/kernel/syscall.c:49 + el0_svc_common+0x130/0x23c arch/arm64/kernel/syscall.c:132 + do_el0_svc+0x48/0x58 arch/arm64/kernel/syscall.c:151 + el0_svc+0x58/0x17c arch/arm64/kernel/entry-common.c:767 + el0t_64_sync_handler+0x78/0x108 arch/arm64/kernel/entry-common.c:786 + el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:600 + Code: f0047182 91178042 528089c3 9771d47b (d4210000) + ---[ end trace 0000000000000000 ]--- + +This happens because we are processing an empty block group, which has +no extents allocated from it, there are no items for this block group, +including the block group item since block group items are stored in a +dedicated tree when using the block group tree feature. It also means +this is the block group with the highest start offset, so there are no +higher keys in the extent root, hence btrfs_search_slot_for_read() +returns 1 (no higher key found). + +Fix this by asserting 'ret' is 0 only if the block group tree feature +is not enabled, in which case we should find a block group item for +the block group since it's stored in the extent root and block group +item keys are greater than extent item keys (the value for +BTRFS_BLOCK_GROUP_ITEM_KEY is 192 and for BTRFS_EXTENT_ITEM_KEY and +BTRFS_METADATA_ITEM_KEY the values are 168 and 169 respectively). +In case 'ret' is 1, we just need to add a record to the free space +tree which spans the whole block group, and we can achieve this by +making 'ret == 0' as the while loop's condition. + +Reported-by: syzbot+36fae25c35159a763a2a@syzkaller.appspotmail.com +Link: https://lore.kernel.org/linux-btrfs/6841dca8.a00a0220.d4325.0020.GAE@google.com/ +Reviewed-by: Qu Wenruo +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/free-space-tree.c | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c +index a0d8160b53757..300ee0b68b498 100644 +--- a/fs/btrfs/free-space-tree.c ++++ b/fs/btrfs/free-space-tree.c +@@ -1104,11 +1104,21 @@ static int populate_free_space_tree(struct btrfs_trans_handle *trans, + ret = btrfs_search_slot_for_read(extent_root, &key, path, 1, 0); + if (ret < 0) + goto out_locked; +- ASSERT(ret == 0); ++ /* ++ * If ret is 1 (no key found), it means this is an empty block group, ++ * without any extents allocated from it and there's no block group ++ * item (key BTRFS_BLOCK_GROUP_ITEM_KEY) located in the extent tree ++ * because we are using the block group tree feature, so block group ++ * items are stored in the block group tree. It also means there are no ++ * extents allocated for block groups with a start offset beyond this ++ * block group's end offset (this is the last, highest, block group). ++ */ ++ if (!btrfs_fs_compat_ro(trans->fs_info, BLOCK_GROUP_TREE)) ++ ASSERT(ret == 0); + + start = block_group->start; + end = block_group->start + block_group->length; +- while (1) { ++ while (ret == 0) { + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); + + if (key.type == BTRFS_EXTENT_ITEM_KEY || +@@ -1138,8 +1148,6 @@ static int populate_free_space_tree(struct btrfs_trans_handle *trans, + ret = btrfs_next_item(extent_root, path); + if (ret < 0) + goto out_locked; +- if (ret) +- break; + } + if (start < end) { + ret = __add_to_free_space_tree(trans, block_group, path2, +-- +2.39.5 + diff --git a/queue-6.6/btrfs-fix-inode-lookup-error-handling-during-log-rep.patch b/queue-6.6/btrfs-fix-inode-lookup-error-handling-during-log-rep.patch new file mode 100644 index 0000000000..312bd62856 --- /dev/null +++ b/queue-6.6/btrfs-fix-inode-lookup-error-handling-during-log-rep.patch @@ -0,0 +1,394 @@ +From 1e8024ecd00d91f64a5dd13878369582bfcf9040 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 12 Jul 2025 00:27:22 -0400 +Subject: btrfs: fix inode lookup error handling during log replay + +From: Filipe Manana + +[ Upstream commit 5f61b961599acbd2bed028d3089105a1f7d224b8 ] + +When replaying log trees we use read_one_inode() to get an inode, which is +just a wrapper around btrfs_iget_logging(), which in turn is a wrapper for +btrfs_iget(). But read_one_inode() always returns NULL for any error +that btrfs_iget_logging() / btrfs_iget() may return and this is a problem +because: + +1) In many callers of read_one_inode() we convert the NULL into -EIO, + which is not accurate since btrfs_iget() may return -ENOMEM and -ENOENT + for example, besides -EIO and other errors. So during log replay we + may end up reporting a false -EIO, which is confusing since we may + not have had any IO error at all; + +2) When replaying directory deletes, at replay_dir_deletes(), we assume + the NULL returned from read_one_inode() means that the inode doesn't + exist and then proceed as if no error had happened. This is wrong + because unless btrfs_iget() returned ERR_PTR(-ENOENT), we had an + actual error and the target inode may exist in the target subvolume + root - this may later result in the log replay code failing at a + later stage (if we are "lucky") or succeed but leaving some + inconsistency in the filesystem. + +So fix this by not ignoring errors from btrfs_iget_logging() and as +a consequence remove the read_one_inode() wrapper and just use +btrfs_iget_logging() directly. Also since btrfs_iget_logging() is +supposed to be called only against subvolume roots, just like +read_one_inode() which had a comment about it, add an assertion to +btrfs_iget_logging() to check that the target root corresponds to a +subvolume root. + +Fixes: 5d4f98a28c7d ("Btrfs: Mixed back reference (FORWARD ROLLING FORMAT CHANGE)") +Reviewed-by: Johannes Thumshirn +Reviewed-by: Qu Wenruo +Signed-off-by: Filipe Manana +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/tree-log.c | 223 +++++++++++++++++++++++++++++--------------- + 1 file changed, 146 insertions(+), 77 deletions(-) + +diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c +index f846dcbd70756..16434106c465d 100644 +--- a/fs/btrfs/tree-log.c ++++ b/fs/btrfs/tree-log.c +@@ -145,6 +145,9 @@ static struct btrfs_inode *btrfs_iget_logging(u64 objectid, struct btrfs_root *r + unsigned int nofs_flag; + struct inode *inode; + ++ /* Only meant to be called for subvolume roots and not for log roots. */ ++ ASSERT(is_fstree(btrfs_root_id(root))); ++ + /* + * We're holding a transaction handle whether we are logging or + * replaying a log tree, so we must make sure NOFS semantics apply +@@ -616,20 +619,6 @@ static int read_alloc_one_name(struct extent_buffer *eb, void *start, int len, + return 0; + } + +-/* +- * simple helper to read an inode off the disk from a given root +- * This can only be called for subvolume roots and not for the log +- */ +-static noinline struct inode *read_one_inode(struct btrfs_root *root, +- u64 objectid) +-{ +- struct btrfs_inode *inode; +- +- inode = btrfs_iget_logging(objectid, root); +- if (IS_ERR(inode)) +- return NULL; +- return &inode->vfs_inode; +-} + + /* replays a single extent in 'eb' at 'slot' with 'key' into the + * subvolume 'root'. path is released on entry and should be released +@@ -684,10 +673,15 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans, + goto out; + } + +- inode = read_one_inode(root, key->objectid); +- if (!inode) { +- ret = -EIO; +- goto out; ++ { ++ struct btrfs_inode *btrfs_inode; ++ ++ btrfs_inode = btrfs_iget_logging(key->objectid, root); ++ if (IS_ERR(btrfs_inode)) { ++ ret = PTR_ERR(btrfs_inode); ++ goto out; ++ } ++ inode = &btrfs_inode->vfs_inode; + } + + /* +@@ -966,10 +960,16 @@ static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans, + + btrfs_release_path(path); + +- inode = read_one_inode(root, location.objectid); +- if (!inode) { +- ret = -EIO; +- goto out; ++ { ++ struct btrfs_inode *btrfs_inode; ++ ++ btrfs_inode = btrfs_iget_logging(location.objectid, root); ++ if (IS_ERR(btrfs_inode)) { ++ ret = PTR_ERR(btrfs_inode); ++ inode = NULL; ++ goto out; ++ } ++ inode = &btrfs_inode->vfs_inode; + } + + ret = link_to_fixup_dir(trans, root, path, location.objectid); +@@ -1186,18 +1186,21 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans, + kfree(victim_name.name); + return ret; + } else if (!ret) { +- ret = -ENOENT; +- victim_parent = read_one_inode(root, +- parent_objectid); +- if (victim_parent) { ++ struct btrfs_inode *btrfs_victim; ++ ++ btrfs_victim = btrfs_iget_logging(parent_objectid, root); ++ if (IS_ERR(btrfs_victim)) { ++ ret = PTR_ERR(btrfs_victim); ++ } else { ++ victim_parent = &btrfs_victim->vfs_inode; + inc_nlink(&inode->vfs_inode); + btrfs_release_path(path); + + ret = unlink_inode_for_log_replay(trans, + BTRFS_I(victim_parent), + inode, &victim_name); ++ iput(victim_parent); + } +- iput(victim_parent); + kfree(victim_name.name); + if (ret) + return ret; +@@ -1334,11 +1337,16 @@ static int unlink_old_inode_refs(struct btrfs_trans_handle *trans, + struct inode *dir; + + btrfs_release_path(path); +- dir = read_one_inode(root, parent_id); +- if (!dir) { +- ret = -ENOENT; +- kfree(name.name); +- goto out; ++ { ++ struct btrfs_inode *btrfs_dir; ++ ++ btrfs_dir = btrfs_iget_logging(parent_id, root); ++ if (IS_ERR(btrfs_dir)) { ++ ret = PTR_ERR(btrfs_dir); ++ kfree(name.name); ++ goto out; ++ } ++ dir = &btrfs_dir->vfs_inode; + } + ret = unlink_inode_for_log_replay(trans, BTRFS_I(dir), + inode, &name); +@@ -1409,16 +1417,28 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, + * copy the back ref in. The link count fixup code will take + * care of the rest + */ +- dir = read_one_inode(root, parent_objectid); +- if (!dir) { +- ret = -ENOENT; +- goto out; ++ { ++ struct btrfs_inode *btrfs_dir; ++ ++ btrfs_dir = btrfs_iget_logging(parent_objectid, root); ++ if (IS_ERR(btrfs_dir)) { ++ ret = PTR_ERR(btrfs_dir); ++ dir = NULL; ++ goto out; ++ } ++ dir = &btrfs_dir->vfs_inode; + } + +- inode = read_one_inode(root, inode_objectid); +- if (!inode) { +- ret = -EIO; +- goto out; ++ { ++ struct btrfs_inode *btrfs_inode; ++ ++ btrfs_inode = btrfs_iget_logging(inode_objectid, root); ++ if (IS_ERR(btrfs_inode)) { ++ ret = PTR_ERR(btrfs_inode); ++ inode = NULL; ++ goto out; ++ } ++ inode = &btrfs_inode->vfs_inode; + } + + while (ref_ptr < ref_end) { +@@ -1429,11 +1449,16 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, + * parent object can change from one array + * item to another. + */ +- if (!dir) +- dir = read_one_inode(root, parent_objectid); + if (!dir) { +- ret = -ENOENT; +- goto out; ++ struct btrfs_inode *btrfs_dir; ++ ++ btrfs_dir = btrfs_iget_logging(parent_objectid, root); ++ if (IS_ERR(btrfs_dir)) { ++ ret = PTR_ERR(btrfs_dir); ++ dir = NULL; ++ goto out; ++ } ++ dir = &btrfs_dir->vfs_inode; + } + } else { + ret = ref_get_fields(eb, ref_ptr, &name, &ref_index); +@@ -1701,10 +1726,15 @@ static noinline int fixup_inode_link_counts(struct btrfs_trans_handle *trans, + break; + + btrfs_release_path(path); +- inode = read_one_inode(root, key.offset); +- if (!inode) { +- ret = -EIO; +- break; ++ { ++ struct btrfs_inode *btrfs_inode; ++ ++ btrfs_inode = btrfs_iget_logging(key.offset, root); ++ if (IS_ERR(btrfs_inode)) { ++ ret = PTR_ERR(btrfs_inode); ++ break; ++ } ++ inode = &btrfs_inode->vfs_inode; + } + + ret = fixup_inode_link_count(trans, inode); +@@ -1738,9 +1768,14 @@ static noinline int link_to_fixup_dir(struct btrfs_trans_handle *trans, + int ret = 0; + struct inode *inode; + +- inode = read_one_inode(root, objectid); +- if (!inode) +- return -EIO; ++ { ++ struct btrfs_inode *btrfs_inode; ++ ++ btrfs_inode = btrfs_iget_logging(objectid, root); ++ if (IS_ERR(btrfs_inode)) ++ return PTR_ERR(btrfs_inode); ++ inode = &btrfs_inode->vfs_inode; ++ } + + key.objectid = BTRFS_TREE_LOG_FIXUP_OBJECTID; + key.type = BTRFS_ORPHAN_ITEM_KEY; +@@ -1778,14 +1813,24 @@ static noinline int insert_one_name(struct btrfs_trans_handle *trans, + struct inode *dir; + int ret; + +- inode = read_one_inode(root, location->objectid); +- if (!inode) +- return -ENOENT; ++ { ++ struct btrfs_inode *btrfs_inode; + +- dir = read_one_inode(root, dirid); +- if (!dir) { +- iput(inode); +- return -EIO; ++ btrfs_inode = btrfs_iget_logging(location->objectid, root); ++ if (IS_ERR(btrfs_inode)) ++ return PTR_ERR(btrfs_inode); ++ inode = &btrfs_inode->vfs_inode; ++ } ++ ++ { ++ struct btrfs_inode *btrfs_dir; ++ ++ btrfs_dir = btrfs_iget_logging(dirid, root); ++ if (IS_ERR(btrfs_dir)) { ++ iput(inode); ++ return PTR_ERR(btrfs_dir); ++ } ++ dir = &btrfs_dir->vfs_inode; + } + + ret = btrfs_add_link(trans, BTRFS_I(dir), BTRFS_I(inode), name, +@@ -1863,9 +1908,14 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans, + bool update_size = true; + bool name_added = false; + +- dir = read_one_inode(root, key->objectid); +- if (!dir) +- return -EIO; ++ { ++ struct btrfs_inode *btrfs_dir; ++ ++ btrfs_dir = btrfs_iget_logging(key->objectid, root); ++ if (IS_ERR(btrfs_dir)) ++ return PTR_ERR(btrfs_dir); ++ dir = &btrfs_dir->vfs_inode; ++ } + + ret = read_alloc_one_name(eb, di + 1, btrfs_dir_name_len(eb, di), &name); + if (ret) +@@ -2167,10 +2217,16 @@ static noinline int check_item_in_log(struct btrfs_trans_handle *trans, + btrfs_dir_item_key_to_cpu(eb, di, &location); + btrfs_release_path(path); + btrfs_release_path(log_path); +- inode = read_one_inode(root, location.objectid); +- if (!inode) { +- ret = -EIO; +- goto out; ++ { ++ struct btrfs_inode *btrfs_inode; ++ ++ btrfs_inode = btrfs_iget_logging(location.objectid, root); ++ if (IS_ERR(btrfs_inode)) { ++ ret = PTR_ERR(btrfs_inode); ++ inode = NULL; ++ goto out; ++ } ++ inode = &btrfs_inode->vfs_inode; + } + + ret = link_to_fixup_dir(trans, root, path, location.objectid); +@@ -2321,14 +2377,22 @@ static noinline int replay_dir_deletes(struct btrfs_trans_handle *trans, + if (!log_path) + return -ENOMEM; + +- dir = read_one_inode(root, dirid); +- /* it isn't an error if the inode isn't there, that can happen +- * because we replay the deletes before we copy in the inode item +- * from the log +- */ +- if (!dir) { +- btrfs_free_path(log_path); +- return 0; ++ { ++ struct btrfs_inode *btrfs_dir; ++ ++ btrfs_dir = btrfs_iget_logging(dirid, root); ++ /* ++ * It isn't an error if the inode isn't there, that can happen because ++ * we replay the deletes before we copy in the inode item from the log. ++ */ ++ if (IS_ERR(btrfs_dir)) { ++ btrfs_free_path(log_path); ++ ret = PTR_ERR(btrfs_dir); ++ if (ret == -ENOENT) ++ ret = 0; ++ return ret; ++ } ++ dir = &btrfs_dir->vfs_inode; + } + + range_start = 0; +@@ -2487,10 +2551,15 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb, + struct inode *inode; + u64 from; + +- inode = read_one_inode(root, key.objectid); +- if (!inode) { +- ret = -EIO; +- break; ++ { ++ struct btrfs_inode *btrfs_inode; ++ ++ btrfs_inode = btrfs_iget_logging(key.objectid, root); ++ if (IS_ERR(btrfs_inode)) { ++ ret = PTR_ERR(btrfs_inode); ++ break; ++ } ++ inode = &btrfs_inode->vfs_inode; + } + from = ALIGN(i_size_read(inode), + root->fs_info->sectorsize); +-- +2.39.5 + diff --git a/queue-6.6/btrfs-remove-noinline-from-btrfs_update_inode.patch b/queue-6.6/btrfs-remove-noinline-from-btrfs_update_inode.patch new file mode 100644 index 0000000000..e30150c5a6 --- /dev/null +++ b/queue-6.6/btrfs-remove-noinline-from-btrfs_update_inode.patch @@ -0,0 +1,42 @@ +From 4d5614ed6349bb50fe298651db0f6c4c12a67016 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 22 Sep 2023 11:37:20 +0100 +Subject: btrfs: remove noinline from btrfs_update_inode() + +From: Filipe Manana + +[ Upstream commit cddaaacca9339d2f13599a822dc2f68be71d2e0d ] + +The noinline attribute of btrfs_update_inode() is pointless as the +function is exported and widely used, so remove it. + +Reviewed-by: Qu Wenruo +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Stable-dep-of: 5f61b961599a ("btrfs: fix inode lookup error handling during log replay") +Signed-off-by: Sasha Levin +--- + fs/btrfs/inode.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c +index e8e57abb032d7..c80c918485547 100644 +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -4014,9 +4014,9 @@ static noinline int btrfs_update_inode_item(struct btrfs_trans_handle *trans, + /* + * copy everything in the in-memory inode into the btree. + */ +-noinline int btrfs_update_inode(struct btrfs_trans_handle *trans, +- struct btrfs_root *root, +- struct btrfs_inode *inode) ++int btrfs_update_inode(struct btrfs_trans_handle *trans, ++ struct btrfs_root *root, ++ struct btrfs_inode *inode) + { + struct btrfs_fs_info *fs_info = root->fs_info; + int ret; +-- +2.39.5 + diff --git a/queue-6.6/btrfs-remove-redundant-root-argument-from-btrfs_upda.patch b/queue-6.6/btrfs-remove-redundant-root-argument-from-btrfs_upda.patch new file mode 100644 index 0000000000..461185b379 --- /dev/null +++ b/queue-6.6/btrfs-remove-redundant-root-argument-from-btrfs_upda.patch @@ -0,0 +1,103 @@ +From 2046013002408a5e995162b1488fb407958f75b2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 22 Sep 2023 11:37:21 +0100 +Subject: btrfs: remove redundant root argument from + btrfs_update_inode_fallback() + +From: Filipe Manana + +[ Upstream commit 0a5d0dc55fcb15da016fa28d27bf50ca7f17ec11 ] + +The root argument for btrfs_update_inode_fallback() always matches the +root of the given inode, so remove the root argument and get it from the +inode argument. + +Reviewed-by: Qu Wenruo +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Stable-dep-of: 5f61b961599a ("btrfs: fix inode lookup error handling during log replay") +Signed-off-by: Sasha Levin +--- + fs/btrfs/btrfs_inode.h | 2 +- + fs/btrfs/inode.c | 12 ++++++------ + fs/btrfs/transaction.c | 2 +- + 3 files changed, 8 insertions(+), 8 deletions(-) + +diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h +index ec6679a538c1d..c23c56ead6b23 100644 +--- a/fs/btrfs/btrfs_inode.h ++++ b/fs/btrfs/btrfs_inode.h +@@ -488,7 +488,7 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode, + int btrfs_update_inode(struct btrfs_trans_handle *trans, + struct btrfs_root *root, struct btrfs_inode *inode); + int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans, +- struct btrfs_root *root, struct btrfs_inode *inode); ++ struct btrfs_inode *inode); + int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct btrfs_inode *inode); + int btrfs_orphan_cleanup(struct btrfs_root *root); + int btrfs_cont_expand(struct btrfs_inode *inode, loff_t oldsize, loff_t size); +diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c +index c80c918485547..218d15f5ddf73 100644 +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -3077,7 +3077,7 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent) + goto out; + } + trans->block_rsv = &inode->block_rsv; +- ret = btrfs_update_inode_fallback(trans, root, inode); ++ ret = btrfs_update_inode_fallback(trans, inode); + if (ret) /* -ENOMEM or corruption */ + btrfs_abort_transaction(trans, ret); + goto out; +@@ -3143,7 +3143,7 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent) + &cached_state); + + btrfs_inode_safe_disk_i_size_write(inode, 0); +- ret = btrfs_update_inode_fallback(trans, root, inode); ++ ret = btrfs_update_inode_fallback(trans, inode); + if (ret) { /* -ENOMEM or corruption */ + btrfs_abort_transaction(trans, ret); + goto out; +@@ -4043,13 +4043,13 @@ int btrfs_update_inode(struct btrfs_trans_handle *trans, + } + + int btrfs_update_inode_fallback(struct btrfs_trans_handle *trans, +- struct btrfs_root *root, struct btrfs_inode *inode) ++ struct btrfs_inode *inode) + { + int ret; + +- ret = btrfs_update_inode(trans, root, inode); ++ ret = btrfs_update_inode(trans, inode->root, inode); + if (ret == -ENOSPC) +- return btrfs_update_inode_item(trans, root, inode); ++ return btrfs_update_inode_item(trans, inode->root, inode); + return ret; + } + +@@ -4327,7 +4327,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, + btrfs_i_size_write(dir, dir->vfs_inode.i_size - fname.disk_name.len * 2); + inode_inc_iversion(&dir->vfs_inode); + dir->vfs_inode.i_mtime = inode_set_ctime_current(&dir->vfs_inode); +- ret = btrfs_update_inode_fallback(trans, root, dir); ++ ret = btrfs_update_inode_fallback(trans, dir); + if (ret) + btrfs_abort_transaction(trans, ret); + out: +diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c +index aa03db69a0164..3989cb19cdae7 100644 +--- a/fs/btrfs/transaction.c ++++ b/fs/btrfs/transaction.c +@@ -1860,7 +1860,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, + btrfs_i_size_write(BTRFS_I(parent_inode), parent_inode->i_size + + fname.disk_name.len * 2); + parent_inode->i_mtime = inode_set_ctime_current(parent_inode); +- ret = btrfs_update_inode_fallback(trans, parent_root, BTRFS_I(parent_inode)); ++ ret = btrfs_update_inode_fallback(trans, BTRFS_I(parent_inode)); + if (ret) { + btrfs_abort_transaction(trans, ret); + goto fail; +-- +2.39.5 + diff --git a/queue-6.6/btrfs-remove-redundant-root-argument-from-fixup_inod.patch b/queue-6.6/btrfs-remove-redundant-root-argument-from-fixup_inod.patch new file mode 100644 index 0000000000..2863158192 --- /dev/null +++ b/queue-6.6/btrfs-remove-redundant-root-argument-from-fixup_inod.patch @@ -0,0 +1,108 @@ +From 7589a191facc63eafdf9d09daf111aea0a344c52 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 22 Sep 2023 11:37:26 +0100 +Subject: btrfs: remove redundant root argument from fixup_inode_link_count() + +From: Filipe Manana + +[ Upstream commit 8befc61cbba2d4567122d400542da8900a352971 ] + +The root argument for fixup_inode_link_count() always matches the root of +the given inode, so remove the root argument and get it from the inode +argument. This also applies to the helpers count_inode_extrefs() and +count_inode_refs() used by fixup_inode_link_count() - they don't need the +root argument, as it always matches the root of the inode passed to them. + +Reviewed-by: Qu Wenruo +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Stable-dep-of: 5f61b961599a ("btrfs: fix inode lookup error handling during log replay") +Signed-off-by: Sasha Levin +--- + fs/btrfs/tree-log.c | 20 +++++++++----------- + 1 file changed, 9 insertions(+), 11 deletions(-) + +diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c +index 13377c3b22897..a17942f4c155b 100644 +--- a/fs/btrfs/tree-log.c ++++ b/fs/btrfs/tree-log.c +@@ -1504,8 +1504,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, + return ret; + } + +-static int count_inode_extrefs(struct btrfs_root *root, +- struct btrfs_inode *inode, struct btrfs_path *path) ++static int count_inode_extrefs(struct btrfs_inode *inode, struct btrfs_path *path) + { + int ret = 0; + int name_len; +@@ -1519,8 +1518,8 @@ static int count_inode_extrefs(struct btrfs_root *root, + struct extent_buffer *leaf; + + while (1) { +- ret = btrfs_find_one_extref(root, inode_objectid, offset, path, +- &extref, &offset); ++ ret = btrfs_find_one_extref(inode->root, inode_objectid, offset, ++ path, &extref, &offset); + if (ret) + break; + +@@ -1548,8 +1547,7 @@ static int count_inode_extrefs(struct btrfs_root *root, + return nlink; + } + +-static int count_inode_refs(struct btrfs_root *root, +- struct btrfs_inode *inode, struct btrfs_path *path) ++static int count_inode_refs(struct btrfs_inode *inode, struct btrfs_path *path) + { + int ret; + struct btrfs_key key; +@@ -1564,7 +1562,7 @@ static int count_inode_refs(struct btrfs_root *root, + key.offset = (u64)-1; + + while (1) { +- ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); ++ ret = btrfs_search_slot(NULL, inode->root, &key, path, 0, 0); + if (ret < 0) + break; + if (ret > 0) { +@@ -1616,9 +1614,9 @@ static int count_inode_refs(struct btrfs_root *root, + * will free the inode. + */ + static noinline int fixup_inode_link_count(struct btrfs_trans_handle *trans, +- struct btrfs_root *root, + struct inode *inode) + { ++ struct btrfs_root *root = BTRFS_I(inode)->root; + struct btrfs_path *path; + int ret; + u64 nlink = 0; +@@ -1628,13 +1626,13 @@ static noinline int fixup_inode_link_count(struct btrfs_trans_handle *trans, + if (!path) + return -ENOMEM; + +- ret = count_inode_refs(root, BTRFS_I(inode), path); ++ ret = count_inode_refs(BTRFS_I(inode), path); + if (ret < 0) + goto out; + + nlink = ret; + +- ret = count_inode_extrefs(root, BTRFS_I(inode), path); ++ ret = count_inode_extrefs(BTRFS_I(inode), path); + if (ret < 0) + goto out; + +@@ -1706,7 +1704,7 @@ static noinline int fixup_inode_link_counts(struct btrfs_trans_handle *trans, + break; + } + +- ret = fixup_inode_link_count(trans, root, inode); ++ ret = fixup_inode_link_count(trans, inode); + iput(inode); + if (ret) + break; +-- +2.39.5 + diff --git a/queue-6.6/btrfs-return-a-btrfs_inode-from-btrfs_iget_logging.patch b/queue-6.6/btrfs-return-a-btrfs_inode-from-btrfs_iget_logging.patch new file mode 100644 index 0000000000..bc6cf25301 --- /dev/null +++ b/queue-6.6/btrfs-return-a-btrfs_inode-from-btrfs_iget_logging.patch @@ -0,0 +1,286 @@ +From 225397e09834c0a56317857e74a78b486a394ee4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 6 Mar 2025 16:42:28 +0000 +Subject: btrfs: return a btrfs_inode from btrfs_iget_logging() + +From: Filipe Manana + +[ Upstream commit a488d8ac2c4d96ecc7da59bb35a573277204ac6b ] + +All callers of btrfs_iget_logging() are interested in the btrfs_inode +structure rather than the VFS inode, so make btrfs_iget_logging() return +the btrfs_inode instead, avoiding lots of BTRFS_I() calls. + +Signed-off-by: Filipe Manana +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Stable-dep-of: 5f61b961599a ("btrfs: fix inode lookup error handling during log replay") +Signed-off-by: Sasha Levin +--- + fs/btrfs/tree-log.c | 94 ++++++++++++++++++++++----------------------- + 1 file changed, 45 insertions(+), 49 deletions(-) + +diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c +index a17942f4c155b..f846dcbd70756 100644 +--- a/fs/btrfs/tree-log.c ++++ b/fs/btrfs/tree-log.c +@@ -140,7 +140,7 @@ static void wait_log_commit(struct btrfs_root *root, int transid); + * and once to do all the other items. + */ + +-static struct inode *btrfs_iget_logging(u64 objectid, struct btrfs_root *root) ++static struct btrfs_inode *btrfs_iget_logging(u64 objectid, struct btrfs_root *root) + { + unsigned int nofs_flag; + struct inode *inode; +@@ -156,7 +156,10 @@ static struct inode *btrfs_iget_logging(u64 objectid, struct btrfs_root *root) + inode = btrfs_iget(root->fs_info->sb, objectid, root); + memalloc_nofs_restore(nofs_flag); + +- return inode; ++ if (IS_ERR(inode)) ++ return ERR_CAST(inode); ++ ++ return BTRFS_I(inode); + } + + /* +@@ -620,12 +623,12 @@ static int read_alloc_one_name(struct extent_buffer *eb, void *start, int len, + static noinline struct inode *read_one_inode(struct btrfs_root *root, + u64 objectid) + { +- struct inode *inode; ++ struct btrfs_inode *inode; + + inode = btrfs_iget_logging(objectid, root); + if (IS_ERR(inode)) +- inode = NULL; +- return inode; ++ return NULL; ++ return &inode->vfs_inode; + } + + /* replays a single extent in 'eb' at 'slot' with 'key' into the +@@ -5419,7 +5422,6 @@ static int log_new_dir_dentries(struct btrfs_trans_handle *trans, + ihold(&curr_inode->vfs_inode); + + while (true) { +- struct inode *vfs_inode; + struct btrfs_key key; + struct btrfs_key found_key; + u64 next_index; +@@ -5435,7 +5437,7 @@ static int log_new_dir_dentries(struct btrfs_trans_handle *trans, + struct extent_buffer *leaf = path->nodes[0]; + struct btrfs_dir_item *di; + struct btrfs_key di_key; +- struct inode *di_inode; ++ struct btrfs_inode *di_inode; + int log_mode = LOG_INODE_EXISTS; + int type; + +@@ -5462,17 +5464,16 @@ static int log_new_dir_dentries(struct btrfs_trans_handle *trans, + goto out; + } + +- if (!need_log_inode(trans, BTRFS_I(di_inode))) { +- btrfs_add_delayed_iput(BTRFS_I(di_inode)); ++ if (!need_log_inode(trans, di_inode)) { ++ btrfs_add_delayed_iput(di_inode); + break; + } + + ctx->log_new_dentries = false; + if (type == BTRFS_FT_DIR) + log_mode = LOG_INODE_ALL; +- ret = btrfs_log_inode(trans, BTRFS_I(di_inode), +- log_mode, ctx); +- btrfs_add_delayed_iput(BTRFS_I(di_inode)); ++ ret = btrfs_log_inode(trans, di_inode, log_mode, ctx); ++ btrfs_add_delayed_iput(di_inode); + if (ret) + goto out; + if (ctx->log_new_dentries) { +@@ -5514,14 +5515,13 @@ static int log_new_dir_dentries(struct btrfs_trans_handle *trans, + kfree(dir_elem); + + btrfs_add_delayed_iput(curr_inode); +- curr_inode = NULL; + +- vfs_inode = btrfs_iget_logging(ino, root); +- if (IS_ERR(vfs_inode)) { +- ret = PTR_ERR(vfs_inode); ++ curr_inode = btrfs_iget_logging(ino, root); ++ if (IS_ERR(curr_inode)) { ++ ret = PTR_ERR(curr_inode); ++ curr_inode = NULL; + break; + } +- curr_inode = BTRFS_I(vfs_inode); + } + out: + btrfs_free_path(path); +@@ -5599,7 +5599,7 @@ static int add_conflicting_inode(struct btrfs_trans_handle *trans, + struct btrfs_log_ctx *ctx) + { + struct btrfs_ino_list *ino_elem; +- struct inode *inode; ++ struct btrfs_inode *inode; + + /* + * It's rare to have a lot of conflicting inodes, in practice it is not +@@ -5690,12 +5690,12 @@ static int add_conflicting_inode(struct btrfs_trans_handle *trans, + * inode in LOG_INODE_EXISTS mode and rename operations update the log, + * so that the log ends up with the new name and without the old name. + */ +- if (!need_log_inode(trans, BTRFS_I(inode))) { +- btrfs_add_delayed_iput(BTRFS_I(inode)); ++ if (!need_log_inode(trans, inode)) { ++ btrfs_add_delayed_iput(inode); + return 0; + } + +- btrfs_add_delayed_iput(BTRFS_I(inode)); ++ btrfs_add_delayed_iput(inode); + + ino_elem = kmalloc(sizeof(*ino_elem), GFP_NOFS); + if (!ino_elem) +@@ -5731,7 +5731,7 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans, + */ + while (!list_empty(&ctx->conflict_inodes)) { + struct btrfs_ino_list *curr; +- struct inode *inode; ++ struct btrfs_inode *inode; + u64 ino; + u64 parent; + +@@ -5767,9 +5767,8 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans, + * dir index key range logged for the directory. So we + * must make sure the deletion is recorded. + */ +- ret = btrfs_log_inode(trans, BTRFS_I(inode), +- LOG_INODE_ALL, ctx); +- btrfs_add_delayed_iput(BTRFS_I(inode)); ++ ret = btrfs_log_inode(trans, inode, LOG_INODE_ALL, ctx); ++ btrfs_add_delayed_iput(inode); + if (ret) + break; + continue; +@@ -5785,8 +5784,8 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans, + * it again because if some other task logged the inode after + * that, we can avoid doing it again. + */ +- if (!need_log_inode(trans, BTRFS_I(inode))) { +- btrfs_add_delayed_iput(BTRFS_I(inode)); ++ if (!need_log_inode(trans, inode)) { ++ btrfs_add_delayed_iput(inode); + continue; + } + +@@ -5797,8 +5796,8 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans, + * well because during a rename we pin the log and update the + * log with the new name before we unpin it. + */ +- ret = btrfs_log_inode(trans, BTRFS_I(inode), LOG_INODE_EXISTS, ctx); +- btrfs_add_delayed_iput(BTRFS_I(inode)); ++ ret = btrfs_log_inode(trans, inode, LOG_INODE_EXISTS, ctx); ++ btrfs_add_delayed_iput(inode); + if (ret) + break; + } +@@ -6290,7 +6289,7 @@ static int log_new_delayed_dentries(struct btrfs_trans_handle *trans, + + list_for_each_entry(item, delayed_ins_list, log_list) { + struct btrfs_dir_item *dir_item; +- struct inode *di_inode; ++ struct btrfs_inode *di_inode; + struct btrfs_key key; + int log_mode = LOG_INODE_EXISTS; + +@@ -6306,8 +6305,8 @@ static int log_new_delayed_dentries(struct btrfs_trans_handle *trans, + break; + } + +- if (!need_log_inode(trans, BTRFS_I(di_inode))) { +- btrfs_add_delayed_iput(BTRFS_I(di_inode)); ++ if (!need_log_inode(trans, di_inode)) { ++ btrfs_add_delayed_iput(di_inode); + continue; + } + +@@ -6315,12 +6314,12 @@ static int log_new_delayed_dentries(struct btrfs_trans_handle *trans, + log_mode = LOG_INODE_ALL; + + ctx->log_new_dentries = false; +- ret = btrfs_log_inode(trans, BTRFS_I(di_inode), log_mode, ctx); ++ ret = btrfs_log_inode(trans, di_inode, log_mode, ctx); + + if (!ret && ctx->log_new_dentries) +- ret = log_new_dir_dentries(trans, BTRFS_I(di_inode), ctx); ++ ret = log_new_dir_dentries(trans, di_inode, ctx); + +- btrfs_add_delayed_iput(BTRFS_I(di_inode)); ++ btrfs_add_delayed_iput(di_inode); + + if (ret) + break; +@@ -6728,7 +6727,7 @@ static int btrfs_log_all_parents(struct btrfs_trans_handle *trans, + ptr = btrfs_item_ptr_offset(leaf, slot); + while (cur_offset < item_size) { + struct btrfs_key inode_key; +- struct inode *dir_inode; ++ struct btrfs_inode *dir_inode; + + inode_key.type = BTRFS_INODE_ITEM_KEY; + inode_key.offset = 0; +@@ -6777,18 +6776,16 @@ static int btrfs_log_all_parents(struct btrfs_trans_handle *trans, + goto out; + } + +- if (!need_log_inode(trans, BTRFS_I(dir_inode))) { +- btrfs_add_delayed_iput(BTRFS_I(dir_inode)); ++ if (!need_log_inode(trans, dir_inode)) { ++ btrfs_add_delayed_iput(dir_inode); + continue; + } + + ctx->log_new_dentries = false; +- ret = btrfs_log_inode(trans, BTRFS_I(dir_inode), +- LOG_INODE_ALL, ctx); ++ ret = btrfs_log_inode(trans, dir_inode, LOG_INODE_ALL, ctx); + if (!ret && ctx->log_new_dentries) +- ret = log_new_dir_dentries(trans, +- BTRFS_I(dir_inode), ctx); +- btrfs_add_delayed_iput(BTRFS_I(dir_inode)); ++ ret = log_new_dir_dentries(trans, dir_inode, ctx); ++ btrfs_add_delayed_iput(dir_inode); + if (ret) + goto out; + } +@@ -6813,7 +6810,7 @@ static int log_new_ancestors(struct btrfs_trans_handle *trans, + struct extent_buffer *leaf; + int slot; + struct btrfs_key search_key; +- struct inode *inode; ++ struct btrfs_inode *inode; + u64 ino; + int ret = 0; + +@@ -6828,11 +6825,10 @@ static int log_new_ancestors(struct btrfs_trans_handle *trans, + if (IS_ERR(inode)) + return PTR_ERR(inode); + +- if (BTRFS_I(inode)->generation >= trans->transid && +- need_log_inode(trans, BTRFS_I(inode))) +- ret = btrfs_log_inode(trans, BTRFS_I(inode), +- LOG_INODE_EXISTS, ctx); +- btrfs_add_delayed_iput(BTRFS_I(inode)); ++ if (inode->generation >= trans->transid && ++ need_log_inode(trans, inode)) ++ ret = btrfs_log_inode(trans, inode, LOG_INODE_EXISTS, ctx); ++ btrfs_add_delayed_iput(inode); + if (ret) + return ret; + +-- +2.39.5 + diff --git a/queue-6.6/can-m_can-m_can_handle_lost_msg-downgrade-msg-lost-i.patch b/queue-6.6/can-m_can-m_can_handle_lost_msg-downgrade-msg-lost-i.patch new file mode 100644 index 0000000000..ef5276d202 --- /dev/null +++ b/queue-6.6/can-m_can-m_can_handle_lost_msg-downgrade-msg-lost-i.patch @@ -0,0 +1,40 @@ +From aea88a3e8086aca0aa44d1cc72fb9b1ecc8277eb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 11 Jul 2025 12:12:02 +0200 +Subject: can: m_can: m_can_handle_lost_msg(): downgrade msg lost in rx message + to debug level + +From: Sean Nyekjaer + +[ Upstream commit 58805e9cbc6f6a28f35d90e740956e983a0e036e ] + +Downgrade the "msg lost in rx" message to debug level, to prevent +flooding the kernel log with error messages. + +Fixes: e0d1f4816f2a ("can: m_can: add Bosch M_CAN controller support") +Reviewed-by: Vincent Mailhol +Signed-off-by: Sean Nyekjaer +Link: https://patch.msgid.link/20250711-mcan_ratelimit-v3-1-7413e8e21b84@geanix.com +[mkl: enhance commit message] +Signed-off-by: Marc Kleine-Budde +Signed-off-by: Sasha Levin +--- + drivers/net/can/m_can/m_can.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c +index ba7f7de25c852..e6a74d66f0d8c 100644 +--- a/drivers/net/can/m_can/m_can.c ++++ b/drivers/net/can/m_can/m_can.c +@@ -606,7 +606,7 @@ static int m_can_handle_lost_msg(struct net_device *dev) + struct can_frame *frame; + u32 timestamp = 0; + +- netdev_err(dev, "msg lost in rxf0\n"); ++ netdev_dbg(dev, "msg lost in rxf0\n"); + + stats->rx_errors++; + stats->rx_over_errors++; +-- +2.39.5 + diff --git a/queue-6.6/cifs-all-initializations-for-tcon-should-happen-in-t.patch b/queue-6.6/cifs-all-initializations-for-tcon-should-happen-in-t.patch new file mode 100644 index 0000000000..94838f825d --- /dev/null +++ b/queue-6.6/cifs-all-initializations-for-tcon-should-happen-in-t.patch @@ -0,0 +1,91 @@ +From 30888b66bbf8d179e80e2756a313f272c8829bfb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 30 Jun 2025 23:09:34 +0530 +Subject: cifs: all initializations for tcon should happen in tcon_info_alloc + +From: Shyam Prasad N + +[ Upstream commit 74ebd02163fde05baa23129e06dde4b8f0f2377a ] + +Today, a few work structs inside tcon are initialized inside +cifs_get_tcon and not in tcon_info_alloc. As a result, if a tcon +is obtained from tcon_info_alloc, but not called as a part of +cifs_get_tcon, we may trip over. + +Cc: +Signed-off-by: Shyam Prasad N +Reviewed-by: Paulo Alcantara (Red Hat) +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/smb/client/cifsproto.h | 1 + + fs/smb/client/connect.c | 8 +------- + fs/smb/client/misc.c | 6 ++++++ + 3 files changed, 8 insertions(+), 7 deletions(-) + +diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h +index 8edb6fe89a97c..5ab877e480abc 100644 +--- a/fs/smb/client/cifsproto.h ++++ b/fs/smb/client/cifsproto.h +@@ -136,6 +136,7 @@ extern int SendReceiveBlockingLock(const unsigned int xid, + struct smb_hdr *out_buf, + int *bytes_returned); + ++void smb2_query_server_interfaces(struct work_struct *work); + void + cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server, + bool all_channels); +diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c +index 14be8822d23a2..33a292dabdb87 100644 +--- a/fs/smb/client/connect.c ++++ b/fs/smb/client/connect.c +@@ -113,7 +113,7 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server) + return rc; + } + +-static void smb2_query_server_interfaces(struct work_struct *work) ++void smb2_query_server_interfaces(struct work_struct *work) + { + int rc; + int xid; +@@ -2818,20 +2818,14 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) + tcon->max_cached_dirs = ctx->max_cached_dirs; + tcon->nodelete = ctx->nodelete; + tcon->local_lease = ctx->local_lease; +- INIT_LIST_HEAD(&tcon->pending_opens); + tcon->status = TID_GOOD; + +- INIT_DELAYED_WORK(&tcon->query_interfaces, +- smb2_query_server_interfaces); + if (ses->server->dialect >= SMB30_PROT_ID && + (ses->server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) { + /* schedule query interfaces poll */ + queue_delayed_work(cifsiod_wq, &tcon->query_interfaces, + (SMB_INTERFACE_POLL_INTERVAL * HZ)); + } +-#ifdef CONFIG_CIFS_DFS_UPCALL +- INIT_DELAYED_WORK(&tcon->dfs_cache_work, dfs_cache_refresh); +-#endif + spin_lock(&cifs_tcp_ses_lock); + list_add(&tcon->tcon_list, &ses->tcon_list); + spin_unlock(&cifs_tcp_ses_lock); +diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c +index 2e9a14e28e466..bbbe48447765d 100644 +--- a/fs/smb/client/misc.c ++++ b/fs/smb/client/misc.c +@@ -148,6 +148,12 @@ tcon_info_alloc(bool dir_leases_enabled, enum smb3_tcon_ref_trace trace) + #ifdef CONFIG_CIFS_DFS_UPCALL + INIT_LIST_HEAD(&ret_buf->dfs_ses_list); + #endif ++ INIT_LIST_HEAD(&ret_buf->pending_opens); ++ INIT_DELAYED_WORK(&ret_buf->query_interfaces, ++ smb2_query_server_interfaces); ++#ifdef CONFIG_CIFS_DFS_UPCALL ++ INIT_DELAYED_WORK(&ret_buf->dfs_cache_work, dfs_cache_refresh); ++#endif + + return ret_buf; + } +-- +2.39.5 + diff --git a/queue-6.6/drm-tegra-nvdec-fix-dma_alloc_coherent-error-check.patch b/queue-6.6/drm-tegra-nvdec-fix-dma_alloc_coherent-error-check.patch new file mode 100644 index 0000000000..aac5fa8ff2 --- /dev/null +++ b/queue-6.6/drm-tegra-nvdec-fix-dma_alloc_coherent-error-check.patch @@ -0,0 +1,41 @@ +From 0a0eeca827173b7911aaa347a24816c11396d162 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 2 Jul 2025 11:08:07 +0900 +Subject: drm/tegra: nvdec: Fix dma_alloc_coherent error check + +From: Mikko Perttunen + +[ Upstream commit 44306a684cd1699b8562a54945ddc43e2abc9eab ] + +Check for NULL return value with dma_alloc_coherent, in line with +Robin's fix for vic.c in 'drm/tegra: vic: Fix DMA API misuse'. + +Fixes: 46f226c93d35 ("drm/tegra: Add NVDEC driver") +Signed-off-by: Mikko Perttunen +Signed-off-by: Thierry Reding +Link: https://lore.kernel.org/r/20250702-nvdec-dma-error-check-v1-1-c388b402c53a@nvidia.com +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/tegra/nvdec.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/tegra/nvdec.c b/drivers/gpu/drm/tegra/nvdec.c +index 4860790666af5..14ef61b44f47c 100644 +--- a/drivers/gpu/drm/tegra/nvdec.c ++++ b/drivers/gpu/drm/tegra/nvdec.c +@@ -261,10 +261,8 @@ static int nvdec_load_falcon_firmware(struct nvdec *nvdec) + + if (!client->group) { + virt = dma_alloc_coherent(nvdec->dev, size, &iova, GFP_KERNEL); +- +- err = dma_mapping_error(nvdec->dev, iova); +- if (err < 0) +- return err; ++ if (!virt) ++ return -ENOMEM; + } else { + virt = tegra_drm_alloc(tegra, size, &iova); + if (IS_ERR(virt)) +-- +2.39.5 + diff --git a/queue-6.6/hid-add-ignore-quirk-for-smartlinktechnology.patch b/queue-6.6/hid-add-ignore-quirk-for-smartlinktechnology.patch new file mode 100644 index 0000000000..1d174a006f --- /dev/null +++ b/queue-6.6/hid-add-ignore-quirk-for-smartlinktechnology.patch @@ -0,0 +1,65 @@ +From 9ef9af8e9df127e45fb899b4e3875a6a62429981 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 5 Jun 2025 15:29:59 +0800 +Subject: HID: Add IGNORE quirk for SMARTLINKTECHNOLOGY + +From: Zhang Heng + +[ Upstream commit 1a8953f4f7746c6a515989774fe03047c522c613 ] + +MARTLINKTECHNOLOGY is a microphone device, when the HID interface in an +audio device is requested to get specific report id, the following error +may occur. + +[ 562.939373] usb 1-1.4.1.2: new full-speed USB device number 21 using xhci_hcd +[ 563.104908] usb 1-1.4.1.2: New USB device found, idVendor=4c4a, idProduct=4155, bcdDevice= 1.00 +[ 563.104910] usb 1-1.4.1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3 +[ 563.104911] usb 1-1.4.1.2: Product: USB Composite Device +[ 563.104912] usb 1-1.4.1.2: Manufacturer: SmartlinkTechnology +[ 563.104913] usb 1-1.4.1.2: SerialNumber: 20201111000001 +[ 563.229499] input: SmartlinkTechnology USB Composite Device as /devices/pci0000:00/0000:00:07.1/0000:04:00.3/usb1/1-1/1-1.4/1-1.4.1/1-1.4.1.2/1-1.4.1.2:1.2/0003:4C4A:4155.000F/input/input35 +[ 563.291505] hid-generic 0003:4C4A:4155.000F: input,hidraw2: USB HID v2.01 Keyboard [SmartlinkTechnology USB Composite Device] on usb-0000:04:00.3-1.4.1.2/input2 +[ 563.291557] usbhid 1-1.4.1.2:1.3: couldn't find an input interrupt endpoint +[ 568.506654] usb 1-1.4.1.2: 1:1: usb_set_interface failed (-110) +[ 573.626656] usb 1-1.4.1.2: 1:1: usb_set_interface failed (-110) +[ 578.746657] usb 1-1.4.1.2: 1:1: usb_set_interface failed (-110) +[ 583.866655] usb 1-1.4.1.2: 1:1: usb_set_interface failed (-110) +[ 588.986657] usb 1-1.4.1.2: 1:1: usb_set_interface failed (-110) + +Ignore HID interface. The device is working properly. + +Signed-off-by: Zhang Heng +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-ids.h | 3 +++ + drivers/hid/hid-quirks.c | 1 + + 2 files changed, 4 insertions(+) + +diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h +index c0ce11393f0fb..f344df00db03a 100644 +--- a/drivers/hid/hid-ids.h ++++ b/drivers/hid/hid-ids.h +@@ -1502,4 +1502,7 @@ + #define USB_VENDOR_ID_SIGNOTEC 0x2133 + #define USB_DEVICE_ID_SIGNOTEC_VIEWSONIC_PD1011 0x0018 + ++#define USB_VENDOR_ID_SMARTLINKTECHNOLOGY 0x4c4a ++#define USB_DEVICE_ID_SMARTLINKTECHNOLOGY_4155 0x4155 ++ + #endif +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +index 73979643315bf..e4d80307b898c 100644 +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -894,6 +894,7 @@ static const struct hid_device_id hid_ignore_list[] = { + #endif + { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) }, + { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_HP_5MP_CAMERA_5473) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_SMARTLINKTECHNOLOGY, USB_DEVICE_ID_SMARTLINKTECHNOLOGY_4155) }, + { } + }; + +-- +2.39.5 + diff --git a/queue-6.6/hid-lenovo-add-support-for-thinkpad-x1-tablet-thin-k.patch b/queue-6.6/hid-lenovo-add-support-for-thinkpad-x1-tablet-thin-k.patch new file mode 100644 index 0000000000..9bdd9c76c2 --- /dev/null +++ b/queue-6.6/hid-lenovo-add-support-for-thinkpad-x1-tablet-thin-k.patch @@ -0,0 +1,120 @@ +From 53d66d2d79c4d0f2adbed2b285ca87a281605742 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 12 Jun 2025 13:34:38 +0900 +Subject: HID: lenovo: Add support for ThinkPad X1 Tablet Thin Keyboard Gen2 + +From: Akira Inoue + +[ Upstream commit a8905238c3bbe13db90065ed74682418f23830c3 ] + +Add "Thinkpad X1 Tablet Gen 2 Keyboard" PID to hid-lenovo driver to fix trackpoint not working issue. + +Signed-off-by: Akira Inoue +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-ids.h | 1 + + drivers/hid/hid-lenovo.c | 8 ++++++++ + drivers/hid/hid-multitouch.c | 8 +++++++- + 3 files changed, 16 insertions(+), 1 deletion(-) + +diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h +index a8665d57094b2..c0ce11393f0fb 100644 +--- a/drivers/hid/hid-ids.h ++++ b/drivers/hid/hid-ids.h +@@ -807,6 +807,7 @@ + #define USB_DEVICE_ID_LENOVO_TPPRODOCK 0x6067 + #define USB_DEVICE_ID_LENOVO_X1_COVER 0x6085 + #define USB_DEVICE_ID_LENOVO_X1_TAB 0x60a3 ++#define USB_DEVICE_ID_LENOVO_X1_TAB2 0x60a4 + #define USB_DEVICE_ID_LENOVO_X1_TAB3 0x60b5 + #define USB_DEVICE_ID_LENOVO_X12_TAB 0x60fe + #define USB_DEVICE_ID_LENOVO_X12_TAB2 0x61ae +diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c +index ee65da98c7d5b..32cb2e75228c4 100644 +--- a/drivers/hid/hid-lenovo.c ++++ b/drivers/hid/hid-lenovo.c +@@ -473,6 +473,7 @@ static int lenovo_input_mapping(struct hid_device *hdev, + return lenovo_input_mapping_tp10_ultrabook_kbd(hdev, hi, field, + usage, bit, max); + case USB_DEVICE_ID_LENOVO_X1_TAB: ++ case USB_DEVICE_ID_LENOVO_X1_TAB2: + case USB_DEVICE_ID_LENOVO_X1_TAB3: + return lenovo_input_mapping_x1_tab_kbd(hdev, hi, field, usage, bit, max); + default: +@@ -587,6 +588,7 @@ static ssize_t attr_fn_lock_store(struct device *dev, + break; + case USB_DEVICE_ID_LENOVO_TP10UBKBD: + case USB_DEVICE_ID_LENOVO_X1_TAB: ++ case USB_DEVICE_ID_LENOVO_X1_TAB2: + case USB_DEVICE_ID_LENOVO_X1_TAB3: + ret = lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, value); + if (ret) +@@ -782,6 +784,7 @@ static int lenovo_event(struct hid_device *hdev, struct hid_field *field, + return lenovo_event_cptkbd(hdev, field, usage, value); + case USB_DEVICE_ID_LENOVO_TP10UBKBD: + case USB_DEVICE_ID_LENOVO_X1_TAB: ++ case USB_DEVICE_ID_LENOVO_X1_TAB2: + case USB_DEVICE_ID_LENOVO_X1_TAB3: + return lenovo_event_tp10ubkbd(hdev, field, usage, value); + default: +@@ -1065,6 +1068,7 @@ static int lenovo_led_brightness_set(struct led_classdev *led_cdev, + break; + case USB_DEVICE_ID_LENOVO_TP10UBKBD: + case USB_DEVICE_ID_LENOVO_X1_TAB: ++ case USB_DEVICE_ID_LENOVO_X1_TAB2: + case USB_DEVICE_ID_LENOVO_X1_TAB3: + ret = lenovo_led_set_tp10ubkbd(hdev, tp10ubkbd_led[led_nr], value); + break; +@@ -1296,6 +1300,7 @@ static int lenovo_probe(struct hid_device *hdev, + break; + case USB_DEVICE_ID_LENOVO_TP10UBKBD: + case USB_DEVICE_ID_LENOVO_X1_TAB: ++ case USB_DEVICE_ID_LENOVO_X1_TAB2: + case USB_DEVICE_ID_LENOVO_X1_TAB3: + ret = lenovo_probe_tp10ubkbd(hdev); + break; +@@ -1383,6 +1388,7 @@ static void lenovo_remove(struct hid_device *hdev) + break; + case USB_DEVICE_ID_LENOVO_TP10UBKBD: + case USB_DEVICE_ID_LENOVO_X1_TAB: ++ case USB_DEVICE_ID_LENOVO_X1_TAB2: + case USB_DEVICE_ID_LENOVO_X1_TAB3: + lenovo_remove_tp10ubkbd(hdev); + break; +@@ -1433,6 +1439,8 @@ static const struct hid_device_id lenovo_devices[] = { + */ + { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, + USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_TAB) }, ++ { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, ++ USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_TAB2) }, + { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, + USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_TAB3) }, + { } +diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c +index 6386043aab0bb..becd4c1ccf93c 100644 +--- a/drivers/hid/hid-multitouch.c ++++ b/drivers/hid/hid-multitouch.c +@@ -2110,12 +2110,18 @@ static const struct hid_device_id mt_devices[] = { + HID_DEVICE(BUS_I2C, HID_GROUP_GENERIC, + USB_VENDOR_ID_LG, I2C_DEVICE_ID_LG_7010) }, + +- /* Lenovo X1 TAB Gen 2 */ ++ /* Lenovo X1 TAB Gen 1 */ + { .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT, + HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, + USB_VENDOR_ID_LENOVO, + USB_DEVICE_ID_LENOVO_X1_TAB) }, + ++ /* Lenovo X1 TAB Gen 2 */ ++ { .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT, ++ HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, ++ USB_VENDOR_ID_LENOVO, ++ USB_DEVICE_ID_LENOVO_X1_TAB2) }, ++ + /* Lenovo X1 TAB Gen 3 */ + { .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT, + HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, +-- +2.39.5 + diff --git a/queue-6.6/hid-quirks-add-quirk-for-2-chicony-electronics-hp-5m.patch b/queue-6.6/hid-quirks-add-quirk-for-2-chicony-electronics-hp-5m.patch new file mode 100644 index 0000000000..c96659c094 --- /dev/null +++ b/queue-6.6/hid-quirks-add-quirk-for-2-chicony-electronics-hp-5m.patch @@ -0,0 +1,54 @@ +From 55e2f2a124a2408e3a599717d594643752db2fad Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 6 May 2025 13:50:15 +0800 +Subject: HID: quirks: Add quirk for 2 Chicony Electronics HP 5MP Cameras + +From: Chia-Lin Kao (AceLan) + +[ Upstream commit 54bae4c17c11688339eb73a04fd24203bb6e7494 ] + +The Chicony Electronics HP 5MP Cameras (USB ID 04F2:B824 & 04F2:B82C) +report a HID sensor interface that is not actually implemented. +Attempting to access this non-functional sensor via iio_info causes +system hangs as runtime PM tries to wake up an unresponsive sensor. + +Add these 2 devices to the HID ignore list since the sensor interface is +non-functional by design and should not be exposed to userspace. + +Signed-off-by: Chia-Lin Kao (AceLan) +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +--- + drivers/hid/hid-ids.h | 2 ++ + drivers/hid/hid-quirks.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h +index f344df00db03a..0d1d7162814f3 100644 +--- a/drivers/hid/hid-ids.h ++++ b/drivers/hid/hid-ids.h +@@ -305,6 +305,8 @@ + #define USB_DEVICE_ID_ASUS_AK1D 0x1125 + #define USB_DEVICE_ID_CHICONY_TOSHIBA_WT10A 0x1408 + #define USB_DEVICE_ID_CHICONY_ACER_SWITCH12 0x1421 ++#define USB_DEVICE_ID_CHICONY_HP_5MP_CAMERA 0xb824 ++#define USB_DEVICE_ID_CHICONY_HP_5MP_CAMERA2 0xb82c + + #define USB_VENDOR_ID_CHUNGHWAT 0x2247 + #define USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH 0x0001 +diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c +index e4d80307b898c..80372342c176a 100644 +--- a/drivers/hid/hid-quirks.c ++++ b/drivers/hid/hid-quirks.c +@@ -747,6 +747,8 @@ static const struct hid_device_id hid_ignore_list[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AXENTIA, USB_DEVICE_ID_AXENTIA_FM_RADIO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_HP_5MP_CAMERA) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_HP_5MP_CAMERA2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CIDC, 0x0103) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI4713) }, +-- +2.39.5 + diff --git a/queue-6.6/ibmvnic-fix-hardcoded-num_rx_stats-num_tx_stats-with.patch b/queue-6.6/ibmvnic-fix-hardcoded-num_rx_stats-num_tx_stats-with.patch new file mode 100644 index 0000000000..bdc6b8c73d --- /dev/null +++ b/queue-6.6/ibmvnic-fix-hardcoded-num_rx_stats-num_tx_stats-with.patch @@ -0,0 +1,68 @@ +From 1d5b7e80dd418664f397061fb40e06c5d8bdd1c7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 9 Jul 2025 08:33:32 -0700 +Subject: ibmvnic: Fix hardcoded NUM_RX_STATS/NUM_TX_STATS with dynamic sizeof + +From: Mingming Cao + +[ Upstream commit 01b8114b432d7baaa5e51ab229c12c4f36b8e2c6 ] + +The previous hardcoded definitions of NUM_RX_STATS and +NUM_TX_STATS were not updated when new fields were added +to the ibmvnic_{rx,tx}_queue_stats structures. Specifically, +commit 2ee73c54a615 ("ibmvnic: Add stat for tx direct vs tx +batched") added a fourth TX stat, but NUM_TX_STATS remained 3, +leading to a mismatch. + +This patch replaces the static defines with dynamic sizeof-based +calculations to ensure the stat arrays are correctly sized. +This fixes incorrect indexing and prevents incomplete stat +reporting in tools like ethtool. + +Fixes: 2ee73c54a615 ("ibmvnic: Add stat for tx direct vs tx batched") +Signed-off-by: Mingming Cao +Reviewed-by: Dave Marquardt +Reviewed-by: Haren Myneni +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20250709153332.73892-1-mmc@linux.ibm.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/ibm/ibmvnic.h | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h +index b3fc18db4f4c3..dfbda2e5ec88a 100644 +--- a/drivers/net/ethernet/ibm/ibmvnic.h ++++ b/drivers/net/ethernet/ibm/ibmvnic.h +@@ -211,7 +211,6 @@ struct ibmvnic_statistics { + u8 reserved[72]; + } __packed __aligned(8); + +-#define NUM_TX_STATS 3 + struct ibmvnic_tx_queue_stats { + u64 batched_packets; + u64 direct_packets; +@@ -219,13 +218,18 @@ struct ibmvnic_tx_queue_stats { + u64 dropped_packets; + }; + +-#define NUM_RX_STATS 3 ++#define NUM_TX_STATS \ ++ (sizeof(struct ibmvnic_tx_queue_stats) / sizeof(u64)) ++ + struct ibmvnic_rx_queue_stats { + u64 packets; + u64 bytes; + u64 interrupts; + }; + ++#define NUM_RX_STATS \ ++ (sizeof(struct ibmvnic_rx_queue_stats) / sizeof(u64)) ++ + struct ibmvnic_acl_buffer { + __be32 len; + __be32 version; +-- +2.39.5 + diff --git a/queue-6.6/io_uring-make-fallocate-be-hashed-work.patch b/queue-6.6/io_uring-make-fallocate-be-hashed-work.patch new file mode 100644 index 0000000000..d4238496cf --- /dev/null +++ b/queue-6.6/io_uring-make-fallocate-be-hashed-work.patch @@ -0,0 +1,36 @@ +From a446f0e838dbab64abc11ab6938eca5ead43a515 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 23 Jun 2025 19:02:18 +0800 +Subject: io_uring: make fallocate be hashed work + +From: Fengnan Chang + +[ Upstream commit 88a80066af1617fab444776135d840467414beb6 ] + +Like ftruncate and write, fallocate operations on the same file cannot +be executed in parallel, so it is better to make fallocate be hashed +work. + +Signed-off-by: Fengnan Chang +Link: https://lore.kernel.org/r/20250623110218.61490-1-changfengnan@bytedance.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + io_uring/opdef.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/io_uring/opdef.c b/io_uring/opdef.c +index 3b9c6489b8b6d..2d0a7db940fdb 100644 +--- a/io_uring/opdef.c ++++ b/io_uring/opdef.c +@@ -202,6 +202,7 @@ const struct io_issue_def io_issue_defs[] = { + }, + [IORING_OP_FALLOCATE] = { + .needs_file = 1, ++ .hash_reg_file = 1, + .prep = io_fallocate_prep, + .issue = io_fallocate, + }, +-- +2.39.5 + diff --git a/queue-6.6/md-raid1-fix-stack-memory-use-after-return-in-raid1_.patch b/queue-6.6/md-raid1-fix-stack-memory-use-after-return-in-raid1_.patch new file mode 100644 index 0000000000..c5a1b7ba07 --- /dev/null +++ b/queue-6.6/md-raid1-fix-stack-memory-use-after-return-in-raid1_.patch @@ -0,0 +1,80 @@ +From 397bfad914f3e59bfad86624c1e5f04097069b9a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 12 Jun 2025 19:28:40 +0800 +Subject: md/raid1: Fix stack memory use after return in raid1_reshape + +From: Wang Jinchao + +[ Upstream commit d67ed2ccd2d1dcfda9292c0ea8697a9d0f2f0d98 ] + +In the raid1_reshape function, newpool is +allocated on the stack and assigned to conf->r1bio_pool. +This results in conf->r1bio_pool.wait.head pointing +to a stack address. +Accessing this address later can lead to a kernel panic. + +Example access path: + +raid1_reshape() +{ + // newpool is on the stack + mempool_t newpool, oldpool; + // initialize newpool.wait.head to stack address + mempool_init(&newpool, ...); + conf->r1bio_pool = newpool; +} + +raid1_read_request() or raid1_write_request() +{ + alloc_r1bio() + { + mempool_alloc() + { + // if pool->alloc fails + remove_element() + { + --pool->curr_nr; + } + } + } +} + +mempool_free() +{ + if (pool->curr_nr < pool->min_nr) { + // pool->wait.head is a stack address + // wake_up() will try to access this invalid address + // which leads to a kernel panic + return; + wake_up(&pool->wait); + } +} + +Fix: +reinit conf->r1bio_pool.wait after assigning newpool. + +Fixes: afeee514ce7f ("md: convert to bioset_init()/mempool_init()") +Signed-off-by: Wang Jinchao +Reviewed-by: Yu Kuai +Link: https://lore.kernel.org/linux-raid/20250612112901.3023950-1-wangjinchao600@gmail.com +Signed-off-by: Yu Kuai +Signed-off-by: Sasha Levin +--- + drivers/md/raid1.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c +index 8b25287c89ed6..4c1f86ca55208 100644 +--- a/drivers/md/raid1.c ++++ b/drivers/md/raid1.c +@@ -3297,6 +3297,7 @@ static int raid1_reshape(struct mddev *mddev) + /* ok, everything is stopped */ + oldpool = conf->r1bio_pool; + conf->r1bio_pool = newpool; ++ init_waitqueue_head(&conf->r1bio_pool.wait); + + for (d = d2 = 0; d < conf->raid_disks; d++) { + struct md_rdev *rdev = conf->mirrors[d].rdev; +-- +2.39.5 + diff --git a/queue-6.6/nbd-fix-uaf-in-nbd_genl_connect-error-path.patch b/queue-6.6/nbd-fix-uaf-in-nbd_genl_connect-error-path.patch new file mode 100644 index 0000000000..f3acc9ee06 --- /dev/null +++ b/queue-6.6/nbd-fix-uaf-in-nbd_genl_connect-error-path.patch @@ -0,0 +1,86 @@ +From 0e044fd9316105418d786b187c56759e93d80ae1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 12 Jun 2025 21:24:05 +0800 +Subject: nbd: fix uaf in nbd_genl_connect() error path + +From: Zheng Qixing + +[ Upstream commit aa9552438ebf015fc5f9f890dbfe39f0c53cf37e ] + +There is a use-after-free issue in nbd: + +block nbd6: Receive control failed (result -104) +block nbd6: shutting down sockets +================================================================== +BUG: KASAN: slab-use-after-free in recv_work+0x694/0xa80 drivers/block/nbd.c:1022 +Write of size 4 at addr ffff8880295de478 by task kworker/u33:0/67 + +CPU: 2 UID: 0 PID: 67 Comm: kworker/u33:0 Not tainted 6.15.0-rc5-syzkaller-00123-g2c89c1b655c0 #0 PREEMPT(full) +Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2~bpo12+1 04/01/2014 +Workqueue: nbd6-recv recv_work +Call Trace: + + __dump_stack lib/dump_stack.c:94 [inline] + dump_stack_lvl+0x116/0x1f0 lib/dump_stack.c:120 + print_address_description mm/kasan/report.c:408 [inline] + print_report+0xc3/0x670 mm/kasan/report.c:521 + kasan_report+0xe0/0x110 mm/kasan/report.c:634 + check_region_inline mm/kasan/generic.c:183 [inline] + kasan_check_range+0xef/0x1a0 mm/kasan/generic.c:189 + instrument_atomic_read_write include/linux/instrumented.h:96 [inline] + atomic_dec include/linux/atomic/atomic-instrumented.h:592 [inline] + recv_work+0x694/0xa80 drivers/block/nbd.c:1022 + process_one_work+0x9cc/0x1b70 kernel/workqueue.c:3238 + process_scheduled_works kernel/workqueue.c:3319 [inline] + worker_thread+0x6c8/0xf10 kernel/workqueue.c:3400 + kthread+0x3c2/0x780 kernel/kthread.c:464 + ret_from_fork+0x45/0x80 arch/x86/kernel/process.c:153 + ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 + + +nbd_genl_connect() does not properly stop the device on certain +error paths after nbd_start_device() has been called. This causes +the error path to put nbd->config while recv_work continue to use +the config after putting it, leading to use-after-free in recv_work. + +This patch moves nbd_start_device() after the backend file creation. + +Reported-by: syzbot+48240bab47e705c53126@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/all/68227a04.050a0220.f2294.00b5.GAE@google.com/T/ +Fixes: 6497ef8df568 ("nbd: provide a way for userspace processes to identify device backends") +Signed-off-by: Zheng Qixing +Reviewed-by: Yu Kuai +Link: https://lore.kernel.org/r/20250612132405.364904-1-zhengqixing@huaweicloud.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + drivers/block/nbd.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c +index 2203686156bfe..3742ddf46c55a 100644 +--- a/drivers/block/nbd.c ++++ b/drivers/block/nbd.c +@@ -2120,9 +2120,7 @@ static int nbd_genl_connect(struct sk_buff *skb, struct genl_info *info) + goto out; + } + } +- ret = nbd_start_device(nbd); +- if (ret) +- goto out; ++ + if (info->attrs[NBD_ATTR_BACKEND_IDENTIFIER]) { + nbd->backend = nla_strdup(info->attrs[NBD_ATTR_BACKEND_IDENTIFIER], + GFP_KERNEL); +@@ -2138,6 +2136,8 @@ static int nbd_genl_connect(struct sk_buff *skb, struct genl_info *info) + goto out; + } + set_bit(NBD_RT_HAS_BACKEND_FILE, &config->runtime_flags); ++ ++ ret = nbd_start_device(nbd); + out: + mutex_unlock(&nbd->config_lock); + if (!ret) { +-- +2.39.5 + diff --git a/queue-6.6/net-appletalk-fix-device-refcount-leak-in-atrtr_crea.patch b/queue-6.6/net-appletalk-fix-device-refcount-leak-in-atrtr_crea.patch new file mode 100644 index 0000000000..ebff33483e --- /dev/null +++ b/queue-6.6/net-appletalk-fix-device-refcount-leak-in-atrtr_crea.patch @@ -0,0 +1,38 @@ +From 427b3862bc9085cc07b864a1c96d678fb3d02a1f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 9 Jul 2025 03:52:51 +0000 +Subject: net: appletalk: Fix device refcount leak in atrtr_create() + +From: Kito Xu + +[ Upstream commit 711c80f7d8b163d3ecd463cd96f07230f488e750 ] + +When updating an existing route entry in atrtr_create(), the old device +reference was not being released before assigning the new device, +leading to a device refcount leak. Fix this by calling dev_put() to +release the old device reference before holding the new one. + +Fixes: c7f905f0f6d4 ("[ATALK]: Add missing dev_hold() to atrtr_create().") +Signed-off-by: Kito Xu +Link: https://patch.msgid.link/tencent_E1A26771CDAB389A0396D1681A90A49E5D09@qq.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/appletalk/ddp.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c +index b070a89912000..febb1617e1a6a 100644 +--- a/net/appletalk/ddp.c ++++ b/net/appletalk/ddp.c +@@ -563,6 +563,7 @@ static int atrtr_create(struct rtentry *r, struct net_device *devhint) + + /* Fill in the routing entry */ + rt->target = ta->sat_addr; ++ dev_put(rt->dev); /* Release old device */ + dev_hold(devhint); + rt->dev = devhint; + rt->flags = r->rt_flags; +-- +2.39.5 + diff --git a/queue-6.6/net-ll_temac-fix-missing-tx_pending-check-in-ethtool.patch b/queue-6.6/net-ll_temac-fix-missing-tx_pending-check-in-ethtool.patch new file mode 100644 index 0000000000..cae0d2484a --- /dev/null +++ b/queue-6.6/net-ll_temac-fix-missing-tx_pending-check-in-ethtool.patch @@ -0,0 +1,45 @@ +From bcbb2b8066793169d86dcfc3e370dd80e8b81679 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 10 Jul 2025 11:06:17 -0700 +Subject: net: ll_temac: Fix missing tx_pending check in + ethtools_set_ringparam() + +From: Alok Tiwari + +[ Upstream commit e81750b4e3826fedce7362dad839cb40384d60ae ] + +The function ll_temac_ethtools_set_ringparam() incorrectly checked +rx_pending twice, once correctly for RX and once mistakenly in place +of tx_pending. This caused tx_pending to be left unchecked against +TX_BD_NUM_MAX. +As a result, invalid TX ring sizes may have been accepted or valid +ones wrongly rejected based on the RX limit, leading to potential +misconfiguration or unexpected results. + +This patch corrects the condition to properly validate tx_pending. + +Fixes: f7b261bfc35e ("net: ll_temac: Make RX/TX ring sizes configurable") +Signed-off-by: Alok Tiwari +Link: https://patch.msgid.link/20250710180621.2383000-1-alok.a.tiwari@oracle.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/xilinx/ll_temac_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c +index c10f94d69dad3..3d622634e82aa 100644 +--- a/drivers/net/ethernet/xilinx/ll_temac_main.c ++++ b/drivers/net/ethernet/xilinx/ll_temac_main.c +@@ -1309,7 +1309,7 @@ ll_temac_ethtools_set_ringparam(struct net_device *ndev, + if (ering->rx_pending > RX_BD_NUM_MAX || + ering->rx_mini_pending || + ering->rx_jumbo_pending || +- ering->rx_pending > TX_BD_NUM_MAX) ++ ering->tx_pending > TX_BD_NUM_MAX) + return -EINVAL; + + if (netif_running(ndev)) +-- +2.39.5 + diff --git a/queue-6.6/net-mana-record-doorbell-physical-address-in-pf-mode.patch b/queue-6.6/net-mana-record-doorbell-physical-address-in-pf-mode.patch new file mode 100644 index 0000000000..6a8eacadfd --- /dev/null +++ b/queue-6.6/net-mana-record-doorbell-physical-address-in-pf-mode.patch @@ -0,0 +1,47 @@ +From cf6f9a4eac01bd80061b2ceda6e8dd7114ecc2da Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 17 Jun 2025 18:36:46 -0700 +Subject: net: mana: Record doorbell physical address in PF mode + +From: Long Li + +[ Upstream commit e0fca6f2cebff539e9317a15a37dcf432e3b851a ] + +MANA supports RDMA in PF mode. The driver should record the doorbell +physical address when in PF mode. + +The doorbell physical address is used by the RDMA driver to map +doorbell pages of the device to user-mode applications through RDMA +verbs interface. In the past, they have been mapped to user-mode while +the device is in VF mode. With the support for PF mode implemented, +also expose those pages in PF mode. + +Support for PF mode is implemented in +290e5d3c49f6 ("net: mana: Add support for Multi Vports on Bare metal") + +Signed-off-by: Long Li +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/1750210606-12167-1-git-send-email-longli@linuxonhyperv.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/ethernet/microsoft/mana/gdma_main.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c +index 9ed965d61e355..d3c9a3020fbf6 100644 +--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c ++++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c +@@ -28,6 +28,9 @@ static void mana_gd_init_pf_regs(struct pci_dev *pdev) + gc->db_page_base = gc->bar0_va + + mana_gd_r64(gc, GDMA_PF_REG_DB_PAGE_OFF); + ++ gc->phys_db_page_base = gc->bar0_pa + ++ mana_gd_r64(gc, GDMA_PF_REG_DB_PAGE_OFF); ++ + sriov_base_off = mana_gd_r64(gc, GDMA_SRIOV_REG_CFG_BASE_OFF); + + sriov_base_va = gc->bar0_va + sriov_base_off; +-- +2.39.5 + diff --git a/queue-6.6/net-phy-microchip-limit-100m-workaround-to-link-down.patch b/queue-6.6/net-phy-microchip-limit-100m-workaround-to-link-down.patch new file mode 100644 index 0000000000..7a4823802b --- /dev/null +++ b/queue-6.6/net-phy-microchip-limit-100m-workaround-to-link-down.patch @@ -0,0 +1,60 @@ +From a7f24a59408f4697f3bce9faabd5c226da4fca84 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 9 Jul 2025 15:07:53 +0200 +Subject: net: phy: microchip: limit 100M workaround to link-down events on + LAN88xx + +From: Oleksij Rempel + +[ Upstream commit dd4360c0e8504f2f7639c7f5d07c93cfd6a98333 ] + +Restrict the 100Mbit forced-mode workaround to link-down transitions +only, to prevent repeated link reset cycles in certain configurations. + +The workaround was originally introduced to improve signal reliability +when switching cables between long and short distances. It temporarily +forces the PHY into 10 Mbps before returning to 100 Mbps. + +However, when used with autonegotiating link partners (e.g., Intel i350), +executing this workaround on every link change can confuse the partner +and cause constant renegotiation loops. This results in repeated link +down/up transitions and the PHY never reaching a stable state. + +Limit the workaround to only run during the PHY_NOLINK state. This ensures +it is triggered only once per link drop, avoiding disruptive toggling +while still preserving its intended effect. + +Note: I am not able to reproduce the original issue that this workaround +addresses. I can only confirm that 100 Mbit mode works correctly in my +test setup. Based on code inspection, I assume the workaround aims to +reset some internal state machine or signal block by toggling speeds. +However, a PHY reset is already performed earlier in the function via +phy_init_hw(), which may achieve a similar effect. Without a reproducer, +I conservatively keep the workaround but restrict its conditions. + +Fixes: e57cf3639c32 ("net: lan78xx: fix accessing the LAN7800's internal phy specific registers from the MAC driver") +Signed-off-by: Oleksij Rempel +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250709130753.3994461-3-o.rempel@pengutronix.de +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/phy/microchip.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c +index 623607fd2cefd..cb8306cd98260 100644 +--- a/drivers/net/phy/microchip.c ++++ b/drivers/net/phy/microchip.c +@@ -310,7 +310,7 @@ static void lan88xx_link_change_notify(struct phy_device *phydev) + * As workaround, set to 10 before setting to 100 + * at forced 100 F/H mode. + */ +- if (!phydev->autoneg && phydev->speed == 100) { ++ if (phydev->state == PHY_NOLINK && !phydev->autoneg && phydev->speed == 100) { + /* disable phy interrupt */ + temp = phy_read(phydev, LAN88XX_INT_MASK); + temp &= ~LAN88XX_INT_MASK_MDINTPIN_EN_; +-- +2.39.5 + diff --git a/queue-6.6/net-usb-qmi_wwan-add-simcom-8230c-composition.patch b/queue-6.6/net-usb-qmi_wwan-add-simcom-8230c-composition.patch new file mode 100644 index 0000000000..7f76907128 --- /dev/null +++ b/queue-6.6/net-usb-qmi_wwan-add-simcom-8230c-composition.patch @@ -0,0 +1,63 @@ +From cf02ac3624ff309d2031d99ff47ed4ba4af18042 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 20 Jun 2025 10:27:02 +0800 +Subject: net: usb: qmi_wwan: add SIMCom 8230C composition +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Xiaowei Li + +[ Upstream commit 0b39b055b5b48cbbdf5746a1ca6e3f6b0221e537 ] + +Add support for SIMCom 8230C which is based on Qualcomm SDX35 chip. +0x9071: tty (DM) + tty (NMEA) + tty (AT) + rmnet +T: Bus=01 Lev=01 Prnt=01 Port=05 Cnt=02 Dev#= 8 Spd=480 MxCh= 0 +D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 +P: Vendor=1e0e ProdID=9071 Rev= 5.15 +S: Manufacturer=SIMCOM +S: Product=SDXBAAGHA-IDP _SN:D744C4C5 +S: SerialNumber=0123456789ABCDEF +C:* #Ifs= 5 Cfg#= 1 Atr=a0 MxPwr=500mA +I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option +E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms +E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms +I:* If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option +E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms +E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms +I:* If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option +E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms +E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms +E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms +I:* If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=50 Driver=qmi_wwan +E: Ad=86(I) Atr=03(Int.) MxPS= 8 Ivl=32ms +E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms +E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms +I:* If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=none +E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms +E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms + +Signed-off-by: Xiaowei Li +Acked-by: Bjørn Mork +Link: https://patch.msgid.link/tencent_21D781FAA4969FEACA6ABB460362B52C9409@qq.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + drivers/net/usb/qmi_wwan.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c +index dc84d9029c2c7..3976bc4295dd1 100644 +--- a/drivers/net/usb/qmi_wwan.c ++++ b/drivers/net/usb/qmi_wwan.c +@@ -1432,6 +1432,7 @@ static const struct usb_device_id products[] = { + {QMI_QUIRK_SET_DTR(0x22de, 0x9051, 2)}, /* Hucom Wireless HM-211S/K */ + {QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */ + {QMI_QUIRK_SET_DTR(0x1e0e, 0x9001, 5)}, /* SIMCom 7100E, 7230E, 7600E ++ */ ++ {QMI_QUIRK_SET_DTR(0x1e0e, 0x9071, 3)}, /* SIMCom 8230C ++ */ + {QMI_QUIRK_SET_DTR(0x2c7c, 0x0121, 4)}, /* Quectel EC21 Mini PCIe */ + {QMI_QUIRK_SET_DTR(0x2c7c, 0x0191, 4)}, /* Quectel EG91 */ + {QMI_QUIRK_SET_DTR(0x2c7c, 0x0195, 4)}, /* Quectel EG95 */ +-- +2.39.5 + diff --git a/queue-6.6/netfilter-flowtable-account-for-ethernet-header-in-n.patch b/queue-6.6/netfilter-flowtable-account-for-ethernet-header-in-n.patch new file mode 100644 index 0000000000..b319a53de1 --- /dev/null +++ b/queue-6.6/netfilter-flowtable-account-for-ethernet-header-in-n.patch @@ -0,0 +1,61 @@ +From 50ab9bf6fbcabc7f8a54f49f9b9feca4a51f4097 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 7 Jul 2025 12:45:17 +0000 +Subject: netfilter: flowtable: account for Ethernet header in + nf_flow_pppoe_proto() + +From: Eric Dumazet + +[ Upstream commit 18cdb3d982da8976b28d57691eb256ec5688fad2 ] + +syzbot found a potential access to uninit-value in nf_flow_pppoe_proto() + +Blamed commit forgot the Ethernet header. + +BUG: KMSAN: uninit-value in nf_flow_offload_inet_hook+0x7e4/0x940 net/netfilter/nf_flow_table_inet.c:27 + nf_flow_offload_inet_hook+0x7e4/0x940 net/netfilter/nf_flow_table_inet.c:27 + nf_hook_entry_hookfn include/linux/netfilter.h:157 [inline] + nf_hook_slow+0xe1/0x3d0 net/netfilter/core.c:623 + nf_hook_ingress include/linux/netfilter_netdev.h:34 [inline] + nf_ingress net/core/dev.c:5742 [inline] + __netif_receive_skb_core+0x4aff/0x70c0 net/core/dev.c:5837 + __netif_receive_skb_one_core net/core/dev.c:5975 [inline] + __netif_receive_skb+0xcc/0xac0 net/core/dev.c:6090 + netif_receive_skb_internal net/core/dev.c:6176 [inline] + netif_receive_skb+0x57/0x630 net/core/dev.c:6235 + tun_rx_batched+0x1df/0x980 drivers/net/tun.c:1485 + tun_get_user+0x4ee0/0x6b40 drivers/net/tun.c:1938 + tun_chr_write_iter+0x3e9/0x5c0 drivers/net/tun.c:1984 + new_sync_write fs/read_write.c:593 [inline] + vfs_write+0xb4b/0x1580 fs/read_write.c:686 + ksys_write fs/read_write.c:738 [inline] + __do_sys_write fs/read_write.c:749 [inline] + +Reported-by: syzbot+bf6ed459397e307c3ad2@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/netdev/686bc073.a00a0220.c7b3.0086.GAE@google.com/T/#u +Fixes: 87b3593bed18 ("netfilter: flowtable: validate pppoe header") +Signed-off-by: Eric Dumazet +Reviewed-by: Pablo Neira Ayuso +Link: https://patch.msgid.link/20250707124517.614489-1-edumazet@google.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + include/net/netfilter/nf_flow_table.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h +index df7775afb92b9..0097791e1eede 100644 +--- a/include/net/netfilter/nf_flow_table.h ++++ b/include/net/netfilter/nf_flow_table.h +@@ -353,7 +353,7 @@ static inline __be16 __nf_flow_pppoe_proto(const struct sk_buff *skb) + + static inline bool nf_flow_pppoe_proto(struct sk_buff *skb, __be16 *inner_proto) + { +- if (!pskb_may_pull(skb, PPPOE_SES_HLEN)) ++ if (!pskb_may_pull(skb, ETH_HLEN + PPPOE_SES_HLEN)) + return false; + + *inner_proto = __nf_flow_pppoe_proto(skb); +-- +2.39.5 + diff --git a/queue-6.6/raid10-cleanup-memleak-at-raid10_make_request.patch b/queue-6.6/raid10-cleanup-memleak-at-raid10_make_request.patch new file mode 100644 index 0000000000..0ce5a9dccc --- /dev/null +++ b/queue-6.6/raid10-cleanup-memleak-at-raid10_make_request.patch @@ -0,0 +1,81 @@ +From 97200163aec7391b498d56bb00854d60fbd1cfed Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 3 Jul 2025 11:23:04 -0400 +Subject: raid10: cleanup memleak at raid10_make_request + +From: Nigel Croxon + +[ Upstream commit 43806c3d5b9bb7d74ba4e33a6a8a41ac988bde24 ] + +If raid10_read_request or raid10_write_request registers a new +request and the REQ_NOWAIT flag is set, the code does not +free the malloc from the mempool. + +unreferenced object 0xffff8884802c3200 (size 192): + comm "fio", pid 9197, jiffies 4298078271 + hex dump (first 32 bytes): + 00 00 00 00 00 00 00 00 88 41 02 00 00 00 00 00 .........A...... + 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + backtrace (crc c1a049a2): + __kmalloc+0x2bb/0x450 + mempool_alloc+0x11b/0x320 + raid10_make_request+0x19e/0x650 [raid10] + md_handle_request+0x3b3/0x9e0 + __submit_bio+0x394/0x560 + __submit_bio_noacct+0x145/0x530 + submit_bio_noacct_nocheck+0x682/0x830 + __blkdev_direct_IO_async+0x4dc/0x6b0 + blkdev_read_iter+0x1e5/0x3b0 + __io_read+0x230/0x1110 + io_read+0x13/0x30 + io_issue_sqe+0x134/0x1180 + io_submit_sqes+0x48c/0xe90 + __do_sys_io_uring_enter+0x574/0x8b0 + do_syscall_64+0x5c/0xe0 + entry_SYSCALL_64_after_hwframe+0x76/0x7e + +V4: changing backing tree to see if CKI tests will pass. +The patch code has not changed between any versions. + +Fixes: c9aa889b035f ("md: raid10 add nowait support") +Signed-off-by: Nigel Croxon +Link: https://lore.kernel.org/linux-raid/c0787379-9caa-42f3-b5fc-369aed784400@redhat.com +Signed-off-by: Yu Kuai +Signed-off-by: Sasha Levin +--- + drivers/md/raid10.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c +index 36b6bf3f8b29f..a75d090a7fa15 100644 +--- a/drivers/md/raid10.c ++++ b/drivers/md/raid10.c +@@ -1205,8 +1205,11 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio, + rcu_read_unlock(); + } + +- if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) ++ if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) { ++ raid_end_bio_io(r10_bio); + return; ++ } ++ + rdev = read_balance(conf, r10_bio, &max_sectors); + if (!rdev) { + if (err_rdev) { +@@ -1428,8 +1431,11 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio, + } + + sectors = r10_bio->sectors; +- if (!regular_request_wait(mddev, conf, bio, sectors)) ++ if (!regular_request_wait(mddev, conf, bio, sectors)) { ++ raid_end_bio_io(r10_bio); + return; ++ } ++ + if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) && + (mddev->reshape_backwards + ? (bio->bi_iter.bi_sector < conf->reshape_safe && +-- +2.39.5 + diff --git a/queue-6.6/series b/queue-6.6/series index 056f0d6284..66c011f8a8 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -63,3 +63,42 @@ ksmbd-fix-a-mount-write-count-leak-in-ksmbd_vfs_kern_path_locked.patch erofs-fix-to-add-missing-tracepoint-in-erofs_read_folio.patch netlink-fix-rmem-check-in-netlink_broadcast_deliver.patch netlink-make-sure-we-allow-at-least-one-dump-skb.patch +btrfs-remove-noinline-from-btrfs_update_inode.patch +btrfs-remove-redundant-root-argument-from-btrfs_upda.patch +btrfs-remove-redundant-root-argument-from-fixup_inod.patch +btrfs-return-a-btrfs_inode-from-btrfs_iget_logging.patch +btrfs-fix-inode-lookup-error-handling-during-log-rep.patch +usb-cdnsp-remove-trb_flush_endpoint-command.patch +usb-cdnsp-replace-snprintf-with-the-safer-scnprintf-.patch +usb-cdnsp-fix-issue-with-cv-bad-descriptor-test.patch +usb-dwc3-abort-suspend-on-soft-disconnect-failure.patch +smb-client-avoid-unnecessary-reconnects-when-refresh.patch +smb-client-fix-dfs-interlink-failover.patch +cifs-all-initializations-for-tcon-should-happen-in-t.patch +wifi-zd1211rw-fix-potential-null-pointer-dereference.patch +drm-tegra-nvdec-fix-dma_alloc_coherent-error-check.patch +md-raid1-fix-stack-memory-use-after-return-in-raid1_.patch +raid10-cleanup-memleak-at-raid10_make_request.patch +nbd-fix-uaf-in-nbd_genl_connect-error-path.patch +netfilter-flowtable-account-for-ethernet-header-in-n.patch +net-appletalk-fix-device-refcount-leak-in-atrtr_crea.patch +ibmvnic-fix-hardcoded-num_rx_stats-num_tx_stats-with.patch +net-phy-microchip-limit-100m-workaround-to-link-down.patch +can-m_can-m_can_handle_lost_msg-downgrade-msg-lost-i.patch +net-ll_temac-fix-missing-tx_pending-check-in-ethtool.patch +bnxt_en-fix-dcb-ets-validation.patch +bnxt_en-set-dma-unmap-len-correctly-for-xdp_redirect.patch +ublk-sanity-check-add_dev-input-for-underflow.patch +atm-idt77252-add-missing-dma_map_error.patch +um-vector-reduce-stack-usage-in-vector_eth_configure.patch +io_uring-make-fallocate-be-hashed-work.patch +asoc-amd-yc-add-quirk-for-acer-nitro-anv15-41-intern.patch +alsa-hda-realtek-enable-mute-led-on-hp-pavilion-lapt.patch +net-usb-qmi_wwan-add-simcom-8230c-composition.patch +hid-lenovo-add-support-for-thinkpad-x1-tablet-thin-k.patch +net-mana-record-doorbell-physical-address-in-pf-mode.patch +btrfs-fix-assertion-when-building-free-space-tree.patch +vt-add-missing-notification-when-switching-back-to-t.patch +bpf-adjust-free-target-to-avoid-global-starvation-of.patch +hid-add-ignore-quirk-for-smartlinktechnology.patch +hid-quirks-add-quirk-for-2-chicony-electronics-hp-5m.patch diff --git a/queue-6.6/smb-client-avoid-unnecessary-reconnects-when-refresh.patch b/queue-6.6/smb-client-avoid-unnecessary-reconnects-when-refresh.patch new file mode 100644 index 0000000000..753f4c2845 --- /dev/null +++ b/queue-6.6/smb-client-avoid-unnecessary-reconnects-when-refresh.patch @@ -0,0 +1,287 @@ +From 27c3598b4b74b262dd04bfd7b935c3683baa8cc7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 18 Sep 2024 02:03:35 -0300 +Subject: smb: client: avoid unnecessary reconnects when refreshing referrals + +From: Paulo Alcantara + +[ Upstream commit 242d23efc987151ecd34bc0cae4c0b737494fc40 ] + +Do not mark tcons for reconnect when current connection matches any of +the targets returned by new referral even when there is no cached +entry. + +Signed-off-by: Paulo Alcantara (Red Hat) +Signed-off-by: Steve French +Stable-dep-of: 74ebd02163fd ("cifs: all initializations for tcon should happen in tcon_info_alloc") +Signed-off-by: Sasha Levin +--- + fs/smb/client/dfs_cache.c | 187 ++++++++++++++++++++++++-------------- + 1 file changed, 117 insertions(+), 70 deletions(-) + +diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c +index 11c8efecf7aa1..3cf7c88489be4 100644 +--- a/fs/smb/client/dfs_cache.c ++++ b/fs/smb/client/dfs_cache.c +@@ -1095,16 +1095,18 @@ int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, + return 0; + } + +-static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, const char *s2) ++static bool target_share_equal(struct cifs_tcon *tcon, const char *s1) + { +- char unc[sizeof("\\\\") + SERVER_NAME_LENGTH] = {0}; ++ struct TCP_Server_Info *server = tcon->ses->server; ++ struct sockaddr_storage ss; + const char *host; ++ const char *s2 = &tcon->tree_name[1]; + size_t hostlen; +- struct sockaddr_storage ss; ++ char unc[sizeof("\\\\") + SERVER_NAME_LENGTH] = {0}; + bool match; + int rc; + +- if (strcasecmp(s1, s2)) ++ if (strcasecmp(s2, s1)) + return false; + + /* +@@ -1128,34 +1130,6 @@ static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, c + return match; + } + +-/* +- * Mark dfs tcon for reconnecting when the currently connected tcon does not match any of the new +- * target shares in @refs. +- */ +-static void mark_for_reconnect_if_needed(struct TCP_Server_Info *server, +- const char *path, +- struct dfs_cache_tgt_list *old_tl, +- struct dfs_cache_tgt_list *new_tl) +-{ +- struct dfs_cache_tgt_iterator *oit, *nit; +- +- for (oit = dfs_cache_get_tgt_iterator(old_tl); oit; +- oit = dfs_cache_get_next_tgt(old_tl, oit)) { +- for (nit = dfs_cache_get_tgt_iterator(new_tl); nit; +- nit = dfs_cache_get_next_tgt(new_tl, nit)) { +- if (target_share_equal(server, +- dfs_cache_get_tgt_name(oit), +- dfs_cache_get_tgt_name(nit))) { +- dfs_cache_noreq_update_tgthint(path, nit); +- return; +- } +- } +- } +- +- cifs_dbg(FYI, "%s: no cached or matched targets. mark dfs share for reconnect.\n", __func__); +- cifs_signal_cifsd_for_reconnect(server, true); +-} +- + static bool is_ses_good(struct cifs_ses *ses) + { + struct TCP_Server_Info *server = ses->server; +@@ -1172,41 +1146,35 @@ static bool is_ses_good(struct cifs_ses *ses) + return ret; + } + +-/* Refresh dfs referral of @ses and mark it for reconnect if needed */ +-static void __refresh_ses_referral(struct cifs_ses *ses, bool force_refresh) ++static char *get_ses_refpath(struct cifs_ses *ses) + { + struct TCP_Server_Info *server = ses->server; +- DFS_CACHE_TGT_LIST(old_tl); +- DFS_CACHE_TGT_LIST(new_tl); +- bool needs_refresh = false; +- struct cache_entry *ce; +- unsigned int xid; +- char *path = NULL; +- int rc = 0; +- +- xid = get_xid(); ++ char *path = ERR_PTR(-ENOENT); + + mutex_lock(&server->refpath_lock); + if (server->leaf_fullpath) { + path = kstrdup(server->leaf_fullpath + 1, GFP_ATOMIC); + if (!path) +- rc = -ENOMEM; ++ path = ERR_PTR(-ENOMEM); + } + mutex_unlock(&server->refpath_lock); +- if (!path) +- goto out; ++ return path; ++} + +- down_read(&htable_rw_lock); +- ce = lookup_cache_entry(path); +- needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce); +- if (!IS_ERR(ce)) { +- rc = get_targets(ce, &old_tl); +- cifs_dbg(FYI, "%s: get_targets: %d\n", __func__, rc); +- } +- up_read(&htable_rw_lock); ++/* Refresh dfs referral of @ses */ ++static void refresh_ses_referral(struct cifs_ses *ses) ++{ ++ struct cache_entry *ce; ++ unsigned int xid; ++ char *path; ++ int rc = 0; + +- if (!needs_refresh) { +- rc = 0; ++ xid = get_xid(); ++ ++ path = get_ses_refpath(ses); ++ if (IS_ERR(path)) { ++ rc = PTR_ERR(path); ++ path = NULL; + goto out; + } + +@@ -1217,29 +1185,106 @@ static void __refresh_ses_referral(struct cifs_ses *ses, bool force_refresh) + goto out; + } + +- ce = cache_refresh_path(xid, ses, path, true); +- if (!IS_ERR(ce)) { +- rc = get_targets(ce, &new_tl); ++ ce = cache_refresh_path(xid, ses, path, false); ++ if (!IS_ERR(ce)) + up_read(&htable_rw_lock); +- cifs_dbg(FYI, "%s: get_targets: %d\n", __func__, rc); +- mark_for_reconnect_if_needed(server, path, &old_tl, &new_tl); +- } ++ else ++ rc = PTR_ERR(ce); + + out: + free_xid(xid); +- dfs_cache_free_tgts(&old_tl); +- dfs_cache_free_tgts(&new_tl); + kfree(path); + } + +-static inline void refresh_ses_referral(struct cifs_ses *ses) ++static int __refresh_tcon_referral(struct cifs_tcon *tcon, ++ const char *path, ++ struct dfs_info3_param *refs, ++ int numrefs, bool force_refresh) + { +- __refresh_ses_referral(ses, false); ++ struct cache_entry *ce; ++ bool reconnect = force_refresh; ++ int rc = 0; ++ int i; ++ ++ if (unlikely(!numrefs)) ++ return 0; ++ ++ if (force_refresh) { ++ for (i = 0; i < numrefs; i++) { ++ /* TODO: include prefix paths in the matching */ ++ if (target_share_equal(tcon, refs[i].node_name)) { ++ reconnect = false; ++ break; ++ } ++ } ++ } ++ ++ down_write(&htable_rw_lock); ++ ce = lookup_cache_entry(path); ++ if (!IS_ERR(ce)) { ++ if (force_refresh || cache_entry_expired(ce)) ++ rc = update_cache_entry_locked(ce, refs, numrefs); ++ } else if (PTR_ERR(ce) == -ENOENT) { ++ ce = add_cache_entry_locked(refs, numrefs); ++ } ++ up_write(&htable_rw_lock); ++ ++ if (IS_ERR(ce)) ++ rc = PTR_ERR(ce); ++ if (reconnect) { ++ cifs_tcon_dbg(FYI, "%s: mark for reconnect\n", __func__); ++ cifs_signal_cifsd_for_reconnect(tcon->ses->server, true); ++ } ++ return rc; + } + +-static inline void force_refresh_ses_referral(struct cifs_ses *ses) ++static void refresh_tcon_referral(struct cifs_tcon *tcon, bool force_refresh) + { +- __refresh_ses_referral(ses, true); ++ struct dfs_info3_param *refs = NULL; ++ struct cache_entry *ce; ++ struct cifs_ses *ses; ++ unsigned int xid; ++ bool needs_refresh; ++ char *path; ++ int numrefs = 0; ++ int rc = 0; ++ ++ xid = get_xid(); ++ ses = tcon->ses; ++ ++ path = get_ses_refpath(ses); ++ if (IS_ERR(path)) { ++ rc = PTR_ERR(path); ++ path = NULL; ++ goto out; ++ } ++ ++ down_read(&htable_rw_lock); ++ ce = lookup_cache_entry(path); ++ needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce); ++ if (!needs_refresh) { ++ up_read(&htable_rw_lock); ++ goto out; ++ } ++ up_read(&htable_rw_lock); ++ ++ ses = CIFS_DFS_ROOT_SES(ses); ++ if (!is_ses_good(ses)) { ++ cifs_dbg(FYI, "%s: skip cache refresh due to disconnected ipc\n", ++ __func__); ++ goto out; ++ } ++ ++ rc = get_dfs_referral(xid, ses, path, &refs, &numrefs); ++ if (!rc) { ++ rc = __refresh_tcon_referral(tcon, path, refs, ++ numrefs, force_refresh); ++ } ++ ++out: ++ free_xid(xid); ++ kfree(path); ++ free_dfs_info_array(refs, numrefs); + } + + /** +@@ -1280,7 +1325,7 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) + */ + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; + +- force_refresh_ses_referral(tcon->ses); ++ refresh_tcon_referral(tcon, true); + return 0; + } + +@@ -1291,9 +1336,11 @@ void dfs_cache_refresh(struct work_struct *work) + struct cifs_ses *ses; + + tcon = container_of(work, struct cifs_tcon, dfs_cache_work.work); ++ ses = tcon->ses->dfs_root_ses; + +- for (ses = tcon->ses; ses; ses = ses->dfs_root_ses) ++ for (; ses; ses = ses->dfs_root_ses) + refresh_ses_referral(ses); ++ refresh_tcon_referral(tcon, false); + + queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work, + atomic_read(&dfs_cache_ttl) * HZ); +-- +2.39.5 + diff --git a/queue-6.6/smb-client-fix-dfs-interlink-failover.patch b/queue-6.6/smb-client-fix-dfs-interlink-failover.patch new file mode 100644 index 0000000000..c1c9f8d74e --- /dev/null +++ b/queue-6.6/smb-client-fix-dfs-interlink-failover.patch @@ -0,0 +1,561 @@ +From 842554dd50f7533b5226dc071e35c764826cea78 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 18 Sep 2024 02:03:55 -0300 +Subject: smb: client: fix DFS interlink failover + +From: Paulo Alcantara + +[ Upstream commit 4f42a8b54b5c6e36519aef3cb1f6210e54abd451 ] + +The DFS interlinks point to different DFS namespaces so make sure to +use the correct DFS root server to chase any DFS links under it by +storing the SMB session in dfs_ref_walk structure and then using it on +every referral walk. + +Signed-off-by: Paulo Alcantara (Red Hat) +Signed-off-by: Steve French +Stable-dep-of: 74ebd02163fd ("cifs: all initializations for tcon should happen in tcon_info_alloc") +Signed-off-by: Sasha Levin +--- + fs/smb/client/cifsglob.h | 3 ++ + fs/smb/client/cifsproto.h | 12 ++----- + fs/smb/client/connect.c | 41 +++++++++++---------- + fs/smb/client/dfs.c | 73 ++++++++++++++++++-------------------- + fs/smb/client/dfs.h | 42 ++++++++++++++-------- + fs/smb/client/dfs_cache.c | 3 +- + fs/smb/client/fs_context.h | 1 + + fs/smb/client/misc.c | 3 ++ + fs/smb/client/namespace.c | 2 +- + 9 files changed, 94 insertions(+), 86 deletions(-) + +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index 5c856adf7be9e..c9b37f2ebde85 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -830,6 +830,7 @@ struct TCP_Server_Info { + * format: \\HOST\SHARE[\OPTIONAL PATH] + */ + char *leaf_fullpath; ++ bool dfs_conn:1; + }; + + static inline bool is_smb1(struct TCP_Server_Info *server) +@@ -1065,6 +1066,7 @@ struct cifs_ses { + struct list_head smb_ses_list; + struct list_head rlist; /* reconnect list */ + struct list_head tcon_list; ++ struct list_head dlist; /* dfs list */ + struct cifs_tcon *tcon_ipc; + spinlock_t ses_lock; /* protect anything here that is not protected */ + struct mutex session_mutex; +@@ -1294,6 +1296,7 @@ struct cifs_tcon { + /* BB add field for back pointer to sb struct(s)? */ + #ifdef CONFIG_CIFS_DFS_UPCALL + struct delayed_work dfs_cache_work; ++ struct list_head dfs_ses_list; + #endif + struct delayed_work query_interfaces; /* query interfaces workqueue job */ + char *origin_fullpath; /* canonical copy of smb3_fs_context::source */ +diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h +index c6d325666b5cd..8edb6fe89a97c 100644 +--- a/fs/smb/client/cifsproto.h ++++ b/fs/smb/client/cifsproto.h +@@ -737,15 +737,9 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options) + + int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry); + +-/* Put references of @ses and its children */ + static inline void cifs_put_smb_ses(struct cifs_ses *ses) + { +- struct cifs_ses *next; +- +- do { +- next = ses->dfs_root_ses; +- __cifs_put_smb_ses(ses); +- } while ((ses = next)); ++ __cifs_put_smb_ses(ses); + } + + /* Get an active reference of @ses and its children. +@@ -759,9 +753,7 @@ static inline void cifs_put_smb_ses(struct cifs_ses *ses) + static inline void cifs_smb_ses_inc_refcount(struct cifs_ses *ses) + { + lockdep_assert_held(&cifs_tcp_ses_lock); +- +- for (; ses; ses = ses->dfs_root_ses) +- ses->ses_count++; ++ ses->ses_count++; + } + + static inline bool dfs_src_pathname_equal(const char *s1, const char *s2) +diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c +index 8298d1745f9b9..14be8822d23a2 100644 +--- a/fs/smb/client/connect.c ++++ b/fs/smb/client/connect.c +@@ -1551,6 +1551,9 @@ static int match_server(struct TCP_Server_Info *server, + if (server->nosharesock) + return 0; + ++ if (!match_super && (ctx->dfs_conn || server->dfs_conn)) ++ return 0; ++ + /* If multidialect negotiation see if existing sessions match one */ + if (strcmp(ctx->vals->version_string, SMB3ANY_VERSION_STRING) == 0) { + if (server->vals->protocol_id < SMB30_PROT_ID) +@@ -1740,6 +1743,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx, + + if (ctx->nosharesock) + tcp_ses->nosharesock = true; ++ tcp_ses->dfs_conn = ctx->dfs_conn; + + tcp_ses->ops = ctx->ops; + tcp_ses->vals = ctx->vals; +@@ -1890,12 +1894,14 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx, + } + + /* this function must be called with ses_lock and chan_lock held */ +-static int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx) ++static int match_session(struct cifs_ses *ses, ++ struct smb3_fs_context *ctx, ++ bool match_super) + { + struct TCP_Server_Info *server = ses->server; + enum securityEnum ctx_sec, ses_sec; + +- if (ctx->dfs_root_ses != ses->dfs_root_ses) ++ if (!match_super && ctx->dfs_root_ses != ses->dfs_root_ses) + return 0; + + /* +@@ -2047,7 +2053,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) + continue; + } + spin_lock(&ses->chan_lock); +- if (match_session(ses, ctx)) { ++ if (match_session(ses, ctx, false)) { + spin_unlock(&ses->chan_lock); + spin_unlock(&ses->ses_lock); + ret = ses; +@@ -2450,8 +2456,6 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) + * need to lock before changing something in the session. + */ + spin_lock(&cifs_tcp_ses_lock); +- if (ctx->dfs_root_ses) +- cifs_smb_ses_inc_refcount(ctx->dfs_root_ses); + ses->dfs_root_ses = ctx->dfs_root_ses; + list_add(&ses->smb_ses_list, &server->smb_ses_list); + spin_unlock(&cifs_tcp_ses_lock); +@@ -2528,6 +2532,7 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace) + { + unsigned int xid; + struct cifs_ses *ses; ++ LIST_HEAD(ses_list); + + /* + * IPC tcon share the lifetime of their session and are +@@ -2552,6 +2557,9 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace) + + list_del_init(&tcon->tcon_list); + tcon->status = TID_EXITING; ++#ifdef CONFIG_CIFS_DFS_UPCALL ++ list_replace_init(&tcon->dfs_ses_list, &ses_list); ++#endif + spin_unlock(&tcon->tc_lock); + spin_unlock(&cifs_tcp_ses_lock); + +@@ -2579,6 +2587,9 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace) + cifs_fscache_release_super_cookie(tcon); + tconInfoFree(tcon, netfs_trace_tcon_ref_free); + cifs_put_smb_ses(ses); ++#ifdef CONFIG_CIFS_DFS_UPCALL ++ dfs_put_root_smb_sessions(&ses_list); ++#endif + } + + /** +@@ -2962,7 +2973,7 @@ cifs_match_super(struct super_block *sb, void *data) + spin_lock(&ses->chan_lock); + spin_lock(&tcon->tc_lock); + if (!match_server(tcp_srv, ctx, true) || +- !match_session(ses, ctx) || ++ !match_session(ses, ctx, true) || + !match_tcon(tcon, ctx) || + !match_prepath(sb, tcon, mnt_data)) { + rc = 0; +@@ -3712,13 +3723,12 @@ int cifs_is_path_remote(struct cifs_mount_ctx *mnt_ctx) + int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) + { + struct cifs_mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; +- bool isdfs; + int rc; + +- rc = dfs_mount_share(&mnt_ctx, &isdfs); ++ rc = dfs_mount_share(&mnt_ctx); + if (rc) + goto error; +- if (!isdfs) ++ if (!ctx->dfs_conn) + goto out; + + /* +@@ -4135,7 +4145,7 @@ cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses) + } + + static struct cifs_tcon * +-__cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) ++cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) + { + int rc; + struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb); +@@ -4233,17 +4243,6 @@ __cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) + return tcon; + } + +-static struct cifs_tcon * +-cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) +-{ +- struct cifs_tcon *ret; +- +- cifs_mount_lock(); +- ret = __cifs_construct_tcon(cifs_sb, fsuid); +- cifs_mount_unlock(); +- return ret; +-} +- + struct cifs_tcon * + cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb) + { +diff --git a/fs/smb/client/dfs.c b/fs/smb/client/dfs.c +index bd259b04cdede..c35953843373e 100644 +--- a/fs/smb/client/dfs.c ++++ b/fs/smb/client/dfs.c +@@ -69,7 +69,7 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path) + * Get an active reference of @ses so that next call to cifs_put_tcon() won't + * release it as any new DFS referrals must go through its IPC tcon. + */ +-static void add_root_smb_session(struct cifs_mount_ctx *mnt_ctx) ++static void set_root_smb_session(struct cifs_mount_ctx *mnt_ctx) + { + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + struct cifs_ses *ses = mnt_ctx->ses; +@@ -95,7 +95,7 @@ static inline int parse_dfs_target(struct smb3_fs_context *ctx, + return rc; + } + +-static int set_ref_paths(struct cifs_mount_ctx *mnt_ctx, ++static int setup_dfs_ref(struct cifs_mount_ctx *mnt_ctx, + struct dfs_info3_param *tgt, + struct dfs_ref_walk *rw) + { +@@ -120,6 +120,7 @@ static int set_ref_paths(struct cifs_mount_ctx *mnt_ctx, + } + ref_walk_path(rw) = ref_path; + ref_walk_fpath(rw) = full_path; ++ ref_walk_ses(rw) = ctx->dfs_root_ses; + return 0; + } + +@@ -128,11 +129,11 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx, + { + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + struct dfs_info3_param tgt = {}; +- bool is_refsrv; + int rc = -ENOENT; + + again: + do { ++ ctx->dfs_root_ses = ref_walk_ses(rw); + if (ref_walk_empty(rw)) { + rc = dfs_get_referral(mnt_ctx, ref_walk_path(rw) + 1, + NULL, ref_walk_tl(rw)); +@@ -158,10 +159,7 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx, + if (rc) + continue; + +- is_refsrv = tgt.server_type == DFS_TYPE_ROOT || +- DFS_INTERLINK(tgt.flags); + ref_walk_set_tgt_hint(rw); +- + if (tgt.flags & DFSREF_STORAGE_SERVER) { + rc = cifs_mount_get_tcon(mnt_ctx); + if (!rc) +@@ -172,12 +170,10 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx, + continue; + } + +- if (is_refsrv) +- add_root_smb_session(mnt_ctx); +- ++ set_root_smb_session(mnt_ctx); + rc = ref_walk_advance(rw); + if (!rc) { +- rc = set_ref_paths(mnt_ctx, &tgt, rw); ++ rc = setup_dfs_ref(mnt_ctx, &tgt, rw); + if (!rc) { + rc = -EREMOTE; + goto again; +@@ -193,20 +189,22 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx, + return rc; + } + +-static int dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx) ++static int dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx, ++ struct dfs_ref_walk **rw) + { +- struct dfs_ref_walk *rw; + int rc; + +- rw = ref_walk_alloc(); +- if (IS_ERR(rw)) +- return PTR_ERR(rw); ++ *rw = ref_walk_alloc(); ++ if (IS_ERR(*rw)) { ++ rc = PTR_ERR(*rw); ++ *rw = NULL; ++ return rc; ++ } + +- ref_walk_init(rw); +- rc = set_ref_paths(mnt_ctx, NULL, rw); ++ ref_walk_init(*rw); ++ rc = setup_dfs_ref(mnt_ctx, NULL, *rw); + if (!rc) +- rc = __dfs_referral_walk(mnt_ctx, rw); +- ref_walk_free(rw); ++ rc = __dfs_referral_walk(mnt_ctx, *rw); + return rc; + } + +@@ -214,16 +212,16 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) + { + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; ++ struct dfs_ref_walk *rw = NULL; + struct cifs_tcon *tcon; + char *origin_fullpath; +- bool new_tcon = true; + int rc; + + origin_fullpath = dfs_get_path(cifs_sb, ctx->source); + if (IS_ERR(origin_fullpath)) + return PTR_ERR(origin_fullpath); + +- rc = dfs_referral_walk(mnt_ctx); ++ rc = dfs_referral_walk(mnt_ctx, &rw); + if (!rc) { + /* + * Prevent superblock from being created with any missing +@@ -241,21 +239,16 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) + + tcon = mnt_ctx->tcon; + spin_lock(&tcon->tc_lock); +- if (!tcon->origin_fullpath) { +- tcon->origin_fullpath = origin_fullpath; +- origin_fullpath = NULL; +- } else { +- new_tcon = false; +- } ++ tcon->origin_fullpath = origin_fullpath; ++ origin_fullpath = NULL; ++ ref_walk_set_tcon(rw, tcon); + spin_unlock(&tcon->tc_lock); +- +- if (new_tcon) { +- queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work, +- dfs_cache_get_ttl() * HZ); +- } ++ queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work, ++ dfs_cache_get_ttl() * HZ); + + out: + kfree(origin_fullpath); ++ ref_walk_free(rw); + return rc; + } + +@@ -279,7 +272,7 @@ static int update_fs_context_dstaddr(struct smb3_fs_context *ctx) + return rc; + } + +-int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) ++int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) + { + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + bool nodfs = ctx->nodfs; +@@ -289,7 +282,6 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) + if (rc) + return rc; + +- *isdfs = false; + rc = get_session(mnt_ctx, NULL); + if (rc) + return rc; +@@ -317,10 +309,15 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) + return rc; + } + +- *isdfs = true; +- add_root_smb_session(mnt_ctx); +- rc = __dfs_mount_share(mnt_ctx); +- dfs_put_root_smb_sessions(mnt_ctx); ++ if (!ctx->dfs_conn) { ++ ctx->dfs_conn = true; ++ cifs_mount_put_conns(mnt_ctx); ++ rc = get_session(mnt_ctx, NULL); ++ } ++ if (!rc) { ++ set_root_smb_session(mnt_ctx); ++ rc = __dfs_mount_share(mnt_ctx); ++ } + return rc; + } + +diff --git a/fs/smb/client/dfs.h b/fs/smb/client/dfs.h +index e5c4dcf837503..1aa2bc65b3bc2 100644 +--- a/fs/smb/client/dfs.h ++++ b/fs/smb/client/dfs.h +@@ -19,6 +19,7 @@ + struct dfs_ref { + char *path; + char *full_path; ++ struct cifs_ses *ses; + struct dfs_cache_tgt_list tl; + struct dfs_cache_tgt_iterator *tit; + }; +@@ -38,6 +39,7 @@ struct dfs_ref_walk { + #define ref_walk_path(w) (ref_walk_cur(w)->path) + #define ref_walk_fpath(w) (ref_walk_cur(w)->full_path) + #define ref_walk_tl(w) (&ref_walk_cur(w)->tl) ++#define ref_walk_ses(w) (ref_walk_cur(w)->ses) + + static inline struct dfs_ref_walk *ref_walk_alloc(void) + { +@@ -60,14 +62,19 @@ static inline void __ref_walk_free(struct dfs_ref *ref) + kfree(ref->path); + kfree(ref->full_path); + dfs_cache_free_tgts(&ref->tl); ++ if (ref->ses) ++ cifs_put_smb_ses(ref->ses); + memset(ref, 0, sizeof(*ref)); + } + + static inline void ref_walk_free(struct dfs_ref_walk *rw) + { +- struct dfs_ref *ref = ref_walk_start(rw); ++ struct dfs_ref *ref; + +- for (; ref <= ref_walk_end(rw); ref++) ++ if (!rw) ++ return; ++ ++ for (ref = ref_walk_start(rw); ref <= ref_walk_end(rw); ref++) + __ref_walk_free(ref); + kfree(rw); + } +@@ -116,9 +123,22 @@ static inline void ref_walk_set_tgt_hint(struct dfs_ref_walk *rw) + ref_walk_tit(rw)); + } + ++static inline void ref_walk_set_tcon(struct dfs_ref_walk *rw, ++ struct cifs_tcon *tcon) ++{ ++ struct dfs_ref *ref = ref_walk_start(rw); ++ ++ for (; ref <= ref_walk_cur(rw); ref++) { ++ if (WARN_ON_ONCE(!ref->ses)) ++ continue; ++ list_add(&ref->ses->dlist, &tcon->dfs_ses_list); ++ ref->ses = NULL; ++ } ++} ++ + int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref, + struct smb3_fs_context *ctx); +-int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs); ++int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx); + + static inline char *dfs_get_path(struct cifs_sb_info *cifs_sb, const char *path) + { +@@ -142,20 +162,14 @@ static inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx, const char *p + * references of all DFS root sessions that were used across the mount process + * in dfs_mount_share(). + */ +-static inline void dfs_put_root_smb_sessions(struct cifs_mount_ctx *mnt_ctx) ++static inline void dfs_put_root_smb_sessions(struct list_head *head) + { +- const struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; +- struct cifs_ses *ses = ctx->dfs_root_ses; +- struct cifs_ses *cur; +- +- if (!ses) +- return; ++ struct cifs_ses *ses, *n; + +- for (cur = ses; cur; cur = cur->dfs_root_ses) { +- if (cur->dfs_root_ses) +- cifs_put_smb_ses(cur->dfs_root_ses); ++ list_for_each_entry_safe(ses, n, head, dlist) { ++ list_del_init(&ses->dlist); ++ cifs_put_smb_ses(ses); + } +- cifs_put_smb_ses(ses); + } + + #endif /* _CIFS_DFS_H */ +diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c +index 3cf7c88489be4..433f546055b97 100644 +--- a/fs/smb/client/dfs_cache.c ++++ b/fs/smb/client/dfs_cache.c +@@ -1336,9 +1336,8 @@ void dfs_cache_refresh(struct work_struct *work) + struct cifs_ses *ses; + + tcon = container_of(work, struct cifs_tcon, dfs_cache_work.work); +- ses = tcon->ses->dfs_root_ses; + +- for (; ses; ses = ses->dfs_root_ses) ++ list_for_each_entry(ses, &tcon->dfs_ses_list, dlist) + refresh_ses_referral(ses); + refresh_tcon_referral(tcon, false); + +diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h +index d0a2043ea4468..52ee72e562f5f 100644 +--- a/fs/smb/client/fs_context.h ++++ b/fs/smb/client/fs_context.h +@@ -287,6 +287,7 @@ struct smb3_fs_context { + struct cifs_ses *dfs_root_ses; + bool dfs_automount:1; /* set for dfs automount only */ + enum cifs_reparse_type reparse_type; ++ bool dfs_conn:1; /* set for dfs mounts */ + }; + + extern const struct fs_parameter_spec smb3_fs_parameters[]; +diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c +index 9e8e0a01ae8eb..2e9a14e28e466 100644 +--- a/fs/smb/client/misc.c ++++ b/fs/smb/client/misc.c +@@ -145,6 +145,9 @@ tcon_info_alloc(bool dir_leases_enabled, enum smb3_tcon_ref_trace trace) + mutex_init(&ret_buf->fscache_lock); + #endif + trace_smb3_tcon_ref(ret_buf->debug_id, ret_buf->tc_count, trace); ++#ifdef CONFIG_CIFS_DFS_UPCALL ++ INIT_LIST_HEAD(&ret_buf->dfs_ses_list); ++#endif + + return ret_buf; + } +diff --git a/fs/smb/client/namespace.c b/fs/smb/client/namespace.c +index ec58c0e507244..a6655807c0865 100644 +--- a/fs/smb/client/namespace.c ++++ b/fs/smb/client/namespace.c +@@ -260,7 +260,7 @@ static struct vfsmount *cifs_do_automount(struct path *path) + ctx->source = NULL; + goto out; + } +- ctx->dfs_automount = is_dfs_mount(mntpt); ++ ctx->dfs_automount = ctx->dfs_conn = is_dfs_mount(mntpt); + cifs_dbg(FYI, "%s: ctx: source=%s UNC=%s prepath=%s dfs_automount=%d\n", + __func__, ctx->source, ctx->UNC, ctx->prepath, ctx->dfs_automount); + +-- +2.39.5 + diff --git a/queue-6.6/ublk-sanity-check-add_dev-input-for-underflow.patch b/queue-6.6/ublk-sanity-check-add_dev-input-for-underflow.patch new file mode 100644 index 0000000000..37249425b1 --- /dev/null +++ b/queue-6.6/ublk-sanity-check-add_dev-input-for-underflow.patch @@ -0,0 +1,38 @@ +From 110ae4fd707a05ff8ae0478223a4090e057acbfa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 26 Jun 2025 12:20:45 +1000 +Subject: ublk: sanity check add_dev input for underflow + +From: Ronnie Sahlberg + +[ Upstream commit 969127bf0783a4ac0c8a27e633a9e8ea1738583f ] + +Add additional checks that queue depth and number of queues are +non-zero. + +Signed-off-by: Ronnie Sahlberg +Reviewed-by: Ming Lei +Link: https://lore.kernel.org/r/20250626022046.235018-1-ronniesahlberg@gmail.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + drivers/block/ublk_drv.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c +index df3e5aab4b5ac..8c873a8e39cd9 100644 +--- a/drivers/block/ublk_drv.c ++++ b/drivers/block/ublk_drv.c +@@ -2323,7 +2323,8 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd) + if (copy_from_user(&info, argp, sizeof(info))) + return -EFAULT; + +- if (info.queue_depth > UBLK_MAX_QUEUE_DEPTH || info.nr_hw_queues > UBLK_MAX_NR_QUEUES) ++ if (info.queue_depth > UBLK_MAX_QUEUE_DEPTH || !info.queue_depth || ++ info.nr_hw_queues > UBLK_MAX_NR_QUEUES || !info.nr_hw_queues) + return -EINVAL; + + if (capable(CAP_SYS_ADMIN)) +-- +2.39.5 + diff --git a/queue-6.6/um-vector-reduce-stack-usage-in-vector_eth_configure.patch b/queue-6.6/um-vector-reduce-stack-usage-in-vector_eth_configure.patch new file mode 100644 index 0000000000..05599f1b30 --- /dev/null +++ b/queue-6.6/um-vector-reduce-stack-usage-in-vector_eth_configure.patch @@ -0,0 +1,93 @@ +From 2a640d89b8662c1c5c9859644619c9ea60d530a6 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 23 Jun 2025 19:08:29 +0800 +Subject: um: vector: Reduce stack usage in vector_eth_configure() + +From: Tiwei Bie + +[ Upstream commit 2d65fc13be85c336c56af7077f08ccd3a3a15a4a ] + +When compiling with clang (19.1.7), initializing *vp using a compound +literal may result in excessive stack usage. Fix it by initializing the +required fields of *vp individually. + +Without this patch: + +$ objdump -d arch/um/drivers/vector_kern.o | ./scripts/checkstack.pl x86_64 0 +... +0x0000000000000540 vector_eth_configure [vector_kern.o]:1472 +... + +With this patch: + +$ objdump -d arch/um/drivers/vector_kern.o | ./scripts/checkstack.pl x86_64 0 +... +0x0000000000000540 vector_eth_configure [vector_kern.o]:208 +... + +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-kbuild-all/202506221017.WtB7Usua-lkp@intel.com/ +Signed-off-by: Tiwei Bie +Link: https://patch.msgid.link/20250623110829.314864-1-tiwei.btw@antgroup.com +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + arch/um/drivers/vector_kern.c | 42 +++++++++++------------------------ + 1 file changed, 13 insertions(+), 29 deletions(-) + +diff --git a/arch/um/drivers/vector_kern.c b/arch/um/drivers/vector_kern.c +index 2baa8d4a33ed3..1a068859a4185 100644 +--- a/arch/um/drivers/vector_kern.c ++++ b/arch/um/drivers/vector_kern.c +@@ -1600,35 +1600,19 @@ static void vector_eth_configure( + + device->dev = dev; + +- *vp = ((struct vector_private) +- { +- .list = LIST_HEAD_INIT(vp->list), +- .dev = dev, +- .unit = n, +- .options = get_transport_options(def), +- .rx_irq = 0, +- .tx_irq = 0, +- .parsed = def, +- .max_packet = get_mtu(def) + ETH_HEADER_OTHER, +- /* TODO - we need to calculate headroom so that ip header +- * is 16 byte aligned all the time +- */ +- .headroom = get_headroom(def), +- .form_header = NULL, +- .verify_header = NULL, +- .header_rxbuffer = NULL, +- .header_txbuffer = NULL, +- .header_size = 0, +- .rx_header_size = 0, +- .rexmit_scheduled = false, +- .opened = false, +- .transport_data = NULL, +- .in_write_poll = false, +- .coalesce = 2, +- .req_size = get_req_size(def), +- .in_error = false, +- .bpf = NULL +- }); ++ INIT_LIST_HEAD(&vp->list); ++ vp->dev = dev; ++ vp->unit = n; ++ vp->options = get_transport_options(def); ++ vp->parsed = def; ++ vp->max_packet = get_mtu(def) + ETH_HEADER_OTHER; ++ /* ++ * TODO - we need to calculate headroom so that ip header ++ * is 16 byte aligned all the time ++ */ ++ vp->headroom = get_headroom(def); ++ vp->coalesce = 2; ++ vp->req_size = get_req_size(def); + + dev->features = dev->hw_features = (NETIF_F_SG | NETIF_F_FRAGLIST); + INIT_WORK(&vp->reset_tx, vector_reset_tx); +-- +2.39.5 + diff --git a/queue-6.6/usb-cdnsp-fix-issue-with-cv-bad-descriptor-test.patch b/queue-6.6/usb-cdnsp-fix-issue-with-cv-bad-descriptor-test.patch new file mode 100644 index 0000000000..2f761a7a02 --- /dev/null +++ b/queue-6.6/usb-cdnsp-fix-issue-with-cv-bad-descriptor-test.patch @@ -0,0 +1,121 @@ +From 70a5692c97828a761b4949c603b02e8670c1b98d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 20 Jun 2025 08:23:12 +0000 +Subject: usb: cdnsp: Fix issue with CV Bad Descriptor test + +From: Pawel Laszczak + +[ Upstream commit 2831a81077f5162f104ba5a97a7d886eb371c21c ] + +The SSP2 controller has extra endpoint state preserve bit (ESP) which +setting causes that endpoint state will be preserved during +Halt Endpoint command. It is used only for EP0. +Without this bit the Command Verifier "TD 9.10 Bad Descriptor Test" +failed. +Setting this bit doesn't have any impact for SSP controller. + +Fixes: 3d82904559f4 ("usb: cdnsp: cdns3 Add main part of Cadence USBSSP DRD Driver") +Cc: stable +Signed-off-by: Pawel Laszczak +Acked-by: Peter Chen +Link: https://lore.kernel.org/r/PH7PR07MB95382CCD50549DABAEFD6156DD7CA@PH7PR07MB9538.namprd07.prod.outlook.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/usb/cdns3/cdnsp-debug.h | 5 +++-- + drivers/usb/cdns3/cdnsp-ep0.c | 18 +++++++++++++++--- + drivers/usb/cdns3/cdnsp-gadget.h | 6 ++++++ + drivers/usb/cdns3/cdnsp-ring.c | 3 ++- + 4 files changed, 26 insertions(+), 6 deletions(-) + +diff --git a/drivers/usb/cdns3/cdnsp-debug.h b/drivers/usb/cdns3/cdnsp-debug.h +index cd138acdcce16..86860686d8363 100644 +--- a/drivers/usb/cdns3/cdnsp-debug.h ++++ b/drivers/usb/cdns3/cdnsp-debug.h +@@ -327,12 +327,13 @@ static inline const char *cdnsp_decode_trb(char *str, size_t size, u32 field0, + case TRB_RESET_EP: + case TRB_HALT_ENDPOINT: + ret = scnprintf(str, size, +- "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c", ++ "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c %c", + cdnsp_trb_type_string(type), + ep_num, ep_id % 2 ? "out" : "in", + TRB_TO_EP_INDEX(field3), field1, field0, + TRB_TO_SLOT_ID(field3), +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ field3 & TRB_CYCLE ? 'C' : 'c', ++ field3 & TRB_ESP ? 'P' : 'p'); + break; + case TRB_STOP_RING: + ret = scnprintf(str, size, +diff --git a/drivers/usb/cdns3/cdnsp-ep0.c b/drivers/usb/cdns3/cdnsp-ep0.c +index f317d3c847810..5cd9b898ce971 100644 +--- a/drivers/usb/cdns3/cdnsp-ep0.c ++++ b/drivers/usb/cdns3/cdnsp-ep0.c +@@ -414,6 +414,7 @@ static int cdnsp_ep0_std_request(struct cdnsp_device *pdev, + void cdnsp_setup_analyze(struct cdnsp_device *pdev) + { + struct usb_ctrlrequest *ctrl = &pdev->setup; ++ struct cdnsp_ep *pep; + int ret = -EINVAL; + u16 len; + +@@ -427,10 +428,21 @@ void cdnsp_setup_analyze(struct cdnsp_device *pdev) + goto out; + } + ++ pep = &pdev->eps[0]; ++ + /* Restore the ep0 to Stopped/Running state. */ +- if (pdev->eps[0].ep_state & EP_HALTED) { +- trace_cdnsp_ep0_halted("Restore to normal state"); +- cdnsp_halt_endpoint(pdev, &pdev->eps[0], 0); ++ if (pep->ep_state & EP_HALTED) { ++ if (GET_EP_CTX_STATE(pep->out_ctx) == EP_STATE_HALTED) ++ cdnsp_halt_endpoint(pdev, pep, 0); ++ ++ /* ++ * Halt Endpoint Command for SSP2 for ep0 preserve current ++ * endpoint state and driver has to synchronize the ++ * software endpoint state with endpoint output context ++ * state. ++ */ ++ pep->ep_state &= ~EP_HALTED; ++ pep->ep_state |= EP_STOPPED; + } + + /* +diff --git a/drivers/usb/cdns3/cdnsp-gadget.h b/drivers/usb/cdns3/cdnsp-gadget.h +index 2afa3e558f85c..a91cca509db08 100644 +--- a/drivers/usb/cdns3/cdnsp-gadget.h ++++ b/drivers/usb/cdns3/cdnsp-gadget.h +@@ -987,6 +987,12 @@ enum cdnsp_setup_dev { + #define STREAM_ID_FOR_TRB(p) ((((p)) << 16) & GENMASK(31, 16)) + #define SCT_FOR_TRB(p) (((p) << 1) & 0x7) + ++/* ++ * Halt Endpoint Command TRB field. ++ * The ESP bit only exists in the SSP2 controller. ++ */ ++#define TRB_ESP BIT(9) ++ + /* Link TRB specific fields. */ + #define TRB_TC BIT(1) + +diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c +index 757fdd918286d..0758f171f73ec 100644 +--- a/drivers/usb/cdns3/cdnsp-ring.c ++++ b/drivers/usb/cdns3/cdnsp-ring.c +@@ -2485,7 +2485,8 @@ void cdnsp_queue_halt_endpoint(struct cdnsp_device *pdev, unsigned int ep_index) + { + cdnsp_queue_command(pdev, 0, 0, 0, TRB_TYPE(TRB_HALT_ENDPOINT) | + SLOT_ID_FOR_TRB(pdev->slot_id) | +- EP_ID_FOR_TRB(ep_index)); ++ EP_ID_FOR_TRB(ep_index) | ++ (!ep_index ? TRB_ESP : 0)); + } + + void cdnsp_force_header_wakeup(struct cdnsp_device *pdev, int intf_num) +-- +2.39.5 + diff --git a/queue-6.6/usb-cdnsp-remove-trb_flush_endpoint-command.patch b/queue-6.6/usb-cdnsp-remove-trb_flush_endpoint-command.patch new file mode 100644 index 0000000000..f6e2f544a2 --- /dev/null +++ b/queue-6.6/usb-cdnsp-remove-trb_flush_endpoint-command.patch @@ -0,0 +1,147 @@ +From 2dfa748de49ab3fb8b83c343d2c62efad76efdbc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 26 Oct 2023 09:37:37 +0200 +Subject: usb:cdnsp: remove TRB_FLUSH_ENDPOINT command + +From: Pawel Laszczak + +[ Upstream commit 2998874736bca1031ca84b0a3235a2cd09dfa426 ] + +Patch removes TRB_FLUSH_ENDPOINT command from driver. +This command is not supported by controller and +USBSSP returns TRB Error completion code for it. + +Signed-off-by: Pawel Laszczak +Acked-by: Peter Chen +Link: https://lore.kernel.org/r/20231026073737.165450-1-pawell@cadence.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 2831a81077f5 ("usb: cdnsp: Fix issue with CV Bad Descriptor test") +Signed-off-by: Sasha Levin +--- + drivers/usb/cdns3/cdnsp-debug.h | 3 --- + drivers/usb/cdns3/cdnsp-gadget.c | 6 +----- + drivers/usb/cdns3/cdnsp-gadget.h | 5 ----- + drivers/usb/cdns3/cdnsp-ring.c | 24 ------------------------ + 4 files changed, 1 insertion(+), 37 deletions(-) + +diff --git a/drivers/usb/cdns3/cdnsp-debug.h b/drivers/usb/cdns3/cdnsp-debug.h +index f0ca865cce2a0..ad617b7455b9c 100644 +--- a/drivers/usb/cdns3/cdnsp-debug.h ++++ b/drivers/usb/cdns3/cdnsp-debug.h +@@ -131,8 +131,6 @@ static inline const char *cdnsp_trb_type_string(u8 type) + return "Endpoint Not ready"; + case TRB_HALT_ENDPOINT: + return "Halt Endpoint"; +- case TRB_FLUSH_ENDPOINT: +- return "FLush Endpoint"; + default: + return "UNKNOWN"; + } +@@ -328,7 +326,6 @@ static inline const char *cdnsp_decode_trb(char *str, size_t size, u32 field0, + break; + case TRB_RESET_EP: + case TRB_HALT_ENDPOINT: +- case TRB_FLUSH_ENDPOINT: + ret = snprintf(str, size, + "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c", + cdnsp_trb_type_string(type), +diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c +index 132885fbb98f6..38e693cd3efc0 100644 +--- a/drivers/usb/cdns3/cdnsp-gadget.c ++++ b/drivers/usb/cdns3/cdnsp-gadget.c +@@ -1061,10 +1061,8 @@ static int cdnsp_gadget_ep_disable(struct usb_ep *ep) + pep->ep_state |= EP_DIS_IN_RROGRESS; + + /* Endpoint was unconfigured by Reset Device command. */ +- if (!(pep->ep_state & EP_UNCONFIGURED)) { ++ if (!(pep->ep_state & EP_UNCONFIGURED)) + cdnsp_cmd_stop_ep(pdev, pep); +- cdnsp_cmd_flush_ep(pdev, pep); +- } + + /* Remove all queued USB requests. */ + while (!list_empty(&pep->pending_list)) { +@@ -1461,8 +1459,6 @@ static void cdnsp_stop(struct cdnsp_device *pdev) + { + u32 temp; + +- cdnsp_cmd_flush_ep(pdev, &pdev->eps[0]); +- + /* Remove internally queued request for ep0. */ + if (!list_empty(&pdev->eps[0].pending_list)) { + struct cdnsp_request *req; +diff --git a/drivers/usb/cdns3/cdnsp-gadget.h b/drivers/usb/cdns3/cdnsp-gadget.h +index 909cee01772a7..2afa3e558f85c 100644 +--- a/drivers/usb/cdns3/cdnsp-gadget.h ++++ b/drivers/usb/cdns3/cdnsp-gadget.h +@@ -1138,8 +1138,6 @@ union cdnsp_trb { + #define TRB_HALT_ENDPOINT 54 + /* Doorbell Overflow Event. */ + #define TRB_DRB_OVERFLOW 57 +-/* Flush Endpoint Command. */ +-#define TRB_FLUSH_ENDPOINT 58 + + #define TRB_TYPE_LINK(x) (((x) & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK)) + #define TRB_TYPE_LINK_LE32(x) (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \ +@@ -1552,8 +1550,6 @@ void cdnsp_queue_configure_endpoint(struct cdnsp_device *pdev, + void cdnsp_queue_reset_ep(struct cdnsp_device *pdev, unsigned int ep_index); + void cdnsp_queue_halt_endpoint(struct cdnsp_device *pdev, + unsigned int ep_index); +-void cdnsp_queue_flush_endpoint(struct cdnsp_device *pdev, +- unsigned int ep_index); + void cdnsp_force_header_wakeup(struct cdnsp_device *pdev, int intf_num); + void cdnsp_queue_reset_device(struct cdnsp_device *pdev); + void cdnsp_queue_new_dequeue_state(struct cdnsp_device *pdev, +@@ -1587,7 +1583,6 @@ void cdnsp_irq_reset(struct cdnsp_device *pdev); + int cdnsp_halt_endpoint(struct cdnsp_device *pdev, + struct cdnsp_ep *pep, int value); + int cdnsp_cmd_stop_ep(struct cdnsp_device *pdev, struct cdnsp_ep *pep); +-int cdnsp_cmd_flush_ep(struct cdnsp_device *pdev, struct cdnsp_ep *pep); + void cdnsp_setup_analyze(struct cdnsp_device *pdev); + int cdnsp_status_stage(struct cdnsp_device *pdev); + int cdnsp_reset_device(struct cdnsp_device *pdev); +diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c +index 3b17d9e4b07d8..757fdd918286d 100644 +--- a/drivers/usb/cdns3/cdnsp-ring.c ++++ b/drivers/usb/cdns3/cdnsp-ring.c +@@ -2159,19 +2159,6 @@ int cdnsp_cmd_stop_ep(struct cdnsp_device *pdev, struct cdnsp_ep *pep) + return ret; + } + +-int cdnsp_cmd_flush_ep(struct cdnsp_device *pdev, struct cdnsp_ep *pep) +-{ +- int ret; +- +- cdnsp_queue_flush_endpoint(pdev, pep->idx); +- cdnsp_ring_cmd_db(pdev); +- ret = cdnsp_wait_for_cmd_compl(pdev); +- +- trace_cdnsp_handle_cmd_flush_ep(pep->out_ctx); +- +- return ret; +-} +- + /* + * The transfer burst count field of the isochronous TRB defines the number of + * bursts that are required to move all packets in this TD. Only SuperSpeed +@@ -2501,17 +2488,6 @@ void cdnsp_queue_halt_endpoint(struct cdnsp_device *pdev, unsigned int ep_index) + EP_ID_FOR_TRB(ep_index)); + } + +-/* +- * Queue a flush endpoint request on the command ring. +- */ +-void cdnsp_queue_flush_endpoint(struct cdnsp_device *pdev, +- unsigned int ep_index) +-{ +- cdnsp_queue_command(pdev, 0, 0, 0, TRB_TYPE(TRB_FLUSH_ENDPOINT) | +- SLOT_ID_FOR_TRB(pdev->slot_id) | +- EP_ID_FOR_TRB(ep_index)); +-} +- + void cdnsp_force_header_wakeup(struct cdnsp_device *pdev, int intf_num) + { + u32 lo, mid; +-- +2.39.5 + diff --git a/queue-6.6/usb-cdnsp-replace-snprintf-with-the-safer-scnprintf-.patch b/queue-6.6/usb-cdnsp-replace-snprintf-with-the-safer-scnprintf-.patch new file mode 100644 index 0000000000..14ef28fcc4 --- /dev/null +++ b/queue-6.6/usb-cdnsp-replace-snprintf-with-the-safer-scnprintf-.patch @@ -0,0 +1,475 @@ +From 8a3cc79fb2cc82a52f676714f76fd24f4b95012e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 30 Nov 2023 10:54:36 +0000 +Subject: usb: cdnsp: Replace snprintf() with the safer scnprintf() variant + +From: Lee Jones + +[ Upstream commit b385ef088c7aab20a2c0dc20d390d69a6620f0f3 ] + +There is a general misunderstanding amongst engineers that {v}snprintf() +returns the length of the data *actually* encoded into the destination +array. However, as per the C99 standard {v}snprintf() really returns +the length of the data that *would have been* written if there were +enough space for it. This misunderstanding has led to buffer-overruns +in the past. It's generally considered safer to use the {v}scnprintf() +variants in their place (or even sprintf() in simple cases). So let's +do that. + +The uses in this file all seem to assume that data *has been* written! + +Link: https://lwn.net/Articles/69419/ +Link: https://github.com/KSPP/linux/issues/105 +Cc: Pawel Laszczak +Cc: Greg Kroah-Hartman +Cc: linux-usb@vger.kernel.org +Signed-off-by: Lee Jones +Link: https://lore.kernel.org/r/20231130105459.3208986-3-lee@kernel.org +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 2831a81077f5 ("usb: cdnsp: Fix issue with CV Bad Descriptor test") +Signed-off-by: Sasha Levin +--- + drivers/usb/cdns3/cdnsp-debug.h | 354 ++++++++++++++++---------------- + 1 file changed, 177 insertions(+), 177 deletions(-) + +diff --git a/drivers/usb/cdns3/cdnsp-debug.h b/drivers/usb/cdns3/cdnsp-debug.h +index ad617b7455b9c..cd138acdcce16 100644 +--- a/drivers/usb/cdns3/cdnsp-debug.h ++++ b/drivers/usb/cdns3/cdnsp-debug.h +@@ -187,202 +187,202 @@ static inline const char *cdnsp_decode_trb(char *str, size_t size, u32 field0, + + switch (type) { + case TRB_LINK: +- ret = snprintf(str, size, +- "LINK %08x%08x intr %ld type '%s' flags %c:%c:%c:%c", +- field1, field0, GET_INTR_TARGET(field2), +- cdnsp_trb_type_string(type), +- field3 & TRB_IOC ? 'I' : 'i', +- field3 & TRB_CHAIN ? 'C' : 'c', +- field3 & TRB_TC ? 'T' : 't', +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ ret = scnprintf(str, size, ++ "LINK %08x%08x intr %ld type '%s' flags %c:%c:%c:%c", ++ field1, field0, GET_INTR_TARGET(field2), ++ cdnsp_trb_type_string(type), ++ field3 & TRB_IOC ? 'I' : 'i', ++ field3 & TRB_CHAIN ? 'C' : 'c', ++ field3 & TRB_TC ? 'T' : 't', ++ field3 & TRB_CYCLE ? 'C' : 'c'); + break; + case TRB_TRANSFER: + case TRB_COMPLETION: + case TRB_PORT_STATUS: + case TRB_HC_EVENT: +- ret = snprintf(str, size, +- "ep%d%s(%d) type '%s' TRB %08x%08x status '%s'" +- " len %ld slot %ld flags %c:%c", +- ep_num, ep_id % 2 ? "out" : "in", +- TRB_TO_EP_INDEX(field3), +- cdnsp_trb_type_string(type), field1, field0, +- cdnsp_trb_comp_code_string(GET_COMP_CODE(field2)), +- EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3), +- field3 & EVENT_DATA ? 'E' : 'e', +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ ret = scnprintf(str, size, ++ "ep%d%s(%d) type '%s' TRB %08x%08x status '%s'" ++ " len %ld slot %ld flags %c:%c", ++ ep_num, ep_id % 2 ? "out" : "in", ++ TRB_TO_EP_INDEX(field3), ++ cdnsp_trb_type_string(type), field1, field0, ++ cdnsp_trb_comp_code_string(GET_COMP_CODE(field2)), ++ EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3), ++ field3 & EVENT_DATA ? 'E' : 'e', ++ field3 & TRB_CYCLE ? 'C' : 'c'); + break; + case TRB_MFINDEX_WRAP: +- ret = snprintf(str, size, "%s: flags %c", +- cdnsp_trb_type_string(type), +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ ret = scnprintf(str, size, "%s: flags %c", ++ cdnsp_trb_type_string(type), ++ field3 & TRB_CYCLE ? 'C' : 'c'); + break; + case TRB_SETUP: +- ret = snprintf(str, size, +- "type '%s' bRequestType %02x bRequest %02x " +- "wValue %02x%02x wIndex %02x%02x wLength %d " +- "length %ld TD size %ld intr %ld Setup ID %ld " +- "flags %c:%c:%c", +- cdnsp_trb_type_string(type), +- field0 & 0xff, +- (field0 & 0xff00) >> 8, +- (field0 & 0xff000000) >> 24, +- (field0 & 0xff0000) >> 16, +- (field1 & 0xff00) >> 8, +- field1 & 0xff, +- (field1 & 0xff000000) >> 16 | +- (field1 & 0xff0000) >> 16, +- TRB_LEN(field2), GET_TD_SIZE(field2), +- GET_INTR_TARGET(field2), +- TRB_SETUPID_TO_TYPE(field3), +- field3 & TRB_IDT ? 'D' : 'd', +- field3 & TRB_IOC ? 'I' : 'i', +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ ret = scnprintf(str, size, ++ "type '%s' bRequestType %02x bRequest %02x " ++ "wValue %02x%02x wIndex %02x%02x wLength %d " ++ "length %ld TD size %ld intr %ld Setup ID %ld " ++ "flags %c:%c:%c", ++ cdnsp_trb_type_string(type), ++ field0 & 0xff, ++ (field0 & 0xff00) >> 8, ++ (field0 & 0xff000000) >> 24, ++ (field0 & 0xff0000) >> 16, ++ (field1 & 0xff00) >> 8, ++ field1 & 0xff, ++ (field1 & 0xff000000) >> 16 | ++ (field1 & 0xff0000) >> 16, ++ TRB_LEN(field2), GET_TD_SIZE(field2), ++ GET_INTR_TARGET(field2), ++ TRB_SETUPID_TO_TYPE(field3), ++ field3 & TRB_IDT ? 'D' : 'd', ++ field3 & TRB_IOC ? 'I' : 'i', ++ field3 & TRB_CYCLE ? 'C' : 'c'); + break; + case TRB_DATA: +- ret = snprintf(str, size, +- "type '%s' Buffer %08x%08x length %ld TD size %ld " +- "intr %ld flags %c:%c:%c:%c:%c:%c:%c", +- cdnsp_trb_type_string(type), +- field1, field0, TRB_LEN(field2), +- GET_TD_SIZE(field2), +- GET_INTR_TARGET(field2), +- field3 & TRB_IDT ? 'D' : 'i', +- field3 & TRB_IOC ? 'I' : 'i', +- field3 & TRB_CHAIN ? 'C' : 'c', +- field3 & TRB_NO_SNOOP ? 'S' : 's', +- field3 & TRB_ISP ? 'I' : 'i', +- field3 & TRB_ENT ? 'E' : 'e', +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ ret = scnprintf(str, size, ++ "type '%s' Buffer %08x%08x length %ld TD size %ld " ++ "intr %ld flags %c:%c:%c:%c:%c:%c:%c", ++ cdnsp_trb_type_string(type), ++ field1, field0, TRB_LEN(field2), ++ GET_TD_SIZE(field2), ++ GET_INTR_TARGET(field2), ++ field3 & TRB_IDT ? 'D' : 'i', ++ field3 & TRB_IOC ? 'I' : 'i', ++ field3 & TRB_CHAIN ? 'C' : 'c', ++ field3 & TRB_NO_SNOOP ? 'S' : 's', ++ field3 & TRB_ISP ? 'I' : 'i', ++ field3 & TRB_ENT ? 'E' : 'e', ++ field3 & TRB_CYCLE ? 'C' : 'c'); + break; + case TRB_STATUS: +- ret = snprintf(str, size, +- "Buffer %08x%08x length %ld TD size %ld intr" +- "%ld type '%s' flags %c:%c:%c:%c", +- field1, field0, TRB_LEN(field2), +- GET_TD_SIZE(field2), +- GET_INTR_TARGET(field2), +- cdnsp_trb_type_string(type), +- field3 & TRB_IOC ? 'I' : 'i', +- field3 & TRB_CHAIN ? 'C' : 'c', +- field3 & TRB_ENT ? 'E' : 'e', +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ ret = scnprintf(str, size, ++ "Buffer %08x%08x length %ld TD size %ld intr" ++ "%ld type '%s' flags %c:%c:%c:%c", ++ field1, field0, TRB_LEN(field2), ++ GET_TD_SIZE(field2), ++ GET_INTR_TARGET(field2), ++ cdnsp_trb_type_string(type), ++ field3 & TRB_IOC ? 'I' : 'i', ++ field3 & TRB_CHAIN ? 'C' : 'c', ++ field3 & TRB_ENT ? 'E' : 'e', ++ field3 & TRB_CYCLE ? 'C' : 'c'); + break; + case TRB_NORMAL: + case TRB_ISOC: + case TRB_EVENT_DATA: + case TRB_TR_NOOP: +- ret = snprintf(str, size, +- "type '%s' Buffer %08x%08x length %ld " +- "TD size %ld intr %ld " +- "flags %c:%c:%c:%c:%c:%c:%c:%c:%c", +- cdnsp_trb_type_string(type), +- field1, field0, TRB_LEN(field2), +- GET_TD_SIZE(field2), +- GET_INTR_TARGET(field2), +- field3 & TRB_BEI ? 'B' : 'b', +- field3 & TRB_IDT ? 'T' : 't', +- field3 & TRB_IOC ? 'I' : 'i', +- field3 & TRB_CHAIN ? 'C' : 'c', +- field3 & TRB_NO_SNOOP ? 'S' : 's', +- field3 & TRB_ISP ? 'I' : 'i', +- field3 & TRB_ENT ? 'E' : 'e', +- field3 & TRB_CYCLE ? 'C' : 'c', +- !(field3 & TRB_EVENT_INVALIDATE) ? 'V' : 'v'); ++ ret = scnprintf(str, size, ++ "type '%s' Buffer %08x%08x length %ld " ++ "TD size %ld intr %ld " ++ "flags %c:%c:%c:%c:%c:%c:%c:%c:%c", ++ cdnsp_trb_type_string(type), ++ field1, field0, TRB_LEN(field2), ++ GET_TD_SIZE(field2), ++ GET_INTR_TARGET(field2), ++ field3 & TRB_BEI ? 'B' : 'b', ++ field3 & TRB_IDT ? 'T' : 't', ++ field3 & TRB_IOC ? 'I' : 'i', ++ field3 & TRB_CHAIN ? 'C' : 'c', ++ field3 & TRB_NO_SNOOP ? 'S' : 's', ++ field3 & TRB_ISP ? 'I' : 'i', ++ field3 & TRB_ENT ? 'E' : 'e', ++ field3 & TRB_CYCLE ? 'C' : 'c', ++ !(field3 & TRB_EVENT_INVALIDATE) ? 'V' : 'v'); + break; + case TRB_CMD_NOOP: + case TRB_ENABLE_SLOT: +- ret = snprintf(str, size, "%s: flags %c", +- cdnsp_trb_type_string(type), +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ ret = scnprintf(str, size, "%s: flags %c", ++ cdnsp_trb_type_string(type), ++ field3 & TRB_CYCLE ? 'C' : 'c'); + break; + case TRB_DISABLE_SLOT: +- ret = snprintf(str, size, "%s: slot %ld flags %c", +- cdnsp_trb_type_string(type), +- TRB_TO_SLOT_ID(field3), +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ ret = scnprintf(str, size, "%s: slot %ld flags %c", ++ cdnsp_trb_type_string(type), ++ TRB_TO_SLOT_ID(field3), ++ field3 & TRB_CYCLE ? 'C' : 'c'); + break; + case TRB_ADDR_DEV: +- ret = snprintf(str, size, +- "%s: ctx %08x%08x slot %ld flags %c:%c", +- cdnsp_trb_type_string(type), field1, field0, +- TRB_TO_SLOT_ID(field3), +- field3 & TRB_BSR ? 'B' : 'b', +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ ret = scnprintf(str, size, ++ "%s: ctx %08x%08x slot %ld flags %c:%c", ++ cdnsp_trb_type_string(type), field1, field0, ++ TRB_TO_SLOT_ID(field3), ++ field3 & TRB_BSR ? 'B' : 'b', ++ field3 & TRB_CYCLE ? 'C' : 'c'); + break; + case TRB_CONFIG_EP: +- ret = snprintf(str, size, +- "%s: ctx %08x%08x slot %ld flags %c:%c", +- cdnsp_trb_type_string(type), field1, field0, +- TRB_TO_SLOT_ID(field3), +- field3 & TRB_DC ? 'D' : 'd', +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ ret = scnprintf(str, size, ++ "%s: ctx %08x%08x slot %ld flags %c:%c", ++ cdnsp_trb_type_string(type), field1, field0, ++ TRB_TO_SLOT_ID(field3), ++ field3 & TRB_DC ? 'D' : 'd', ++ field3 & TRB_CYCLE ? 'C' : 'c'); + break; + case TRB_EVAL_CONTEXT: +- ret = snprintf(str, size, +- "%s: ctx %08x%08x slot %ld flags %c", +- cdnsp_trb_type_string(type), field1, field0, +- TRB_TO_SLOT_ID(field3), +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ ret = scnprintf(str, size, ++ "%s: ctx %08x%08x slot %ld flags %c", ++ cdnsp_trb_type_string(type), field1, field0, ++ TRB_TO_SLOT_ID(field3), ++ field3 & TRB_CYCLE ? 'C' : 'c'); + break; + case TRB_RESET_EP: + case TRB_HALT_ENDPOINT: +- ret = snprintf(str, size, +- "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c", +- cdnsp_trb_type_string(type), +- ep_num, ep_id % 2 ? "out" : "in", +- TRB_TO_EP_INDEX(field3), field1, field0, +- TRB_TO_SLOT_ID(field3), +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ ret = scnprintf(str, size, ++ "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c", ++ cdnsp_trb_type_string(type), ++ ep_num, ep_id % 2 ? "out" : "in", ++ TRB_TO_EP_INDEX(field3), field1, field0, ++ TRB_TO_SLOT_ID(field3), ++ field3 & TRB_CYCLE ? 'C' : 'c'); + break; + case TRB_STOP_RING: +- ret = snprintf(str, size, +- "%s: ep%d%s(%d) slot %ld sp %d flags %c", +- cdnsp_trb_type_string(type), +- ep_num, ep_id % 2 ? "out" : "in", +- TRB_TO_EP_INDEX(field3), +- TRB_TO_SLOT_ID(field3), +- TRB_TO_SUSPEND_PORT(field3), +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ ret = scnprintf(str, size, ++ "%s: ep%d%s(%d) slot %ld sp %d flags %c", ++ cdnsp_trb_type_string(type), ++ ep_num, ep_id % 2 ? "out" : "in", ++ TRB_TO_EP_INDEX(field3), ++ TRB_TO_SLOT_ID(field3), ++ TRB_TO_SUSPEND_PORT(field3), ++ field3 & TRB_CYCLE ? 'C' : 'c'); + break; + case TRB_SET_DEQ: +- ret = snprintf(str, size, +- "%s: ep%d%s(%d) deq %08x%08x stream %ld slot %ld flags %c", +- cdnsp_trb_type_string(type), +- ep_num, ep_id % 2 ? "out" : "in", +- TRB_TO_EP_INDEX(field3), field1, field0, +- TRB_TO_STREAM_ID(field2), +- TRB_TO_SLOT_ID(field3), +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ ret = scnprintf(str, size, ++ "%s: ep%d%s(%d) deq %08x%08x stream %ld slot %ld flags %c", ++ cdnsp_trb_type_string(type), ++ ep_num, ep_id % 2 ? "out" : "in", ++ TRB_TO_EP_INDEX(field3), field1, field0, ++ TRB_TO_STREAM_ID(field2), ++ TRB_TO_SLOT_ID(field3), ++ field3 & TRB_CYCLE ? 'C' : 'c'); + break; + case TRB_RESET_DEV: +- ret = snprintf(str, size, "%s: slot %ld flags %c", +- cdnsp_trb_type_string(type), +- TRB_TO_SLOT_ID(field3), +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ ret = scnprintf(str, size, "%s: slot %ld flags %c", ++ cdnsp_trb_type_string(type), ++ TRB_TO_SLOT_ID(field3), ++ field3 & TRB_CYCLE ? 'C' : 'c'); + break; + case TRB_ENDPOINT_NRDY: + temp = TRB_TO_HOST_STREAM(field2); + +- ret = snprintf(str, size, +- "%s: ep%d%s(%d) H_SID %x%s%s D_SID %lx flags %c:%c", +- cdnsp_trb_type_string(type), +- ep_num, ep_id % 2 ? "out" : "in", +- TRB_TO_EP_INDEX(field3), temp, +- temp == STREAM_PRIME_ACK ? "(PRIME)" : "", +- temp == STREAM_REJECTED ? "(REJECTED)" : "", +- TRB_TO_DEV_STREAM(field0), +- field3 & TRB_STAT ? 'S' : 's', +- field3 & TRB_CYCLE ? 'C' : 'c'); ++ ret = scnprintf(str, size, ++ "%s: ep%d%s(%d) H_SID %x%s%s D_SID %lx flags %c:%c", ++ cdnsp_trb_type_string(type), ++ ep_num, ep_id % 2 ? "out" : "in", ++ TRB_TO_EP_INDEX(field3), temp, ++ temp == STREAM_PRIME_ACK ? "(PRIME)" : "", ++ temp == STREAM_REJECTED ? "(REJECTED)" : "", ++ TRB_TO_DEV_STREAM(field0), ++ field3 & TRB_STAT ? 'S' : 's', ++ field3 & TRB_CYCLE ? 'C' : 'c'); + break; + default: +- ret = snprintf(str, size, +- "type '%s' -> raw %08x %08x %08x %08x", +- cdnsp_trb_type_string(type), +- field0, field1, field2, field3); ++ ret = scnprintf(str, size, ++ "type '%s' -> raw %08x %08x %08x %08x", ++ cdnsp_trb_type_string(type), ++ field0, field1, field2, field3); + } + +- if (ret >= size) +- pr_info("CDNSP: buffer overflowed.\n"); ++ if (ret == size - 1) ++ pr_info("CDNSP: buffer may be truncated.\n"); + + return str; + } +@@ -465,32 +465,32 @@ static inline const char *cdnsp_decode_portsc(char *str, size_t size, + { + int ret; + +- ret = snprintf(str, size, "%s %s %s Link:%s PortSpeed:%d ", +- portsc & PORT_POWER ? "Powered" : "Powered-off", +- portsc & PORT_CONNECT ? "Connected" : "Not-connected", +- portsc & PORT_PED ? "Enabled" : "Disabled", +- cdnsp_portsc_link_state_string(portsc), +- DEV_PORT_SPEED(portsc)); ++ ret = scnprintf(str, size, "%s %s %s Link:%s PortSpeed:%d ", ++ portsc & PORT_POWER ? "Powered" : "Powered-off", ++ portsc & PORT_CONNECT ? "Connected" : "Not-connected", ++ portsc & PORT_PED ? "Enabled" : "Disabled", ++ cdnsp_portsc_link_state_string(portsc), ++ DEV_PORT_SPEED(portsc)); + + if (portsc & PORT_RESET) +- ret += snprintf(str + ret, size - ret, "In-Reset "); ++ ret += scnprintf(str + ret, size - ret, "In-Reset "); + +- ret += snprintf(str + ret, size - ret, "Change: "); ++ ret += scnprintf(str + ret, size - ret, "Change: "); + if (portsc & PORT_CSC) +- ret += snprintf(str + ret, size - ret, "CSC "); ++ ret += scnprintf(str + ret, size - ret, "CSC "); + if (portsc & PORT_WRC) +- ret += snprintf(str + ret, size - ret, "WRC "); ++ ret += scnprintf(str + ret, size - ret, "WRC "); + if (portsc & PORT_RC) +- ret += snprintf(str + ret, size - ret, "PRC "); ++ ret += scnprintf(str + ret, size - ret, "PRC "); + if (portsc & PORT_PLC) +- ret += snprintf(str + ret, size - ret, "PLC "); ++ ret += scnprintf(str + ret, size - ret, "PLC "); + if (portsc & PORT_CEC) +- ret += snprintf(str + ret, size - ret, "CEC "); +- ret += snprintf(str + ret, size - ret, "Wake: "); ++ ret += scnprintf(str + ret, size - ret, "CEC "); ++ ret += scnprintf(str + ret, size - ret, "Wake: "); + if (portsc & PORT_WKCONN_E) +- ret += snprintf(str + ret, size - ret, "WCE "); ++ ret += scnprintf(str + ret, size - ret, "WCE "); + if (portsc & PORT_WKDISC_E) +- ret += snprintf(str + ret, size - ret, "WDE "); ++ ret += scnprintf(str + ret, size - ret, "WDE "); + + return str; + } +@@ -562,20 +562,20 @@ static inline const char *cdnsp_decode_ep_context(char *str, size_t size, + + avg = EP_AVG_TRB_LENGTH(tx_info); + +- ret = snprintf(str, size, "State %s mult %d max P. Streams %d %s", +- cdnsp_ep_state_string(ep_state), mult, +- max_pstr, lsa ? "LSA " : ""); ++ ret = scnprintf(str, size, "State %s mult %d max P. Streams %d %s", ++ cdnsp_ep_state_string(ep_state), mult, ++ max_pstr, lsa ? "LSA " : ""); + +- ret += snprintf(str + ret, size - ret, +- "interval %d us max ESIT payload %d CErr %d ", +- (1 << interval) * 125, esit, cerr); ++ ret += scnprintf(str + ret, size - ret, ++ "interval %d us max ESIT payload %d CErr %d ", ++ (1 << interval) * 125, esit, cerr); + +- ret += snprintf(str + ret, size - ret, +- "Type %s %sburst %d maxp %d deq %016llx ", +- cdnsp_ep_type_string(ep_type), hid ? "HID" : "", +- burst, maxp, deq); ++ ret += scnprintf(str + ret, size - ret, ++ "Type %s %sburst %d maxp %d deq %016llx ", ++ cdnsp_ep_type_string(ep_type), hid ? "HID" : "", ++ burst, maxp, deq); + +- ret += snprintf(str + ret, size - ret, "avg trb len %d", avg); ++ ret += scnprintf(str + ret, size - ret, "avg trb len %d", avg); + + return str; + } +-- +2.39.5 + diff --git a/queue-6.6/usb-dwc3-abort-suspend-on-soft-disconnect-failure.patch b/queue-6.6/usb-dwc3-abort-suspend-on-soft-disconnect-failure.patch new file mode 100644 index 0000000000..b6fa82eb2d --- /dev/null +++ b/queue-6.6/usb-dwc3-abort-suspend-on-soft-disconnect-failure.patch @@ -0,0 +1,108 @@ +From dc6aff1b5b49b3279319c637cfcb323533bae831 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 28 May 2025 18:03:11 +0800 +Subject: usb: dwc3: Abort suspend on soft disconnect failure + +From: Kuen-Han Tsai + +[ Upstream commit 630a1dec3b0eba2a695b9063f1c205d585cbfec9 ] + +When dwc3_gadget_soft_disconnect() fails, dwc3_suspend_common() keeps +going with the suspend, resulting in a period where the power domain is +off, but the gadget driver remains connected. Within this time frame, +invoking vbus_event_work() will cause an error as it attempts to access +DWC3 registers for endpoint disabling after the power domain has been +completely shut down. + +Abort the suspend sequence when dwc3_gadget_suspend() cannot halt the +controller and proceeds with a soft connect. + +Fixes: 9f8a67b65a49 ("usb: dwc3: gadget: fix gadget suspend/resume") +Cc: stable +Acked-by: Thinh Nguyen +Signed-off-by: Kuen-Han Tsai +Link: https://lore.kernel.org/r/20250528100315.2162699-1-khtsai@google.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/usb/dwc3/core.c | 9 +++++++-- + drivers/usb/dwc3/gadget.c | 22 +++++++++------------- + 2 files changed, 16 insertions(+), 15 deletions(-) + +diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c +index 30404461ef7de..b7eaad099309c 100644 +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -2128,6 +2128,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc) + static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) + { + u32 reg; ++ int ret; + + if (!pm_runtime_suspended(dwc->dev) && !PMSG_IS_AUTO(msg)) { + dwc->susphy_state = (dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)) & +@@ -2146,7 +2147,9 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) + case DWC3_GCTL_PRTCAP_DEVICE: + if (pm_runtime_suspended(dwc->dev)) + break; +- dwc3_gadget_suspend(dwc); ++ ret = dwc3_gadget_suspend(dwc); ++ if (ret) ++ return ret; + synchronize_irq(dwc->irq_gadget); + dwc3_core_exit(dwc); + break; +@@ -2177,7 +2180,9 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) + break; + + if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) { +- dwc3_gadget_suspend(dwc); ++ ret = dwc3_gadget_suspend(dwc); ++ if (ret) ++ return ret; + synchronize_irq(dwc->irq_gadget); + } + +diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c +index f51d743bb3ecc..a17af4ab20a32 100644 +--- a/drivers/usb/dwc3/gadget.c ++++ b/drivers/usb/dwc3/gadget.c +@@ -4802,26 +4802,22 @@ int dwc3_gadget_suspend(struct dwc3 *dwc) + int ret; + + ret = dwc3_gadget_soft_disconnect(dwc); +- if (ret) +- goto err; +- +- spin_lock_irqsave(&dwc->lock, flags); +- if (dwc->gadget_driver) +- dwc3_disconnect_gadget(dwc); +- spin_unlock_irqrestore(&dwc->lock, flags); +- +- return 0; +- +-err: + /* + * Attempt to reset the controller's state. Likely no + * communication can be established until the host + * performs a port reset. + */ +- if (dwc->softconnect) ++ if (ret && dwc->softconnect) { + dwc3_gadget_soft_connect(dwc); ++ return -EAGAIN; ++ } + +- return ret; ++ spin_lock_irqsave(&dwc->lock, flags); ++ if (dwc->gadget_driver) ++ dwc3_disconnect_gadget(dwc); ++ spin_unlock_irqrestore(&dwc->lock, flags); ++ ++ return 0; + } + + int dwc3_gadget_resume(struct dwc3 *dwc) +-- +2.39.5 + diff --git a/queue-6.6/vt-add-missing-notification-when-switching-back-to-t.patch b/queue-6.6/vt-add-missing-notification-when-switching-back-to-t.patch new file mode 100644 index 0000000000..f20bf0ed5a --- /dev/null +++ b/queue-6.6/vt-add-missing-notification-when-switching-back-to-t.patch @@ -0,0 +1,35 @@ +From 58e7dd5627d05ad8426c17d1713600fe883d8505 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 10 Jun 2025 21:41:44 -0400 +Subject: vt: add missing notification when switching back to text mode + +From: Nicolas Pitre + +[ Upstream commit ff78538e07fa284ce08cbbcb0730daa91ed16722 ] + +Programs using poll() on /dev/vcsa to be notified when VT changes occur +were missing one case: the switch from gfx to text mode. + +Signed-off-by: Nicolas Pitre +Link: https://lore.kernel.org/r/9o5ro928-0pp4-05rq-70p4-ro385n21n723@onlyvoer.pbz +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/tty/vt/vt.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c +index c5ec7306aa713..60c878ea95f92 100644 +--- a/drivers/tty/vt/vt.c ++++ b/drivers/tty/vt/vt.c +@@ -4392,6 +4392,7 @@ void do_unblank_screen(int leaving_gfx) + set_palette(vc); + set_cursor(vc); + vt_event_post(VT_EVENT_UNBLANK, vc->vc_num, vc->vc_num); ++ notify_update(vc); + } + EXPORT_SYMBOL(do_unblank_screen); + +-- +2.39.5 + diff --git a/queue-6.6/wifi-zd1211rw-fix-potential-null-pointer-dereference.patch b/queue-6.6/wifi-zd1211rw-fix-potential-null-pointer-dereference.patch new file mode 100644 index 0000000000..5f0239dde1 --- /dev/null +++ b/queue-6.6/wifi-zd1211rw-fix-potential-null-pointer-dereference.patch @@ -0,0 +1,68 @@ +From 7c285ed4c567e14b06cfbb5d92d770c62655c292 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 26 Jun 2025 14:46:19 +0300 +Subject: wifi: zd1211rw: Fix potential NULL pointer dereference in + zd_mac_tx_to_dev() + +From: Daniil Dulov + +[ Upstream commit 74b1ec9f5d627d2bdd5e5b6f3f81c23317657023 ] + +There is a potential NULL pointer dereference in zd_mac_tx_to_dev(). For +example, the following is possible: + + T0 T1 +zd_mac_tx_to_dev() + /* len == skb_queue_len(q) */ + while (len > ZD_MAC_MAX_ACK_WAITERS) { + + filter_ack() + spin_lock_irqsave(&q->lock, flags); + /* position == skb_queue_len(q) */ + for (i=1; itype == NL80211_IFTYPE_AP) + skb = __skb_dequeue(q); + spin_unlock_irqrestore(&q->lock, flags); + + skb_dequeue() -> NULL + +Since there is a small gap between checking skb queue length and skb being +unconditionally dequeued in zd_mac_tx_to_dev(), skb_dequeue() can return NULL. +Then the pointer is passed to zd_mac_tx_status() where it is dereferenced. + +In order to avoid potential NULL pointer dereference due to situations like +above, check if skb is not NULL before passing it to zd_mac_tx_status(). + +Found by Linux Verification Center (linuxtesting.org) with SVACE. + +Fixes: 459c51ad6e1f ("zd1211rw: port to mac80211") +Signed-off-by: Daniil Dulov +Link: https://patch.msgid.link/20250626114619.172631-1-d.dulov@aladdin.ru +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/zydas/zd1211rw/zd_mac.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c +index 5d534e15a844f..278875c02f41f 100644 +--- a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c ++++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c +@@ -583,7 +583,11 @@ void zd_mac_tx_to_dev(struct sk_buff *skb, int error) + + skb_queue_tail(q, skb); + while (skb_queue_len(q) > ZD_MAC_MAX_ACK_WAITERS) { +- zd_mac_tx_status(hw, skb_dequeue(q), ++ skb = skb_dequeue(q); ++ if (!skb) ++ break; ++ ++ zd_mac_tx_status(hw, skb, + mac->ack_pending ? mac->ack_signal : 0, + NULL); + mac->ack_pending = 0; +-- +2.39.5 +