From: Greg Kroah-Hartman Date: Wed, 8 Apr 2026 13:07:33 +0000 (+0200) Subject: 6.12-stable patches X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4b37ecf9a5e003b37781dfe1dbe900085aefee27;p=thirdparty%2Fkernel%2Fstable-queue.git 6.12-stable patches added patches: bpf-add-third-round-of-bounds-deduction.patch bpf-fix-u32-s32-bounds-when-ranges-cross-min-max-boundary.patch bpf-improve-bounds-when-s64-crosses-sign-boundary.patch drm-amd-amdgpu-decouple-aspm-with-pcie-dpm.patch drm-amd-amdgpu-disable-aspm-in-some-situations.patch drm-amd-disable-aspm-on-si.patch drm-amd-display-adjust-dce-8-10-clock-don-t-overclock-by-15.patch drm-amd-display-correct-logic-check-error-for-fastboot.patch drm-amd-display-disable-fastboot-on-dce-6-too.patch drm-amd-display-disable-scaling-on-dce6-for-now.patch drm-amd-display-fix-dce-6.0-and-6.4-pll-programming.patch drm-amd-display-keep-pll0-running-on-dce-6.0-and-6.4.patch drm-amd-display-reject-modes-with-too-high-pixel-clock-on-dce6-10.patch drm-amd-pm-disable-od_fan_curve-if-temp-or-pwm-range-invalid-for-smu-v13.patch ext4-publish-jinode-after-initialization.patch mm-huge_memory-fix-folio-isn-t-locked-in-softleaf_to_folio.patch mm-memory-fix-pmd-pud-checks-in-follow_pfnmap_start.patch mm-replace-read_once-with-standard-page-table-accessors.patch mptcp-fix-lock-class-name-family-in-pm_nl_create_listen_socket.patch net-correctly-handle-tunneled-traffic-on-ipv6_csum-gso-fallback.patch net-mana-fix-use-after-free-in-add_adev-error-path.patch s390-cpum_sf-cap-sampling-rate-to-prevent-lsctl-exception.patch s390-perf_cpum_sf-convert-to-use-try_cmpxchg128.patch scsi-target-tcm_loop-drain-commands-in-target_reset-handler.patch selftests-bpf-test-cross-sign-64bits-range-refinement.patch selftests-bpf-test-invariants-on-jslt-crossing-sign.patch selftests-bpf-test-refining-u32-s32-bounds-when-ranges-cross-min-max-boundary.patch spi-cadence-qspi-fix-exec_mem_op-error-handling.patch wifi-virt_wifi-remove-set_netdev_dev-to-avoid-use-after-free.patch x86-cpu-amd-add-additional-fixed-rdseed-microcode-revisions.patch x86-fred-fix-early-boot-failures-on-sev-es-snp-guests.patch --- diff --git a/queue-6.12/bpf-add-third-round-of-bounds-deduction.patch b/queue-6.12/bpf-add-third-round-of-bounds-deduction.patch new file mode 100644 index 0000000000..04a3b4d94e --- /dev/null +++ b/queue-6.12/bpf-add-third-round-of-bounds-deduction.patch @@ -0,0 +1,93 @@ +From paul.chaignon@gmail.com Sat Apr 4 10:13:19 2026 +From: Paul Chaignon +Date: Sat, 4 Apr 2026 10:13:16 +0200 +Subject: bpf: Add third round of bounds deduction +To: stable@vger.kernel.org +Cc: Greg Kroah-Hartman , Eduard Zingerman , Shung-Hsi Yu , Alexei Starovoitov +Message-ID: +Content-Disposition: inline + +From: Paul Chaignon + +[ Upstream commit 5dbb19b16ac498b0b7f3a8a85f9d25d6d8af397d ] + +Commit d7f008738171 ("bpf: try harder to deduce register bounds from +different numeric domains") added a second call to __reg_deduce_bounds +in reg_bounds_sync because a single call wasn't enough to converge to a +fixed point in terms of register bounds. + +With patch "bpf: Improve bounds when s64 crosses sign boundary" from +this series, Eduard noticed that calling __reg_deduce_bounds twice isn't +enough anymore to converge. The first selftest added in "selftests/bpf: +Test cross-sign 64bits range refinement" highlights the need for a third +call to __reg_deduce_bounds. After instruction 7, reg_bounds_sync +performs the following bounds deduction: + + reg_bounds_sync entry: scalar(smin=-655,smax=0xeffffeee,smin32=-783,smax32=-146) + __update_reg_bounds: scalar(smin=-655,smax=0xeffffeee,smin32=-783,smax32=-146) + __reg_deduce_bounds: + __reg32_deduce_bounds: scalar(smin=-655,smax=0xeffffeee,smin32=-783,smax32=-146,umin32=0xfffffcf1,umax32=0xffffff6e) + __reg64_deduce_bounds: scalar(smin=-655,smax=0xeffffeee,smin32=-783,smax32=-146,umin32=0xfffffcf1,umax32=0xffffff6e) + __reg_deduce_mixed_bounds: scalar(smin=-655,smax=0xeffffeee,umin=umin32=0xfffffcf1,umax=0xffffffffffffff6e,smin32=-783,smax32=-146,umax32=0xffffff6e) + __reg_deduce_bounds: + __reg32_deduce_bounds: scalar(smin=-655,smax=0xeffffeee,umin=umin32=0xfffffcf1,umax=0xffffffffffffff6e,smin32=-783,smax32=-146,umax32=0xffffff6e) + __reg64_deduce_bounds: scalar(smin=-655,smax=smax32=-146,umin=0xfffffffffffffd71,umax=0xffffffffffffff6e,smin32=-783,umin32=0xfffffcf1,umax32=0xffffff6e) + __reg_deduce_mixed_bounds: scalar(smin=-655,smax=smax32=-146,umin=0xfffffffffffffd71,umax=0xffffffffffffff6e,smin32=-783,umin32=0xfffffcf1,umax32=0xffffff6e) + __reg_bound_offset: scalar(smin=-655,smax=smax32=-146,umin=0xfffffffffffffd71,umax=0xffffffffffffff6e,smin32=-783,umin32=0xfffffcf1,umax32=0xffffff6e,var_off=(0xfffffffffffffc00; 0x3ff)) + __update_reg_bounds: scalar(smin=-655,smax=smax32=-146,umin=0xfffffffffffffd71,umax=0xffffffffffffff6e,smin32=-783,umin32=0xfffffcf1,umax32=0xffffff6e,var_off=(0xfffffffffffffc00; 0x3ff)) + +In particular, notice how: +1. In the first call to __reg_deduce_bounds, __reg32_deduce_bounds + learns new u32 bounds. +2. __reg64_deduce_bounds is unable to improve bounds at this point. +3. __reg_deduce_mixed_bounds derives new u64 bounds from the u32 bounds. +4. In the second call to __reg_deduce_bounds, __reg64_deduce_bounds + improves the smax and umin bounds thanks to patch "bpf: Improve + bounds when s64 crosses sign boundary" from this series. +5. Subsequent functions are unable to improve the ranges further (only + tnums). Yet, a better smin32 bound could be learned from the smin + bound. + +__reg32_deduce_bounds is able to improve smin32 from smin, but for that +we need a third call to __reg_deduce_bounds. + +As discussed in [1], there may be a better way to organize the deduction +rules to learn the same information with less calls to the same +functions. Such an optimization requires further analysis and is +orthogonal to the present patchset. + +Link: https://lore.kernel.org/bpf/aIKtSK9LjQXB8FLY@mail.gmail.com/ [1] +Acked-by: Eduard Zingerman +Co-developed-by: Eduard Zingerman +Signed-off-by: Eduard Zingerman +Signed-off-by: Paul Chaignon +Link: https://lore.kernel.org/r/79619d3b42e5525e0e174ed534b75879a5ba15de.1753695655.git.paul.chaignon@gmail.com +Signed-off-by: Alexei Starovoitov +Signed-off-by: Paul Chaignon +Signed-off-by: Greg Kroah-Hartman +--- + kernel/bpf/verifier.c | 1 + + tools/testing/selftests/bpf/progs/verifier_bounds.c | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -2292,6 +2292,7 @@ static void reg_bounds_sync(struct bpf_r + /* We might have learned something about the sign bit. */ + __reg_deduce_bounds(reg); + __reg_deduce_bounds(reg); ++ __reg_deduce_bounds(reg); + /* We might have learned some bits from the bounds. */ + __reg_bound_offset(reg); + /* Intersecting with the old var_off might have improved our bounds +--- a/tools/testing/selftests/bpf/progs/verifier_bounds.c ++++ b/tools/testing/selftests/bpf/progs/verifier_bounds.c +@@ -1223,7 +1223,7 @@ l0_%=: r0 = 0; \ + SEC("socket") + __description("bounds deduction cross sign boundary, negative overlap") + __success __log_level(2) __flag(BPF_F_TEST_REG_INVARIANTS) +-__msg("7: (1f) r0 -= r6 {{.*}} R0=scalar(smin=-655,smax=smax32=-146,umin=0xfffffffffffffd71,umax=0xffffffffffffff6e,smin32=-783,umin32=0xfffffcf1,umax32=0xffffff6e,var_off=(0xfffffffffffffc00; 0x3ff))") ++__msg("7: (1f) r0 -= r6 {{.*}} R0=scalar(smin=smin32=-655,smax=smax32=-146,umin=0xfffffffffffffd71,umax=0xffffffffffffff6e,umin32=0xfffffd71,umax32=0xffffff6e,var_off=(0xfffffffffffffc00; 0x3ff))") + __retval(0) + __naked void bounds_deduct_negative_overlap(void) + { diff --git a/queue-6.12/bpf-fix-u32-s32-bounds-when-ranges-cross-min-max-boundary.patch b/queue-6.12/bpf-fix-u32-s32-bounds-when-ranges-cross-min-max-boundary.patch new file mode 100644 index 0000000000..6bf9456ee0 --- /dev/null +++ b/queue-6.12/bpf-fix-u32-s32-bounds-when-ranges-cross-min-max-boundary.patch @@ -0,0 +1,196 @@ +From paul.chaignon@gmail.com Sat Apr 4 10:14:24 2026 +From: Paul Chaignon +Date: Sat, 4 Apr 2026 10:14:20 +0200 +Subject: bpf: Fix u32/s32 bounds when ranges cross min/max boundary +To: stable@vger.kernel.org +Cc: Greg Kroah-Hartman , Eduard Zingerman , Shung-Hsi Yu , Alexei Starovoitov , Emil Tsalapatis , Andrea Righi +Message-ID: +Content-Disposition: inline + +From: Eduard Zingerman + +[ Upstream commit fbc7aef517d8765e4c425d2792409bb9bf2e1f13 ] + +Same as in __reg64_deduce_bounds(), refine s32/u32 ranges +in __reg32_deduce_bounds() in the following situations: + +- s32 range crosses U32_MAX/0 boundary, positive part of the s32 range + overlaps with u32 range: + + 0 U32_MAX + | [xxxxxxxxxxxxxx u32 range xxxxxxxxxxxxxx] | + |----------------------------|----------------------------| + |xxxxx s32 range xxxxxxxxx] [xxxxxxx| + 0 S32_MAX S32_MIN -1 + +- s32 range crosses U32_MAX/0 boundary, negative part of the s32 range + overlaps with u32 range: + + 0 U32_MAX + | [xxxxxxxxxxxxxx u32 range xxxxxxxxxxxxxx] | + |----------------------------|----------------------------| + |xxxxxxxxx] [xxxxxxxxxxxx s32 range | + 0 S32_MAX S32_MIN -1 + +- No refinement if ranges overlap in two intervals. + +This helps for e.g. consider the following program: + + call %[bpf_get_prandom_u32]; + w0 &= 0xffffffff; + if w0 < 0x3 goto 1f; // on fall-through u32 range [3..U32_MAX] + if w0 s> 0x1 goto 1f; // on fall-through s32 range [S32_MIN..1] + if w0 s< 0x0 goto 1f; // range can be narrowed to [S32_MIN..-1] + r10 = 0; +1: ...; + +The reg_bounds.c selftest is updated to incorporate identical logic, +refinement based on non-overflowing range halves: + + ((x ∩ [0, smax]) ∩ (y ∩ [0, smax])) ∪ + ((x ∩ [smin,-1]) ∩ (y ∩ [smin,-1])) + +Reported-by: Andrea Righi +Reported-by: Emil Tsalapatis +Closes: https://lore.kernel.org/bpf/aakqucg4vcujVwif@gpd4/T/ +Reviewed-by: Emil Tsalapatis +Acked-by: Shung-Hsi Yu +Signed-off-by: Eduard Zingerman +Link: https://lore.kernel.org/r/20260306-bpf-32-bit-range-overflow-v3-1-f7f67e060a6b@gmail.com +Signed-off-by: Alexei Starovoitov +Signed-off-by: Paul Chaignon +Signed-off-by: Greg Kroah-Hartman +--- + kernel/bpf/verifier.c | 24 +++++++ + tools/testing/selftests/bpf/prog_tests/reg_bounds.c | 62 ++++++++++++++++++-- + 2 files changed, 82 insertions(+), 4 deletions(-) + +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -2046,6 +2046,30 @@ static void __reg32_deduce_bounds(struct + if ((u32)reg->s32_min_value <= (u32)reg->s32_max_value) { + reg->u32_min_value = max_t(u32, reg->s32_min_value, reg->u32_min_value); + reg->u32_max_value = min_t(u32, reg->s32_max_value, reg->u32_max_value); ++ } else { ++ if (reg->u32_max_value < (u32)reg->s32_min_value) { ++ /* See __reg64_deduce_bounds() for detailed explanation. ++ * Refine ranges in the following situation: ++ * ++ * 0 U32_MAX ++ * | [xxxxxxxxxxxxxx u32 range xxxxxxxxxxxxxx] | ++ * |----------------------------|----------------------------| ++ * |xxxxx s32 range xxxxxxxxx] [xxxxxxx| ++ * 0 S32_MAX S32_MIN -1 ++ */ ++ reg->s32_min_value = (s32)reg->u32_min_value; ++ reg->u32_max_value = min_t(u32, reg->u32_max_value, reg->s32_max_value); ++ } else if ((u32)reg->s32_max_value < reg->u32_min_value) { ++ /* ++ * 0 U32_MAX ++ * | [xxxxxxxxxxxxxx u32 range xxxxxxxxxxxxxx] | ++ * |----------------------------|----------------------------| ++ * |xxxxxxxxx] [xxxxxxxxxxxx s32 range | ++ * 0 S32_MAX S32_MIN -1 ++ */ ++ reg->s32_max_value = (s32)reg->u32_max_value; ++ reg->u32_min_value = max_t(u32, reg->u32_min_value, reg->s32_min_value); ++ } + } + } + +--- a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c ++++ b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c +@@ -422,15 +422,69 @@ static bool is_valid_range(enum num_t t, + } + } + +-static struct range range_improve(enum num_t t, struct range old, struct range new) ++static struct range range_intersection(enum num_t t, struct range old, struct range new) + { + return range(t, max_t(t, old.a, new.a), min_t(t, old.b, new.b)); + } + ++/* ++ * Result is precise when 'x' and 'y' overlap or form a continuous range, ++ * result is an over-approximation if 'x' and 'y' do not overlap. ++ */ ++static struct range range_union(enum num_t t, struct range x, struct range y) ++{ ++ if (!is_valid_range(t, x)) ++ return y; ++ if (!is_valid_range(t, y)) ++ return x; ++ return range(t, min_t(t, x.a, y.a), max_t(t, x.b, y.b)); ++} ++ ++/* ++ * This function attempts to improve x range intersecting it with y. ++ * range_cast(... to_t ...) looses precision for ranges that pass to_t ++ * min/max boundaries. To avoid such precision loses this function ++ * splits both x and y into halves corresponding to non-overflowing ++ * sub-ranges: [0, smin] and [smax, -1]. ++ * Final result is computed as follows: ++ * ++ * ((x ∩ [0, smax]) ∩ (y ∩ [0, smax])) ∪ ++ * ((x ∩ [smin,-1]) ∩ (y ∩ [smin,-1])) ++ * ++ * Precision might still be lost if final union is not a continuous range. ++ */ ++static struct range range_refine_in_halves(enum num_t x_t, struct range x, ++ enum num_t y_t, struct range y) ++{ ++ struct range x_pos, x_neg, y_pos, y_neg, r_pos, r_neg; ++ u64 smax, smin, neg_one; ++ ++ if (t_is_32(x_t)) { ++ smax = (u64)(u32)S32_MAX; ++ smin = (u64)(u32)S32_MIN; ++ neg_one = (u64)(u32)(s32)(-1); ++ } else { ++ smax = (u64)S64_MAX; ++ smin = (u64)S64_MIN; ++ neg_one = U64_MAX; ++ } ++ x_pos = range_intersection(x_t, x, range(x_t, 0, smax)); ++ x_neg = range_intersection(x_t, x, range(x_t, smin, neg_one)); ++ y_pos = range_intersection(y_t, y, range(x_t, 0, smax)); ++ y_neg = range_intersection(y_t, y, range(y_t, smin, neg_one)); ++ r_pos = range_intersection(x_t, x_pos, range_cast(y_t, x_t, y_pos)); ++ r_neg = range_intersection(x_t, x_neg, range_cast(y_t, x_t, y_neg)); ++ return range_union(x_t, r_pos, r_neg); ++ ++} ++ + static struct range range_refine(enum num_t x_t, struct range x, enum num_t y_t, struct range y) + { + struct range y_cast; + ++ if (t_is_32(x_t) == t_is_32(y_t)) ++ x = range_refine_in_halves(x_t, x, y_t, y); ++ + y_cast = range_cast(y_t, x_t, y); + + /* If we know that +@@ -444,7 +498,7 @@ static struct range range_refine(enum nu + */ + if (x_t == S64 && y_t == S32 && y_cast.a <= S32_MAX && y_cast.b <= S32_MAX && + (s64)x.a >= S32_MIN && (s64)x.b <= S32_MAX) +- return range_improve(x_t, x, y_cast); ++ return range_intersection(x_t, x, y_cast); + + /* the case when new range knowledge, *y*, is a 32-bit subregister + * range, while previous range knowledge, *x*, is a full register +@@ -462,11 +516,11 @@ static struct range range_refine(enum nu + x_swap = range(x_t, swap_low32(x.a, y_cast.a), swap_low32(x.b, y_cast.b)); + if (!is_valid_range(x_t, x_swap)) + return x; +- return range_improve(x_t, x, x_swap); ++ return range_intersection(x_t, x, x_swap); + } + + /* otherwise, plain range cast and intersection works */ +- return range_improve(x_t, x, y_cast); ++ return range_intersection(x_t, x, y_cast); + } + + /* ======================= diff --git a/queue-6.12/bpf-improve-bounds-when-s64-crosses-sign-boundary.patch b/queue-6.12/bpf-improve-bounds-when-s64-crosses-sign-boundary.patch new file mode 100644 index 0000000000..9f0baa8cae --- /dev/null +++ b/queue-6.12/bpf-improve-bounds-when-s64-crosses-sign-boundary.patch @@ -0,0 +1,116 @@ +From paul.chaignon@gmail.com Sat Apr 4 10:10:56 2026 +From: Paul Chaignon +Date: Sat, 4 Apr 2026 10:10:52 +0200 +Subject: bpf: Improve bounds when s64 crosses sign boundary +To: stable@vger.kernel.org +Cc: Greg Kroah-Hartman , Eduard Zingerman , Shung-Hsi Yu , Alexei Starovoitov +Message-ID: <0f6da4f74ebc491dd651dfcf3ba984bbd3dc566f.1775289842.git.paul.chaignon@gmail.com> +Content-Disposition: inline + +From: Paul Chaignon + +[ Upstream commit 00bf8d0c6c9be0c481fc45a3f7d87c7f8812f229 ] + +__reg64_deduce_bounds currently improves the s64 range using the u64 +range and vice versa, but only if it doesn't cross the sign boundary. + +This patch improves __reg64_deduce_bounds to cover the case where the +s64 range crosses the sign boundary but overlaps with the u64 range on +only one end. In that case, we can improve both ranges. Consider the +following example, with the s64 range crossing the sign boundary: + + 0 U64_MAX + | [xxxxxxxxxxxxxx u64 range xxxxxxxxxxxxxx] | + |----------------------------|----------------------------| + |xxxxx s64 range xxxxxxxxx] [xxxxxxx| + 0 S64_MAX S64_MIN -1 + +The u64 range overlaps only with positive portion of the s64 range. We +can thus derive the following new s64 and u64 ranges. + + 0 U64_MAX + | [xxxxxx u64 range xxxxx] | + |----------------------------|----------------------------| + | [xxxxxx s64 range xxxxx] | + 0 S64_MAX S64_MIN -1 + +The same logic can probably apply to the s32/u32 ranges, but this patch +doesn't implement that change. + +In addition to the selftests, the __reg64_deduce_bounds change was +also tested with Agni, the formal verification tool for the range +analysis [1]. + +Link: https://github.com/bpfverif/agni [1] +Acked-by: Eduard Zingerman +Acked-by: Shung-Hsi Yu +Signed-off-by: Paul Chaignon +Link: https://lore.kernel.org/r/933bd9ce1f36ded5559f92fdc09e5dbc823fa245.1753695655.git.paul.chaignon@gmail.com +Signed-off-by: Alexei Starovoitov +Signed-off-by: Paul Chaignon +Signed-off-by: Greg Kroah-Hartman +--- + kernel/bpf/verifier.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 52 insertions(+) + +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -2129,6 +2129,58 @@ static void __reg64_deduce_bounds(struct + if ((u64)reg->smin_value <= (u64)reg->smax_value) { + reg->umin_value = max_t(u64, reg->smin_value, reg->umin_value); + reg->umax_value = min_t(u64, reg->smax_value, reg->umax_value); ++ } else { ++ /* If the s64 range crosses the sign boundary, then it's split ++ * between the beginning and end of the U64 domain. In that ++ * case, we can derive new bounds if the u64 range overlaps ++ * with only one end of the s64 range. ++ * ++ * In the following example, the u64 range overlaps only with ++ * positive portion of the s64 range. ++ * ++ * 0 U64_MAX ++ * | [xxxxxxxxxxxxxx u64 range xxxxxxxxxxxxxx] | ++ * |----------------------------|----------------------------| ++ * |xxxxx s64 range xxxxxxxxx] [xxxxxxx| ++ * 0 S64_MAX S64_MIN -1 ++ * ++ * We can thus derive the following new s64 and u64 ranges. ++ * ++ * 0 U64_MAX ++ * | [xxxxxx u64 range xxxxx] | ++ * |----------------------------|----------------------------| ++ * | [xxxxxx s64 range xxxxx] | ++ * 0 S64_MAX S64_MIN -1 ++ * ++ * If they overlap in two places, we can't derive anything ++ * because reg_state can't represent two ranges per numeric ++ * domain. ++ * ++ * 0 U64_MAX ++ * | [xxxxxxxxxxxxxxxxx u64 range xxxxxxxxxxxxxxxxx] | ++ * |----------------------------|----------------------------| ++ * |xxxxx s64 range xxxxxxxxx] [xxxxxxxxxx| ++ * 0 S64_MAX S64_MIN -1 ++ * ++ * The first condition below corresponds to the first diagram ++ * above. ++ */ ++ if (reg->umax_value < (u64)reg->smin_value) { ++ reg->smin_value = (s64)reg->umin_value; ++ reg->umax_value = min_t(u64, reg->umax_value, reg->smax_value); ++ } else if ((u64)reg->smax_value < reg->umin_value) { ++ /* This second condition considers the case where the u64 range ++ * overlaps with the negative portion of the s64 range: ++ * ++ * 0 U64_MAX ++ * | [xxxxxxxxxxxxxx u64 range xxxxxxxxxxxxxx] | ++ * |----------------------------|----------------------------| ++ * |xxxxxxxxx] [xxxxxxxxxxxx s64 range | ++ * 0 S64_MAX S64_MIN -1 ++ */ ++ reg->smax_value = (s64)reg->umax_value; ++ reg->umin_value = max_t(u64, reg->umin_value, reg->smin_value); ++ } + } + } + diff --git a/queue-6.12/drm-amd-amdgpu-decouple-aspm-with-pcie-dpm.patch b/queue-6.12/drm-amd-amdgpu-decouple-aspm-with-pcie-dpm.patch new file mode 100644 index 0000000000..255086d6c6 --- /dev/null +++ b/queue-6.12/drm-amd-amdgpu-decouple-aspm-with-pcie-dpm.patch @@ -0,0 +1,35 @@ +From stable+bounces-232620-greg=kroah.com@vger.kernel.org Wed Apr 1 02:41:51 2026 +From: Rosen Penev +Date: Tue, 31 Mar 2026 17:38:59 -0700 +Subject: drm/amd/amdgpu: decouple ASPM with pcie dpm +To: stable@vger.kernel.org +Cc: "Alex Deucher" , "Christian König" , "Xinhui Pan" , "David Airlie" , "Simona Vetter" , "Harry Wentland" , "Leo Li" , "Rodrigo Siqueira" , "Ray Wu" , "Wayne Lin" , "Mario Limonciello" , "Roman Li" , "Eric Yang" , "Tony Cheng" , "Mauro Rossi" , "Timur Kristóf" , "Alex Hung" , amd-gfx@lists.freedesktop.org (open list:RADEON and AMDGPU DRM DRIVERS), dri-devel@lists.freedesktop.org (open list:DRM DRIVERS), linux-kernel@vger.kernel.org (open list) +Message-ID: <20260401003908.3438-2-rosenp@gmail.com> + +From: Kenneth Feng + +[ Upstream commit df0e722fbdbedb6f2b682dc2fad9e0c221e3622d ] + +ASPM doesn't need to be disabled if pcie dpm is disabled. +So ASPM can be independantly enabled. + +Signed-off-by: Kenneth Feng +Reviewed-by: Yang Wang +Signed-off-by: Alex Deucher +Signed-off-by: Rosen Penev +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +@@ -1782,8 +1782,6 @@ bool amdgpu_device_should_use_aspm(struc + } + if (adev->flags & AMD_IS_APU) + return false; +- if (!(adev->pm.pp_feature & PP_PCIE_DPM_MASK)) +- return false; + return pcie_aspm_enabled(adev->pdev); + } + diff --git a/queue-6.12/drm-amd-amdgpu-disable-aspm-in-some-situations.patch b/queue-6.12/drm-amd-amdgpu-disable-aspm-in-some-situations.patch new file mode 100644 index 0000000000..62a779c0f7 --- /dev/null +++ b/queue-6.12/drm-amd-amdgpu-disable-aspm-in-some-situations.patch @@ -0,0 +1,79 @@ +From stable+bounces-232621-greg=kroah.com@vger.kernel.org Wed Apr 1 02:42:32 2026 +From: Rosen Penev +Date: Tue, 31 Mar 2026 17:39:00 -0700 +Subject: drm/amd/amdgpu: disable ASPM in some situations +To: stable@vger.kernel.org +Cc: "Alex Deucher" , "Christian König" , "Xinhui Pan" , "David Airlie" , "Simona Vetter" , "Harry Wentland" , "Leo Li" , "Rodrigo Siqueira" , "Ray Wu" , "Wayne Lin" , "Mario Limonciello" , "Roman Li" , "Eric Yang" , "Tony Cheng" , "Mauro Rossi" , "Timur Kristóf" , "Alex Hung" , amd-gfx@lists.freedesktop.org (open list:RADEON and AMDGPU DRM DRIVERS), dri-devel@lists.freedesktop.org (open list:DRM DRIVERS), linux-kernel@vger.kernel.org (open list) +Message-ID: <20260401003908.3438-3-rosenp@gmail.com> + +From: Kenneth Feng + +[ Upstream commit c770ef19673fb1defcbde2ee2b91c3c89bfcf164 ] + +disable ASPM with some ASICs on some specific platforms. +required from PCIe controller owner. + +Signed-off-by: Kenneth Feng +Reviewed-by: Yang Wang +Signed-off-by: Alex Deucher +Signed-off-by: Rosen Penev +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 32 +++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +@@ -84,6 +84,7 @@ + + #if IS_ENABLED(CONFIG_X86) + #include ++#include + #endif + + MODULE_FIRMWARE("amdgpu/vega10_gpu_info.bin"); +@@ -1758,6 +1759,35 @@ static bool amdgpu_device_pcie_dynamic_s + return true; + } + ++static bool amdgpu_device_aspm_support_quirk(struct amdgpu_device *adev) ++{ ++#if IS_ENABLED(CONFIG_X86) ++ struct cpuinfo_x86 *c = &cpu_data(0); ++ ++ if (!(amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(12, 0, 0) || ++ amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(12, 0, 1))) ++ return false; ++ ++ if (c->x86 == 6 && ++ adev->pm.pcie_gen_mask & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN5) { ++ switch (c->x86_model) { ++ case VFM_MODEL(INTEL_ALDERLAKE): ++ case VFM_MODEL(INTEL_ALDERLAKE_L): ++ case VFM_MODEL(INTEL_RAPTORLAKE): ++ case VFM_MODEL(INTEL_RAPTORLAKE_P): ++ case VFM_MODEL(INTEL_RAPTORLAKE_S): ++ return true; ++ default: ++ return false; ++ } ++ } else { ++ return false; ++ } ++#else ++ return false; ++#endif ++} ++ + /** + * amdgpu_device_should_use_aspm - check if the device should program ASPM + * +@@ -1782,6 +1812,8 @@ bool amdgpu_device_should_use_aspm(struc + } + if (adev->flags & AMD_IS_APU) + return false; ++ if (amdgpu_device_aspm_support_quirk(adev)) ++ return false; + return pcie_aspm_enabled(adev->pdev); + } + diff --git a/queue-6.12/drm-amd-disable-aspm-on-si.patch b/queue-6.12/drm-amd-disable-aspm-on-si.patch new file mode 100644 index 0000000000..bfdca20129 --- /dev/null +++ b/queue-6.12/drm-amd-disable-aspm-on-si.patch @@ -0,0 +1,41 @@ +From stable+bounces-232629-greg=kroah.com@vger.kernel.org Wed Apr 1 02:40:16 2026 +From: Rosen Penev +Date: Tue, 31 Mar 2026 17:39:07 -0700 +Subject: drm/amd: Disable ASPM on SI +To: stable@vger.kernel.org +Cc: "Alex Deucher" , "Christian König" , "Xinhui Pan" , "David Airlie" , "Simona Vetter" , "Harry Wentland" , "Leo Li" , "Rodrigo Siqueira" , "Ray Wu" , "Wayne Lin" , "Mario Limonciello" , "Roman Li" , "Eric Yang" , "Tony Cheng" , "Mauro Rossi" , "Timur Kristóf" , "Alex Hung" , amd-gfx@lists.freedesktop.org (open list:RADEON and AMDGPU DRM DRIVERS), dri-devel@lists.freedesktop.org (open list:DRM DRIVERS), linux-kernel@vger.kernel.org (open list) +Message-ID: <20260401003908.3438-10-rosenp@gmail.com> + +From: Timur Kristóf + +[ Upstream commit 7bdd91abf0cb3ea78160e2e78fb58b12f6a38d55 ] + +Enabling ASPM causes randoms hangs on Tahiti and Oland on Zen4. +It's unclear if this is a platform-specific or GPU-specific issue. +Disable ASPM on SI for the time being. + +Reviewed-by: Alex Deucher +Signed-off-by: Timur Kristóf +Signed-off-by: Alex Deucher +Signed-off-by: Rosen Penev +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +@@ -1761,6 +1761,13 @@ static bool amdgpu_device_pcie_dynamic_s + + static bool amdgpu_device_aspm_support_quirk(struct amdgpu_device *adev) + { ++ /* Enabling ASPM causes randoms hangs on Tahiti and Oland on Zen4. ++ * It's unclear if this is a platform-specific or GPU-specific issue. ++ * Disable ASPM on SI for the time being. ++ */ ++ if (adev->family == AMDGPU_FAMILY_SI) ++ return true; ++ + #if IS_ENABLED(CONFIG_X86) + struct cpuinfo_x86 *c = &cpu_data(0); + diff --git a/queue-6.12/drm-amd-display-adjust-dce-8-10-clock-don-t-overclock-by-15.patch b/queue-6.12/drm-amd-display-adjust-dce-8-10-clock-don-t-overclock-by-15.patch new file mode 100644 index 0000000000..20f8fa67cd --- /dev/null +++ b/queue-6.12/drm-amd-display-adjust-dce-8-10-clock-don-t-overclock-by-15.patch @@ -0,0 +1,70 @@ +From stable+bounces-232627-greg=kroah.com@vger.kernel.org Wed Apr 1 02:45:17 2026 +From: Rosen Penev +Date: Tue, 31 Mar 2026 17:39:05 -0700 +Subject: drm/amd/display: Adjust DCE 8-10 clock, don't overclock by 15% +To: stable@vger.kernel.org +Cc: "Alex Deucher" , "Christian König" , "Xinhui Pan" , "David Airlie" , "Simona Vetter" , "Harry Wentland" , "Leo Li" , "Rodrigo Siqueira" , "Ray Wu" , "Wayne Lin" , "Mario Limonciello" , "Roman Li" , "Eric Yang" , "Tony Cheng" , "Mauro Rossi" , "Timur Kristóf" , "Alex Hung" , amd-gfx@lists.freedesktop.org (open list:RADEON and AMDGPU DRM DRIVERS), dri-devel@lists.freedesktop.org (open list:DRM DRIVERS), linux-kernel@vger.kernel.org (open list) +Message-ID: <20260401003908.3438-8-rosenp@gmail.com> + +From: Timur Kristóf + +[ Upstream commit 1ae45b5d4f371af8ae51a3827d0ec9fe27eeb867 ] + +Adjust the nominal (and performance) clocks for DCE 8-10, +and set them to 625 MHz, which is the value used by the legacy +display code in amdgpu_atombios_get_clock_info. + +This was tested with Hawaii, Tonga and Fiji. +These GPUs can output 4K 60Hz (10-bit depth) at 625 MHz. + +The extra 15% clock was added as a workaround for a Polaris issue +which uses DCE 11, and should not have been used on DCE 8-10 which +are already hardcoded to the highest possible display clock. +Unfortunately, the extra 15% was mistakenly copied and kept +even on code paths which don't affect Polaris. + +This commit fixes that and also adds a check to make sure +not to exceed the maximum DCE 8-10 display clock. + +Fixes: 8cd61c313d8b ("drm/amd/display: Raise dispclk value for Polaris") +Fixes: dc88b4a684d2 ("drm/amd/display: make clk mgr soc specific") +Signed-off-by: Timur Kristóf +Acked-by: Alex Deucher +Reviewed-by: Rodrigo Siqueira +Reviewed-by: Alex Hung +Signed-off-by: Alex Deucher +Signed-off-by: Rosen Penev +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c | 12 +++++------- + 1 file changed, 5 insertions(+), 7 deletions(-) + +--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c ++++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c +@@ -72,9 +72,9 @@ static const struct state_dependent_cloc + /* ClocksStateLow */ + { .display_clk_khz = 352000, .pixel_clk_khz = 330000}, + /* ClocksStateNominal */ +-{ .display_clk_khz = 600000, .pixel_clk_khz = 400000 }, ++{ .display_clk_khz = 625000, .pixel_clk_khz = 400000 }, + /* ClocksStatePerformance */ +-{ .display_clk_khz = 600000, .pixel_clk_khz = 400000 } }; ++{ .display_clk_khz = 625000, .pixel_clk_khz = 400000 } }; + + int dentist_get_divider_from_did(int did) + { +@@ -403,11 +403,9 @@ static void dce_update_clocks(struct clk + { + struct clk_mgr_internal *clk_mgr_dce = TO_CLK_MGR_INTERNAL(clk_mgr_base); + struct dm_pp_power_level_change_request level_change_req; +- int patched_disp_clk = context->bw_ctx.bw.dce.dispclk_khz; +- +- /*TODO: W/A for dal3 linux, investigate why this works */ +- if (!clk_mgr_dce->dfs_bypass_active) +- patched_disp_clk = patched_disp_clk * 115 / 100; ++ const int max_disp_clk = ++ clk_mgr_dce->max_clks_by_state[DM_PP_CLOCKS_STATE_PERFORMANCE].display_clk_khz; ++ int patched_disp_clk = MIN(max_disp_clk, context->bw_ctx.bw.dce.dispclk_khz); + + level_change_req.power_level = dce_get_required_clocks_state(clk_mgr_base, context); + /* get max clock state from PPLIB */ diff --git a/queue-6.12/drm-amd-display-correct-logic-check-error-for-fastboot.patch b/queue-6.12/drm-amd-display-correct-logic-check-error-for-fastboot.patch new file mode 100644 index 0000000000..c8fe91ed62 --- /dev/null +++ b/queue-6.12/drm-amd-display-correct-logic-check-error-for-fastboot.patch @@ -0,0 +1,51 @@ +From stable+bounces-232628-greg=kroah.com@vger.kernel.org Wed Apr 1 02:45:51 2026 +From: Rosen Penev +Date: Tue, 31 Mar 2026 17:39:08 -0700 +Subject: drm/amd/display: Correct logic check error for fastboot +To: stable@vger.kernel.org +Cc: "Alex Deucher" , "Christian König" , "Xinhui Pan" , "David Airlie" , "Simona Vetter" , "Harry Wentland" , "Leo Li" , "Rodrigo Siqueira" , "Ray Wu" , "Wayne Lin" , "Mario Limonciello" , "Roman Li" , "Eric Yang" , "Tony Cheng" , "Mauro Rossi" , "Timur Kristóf" , "Alex Hung" , amd-gfx@lists.freedesktop.org (open list:RADEON and AMDGPU DRM DRIVERS), dri-devel@lists.freedesktop.org (open list:DRM DRIVERS), linux-kernel@vger.kernel.org (open list) +Message-ID: <20260401003908.3438-11-rosenp@gmail.com> + +From: Charlene Liu + +[ Upstream commit b6a65009e7ce3f0cc72da18f186adb60717b51a0 ] + +[Why] +Fix fastboot broken in driver. +This is caused by an open source backport change 7495962c. + +from the comment, the intended check is to disable fastboot +for pre-DCN10. but the logic check is reversed, and causes +fastboot to be disabled on all DCN10 and after. + +fastboot is for driver trying to pick up bios used hw setting +and bypass reprogramming the hw if dc_validate_boot_timing() +condition meets. + +Fixes: 7495962cbceb ("drm/amd/display: Disable fastboot on DCE 6 too") +Cc: stable@vger.kernel.org +Reviewed-by: Mario Limonciello +Reviewed-by: Ovidiu Bunea +Signed-off-by: Charlene Liu +Signed-off-by: Ray Wu +Tested-by: Daniel Wheeler +Signed-off-by: Alex Deucher +Signed-off-by: Rosen Penev +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c ++++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c +@@ -1910,8 +1910,8 @@ void dce110_enable_accelerated_mode(stru + + get_edp_streams(context, edp_streams, &edp_stream_num); + +- /* Check fastboot support, disable on DCE 6-8 because of blank screens */ +- if (edp_num && edp_stream_num && dc->ctx->dce_version < DCE_VERSION_10_0) { ++ /* Check fastboot support, disable on DCE 6-8-10 because of blank screens */ ++ if (edp_num && edp_stream_num && dc->ctx->dce_version > DCE_VERSION_10_0) { + for (i = 0; i < edp_num; i++) { + edp_link = edp_links[i]; + if (edp_link != edp_streams[0]->link) diff --git a/queue-6.12/drm-amd-display-disable-fastboot-on-dce-6-too.patch b/queue-6.12/drm-amd-display-disable-fastboot-on-dce-6-too.patch new file mode 100644 index 0000000000..df5ddfeb8f --- /dev/null +++ b/queue-6.12/drm-amd-display-disable-fastboot-on-dce-6-too.patch @@ -0,0 +1,41 @@ +From stable+bounces-232622-greg=kroah.com@vger.kernel.org Wed Apr 1 02:43:13 2026 +From: Rosen Penev +Date: Tue, 31 Mar 2026 17:39:01 -0700 +Subject: drm/amd/display: Disable fastboot on DCE 6 too +To: stable@vger.kernel.org +Cc: "Alex Deucher" , "Christian König" , "Xinhui Pan" , "David Airlie" , "Simona Vetter" , "Harry Wentland" , "Leo Li" , "Rodrigo Siqueira" , "Ray Wu" , "Wayne Lin" , "Mario Limonciello" , "Roman Li" , "Eric Yang" , "Tony Cheng" , "Mauro Rossi" , "Timur Kristóf" , "Alex Hung" , amd-gfx@lists.freedesktop.org (open list:RADEON and AMDGPU DRM DRIVERS), dri-devel@lists.freedesktop.org (open list:DRM DRIVERS), linux-kernel@vger.kernel.org (open list) +Message-ID: <20260401003908.3438-4-rosenp@gmail.com> + +From: Timur Kristóf + +[ Upstream commit 7495962cbceb967e095233a5673ea71f3bcdee7e ] + +It already didn't work on DCE 8, +so there is no reason to assume it would on DCE 6. + +Signed-off-by: Timur Kristóf +Reviewed-by: Rodrigo Siqueira +Reviewed-by: Alex Deucher +Reviewed-by: Alex Hung +Signed-off-by: Alex Deucher +Signed-off-by: Rosen Penev +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +--- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c ++++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c +@@ -1910,10 +1910,8 @@ void dce110_enable_accelerated_mode(stru + + get_edp_streams(context, edp_streams, &edp_stream_num); + +- // Check fastboot support, disable on DCE8 because of blank screens +- if (edp_num && edp_stream_num && dc->ctx->dce_version != DCE_VERSION_8_0 && +- dc->ctx->dce_version != DCE_VERSION_8_1 && +- dc->ctx->dce_version != DCE_VERSION_8_3) { ++ /* Check fastboot support, disable on DCE 6-8 because of blank screens */ ++ if (edp_num && edp_stream_num && dc->ctx->dce_version < DCE_VERSION_10_0) { + for (i = 0; i < edp_num; i++) { + edp_link = edp_links[i]; + if (edp_link != edp_streams[0]->link) diff --git a/queue-6.12/drm-amd-display-disable-scaling-on-dce6-for-now.patch b/queue-6.12/drm-amd-display-disable-scaling-on-dce6-for-now.patch new file mode 100644 index 0000000000..5d17d7064e --- /dev/null +++ b/queue-6.12/drm-amd-display-disable-scaling-on-dce6-for-now.patch @@ -0,0 +1,47 @@ +From stable+bounces-232626-greg=kroah.com@vger.kernel.org Wed Apr 1 02:45:16 2026 +From: Rosen Penev +Date: Tue, 31 Mar 2026 17:39:06 -0700 +Subject: drm/amd/display: Disable scaling on DCE6 for now +To: stable@vger.kernel.org +Cc: "Alex Deucher" , "Christian König" , "Xinhui Pan" , "David Airlie" , "Simona Vetter" , "Harry Wentland" , "Leo Li" , "Rodrigo Siqueira" , "Ray Wu" , "Wayne Lin" , "Mario Limonciello" , "Roman Li" , "Eric Yang" , "Tony Cheng" , "Mauro Rossi" , "Timur Kristóf" , "Alex Hung" , amd-gfx@lists.freedesktop.org (open list:RADEON and AMDGPU DRM DRIVERS), dri-devel@lists.freedesktop.org (open list:DRM DRIVERS), linux-kernel@vger.kernel.org (open list) +Message-ID: <20260401003908.3438-9-rosenp@gmail.com> + +From: Timur Kristóf + +[ Upstream commit 0e190a0446ec517666dab4691b296a9b758e590f ] + +Scaling doesn't work on DCE6 at the moment, the current +register programming produces incorrect output when using +fractional scaling (between 100-200%) on resolutions higher +than 1080p. + +Disable it until we figure out how to program it properly. + +Fixes: 7c15fd86aaec ("drm/amd/display: dc/dce: add initial DCE6 support (v10)") +Reviewed-by: Alex Deucher +Signed-off-by: Timur Kristóf +Signed-off-by: Alex Deucher +Signed-off-by: Rosen Penev +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.c ++++ b/drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.c +@@ -404,13 +404,13 @@ static const struct dc_plane_cap plane_c + }, + + .max_upscale_factor = { +- .argb8888 = 16000, ++ .argb8888 = 1, + .nv12 = 1, + .fp16 = 1 + }, + + .max_downscale_factor = { +- .argb8888 = 250, ++ .argb8888 = 1, + .nv12 = 1, + .fp16 = 1 + } diff --git a/queue-6.12/drm-amd-display-fix-dce-6.0-and-6.4-pll-programming.patch b/queue-6.12/drm-amd-display-fix-dce-6.0-and-6.4-pll-programming.patch new file mode 100644 index 0000000000..3132cfa030 --- /dev/null +++ b/queue-6.12/drm-amd-display-fix-dce-6.0-and-6.4-pll-programming.patch @@ -0,0 +1,132 @@ +From stable+bounces-232625-greg=kroah.com@vger.kernel.org Wed Apr 1 02:44:20 2026 +From: Rosen Penev +Date: Tue, 31 Mar 2026 17:39:04 -0700 +Subject: drm/amd/display: Fix DCE 6.0 and 6.4 PLL programming. +To: stable@vger.kernel.org +Cc: "Alex Deucher" , "Christian König" , "Xinhui Pan" , "David Airlie" , "Simona Vetter" , "Harry Wentland" , "Leo Li" , "Rodrigo Siqueira" , "Ray Wu" , "Wayne Lin" , "Mario Limonciello" , "Roman Li" , "Eric Yang" , "Tony Cheng" , "Mauro Rossi" , "Timur Kristóf" , "Alex Hung" , amd-gfx@lists.freedesktop.org (open list:RADEON and AMDGPU DRM DRIVERS), dri-devel@lists.freedesktop.org (open list:DRM DRIVERS), linux-kernel@vger.kernel.org (open list) +Message-ID: <20260401003908.3438-7-rosenp@gmail.com> + +From: Timur Kristóf + +[ Upstream commit 35222b5934ec8d762473592ece98659baf6bc48e ] + +Apparently, both DCE 6.0 and 6.4 have 3 PLLs, but PLL0 can only +be used for DP. Make sure to initialize the correct amount of PLLs +in DC for these DCE versions and use PLL0 only for DP. + +Also, on DCE 6.0 and 6.4, the PLL0 needs to be powered on at +initialization as opposed to DCE 6.1 and 7.x which use a different +clock source for DFS. + +The following functions were used as reference from the old +radeon driver implementation of DCE 6.x: +- radeon_atom_pick_pll +- atombios_crtc_set_disp_eng_pll + +Reviewed-by: Rodrigo Siqueira +Reviewed-by: Alex Deucher +Signed-off-by: Timur Kristóf +Signed-off-by: Alex Deucher +Signed-off-by: Rosen Penev +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c | 5 + + drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.c | 34 +++++++----- + 2 files changed, 25 insertions(+), 14 deletions(-) + +--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c ++++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c +@@ -245,6 +245,11 @@ int dce_set_clock( + pxl_clk_params.target_pixel_clock_100hz = requested_clk_khz * 10; + pxl_clk_params.pll_id = CLOCK_SOURCE_ID_DFS; + ++ /* DCE 6.0, DCE 6.4: engine clock is the same as PLL0 */ ++ if (clk_mgr_base->ctx->dce_version == DCE_VERSION_6_0 || ++ clk_mgr_base->ctx->dce_version == DCE_VERSION_6_4) ++ pxl_clk_params.pll_id = CLOCK_SOURCE_ID_PLL0; ++ + if (clk_mgr_dce->dfs_bypass_active) + pxl_clk_params.flags.SET_DISPCLK_DFS_BYPASS = true; + +--- a/drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.c ++++ b/drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.c +@@ -374,7 +374,7 @@ static const struct resource_caps res_ca + .num_timing_generator = 6, + .num_audio = 6, + .num_stream_encoder = 6, +- .num_pll = 2, ++ .num_pll = 3, + .num_ddc = 6, + }; + +@@ -390,7 +390,7 @@ static const struct resource_caps res_ca + .num_timing_generator = 2, + .num_audio = 2, + .num_stream_encoder = 2, +- .num_pll = 2, ++ .num_pll = 3, + .num_ddc = 2, + }; + +@@ -990,21 +990,24 @@ static bool dce60_construct( + + if (bp->fw_info_valid && bp->fw_info.external_clock_source_frequency_for_dp != 0) { + pool->base.dp_clock_source = +- dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); ++ dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + ++ /* DCE 6.0 and 6.4: PLL0 can only be used with DP. Don't initialize it here. */ + pool->base.clock_sources[0] = +- dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], false); ++ dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[1] = +- dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); ++ dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 2; + + } else { + pool->base.dp_clock_source = +- dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], true); ++ dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], true); + + pool->base.clock_sources[0] = +- dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); +- pool->base.clk_src_count = 1; ++ dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); ++ pool->base.clock_sources[1] = ++ dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); ++ pool->base.clk_src_count = 2; + } + + if (pool->base.dp_clock_source == NULL) { +@@ -1382,21 +1385,24 @@ static bool dce64_construct( + + if (bp->fw_info_valid && bp->fw_info.external_clock_source_frequency_for_dp != 0) { + pool->base.dp_clock_source = +- dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); ++ dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_EXTERNAL, NULL, true); + ++ /* DCE 6.0 and 6.4: PLL0 can only be used with DP. Don't initialize it here. */ + pool->base.clock_sources[0] = +- dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[0], false); ++ dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); + pool->base.clock_sources[1] = +- dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[1], false); ++ dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); + pool->base.clk_src_count = 2; + + } else { + pool->base.dp_clock_source = +- dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[0], true); ++ dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL0, &clk_src_regs[0], true); + + pool->base.clock_sources[0] = +- dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[1], false); +- pool->base.clk_src_count = 1; ++ dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL1, &clk_src_regs[1], false); ++ pool->base.clock_sources[1] = ++ dce60_clock_source_create(ctx, bp, CLOCK_SOURCE_ID_PLL2, &clk_src_regs[2], false); ++ pool->base.clk_src_count = 2; + } + + if (pool->base.dp_clock_source == NULL) { diff --git a/queue-6.12/drm-amd-display-keep-pll0-running-on-dce-6.0-and-6.4.patch b/queue-6.12/drm-amd-display-keep-pll0-running-on-dce-6.0-and-6.4.patch new file mode 100644 index 0000000000..8454019917 --- /dev/null +++ b/queue-6.12/drm-amd-display-keep-pll0-running-on-dce-6.0-and-6.4.patch @@ -0,0 +1,63 @@ +From stable+bounces-232624-greg=kroah.com@vger.kernel.org Wed Apr 1 02:44:15 2026 +From: Rosen Penev +Date: Tue, 31 Mar 2026 17:39:03 -0700 +Subject: drm/amd/display: Keep PLL0 running on DCE 6.0 and 6.4 +To: stable@vger.kernel.org +Cc: "Alex Deucher" , "Christian König" , "Xinhui Pan" , "David Airlie" , "Simona Vetter" , "Harry Wentland" , "Leo Li" , "Rodrigo Siqueira" , "Ray Wu" , "Wayne Lin" , "Mario Limonciello" , "Roman Li" , "Eric Yang" , "Tony Cheng" , "Mauro Rossi" , "Timur Kristóf" , "Alex Hung" , amd-gfx@lists.freedesktop.org (open list:RADEON and AMDGPU DRM DRIVERS), dri-devel@lists.freedesktop.org (open list:DRM DRIVERS), linux-kernel@vger.kernel.org (open list) +Message-ID: <20260401003908.3438-6-rosenp@gmail.com> + +From: Timur Kristóf + +[ Upstream commit 0449726b58ea64ec96b95f95944f0a3650204059 ] + +DC can turn off the display clock when no displays are connected +or when all displays are off, for reference see: +- dce*_validate_bandwidth + +DC also assumes that the DP clock is always on and never powers +it down, for reference see: +- dce110_clock_source_power_down + +In case of DCE 6.0 and 6.4, PLL0 is the clock source for both +the engine clock and DP clock, for reference see: +- radeon_atom_pick_pll +- atombios_crtc_set_disp_eng_pll + +Therefore, PLL0 should be always kept running on DCE 6.0 and 6.4. +This commit achieves that by ensuring that by setting the display +clock to the corresponding value in low power state instead of +zero. + +This fixes a page flip timeout on SI with DC which happens when +all connected displays are blanked. + +Signed-off-by: Timur Kristóf +Reviewed-by: Alex Deucher +Reviewed-by: Alex Hung +Signed-off-by: Alex Deucher +Signed-off-by: Rosen Penev +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.c | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +--- a/drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.c ++++ b/drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.c +@@ -889,7 +889,16 @@ static bool dce60_validate_bandwidth( + context->bw_ctx.bw.dce.dispclk_khz = 681000; + context->bw_ctx.bw.dce.yclk_khz = 250000 * MEMORY_TYPE_MULTIPLIER_CZ; + } else { +- context->bw_ctx.bw.dce.dispclk_khz = 0; ++ /* On DCE 6.0 and 6.4 the PLL0 is both the display engine clock and ++ * the DP clock, and shouldn't be turned off. Just select the display ++ * clock value from its low power mode. ++ */ ++ if (dc->ctx->dce_version == DCE_VERSION_6_0 || ++ dc->ctx->dce_version == DCE_VERSION_6_4) ++ context->bw_ctx.bw.dce.dispclk_khz = 352000; ++ else ++ context->bw_ctx.bw.dce.dispclk_khz = 0; ++ + context->bw_ctx.bw.dce.yclk_khz = 0; + } + diff --git a/queue-6.12/drm-amd-display-reject-modes-with-too-high-pixel-clock-on-dce6-10.patch b/queue-6.12/drm-amd-display-reject-modes-with-too-high-pixel-clock-on-dce6-10.patch new file mode 100644 index 0000000000..acfdc9c13f --- /dev/null +++ b/queue-6.12/drm-amd-display-reject-modes-with-too-high-pixel-clock-on-dce6-10.patch @@ -0,0 +1,158 @@ +From stable+bounces-232623-greg=kroah.com@vger.kernel.org Wed Apr 1 02:43:45 2026 +From: Rosen Penev +Date: Tue, 31 Mar 2026 17:39:02 -0700 +Subject: drm/amd/display: Reject modes with too high pixel clock on DCE6-10 +To: stable@vger.kernel.org +Cc: "Alex Deucher" , "Christian König" , "Xinhui Pan" , "David Airlie" , "Simona Vetter" , "Harry Wentland" , "Leo Li" , "Rodrigo Siqueira" , "Ray Wu" , "Wayne Lin" , "Mario Limonciello" , "Roman Li" , "Eric Yang" , "Tony Cheng" , "Mauro Rossi" , "Timur Kristóf" , "Alex Hung" , amd-gfx@lists.freedesktop.org (open list:RADEON and AMDGPU DRM DRIVERS), dri-devel@lists.freedesktop.org (open list:DRM DRIVERS), linux-kernel@vger.kernel.org (open list) +Message-ID: <20260401003908.3438-5-rosenp@gmail.com> + +From: Timur Kristóf + +[ Upstream commit 118800b0797a046adaa2a8e9dee9b971b78802a7 ] + +Reject modes with a pixel clock higher than the maximum display +clock. Use 400 MHz as a fallback value when the maximum display +clock is not known. Pixel clocks that are higher than the display +clock just won't work and are not supported. + +With the addition of the YUV422 fallback, DC can now accidentally +select a mode requiring higher pixel clock than actually supported +when the DP version supports the required bandwidth but the clock +is otherwise too high for the display engine. DCE 6-10 don't +support these modes but they don't have a bandwidth calculation +to reject them properly. + +Fixes: db291ed1732e ("drm/amd/display: Add fallback path for YCBCR422") +Reviewed-by: Alex Deucher +Signed-off-by: Timur Kristóf +Signed-off-by: Mario Limonciello +Signed-off-by: Alex Deucher +Signed-off-by: Rosen Penev +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c | 3 +++ + drivers/gpu/drm/amd/display/dc/clk_mgr/dce60/dce60_clk_mgr.c | 5 +++++ + drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.c | 10 +++++++++- + drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c | 10 +++++++++- + drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c | 10 +++++++++- + 5 files changed, 35 insertions(+), 3 deletions(-) + +--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c ++++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c +@@ -460,6 +460,9 @@ void dce_clk_mgr_construct( + clk_mgr->max_clks_state = DM_PP_CLOCKS_STATE_NOMINAL; + clk_mgr->cur_min_clks_state = DM_PP_CLOCKS_STATE_INVALID; + ++ base->clks.max_supported_dispclk_khz = ++ clk_mgr->max_clks_by_state[DM_PP_CLOCKS_STATE_PERFORMANCE].display_clk_khz; ++ + dce_clock_read_integrated_info(clk_mgr); + dce_clock_read_ss_info(clk_mgr); + } +--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce60/dce60_clk_mgr.c ++++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce60/dce60_clk_mgr.c +@@ -147,6 +147,8 @@ void dce60_clk_mgr_construct( + struct dc_context *ctx, + struct clk_mgr_internal *clk_mgr) + { ++ struct clk_mgr *base = &clk_mgr->base; ++ + dce_clk_mgr_construct(ctx, clk_mgr); + + memcpy(clk_mgr->max_clks_by_state, +@@ -157,5 +159,8 @@ void dce60_clk_mgr_construct( + clk_mgr->clk_mgr_shift = &disp_clk_shift; + clk_mgr->clk_mgr_mask = &disp_clk_mask; + clk_mgr->base.funcs = &dce60_funcs; ++ ++ base->clks.max_supported_dispclk_khz = ++ clk_mgr->max_clks_by_state[DM_PP_CLOCKS_STATE_PERFORMANCE].display_clk_khz; + } + +--- a/drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.c ++++ b/drivers/gpu/drm/amd/display/dc/dce60/dce60_resource.c +@@ -34,6 +34,7 @@ + #include "stream_encoder.h" + + #include "resource.h" ++#include "clk_mgr.h" + #include "include/irq_service_interface.h" + #include "irq/dce60/irq_service_dce60.h" + #include "dce110/dce110_timing_generator.h" +@@ -870,10 +871,17 @@ static bool dce60_validate_bandwidth( + { + int i; + bool at_least_one_pipe = false; ++ struct dc_stream_state *stream = NULL; ++ const uint32_t max_pix_clk_khz = max(dc->clk_mgr->clks.max_supported_dispclk_khz, 400000); + + for (i = 0; i < dc->res_pool->pipe_count; i++) { +- if (context->res_ctx.pipe_ctx[i].stream) ++ stream = context->res_ctx.pipe_ctx[i].stream; ++ if (stream) { + at_least_one_pipe = true; ++ ++ if (stream->timing.pix_clk_100hz >= max_pix_clk_khz * 10) ++ return DC_FAIL_BANDWIDTH_VALIDATE; ++ } + } + + if (at_least_one_pipe) { +--- a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c ++++ b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c +@@ -29,6 +29,7 @@ + #include "stream_encoder.h" + + #include "resource.h" ++#include "clk_mgr.h" + #include "include/irq_service_interface.h" + #include "virtual/virtual_stream_encoder.h" + #include "dce110/dce110_resource.h" +@@ -843,10 +844,17 @@ static bool dce100_validate_bandwidth( + { + int i; + bool at_least_one_pipe = false; ++ struct dc_stream_state *stream = NULL; ++ const uint32_t max_pix_clk_khz = max(dc->clk_mgr->clks.max_supported_dispclk_khz, 400000); + + for (i = 0; i < dc->res_pool->pipe_count; i++) { +- if (context->res_ctx.pipe_ctx[i].stream) ++ stream = context->res_ctx.pipe_ctx[i].stream; ++ if (stream) { + at_least_one_pipe = true; ++ ++ if (stream->timing.pix_clk_100hz >= max_pix_clk_khz * 10) ++ return DC_FAIL_BANDWIDTH_VALIDATE; ++ } + } + + if (at_least_one_pipe) { +--- a/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c ++++ b/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c +@@ -32,6 +32,7 @@ + #include "stream_encoder.h" + + #include "resource.h" ++#include "clk_mgr.h" + #include "include/irq_service_interface.h" + #include "irq/dce80/irq_service_dce80.h" + #include "dce110/dce110_timing_generator.h" +@@ -876,10 +877,17 @@ static bool dce80_validate_bandwidth( + { + int i; + bool at_least_one_pipe = false; ++ struct dc_stream_state *stream = NULL; ++ const uint32_t max_pix_clk_khz = max(dc->clk_mgr->clks.max_supported_dispclk_khz, 400000); + + for (i = 0; i < dc->res_pool->pipe_count; i++) { +- if (context->res_ctx.pipe_ctx[i].stream) ++ stream = context->res_ctx.pipe_ctx[i].stream; ++ if (stream) { + at_least_one_pipe = true; ++ ++ if (stream->timing.pix_clk_100hz >= max_pix_clk_khz * 10) ++ return DC_FAIL_BANDWIDTH_VALIDATE; ++ } + } + + if (at_least_one_pipe) { diff --git a/queue-6.12/drm-amd-pm-disable-od_fan_curve-if-temp-or-pwm-range-invalid-for-smu-v13.patch b/queue-6.12/drm-amd-pm-disable-od_fan_curve-if-temp-or-pwm-range-invalid-for-smu-v13.patch new file mode 100644 index 0000000000..e02825c553 --- /dev/null +++ b/queue-6.12/drm-amd-pm-disable-od_fan_curve-if-temp-or-pwm-range-invalid-for-smu-v13.patch @@ -0,0 +1,148 @@ +From stable+bounces-232829-greg=kroah.com@vger.kernel.org Wed Apr 1 19:17:47 2026 +From: Sasha Levin +Date: Wed, 1 Apr 2026 13:06:47 -0400 +Subject: drm/amd/pm: disable OD_FAN_CURVE if temp or pwm range invalid for smu v13 +To: stable@vger.kernel.org +Cc: Yang Wang , Alex Deucher , Sasha Levin +Message-ID: <20260401170647.151337-1-sashal@kernel.org> + +From: Yang Wang + +[ Upstream commit 3e6dd28a11083e83e11a284d99fcc9eb748c321c ] + +Forcibly disable the OD_FAN_CURVE feature when temperature or PWM range is invalid, +otherwise PMFW will reject this configuration on smu v13.0.x + +example: +$ sudo cat /sys/bus/pci/devices//gpu_od/fan_ctrl/fan_curve + +OD_FAN_CURVE: +0: 0C 0% +1: 0C 0% +2: 0C 0% +3: 0C 0% +4: 0C 0% +OD_RANGE: +FAN_CURVE(hotspot temp): 0C 0C +FAN_CURVE(fan speed): 0% 0% + +$ echo "0 50 40" | sudo tee fan_curve + +kernel log: +[ 756.442527] amdgpu 0000:03:00.0: amdgpu: Fan curve temp setting(50) must be within [0, 0]! +[ 777.345800] amdgpu 0000:03:00.0: amdgpu: Fan curve temp setting(50) must be within [0, 0]! + +Closes: https://github.com/ROCm/amdgpu/issues/208 +Signed-off-by: Yang Wang +Acked-by: Alex Deucher +Signed-off-by: Alex Deucher +(cherry picked from commit 470891606c5a97b1d0d937e0aa67a3bed9fcb056) +Cc: stable@vger.kernel.org +[ adapted forward declaration placement to existing FEATURE_MASK macro ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c | 33 ++++++++++++++++++- + drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c | 33 ++++++++++++++++++- + 2 files changed, 64 insertions(+), 2 deletions(-) + +--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c ++++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +@@ -59,6 +59,10 @@ + + #define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c)) + ++static void smu_v13_0_0_get_od_setting_limits(struct smu_context *smu, ++ int od_feature_bit, ++ int32_t *min, int32_t *max); ++ + #define FEATURE_MASK(feature) (1ULL << feature) + #define SMC_DPM_FEATURE ( \ + FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT) | \ +@@ -1082,8 +1086,35 @@ static bool smu_v13_0_0_is_od_feature_su + PPTable_t *pptable = smu->smu_table.driver_pptable; + const OverDriveLimits_t * const overdrive_upperlimits = + &pptable->SkuTable.OverDriveLimitsBasicMax; ++ int32_t min_value, max_value; ++ bool feature_enabled; ++ ++ switch (od_feature_bit) { ++ case PP_OD_FEATURE_FAN_CURVE_BIT: ++ feature_enabled = !!(overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit)); ++ if (feature_enabled) { ++ smu_v13_0_0_get_od_setting_limits(smu, PP_OD_FEATURE_FAN_CURVE_TEMP, ++ &min_value, &max_value); ++ if (!min_value && !max_value) { ++ feature_enabled = false; ++ goto out; ++ } ++ ++ smu_v13_0_0_get_od_setting_limits(smu, PP_OD_FEATURE_FAN_CURVE_PWM, ++ &min_value, &max_value); ++ if (!min_value && !max_value) { ++ feature_enabled = false; ++ goto out; ++ } ++ } ++ break; ++ default: ++ feature_enabled = !!(overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit)); ++ break; ++ } + +- return overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit); ++out: ++ return feature_enabled; + } + + static void smu_v13_0_0_get_od_setting_limits(struct smu_context *smu, +--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c ++++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +@@ -59,6 +59,10 @@ + + #define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c)) + ++static void smu_v13_0_7_get_od_setting_limits(struct smu_context *smu, ++ int od_feature_bit, ++ int32_t *min, int32_t *max); ++ + #define FEATURE_MASK(feature) (1ULL << feature) + #define SMC_DPM_FEATURE ( \ + FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT) | \ +@@ -1071,8 +1075,35 @@ static bool smu_v13_0_7_is_od_feature_su + PPTable_t *pptable = smu->smu_table.driver_pptable; + const OverDriveLimits_t * const overdrive_upperlimits = + &pptable->SkuTable.OverDriveLimitsBasicMax; ++ int32_t min_value, max_value; ++ bool feature_enabled; ++ ++ switch (od_feature_bit) { ++ case PP_OD_FEATURE_FAN_CURVE_BIT: ++ feature_enabled = !!(overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit)); ++ if (feature_enabled) { ++ smu_v13_0_7_get_od_setting_limits(smu, PP_OD_FEATURE_FAN_CURVE_TEMP, ++ &min_value, &max_value); ++ if (!min_value && !max_value) { ++ feature_enabled = false; ++ goto out; ++ } ++ ++ smu_v13_0_7_get_od_setting_limits(smu, PP_OD_FEATURE_FAN_CURVE_PWM, ++ &min_value, &max_value); ++ if (!min_value && !max_value) { ++ feature_enabled = false; ++ goto out; ++ } ++ } ++ break; ++ default: ++ feature_enabled = !!(overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit)); ++ break; ++ } + +- return overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit); ++out: ++ return feature_enabled; + } + + static void smu_v13_0_7_get_od_setting_limits(struct smu_context *smu, diff --git a/queue-6.12/ext4-publish-jinode-after-initialization.patch b/queue-6.12/ext4-publish-jinode-after-initialization.patch new file mode 100644 index 0000000000..8a8441b92d --- /dev/null +++ b/queue-6.12/ext4-publish-jinode-after-initialization.patch @@ -0,0 +1,152 @@ +From stable+bounces-233071-greg=kroah.com@vger.kernel.org Thu Apr 2 18:56:36 2026 +From: Sasha Levin +Date: Thu, 2 Apr 2026 12:51:19 -0400 +Subject: ext4: publish jinode after initialization +To: stable@vger.kernel.org +Cc: Li Chen , Jan Kara , Theodore Ts'o , stable@kernel.org, Sasha Levin +Message-ID: <20260402165119.1482773-1-sashal@kernel.org> + +From: Li Chen + +[ Upstream commit 1aec30021edd410b986c156f195f3d23959a9d11 ] + +ext4_inode_attach_jinode() publishes ei->jinode to concurrent users. +It used to set ei->jinode before jbd2_journal_init_jbd_inode(), +allowing a reader to observe a non-NULL jinode with i_vfs_inode +still unset. + +The fast commit flush path can then pass this jinode to +jbd2_wait_inode_data(), which dereferences i_vfs_inode->i_mapping and +may crash. + +Below is the crash I observe: +``` +BUG: unable to handle page fault for address: 000000010beb47f4 +PGD 110e51067 P4D 110e51067 PUD 0 +Oops: Oops: 0000 [#1] SMP NOPTI +CPU: 1 UID: 0 PID: 4850 Comm: fc_fsync_bench_ Not tainted 6.18.0-00764-g795a690c06a5 #1 PREEMPT(voluntary) +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Arch Linux 1.17.0-2-2 04/01/2014 +RIP: 0010:xas_find_marked+0x3d/0x2e0 +Code: e0 03 48 83 f8 02 0f 84 f0 01 00 00 48 8b 47 08 48 89 c3 48 39 c6 0f 82 fd 01 00 00 48 85 c9 74 3d 48 83 f9 03 77 63 4c 8b 0f <49> 8b 71 08 48 c7 47 18 00 00 00 00 48 89 f1 83 e1 03 48 83 f9 02 +RSP: 0018:ffffbbee806e7bf0 EFLAGS: 00010246 +RAX: 000000000010beb4 RBX: 000000000010beb4 RCX: 0000000000000003 +RDX: 0000000000000001 RSI: 0000002000300000 RDI: ffffbbee806e7c10 +RBP: 0000000000000001 R08: 0000002000300000 R09: 000000010beb47ec +R10: ffff9ea494590090 R11: 0000000000000000 R12: 0000002000300000 +R13: ffffbbee806e7c90 R14: ffff9ea494513788 R15: ffffbbee806e7c88 +FS: 00007fc2f9e3e6c0(0000) GS:ffff9ea6b1444000(0000) knlGS:0000000000000000 +CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +CR2: 000000010beb47f4 CR3: 0000000119ac5000 CR4: 0000000000750ef0 +PKRU: 55555554 +Call Trace: + +filemap_get_folios_tag+0x87/0x2a0 +__filemap_fdatawait_range+0x5f/0xd0 +? srso_alias_return_thunk+0x5/0xfbef5 +? __schedule+0x3e7/0x10c0 +? srso_alias_return_thunk+0x5/0xfbef5 +? srso_alias_return_thunk+0x5/0xfbef5 +? srso_alias_return_thunk+0x5/0xfbef5 +? preempt_count_sub+0x5f/0x80 +? srso_alias_return_thunk+0x5/0xfbef5 +? cap_safe_nice+0x37/0x70 +? srso_alias_return_thunk+0x5/0xfbef5 +? preempt_count_sub+0x5f/0x80 +? srso_alias_return_thunk+0x5/0xfbef5 +filemap_fdatawait_range_keep_errors+0x12/0x40 +ext4_fc_commit+0x697/0x8b0 +? ext4_file_write_iter+0x64b/0x950 +? srso_alias_return_thunk+0x5/0xfbef5 +? preempt_count_sub+0x5f/0x80 +? srso_alias_return_thunk+0x5/0xfbef5 +? vfs_write+0x356/0x480 +? srso_alias_return_thunk+0x5/0xfbef5 +? preempt_count_sub+0x5f/0x80 +ext4_sync_file+0xf7/0x370 +do_fsync+0x3b/0x80 +? syscall_trace_enter+0x108/0x1d0 +__x64_sys_fdatasync+0x16/0x20 +do_syscall_64+0x62/0x2c0 +entry_SYSCALL_64_after_hwframe+0x76/0x7e +... +``` + +Fix this by initializing the jbd2_inode first. +Use smp_wmb() and WRITE_ONCE() to publish ei->jinode after +initialization. Readers use READ_ONCE() to fetch the pointer. + +Fixes: a361293f5fede ("jbd2: Fix oops in jbd2_journal_file_inode()") +Cc: stable@vger.kernel.org +Signed-off-by: Li Chen +Reviewed-by: Jan Kara +Link: https://patch.msgid.link/20260225082617.147957-1-me@linux.beauty +Signed-off-by: Theodore Ts'o +Cc: stable@kernel.org +[ adapted READ_ONCE(ei->jinode) to use pos->jinode ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/ext4/fast_commit.c | 4 ++-- + fs/ext4/inode.c | 15 +++++++++++---- + 2 files changed, 13 insertions(+), 6 deletions(-) + +--- a/fs/ext4/fast_commit.c ++++ b/fs/ext4/fast_commit.c +@@ -997,7 +997,7 @@ static int ext4_fc_submit_inode_data_all + finish_wait(&ei->i_fc_wait, &wait); + } + spin_unlock(&sbi->s_fc_lock); +- ret = jbd2_submit_inode_data(journal, ei->jinode); ++ ret = jbd2_submit_inode_data(journal, READ_ONCE(ei->jinode)); + if (ret) + return ret; + spin_lock(&sbi->s_fc_lock); +@@ -1022,7 +1022,7 @@ static int ext4_fc_wait_inode_data_all(j + continue; + spin_unlock(&sbi->s_fc_lock); + +- ret = jbd2_wait_inode_data(journal, pos->jinode); ++ ret = jbd2_wait_inode_data(journal, READ_ONCE(pos->jinode)); + if (ret) + return ret; + spin_lock(&sbi->s_fc_lock); +--- a/fs/ext4/inode.c ++++ b/fs/ext4/inode.c +@@ -128,6 +128,8 @@ void ext4_inode_csum_set(struct inode *i + static inline int ext4_begin_ordered_truncate(struct inode *inode, + loff_t new_size) + { ++ struct jbd2_inode *jinode = READ_ONCE(EXT4_I(inode)->jinode); ++ + trace_ext4_begin_ordered_truncate(inode, new_size); + /* + * If jinode is zero, then we never opened the file for +@@ -135,10 +137,10 @@ static inline int ext4_begin_ordered_tru + * jbd2_journal_begin_ordered_truncate() since there's no + * outstanding writes we need to flush. + */ +- if (!EXT4_I(inode)->jinode) ++ if (!jinode) + return 0; + return jbd2_journal_begin_ordered_truncate(EXT4_JOURNAL(inode), +- EXT4_I(inode)->jinode, ++ jinode, + new_size); + } + +@@ -4120,8 +4122,13 @@ int ext4_inode_attach_jinode(struct inod + spin_unlock(&inode->i_lock); + return -ENOMEM; + } +- ei->jinode = jinode; +- jbd2_journal_init_jbd_inode(ei->jinode, inode); ++ jbd2_journal_init_jbd_inode(jinode, inode); ++ /* ++ * Publish ->jinode only after it is fully initialized so that ++ * readers never observe a partially initialized jbd2_inode. ++ */ ++ smp_wmb(); ++ WRITE_ONCE(ei->jinode, jinode); + jinode = NULL; + } + spin_unlock(&inode->i_lock); diff --git a/queue-6.12/mm-huge_memory-fix-folio-isn-t-locked-in-softleaf_to_folio.patch b/queue-6.12/mm-huge_memory-fix-folio-isn-t-locked-in-softleaf_to_folio.patch new file mode 100644 index 0000000000..319dae59fe --- /dev/null +++ b/queue-6.12/mm-huge_memory-fix-folio-isn-t-locked-in-softleaf_to_folio.patch @@ -0,0 +1,121 @@ +From stable+bounces-231405-greg=kroah.com@vger.kernel.org Tue Mar 31 13:42:48 2026 +From: Sasha Levin +Date: Tue, 31 Mar 2026 07:39:06 -0400 +Subject: mm/huge_memory: fix folio isn't locked in softleaf_to_folio() +To: stable@vger.kernel.org +Cc: Jinjiang Tu , "David Hildenbrand (Arm)" , "Lorenzo Stoakes (Oracle)" , Barry Song , Kefeng Wang , Liam Howlett , Michal Hocko , Mike Rapoport , Nanyong Sun , Ryan Roberts , Suren Baghdasaryan , Vlastimil Babka , Andrew Morton , Sasha Levin +Message-ID: <20260331113906.2080339-1-sashal@kernel.org> + +From: Jinjiang Tu + +[ Upstream commit 4c5e7f0fcd592801c9cc18f29f80fbee84eb8669 ] + +On arm64 server, we found folio that get from migration entry isn't locked +in softleaf_to_folio(). This issue triggers when mTHP splitting and +zap_nonpresent_ptes() races, and the root cause is lack of memory barrier +in softleaf_to_folio(). The race is as follows: + + CPU0 CPU1 + +deferred_split_scan() zap_nonpresent_ptes() + lock folio + split_folio() + unmap_folio() + change ptes to migration entries + __split_folio_to_order() softleaf_to_folio() + set flags(including PG_locked) for tail pages folio = pfn_folio(softleaf_to_pfn(entry)) + smp_wmb() VM_WARN_ON_ONCE(!folio_test_locked(folio)) + prep_compound_page() for tail pages + +In __split_folio_to_order(), smp_wmb() guarantees page flags of tail pages +are visible before the tail page becomes non-compound. smp_wmb() should +be paired with smp_rmb() in softleaf_to_folio(), which is missed. As a +result, if zap_nonpresent_ptes() accesses migration entry that stores tail +pfn, softleaf_to_folio() may see the updated compound_head of tail page +before page->flags. + +This issue will trigger VM_WARN_ON_ONCE() in pfn_swap_entry_folio() +because of the race between folio split and zap_nonpresent_ptes() +leading to a folio incorrectly undergoing modification without a folio +lock being held. + +This is a BUG_ON() before commit 93976a20345b ("mm: eliminate further +swapops predicates"), which in merged in v6.19-rc1. + +To fix it, add missing smp_rmb() if the softleaf entry is migration entry +in softleaf_to_folio() and softleaf_to_page(). + +[tujinjiang@huawei.com: update function name and comments] + Link: https://lkml.kernel.org/r/20260321075214.3305564-1-tujinjiang@huawei.com +Link: https://lkml.kernel.org/r/20260319012541.4158561-1-tujinjiang@huawei.com +Fixes: e9b61f19858a ("thp: reintroduce split_huge_page()") +Signed-off-by: Jinjiang Tu +Acked-by: David Hildenbrand (Arm) +Reviewed-by: Lorenzo Stoakes (Oracle) +Cc: Barry Song +Cc: Kefeng Wang +Cc: Liam Howlett +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Nanyong Sun +Cc: Ryan Roberts +Cc: Suren Baghdasaryan +Cc: Vlastimil Babka +Cc: +Signed-off-by: Andrew Morton +[ applied fix to swapops.h using old pfn_swap_entry/swp_entry_t naming ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/swapops.h | 27 +++++++++++++++++++-------- + 1 file changed, 19 insertions(+), 8 deletions(-) + +--- a/include/linux/swapops.h ++++ b/include/linux/swapops.h +@@ -484,15 +484,29 @@ static inline int pte_none_mostly(pte_t + return pte_none(pte) || is_pte_marker(pte); + } + +-static inline struct page *pfn_swap_entry_to_page(swp_entry_t entry) ++static inline void swap_entry_migration_sync(swp_entry_t entry, ++ struct folio *folio) + { +- struct page *p = pfn_to_page(swp_offset_pfn(entry)); ++ /* ++ * Ensure we do not race with split, which might alter tail pages into new ++ * folios and thus result in observing an unlocked folio. ++ * This matches the write barrier in __split_folio_to_order(). ++ */ ++ smp_rmb(); + + /* + * Any use of migration entries may only occur while the + * corresponding page is locked + */ +- BUG_ON(is_migration_entry(entry) && !PageLocked(p)); ++ BUG_ON(!folio_test_locked(folio)); ++} ++ ++static inline struct page *pfn_swap_entry_to_page(swp_entry_t entry) ++{ ++ struct page *p = pfn_to_page(swp_offset_pfn(entry)); ++ ++ if (is_migration_entry(entry)) ++ swap_entry_migration_sync(entry, page_folio(p)); + + return p; + } +@@ -501,11 +515,8 @@ static inline struct folio *pfn_swap_ent + { + struct folio *folio = pfn_folio(swp_offset_pfn(entry)); + +- /* +- * Any use of migration entries may only occur while the +- * corresponding folio is locked +- */ +- BUG_ON(is_migration_entry(entry) && !folio_test_locked(folio)); ++ if (is_migration_entry(entry)) ++ swap_entry_migration_sync(entry, folio); + + return folio; + } diff --git a/queue-6.12/mm-memory-fix-pmd-pud-checks-in-follow_pfnmap_start.patch b/queue-6.12/mm-memory-fix-pmd-pud-checks-in-follow_pfnmap_start.patch new file mode 100644 index 0000000000..bbecca6a39 --- /dev/null +++ b/queue-6.12/mm-memory-fix-pmd-pud-checks-in-follow_pfnmap_start.patch @@ -0,0 +1,103 @@ +From stable+bounces-232830-greg=kroah.com@vger.kernel.org Wed Apr 1 19:17:40 2026 +From: Sasha Levin +Date: Wed, 1 Apr 2026 13:06:43 -0400 +Subject: mm/memory: fix PMD/PUD checks in follow_pfnmap_start() +To: stable@vger.kernel.org +Cc: "David Hildenbrand (Arm)" , "Mike Rapoport (Microsoft)" , "Lorenzo Stoakes (Oracle)" , Liam Howlett , Michal Hocko , Peter Xu , Suren Baghdasaryan , Vlastimil Babka , Andrew Morton , Sasha Levin +Message-ID: <20260401170643.151278-2-sashal@kernel.org> + +From: "David Hildenbrand (Arm)" + +[ Upstream commit ffef67b93aa352b34e6aeba3d52c19a63885409a ] + +follow_pfnmap_start() suffers from two problems: + +(1) We are not re-fetching the pmd/pud after taking the PTL + +Therefore, we are not properly stabilizing what the lock actually +protects. If there is concurrent zapping, we would indicate to the +caller that we found an entry, however, that entry might already have +been invalidated, or contain a different PFN after taking the lock. + +Properly use pmdp_get() / pudp_get() after taking the lock. + +(2) pmd_leaf() / pud_leaf() are not well defined on non-present entries + +pmd_leaf()/pud_leaf() could wrongly trigger on non-present entries. + +There is no real guarantee that pmd_leaf()/pud_leaf() returns something +reasonable on non-present entries. Most architectures indeed either +perform a present check or make it work by smart use of flags. + +However, for example loongarch checks the _PAGE_HUGE flag in pmd_leaf(), +and always sets the _PAGE_HUGE flag in __swp_entry_to_pmd(). Whereby +pmd_trans_huge() explicitly checks pmd_present(), pmd_leaf() does not do +that. + +Let's check pmd_present()/pud_present() before assuming "the is a present +PMD leaf" when spotting pmd_leaf()/pud_leaf(), like other page table +handling code that traverses user page tables does. + +Given that non-present PMD entries are likely rare in VM_IO|VM_PFNMAP, (1) +is likely more relevant than (2). It is questionable how often (1) would +actually trigger, but let's CC stable to be sure. + +This was found by code inspection. + +Link: https://lkml.kernel.org/r/20260323-follow_pfnmap_fix-v1-1-5b0ec10872b3@kernel.org +Fixes: 6da8e9634bb7 ("mm: new follow_pfnmap API") +Signed-off-by: David Hildenbrand (Arm) +Acked-by: Mike Rapoport (Microsoft) +Reviewed-by: Lorenzo Stoakes (Oracle) +Cc: Liam Howlett +Cc: Michal Hocko +Cc: Peter Xu +Cc: Suren Baghdasaryan +Cc: Vlastimil Babka +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + mm/memory.c | 18 +++++++++++++++--- + 1 file changed, 15 insertions(+), 3 deletions(-) + +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -6457,11 +6457,16 @@ retry: + + pudp = pud_offset(p4dp, address); + pud = pudp_get(pudp); +- if (pud_none(pud)) ++ if (!pud_present(pud)) + goto out; + if (pud_leaf(pud)) { + lock = pud_lock(mm, pudp); +- if (!unlikely(pud_leaf(pud))) { ++ pud = pudp_get(pudp); ++ ++ if (unlikely(!pud_present(pud))) { ++ spin_unlock(lock); ++ goto out; ++ } else if (unlikely(!pud_leaf(pud))) { + spin_unlock(lock); + goto retry; + } +@@ -6473,9 +6478,16 @@ retry: + + pmdp = pmd_offset(pudp, address); + pmd = pmdp_get_lockless(pmdp); ++ if (!pmd_present(pmd)) ++ goto out; + if (pmd_leaf(pmd)) { + lock = pmd_lock(mm, pmdp); +- if (!unlikely(pmd_leaf(pmd))) { ++ pmd = pmdp_get(pmdp); ++ ++ if (unlikely(!pmd_present(pmd))) { ++ spin_unlock(lock); ++ goto out; ++ } else if (unlikely(!pmd_leaf(pmd))) { + spin_unlock(lock); + goto retry; + } diff --git a/queue-6.12/mm-replace-read_once-with-standard-page-table-accessors.patch b/queue-6.12/mm-replace-read_once-with-standard-page-table-accessors.patch new file mode 100644 index 0000000000..0797988685 --- /dev/null +++ b/queue-6.12/mm-replace-read_once-with-standard-page-table-accessors.patch @@ -0,0 +1,143 @@ +From stable+bounces-232828-greg=kroah.com@vger.kernel.org Wed Apr 1 19:17:48 2026 +From: Sasha Levin +Date: Wed, 1 Apr 2026 13:06:42 -0400 +Subject: mm: replace READ_ONCE() with standard page table accessors +To: stable@vger.kernel.org +Cc: Anshuman Khandual , David Hildenbrand , Lance Yang , Wei Yang , Dev Jain , Andrew Morton , Sasha Levin +Message-ID: <20260401170643.151278-1-sashal@kernel.org> + +From: Anshuman Khandual + +[ Upstream commit c0efdb373c3aaacb32db59cadb0710cac13e44ae ] + +Replace all READ_ONCE() with a standard page table accessors i.e +pxdp_get() that defaults into READ_ONCE() in cases where platform does not +override. + +Link: https://lkml.kernel.org/r/20251007063100.2396936-1-anshuman.khandual@arm.com +Signed-off-by: Anshuman Khandual +Acked-by: David Hildenbrand +Reviewed-by: Lance Yang +Reviewed-by: Wei Yang +Reviewed-by: Dev Jain +Signed-off-by: Andrew Morton +Stable-dep-of: ffef67b93aa3 ("mm/memory: fix PMD/PUD checks in follow_pfnmap_start()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + mm/gup.c | 10 +++++----- + mm/hmm.c | 2 +- + mm/memory.c | 4 ++-- + mm/mprotect.c | 2 +- + mm/sparse-vmemmap.c | 2 +- + mm/vmscan.c | 2 +- + 6 files changed, 11 insertions(+), 11 deletions(-) + +--- a/mm/gup.c ++++ b/mm/gup.c +@@ -1013,7 +1013,7 @@ static struct page *follow_pud_mask(stru + struct mm_struct *mm = vma->vm_mm; + + pudp = pud_offset(p4dp, address); +- pud = READ_ONCE(*pudp); ++ pud = pudp_get(pudp); + if (!pud_present(pud)) + return no_page_table(vma, flags, address); + if (pud_leaf(pud)) { +@@ -1038,7 +1038,7 @@ static struct page *follow_p4d_mask(stru + p4d_t *p4dp, p4d; + + p4dp = p4d_offset(pgdp, address); +- p4d = READ_ONCE(*p4dp); ++ p4d = p4dp_get(p4dp); + BUILD_BUG_ON(p4d_leaf(p4d)); + + if (!p4d_present(p4d) || p4d_bad(p4d)) +@@ -3301,7 +3301,7 @@ static int gup_fast_pud_range(p4d_t *p4d + + pudp = pud_offset_lockless(p4dp, p4d, addr); + do { +- pud_t pud = READ_ONCE(*pudp); ++ pud_t pud = pudp_get(pudp); + + next = pud_addr_end(addr, end); + if (unlikely(!pud_present(pud))) +@@ -3327,7 +3327,7 @@ static int gup_fast_p4d_range(pgd_t *pgd + + p4dp = p4d_offset_lockless(pgdp, pgd, addr); + do { +- p4d_t p4d = READ_ONCE(*p4dp); ++ p4d_t p4d = p4dp_get(p4dp); + + next = p4d_addr_end(addr, end); + if (!p4d_present(p4d)) +@@ -3349,7 +3349,7 @@ static void gup_fast_pgd_range(unsigned + + pgdp = pgd_offset(current->mm, addr); + do { +- pgd_t pgd = READ_ONCE(*pgdp); ++ pgd_t pgd = pgdp_get(pgdp); + + next = pgd_addr_end(addr, end); + if (pgd_none(pgd)) +--- a/mm/hmm.c ++++ b/mm/hmm.c +@@ -423,7 +423,7 @@ static int hmm_vma_walk_pud(pud_t *pudp, + /* Normally we don't want to split the huge page */ + walk->action = ACTION_CONTINUE; + +- pud = READ_ONCE(*pudp); ++ pud = pudp_get(pudp); + if (!pud_present(pud)) { + spin_unlock(ptl); + return hmm_vma_walk_hole(start, end, -1, walk); +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -6451,12 +6451,12 @@ retry: + goto out; + + p4dp = p4d_offset(pgdp, address); +- p4d = READ_ONCE(*p4dp); ++ p4d = p4dp_get(p4dp); + if (p4d_none(p4d) || unlikely(p4d_bad(p4d))) + goto out; + + pudp = pud_offset(p4dp, address); +- pud = READ_ONCE(*pudp); ++ pud = pudp_get(pudp); + if (pud_none(pud)) + goto out; + if (pud_leaf(pud)) { +--- a/mm/mprotect.c ++++ b/mm/mprotect.c +@@ -447,7 +447,7 @@ again: + break; + } + +- pud = READ_ONCE(*pudp); ++ pud = pudp_get(pudp); + if (pud_none(pud)) + continue; + +--- a/mm/sparse-vmemmap.c ++++ b/mm/sparse-vmemmap.c +@@ -337,7 +337,7 @@ int __meminit vmemmap_populate_hugepages + return -ENOMEM; + + pmd = pmd_offset(pud, addr); +- if (pmd_none(READ_ONCE(*pmd))) { ++ if (pmd_none(pmdp_get(pmd))) { + void *p; + + p = vmemmap_alloc_block_buf(PMD_SIZE, node, altmap); +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -3631,7 +3631,7 @@ static int walk_pud_range(p4d_t *p4d, un + pud = pud_offset(p4d, start & P4D_MASK); + restart: + for (i = pud_index(start), addr = start; addr != end; i++, addr = next) { +- pud_t val = READ_ONCE(pud[i]); ++ pud_t val = pudp_get(pud + i); + + next = pud_addr_end(addr, end); + diff --git a/queue-6.12/mptcp-fix-lock-class-name-family-in-pm_nl_create_listen_socket.patch b/queue-6.12/mptcp-fix-lock-class-name-family-in-pm_nl_create_listen_socket.patch new file mode 100644 index 0000000000..bbbafe61e6 --- /dev/null +++ b/queue-6.12/mptcp-fix-lock-class-name-family-in-pm_nl_create_listen_socket.patch @@ -0,0 +1,41 @@ +From stable+bounces-233080-greg=kroah.com@vger.kernel.org Thu Apr 2 19:35:28 2026 +From: "Matthieu Baerts (NGI0)" +Date: Thu, 2 Apr 2026 19:33:10 +0200 +Subject: MPTCP: fix lock class name family in pm_nl_create_listen_socket +To: stable@vger.kernel.org, gregkh@linuxfoundation.org +Cc: MPTCP Upstream , Li Xiasong , "Matthieu Baerts (NGI0)" , Jakub Kicinski +Message-ID: <20260402173309.3282169-2-matttbe@kernel.org> + +From: Li Xiasong + +commit 7ab4a7c5d969642782b8a5b608da0dd02aa9f229 upstream. + +In mptcp_pm_nl_create_listen_socket(), use entry->addr.family +instead of sk->sk_family for lock class setup. The 'sk' parameter +is a netlink socket, not the MPTCP subflow socket being created. + +Fixes: cee4034a3db1 ("mptcp: fix lockdep false positive in mptcp_pm_nl_create_listen_socket()") +Signed-off-by: Li Xiasong +Reviewed-by: Matthieu Baerts (NGI0) +Link: https://patch.msgid.link/20260319112159.3118874-1-lixiasong1@huawei.com +Signed-off-by: Jakub Kicinski +[ Conflict in pm_kernel.c, because commit 8617e85e04bd ("mptcp: pm: + split in-kernel PM specific code") is not in this version, and moves + code from pm_netlink.c to pm_kernel.c. ] +Signed-off-by: Matthieu Baerts (NGI0) +Signed-off-by: Greg Kroah-Hartman +--- + net/mptcp/pm_netlink.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/net/mptcp/pm_netlink.c ++++ b/net/mptcp/pm_netlink.c +@@ -1186,7 +1186,7 @@ static struct lock_class_key mptcp_keys[ + static int mptcp_pm_nl_create_listen_socket(struct sock *sk, + struct mptcp_pm_addr_entry *entry) + { +- bool is_ipv6 = sk->sk_family == AF_INET6; ++ bool is_ipv6 = entry->addr.family == AF_INET6; + int addrlen = sizeof(struct sockaddr_in); + struct sockaddr_storage addr; + struct sock *newsk, *ssk; diff --git a/queue-6.12/net-correctly-handle-tunneled-traffic-on-ipv6_csum-gso-fallback.patch b/queue-6.12/net-correctly-handle-tunneled-traffic-on-ipv6_csum-gso-fallback.patch new file mode 100644 index 0000000000..5097779bba --- /dev/null +++ b/queue-6.12/net-correctly-handle-tunneled-traffic-on-ipv6_csum-gso-fallback.patch @@ -0,0 +1,77 @@ +From stable+bounces-232529-greg=kroah.com@vger.kernel.org Tue Mar 31 19:48:09 2026 +From: Sasha Levin +Date: Tue, 31 Mar 2026 13:09:35 -0400 +Subject: net: correctly handle tunneled traffic on IPV6_CSUM GSO fallback +To: stable@vger.kernel.org +Cc: Willem de Bruijn , Tangxin Xie , Paolo Abeni , Sasha Levin +Message-ID: <20260331170935.2813592-1-sashal@kernel.org> + +From: Willem de Bruijn + +[ Upstream commit c4336a07eb6b2526dc2b62928b5104b41a7f81f5 ] + +NETIF_F_IPV6_CSUM only advertises support for checksum offload of +packets without IPv6 extension headers. Packets with extension +headers must fall back onto software checksumming. Since TSO +depends on checksum offload, those must revert to GSO. + +The below commit introduces that fallback. It always checks +network header length. For tunneled packets, the inner header length +must be checked instead. Extend the check accordingly. + +A special case is tunneled packets without inner IP protocol. Such as +RFC 6951 SCTP in UDP. Those are not standard IPv6 followed by +transport header either, so also must revert to the software GSO path. + +Cc: stable@vger.kernel.org +Fixes: 864e3396976e ("net: gso: Forbid IPv6 TSO with extensions on devices with only IPV6_CSUM") +Reported-by: Tangxin Xie +Closes: https://lore.kernel.org/netdev/0414e7e2-9a1c-4d7c-a99d-b9039cf68f40@yeah.net/ +Suggested-by: Paolo Abeni +Signed-off-by: Willem de Bruijn +Link: https://patch.msgid.link/20260320190148.2409107-1-willemdebruijn.kernel@gmail.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/core/dev.c | 22 +++++++++++++++++----- + 1 file changed, 17 insertions(+), 5 deletions(-) + +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -3579,6 +3579,22 @@ static netdev_features_t dflt_features_c + return vlan_features_check(skb, features); + } + ++static bool skb_gso_has_extension_hdr(const struct sk_buff *skb) ++{ ++ if (!skb->encapsulation) ++ return ((skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6 || ++ (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 && ++ vlan_get_protocol(skb) == htons(ETH_P_IPV6))) && ++ skb_transport_header_was_set(skb) && ++ skb_network_header_len(skb) != sizeof(struct ipv6hdr)); ++ else ++ return (!skb_inner_network_header_was_set(skb) || ++ ((skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6 || ++ (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 && ++ inner_ip_hdr(skb)->version == 6)) && ++ skb_inner_network_header_len(skb) != sizeof(struct ipv6hdr))); ++} ++ + static netdev_features_t gso_features_check(const struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +@@ -3625,11 +3641,7 @@ static netdev_features_t gso_features_ch + * so neither does TSO that depends on it. + */ + if (features & NETIF_F_IPV6_CSUM && +- (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6 || +- (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 && +- vlan_get_protocol(skb) == htons(ETH_P_IPV6))) && +- skb_transport_header_was_set(skb) && +- skb_network_header_len(skb) != sizeof(struct ipv6hdr) && ++ skb_gso_has_extension_hdr(skb) && + !ipv6_has_hopopt_jumbo(skb)) + features &= ~(NETIF_F_IPV6_CSUM | NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4); + diff --git a/queue-6.12/net-mana-fix-use-after-free-in-add_adev-error-path.patch b/queue-6.12/net-mana-fix-use-after-free-in-add_adev-error-path.patch new file mode 100644 index 0000000000..ad94ce7cad --- /dev/null +++ b/queue-6.12/net-mana-fix-use-after-free-in-add_adev-error-path.patch @@ -0,0 +1,65 @@ +From stable+bounces-232527-greg=kroah.com@vger.kernel.org Tue Mar 31 19:43:51 2026 +From: Sasha Levin +Date: Tue, 31 Mar 2026 13:09:32 -0400 +Subject: net: mana: fix use-after-free in add_adev() error path +To: stable@vger.kernel.org +Cc: Guangshuo Li , Long Li , Jakub Kicinski , Sasha Levin +Message-ID: <20260331170932.2813528-1-sashal@kernel.org> + +From: Guangshuo Li + +[ Upstream commit c4ea7d8907cf72b259bf70bd8c2e791e1c4ff70f ] + +If auxiliary_device_add() fails, add_adev() jumps to add_fail and calls +auxiliary_device_uninit(adev). + +The auxiliary device has its release callback set to adev_release(), +which frees the containing struct mana_adev. Since adev is embedded in +struct mana_adev, the subsequent fall-through to init_fail and access +to adev->id may result in a use-after-free. + +Fix this by saving the allocated auxiliary device id in a local +variable before calling auxiliary_device_add(), and use that saved id +in the cleanup path after auxiliary_device_uninit(). + +Fixes: a69839d4327d ("net: mana: Add support for auxiliary device") +Cc: stable@vger.kernel.org +Reviewed-by: Long Li +Signed-off-by: Guangshuo Li +Link: https://patch.msgid.link/20260323165730.945365-1-lgs201920130244@gmail.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/ethernet/microsoft/mana/mana_en.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/microsoft/mana/mana_en.c ++++ b/drivers/net/ethernet/microsoft/mana/mana_en.c +@@ -2823,6 +2823,7 @@ static int add_adev(struct gdma_dev *gd) + struct auxiliary_device *adev; + struct mana_adev *madev; + int ret; ++ int id; + + madev = kzalloc(sizeof(*madev), GFP_KERNEL); + if (!madev) +@@ -2832,7 +2833,8 @@ static int add_adev(struct gdma_dev *gd) + ret = mana_adev_idx_alloc(); + if (ret < 0) + goto idx_fail; +- adev->id = ret; ++ id = ret; ++ adev->id = id; + + adev->name = "rdma"; + adev->dev.parent = gd->gdma_context->dev; +@@ -2856,7 +2858,7 @@ add_fail: + auxiliary_device_uninit(adev); + + init_fail: +- mana_adev_idx_free(adev->id); ++ mana_adev_idx_free(id); + + idx_fail: + kfree(madev); diff --git a/queue-6.12/s390-cpum_sf-cap-sampling-rate-to-prevent-lsctl-exception.patch b/queue-6.12/s390-cpum_sf-cap-sampling-rate-to-prevent-lsctl-exception.patch new file mode 100644 index 0000000000..13d03b5735 --- /dev/null +++ b/queue-6.12/s390-cpum_sf-cap-sampling-rate-to-prevent-lsctl-exception.patch @@ -0,0 +1,61 @@ +From stable+bounces-233842-greg=kroah.com@vger.kernel.org Wed Apr 8 13:06:45 2026 +From: Sasha Levin +Date: Wed, 8 Apr 2026 07:06:37 -0400 +Subject: s390/cpum_sf: Cap sampling rate to prevent lsctl exception +To: stable@vger.kernel.org +Cc: Thomas Richter , Sumanth Korikkar , Hendrik Brueckner , Vasily Gorbik , Sasha Levin +Message-ID: <20260408110637.978601-2-sashal@kernel.org> + +From: Thomas Richter + +[ Upstream commit 57ad0d4a00f5d3e80f33ba2da8d560c73d83dc22 ] + +commit fcc43a7e294f ("s390/configs: Set HZ=1000") changed the interrupt +frequency of the system. On machines with heavy load and many perf event +overflows, this might lead to an exception. Dmesg displays these entries: + [112.242542] cpum_sf: Loading sampling controls failed: op 1 err -22 +One line per CPU online. + +The root cause is the CPU Measurement sampling facility overflow +adjustment. Whenever an overflow (too much samples per tick) occurs, the +sampling rate is adjusted and increased. This was done without observing +the maximum sampling rate limit. When the current sampling interval is +higher than the maximum sampling rate limit, the lsctl instruction raises +an exception. The error messages is the result of such an exception. +Observe the upper limit when the new sampling rate is recalculated. + +Cc: stable@vger.kernel.org +Fixes: 39d4a501a9ef ("s390/cpum_sf: Adjust sampling interval to avoid hitting sample limits") +Signed-off-by: Thomas Richter +Reviewed-by: Sumanth Korikkar +Reviewed-by: Hendrik Brueckner +Signed-off-by: Vasily Gorbik +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + arch/s390/kernel/perf_cpum_sf.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/arch/s390/kernel/perf_cpum_sf.c ++++ b/arch/s390/kernel/perf_cpum_sf.c +@@ -1188,6 +1188,7 @@ static void hw_collect_samples(struct pe + static void hw_perf_event_update(struct perf_event *event, int flush_all) + { + unsigned long long event_overflow, sampl_overflow, num_sdb; ++ struct cpu_hw_sf *cpuhw = this_cpu_ptr(&cpu_hw_sf); + struct hw_perf_event *hwc = &event->hw; + union hws_trailer_header prev, new; + struct hws_trailer_entry *te; +@@ -1267,8 +1268,11 @@ static void hw_perf_event_update(struct + * are dropped. + * Slightly increase the interval to avoid hitting this limit. + */ +- if (event_overflow) ++ if (event_overflow) { + SAMPL_RATE(hwc) += DIV_ROUND_UP(SAMPL_RATE(hwc), 10); ++ if (SAMPL_RATE(hwc) > cpuhw->qsi.max_sampl_rate) ++ SAMPL_RATE(hwc) = cpuhw->qsi.max_sampl_rate; ++ } + } + + static inline unsigned long aux_sdb_index(struct aux_buffer *aux, diff --git a/queue-6.12/s390-perf_cpum_sf-convert-to-use-try_cmpxchg128.patch b/queue-6.12/s390-perf_cpum_sf-convert-to-use-try_cmpxchg128.patch new file mode 100644 index 0000000000..f643a81ca0 --- /dev/null +++ b/queue-6.12/s390-perf_cpum_sf-convert-to-use-try_cmpxchg128.patch @@ -0,0 +1,110 @@ +From stable+bounces-233841-greg=kroah.com@vger.kernel.org Wed Apr 8 13:06:44 2026 +From: Sasha Levin +Date: Wed, 8 Apr 2026 07:06:36 -0400 +Subject: s390/perf_cpum_sf: Convert to use try_cmpxchg128() +To: stable@vger.kernel.org +Cc: Heiko Carstens , Juergen Christ , Sasha Levin +Message-ID: <20260408110637.978601-1-sashal@kernel.org> + +From: Heiko Carstens + +[ Upstream commit e449399ffd295a1202b74a258227193454ef333f ] + +Convert cmpxchg128() usages to try_cmpxchg128() in order to generate +slightly better code. + +Reviewed-by: Juergen Christ +Signed-off-by: Heiko Carstens +Stable-dep-of: 57ad0d4a00f5 ("s390/cpum_sf: Cap sampling rate to prevent lsctl exception") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + arch/s390/kernel/perf_cpum_sf.c | 24 +++++++++--------------- + 1 file changed, 9 insertions(+), 15 deletions(-) + +--- a/arch/s390/kernel/perf_cpum_sf.c ++++ b/arch/s390/kernel/perf_cpum_sf.c +@@ -1188,8 +1188,8 @@ static void hw_collect_samples(struct pe + static void hw_perf_event_update(struct perf_event *event, int flush_all) + { + unsigned long long event_overflow, sampl_overflow, num_sdb; +- union hws_trailer_header old, prev, new; + struct hw_perf_event *hwc = &event->hw; ++ union hws_trailer_header prev, new; + struct hws_trailer_entry *te; + unsigned long *sdbt, sdb; + int done; +@@ -1233,13 +1233,11 @@ static void hw_perf_event_update(struct + /* Reset trailer (using compare-double-and-swap) */ + prev.val = READ_ONCE_ALIGNED_128(te->header.val); + do { +- old.val = prev.val; + new.val = prev.val; + new.f = 0; + new.a = 1; + new.overflow = 0; +- prev.val = cmpxchg128(&te->header.val, old.val, new.val); +- } while (prev.val != old.val); ++ } while (!try_cmpxchg128(&te->header.val, &prev.val, new.val)); + + /* Advance to next sample-data-block */ + sdbt++; +@@ -1405,16 +1403,15 @@ static int aux_output_begin(struct perf_ + static bool aux_set_alert(struct aux_buffer *aux, unsigned long alert_index, + unsigned long long *overflow) + { +- union hws_trailer_header old, prev, new; ++ union hws_trailer_header prev, new; + struct hws_trailer_entry *te; + + te = aux_sdb_trailer(aux, alert_index); + prev.val = READ_ONCE_ALIGNED_128(te->header.val); + do { +- old.val = prev.val; + new.val = prev.val; +- *overflow = old.overflow; +- if (old.f) { ++ *overflow = prev.overflow; ++ if (prev.f) { + /* + * SDB is already set by hardware. + * Abort and try to set somewhere +@@ -1424,8 +1421,7 @@ static bool aux_set_alert(struct aux_buf + } + new.a = 1; + new.overflow = 0; +- prev.val = cmpxchg128(&te->header.val, old.val, new.val); +- } while (prev.val != old.val); ++ } while (!try_cmpxchg128(&te->header.val, &prev.val, new.val)); + return true; + } + +@@ -1454,7 +1450,7 @@ static bool aux_set_alert(struct aux_buf + static bool aux_reset_buffer(struct aux_buffer *aux, unsigned long range, + unsigned long long *overflow) + { +- union hws_trailer_header old, prev, new; ++ union hws_trailer_header prev, new; + unsigned long i, range_scan, idx; + unsigned long long orig_overflow; + struct hws_trailer_entry *te; +@@ -1486,17 +1482,15 @@ static bool aux_reset_buffer(struct aux_ + te = aux_sdb_trailer(aux, idx); + prev.val = READ_ONCE_ALIGNED_128(te->header.val); + do { +- old.val = prev.val; + new.val = prev.val; +- orig_overflow = old.overflow; ++ orig_overflow = prev.overflow; + new.f = 0; + new.overflow = 0; + if (idx == aux->alert_mark) + new.a = 1; + else + new.a = 0; +- prev.val = cmpxchg128(&te->header.val, old.val, new.val); +- } while (prev.val != old.val); ++ } while (!try_cmpxchg128(&te->header.val, &prev.val, new.val)); + *overflow += orig_overflow; + } + diff --git a/queue-6.12/scsi-target-tcm_loop-drain-commands-in-target_reset-handler.patch b/queue-6.12/scsi-target-tcm_loop-drain-commands-in-target_reset-handler.patch new file mode 100644 index 0000000000..bce0a49d74 --- /dev/null +++ b/queue-6.12/scsi-target-tcm_loop-drain-commands-in-target_reset-handler.patch @@ -0,0 +1,147 @@ +From stable+bounces-231442-greg=kroah.com@vger.kernel.org Tue Mar 31 16:55:48 2026 +From: Sasha Levin +Date: Tue, 31 Mar 2026 10:53:49 -0400 +Subject: scsi: target: tcm_loop: Drain commands in target_reset handler +To: stable@vger.kernel.org +Cc: Josef Bacik , "Martin K. Petersen" , Sasha Levin +Message-ID: <20260331145349.2557768-1-sashal@kernel.org> + +From: Josef Bacik + +[ Upstream commit 1333eee56cdf3f0cf67c6ab4114c2c9e0a952026 ] + +tcm_loop_target_reset() violates the SCSI EH contract: it returns SUCCESS +without draining any in-flight commands. The SCSI EH documentation +(scsi_eh.rst) requires that when a reset handler returns SUCCESS the driver +has made lower layers "forget about timed out scmds" and is ready for new +commands. Every other SCSI LLD (virtio_scsi, mpt3sas, ipr, scsi_debug, +mpi3mr) enforces this by draining or completing outstanding commands before +returning SUCCESS. + +Because tcm_loop_target_reset() doesn't drain, the SCSI EH reuses in-flight +scsi_cmnd structures for recovery commands (e.g. TUR) while the target core +still has async completion work queued for the old se_cmd. The memset in +queuecommand zeroes se_lun and lun_ref_active, causing +transport_lun_remove_cmd() to skip its percpu_ref_put(). The leaked LUN +reference prevents transport_clear_lun_ref() from completing, hanging +configfs LUN unlink forever in D-state: + + INFO: task rm:264 blocked for more than 122 seconds. + rm D 0 264 258 0x00004000 + Call Trace: + __schedule+0x3d0/0x8e0 + schedule+0x36/0xf0 + transport_clear_lun_ref+0x78/0x90 [target_core_mod] + core_tpg_remove_lun+0x28/0xb0 [target_core_mod] + target_fabric_port_unlink+0x50/0x60 [target_core_mod] + configfs_unlink+0x156/0x1f0 [configfs] + vfs_unlink+0x109/0x290 + do_unlinkat+0x1d5/0x2d0 + +Fix this by making tcm_loop_target_reset() actually drain commands: + + 1. Issue TMR_LUN_RESET via tcm_loop_issue_tmr() to drain all commands that + the target core knows about (those not yet CMD_T_COMPLETE). + + 2. Use blk_mq_tagset_busy_iter() to iterate all started requests and + flush_work() on each se_cmd — this drains any deferred completion work + for commands that already had CMD_T_COMPLETE set before the TMR (which + the TMR skips via __target_check_io_state()). This is the same pattern + used by mpi3mr, scsi_debug, and libsas to drain outstanding commands + during reset. + +Fixes: e0eb5d38b732 ("scsi: target: tcm_loop: Use block cmd allocator for se_cmds") +Cc: stable@vger.kernel.org +Assisted-by: Claude:claude-opus-4-6 +Signed-off-by: Josef Bacik +Link: https://patch.msgid.link/27011aa34c8f6b1b94d2e3cf5655b6d037f53428.1773706803.git.josef@toxicpanda.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/target/loopback/tcm_loop.c | 52 ++++++++++++++++++++++++++++++++----- + 1 file changed, 46 insertions(+), 6 deletions(-) + +--- a/drivers/target/loopback/tcm_loop.c ++++ b/drivers/target/loopback/tcm_loop.c +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -267,15 +268,27 @@ static int tcm_loop_device_reset(struct + return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; + } + ++static bool tcm_loop_flush_work_iter(struct request *rq, void *data) ++{ ++ struct scsi_cmnd *sc = blk_mq_rq_to_pdu(rq); ++ struct tcm_loop_cmd *tl_cmd = scsi_cmd_priv(sc); ++ struct se_cmd *se_cmd = &tl_cmd->tl_se_cmd; ++ ++ flush_work(&se_cmd->work); ++ return true; ++} ++ + static int tcm_loop_target_reset(struct scsi_cmnd *sc) + { + struct tcm_loop_hba *tl_hba; + struct tcm_loop_tpg *tl_tpg; ++ struct Scsi_Host *sh = sc->device->host; ++ int ret; + + /* + * Locate the tcm_loop_hba_t pointer + */ +- tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); ++ tl_hba = *(struct tcm_loop_hba **)shost_priv(sh); + if (!tl_hba) { + pr_err("Unable to perform device reset without active I_T Nexus\n"); + return FAILED; +@@ -284,11 +297,38 @@ static int tcm_loop_target_reset(struct + * Locate the tl_tpg pointer from TargetID in sc->device->id + */ + tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; +- if (tl_tpg) { +- tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE; +- return SUCCESS; +- } +- return FAILED; ++ if (!tl_tpg) ++ return FAILED; ++ ++ /* ++ * Issue a LUN_RESET to drain all commands that the target core ++ * knows about. This handles commands not yet marked CMD_T_COMPLETE. ++ */ ++ ret = tcm_loop_issue_tmr(tl_tpg, sc->device->lun, 0, TMR_LUN_RESET); ++ if (ret != TMR_FUNCTION_COMPLETE) ++ return FAILED; ++ ++ /* ++ * Flush any deferred target core completion work that may still be ++ * queued. Commands that already had CMD_T_COMPLETE set before the TMR ++ * are skipped by the TMR drain, but their async completion work ++ * (transport_lun_remove_cmd → percpu_ref_put, release_cmd → scsi_done) ++ * may still be pending in target_completion_wq. ++ * ++ * The SCSI EH will reuse in-flight scsi_cmnd structures for recovery ++ * commands (e.g. TUR) immediately after this handler returns SUCCESS — ++ * if deferred work is still pending, the memset in queuecommand would ++ * zero the se_cmd while the work accesses it, leaking the LUN ++ * percpu_ref and hanging configfs unlink forever. ++ * ++ * Use blk_mq_tagset_busy_iter() to find all started requests and ++ * flush_work() on each — the same pattern used by mpi3mr, scsi_debug, ++ * and other SCSI drivers to drain outstanding commands during reset. ++ */ ++ blk_mq_tagset_busy_iter(&sh->tag_set, tcm_loop_flush_work_iter, NULL); ++ ++ tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE; ++ return SUCCESS; + } + + static const struct scsi_host_template tcm_loop_driver_template = { diff --git a/queue-6.12/selftests-bpf-test-cross-sign-64bits-range-refinement.patch b/queue-6.12/selftests-bpf-test-cross-sign-64bits-range-refinement.patch new file mode 100644 index 0000000000..3873edcfc7 --- /dev/null +++ b/queue-6.12/selftests-bpf-test-cross-sign-64bits-range-refinement.patch @@ -0,0 +1,173 @@ +From stable+bounces-233270-greg=kroah.com@vger.kernel.org Sat Apr 4 10:15:44 2026 +From: Paul Chaignon +Date: Sat, 4 Apr 2026 10:11:42 +0200 +Subject: selftests/bpf: Test cross-sign 64bits range refinement +To: stable@vger.kernel.org +Cc: Greg Kroah-Hartman , Eduard Zingerman , Shung-Hsi Yu , Alexei Starovoitov +Message-ID: +Content-Disposition: inline + +From: Paul Chaignon + +[ Upstream commit 26e5e346a52c796190e63af1c2a80a417fda261a ] + +This patch adds coverage for the new cross-sign 64bits range refinement +logic. The three tests cover the cases when the u64 and s64 ranges +overlap (1) in the negative portion of s64, (2) in the positive portion +of s64, and (3) in both portions. + +The first test is a simplified version of a BPF program generated by +syzkaller that caused an invariant violation [1]. It looks like +syzkaller could not extract the reproducer itself (and therefore didn't +report it to the mailing list), but I was able to extract it from the +console logs of a crash. + +The principle is similar to the invariant violation described in +commit 6279846b9b25 ("bpf: Forget ranges when refining tnum after +JSET"): the verifier walks a dead branch, uses the condition to refine +ranges, and ends up with inconsistent ranges. In this case, the dead +branch is when we fallthrough on both jumps. The new refinement logic +improves the bounds such that the second jump is properly detected as +always-taken and the verifier doesn't end up walking a dead branch. + +The second and third tests are inspired by the first, but rely on +condition jumps to prepare the bounds instead of ALU instructions. An +R10 write is used to trigger a verifier error when the bounds can't be +refined. + +Link: https://syzkaller.appspot.com/bug?extid=c711ce17dd78e5d4fdcf [1] +Acked-by: Eduard Zingerman +Signed-off-by: Paul Chaignon +Link: https://lore.kernel.org/r/a0e17b00dab8dabcfa6f8384e7e151186efedfdd.1753695655.git.paul.chaignon@gmail.com +Signed-off-by: Alexei Starovoitov +Signed-off-by: Paul Chaignon +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/selftests/bpf/progs/verifier_bounds.c | 118 ++++++++++++++++++++ + 1 file changed, 118 insertions(+) + +--- a/tools/testing/selftests/bpf/progs/verifier_bounds.c ++++ b/tools/testing/selftests/bpf/progs/verifier_bounds.c +@@ -1200,4 +1200,122 @@ l0_%=: r0 = 0; \ + : __clobber_all); + } + ++/* This test covers the bounds deduction on 64bits when the s64 and u64 ranges ++ * overlap on the negative side. At instruction 7, the ranges look as follows: ++ * ++ * 0 umin=0xfffffcf1 umax=0xff..ff6e U64_MAX ++ * | [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] | ++ * |----------------------------|------------------------------| ++ * |xxxxxxxxxx] [xxxxxxxxxxxx| ++ * 0 smax=0xeffffeee smin=-655 -1 ++ * ++ * We should therefore deduce the following new bounds: ++ * ++ * 0 u64=[0xff..ffd71;0xff..ff6e] U64_MAX ++ * | [xxx] | ++ * |----------------------------|------------------------------| ++ * | [xxx] | ++ * 0 s64=[-655;-146] -1 ++ * ++ * Without the deduction cross sign boundary, we end up with an invariant ++ * violation error. ++ */ ++SEC("socket") ++__description("bounds deduction cross sign boundary, negative overlap") ++__success __log_level(2) __flag(BPF_F_TEST_REG_INVARIANTS) ++__msg("7: (1f) r0 -= r6 {{.*}} R0=scalar(smin=-655,smax=smax32=-146,umin=0xfffffffffffffd71,umax=0xffffffffffffff6e,smin32=-783,umin32=0xfffffcf1,umax32=0xffffff6e,var_off=(0xfffffffffffffc00; 0x3ff))") ++__retval(0) ++__naked void bounds_deduct_negative_overlap(void) ++{ ++ asm volatile(" \ ++ call %[bpf_get_prandom_u32]; \ ++ w3 = w0; \ ++ w6 = (s8)w0; \ ++ r0 = (s8)r0; \ ++ if w6 >= 0xf0000000 goto l0_%=; \ ++ r0 += r6; \ ++ r6 += 400; \ ++ r0 -= r6; \ ++ if r3 < r0 goto l0_%=; \ ++l0_%=: r0 = 0; \ ++ exit; \ ++" : ++ : __imm(bpf_get_prandom_u32) ++ : __clobber_all); ++} ++ ++/* This test covers the bounds deduction on 64bits when the s64 and u64 ranges ++ * overlap on the positive side. At instruction 3, the ranges look as follows: ++ * ++ * 0 umin=0 umax=0xffffffffffffff00 U64_MAX ++ * [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] | ++ * |----------------------------|------------------------------| ++ * |xxxxxxxx] [xxxxxxxx| ++ * 0 smax=127 smin=-128 -1 ++ * ++ * We should therefore deduce the following new bounds: ++ * ++ * 0 u64=[0;127] U64_MAX ++ * [xxxxxxxx] | ++ * |----------------------------|------------------------------| ++ * [xxxxxxxx] | ++ * 0 s64=[0;127] -1 ++ * ++ * Without the deduction cross sign boundary, the program is rejected due to ++ * the frame pointer write. ++ */ ++SEC("socket") ++__description("bounds deduction cross sign boundary, positive overlap") ++__success __log_level(2) __flag(BPF_F_TEST_REG_INVARIANTS) ++__msg("3: (2d) if r0 > r1 {{.*}} R0_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=127,var_off=(0x0; 0x7f))") ++__retval(0) ++__naked void bounds_deduct_positive_overlap(void) ++{ ++ asm volatile(" \ ++ call %[bpf_get_prandom_u32]; \ ++ r0 = (s8)r0; \ ++ r1 = 0xffffffffffffff00; \ ++ if r0 > r1 goto l0_%=; \ ++ if r0 < 128 goto l0_%=; \ ++ r10 = 0; \ ++l0_%=: r0 = 0; \ ++ exit; \ ++" : ++ : __imm(bpf_get_prandom_u32) ++ : __clobber_all); ++} ++ ++/* This test is the same as above, but the s64 and u64 ranges overlap in two ++ * places. At instruction 3, the ranges look as follows: ++ * ++ * 0 umin=0 umax=0xffffffffffffff80 U64_MAX ++ * [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] | ++ * |----------------------------|------------------------------| ++ * |xxxxxxxx] [xxxxxxxx| ++ * 0 smax=127 smin=-128 -1 ++ * ++ * 0xffffffffffffff80 = (u64)-128. We therefore can't deduce anything new and ++ * the program should fail due to the frame pointer write. ++ */ ++SEC("socket") ++__description("bounds deduction cross sign boundary, two overlaps") ++__failure __flag(BPF_F_TEST_REG_INVARIANTS) ++__msg("3: (2d) if r0 > r1 {{.*}} R0_w=scalar(smin=smin32=-128,smax=smax32=127,umax=0xffffffffffffff80)") ++__msg("frame pointer is read only") ++__naked void bounds_deduct_two_overlaps(void) ++{ ++ asm volatile(" \ ++ call %[bpf_get_prandom_u32]; \ ++ r0 = (s8)r0; \ ++ r1 = 0xffffffffffffff80; \ ++ if r0 > r1 goto l0_%=; \ ++ if r0 < 128 goto l0_%=; \ ++ r10 = 0; \ ++l0_%=: r0 = 0; \ ++ exit; \ ++" : ++ : __imm(bpf_get_prandom_u32) ++ : __clobber_all); ++} ++ + char _license[] SEC("license") = "GPL"; diff --git a/queue-6.12/selftests-bpf-test-invariants-on-jslt-crossing-sign.patch b/queue-6.12/selftests-bpf-test-invariants-on-jslt-crossing-sign.patch new file mode 100644 index 0000000000..1a2ac6a63c --- /dev/null +++ b/queue-6.12/selftests-bpf-test-invariants-on-jslt-crossing-sign.patch @@ -0,0 +1,41 @@ +From stable+bounces-233271-greg=kroah.com@vger.kernel.org Sat Apr 4 10:15:48 2026 +From: Paul Chaignon +Date: Sat, 4 Apr 2026 10:12:32 +0200 +Subject: selftests/bpf: Test invariants on JSLT crossing sign +To: stable@vger.kernel.org +Cc: Greg Kroah-Hartman , Eduard Zingerman , Shung-Hsi Yu , Alexei Starovoitov +Message-ID: <8391b533f7f9876aadc8ae1bf9915516db575cd9.1775289842.git.paul.chaignon@gmail.com> +Content-Disposition: inline + +From: Paul Chaignon + +[ Upstream commit f96841bbf4a1ee4ed0336ba192a01278fdea6383 ] + +The improvement of the u64/s64 range refinement fixed the invariant +violation that was happening on this test for BPF_JSLT when crossing the +sign boundary. + +After this patch, we have one test remaining with a known invariant +violation. It's the same test as fixed here but for 32 bits ranges. + +Acked-by: Eduard Zingerman +Signed-off-by: Paul Chaignon +Link: https://lore.kernel.org/r/ad046fb0016428f1a33c3b81617aabf31b51183f.1753695655.git.paul.chaignon@gmail.com +Signed-off-by: Alexei Starovoitov +Signed-off-by: Paul Chaignon +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/selftests/bpf/progs/verifier_bounds.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/tools/testing/selftests/bpf/progs/verifier_bounds.c ++++ b/tools/testing/selftests/bpf/progs/verifier_bounds.c +@@ -1028,7 +1028,7 @@ l0_%=: r0 = 0; \ + SEC("xdp") + __description("bound check with JMP_JSLT for crossing 64-bit signed boundary") + __success __retval(0) +-__flag(!BPF_F_TEST_REG_INVARIANTS) /* known invariants violation */ ++__flag(BPF_F_TEST_REG_INVARIANTS) + __naked void crossing_64_bit_signed_boundary_2(void) + { + asm volatile (" \ diff --git a/queue-6.12/selftests-bpf-test-refining-u32-s32-bounds-when-ranges-cross-min-max-boundary.patch b/queue-6.12/selftests-bpf-test-refining-u32-s32-bounds-when-ranges-cross-min-max-boundary.patch new file mode 100644 index 0000000000..fb13c95f18 --- /dev/null +++ b/queue-6.12/selftests-bpf-test-refining-u32-s32-bounds-when-ranges-cross-min-max-boundary.patch @@ -0,0 +1,92 @@ +From stable+bounces-233274-greg=kroah.com@vger.kernel.org Sat Apr 4 10:15:39 2026 +From: Paul Chaignon +Date: Sat, 4 Apr 2026 10:15:26 +0200 +Subject: selftests/bpf: test refining u32/s32 bounds when ranges cross min/max boundary +To: stable@vger.kernel.org +Cc: Greg Kroah-Hartman , Eduard Zingerman , Shung-Hsi Yu , Alexei Starovoitov , Emil Tsalapatis +Message-ID: +Content-Disposition: inline + +From: Eduard Zingerman + +[ Upstream commit f81fdfd16771e266753146bd83f6dd23515ebee9 ] + +Two test cases for signed/unsigned 32-bit bounds refinement +when s32 range crosses the sign boundary: +- s32 range [S32_MIN..1] overlapping with u32 range [3..U32_MAX], + s32 range tail before sign boundary overlaps with u32 range. +- s32 range [-3..5] overlapping with u32 range [0..S32_MIN+3], + s32 range head after the sign boundary overlaps with u32 range. + +This covers both branches added in the __reg32_deduce_bounds(). + +Also, crossing_32_bit_signed_boundary_2() no longer triggers invariant +violations. + +Reviewed-by: Emil Tsalapatis +Reviewed-by: Paul Chaignon +Acked-by: Shung-Hsi Yu +Signed-off-by: Eduard Zingerman +Link: https://lore.kernel.org/r/20260306-bpf-32-bit-range-overflow-v3-2-f7f67e060a6b@gmail.com +Signed-off-by: Alexei Starovoitov +Signed-off-by: Paul Chaignon +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/selftests/bpf/progs/verifier_bounds.c | 39 +++++++++++++++++++- + 1 file changed, 38 insertions(+), 1 deletion(-) + +--- a/tools/testing/selftests/bpf/progs/verifier_bounds.c ++++ b/tools/testing/selftests/bpf/progs/verifier_bounds.c +@@ -1110,7 +1110,7 @@ l0_%=: r0 = 0; \ + SEC("xdp") + __description("bound check with JMP32_JSLT for crossing 32-bit signed boundary") + __success __retval(0) +-__flag(!BPF_F_TEST_REG_INVARIANTS) /* known invariants violation */ ++__flag(BPF_F_TEST_REG_INVARIANTS) + __naked void crossing_32_bit_signed_boundary_2(void) + { + asm volatile (" \ +@@ -1316,6 +1316,43 @@ l0_%=: r0 = 0; \ + " : + : __imm(bpf_get_prandom_u32) + : __clobber_all); ++} ++ ++SEC("socket") ++__success ++__flag(BPF_F_TEST_REG_INVARIANTS) ++__naked void signed_unsigned_intersection32_case1(void *ctx) ++{ ++ asm volatile(" \ ++ call %[bpf_get_prandom_u32]; \ ++ w0 &= 0xffffffff; \ ++ if w0 < 0x3 goto 1f; /* on fall-through u32 range [3..U32_MAX] */ \ ++ if w0 s> 0x1 goto 1f; /* on fall-through s32 range [S32_MIN..1] */ \ ++ if w0 s< 0x0 goto 1f; /* range can be narrowed to [S32_MIN..-1] */ \ ++ r10 = 0; /* thus predicting the jump. */ \ ++1: exit; \ ++" : ++ : __imm(bpf_get_prandom_u32) ++ : __clobber_all); ++} ++ ++SEC("socket") ++__success ++__flag(BPF_F_TEST_REG_INVARIANTS) ++__naked void signed_unsigned_intersection32_case2(void *ctx) ++{ ++ asm volatile(" \ ++ call %[bpf_get_prandom_u32]; \ ++ w0 &= 0xffffffff; \ ++ if w0 > 0x80000003 goto 1f; /* on fall-through u32 range [0..S32_MIN+3] */ \ ++ if w0 s< -3 goto 1f; /* on fall-through s32 range [-3..S32_MAX] */ \ ++ if w0 s> 5 goto 1f; /* on fall-through s32 range [-3..5] */ \ ++ if w0 <= 5 goto 1f; /* range can be narrowed to [0..5] */ \ ++ r10 = 0; /* thus predicting the jump */ \ ++1: exit; \ ++" : ++ : __imm(bpf_get_prandom_u32) ++ : __clobber_all); + } + + char _license[] SEC("license") = "GPL"; diff --git a/queue-6.12/series b/queue-6.12/series index 7990d2c089..3f771ace74 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -214,3 +214,34 @@ usb-gadget-f_subset-fix-net_device-lifecycle-with-device_move.patch usb-gadget-f_rndis-fix-net_device-lifecycle-with-device_move.patch usb-gadget-f_hid-move-list-and-spinlock-inits-from-bind-to-alloc.patch usb-gadget-f_uac1_legacy-validate-control-request-size.patch +wifi-virt_wifi-remove-set_netdev_dev-to-avoid-use-after-free.patch +spi-cadence-qspi-fix-exec_mem_op-error-handling.patch +net-correctly-handle-tunneled-traffic-on-ipv6_csum-gso-fallback.patch +net-mana-fix-use-after-free-in-add_adev-error-path.patch +scsi-target-tcm_loop-drain-commands-in-target_reset-handler.patch +x86-fred-fix-early-boot-failures-on-sev-es-snp-guests.patch +mm-huge_memory-fix-folio-isn-t-locked-in-softleaf_to_folio.patch +mm-replace-read_once-with-standard-page-table-accessors.patch +mm-memory-fix-pmd-pud-checks-in-follow_pfnmap_start.patch +drm-amd-pm-disable-od_fan_curve-if-temp-or-pwm-range-invalid-for-smu-v13.patch +ext4-publish-jinode-after-initialization.patch +s390-perf_cpum_sf-convert-to-use-try_cmpxchg128.patch +s390-cpum_sf-cap-sampling-rate-to-prevent-lsctl-exception.patch +mptcp-fix-lock-class-name-family-in-pm_nl_create_listen_socket.patch +x86-cpu-amd-add-additional-fixed-rdseed-microcode-revisions.patch +drm-amd-amdgpu-decouple-aspm-with-pcie-dpm.patch +drm-amd-amdgpu-disable-aspm-in-some-situations.patch +drm-amd-display-disable-fastboot-on-dce-6-too.patch +drm-amd-display-reject-modes-with-too-high-pixel-clock-on-dce6-10.patch +drm-amd-display-keep-pll0-running-on-dce-6.0-and-6.4.patch +drm-amd-display-fix-dce-6.0-and-6.4-pll-programming.patch +drm-amd-display-adjust-dce-8-10-clock-don-t-overclock-by-15.patch +drm-amd-display-disable-scaling-on-dce6-for-now.patch +drm-amd-disable-aspm-on-si.patch +drm-amd-display-correct-logic-check-error-for-fastboot.patch +bpf-improve-bounds-when-s64-crosses-sign-boundary.patch +selftests-bpf-test-cross-sign-64bits-range-refinement.patch +selftests-bpf-test-invariants-on-jslt-crossing-sign.patch +bpf-add-third-round-of-bounds-deduction.patch +bpf-fix-u32-s32-bounds-when-ranges-cross-min-max-boundary.patch +selftests-bpf-test-refining-u32-s32-bounds-when-ranges-cross-min-max-boundary.patch diff --git a/queue-6.12/spi-cadence-qspi-fix-exec_mem_op-error-handling.patch b/queue-6.12/spi-cadence-qspi-fix-exec_mem_op-error-handling.patch new file mode 100644 index 0000000000..2826e313d7 --- /dev/null +++ b/queue-6.12/spi-cadence-qspi-fix-exec_mem_op-error-handling.patch @@ -0,0 +1,64 @@ +From 59e1be1278f064d7172b00473b7e0c453cb1ec52 Mon Sep 17 00:00:00 2001 +From: Emanuele Ghidoli +Date: Fri, 13 Mar 2026 14:52:31 +0100 +Subject: spi: cadence-qspi: Fix exec_mem_op error handling + +From: Emanuele Ghidoli + +commit 59e1be1278f064d7172b00473b7e0c453cb1ec52 upstream. + +cqspi_exec_mem_op() increments the runtime PM usage counter before all +refcount checks are performed. If one of these checks fails, the function +returns without dropping the PM reference. + +Move the pm_runtime_resume_and_get() call after the refcount checks so +that runtime PM is only acquired when the operation can proceed and +drop the inflight_ops refcount if the PM resume fails. + +Cc: stable@vger.kernel.org +Fixes: 7446284023e8 ("spi: cadence-quadspi: Implement refcount to handle unbind during busy") +Signed-off-by: Emanuele Ghidoli +Link: https://patch.msgid.link/20260313135236.46642-1-ghidoliemanuele@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Emanuele Ghidoli +Signed-off-by: Greg Kroah-Hartman +--- + drivers/spi/spi-cadence-quadspi.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +--- a/drivers/spi/spi-cadence-quadspi.c ++++ b/drivers/spi/spi-cadence-quadspi.c +@@ -1461,12 +1461,6 @@ static int cqspi_exec_mem_op(struct spi_ + if (refcount_read(&cqspi->inflight_ops) == 0) + return -ENODEV; + +- ret = pm_runtime_resume_and_get(dev); +- if (ret) { +- dev_err(&mem->spi->dev, "resume failed with %d\n", ret); +- return ret; +- } +- + if (!refcount_read(&cqspi->refcount)) + return -EBUSY; + +@@ -1478,6 +1472,12 @@ static int cqspi_exec_mem_op(struct spi_ + return -EBUSY; + } + ++ ret = pm_runtime_resume_and_get(dev); ++ if (ret) { ++ dev_err(&mem->spi->dev, "resume failed with %d\n", ret); ++ goto dec_inflight_refcount; ++ } ++ + ret = cqspi_mem_process(mem, op); + + pm_runtime_mark_last_busy(dev); +@@ -1486,6 +1486,7 @@ static int cqspi_exec_mem_op(struct spi_ + if (ret) + dev_err(&mem->spi->dev, "operation failed with %d\n", ret); + ++dec_inflight_refcount: + if (refcount_read(&cqspi->inflight_ops) > 1) + refcount_dec(&cqspi->inflight_ops); + diff --git a/queue-6.12/wifi-virt_wifi-remove-set_netdev_dev-to-avoid-use-after-free.patch b/queue-6.12/wifi-virt_wifi-remove-set_netdev_dev-to-avoid-use-after-free.patch new file mode 100644 index 0000000000..9eab462763 --- /dev/null +++ b/queue-6.12/wifi-virt_wifi-remove-set_netdev_dev-to-avoid-use-after-free.patch @@ -0,0 +1,101 @@ +From 789b06f9f39cdc7e895bdab2c034e39c41c8f8d6 Mon Sep 17 00:00:00 2001 +From: Alexander Popov +Date: Wed, 25 Mar 2026 01:46:02 +0300 +Subject: wifi: virt_wifi: remove SET_NETDEV_DEV to avoid use-after-free + +From: Alexander Popov + +commit 789b06f9f39cdc7e895bdab2c034e39c41c8f8d6 upstream. + +Currently we execute `SET_NETDEV_DEV(dev, &priv->lowerdev->dev)` for +the virt_wifi net devices. However, unregistering a virt_wifi device in +netdev_run_todo() can happen together with the device referenced by +SET_NETDEV_DEV(). + +It can result in use-after-free during the ethtool operations performed +on a virt_wifi device that is currently being unregistered. Such a net +device can have the `dev.parent` field pointing to the freed memory, +but ethnl_ops_begin() calls `pm_runtime_get_sync(dev->dev.parent)`. + +Let's remove SET_NETDEV_DEV for virt_wifi to avoid bugs like this: + + ================================================================== + BUG: KASAN: slab-use-after-free in __pm_runtime_resume+0xe2/0xf0 + Read of size 2 at addr ffff88810cfc46f8 by task pm/606 + + Call Trace: + + dump_stack_lvl+0x4d/0x70 + print_report+0x170/0x4f3 + ? __pfx__raw_spin_lock_irqsave+0x10/0x10 + kasan_report+0xda/0x110 + ? __pm_runtime_resume+0xe2/0xf0 + ? __pm_runtime_resume+0xe2/0xf0 + __pm_runtime_resume+0xe2/0xf0 + ethnl_ops_begin+0x49/0x270 + ethnl_set_features+0x23c/0xab0 + ? __pfx_ethnl_set_features+0x10/0x10 + ? kvm_sched_clock_read+0x11/0x20 + ? local_clock_noinstr+0xf/0xf0 + ? local_clock+0x10/0x30 + ? kasan_save_track+0x25/0x60 + ? __kasan_kmalloc+0x7f/0x90 + ? genl_family_rcv_msg_attrs_parse.isra.0+0x150/0x2c0 + genl_family_rcv_msg_doit+0x1e7/0x2c0 + ? __pfx_genl_family_rcv_msg_doit+0x10/0x10 + ? __pfx_cred_has_capability.isra.0+0x10/0x10 + ? stack_trace_save+0x8e/0xc0 + genl_rcv_msg+0x411/0x660 + ? __pfx_genl_rcv_msg+0x10/0x10 + ? __pfx_ethnl_set_features+0x10/0x10 + netlink_rcv_skb+0x121/0x380 + ? __pfx_genl_rcv_msg+0x10/0x10 + ? __pfx_netlink_rcv_skb+0x10/0x10 + ? __pfx_down_read+0x10/0x10 + genl_rcv+0x23/0x30 + netlink_unicast+0x60f/0x830 + ? __pfx_netlink_unicast+0x10/0x10 + ? __pfx___alloc_skb+0x10/0x10 + netlink_sendmsg+0x6ea/0xbc0 + ? __pfx_netlink_sendmsg+0x10/0x10 + ? __futex_queue+0x10b/0x1f0 + ____sys_sendmsg+0x7a2/0x950 + ? copy_msghdr_from_user+0x26b/0x430 + ? __pfx_____sys_sendmsg+0x10/0x10 + ? __pfx_copy_msghdr_from_user+0x10/0x10 + ___sys_sendmsg+0xf8/0x180 + ? __pfx____sys_sendmsg+0x10/0x10 + ? __pfx_futex_wait+0x10/0x10 + ? fdget+0x2e4/0x4a0 + __sys_sendmsg+0x11f/0x1c0 + ? __pfx___sys_sendmsg+0x10/0x10 + do_syscall_64+0xe2/0x570 + ? exc_page_fault+0x66/0xb0 + entry_SYSCALL_64_after_hwframe+0x77/0x7f + + +This fix may be combined with another one in the ethtool subsystem: +https://lore.kernel.org/all/20260322075917.254874-1-alex.popov@linux.com/T/#u + +Fixes: d43c65b05b848e0b ("ethtool: runtime-resume netdev parent in ethnl_ops_begin") +Cc: stable@vger.kernel.org +Signed-off-by: Alexander Popov +Acked-by: Greg Kroah-Hartman +Reviewed-by: Breno Leitao +Link: https://patch.msgid.link/20260324224607.374327-1-alex.popov@linux.com +Signed-off-by: Johannes Berg +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/virtual/virt_wifi.c | 1 - + 1 file changed, 1 deletion(-) + +--- a/drivers/net/wireless/virtual/virt_wifi.c ++++ b/drivers/net/wireless/virtual/virt_wifi.c +@@ -555,7 +555,6 @@ static int virt_wifi_newlink(struct net + eth_hw_addr_inherit(dev, priv->lowerdev); + netif_stacked_transfer_operstate(priv->lowerdev, dev); + +- SET_NETDEV_DEV(dev, &priv->lowerdev->dev); + dev->ieee80211_ptr = kzalloc(sizeof(*dev->ieee80211_ptr), GFP_KERNEL); + + if (!dev->ieee80211_ptr) { diff --git a/queue-6.12/x86-cpu-amd-add-additional-fixed-rdseed-microcode-revisions.patch b/queue-6.12/x86-cpu-amd-add-additional-fixed-rdseed-microcode-revisions.patch new file mode 100644 index 0000000000..d6829997bc --- /dev/null +++ b/queue-6.12/x86-cpu-amd-add-additional-fixed-rdseed-microcode-revisions.patch @@ -0,0 +1,48 @@ +From dan.g.tob@gmail.com Thu Apr 2 05:15:35 2026 +From: Daniel Tobias +Date: Thu, 2 Apr 2026 14:14:45 +1100 +Subject: x86/CPU/AMD: Add additional fixed RDSEED microcode revisions +To: stable@vger.kernel.org +Cc: gregkh@linuxfoundation.org, x86@kernel.org, Mario Limonciello , "Borislav Petkov (AMD)" , Daniel Tobias +Message-ID: <20260402031445.48620-1-dan.g.tob@gmail.com> + +From: Mario Limonciello + +[ Upstream commit e1a97a627cd01d73fac5dd054d8f3de601ef2781 ] + +Microcode that resolves the RDSEED failure (SB-7055 [1]) has been released for +additional Zen5 models to linux-firmware [2]. Update the zen5_rdseed_microcode +array to cover these new models. + +Fixes: e980de2ff109 ("x86/CPU/AMD: Add RDSEED fix for Zen5") +Signed-off-by: Mario Limonciello +Signed-off-by: Borislav Petkov (AMD) +Link: https://www.amd.com/en/resources/product-security/bulletin/amd-sb-7055.html [1] +Link: https://gitlab.com/kernel-firmware/linux-firmware/-/commit/6167e5566900cf236f7a69704e8f4c441bc7212a [2] +Link: https://patch.msgid.link/20251113223608.1495655-1-mario.limonciello@amd.com +[ backport: 6.12.y uses a custom check_rdseed_microcode() function with + a switch statement. Updated the switch cases to include the new + models and revisions from the upstream patch. ] +Signed-off-by: Daniel Tobias +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/kernel/cpu/amd.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/arch/x86/kernel/cpu/amd.c ++++ b/arch/x86/kernel/cpu/amd.c +@@ -1034,7 +1034,14 @@ static bool check_rdseed_microcode(void) + if (cpu_has(c, X86_FEATURE_ZEN5)) { + switch (p.ucode_rev >> 8) { + case 0xb0021: min_rev = 0xb00215a; break; ++ case 0xb0081: min_rev = 0xb008121; break; + case 0xb1010: min_rev = 0xb101054; break; ++ case 0xb2040: min_rev = 0xb204037; break; ++ case 0xb4040: min_rev = 0xb404035; break; ++ case 0xb4041: min_rev = 0xb404108; break; ++ case 0xb6000: min_rev = 0xb600037; break; ++ case 0xb6080: min_rev = 0xb608038; break; ++ case 0xb7000: min_rev = 0xb700037; break; + default: + pr_debug("%s: ucode_rev: 0x%x, current revision: 0x%x\n", + __func__, p.ucode_rev, c->microcode); diff --git a/queue-6.12/x86-fred-fix-early-boot-failures-on-sev-es-snp-guests.patch b/queue-6.12/x86-fred-fix-early-boot-failures-on-sev-es-snp-guests.patch new file mode 100644 index 0000000000..aeab25eaa8 --- /dev/null +++ b/queue-6.12/x86-fred-fix-early-boot-failures-on-sev-es-snp-guests.patch @@ -0,0 +1,94 @@ +From stable+bounces-231415-greg=kroah.com@vger.kernel.org Tue Mar 31 14:17:13 2026 +From: Sasha Levin +Date: Tue, 31 Mar 2026 08:17:06 -0400 +Subject: x86/fred: Fix early boot failures on SEV-ES/SNP guests +To: stable@vger.kernel.org +Cc: Nikunj A Dadhania , "Borislav Petkov (AMD)" , Tom Lendacky , stable@kernel.org, Sasha Levin +Message-ID: <20260331121706.2197458-1-sashal@kernel.org> + +From: Nikunj A Dadhania + +[ Upstream commit 3645eb7e3915990a149460c151a00894cb586253 ] + +FRED-enabled SEV-(ES,SNP) guests fail to boot due to the following issues +in the early boot sequence: + +* FRED does not have a #VC exception handler in the dispatch logic + +* Early FRED #VC exceptions attempt to use uninitialized per-CPU GHCBs + instead of boot_ghcb + +Add X86_TRAP_VC case to fred_hwexc() with a new exc_vmm_communication() +function that provides the unified entry point FRED requires, dispatching +to existing user/kernel handlers based on privilege level. The function is +already declared via DECLARE_IDTENTRY_VC(). + +Fix early GHCB access by falling back to boot_ghcb in +__sev_{get,put}_ghcb() when per-CPU GHCBs are not yet initialized. + +Fixes: 14619d912b65 ("x86/fred: FRED entry/exit and dispatch code") +Signed-off-by: Nikunj A Dadhania +Signed-off-by: Borislav Petkov (AMD) +Reviewed-by: Tom Lendacky +Cc: # 6.12+ +Link: https://patch.msgid.link/20260318075654.1792916-4-nikunj@amd.com +[ applied GHCB early-return changes to core.c instead of noinstr.c ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/coco/sev/core.c | 6 ++++++ + arch/x86/entry/entry_fred.c | 14 ++++++++++++++ + 2 files changed, 20 insertions(+) + +--- a/arch/x86/coco/sev/core.c ++++ b/arch/x86/coco/sev/core.c +@@ -253,6 +253,9 @@ static noinstr struct ghcb *__sev_get_gh + + WARN_ON(!irqs_disabled()); + ++ if (!sev_cfg.ghcbs_initialized) ++ return boot_ghcb; ++ + data = this_cpu_read(runtime_data); + ghcb = &data->ghcb_page; + +@@ -649,6 +652,9 @@ static noinstr void __sev_put_ghcb(struc + + WARN_ON(!irqs_disabled()); + ++ if (!sev_cfg.ghcbs_initialized) ++ return; ++ + data = this_cpu_read(runtime_data); + ghcb = &data->ghcb_page; + +--- a/arch/x86/entry/entry_fred.c ++++ b/arch/x86/entry/entry_fred.c +@@ -176,6 +176,16 @@ static noinstr void fred_extint(struct p + } + } + ++#ifdef CONFIG_AMD_MEM_ENCRYPT ++noinstr void exc_vmm_communication(struct pt_regs *regs, unsigned long error_code) ++{ ++ if (user_mode(regs)) ++ return user_exc_vmm_communication(regs, error_code); ++ else ++ return kernel_exc_vmm_communication(regs, error_code); ++} ++#endif ++ + static noinstr void fred_hwexc(struct pt_regs *regs, unsigned long error_code) + { + /* Optimize for #PF. That's the only exception which matters performance wise */ +@@ -206,6 +216,10 @@ static noinstr void fred_hwexc(struct pt + #ifdef CONFIG_X86_CET + case X86_TRAP_CP: return exc_control_protection(regs, error_code); + #endif ++#ifdef CONFIG_AMD_MEM_ENCRYPT ++ case X86_TRAP_VC: return exc_vmm_communication(regs, error_code); ++#endif ++ + default: return fred_bad_type(regs, error_code); + } +