From: Sasha Levin Date: Wed, 7 May 2025 15:36:16 +0000 (-0400) Subject: Fixes for 6.1 X-Git-Tag: v5.15.182~23 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7467a85c952ba1201643f364bbd20f9bace21dc9;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 6.1 Signed-off-by: Sasha Levin --- diff --git a/queue-6.1/arm-dts-opos6ul-add-ksz8081-phy-properties.patch b/queue-6.1/arm-dts-opos6ul-add-ksz8081-phy-properties.patch new file mode 100644 index 0000000000..901de03287 --- /dev/null +++ b/queue-6.1/arm-dts-opos6ul-add-ksz8081-phy-properties.patch @@ -0,0 +1,44 @@ +From 503eccd1d48069fb548593d9b900b3c9181ff279 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 14 Mar 2025 17:20:38 +0100 +Subject: ARM: dts: opos6ul: add ksz8081 phy properties +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Sébastien Szymanski + +[ Upstream commit 6e1a7bc8382b0d4208258f7d2a4474fae788dd90 ] + +Commit c7e73b5051d6 ("ARM: imx: mach-imx6ul: remove 14x14 EVK specific +PHY fixup") removed a PHY fixup that setted the clock mode and the LED +mode. +Make the Ethernet interface work again by doing as advised in the +commit's log, set clock mode and the LED mode in the device tree. + +Fixes: c7e73b5051d6 ("ARM: imx: mach-imx6ul: remove 14x14 EVK specific PHY fixup") +Signed-off-by: Sébastien Szymanski +Reviewed-by: Oleksij Rempel +Signed-off-by: Shawn Guo +Signed-off-by: Sasha Levin +--- + arch/arm/boot/dts/imx6ul-imx6ull-opos6ul.dtsi | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/arm/boot/dts/imx6ul-imx6ull-opos6ul.dtsi b/arch/arm/boot/dts/imx6ul-imx6ull-opos6ul.dtsi +index f2386dcb9ff2c..dda4fa91b2f2c 100644 +--- a/arch/arm/boot/dts/imx6ul-imx6ull-opos6ul.dtsi ++++ b/arch/arm/boot/dts/imx6ul-imx6ull-opos6ul.dtsi +@@ -40,6 +40,9 @@ + reg = <1>; + interrupt-parent = <&gpio4>; + interrupts = <16 IRQ_TYPE_LEVEL_LOW>; ++ micrel,led-mode = <1>; ++ clocks = <&clks IMX6UL_CLK_ENET_REF>; ++ clock-names = "rmii-ref"; + status = "okay"; + }; + }; +-- +2.39.5 + diff --git a/queue-6.1/asoc-soc-core-stop-using-of_property_read_bool-for-n.patch b/queue-6.1/asoc-soc-core-stop-using-of_property_read_bool-for-n.patch new file mode 100644 index 0000000000..cd01209329 --- /dev/null +++ b/queue-6.1/asoc-soc-core-stop-using-of_property_read_bool-for-n.patch @@ -0,0 +1,101 @@ +From 8a3dcc949925103d1f19b2acb48f86a5a5311af4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jan 2025 09:21:27 +0100 +Subject: ASoC: soc-core: Stop using of_property_read_bool() for non-boolean + properties + +From: Geert Uytterhoeven + +[ Upstream commit 6eab7034579917f207ca6d8e3f4e11e85e0ab7d5 ] + +On R-Car: + + OF: /sound: Read of boolean property 'simple-audio-card,bitclock-master' with a value. + OF: /sound: Read of boolean property 'simple-audio-card,frame-master' with a value. + +or: + + OF: /soc/sound@ec500000/ports/port@0/endpoint: Read of boolean property 'bitclock-master' with a value. + OF: /soc/sound@ec500000/ports/port@0/endpoint: Read of boolean property 'frame-master' with a value. + +The use of of_property_read_bool() for non-boolean properties is +deprecated in favor of of_property_present() when testing for property +presence. + +Replace testing for presence before calling of_property_read_u32() by +testing for an -EINVAL return value from the latter, to simplify the +code. + +Signed-off-by: Geert Uytterhoeven +Link: https://patch.msgid.link/db10e96fbda121e7456d70e97a013cbfc9755f4d.1737533954.git.geert+renesas@glider.be +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/soc-core.c | 32 +++++++++++++------------------- + 1 file changed, 13 insertions(+), 19 deletions(-) + +diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c +index 58e07296144e0..b13370d2ec1d7 100644 +--- a/sound/soc/soc-core.c ++++ b/sound/soc/soc-core.c +@@ -2837,7 +2837,7 @@ int snd_soc_of_parse_pin_switches(struct snd_soc_card *card, const char *prop) + unsigned int i, nb_controls; + int ret; + +- if (!of_property_read_bool(dev->of_node, prop)) ++ if (!of_property_present(dev->of_node, prop)) + return 0; + + strings = devm_kcalloc(dev, nb_controls_max, +@@ -2911,23 +2911,17 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np, + if (rx_mask) + snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask); + +- if (of_property_read_bool(np, "dai-tdm-slot-num")) { +- ret = of_property_read_u32(np, "dai-tdm-slot-num", &val); +- if (ret) +- return ret; +- +- if (slots) +- *slots = val; +- } +- +- if (of_property_read_bool(np, "dai-tdm-slot-width")) { +- ret = of_property_read_u32(np, "dai-tdm-slot-width", &val); +- if (ret) +- return ret; ++ ret = of_property_read_u32(np, "dai-tdm-slot-num", &val); ++ if (ret && ret != -EINVAL) ++ return ret; ++ if (!ret && slots) ++ *slots = val; + +- if (slot_width) +- *slot_width = val; +- } ++ ret = of_property_read_u32(np, "dai-tdm-slot-width", &val); ++ if (ret && ret != -EINVAL) ++ return ret; ++ if (!ret && slot_width) ++ *slot_width = val; + + return 0; + } +@@ -3183,12 +3177,12 @@ unsigned int snd_soc_daifmt_parse_clock_provider_raw(struct device_node *np, + * check "[prefix]frame-master" + */ + snprintf(prop, sizeof(prop), "%sbitclock-master", prefix); +- bit = of_property_read_bool(np, prop); ++ bit = of_property_present(np, prop); + if (bit && bitclkmaster) + *bitclkmaster = of_parse_phandle(np, prop, 0); + + snprintf(prop, sizeof(prop), "%sframe-master", prefix); +- frame = of_property_read_bool(np, prop); ++ frame = of_property_present(np, prop); + if (frame && framemaster) + *framemaster = of_parse_phandle(np, prop, 0); + +-- +2.39.5 + diff --git a/queue-6.1/asoc-use-of_property_read_bool.patch b/queue-6.1/asoc-use-of_property_read_bool.patch new file mode 100644 index 0000000000..14587a726c --- /dev/null +++ b/queue-6.1/asoc-use-of_property_read_bool.patch @@ -0,0 +1,81 @@ +From 4acb7b8349c6eeb995c887f03f9d39c90707270b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 31 Jul 2024 13:12:58 -0600 +Subject: ASoC: Use of_property_read_bool() + +From: Rob Herring (Arm) + +[ Upstream commit 69dd15a8ef0ae494179fd15023aa8172188db6b7 ] + +Use of_property_read_bool() to read boolean properties rather than +of_get_property(). This is part of a larger effort to remove callers +of of_get_property() and similar functions. of_get_property() leaks +the DT property data pointer which is a problem for dynamically +allocated nodes which may be freed. + +Signed-off-by: Rob Herring (Arm) +Link: https://patch.msgid.link/20240731191312.1710417-20-robh@kernel.org +Signed-off-by: Mark Brown +Stable-dep-of: 6eab70345799 ("ASoC: soc-core: Stop using of_property_read_bool() for non-boolean properties") +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/ak4613.c | 4 ++-- + sound/soc/soc-core.c | 8 ++++---- + 2 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c +index f75c19ef35511..3f790d1f11a94 100644 +--- a/sound/soc/codecs/ak4613.c ++++ b/sound/soc/codecs/ak4613.c +@@ -840,14 +840,14 @@ static void ak4613_parse_of(struct ak4613_priv *priv, + /* Input 1 - 2 */ + for (i = 0; i < 2; i++) { + snprintf(prop, sizeof(prop), "asahi-kasei,in%d-single-end", i + 1); +- if (!of_get_property(np, prop, NULL)) ++ if (!of_property_read_bool(np, prop)) + priv->ic |= 1 << i; + } + + /* Output 1 - 6 */ + for (i = 0; i < 6; i++) { + snprintf(prop, sizeof(prop), "asahi-kasei,out%d-single-end", i + 1); +- if (!of_get_property(np, prop, NULL)) ++ if (!of_property_read_bool(np, prop)) + priv->oc |= 1 << i; + } + +diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c +index 6a4101dc15a54..58e07296144e0 100644 +--- a/sound/soc/soc-core.c ++++ b/sound/soc/soc-core.c +@@ -3143,10 +3143,10 @@ unsigned int snd_soc_daifmt_parse_format(struct device_node *np, + * SND_SOC_DAIFMT_INV_MASK area + */ + snprintf(prop, sizeof(prop), "%sbitclock-inversion", prefix); +- bit = !!of_get_property(np, prop, NULL); ++ bit = of_property_read_bool(np, prop); + + snprintf(prop, sizeof(prop), "%sframe-inversion", prefix); +- frame = !!of_get_property(np, prop, NULL); ++ frame = of_property_read_bool(np, prop); + + switch ((bit << 4) + frame) { + case 0x11: +@@ -3183,12 +3183,12 @@ unsigned int snd_soc_daifmt_parse_clock_provider_raw(struct device_node *np, + * check "[prefix]frame-master" + */ + snprintf(prop, sizeof(prop), "%sbitclock-master", prefix); +- bit = !!of_get_property(np, prop, NULL); ++ bit = of_property_read_bool(np, prop); + if (bit && bitclkmaster) + *bitclkmaster = of_parse_phandle(np, prop, 0); + + snprintf(prop, sizeof(prop), "%sframe-master", prefix); +- frame = !!of_get_property(np, prop, NULL); ++ frame = of_property_read_bool(np, prop); + if (frame && framemaster) + *framemaster = of_parse_phandle(np, prop, 0); + +-- +2.39.5 + diff --git a/queue-6.1/cpufreq-intel_pstate-do-not-update-global.turbo_disa.patch b/queue-6.1/cpufreq-intel_pstate-do-not-update-global.turbo_disa.patch new file mode 100644 index 0000000000..e270f90888 --- /dev/null +++ b/queue-6.1/cpufreq-intel_pstate-do-not-update-global.turbo_disa.patch @@ -0,0 +1,196 @@ +From 05f4584ff713d6064d7a12ded2952a58cc5c7678 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 25 Mar 2024 18:02:42 +0100 +Subject: cpufreq: intel_pstate: Do not update global.turbo_disabled after + initialization + +From: Rafael J. Wysocki + +[ Upstream commit 0940f1a8011fd69be5082015068e0dc31c800c20 ] + +The global.turbo_disabled is updated quite often, especially in the +passive mode in which case it is updated every time the scheduler calls +into the driver. However, this is generally not necessary and it adds +MSR read overhead to scheduler code paths (and that particular MSR is +slow to read). + +For this reason, make the driver read MSR_IA32_MISC_ENABLE_TURBO_DISABLE +just once at the cpufreq driver registration time and remove all of the +in-flight updates of global.turbo_disabled. + +Signed-off-by: Rafael J. Wysocki +Acked-by: Srinivas Pandruvada +Stable-dep-of: ac4e04d9e378 ("cpufreq: intel_pstate: Unchecked MSR aceess in legacy mode") +Signed-off-by: Sasha Levin +--- + drivers/cpufreq/intel_pstate.c | 51 ++++++---------------------------- + 1 file changed, 8 insertions(+), 43 deletions(-) + +diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c +index e26c0b0096080..18d6d2a357ce1 100644 +--- a/drivers/cpufreq/intel_pstate.c ++++ b/drivers/cpufreq/intel_pstate.c +@@ -172,7 +172,6 @@ struct vid_data { + * based on the MSR_IA32_MISC_ENABLE value and whether or + * not the maximum reported turbo P-state is different from + * the maximum reported non-turbo one. +- * @turbo_disabled_mf: The @turbo_disabled value reflected by cpuinfo.max_freq. + * @min_perf_pct: Minimum capacity limit in percent of the maximum turbo + * P-state capacity. + * @max_perf_pct: Maximum capacity limit in percent of the maximum turbo +@@ -181,7 +180,6 @@ struct vid_data { + struct global_params { + bool no_turbo; + bool turbo_disabled; +- bool turbo_disabled_mf; + int max_perf_pct; + int min_perf_pct; + }; +@@ -559,12 +557,13 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu) + cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, freq); + } + +-static inline void update_turbo_state(void) ++static bool turbo_is_disabled(void) + { + u64 misc_en; + + rdmsrl(MSR_IA32_MISC_ENABLE, misc_en); +- global.turbo_disabled = misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE; ++ ++ return !!(misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE); + } + + static int min_perf_pct_min(void) +@@ -1119,40 +1118,16 @@ static void intel_pstate_update_policies(void) + static void __intel_pstate_update_max_freq(struct cpudata *cpudata, + struct cpufreq_policy *policy) + { +- policy->cpuinfo.max_freq = global.turbo_disabled_mf ? ++ policy->cpuinfo.max_freq = global.turbo_disabled ? + cpudata->pstate.max_freq : cpudata->pstate.turbo_freq; + refresh_frequency_limits(policy); + } + +-static void intel_pstate_update_max_freq(unsigned int cpu) +-{ +- struct cpufreq_policy *policy = cpufreq_cpu_acquire(cpu); +- +- if (!policy) +- return; +- +- __intel_pstate_update_max_freq(all_cpu_data[cpu], policy); +- +- cpufreq_cpu_release(policy); +-} +- + static void intel_pstate_update_limits(unsigned int cpu) + { + mutex_lock(&intel_pstate_driver_lock); + +- update_turbo_state(); +- /* +- * If turbo has been turned on or off globally, policy limits for +- * all CPUs need to be updated to reflect that. +- */ +- if (global.turbo_disabled_mf != global.turbo_disabled) { +- global.turbo_disabled_mf = global.turbo_disabled; +- arch_set_max_freq_ratio(global.turbo_disabled); +- for_each_possible_cpu(cpu) +- intel_pstate_update_max_freq(cpu); +- } else { +- cpufreq_update_policy(cpu); +- } ++ cpufreq_update_policy(cpu); + + mutex_unlock(&intel_pstate_driver_lock); + } +@@ -1252,7 +1227,6 @@ static ssize_t show_no_turbo(struct kobject *kobj, + return -EAGAIN; + } + +- update_turbo_state(); + if (global.turbo_disabled) + ret = sprintf(buf, "%u\n", global.turbo_disabled); + else +@@ -1282,7 +1256,6 @@ static ssize_t store_no_turbo(struct kobject *a, struct kobj_attribute *b, + + mutex_lock(&intel_pstate_limits_lock); + +- update_turbo_state(); + if (global.turbo_disabled) { + pr_notice_once("Turbo disabled by BIOS or unavailable on processor\n"); + mutex_unlock(&intel_pstate_limits_lock); +@@ -2253,8 +2226,6 @@ static void intel_pstate_adjust_pstate(struct cpudata *cpu) + struct sample *sample; + int target_pstate; + +- update_turbo_state(); +- + target_pstate = get_target_pstate(cpu); + target_pstate = intel_pstate_prepare_request(cpu, target_pstate); + trace_cpu_frequency(target_pstate * cpu->pstate.scaling, cpu->cpu); +@@ -2572,7 +2543,6 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) + * be invoked on them. + */ + intel_pstate_clear_update_util_hook(policy->cpu); +- update_turbo_state(); + intel_pstate_set_pstate(cpu, pstate); + } else { + intel_pstate_set_update_util_hook(policy->cpu); +@@ -2616,7 +2586,6 @@ static void intel_pstate_verify_cpu_policy(struct cpudata *cpu, + { + int max_freq; + +- update_turbo_state(); + if (hwp_active) { + intel_pstate_get_hwp_cap(cpu); + max_freq = global.no_turbo || global.turbo_disabled ? +@@ -2713,8 +2682,6 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy) + + /* cpuinfo and default policy values */ + policy->cpuinfo.min_freq = cpu->pstate.min_freq; +- update_turbo_state(); +- global.turbo_disabled_mf = global.turbo_disabled; + policy->cpuinfo.max_freq = global.turbo_disabled ? + cpu->pstate.max_freq : cpu->pstate.turbo_freq; + +@@ -2880,8 +2847,6 @@ static int intel_cpufreq_target(struct cpufreq_policy *policy, + struct cpufreq_freqs freqs; + int target_pstate; + +- update_turbo_state(); +- + freqs.old = policy->cur; + freqs.new = target_freq; + +@@ -2903,8 +2868,6 @@ static unsigned int intel_cpufreq_fast_switch(struct cpufreq_policy *policy, + struct cpudata *cpu = all_cpu_data[policy->cpu]; + int target_pstate; + +- update_turbo_state(); +- + target_pstate = intel_pstate_freq_to_hwp(cpu, target_freq); + + target_pstate = intel_cpufreq_update_pstate(policy, target_pstate, true); +@@ -2922,7 +2885,6 @@ static void intel_cpufreq_adjust_perf(unsigned int cpunum, + int old_pstate = cpu->pstate.current_pstate; + int cap_pstate, min_pstate, max_pstate, target_pstate; + +- update_turbo_state(); + cap_pstate = global.turbo_disabled ? HWP_GUARANTEED_PERF(hwp_cap) : + HWP_HIGHEST_PERF(hwp_cap); + +@@ -3112,6 +3074,9 @@ static int intel_pstate_register_driver(struct cpufreq_driver *driver) + + memset(&global, 0, sizeof(global)); + global.max_perf_pct = 100; ++ global.turbo_disabled = turbo_is_disabled(); ++ ++ arch_set_max_freq_ratio(global.turbo_disabled); + + intel_pstate_driver = driver; + ret = cpufreq_register_driver(intel_pstate_driver); +-- +2.39.5 + diff --git a/queue-6.1/cpufreq-intel_pstate-fold-intel_pstate_max_within_li.patch b/queue-6.1/cpufreq-intel_pstate-fold-intel_pstate_max_within_li.patch new file mode 100644 index 0000000000..8225c1e02c --- /dev/null +++ b/queue-6.1/cpufreq-intel_pstate-fold-intel_pstate_max_within_li.patch @@ -0,0 +1,61 @@ +From 5920bf68fdd391d9ca9c68a0da71b7b0e616aa22 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 25 Mar 2024 18:01:58 +0100 +Subject: cpufreq: intel_pstate: Fold intel_pstate_max_within_limits() into + caller + +From: Rafael J. Wysocki + +[ Upstream commit 032c5565eb80edb6f2faeb31939540c897987119 ] + +Fold intel_pstate_max_within_limits() into its only caller. + +No functional impact. + +Signed-off-by: Rafael J. Wysocki +Acked-by: Srinivas Pandruvada +Stable-dep-of: ac4e04d9e378 ("cpufreq: intel_pstate: Unchecked MSR aceess in legacy mode") +Signed-off-by: Sasha Levin +--- + drivers/cpufreq/intel_pstate.c | 13 ++++--------- + 1 file changed, 4 insertions(+), 9 deletions(-) + +diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c +index faeeef4acdf70..e26c0b0096080 100644 +--- a/drivers/cpufreq/intel_pstate.c ++++ b/drivers/cpufreq/intel_pstate.c +@@ -1984,14 +1984,6 @@ static void intel_pstate_set_min_pstate(struct cpudata *cpu) + intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); + } + +-static void intel_pstate_max_within_limits(struct cpudata *cpu) +-{ +- int pstate = max(cpu->pstate.min_pstate, cpu->max_perf_ratio); +- +- update_turbo_state(); +- intel_pstate_set_pstate(cpu, pstate); +-} +- + static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) + { + int perf_ctl_max_phys = pstate_funcs.get_max_physical(cpu->cpu); +@@ -2573,12 +2565,15 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) + intel_pstate_update_perf_limits(cpu, policy->min, policy->max); + + if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { ++ int pstate = max(cpu->pstate.min_pstate, cpu->max_perf_ratio); ++ + /* + * NOHZ_FULL CPUs need this as the governor callback may not + * be invoked on them. + */ + intel_pstate_clear_update_util_hook(policy->cpu); +- intel_pstate_max_within_limits(cpu); ++ update_turbo_state(); ++ intel_pstate_set_pstate(cpu, pstate); + } else { + intel_pstate_set_update_util_hook(policy->cpu); + } +-- +2.39.5 + diff --git a/queue-6.1/cpufreq-intel_pstate-revise-global-turbo-disable-che.patch b/queue-6.1/cpufreq-intel_pstate-revise-global-turbo-disable-che.patch new file mode 100644 index 0000000000..49443d0947 --- /dev/null +++ b/queue-6.1/cpufreq-intel_pstate-revise-global-turbo-disable-che.patch @@ -0,0 +1,69 @@ +From a7b500eb867264fb33adcfc8ba370ccd893b0cd2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 7 Sep 2023 11:02:07 -0700 +Subject: cpufreq: intel_pstate: Revise global turbo disable check + +From: Srinivas Pandruvada + +[ Upstream commit 37b6ddba967c601479bea418a7ac6ff16b6232b7 ] + +Setting global turbo flag based on CPU 0 P-state limits is problematic +as it limits max P-state request on every CPU on the system just based +on its P-state limits. + +There are two cases in which global.turbo_disabled flag is set: +- When the MSR_IA32_MISC_ENABLE_TURBO_DISABLE bit is set to 1 +in the MSR MSR_IA32_MISC_ENABLE. This bit can be only changed by +the system BIOS before power up. +- When the max non turbo P-state is same as max turbo P-state for CPU 0. + +The second check is not a valid to decide global turbo state based on +the CPU 0. CPU 0 max turbo P-state can be same as max non turbo P-state, +but for other CPUs this may not be true. + +There is no guarantee that max P-state limits are same for every CPU. This +is possible that during fusing max P-state for a CPU is constrained. Also +with the Intel Speed Select performance profile, CPU 0 may not be present +in all profiles. In this case the max non turbo and turbo P-state can be +set to the lowest possible P-state by the hardware when switched to +such profile. Since max non turbo and turbo P-state is same, +global.turbo_disabled flag will be set. + +Once global.turbo_disabled is set, any scaling max and min frequency +update for any CPU will result in its max P-state constrained to the max +non turbo P-state. + +Hence remove the check of max non turbo P-state equal to max turbo P-state +of CPU 0 to set global turbo disabled flag. + +Signed-off-by: Srinivas Pandruvada +[ rjw: Subject edit ] +Signed-off-by: Rafael J. Wysocki +Stable-dep-of: ac4e04d9e378 ("cpufreq: intel_pstate: Unchecked MSR aceess in legacy mode") +Signed-off-by: Sasha Levin +--- + drivers/cpufreq/intel_pstate.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c +index d471d74df3bbb..faeeef4acdf70 100644 +--- a/drivers/cpufreq/intel_pstate.c ++++ b/drivers/cpufreq/intel_pstate.c +@@ -562,13 +562,9 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu) + static inline void update_turbo_state(void) + { + u64 misc_en; +- struct cpudata *cpu; + +- cpu = all_cpu_data[0]; + rdmsrl(MSR_IA32_MISC_ENABLE, misc_en); +- global.turbo_disabled = +- (misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE || +- cpu->pstate.max_pstate == cpu->pstate.turbo_pstate); ++ global.turbo_disabled = misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE; + } + + static int min_perf_pct_min(void) +-- +2.39.5 + diff --git a/queue-6.1/cpufreq-intel_pstate-unchecked-msr-aceess-in-legacy-.patch b/queue-6.1/cpufreq-intel_pstate-unchecked-msr-aceess-in-legacy-.patch new file mode 100644 index 0000000000..0cd9080d13 --- /dev/null +++ b/queue-6.1/cpufreq-intel_pstate-unchecked-msr-aceess-in-legacy-.patch @@ -0,0 +1,65 @@ +From eda97920501cd72b95367e79b7345efb45705636 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 29 Apr 2025 14:07:11 -0700 +Subject: cpufreq: intel_pstate: Unchecked MSR aceess in legacy mode + +From: Srinivas Pandruvada + +[ Upstream commit ac4e04d9e378f5aa826c2406ad7871ae1b6a6fb9 ] + +When turbo mode is unavailable on a Skylake-X system, executing the +command: + + # echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo + +results in an unchecked MSR access error: + + WRMSR to 0x199 (attempted to write 0x0000000100001300). + +This issue was reproduced on an OEM (Original Equipment Manufacturer) +system and is not a common problem across all Skylake-X systems. + +This error occurs because the MSR 0x199 Turbo Engage Bit (bit 32) is set +when turbo mode is disabled. The issue arises when intel_pstate fails to +detect that turbo mode is disabled. Here intel_pstate relies on +MSR_IA32_MISC_ENABLE bit 38 to determine the status of turbo mode. +However, on this system, bit 38 is not set even when turbo mode is +disabled. + +According to the Intel Software Developer's Manual (SDM), the BIOS sets +this bit during platform initialization to enable or disable +opportunistic processor performance operations. Logically, this bit +should be set in such cases. However, the SDM also specifies that "OS +and applications must use CPUID leaf 06H to detect processors with +opportunistic processor performance operations enabled." + +Therefore, in addition to checking MSR_IA32_MISC_ENABLE bit 38, verify +that CPUID.06H:EAX[1] is 0 to accurately determine if turbo mode is +disabled. + +Fixes: 4521e1a0ce17 ("cpufreq: intel_pstate: Reflect current no_turbo state correctly") +Signed-off-by: Srinivas Pandruvada +Cc: All applicable +Signed-off-by: Rafael J. Wysocki +Signed-off-by: Sasha Levin +--- + drivers/cpufreq/intel_pstate.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c +index 18d6d2a357ce1..bc0dec57bc8e2 100644 +--- a/drivers/cpufreq/intel_pstate.c ++++ b/drivers/cpufreq/intel_pstate.c +@@ -561,6 +561,9 @@ static bool turbo_is_disabled(void) + { + u64 misc_en; + ++ if (!cpu_feature_enabled(X86_FEATURE_IDA)) ++ return true; ++ + rdmsrl(MSR_IA32_MISC_ENABLE, misc_en); + + return !!(misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE); +-- +2.39.5 + diff --git a/queue-6.1/drm-amd-display-add-scoped-mutexes-for-amdgpu_dm_dhc.patch b/queue-6.1/drm-amd-display-add-scoped-mutexes-for-amdgpu_dm_dhc.patch new file mode 100644 index 0000000000..1af24e3349 --- /dev/null +++ b/queue-6.1/drm-amd-display-add-scoped-mutexes-for-amdgpu_dm_dhc.patch @@ -0,0 +1,205 @@ +From d238016ec4e321aa99127854f07f86691d7305a0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 28 Feb 2025 13:30:01 -0600 +Subject: drm/amd/display: Add scoped mutexes for amdgpu_dm_dhcp + +From: Mario Limonciello + +[ Upstream commit 6b675ab8efbf2bcee25be29e865455c56e246401 ] + +[Why] +Guards automatically release mutex when it goes out of scope making +code easier to follow. + +[How] +Replace all use of mutex_lock()/mutex_unlock() with guard(mutex). + +Reviewed-by: Alex Hung +Signed-off-by: Mario Limonciello +Signed-off-by: Tom Chung +Tested-by: Daniel Wheeler +Signed-off-by: Alex Deucher +Stable-dep-of: be593d9d91c5 ("drm/amd/display: Fix slab-use-after-free in hdcp") +Signed-off-by: Sasha Levin +--- + .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 37 +++++-------------- + 1 file changed, 10 insertions(+), 27 deletions(-) + +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +index 7c67bb771f996..6222d5a168832 100644 +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +@@ -172,7 +172,7 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work, + struct mod_hdcp_display_adjustment display_adjust; + unsigned int conn_index = aconnector->base.index; + +- mutex_lock(&hdcp_w->mutex); ++ guard(mutex)(&hdcp_w->mutex); + hdcp_w->aconnector[conn_index] = aconnector; + + memset(&link_adjust, 0, sizeof(link_adjust)); +@@ -209,7 +209,6 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work, + mod_hdcp_update_display(&hdcp_w->hdcp, conn_index, &link_adjust, &display_adjust, &hdcp_w->output); + + process_output(hdcp_w); +- mutex_unlock(&hdcp_w->mutex); + } + + static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work, +@@ -220,7 +219,7 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work, + struct drm_connector_state *conn_state = aconnector->base.state; + unsigned int conn_index = aconnector->base.index; + +- mutex_lock(&hdcp_w->mutex); ++ guard(mutex)(&hdcp_w->mutex); + hdcp_w->aconnector[conn_index] = aconnector; + + /* the removal of display will invoke auth reset -> hdcp destroy and +@@ -239,7 +238,6 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work, + mod_hdcp_remove_display(&hdcp_w->hdcp, aconnector->base.index, &hdcp_w->output); + + process_output(hdcp_w); +- mutex_unlock(&hdcp_w->mutex); + } + + void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_index) +@@ -247,7 +245,7 @@ void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_inde + struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index]; + unsigned int conn_index; + +- mutex_lock(&hdcp_w->mutex); ++ guard(mutex)(&hdcp_w->mutex); + + mod_hdcp_reset_connection(&hdcp_w->hdcp, &hdcp_w->output); + +@@ -259,8 +257,6 @@ void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_inde + } + + process_output(hdcp_w); +- +- mutex_unlock(&hdcp_w->mutex); + } + + void hdcp_handle_cpirq(struct hdcp_workqueue *hdcp_work, unsigned int link_index) +@@ -277,7 +273,7 @@ static void event_callback(struct work_struct *work) + hdcp_work = container_of(to_delayed_work(work), struct hdcp_workqueue, + callback_dwork); + +- mutex_lock(&hdcp_work->mutex); ++ guard(mutex)(&hdcp_work->mutex); + + cancel_delayed_work(&hdcp_work->callback_dwork); + +@@ -285,8 +281,6 @@ static void event_callback(struct work_struct *work) + &hdcp_work->output); + + process_output(hdcp_work); +- +- mutex_unlock(&hdcp_work->mutex); + } + + static void event_property_update(struct work_struct *work) +@@ -326,7 +320,7 @@ static void event_property_update(struct work_struct *work) + continue; + + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); +- mutex_lock(&hdcp_work->mutex); ++ guard(mutex)(&hdcp_work->mutex); + + if (conn_state->commit) { + ret = wait_for_completion_interruptible_timeout(&conn_state->commit->hw_done, +@@ -358,7 +352,6 @@ static void event_property_update(struct work_struct *work) + drm_hdcp_update_content_protection(connector, + DRM_MODE_CONTENT_PROTECTION_DESIRED); + } +- mutex_unlock(&hdcp_work->mutex); + drm_modeset_unlock(&dev->mode_config.connection_mutex); + } + } +@@ -371,7 +364,7 @@ static void event_property_validate(struct work_struct *work) + struct amdgpu_dm_connector *aconnector; + unsigned int conn_index; + +- mutex_lock(&hdcp_work->mutex); ++ guard(mutex)(&hdcp_work->mutex); + + for (conn_index = 0; conn_index < AMDGPU_DM_MAX_DISPLAY_INDEX; + conn_index++) { +@@ -415,8 +408,6 @@ static void event_property_validate(struct work_struct *work) + schedule_work(&hdcp_work->property_update_work); + } + } +- +- mutex_unlock(&hdcp_work->mutex); + } + + static void event_watchdog_timer(struct work_struct *work) +@@ -427,7 +418,7 @@ static void event_watchdog_timer(struct work_struct *work) + struct hdcp_workqueue, + watchdog_timer_dwork); + +- mutex_lock(&hdcp_work->mutex); ++ guard(mutex)(&hdcp_work->mutex); + + cancel_delayed_work(&hdcp_work->watchdog_timer_dwork); + +@@ -436,8 +427,6 @@ static void event_watchdog_timer(struct work_struct *work) + &hdcp_work->output); + + process_output(hdcp_work); +- +- mutex_unlock(&hdcp_work->mutex); + } + + static void event_cpirq(struct work_struct *work) +@@ -446,13 +435,11 @@ static void event_cpirq(struct work_struct *work) + + hdcp_work = container_of(work, struct hdcp_workqueue, cpirq_work); + +- mutex_lock(&hdcp_work->mutex); ++ guard(mutex)(&hdcp_work->mutex); + + mod_hdcp_process_event(&hdcp_work->hdcp, MOD_HDCP_EVENT_CPIRQ, &hdcp_work->output); + + process_output(hdcp_work); +- +- mutex_unlock(&hdcp_work->mutex); + } + + void hdcp_destroy(struct kobject *kobj, struct hdcp_workqueue *hdcp_work) +@@ -486,7 +473,7 @@ static bool enable_assr(void *handle, struct dc_link *link) + + dtm_cmd = (struct ta_dtm_shared_memory *)psp->dtm_context.context.mem_context.shared_buf; + +- mutex_lock(&psp->dtm_context.mutex); ++ guard(mutex)(&psp->dtm_context.mutex); + memset(dtm_cmd, 0, sizeof(struct ta_dtm_shared_memory)); + + dtm_cmd->cmd_id = TA_DTM_COMMAND__TOPOLOGY_ASSR_ENABLE; +@@ -501,8 +488,6 @@ static bool enable_assr(void *handle, struct dc_link *link) + res = false; + } + +- mutex_unlock(&psp->dtm_context.mutex); +- + return res; + } + +@@ -563,13 +548,11 @@ static void update_config(void *handle, struct cp_psp_stream_config *config) + (!!aconnector->base.state) ? + aconnector->base.state->hdcp_content_type : -1); + +- mutex_lock(&hdcp_w->mutex); ++ guard(mutex)(&hdcp_w->mutex); + + mod_hdcp_add_display(&hdcp_w->hdcp, link, display, &hdcp_w->output); + + process_output(hdcp_w); +- mutex_unlock(&hdcp_w->mutex); +- + } + + /** +-- +2.39.5 + diff --git a/queue-6.1/drm-amd-display-change-hdcp-update-sequence-for-dm.patch b/queue-6.1/drm-amd-display-change-hdcp-update-sequence-for-dm.patch new file mode 100644 index 0000000000..ef6ff2872d --- /dev/null +++ b/queue-6.1/drm-amd-display-change-hdcp-update-sequence-for-dm.patch @@ -0,0 +1,163 @@ +From 4f489c9cefe14311c127a2b8341ca34945a7f5a3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 24 Jul 2023 16:32:47 -0400 +Subject: drm/amd/display: Change HDCP update sequence for DM + +From: Bhawanpreet Lakha + +[ Upstream commit 393e83484839970e4975dfa1f0666f939a6f3e3d ] + +Refactor the sequence in hdcp_update_display() to use +mod_hdcp_update_display(). + +Previous sequence: + - remove()->add() + +This Sequence was used to update the display, (mod_hdcp_update_display +didn't exist at the time). This meant for any hdcp updates (type changes, +enable/disable) we would remove, reconstruct, and add. This leads to +unnecessary calls to psp eventually + +New Sequence using mod_hdcp_update_display(): + - add() once when stream is enabled + - use update() for all updates + +The update function checks for prev == new states and will not +unnecessarily end up calling psp via add/remove. + +Reviewed-by: Qingqing Zhuo +Acked-by: Tom Chung +Signed-off-by: Bhawanpreet Lakha +Tested-by: Daniel Wheeler +Signed-off-by: Alex Deucher +Stable-dep-of: be593d9d91c5 ("drm/amd/display: Fix slab-use-after-free in hdcp") +Signed-off-by: Sasha Levin +--- + .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 80 +++++++++---------- + 1 file changed, 38 insertions(+), 42 deletions(-) + +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +index 15537f554ca86..7c67bb771f996 100644 +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +@@ -168,53 +168,45 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work, + bool enable_encryption) + { + struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index]; +- struct mod_hdcp_display *display = &hdcp_work[link_index].display; +- struct mod_hdcp_link *link = &hdcp_work[link_index].link; +- struct mod_hdcp_display_query query; ++ struct mod_hdcp_link_adjustment link_adjust; ++ struct mod_hdcp_display_adjustment display_adjust; + unsigned int conn_index = aconnector->base.index; + + mutex_lock(&hdcp_w->mutex); + hdcp_w->aconnector[conn_index] = aconnector; + +- query.display = NULL; +- mod_hdcp_query_display(&hdcp_w->hdcp, aconnector->base.index, &query); +- +- if (query.display) { +- memcpy(display, query.display, sizeof(struct mod_hdcp_display)); +- mod_hdcp_remove_display(&hdcp_w->hdcp, aconnector->base.index, &hdcp_w->output); +- +- hdcp_w->link.adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0; +- +- if (enable_encryption) { +- /* Explicitly set the saved SRM as sysfs call will be after +- * we already enabled hdcp (s3 resume case) +- */ +- if (hdcp_work->srm_size > 0) +- psp_set_srm(hdcp_work->hdcp.config.psp.handle, hdcp_work->srm, +- hdcp_work->srm_size, +- &hdcp_work->srm_version); +- +- display->adjust.disable = MOD_HDCP_DISPLAY_NOT_DISABLE; +- if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) { +- hdcp_w->link.adjust.hdcp1.disable = 0; +- hdcp_w->link.adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0; +- } else if (content_type == DRM_MODE_HDCP_CONTENT_TYPE1) { +- hdcp_w->link.adjust.hdcp1.disable = 1; +- hdcp_w->link.adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_1; +- } ++ memset(&link_adjust, 0, sizeof(link_adjust)); ++ memset(&display_adjust, 0, sizeof(display_adjust)); + +- schedule_delayed_work(&hdcp_w->property_validate_dwork, +- msecs_to_jiffies(DRM_HDCP_CHECK_PERIOD_MS)); +- } else { +- display->adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION; +- hdcp_w->encryption_status[conn_index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; +- cancel_delayed_work(&hdcp_w->property_validate_dwork); ++ if (enable_encryption) { ++ /* Explicitly set the saved SRM as sysfs call will be after we already enabled hdcp ++ * (s3 resume case) ++ */ ++ if (hdcp_work->srm_size > 0) ++ psp_set_srm(hdcp_work->hdcp.config.psp.handle, hdcp_work->srm, ++ hdcp_work->srm_size, ++ &hdcp_work->srm_version); ++ ++ display_adjust.disable = MOD_HDCP_DISPLAY_NOT_DISABLE; ++ ++ link_adjust.auth_delay = 2; ++ ++ if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) { ++ link_adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0; ++ } else if (content_type == DRM_MODE_HDCP_CONTENT_TYPE1) { ++ link_adjust.hdcp1.disable = 1; ++ link_adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_1; + } + +- display->state = MOD_HDCP_DISPLAY_ACTIVE; ++ schedule_delayed_work(&hdcp_w->property_validate_dwork, ++ msecs_to_jiffies(DRM_HDCP_CHECK_PERIOD_MS)); ++ } else { ++ display_adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION; ++ hdcp_w->encryption_status[conn_index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; ++ cancel_delayed_work(&hdcp_w->property_validate_dwork); + } + +- mod_hdcp_add_display(&hdcp_w->hdcp, link, display, &hdcp_w->output); ++ mod_hdcp_update_display(&hdcp_w->hdcp, conn_index, &link_adjust, &display_adjust, &hdcp_w->output); + + process_output(hdcp_w); + mutex_unlock(&hdcp_w->mutex); +@@ -521,7 +513,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config) + int link_index = aconnector->dc_link->link_index; + struct mod_hdcp_display *display = &hdcp_work[link_index].display; + struct mod_hdcp_link *link = &hdcp_work[link_index].link; +- struct drm_connector_state *conn_state; ++ struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index]; + struct dc_sink *sink = NULL; + bool link_is_hdcp14 = false; + +@@ -563,7 +555,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config) + display->adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION; + link->adjust.auth_delay = 3; + link->adjust.hdcp1.disable = 0; +- conn_state = aconnector->base.state; ++ hdcp_w->encryption_status[display->index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; + + DRM_DEBUG_DRIVER("[HDCP_DM] display %d, CP %d, type %d\n", aconnector->base.index, + (!!aconnector->base.state) ? +@@ -571,9 +563,13 @@ static void update_config(void *handle, struct cp_psp_stream_config *config) + (!!aconnector->base.state) ? + aconnector->base.state->hdcp_content_type : -1); + +- if (conn_state) +- hdcp_update_display(hdcp_work, link_index, aconnector, +- conn_state->hdcp_content_type, false); ++ mutex_lock(&hdcp_w->mutex); ++ ++ mod_hdcp_add_display(&hdcp_w->hdcp, link, display, &hdcp_w->output); ++ ++ process_output(hdcp_w); ++ mutex_unlock(&hdcp_w->mutex); ++ + } + + /** +-- +2.39.5 + diff --git a/queue-6.1/drm-amd-display-clean-up-style-problems-in-amdgpu_dm.patch b/queue-6.1/drm-amd-display-clean-up-style-problems-in-amdgpu_dm.patch new file mode 100644 index 0000000000..60568231ac --- /dev/null +++ b/queue-6.1/drm-amd-display-clean-up-style-problems-in-amdgpu_dm.patch @@ -0,0 +1,510 @@ +From 51ee25db9428ced71fa4d874256222d611a6770f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 13 Jul 2023 17:10:57 +0530 +Subject: drm/amd/display: Clean up style problems in amdgpu_dm_hdcp.c + +From: Srinivasan Shanmugam + +[ Upstream commit a19de9dbb4d293c064b02cec8ef134cb9812d639 ] + +Conform to Linux kernel coding style. + +And promote sysfs entry for set/get srm to kdoc. + +Suggested-by: Rodrigo Siqueira +Cc: Rodrigo Siqueira +Cc: Aurabindo Pillai +Signed-off-by: Srinivasan Shanmugam +Reviewed-by: Aurabindo Pillai +Signed-off-by: Alex Deucher +Stable-dep-of: be593d9d91c5 ("drm/amd/display: Fix slab-use-after-free in hdcp") +Signed-off-by: Sasha Levin +--- + .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 185 +++++++++--------- + 1 file changed, 89 insertions(+), 96 deletions(-) + +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +index 7fc26ca30dcd6..15537f554ca86 100644 +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +@@ -39,10 +39,10 @@ + static bool + lp_write_i2c(void *handle, uint32_t address, const uint8_t *data, uint32_t size) + { +- + struct dc_link *link = handle; + struct i2c_payload i2c_payloads[] = {{true, address, size, (void *)data} }; +- struct i2c_command cmd = {i2c_payloads, 1, I2C_COMMAND_ENGINE_HW, link->dc->caps.i2c_speed_in_khz}; ++ struct i2c_command cmd = {i2c_payloads, 1, I2C_COMMAND_ENGINE_HW, ++ link->dc->caps.i2c_speed_in_khz}; + + return dm_helpers_submit_i2c(link->ctx, link, &cmd); + } +@@ -52,8 +52,10 @@ lp_read_i2c(void *handle, uint32_t address, uint8_t offset, uint8_t *data, uint3 + { + struct dc_link *link = handle; + +- struct i2c_payload i2c_payloads[] = {{true, address, 1, &offset}, {false, address, size, data} }; +- struct i2c_command cmd = {i2c_payloads, 2, I2C_COMMAND_ENGINE_HW, link->dc->caps.i2c_speed_in_khz}; ++ struct i2c_payload i2c_payloads[] = {{true, address, 1, &offset}, ++ {false, address, size, data} }; ++ struct i2c_command cmd = {i2c_payloads, 2, I2C_COMMAND_ENGINE_HW, ++ link->dc->caps.i2c_speed_in_khz}; + + return dm_helpers_submit_i2c(link->ctx, link, &cmd); + } +@@ -76,7 +78,6 @@ lp_read_dpcd(void *handle, uint32_t address, uint8_t *data, uint32_t size) + + static uint8_t *psp_get_srm(struct psp_context *psp, uint32_t *srm_version, uint32_t *srm_size) + { +- + struct ta_hdcp_shared_memory *hdcp_cmd; + + if (!psp->hdcp_context.context.initialized) { +@@ -96,13 +97,12 @@ static uint8_t *psp_get_srm(struct psp_context *psp, uint32_t *srm_version, uint + *srm_version = hdcp_cmd->out_msg.hdcp_get_srm.srm_version; + *srm_size = hdcp_cmd->out_msg.hdcp_get_srm.srm_buf_size; + +- + return hdcp_cmd->out_msg.hdcp_get_srm.srm_buf; + } + +-static int psp_set_srm(struct psp_context *psp, uint8_t *srm, uint32_t srm_size, uint32_t *srm_version) ++static int psp_set_srm(struct psp_context *psp, ++ u8 *srm, uint32_t srm_size, uint32_t *srm_version) + { +- + struct ta_hdcp_shared_memory *hdcp_cmd; + + if (!psp->hdcp_context.context.initialized) { +@@ -119,7 +119,8 @@ static int psp_set_srm(struct psp_context *psp, uint8_t *srm, uint32_t srm_size, + + psp_hdcp_invoke(psp, hdcp_cmd->cmd_id); + +- if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS || hdcp_cmd->out_msg.hdcp_set_srm.valid_signature != 1 || ++ if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS || ++ hdcp_cmd->out_msg.hdcp_set_srm.valid_signature != 1 || + hdcp_cmd->out_msg.hdcp_set_srm.srm_version == PSP_SRM_VERSION_MAX) + return -EINVAL; + +@@ -150,7 +151,6 @@ static void process_output(struct hdcp_workqueue *hdcp_work) + + static void link_lock(struct hdcp_workqueue *work, bool lock) + { +- + int i = 0; + + for (i = 0; i < work->max_link; i++) { +@@ -160,10 +160,11 @@ static void link_lock(struct hdcp_workqueue *work, bool lock) + mutex_unlock(&work[i].mutex); + } + } ++ + void hdcp_update_display(struct hdcp_workqueue *hdcp_work, + unsigned int link_index, + struct amdgpu_dm_connector *aconnector, +- uint8_t content_type, ++ u8 content_type, + bool enable_encryption) + { + struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index]; +@@ -178,18 +179,19 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work, + query.display = NULL; + mod_hdcp_query_display(&hdcp_w->hdcp, aconnector->base.index, &query); + +- if (query.display != NULL) { ++ if (query.display) { + memcpy(display, query.display, sizeof(struct mod_hdcp_display)); + mod_hdcp_remove_display(&hdcp_w->hdcp, aconnector->base.index, &hdcp_w->output); + + hdcp_w->link.adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0; + + if (enable_encryption) { +- /* Explicitly set the saved SRM as sysfs call will be after we already enabled hdcp +- * (s3 resume case) ++ /* Explicitly set the saved SRM as sysfs call will be after ++ * we already enabled hdcp (s3 resume case) + */ + if (hdcp_work->srm_size > 0) +- psp_set_srm(hdcp_work->hdcp.config.psp.handle, hdcp_work->srm, hdcp_work->srm_size, ++ psp_set_srm(hdcp_work->hdcp.config.psp.handle, hdcp_work->srm, ++ hdcp_work->srm_size, + &hdcp_work->srm_version); + + display->adjust.disable = MOD_HDCP_DISPLAY_NOT_DISABLE; +@@ -219,7 +221,7 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work, + } + + static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work, +- unsigned int link_index, ++ unsigned int link_index, + struct amdgpu_dm_connector *aconnector) + { + struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index]; +@@ -238,7 +240,8 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work, + conn_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + + DRM_DEBUG_DRIVER("[HDCP_DM] display %d, CP 2 -> 1, type %u, DPMS %u\n", +- aconnector->base.index, conn_state->hdcp_content_type, aconnector->base.dpms); ++ aconnector->base.index, conn_state->hdcp_content_type, ++ aconnector->base.dpms); + } + + mod_hdcp_remove_display(&hdcp_w->hdcp, aconnector->base.index, &hdcp_w->output); +@@ -246,6 +249,7 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work, + process_output(hdcp_w); + mutex_unlock(&hdcp_w->mutex); + } ++ + void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_index) + { + struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index]; +@@ -274,15 +278,12 @@ void hdcp_handle_cpirq(struct hdcp_workqueue *hdcp_work, unsigned int link_index + schedule_work(&hdcp_w->cpirq_work); + } + +- +- +- + static void event_callback(struct work_struct *work) + { + struct hdcp_workqueue *hdcp_work; + + hdcp_work = container_of(to_delayed_work(work), struct hdcp_workqueue, +- callback_dwork); ++ callback_dwork); + + mutex_lock(&hdcp_work->mutex); + +@@ -294,13 +295,12 @@ static void event_callback(struct work_struct *work) + process_output(hdcp_work); + + mutex_unlock(&hdcp_work->mutex); +- +- + } + + static void event_property_update(struct work_struct *work) + { +- struct hdcp_workqueue *hdcp_work = container_of(work, struct hdcp_workqueue, property_update_work); ++ struct hdcp_workqueue *hdcp_work = container_of(work, struct hdcp_workqueue, ++ property_update_work); + struct amdgpu_dm_connector *aconnector = NULL; + struct drm_device *dev; + long ret; +@@ -337,11 +337,10 @@ static void event_property_update(struct work_struct *work) + mutex_lock(&hdcp_work->mutex); + + if (conn_state->commit) { +- ret = wait_for_completion_interruptible_timeout( +- &conn_state->commit->hw_done, 10 * HZ); ++ ret = wait_for_completion_interruptible_timeout(&conn_state->commit->hw_done, ++ 10 * HZ); + if (ret == 0) { +- DRM_ERROR( +- "HDCP state unknown! Setting it to DESIRED"); ++ DRM_ERROR("HDCP state unknown! Setting it to DESIRED\n"); + hdcp_work->encryption_status[conn_index] = + MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; + } +@@ -352,24 +351,20 @@ static void event_property_update(struct work_struct *work) + DRM_MODE_HDCP_CONTENT_TYPE0 && + hdcp_work->encryption_status[conn_index] <= + MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON) { +- + DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_ENABLED\n"); +- drm_hdcp_update_content_protection( +- connector, +- DRM_MODE_CONTENT_PROTECTION_ENABLED); ++ drm_hdcp_update_content_protection(connector, ++ DRM_MODE_CONTENT_PROTECTION_ENABLED); + } else if (conn_state->hdcp_content_type == + DRM_MODE_HDCP_CONTENT_TYPE1 && + hdcp_work->encryption_status[conn_index] == + MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON) { +- drm_hdcp_update_content_protection( +- connector, +- DRM_MODE_CONTENT_PROTECTION_ENABLED); ++ drm_hdcp_update_content_protection(connector, ++ DRM_MODE_CONTENT_PROTECTION_ENABLED); + } + } else { + DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_DESIRED\n"); +- drm_hdcp_update_content_protection( +- connector, DRM_MODE_CONTENT_PROTECTION_DESIRED); +- ++ drm_hdcp_update_content_protection(connector, ++ DRM_MODE_CONTENT_PROTECTION_DESIRED); + } + mutex_unlock(&hdcp_work->mutex); + drm_modeset_unlock(&dev->mode_config.connection_mutex); +@@ -409,7 +404,7 @@ static void event_property_validate(struct work_struct *work) + &query); + + DRM_DEBUG_DRIVER("[HDCP_DM] disp %d, connector->CP %u, (query, work): (%d, %d)\n", +- aconnector->base.index, ++ aconnector->base.index, + aconnector->base.state->content_protection, + query.encryption_status, + hdcp_work->encryption_status[conn_index]); +@@ -417,7 +412,8 @@ static void event_property_validate(struct work_struct *work) + if (query.encryption_status != + hdcp_work->encryption_status[conn_index]) { + DRM_DEBUG_DRIVER("[HDCP_DM] encryption_status change from %x to %x\n", +- hdcp_work->encryption_status[conn_index], query.encryption_status); ++ hdcp_work->encryption_status[conn_index], ++ query.encryption_status); + + hdcp_work->encryption_status[conn_index] = + query.encryption_status; +@@ -436,7 +432,7 @@ static void event_watchdog_timer(struct work_struct *work) + struct hdcp_workqueue *hdcp_work; + + hdcp_work = container_of(to_delayed_work(work), +- struct hdcp_workqueue, ++ struct hdcp_workqueue, + watchdog_timer_dwork); + + mutex_lock(&hdcp_work->mutex); +@@ -450,7 +446,6 @@ static void event_watchdog_timer(struct work_struct *work) + process_output(hdcp_work); + + mutex_unlock(&hdcp_work->mutex); +- + } + + static void event_cpirq(struct work_struct *work) +@@ -466,10 +461,8 @@ static void event_cpirq(struct work_struct *work) + process_output(hdcp_work); + + mutex_unlock(&hdcp_work->mutex); +- + } + +- + void hdcp_destroy(struct kobject *kobj, struct hdcp_workqueue *hdcp_work) + { + int i = 0; +@@ -486,10 +479,8 @@ void hdcp_destroy(struct kobject *kobj, struct hdcp_workqueue *hdcp_work) + kfree(hdcp_work); + } + +- + static bool enable_assr(void *handle, struct dc_link *link) + { +- + struct hdcp_workqueue *hdcp_work = handle; + struct mod_hdcp hdcp = hdcp_work->hdcp; + struct psp_context *psp = hdcp.config.psp.handle; +@@ -507,7 +498,8 @@ static bool enable_assr(void *handle, struct dc_link *link) + memset(dtm_cmd, 0, sizeof(struct ta_dtm_shared_memory)); + + dtm_cmd->cmd_id = TA_DTM_COMMAND__TOPOLOGY_ASSR_ENABLE; +- dtm_cmd->dtm_in_message.topology_assr_enable.display_topology_dig_be_index = link->link_enc_hw_inst; ++ dtm_cmd->dtm_in_message.topology_assr_enable.display_topology_dig_be_index = ++ link->link_enc_hw_inst; + dtm_cmd->dtm_status = TA_DTM_STATUS__GENERIC_FAILURE; + + psp_dtm_invoke(psp, dtm_cmd->cmd_id); +@@ -549,7 +541,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config) + else if (aconnector->dc_em_sink) + sink = aconnector->dc_em_sink; + +- if (sink != NULL) ++ if (sink) + link->mode = mod_hdcp_signal_type_to_operation_mode(sink->sink_signal); + + display->controller = CONTROLLER_ID_D0 + config->otg_inst; +@@ -574,16 +566,20 @@ static void update_config(void *handle, struct cp_psp_stream_config *config) + conn_state = aconnector->base.state; + + DRM_DEBUG_DRIVER("[HDCP_DM] display %d, CP %d, type %d\n", aconnector->base.index, +- (!!aconnector->base.state) ? aconnector->base.state->content_protection : -1, +- (!!aconnector->base.state) ? aconnector->base.state->hdcp_content_type : -1); ++ (!!aconnector->base.state) ? ++ aconnector->base.state->content_protection : -1, ++ (!!aconnector->base.state) ? ++ aconnector->base.state->hdcp_content_type : -1); + + if (conn_state) + hdcp_update_display(hdcp_work, link_index, aconnector, +- conn_state->hdcp_content_type, false); ++ conn_state->hdcp_content_type, false); + } + +- +-/* NOTE: From the usermodes prospective you only need to call write *ONCE*, the kernel ++/** ++ * DOC: Add sysfs interface for set/get srm ++ * ++ * NOTE: From the usermodes prospective you only need to call write *ONCE*, the kernel + * will automatically call once or twice depending on the size + * + * call: "cat file > /sys/class/drm/card0/device/hdcp_srm" from usermode no matter what the size is +@@ -594,23 +590,23 @@ static void update_config(void *handle, struct cp_psp_stream_config *config) + * sysfs interface doesn't tell us the size we will get so we are sending partial SRMs to psp and on + * the last call we will send the full SRM. PSP will fail on every call before the last. + * +- * This means we don't know if the SRM is good until the last call. And because of this limitation we +- * cannot throw errors early as it will stop the kernel from writing to sysfs ++ * This means we don't know if the SRM is good until the last call. And because of this ++ * limitation we cannot throw errors early as it will stop the kernel from writing to sysfs + * + * Example 1: +- * Good SRM size = 5096 +- * first call to write 4096 -> PSP fails +- * Second call to write 1000 -> PSP Pass -> SRM is set ++ * Good SRM size = 5096 ++ * first call to write 4096 -> PSP fails ++ * Second call to write 1000 -> PSP Pass -> SRM is set + * + * Example 2: +- * Bad SRM size = 4096 +- * first call to write 4096 -> PSP fails (This is the same as above, but we don't know if this +- * is the last call) ++ * Bad SRM size = 4096 ++ * first call to write 4096 -> PSP fails (This is the same as above, but we don't know if this ++ * is the last call) + * + * Solution?: +- * 1: Parse the SRM? -> It is signed so we don't know the EOF +- * 2: We can have another sysfs that passes the size before calling set. -> simpler solution +- * below ++ * 1: Parse the SRM? -> It is signed so we don't know the EOF ++ * 2: We can have another sysfs that passes the size before calling set. -> simpler solution ++ * below + * + * Easy Solution: + * Always call get after Set to verify if set was successful. +@@ -619,20 +615,21 @@ static void update_config(void *handle, struct cp_psp_stream_config *config) + * +----------------------+ + * PSP will only update its srm if its older than the one we are trying to load. + * Always do set first than get. +- * -if we try to "1. SET" a older version PSP will reject it and we can "2. GET" the newer +- * version and save it ++ * -if we try to "1. SET" a older version PSP will reject it and we can "2. GET" the newer ++ * version and save it + * +- * -if we try to "1. SET" a newer version PSP will accept it and we can "2. GET" the +- * same(newer) version back and save it ++ * -if we try to "1. SET" a newer version PSP will accept it and we can "2. GET" the ++ * same(newer) version back and save it + * +- * -if we try to "1. SET" a newer version and PSP rejects it. That means the format is +- * incorrect/corrupted and we should correct our SRM by getting it from PSP ++ * -if we try to "1. SET" a newer version and PSP rejects it. That means the format is ++ * incorrect/corrupted and we should correct our SRM by getting it from PSP + */ +-static ssize_t srm_data_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, ++static ssize_t srm_data_write(struct file *filp, struct kobject *kobj, ++ struct bin_attribute *bin_attr, char *buffer, + loff_t pos, size_t count) + { + struct hdcp_workqueue *work; +- uint32_t srm_version = 0; ++ u32 srm_version = 0; + + work = container_of(bin_attr, struct hdcp_workqueue, attr); + link_lock(work, true); +@@ -646,19 +643,19 @@ static ssize_t srm_data_write(struct file *filp, struct kobject *kobj, struct bi + work->srm_version = srm_version; + } + +- + link_lock(work, false); + + return count; + } + +-static ssize_t srm_data_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, ++static ssize_t srm_data_read(struct file *filp, struct kobject *kobj, ++ struct bin_attribute *bin_attr, char *buffer, + loff_t pos, size_t count) + { + struct hdcp_workqueue *work; +- uint8_t *srm = NULL; +- uint32_t srm_version; +- uint32_t srm_size; ++ u8 *srm = NULL; ++ u32 srm_version; ++ u32 srm_size; + size_t ret = count; + + work = container_of(bin_attr, struct hdcp_workqueue, attr); +@@ -691,12 +688,12 @@ static ssize_t srm_data_read(struct file *filp, struct kobject *kobj, struct bin + /* From the hdcp spec (5.Renewability) SRM needs to be stored in a non-volatile memory. + * + * For example, +- * if Application "A" sets the SRM (ver 2) and we reboot/suspend and later when Application "B" +- * needs to use HDCP, the version in PSP should be SRM(ver 2). So SRM should be persistent +- * across boot/reboots/suspend/resume/shutdown ++ * if Application "A" sets the SRM (ver 2) and we reboot/suspend and later when Application "B" ++ * needs to use HDCP, the version in PSP should be SRM(ver 2). So SRM should be persistent ++ * across boot/reboots/suspend/resume/shutdown + * +- * Currently when the system goes down (suspend/shutdown) the SRM is cleared from PSP. For HDCP we need +- * to make the SRM persistent. ++ * Currently when the system goes down (suspend/shutdown) the SRM is cleared from PSP. For HDCP ++ * we need to make the SRM persistent. + * + * -PSP owns the checking of SRM but doesn't have the ability to store it in a non-volatile memory. + * -The kernel cannot write to the file systems. +@@ -706,8 +703,8 @@ static ssize_t srm_data_read(struct file *filp, struct kobject *kobj, struct bin + * + * Usermode can read/write to/from PSP using the sysfs interface + * For example: +- * to save SRM from PSP to storage : cat /sys/class/drm/card0/device/hdcp_srm > srmfile +- * to load from storage to PSP: cat srmfile > /sys/class/drm/card0/device/hdcp_srm ++ * to save SRM from PSP to storage : cat /sys/class/drm/card0/device/hdcp_srm > srmfile ++ * to load from storage to PSP: cat srmfile > /sys/class/drm/card0/device/hdcp_srm + */ + static const struct bin_attribute data_attr = { + .attr = {.name = "hdcp_srm", .mode = 0664}, +@@ -716,10 +713,9 @@ static const struct bin_attribute data_attr = { + .read = srm_data_read, + }; + +- +-struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct cp_psp *cp_psp, struct dc *dc) ++struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, ++ struct cp_psp *cp_psp, struct dc *dc) + { +- + int max_caps = dc->caps.max_links; + struct hdcp_workqueue *hdcp_work; + int i = 0; +@@ -728,14 +724,16 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct + if (ZERO_OR_NULL_PTR(hdcp_work)) + return NULL; + +- hdcp_work->srm = kcalloc(PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE, sizeof(*hdcp_work->srm), GFP_KERNEL); ++ hdcp_work->srm = kcalloc(PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE, ++ sizeof(*hdcp_work->srm), GFP_KERNEL); + +- if (hdcp_work->srm == NULL) ++ if (!hdcp_work->srm) + goto fail_alloc_context; + +- hdcp_work->srm_temp = kcalloc(PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE, sizeof(*hdcp_work->srm_temp), GFP_KERNEL); ++ hdcp_work->srm_temp = kcalloc(PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE, ++ sizeof(*hdcp_work->srm_temp), GFP_KERNEL); + +- if (hdcp_work->srm_temp == NULL) ++ if (!hdcp_work->srm_temp) + goto fail_alloc_context; + + hdcp_work->max_link = max_caps; +@@ -788,10 +786,5 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct + kfree(hdcp_work); + + return NULL; +- +- +- + } + +- +- +-- +2.39.5 + diff --git a/queue-6.1/drm-amd-display-fix-slab-use-after-free-in-hdcp.patch b/queue-6.1/drm-amd-display-fix-slab-use-after-free-in-hdcp.patch new file mode 100644 index 0000000000..bdaa6f22f6 --- /dev/null +++ b/queue-6.1/drm-amd-display-fix-slab-use-after-free-in-hdcp.patch @@ -0,0 +1,177 @@ +From e29a7d118e5e0feb8cda94167b19c7bdd49c58dc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 17 Apr 2025 16:50:05 -0500 +Subject: drm/amd/display: Fix slab-use-after-free in hdcp + +From: Chris Bainbridge + +[ Upstream commit be593d9d91c5a3a363d456b9aceb71029aeb3f1d ] + +The HDCP code in amdgpu_dm_hdcp.c copies pointers to amdgpu_dm_connector +objects without incrementing the kref reference counts. When using a +USB-C dock, and the dock is unplugged, the corresponding +amdgpu_dm_connector objects are freed, creating dangling pointers in the +HDCP code. When the dock is plugged back, the dangling pointers are +dereferenced, resulting in a slab-use-after-free: + +[ 66.775837] BUG: KASAN: slab-use-after-free in event_property_validate+0x42f/0x6c0 [amdgpu] +[ 66.776171] Read of size 4 at addr ffff888127804120 by task kworker/0:1/10 + +[ 66.776179] CPU: 0 UID: 0 PID: 10 Comm: kworker/0:1 Not tainted 6.14.0-rc7-00180-g54505f727a38-dirty #233 +[ 66.776183] Hardware name: HP HP Pavilion Aero Laptop 13-be0xxx/8916, BIOS F.17 12/18/2024 +[ 66.776186] Workqueue: events event_property_validate [amdgpu] +[ 66.776494] Call Trace: +[ 66.776496] +[ 66.776497] dump_stack_lvl+0x70/0xa0 +[ 66.776504] print_report+0x175/0x555 +[ 66.776507] ? __virt_addr_valid+0x243/0x450 +[ 66.776510] ? kasan_complete_mode_report_info+0x66/0x1c0 +[ 66.776515] kasan_report+0xeb/0x1c0 +[ 66.776518] ? event_property_validate+0x42f/0x6c0 [amdgpu] +[ 66.776819] ? event_property_validate+0x42f/0x6c0 [amdgpu] +[ 66.777121] __asan_report_load4_noabort+0x14/0x20 +[ 66.777124] event_property_validate+0x42f/0x6c0 [amdgpu] +[ 66.777342] ? __lock_acquire+0x6b40/0x6b40 +[ 66.777347] ? enable_assr+0x250/0x250 [amdgpu] +[ 66.777571] process_one_work+0x86b/0x1510 +[ 66.777575] ? pwq_dec_nr_in_flight+0xcf0/0xcf0 +[ 66.777578] ? assign_work+0x16b/0x280 +[ 66.777580] ? lock_is_held_type+0xa3/0x130 +[ 66.777583] worker_thread+0x5c0/0xfa0 +[ 66.777587] ? process_one_work+0x1510/0x1510 +[ 66.777588] kthread+0x3a2/0x840 +[ 66.777591] ? kthread_is_per_cpu+0xd0/0xd0 +[ 66.777594] ? trace_hardirqs_on+0x4f/0x60 +[ 66.777597] ? _raw_spin_unlock_irq+0x27/0x60 +[ 66.777599] ? calculate_sigpending+0x77/0xa0 +[ 66.777602] ? kthread_is_per_cpu+0xd0/0xd0 +[ 66.777605] ret_from_fork+0x40/0x90 +[ 66.777607] ? kthread_is_per_cpu+0xd0/0xd0 +[ 66.777609] ret_from_fork_asm+0x11/0x20 +[ 66.777614] + +[ 66.777643] Allocated by task 10: +[ 66.777646] kasan_save_stack+0x39/0x60 +[ 66.777649] kasan_save_track+0x14/0x40 +[ 66.777652] kasan_save_alloc_info+0x37/0x50 +[ 66.777655] __kasan_kmalloc+0xbb/0xc0 +[ 66.777658] __kmalloc_cache_noprof+0x1c8/0x4b0 +[ 66.777661] dm_dp_add_mst_connector+0xdd/0x5c0 [amdgpu] +[ 66.777880] drm_dp_mst_port_add_connector+0x47e/0x770 [drm_display_helper] +[ 66.777892] drm_dp_send_link_address+0x1554/0x2bf0 [drm_display_helper] +[ 66.777901] drm_dp_check_and_send_link_address+0x187/0x1f0 [drm_display_helper] +[ 66.777909] drm_dp_mst_link_probe_work+0x2b8/0x410 [drm_display_helper] +[ 66.777917] process_one_work+0x86b/0x1510 +[ 66.777919] worker_thread+0x5c0/0xfa0 +[ 66.777922] kthread+0x3a2/0x840 +[ 66.777925] ret_from_fork+0x40/0x90 +[ 66.777927] ret_from_fork_asm+0x11/0x20 + +[ 66.777932] Freed by task 1713: +[ 66.777935] kasan_save_stack+0x39/0x60 +[ 66.777938] kasan_save_track+0x14/0x40 +[ 66.777940] kasan_save_free_info+0x3b/0x60 +[ 66.777944] __kasan_slab_free+0x52/0x70 +[ 66.777946] kfree+0x13f/0x4b0 +[ 66.777949] dm_dp_mst_connector_destroy+0xfa/0x150 [amdgpu] +[ 66.778179] drm_connector_free+0x7d/0xb0 +[ 66.778184] drm_mode_object_put.part.0+0xee/0x160 +[ 66.778188] drm_mode_object_put+0x37/0x50 +[ 66.778191] drm_atomic_state_default_clear+0x220/0xd60 +[ 66.778194] __drm_atomic_state_free+0x16e/0x2a0 +[ 66.778197] drm_mode_atomic_ioctl+0x15ed/0x2ba0 +[ 66.778200] drm_ioctl_kernel+0x17a/0x310 +[ 66.778203] drm_ioctl+0x584/0xd10 +[ 66.778206] amdgpu_drm_ioctl+0xd2/0x1c0 [amdgpu] +[ 66.778375] __x64_sys_ioctl+0x139/0x1a0 +[ 66.778378] x64_sys_call+0xee7/0xfb0 +[ 66.778381] do_syscall_64+0x87/0x140 +[ 66.778385] entry_SYSCALL_64_after_hwframe+0x4b/0x53 + +Fix this by properly incrementing and decrementing the reference counts +when making and deleting copies of the amdgpu_dm_connector pointers. + +(Mario: rebase on current code and update fixes tag) + +Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/4006 +Signed-off-by: Chris Bainbridge +Fixes: da3fd7ac0bcf3 ("drm/amd/display: Update CP property based on HW query") +Reviewed-by: Alex Hung +Link: https://lore.kernel.org/r/20250417215005.37964-1-mario.limonciello@amd.com +Signed-off-by: Mario Limonciello +Signed-off-by: Alex Deucher +(cherry picked from commit d4673f3c3b3dcb74e36e53cdfc880baa7a87b330) +Cc: stable@vger.kernel.org +Signed-off-by: Sasha Levin +--- + .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 19 ++++++++++++++++--- + 1 file changed, 16 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +index 6222d5a168832..6110d88efdbba 100644 +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +@@ -173,6 +173,9 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work, + unsigned int conn_index = aconnector->base.index; + + guard(mutex)(&hdcp_w->mutex); ++ drm_connector_get(&aconnector->base); ++ if (hdcp_w->aconnector[conn_index]) ++ drm_connector_put(&hdcp_w->aconnector[conn_index]->base); + hdcp_w->aconnector[conn_index] = aconnector; + + memset(&link_adjust, 0, sizeof(link_adjust)); +@@ -220,7 +223,6 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work, + unsigned int conn_index = aconnector->base.index; + + guard(mutex)(&hdcp_w->mutex); +- hdcp_w->aconnector[conn_index] = aconnector; + + /* the removal of display will invoke auth reset -> hdcp destroy and + * we'd expect the Content Protection (CP) property changed back to +@@ -236,7 +238,10 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work, + } + + mod_hdcp_remove_display(&hdcp_w->hdcp, aconnector->base.index, &hdcp_w->output); +- ++ if (hdcp_w->aconnector[conn_index]) { ++ drm_connector_put(&hdcp_w->aconnector[conn_index]->base); ++ hdcp_w->aconnector[conn_index] = NULL; ++ } + process_output(hdcp_w); + } + +@@ -254,6 +259,10 @@ void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_inde + for (conn_index = 0; conn_index < AMDGPU_DM_MAX_DISPLAY_INDEX; conn_index++) { + hdcp_w->encryption_status[conn_index] = + MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; ++ if (hdcp_w->aconnector[conn_index]) { ++ drm_connector_put(&hdcp_w->aconnector[conn_index]->base); ++ hdcp_w->aconnector[conn_index] = NULL; ++ } + } + + process_output(hdcp_w); +@@ -496,6 +505,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config) + struct hdcp_workqueue *hdcp_work = handle; + struct amdgpu_dm_connector *aconnector = config->dm_stream_ctx; + int link_index = aconnector->dc_link->link_index; ++ unsigned int conn_index = aconnector->base.index; + struct mod_hdcp_display *display = &hdcp_work[link_index].display; + struct mod_hdcp_link *link = &hdcp_work[link_index].link; + struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index]; +@@ -551,7 +561,10 @@ static void update_config(void *handle, struct cp_psp_stream_config *config) + guard(mutex)(&hdcp_w->mutex); + + mod_hdcp_add_display(&hdcp_w->hdcp, link, display, &hdcp_w->output); +- ++ drm_connector_get(&aconnector->base); ++ if (hdcp_w->aconnector[conn_index]) ++ drm_connector_put(&hdcp_w->aconnector[conn_index]->base); ++ hdcp_w->aconnector[conn_index] = aconnector; + process_output(hdcp_w); + } + +-- +2.39.5 + diff --git a/queue-6.1/drm-amd-display-phase2-enable-mst-hdcp-multiple-disp.patch b/queue-6.1/drm-amd-display-phase2-enable-mst-hdcp-multiple-disp.patch new file mode 100644 index 0000000000..82d2ccb056 --- /dev/null +++ b/queue-6.1/drm-amd-display-phase2-enable-mst-hdcp-multiple-disp.patch @@ -0,0 +1,306 @@ +From 713c15fbc2d53a2a0b7b6689297e4d23a3d9c96b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 14 Nov 2022 14:29:56 -0500 +Subject: drm/amd/display: phase2 enable mst hdcp multiple displays + +From: hersen wu + +[ Upstream commit aa9fdd5d5add50305d2022fa072fe6f189283415 ] + +[why] +For MST topology with 1 physical link and multiple connectors (>=2), +e.g. daisy cahined MST + SST, or 1-to-multi MST hub, if userspace +set to enable the HDCP simultaneously on all connected outputs, the +commit tail iteratively call the hdcp_update_display() for each +display (connector). However, the hdcp workqueue data structure for +each link has only one DM connector and encryption status members, +which means the work queue of property_validate/update() would only +be triggered for the last connector within this physical link, and +therefore the HDCP property value of other connectors would stay on +DESIRED instead of switching to ENABLED, which is NOT as expected. + +[how] +Use array of AMDGPU_DM_MAX_DISPLAY_INDEX for both aconnector and +encryption status in hdcp workqueue data structure for each physical +link. For property validate/update work queue, we iterates over the +array and do similar operation/check for each connected display. + +Tested-by: Daniel Wheeler +Signed-off-by: hersen wu +Signed-off-by: Alex Deucher +Stable-dep-of: be593d9d91c5 ("drm/amd/display: Fix slab-use-after-free in hdcp") +Signed-off-by: Sasha Levin +--- + .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 160 +++++++++++++----- + .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.h | 5 +- + 2 files changed, 122 insertions(+), 43 deletions(-) + +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +index 3f211c0308a2f..7fc26ca30dcd6 100644 +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +@@ -170,9 +170,10 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work, + struct mod_hdcp_display *display = &hdcp_work[link_index].display; + struct mod_hdcp_link *link = &hdcp_work[link_index].link; + struct mod_hdcp_display_query query; ++ unsigned int conn_index = aconnector->base.index; + + mutex_lock(&hdcp_w->mutex); +- hdcp_w->aconnector = aconnector; ++ hdcp_w->aconnector[conn_index] = aconnector; + + query.display = NULL; + mod_hdcp_query_display(&hdcp_w->hdcp, aconnector->base.index, &query); +@@ -204,7 +205,7 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work, + msecs_to_jiffies(DRM_HDCP_CHECK_PERIOD_MS)); + } else { + display->adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION; +- hdcp_w->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; ++ hdcp_w->encryption_status[conn_index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; + cancel_delayed_work(&hdcp_w->property_validate_dwork); + } + +@@ -223,9 +224,10 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work, + { + struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index]; + struct drm_connector_state *conn_state = aconnector->base.state; ++ unsigned int conn_index = aconnector->base.index; + + mutex_lock(&hdcp_w->mutex); +- hdcp_w->aconnector = aconnector; ++ hdcp_w->aconnector[conn_index] = aconnector; + + /* the removal of display will invoke auth reset -> hdcp destroy and + * we'd expect the Content Protection (CP) property changed back to +@@ -247,13 +249,18 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work, + void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_index) + { + struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index]; ++ unsigned int conn_index; + + mutex_lock(&hdcp_w->mutex); + + mod_hdcp_reset_connection(&hdcp_w->hdcp, &hdcp_w->output); + + cancel_delayed_work(&hdcp_w->property_validate_dwork); +- hdcp_w->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; ++ ++ for (conn_index = 0; conn_index < AMDGPU_DM_MAX_DISPLAY_INDEX; conn_index++) { ++ hdcp_w->encryption_status[conn_index] = ++ MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; ++ } + + process_output(hdcp_w); + +@@ -290,49 +297,83 @@ static void event_callback(struct work_struct *work) + + + } ++ + static void event_property_update(struct work_struct *work) + { +- + struct hdcp_workqueue *hdcp_work = container_of(work, struct hdcp_workqueue, property_update_work); +- struct amdgpu_dm_connector *aconnector = hdcp_work->aconnector; +- struct drm_device *dev = hdcp_work->aconnector->base.dev; ++ struct amdgpu_dm_connector *aconnector = NULL; ++ struct drm_device *dev; + long ret; ++ unsigned int conn_index; ++ struct drm_connector *connector; ++ struct drm_connector_state *conn_state; + +- drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); +- mutex_lock(&hdcp_work->mutex); ++ for (conn_index = 0; conn_index < AMDGPU_DM_MAX_DISPLAY_INDEX; conn_index++) { ++ aconnector = hdcp_work->aconnector[conn_index]; + ++ if (!aconnector) ++ continue; + +- if (aconnector->base.state && aconnector->base.state->commit) { +- ret = wait_for_completion_interruptible_timeout(&aconnector->base.state->commit->hw_done, 10 * HZ); ++ if (!aconnector->base.index) ++ continue; + +- if (ret == 0) { +- DRM_ERROR("HDCP state unknown! Setting it to DESIRED"); +- hdcp_work->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; +- } +- } ++ connector = &aconnector->base; ++ ++ /* check if display connected */ ++ if (connector->status != connector_status_connected) ++ continue; + +- if (aconnector->base.state) { +- if (hdcp_work->encryption_status != MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF) { +- if (aconnector->base.state->hdcp_content_type == ++ conn_state = aconnector->base.state; ++ ++ if (!conn_state) ++ continue; ++ ++ dev = connector->dev; ++ ++ if (!dev) ++ continue; ++ ++ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); ++ mutex_lock(&hdcp_work->mutex); ++ ++ if (conn_state->commit) { ++ ret = wait_for_completion_interruptible_timeout( ++ &conn_state->commit->hw_done, 10 * HZ); ++ if (ret == 0) { ++ DRM_ERROR( ++ "HDCP state unknown! Setting it to DESIRED"); ++ hdcp_work->encryption_status[conn_index] = ++ MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; ++ } ++ } ++ if (hdcp_work->encryption_status[conn_index] != ++ MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF) { ++ if (conn_state->hdcp_content_type == + DRM_MODE_HDCP_CONTENT_TYPE0 && +- hdcp_work->encryption_status <= +- MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON) +- drm_hdcp_update_content_protection(&aconnector->base, ++ hdcp_work->encryption_status[conn_index] <= ++ MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON) { ++ ++ DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_ENABLED\n"); ++ drm_hdcp_update_content_protection( ++ connector, + DRM_MODE_CONTENT_PROTECTION_ENABLED); +- else if (aconnector->base.state->hdcp_content_type == ++ } else if (conn_state->hdcp_content_type == + DRM_MODE_HDCP_CONTENT_TYPE1 && +- hdcp_work->encryption_status == +- MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON) +- drm_hdcp_update_content_protection(&aconnector->base, ++ hdcp_work->encryption_status[conn_index] == ++ MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON) { ++ drm_hdcp_update_content_protection( ++ connector, + DRM_MODE_CONTENT_PROTECTION_ENABLED); ++ } + } else { +- drm_hdcp_update_content_protection(&aconnector->base, +- DRM_MODE_CONTENT_PROTECTION_DESIRED); ++ DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_DESIRED\n"); ++ drm_hdcp_update_content_protection( ++ connector, DRM_MODE_CONTENT_PROTECTION_DESIRED); ++ + } ++ mutex_unlock(&hdcp_work->mutex); ++ drm_modeset_unlock(&dev->mode_config.connection_mutex); + } +- +- mutex_unlock(&hdcp_work->mutex); +- drm_modeset_unlock(&dev->mode_config.connection_mutex); + } + + static void event_property_validate(struct work_struct *work) +@@ -340,19 +381,51 @@ static void event_property_validate(struct work_struct *work) + struct hdcp_workqueue *hdcp_work = + container_of(to_delayed_work(work), struct hdcp_workqueue, property_validate_dwork); + struct mod_hdcp_display_query query; +- struct amdgpu_dm_connector *aconnector = hdcp_work->aconnector; +- +- if (!aconnector) +- return; ++ struct amdgpu_dm_connector *aconnector; ++ unsigned int conn_index; + + mutex_lock(&hdcp_work->mutex); + +- query.encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; +- mod_hdcp_query_display(&hdcp_work->hdcp, aconnector->base.index, &query); ++ for (conn_index = 0; conn_index < AMDGPU_DM_MAX_DISPLAY_INDEX; ++ conn_index++) { ++ aconnector = hdcp_work->aconnector[conn_index]; ++ ++ ++ if (!aconnector) ++ continue; ++ ++ if (!aconnector->base.index) ++ continue; ++ ++ /* check if display connected */ ++ if (aconnector->base.status != connector_status_connected) ++ continue; + +- if (query.encryption_status != hdcp_work->encryption_status) { +- hdcp_work->encryption_status = query.encryption_status; +- schedule_work(&hdcp_work->property_update_work); ++ if (!aconnector->base.state) ++ continue; ++ ++ query.encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; ++ mod_hdcp_query_display(&hdcp_work->hdcp, aconnector->base.index, ++ &query); ++ ++ DRM_DEBUG_DRIVER("[HDCP_DM] disp %d, connector->CP %u, (query, work): (%d, %d)\n", ++ aconnector->base.index, ++ aconnector->base.state->content_protection, ++ query.encryption_status, ++ hdcp_work->encryption_status[conn_index]); ++ ++ if (query.encryption_status != ++ hdcp_work->encryption_status[conn_index]) { ++ DRM_DEBUG_DRIVER("[HDCP_DM] encryption_status change from %x to %x\n", ++ hdcp_work->encryption_status[conn_index], query.encryption_status); ++ ++ hdcp_work->encryption_status[conn_index] = ++ query.encryption_status; ++ ++ DRM_DEBUG_DRIVER("[HDCP_DM] trigger property_update_work\n"); ++ ++ schedule_work(&hdcp_work->property_update_work); ++ } + } + + mutex_unlock(&hdcp_work->mutex); +@@ -687,6 +760,13 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct + hdcp_work[i].hdcp.config.ddc.funcs.read_i2c = lp_read_i2c; + hdcp_work[i].hdcp.config.ddc.funcs.write_dpcd = lp_write_dpcd; + hdcp_work[i].hdcp.config.ddc.funcs.read_dpcd = lp_read_dpcd; ++ ++ memset(hdcp_work[i].aconnector, 0, ++ sizeof(struct amdgpu_dm_connector *) * ++ AMDGPU_DM_MAX_DISPLAY_INDEX); ++ memset(hdcp_work[i].encryption_status, 0, ++ sizeof(enum mod_hdcp_encryption_status) * ++ AMDGPU_DM_MAX_DISPLAY_INDEX); + } + + cp_psp->funcs.update_stream_config = update_config; +diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h +index bbbf7d0eff82f..69b445b011c8c 100644 +--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h ++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h +@@ -43,7 +43,7 @@ struct hdcp_workqueue { + struct delayed_work callback_dwork; + struct delayed_work watchdog_timer_dwork; + struct delayed_work property_validate_dwork; +- struct amdgpu_dm_connector *aconnector; ++ struct amdgpu_dm_connector *aconnector[AMDGPU_DM_MAX_DISPLAY_INDEX]; + struct mutex mutex; + + struct mod_hdcp hdcp; +@@ -51,8 +51,7 @@ struct hdcp_workqueue { + struct mod_hdcp_display display; + struct mod_hdcp_link link; + +- enum mod_hdcp_encryption_status encryption_status; +- ++ enum mod_hdcp_encryption_status encryption_status[AMDGPU_DM_MAX_DISPLAY_INDEX]; + /* when display is unplugged from mst hub, connctor will be + * destroyed within dm_dp_mst_connector_destroy. connector + * hdcp perperties, like type, undesired, desired, enabled, +-- +2.39.5 + diff --git a/queue-6.1/firmware-arm_ffa-skip-rx-buffer-ownership-release-if.patch b/queue-6.1/firmware-arm_ffa-skip-rx-buffer-ownership-release-if.patch new file mode 100644 index 0000000000..9eda2f6200 --- /dev/null +++ b/queue-6.1/firmware-arm_ffa-skip-rx-buffer-ownership-release-if.patch @@ -0,0 +1,52 @@ +From 3d1bbe519609a18914363c561cdbc3f27d88a923 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 21 Mar 2025 11:57:00 +0000 +Subject: firmware: arm_ffa: Skip Rx buffer ownership release if not acquired +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Sudeep Holla + +[ Upstream commit 4567bdaaaaa1744da3d7da07d9aca2f941f5b4e5 ] + +Completion of the FFA_PARTITION_INFO_GET ABI transfers the ownership of +the caller’s Rx buffer from the producer(typically partition mnager) to +the consumer(this driver/OS). FFA_RX_RELEASE transfers the ownership +from the consumer back to the producer. + +However, when we set the flag to just return the count of partitions +deployed in the system corresponding to the specified UUID while +invoking FFA_PARTITION_INFO_GET, the Rx buffer ownership shouldn't be +transferred to this driver. We must be able to skip transferring back +the ownership to the partition manager when we request just to get the +count of the partitions as the buffers are not acquired in this case. + +Firmware may return FFA_RET_DENIED or other error for the ffa_rx_release() +in such cases. + +Fixes: bb1be7498500 ("firmware: arm_ffa: Add v1.1 get_partition_info support") +Message-Id: <20250321115700.3525197-1-sudeep.holla@arm.com> +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_ffa/driver.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c +index e9f86b7573012..e1e278d431e97 100644 +--- a/drivers/firmware/arm_ffa/driver.c ++++ b/drivers/firmware/arm_ffa/driver.c +@@ -306,7 +306,8 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, + memcpy(buffer + idx, drv_info->rx_buffer + idx * sz, + buf_sz); + +- ffa_rx_release(); ++ if (!(flags & PARTITION_INFO_GET_RETURN_COUNT_ONLY)) ++ ffa_rx_release(); + + mutex_unlock(&drv_info->rx_lock); + +-- +2.39.5 + diff --git a/queue-6.1/firmware-arm_scmi-balance-device-refcount-when-destr.patch b/queue-6.1/firmware-arm_scmi-balance-device-refcount-when-destr.patch new file mode 100644 index 0000000000..27cec049b4 --- /dev/null +++ b/queue-6.1/firmware-arm_scmi-balance-device-refcount-when-destr.patch @@ -0,0 +1,86 @@ +From 694e80a2b44920eb504fe9d9f510154d0e23b62b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 6 Mar 2025 18:54:47 +0000 +Subject: firmware: arm_scmi: Balance device refcount when destroying devices + +From: Cristian Marussi + +[ Upstream commit 9ca67840c0ddf3f39407339624cef824a4f27599 ] + +Using device_find_child() to lookup the proper SCMI device to destroy +causes an unbalance in device refcount, since device_find_child() calls an +implicit get_device(): this, in turns, inhibits the call of the provided +release methods upon devices destruction. + +As a consequence, one of the structures that is not freed properly upon +destruction is the internal struct device_private dev->p populated by the +drivers subsystem core. + +KMemleak detects this situation since loading/unloding some SCMI driver +causes related devices to be created/destroyed without calling any +device_release method. + +unreferenced object 0xffff00000f583800 (size 512): + comm "insmod", pid 227, jiffies 4294912190 + hex dump (first 32 bytes): + 00 00 00 00 ad 4e ad de ff ff ff ff 00 00 00 00 .....N.......... + ff ff ff ff ff ff ff ff 60 36 1d 8a 00 80 ff ff ........`6...... + backtrace (crc 114e2eed): + kmemleak_alloc+0xbc/0xd8 + __kmalloc_cache_noprof+0x2dc/0x398 + device_add+0x954/0x12d0 + device_register+0x28/0x40 + __scmi_device_create.part.0+0x1bc/0x380 + scmi_device_create+0x2d0/0x390 + scmi_create_protocol_devices+0x74/0xf8 + scmi_device_request_notifier+0x1f8/0x2a8 + notifier_call_chain+0x110/0x3b0 + blocking_notifier_call_chain+0x70/0xb0 + scmi_driver_register+0x350/0x7f0 + 0xffff80000a3b3038 + do_one_initcall+0x12c/0x730 + do_init_module+0x1dc/0x640 + load_module+0x4b20/0x5b70 + init_module_from_file+0xec/0x158 + +$ ./scripts/faddr2line ./vmlinux device_add+0x954/0x12d0 +device_add+0x954/0x12d0: +kmalloc_noprof at include/linux/slab.h:901 +(inlined by) kzalloc_noprof at include/linux/slab.h:1037 +(inlined by) device_private_init at drivers/base/core.c:3510 +(inlined by) device_add at drivers/base/core.c:3561 + +Balance device refcount by issuing a put_device() on devices found via +device_find_child(). + +Reported-by: Alice Ryhl +Closes: https://lore.kernel.org/linux-arm-kernel/Z8nK3uFkspy61yjP@arm.com/T/#mc1f73a0ea5e41014fa145147b7b839fc988ada8f +CC: Sudeep Holla +CC: Catalin Marinas +Fixes: d4f9dddd21f3 ("firmware: arm_scmi: Add dynamic scmi devices creation") +Signed-off-by: Cristian Marussi +Tested-by: Alice Ryhl +Message-Id: <20250306185447.2039336-1-cristian.marussi@arm.com> +Signed-off-by: Sudeep Holla +Signed-off-by: Sasha Levin +--- + drivers/firmware/arm_scmi/bus.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c +index 35bb70724d44b..8c6f99d15f22f 100644 +--- a/drivers/firmware/arm_scmi/bus.c ++++ b/drivers/firmware/arm_scmi/bus.c +@@ -73,6 +73,9 @@ struct scmi_device *scmi_child_dev_find(struct device *parent, + if (!dev) + return NULL; + ++ /* Drop the refcnt bumped implicitly by device_find_child */ ++ put_device(dev); ++ + return to_scmi_dev(dev); + } + +-- +2.39.5 + diff --git a/queue-6.1/iommu-arm-smmu-v3-fix-iommu_device_probe-bug-due-to-.patch b/queue-6.1/iommu-arm-smmu-v3-fix-iommu_device_probe-bug-due-to-.patch new file mode 100644 index 0000000000..77e9bbda54 --- /dev/null +++ b/queue-6.1/iommu-arm-smmu-v3-fix-iommu_device_probe-bug-due-to-.patch @@ -0,0 +1,123 @@ +From d4de1adc22717fd9c0e1369a984274ec2de9f6eb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 15 Apr 2025 11:56:20 -0700 +Subject: iommu/arm-smmu-v3: Fix iommu_device_probe bug due to duplicated + stream ids + +From: Nicolin Chen + +[ Upstream commit b00d24997a11c10d3e420614f0873b83ce358a34 ] + +ASPEED VGA card has two built-in devices: + 0008:06:00.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge (rev 06) + 0008:07:00.0 VGA compatible controller: ASPEED Technology, Inc. ASPEED Graphics Family (rev 52) + +Its toplogy looks like this: + +-[0008:00]---00.0-[01-09]--+-00.0-[02-09]--+-00.0-[03]----00.0 Sandisk Corp Device 5017 + | +-01.0-[04]-- + | +-02.0-[05]----00.0 NVIDIA Corporation Device + | +-03.0-[06-07]----00.0-[07]----00.0 ASPEED Technology, Inc. ASPEED Graphics Family + | +-04.0-[08]----00.0 Renesas Technology Corp. uPD720201 USB 3.0 Host Controller + | \-05.0-[09]----00.0 Realtek Semiconductor Co., Ltd. RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller + \-00.1 PMC-Sierra Inc. Device 4028 + +The IORT logic populaties two identical IDs into the fwspec->ids array via +DMA aliasing in iort_pci_iommu_init() called by pci_for_each_dma_alias(). + +Though the SMMU driver had been able to handle this situation since commit +563b5cbe334e ("iommu/arm-smmu-v3: Cope with duplicated Stream IDs"), that +got broken by the later commit cdf315f907d4 ("iommu/arm-smmu-v3: Maintain +a SID->device structure"), which ended up with allocating separate streams +with the same stuffing. + +On a kernel prior to v6.15-rc1, there has been an overlooked warning: + pci 0008:07:00.0: vgaarb: setting as boot VGA device + pci 0008:07:00.0: vgaarb: bridge control possible + pci 0008:07:00.0: vgaarb: VGA device added: decodes=io+mem,owns=none,locks=none + pcieport 0008:06:00.0: Adding to iommu group 14 + ast 0008:07:00.0: stream 67328 already in tree <===== WARNING + ast 0008:07:00.0: enabling device (0002 -> 0003) + ast 0008:07:00.0: Using default configuration + ast 0008:07:00.0: AST 2600 detected + ast 0008:07:00.0: [drm] Using analog VGA + ast 0008:07:00.0: [drm] dram MCLK=396 Mhz type=1 bus_width=16 + [drm] Initialized ast 0.1.0 for 0008:07:00.0 on minor 0 + ast 0008:07:00.0: [drm] fb0: astdrmfb frame buffer device + +With v6.15-rc, since the commit bcb81ac6ae3c ("iommu: Get DT/ACPI parsing +into the proper probe path"), the error returned with the warning is moved +to the SMMU device probe flow: + arm_smmu_probe_device+0x15c/0x4c0 + __iommu_probe_device+0x150/0x4f8 + probe_iommu_group+0x44/0x80 + bus_for_each_dev+0x7c/0x100 + bus_iommu_probe+0x48/0x1a8 + iommu_device_register+0xb8/0x178 + arm_smmu_device_probe+0x1350/0x1db0 +which then fails the entire SMMU driver probe: + pci 0008:06:00.0: Adding to iommu group 21 + pci 0008:07:00.0: stream 67328 already in tree + arm-smmu-v3 arm-smmu-v3.9.auto: Failed to register iommu + arm-smmu-v3 arm-smmu-v3.9.auto: probe with driver arm-smmu-v3 failed with error -22 + +Since SMMU driver had been already expecting a potential duplicated Stream +ID in arm_smmu_install_ste_for_dev(), change the arm_smmu_insert_master() +routine to ignore a duplicated ID from the fwspec->sids array as well. + +Note: this has been failing the iommu_device_probe() since 2021, although a +recent iommu commit in v6.15-rc1 that moves iommu_device_probe() started to +fail the SMMU driver probe. Since nobody has cared about DMA Alias support, +leave that as it was but fix the fundamental iommu_device_probe() breakage. + +Fixes: cdf315f907d4 ("iommu/arm-smmu-v3: Maintain a SID->device structure") +Cc: stable@vger.kernel.org +Suggested-by: Jason Gunthorpe +Reviewed-by: Jason Gunthorpe +Signed-off-by: Nicolin Chen +Link: https://lore.kernel.org/r/20250415185620.504299-1-nicolinc@nvidia.com +Signed-off-by: Will Deacon +Signed-off-by: Sasha Levin +--- + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +index 1ab2abab46800..6a60bad48b277 100644 +--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c ++++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +@@ -2612,6 +2612,7 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu, + mutex_lock(&smmu->streams_mutex); + for (i = 0; i < fwspec->num_ids; i++) { + struct arm_smmu_stream *new_stream = &master->streams[i]; ++ struct rb_node *existing; + u32 sid = fwspec->ids[i]; + + new_stream->id = sid; +@@ -2622,10 +2623,20 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu, + break; + + /* Insert into SID tree */ +- if (rb_find_add(&new_stream->node, &smmu->streams, +- arm_smmu_streams_cmp_node)) { +- dev_warn(master->dev, "stream %u already in tree\n", +- sid); ++ existing = rb_find_add(&new_stream->node, &smmu->streams, ++ arm_smmu_streams_cmp_node); ++ if (existing) { ++ struct arm_smmu_master *existing_master = ++ rb_entry(existing, struct arm_smmu_stream, node) ++ ->master; ++ ++ /* Bridged PCI devices may end up with duplicated IDs */ ++ if (existing_master == master) ++ continue; ++ ++ dev_warn(master->dev, ++ "stream %u already in tree from dev %s\n", sid, ++ dev_name(existing_master->dev)); + ret = -EINVAL; + break; + } +-- +2.39.5 + diff --git a/queue-6.1/iommu-arm-smmu-v3-use-the-new-rb-tree-helpers.patch b/queue-6.1/iommu-arm-smmu-v3-use-the-new-rb-tree-helpers.patch new file mode 100644 index 0000000000..20592fd58b --- /dev/null +++ b/queue-6.1/iommu-arm-smmu-v3-use-the-new-rb-tree-helpers.patch @@ -0,0 +1,140 @@ +From 8d69ac0bba7c2b38564e68d8c571cef24d65f101 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 6 Aug 2024 20:31:15 -0300 +Subject: iommu/arm-smmu-v3: Use the new rb tree helpers + +From: Jason Gunthorpe + +[ Upstream commit a2bb820e862d61f9ca1499e500915f9f505a2655 ] + +Since v5.12 the rbtree has gained some simplifying helpers aimed at making +rb tree users write less convoluted boiler plate code. Instead the caller +provides a single comparison function and the helpers generate the prior +open-coded stuff. + +Update smmu->streams to use rb_find_add() and rb_find(). + +Tested-by: Nicolin Chen +Reviewed-by: Mostafa Saleh +Signed-off-by: Jason Gunthorpe +Link: https://lore.kernel.org/r/1-v3-9fef8cdc2ff6+150d1-smmuv3_tidy_jgg@nvidia.com +Signed-off-by: Will Deacon +Stable-dep-of: b00d24997a11 ("iommu/arm-smmu-v3: Fix iommu_device_probe bug due to duplicated stream ids") +Signed-off-by: Sasha Levin +--- + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 68 ++++++++++----------- + 1 file changed, 31 insertions(+), 37 deletions(-) + +diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +index 96b72f3dad0d0..1ab2abab46800 100644 +--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c ++++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +@@ -1443,26 +1443,37 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) + return 0; + } + ++static int arm_smmu_streams_cmp_key(const void *lhs, const struct rb_node *rhs) ++{ ++ struct arm_smmu_stream *stream_rhs = ++ rb_entry(rhs, struct arm_smmu_stream, node); ++ const u32 *sid_lhs = lhs; ++ ++ if (*sid_lhs < stream_rhs->id) ++ return -1; ++ if (*sid_lhs > stream_rhs->id) ++ return 1; ++ return 0; ++} ++ ++static int arm_smmu_streams_cmp_node(struct rb_node *lhs, ++ const struct rb_node *rhs) ++{ ++ return arm_smmu_streams_cmp_key( ++ &rb_entry(lhs, struct arm_smmu_stream, node)->id, rhs); ++} ++ + static struct arm_smmu_master * + arm_smmu_find_master(struct arm_smmu_device *smmu, u32 sid) + { + struct rb_node *node; +- struct arm_smmu_stream *stream; + + lockdep_assert_held(&smmu->streams_mutex); + +- node = smmu->streams.rb_node; +- while (node) { +- stream = rb_entry(node, struct arm_smmu_stream, node); +- if (stream->id < sid) +- node = node->rb_right; +- else if (stream->id > sid) +- node = node->rb_left; +- else +- return stream->master; +- } +- +- return NULL; ++ node = rb_find(&sid, &smmu->streams, arm_smmu_streams_cmp_key); ++ if (!node) ++ return NULL; ++ return rb_entry(node, struct arm_smmu_stream, node)->master; + } + + /* IRQ and event handlers */ +@@ -2590,8 +2601,6 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu, + { + int i; + int ret = 0; +- struct arm_smmu_stream *new_stream, *cur_stream; +- struct rb_node **new_node, *parent_node = NULL; + struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev); + + master->streams = kcalloc(fwspec->num_ids, sizeof(*master->streams), +@@ -2602,9 +2611,9 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu, + + mutex_lock(&smmu->streams_mutex); + for (i = 0; i < fwspec->num_ids; i++) { ++ struct arm_smmu_stream *new_stream = &master->streams[i]; + u32 sid = fwspec->ids[i]; + +- new_stream = &master->streams[i]; + new_stream->id = sid; + new_stream->master = master; + +@@ -2613,28 +2622,13 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu, + break; + + /* Insert into SID tree */ +- new_node = &(smmu->streams.rb_node); +- while (*new_node) { +- cur_stream = rb_entry(*new_node, struct arm_smmu_stream, +- node); +- parent_node = *new_node; +- if (cur_stream->id > new_stream->id) { +- new_node = &((*new_node)->rb_left); +- } else if (cur_stream->id < new_stream->id) { +- new_node = &((*new_node)->rb_right); +- } else { +- dev_warn(master->dev, +- "stream %u already in tree\n", +- cur_stream->id); +- ret = -EINVAL; +- break; +- } +- } +- if (ret) ++ if (rb_find_add(&new_stream->node, &smmu->streams, ++ arm_smmu_streams_cmp_node)) { ++ dev_warn(master->dev, "stream %u already in tree\n", ++ sid); ++ ret = -EINVAL; + break; +- +- rb_link_node(&new_stream->node, parent_node, new_node); +- rb_insert_color(&new_stream->node, &smmu->streams); ++ } + } + + if (ret) { +-- +2.39.5 + diff --git a/queue-6.1/irqchip-gic-v2m-mark-a-few-functions-__init.patch b/queue-6.1/irqchip-gic-v2m-mark-a-few-functions-__init.patch new file mode 100644 index 0000000000..68099852f8 --- /dev/null +++ b/queue-6.1/irqchip-gic-v2m-mark-a-few-functions-__init.patch @@ -0,0 +1,72 @@ +From 6b3dd655c1d4d29b0c1e15f6fa8f68f3be8f3c54 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 21 Nov 2022 15:39:33 +0100 +Subject: irqchip/gic-v2m: Mark a few functions __init + +From: Thomas Gleixner + +[ Upstream commit d51a15af37ce8cf59e73de51dcdce3c9f4944974 ] + +They are all part of the init sequence. + +Signed-off-by: Thomas Gleixner +Acked-by: Marc Zyngier +Link: https://lore.kernel.org/r/20221121140048.534395323@linutronix.de +Stable-dep-of: 3318dc299b07 ("irqchip/gic-v2m: Prevent use after free of gicv2m_get_fwnode()") +Signed-off-by: Sasha Levin +--- + drivers/irqchip/irq-gic-v2m.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c +index 414cd925064f4..c2e52c9a38546 100644 +--- a/drivers/irqchip/irq-gic-v2m.c ++++ b/drivers/irqchip/irq-gic-v2m.c +@@ -262,7 +262,7 @@ static struct msi_domain_info gicv2m_pmsi_domain_info = { + .chip = &gicv2m_pmsi_irq_chip, + }; + +-static void gicv2m_teardown(void) ++static void __init gicv2m_teardown(void) + { + struct v2m_data *v2m, *tmp; + +@@ -277,7 +277,7 @@ static void gicv2m_teardown(void) + } + } + +-static int gicv2m_allocate_domains(struct irq_domain *parent) ++static __init int gicv2m_allocate_domains(struct irq_domain *parent) + { + struct irq_domain *inner_domain, *pci_domain, *plat_domain; + struct v2m_data *v2m; +@@ -404,7 +404,7 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode, + return ret; + } + +-static const struct of_device_id gicv2m_device_id[] = { ++static __initconst struct of_device_id gicv2m_device_id[] = { + { .compatible = "arm,gic-v2m-frame", }, + {}, + }; +@@ -454,7 +454,7 @@ static int __init gicv2m_of_init(struct fwnode_handle *parent_handle, + #ifdef CONFIG_ACPI + static int acpi_num_msi; + +-static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev) ++static __init struct fwnode_handle *gicv2m_get_fwnode(struct device *dev) + { + struct v2m_data *data; + +@@ -469,7 +469,7 @@ static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev) + return data->fwnode; + } + +-static bool acpi_check_amazon_graviton_quirks(void) ++static __init bool acpi_check_amazon_graviton_quirks(void) + { + static struct acpi_table_madt *madt; + acpi_status status; +-- +2.39.5 + diff --git a/queue-6.1/irqchip-gic-v2m-prevent-use-after-free-of-gicv2m_get.patch b/queue-6.1/irqchip-gic-v2m-prevent-use-after-free-of-gicv2m_get.patch new file mode 100644 index 0000000000..e53193d3a2 --- /dev/null +++ b/queue-6.1/irqchip-gic-v2m-prevent-use-after-free-of-gicv2m_get.patch @@ -0,0 +1,51 @@ +From d3b764f43a2f9040bd08c4edd77873671eab55bf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 22 Apr 2025 17:16:16 +0100 +Subject: irqchip/gic-v2m: Prevent use after free of gicv2m_get_fwnode() + +From: Suzuki K Poulose + +[ Upstream commit 3318dc299b072a0511d6dfd8367f3304fb6d9827 ] + +With ACPI in place, gicv2m_get_fwnode() is registered with the pci +subsystem as pci_msi_get_fwnode_cb(), which may get invoked at runtime +during a PCI host bridge probe. But, the call back is wrongly marked as +__init, causing it to be freed, while being registered with the PCI +subsystem and could trigger: + + Unable to handle kernel paging request at virtual address ffff8000816c0400 + gicv2m_get_fwnode+0x0/0x58 (P) + pci_set_bus_msi_domain+0x74/0x88 + pci_register_host_bridge+0x194/0x548 + +This is easily reproducible on a Juno board with ACPI boot. + +Retain the function for later use. + +Fixes: 0644b3daca28 ("irqchip/gic-v2m: acpi: Introducing GICv2m ACPI support") +Signed-off-by: Suzuki K Poulose +Signed-off-by: Thomas Gleixner +Signed-off-by: Ingo Molnar +Reviewed-by: Marc Zyngier +Cc: stable@vger.kernel.org +Signed-off-by: Sasha Levin +--- + drivers/irqchip/irq-gic-v2m.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c +index c2e52c9a38546..c04f2481068bb 100644 +--- a/drivers/irqchip/irq-gic-v2m.c ++++ b/drivers/irqchip/irq-gic-v2m.c +@@ -454,7 +454,7 @@ static int __init gicv2m_of_init(struct fwnode_handle *parent_handle, + #ifdef CONFIG_ACPI + static int acpi_num_msi; + +-static __init struct fwnode_handle *gicv2m_get_fwnode(struct device *dev) ++static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev) + { + struct v2m_data *data; + +-- +2.39.5 + diff --git a/queue-6.1/memcg-drain-obj-stock-on-cpu-hotplug-teardown.patch b/queue-6.1/memcg-drain-obj-stock-on-cpu-hotplug-teardown.patch new file mode 100644 index 0000000000..63b931522d --- /dev/null +++ b/queue-6.1/memcg-drain-obj-stock-on-cpu-hotplug-teardown.patch @@ -0,0 +1,55 @@ +From 23250aec43b05632c5aa65fc4a8c7061677e49e2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 10 Mar 2025 16:09:34 -0700 +Subject: memcg: drain obj stock on cpu hotplug teardown + +From: Shakeel Butt + +[ Upstream commit 9f01b4954490d4ccdbcc2b9be34a9921ceee9cbb ] + +Currently on cpu hotplug teardown, only memcg stock is drained but we +need to drain the obj stock as well otherwise we will miss the stats +accumulated on the target cpu as well as the nr_bytes cached. The stats +include MEMCG_KMEM, NR_SLAB_RECLAIMABLE_B & NR_SLAB_UNRECLAIMABLE_B. In +addition we are leaking reference to struct obj_cgroup object. + +Link: https://lkml.kernel.org/r/20250310230934.2913113-1-shakeel.butt@linux.dev +Fixes: bf4f059954dc ("mm: memcg/slab: obj_cgroup API") +Signed-off-by: Shakeel Butt +Reviewed-by: Roman Gushchin +Acked-by: Johannes Weiner +Cc: Michal Hocko +Cc: Muchun Song +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Sasha Levin +--- + mm/memcontrol.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/mm/memcontrol.c b/mm/memcontrol.c +index 3f7cab196eb62..8c586133abb7c 100644 +--- a/mm/memcontrol.c ++++ b/mm/memcontrol.c +@@ -2368,9 +2368,18 @@ static void drain_all_stock(struct mem_cgroup *root_memcg) + static int memcg_hotplug_cpu_dead(unsigned int cpu) + { + struct memcg_stock_pcp *stock; ++ struct obj_cgroup *old; ++ unsigned long flags; + + stock = &per_cpu(memcg_stock, cpu); ++ ++ /* drain_obj_stock requires stock_lock */ ++ local_lock_irqsave(&memcg_stock.stock_lock, flags); ++ old = drain_obj_stock(stock); ++ local_unlock_irqrestore(&memcg_stock.stock_lock, flags); ++ + drain_stock(stock); ++ obj_cgroup_put(old); + + return 0; + } +-- +2.39.5 + diff --git a/queue-6.1/net-phy-microchip-force-irq-polling-mode-for-lan88xx.patch b/queue-6.1/net-phy-microchip-force-irq-polling-mode-for-lan88xx.patch new file mode 100644 index 0000000000..e00051d095 --- /dev/null +++ b/queue-6.1/net-phy-microchip-force-irq-polling-mode-for-lan88xx.patch @@ -0,0 +1,111 @@ +From 57c6431bc0f9c39d8128e45de7ec69e307d696b8 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 16 Apr 2025 12:24:13 +0200 +Subject: net: phy: microchip: force IRQ polling mode for lan88xx + +From: Fiona Klute + +[ Upstream commit 30a41ed32d3088cd0d682a13d7f30b23baed7e93 ] + +With lan88xx based devices the lan78xx driver can get stuck in an +interrupt loop while bringing the device up, flooding the kernel log +with messages like the following: + +lan78xx 2-3:1.0 enp1s0u3: kevent 4 may have been dropped + +Removing interrupt support from the lan88xx PHY driver forces the +driver to use polling instead, which avoids the problem. + +The issue has been observed with Raspberry Pi devices at least since +4.14 (see [1], bug report for their downstream kernel), as well as +with Nvidia devices [2] in 2020, where disabling interrupts was the +vendor-suggested workaround (together with the claim that phylib +changes in 4.9 made the interrupt handling in lan78xx incompatible). + +Iperf reports well over 900Mbits/sec per direction with client in +--dualtest mode, so there does not seem to be a significant impact on +throughput (lan88xx device connected via switch to the peer). + +[1] https://github.com/raspberrypi/linux/issues/2447 +[2] https://forums.developer.nvidia.com/t/jetson-xavier-and-lan7800-problem/142134/11 + +Link: https://lore.kernel.org/0901d90d-3f20-4a10-b680-9c978e04ddda@lunn.ch +Fixes: 792aec47d59d ("add microchip LAN88xx phy driver") +Signed-off-by: Fiona Klute +Cc: kernel-list@raspberrypi.com +Cc: stable@vger.kernel.org +Reviewed-by: Andrew Lunn +Link: https://patch.msgid.link/20250416102413.30654-1-fiona.klute@gmx.de +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +--- + drivers/net/phy/microchip.c | 46 +++---------------------------------- + 1 file changed, 3 insertions(+), 43 deletions(-) + +diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c +index 0b88635f4fbca..623607fd2cefd 100644 +--- a/drivers/net/phy/microchip.c ++++ b/drivers/net/phy/microchip.c +@@ -31,47 +31,6 @@ static int lan88xx_write_page(struct phy_device *phydev, int page) + return __phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, page); + } + +-static int lan88xx_phy_config_intr(struct phy_device *phydev) +-{ +- int rc; +- +- if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { +- /* unmask all source and clear them before enable */ +- rc = phy_write(phydev, LAN88XX_INT_MASK, 0x7FFF); +- rc = phy_read(phydev, LAN88XX_INT_STS); +- rc = phy_write(phydev, LAN88XX_INT_MASK, +- LAN88XX_INT_MASK_MDINTPIN_EN_ | +- LAN88XX_INT_MASK_LINK_CHANGE_); +- } else { +- rc = phy_write(phydev, LAN88XX_INT_MASK, 0); +- if (rc) +- return rc; +- +- /* Ack interrupts after they have been disabled */ +- rc = phy_read(phydev, LAN88XX_INT_STS); +- } +- +- return rc < 0 ? rc : 0; +-} +- +-static irqreturn_t lan88xx_handle_interrupt(struct phy_device *phydev) +-{ +- int irq_status; +- +- irq_status = phy_read(phydev, LAN88XX_INT_STS); +- if (irq_status < 0) { +- phy_error(phydev); +- return IRQ_NONE; +- } +- +- if (!(irq_status & LAN88XX_INT_STS_LINK_CHANGE_)) +- return IRQ_NONE; +- +- phy_trigger_machine(phydev); +- +- return IRQ_HANDLED; +-} +- + static int lan88xx_suspend(struct phy_device *phydev) + { + struct lan88xx_priv *priv = phydev->priv; +@@ -392,8 +351,9 @@ static struct phy_driver microchip_phy_driver[] = { + .config_aneg = lan88xx_config_aneg, + .link_change_notify = lan88xx_link_change_notify, + +- .config_intr = lan88xx_phy_config_intr, +- .handle_interrupt = lan88xx_handle_interrupt, ++ /* Interrupt handling is broken, do not define related ++ * functions to force polling. ++ */ + + .suspend = lan88xx_suspend, + .resume = genphy_resume, +-- +2.39.5 + diff --git a/queue-6.1/revert-drm-meson-vclk-fix-calculation-of-59.94-fract.patch b/queue-6.1/revert-drm-meson-vclk-fix-calculation-of-59.94-fract.patch new file mode 100644 index 0000000000..6b014f8bcf --- /dev/null +++ b/queue-6.1/revert-drm-meson-vclk-fix-calculation-of-59.94-fract.patch @@ -0,0 +1,61 @@ +From af710f97542c389eaa50bc48fc02e7b19c1acebf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 21 Apr 2025 22:12:59 +0200 +Subject: Revert "drm/meson: vclk: fix calculation of 59.94 fractional rates" + +From: Christian Hewitt + +[ Upstream commit f37bb5486ea536c1d61df89feeaeff3f84f0b560 ] + +This reverts commit bfbc68e. + +The patch does permit the offending YUV420 @ 59.94 phy_freq and +vclk_freq mode to match in calculations. It also results in all +fractional rates being unavailable for use. This was unintended +and requires the patch to be reverted. + +Fixes: bfbc68e4d869 ("drm/meson: vclk: fix calculation of 59.94 fractional rates") +Cc: stable@vger.kernel.org +Signed-off-by: Christian Hewitt +Signed-off-by: Martin Blumenstingl +Reviewed-by: Neil Armstrong +Link: https://lore.kernel.org/r/20250421201300.778955-2-martin.blumenstingl@googlemail.com +Signed-off-by: Neil Armstrong +Link: https://lore.kernel.org/r/20250421201300.778955-2-martin.blumenstingl@googlemail.com +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/meson/meson_vclk.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c +index 2a942dc6a6dc2..2a82119eb58ed 100644 +--- a/drivers/gpu/drm/meson/meson_vclk.c ++++ b/drivers/gpu/drm/meson/meson_vclk.c +@@ -790,13 +790,13 @@ meson_vclk_vic_supported_freq(struct meson_drm *priv, unsigned int phy_freq, + FREQ_1000_1001(params[i].pixel_freq)); + DRM_DEBUG_DRIVER("i = %d phy_freq = %d alt = %d\n", + i, params[i].phy_freq, +- FREQ_1000_1001(params[i].phy_freq/1000)*1000); ++ FREQ_1000_1001(params[i].phy_freq/10)*10); + /* Match strict frequency */ + if (phy_freq == params[i].phy_freq && + vclk_freq == params[i].vclk_freq) + return MODE_OK; + /* Match 1000/1001 variant */ +- if (phy_freq == (FREQ_1000_1001(params[i].phy_freq/1000)*1000) && ++ if (phy_freq == (FREQ_1000_1001(params[i].phy_freq/10)*10) && + vclk_freq == FREQ_1000_1001(params[i].vclk_freq)) + return MODE_OK; + } +@@ -1070,7 +1070,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, + + for (freq = 0 ; params[freq].pixel_freq ; ++freq) { + if ((phy_freq == params[freq].phy_freq || +- phy_freq == FREQ_1000_1001(params[freq].phy_freq/1000)*1000) && ++ phy_freq == FREQ_1000_1001(params[freq].phy_freq/10)*10) && + (vclk_freq == params[freq].vclk_freq || + vclk_freq == FREQ_1000_1001(params[freq].vclk_freq))) { + if (vclk_freq != params[freq].vclk_freq) +-- +2.39.5 + diff --git a/queue-6.1/riscv-uprobes-add-missing-fence.i-after-building-the.patch b/queue-6.1/riscv-uprobes-add-missing-fence.i-after-building-the.patch new file mode 100644 index 0000000000..72882fec83 --- /dev/null +++ b/queue-6.1/riscv-uprobes-add-missing-fence.i-after-building-the.patch @@ -0,0 +1,61 @@ +From 07d22744abb51b806341b22c08041e12965cfd8f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 19 Apr 2025 13:14:00 +0200 +Subject: riscv: uprobes: Add missing fence.i after building the XOL buffer +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Björn Töpel + +[ Upstream commit 7d1d19a11cfbfd8bae1d89cc010b2cc397cd0c48 ] + +The XOL (execute out-of-line) buffer is used to single-step the +replaced instruction(s) for uprobes. The RISC-V port was missing a +proper fence.i (i$ flushing) after constructing the XOL buffer, which +can result in incorrect execution of stale/broken instructions. + +This was found running the BPF selftests "test_progs: +uprobe_autoattach, attach_probe" on the Spacemit K1/X60, where the +uprobes tests randomly blew up. + +Reviewed-by: Guo Ren +Fixes: 74784081aac8 ("riscv: Add uprobes supported") +Signed-off-by: Björn Töpel +Link: https://lore.kernel.org/r/20250419111402.1660267-2-bjorn@kernel.org +Signed-off-by: Palmer Dabbelt +Signed-off-by: Sasha Levin +--- + arch/riscv/kernel/probes/uprobes.c | 10 ++-------- + 1 file changed, 2 insertions(+), 8 deletions(-) + +diff --git a/arch/riscv/kernel/probes/uprobes.c b/arch/riscv/kernel/probes/uprobes.c +index 194f166b2cc40..0d18ee53fd649 100644 +--- a/arch/riscv/kernel/probes/uprobes.c ++++ b/arch/riscv/kernel/probes/uprobes.c +@@ -161,6 +161,7 @@ void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr, + /* Initialize the slot */ + void *kaddr = kmap_atomic(page); + void *dst = kaddr + (vaddr & ~PAGE_MASK); ++ unsigned long start = (unsigned long)dst; + + memcpy(dst, src, len); + +@@ -170,13 +171,6 @@ void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr, + *(uprobe_opcode_t *)dst = __BUG_INSN_32; + } + ++ flush_icache_range(start, start + len); + kunmap_atomic(kaddr); +- +- /* +- * We probably need flush_icache_user_page() but it needs vma. +- * This should work on most of architectures by default. If +- * architecture needs to do something different it can define +- * its own version of the function. +- */ +- flush_dcache_page(page); + } +-- +2.39.5 + diff --git a/queue-6.1/series b/queue-6.1/series index f188b48229..db2390e0e8 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -77,3 +77,27 @@ sch_hfsc-make-hfsc_qlen_notify-idempotent.patch sch_qfq-make-qfq_qlen_notify-idempotent.patch sch_ets-make-est_qlen_notify-idempotent.patch revert-x86-kexec-allocate-pgd-for-x86_64-transition-page-tables-separately.patch +firmware-arm_scmi-balance-device-refcount-when-destr.patch +firmware-arm_ffa-skip-rx-buffer-ownership-release-if.patch +arm-dts-opos6ul-add-ksz8081-phy-properties.patch +net-phy-microchip-force-irq-polling-mode-for-lan88xx.patch +revert-drm-meson-vclk-fix-calculation-of-59.94-fract.patch +irqchip-gic-v2m-mark-a-few-functions-__init.patch +irqchip-gic-v2m-prevent-use-after-free-of-gicv2m_get.patch +memcg-drain-obj-stock-on-cpu-hotplug-teardown.patch +riscv-uprobes-add-missing-fence.i-after-building-the.patch +cpufreq-intel_pstate-revise-global-turbo-disable-che.patch +cpufreq-intel_pstate-fold-intel_pstate_max_within_li.patch +cpufreq-intel_pstate-do-not-update-global.turbo_disa.patch +cpufreq-intel_pstate-unchecked-msr-aceess-in-legacy-.patch +spi-tegra114-remove-unnecessary-null-pointer-checks.patch +spi-tegra114-don-t-fail-set_cs_timing-when-delays-ar.patch +iommu-arm-smmu-v3-use-the-new-rb-tree-helpers.patch +iommu-arm-smmu-v3-fix-iommu_device_probe-bug-due-to-.patch +drm-amd-display-phase2-enable-mst-hdcp-multiple-disp.patch +drm-amd-display-clean-up-style-problems-in-amdgpu_dm.patch +drm-amd-display-change-hdcp-update-sequence-for-dm.patch +drm-amd-display-add-scoped-mutexes-for-amdgpu_dm_dhc.patch +drm-amd-display-fix-slab-use-after-free-in-hdcp.patch +asoc-use-of_property_read_bool.patch +asoc-soc-core-stop-using-of_property_read_bool-for-n.patch diff --git a/queue-6.1/spi-tegra114-don-t-fail-set_cs_timing-when-delays-ar.patch b/queue-6.1/spi-tegra114-don-t-fail-set_cs_timing-when-delays-ar.patch new file mode 100644 index 0000000000..820abae578 --- /dev/null +++ b/queue-6.1/spi-tegra114-don-t-fail-set_cs_timing-when-delays-ar.patch @@ -0,0 +1,44 @@ +From 7cbc152533a52b99ee873db1e13646947d8ccc93 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 23 Apr 2025 21:03:03 -0500 +Subject: spi: tegra114: Don't fail set_cs_timing when delays are zero + +From: Aaron Kling + +[ Upstream commit 4426e6b4ecf632bb75d973051e1179b8bfac2320 ] + +The original code would skip null delay pointers, but when the pointers +were converted to point within the spi_device struct, the check was not +updated to skip delays of zero. Hence all spi devices that didn't set +delays would fail to probe. + +Fixes: 04e6bb0d6bb1 ("spi: modify set_cs_timing parameter") +Cc: stable@vger.kernel.org +Signed-off-by: Aaron Kling +Link: https://patch.msgid.link/20250423-spi-tegra114-v1-1-2d608bcc12f9@gmail.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-tegra114.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c +index 6b56108308fc5..60799ab60eb45 100644 +--- a/drivers/spi/spi-tegra114.c ++++ b/drivers/spi/spi-tegra114.c +@@ -729,9 +729,9 @@ static int tegra_spi_set_hw_cs_timing(struct spi_device *spi) + u32 inactive_cycles; + u8 cs_state; + +- if (setup->unit != SPI_DELAY_UNIT_SCK || +- hold->unit != SPI_DELAY_UNIT_SCK || +- inactive->unit != SPI_DELAY_UNIT_SCK) { ++ if ((setup->unit && setup->unit != SPI_DELAY_UNIT_SCK) || ++ (hold->unit && hold->unit != SPI_DELAY_UNIT_SCK) || ++ (inactive->unit && inactive->unit != SPI_DELAY_UNIT_SCK)) { + dev_err(&spi->dev, + "Invalid delay unit %d, should be SPI_DELAY_UNIT_SCK\n", + SPI_DELAY_UNIT_SCK); +-- +2.39.5 + diff --git a/queue-6.1/spi-tegra114-remove-unnecessary-null-pointer-checks.patch b/queue-6.1/spi-tegra114-remove-unnecessary-null-pointer-checks.patch new file mode 100644 index 0000000000..38decc0d75 --- /dev/null +++ b/queue-6.1/spi-tegra114-remove-unnecessary-null-pointer-checks.patch @@ -0,0 +1,74 @@ +From 9f6421ddc72eaeff8e254d44e7d3e650d26f0c5a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 15 Aug 2023 12:20:58 +0300 +Subject: spi: tegra114: Remove unnecessary NULL-pointer checks + +From: Alexander Danilenko + +[ Upstream commit 373c36bf7914e3198ac2654dede499f340c52950 ] + +cs_setup, cs_hold and cs_inactive points to fields of spi_device struct, +so there is no sense in checking them for NULL. + +Found by Linux Verification Center (linuxtesting.org) with SVACE. + +Fixes: 04e6bb0d6bb1 ("spi: modify set_cs_timing parameter") +Signed-off-by: Alexander Danilenko +Link: https://lore.kernel.org/r/20230815092058.4083-1-al.b.danilenko@gmail.com +Signed-off-by: Mark Brown +Stable-dep-of: 4426e6b4ecf6 ("spi: tegra114: Don't fail set_cs_timing when delays are zero") +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-tegra114.c | 18 +++++++----------- + 1 file changed, 7 insertions(+), 11 deletions(-) + +diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c +index d9be80e3e1bcb..6b56108308fc5 100644 +--- a/drivers/spi/spi-tegra114.c ++++ b/drivers/spi/spi-tegra114.c +@@ -723,27 +723,23 @@ static int tegra_spi_set_hw_cs_timing(struct spi_device *spi) + struct spi_delay *setup = &spi->cs_setup; + struct spi_delay *hold = &spi->cs_hold; + struct spi_delay *inactive = &spi->cs_inactive; +- u8 setup_dly, hold_dly, inactive_dly; ++ u8 setup_dly, hold_dly; + u32 setup_hold; + u32 spi_cs_timing; + u32 inactive_cycles; + u8 cs_state; + +- if ((setup && setup->unit != SPI_DELAY_UNIT_SCK) || +- (hold && hold->unit != SPI_DELAY_UNIT_SCK) || +- (inactive && inactive->unit != SPI_DELAY_UNIT_SCK)) { ++ if (setup->unit != SPI_DELAY_UNIT_SCK || ++ hold->unit != SPI_DELAY_UNIT_SCK || ++ inactive->unit != SPI_DELAY_UNIT_SCK) { + dev_err(&spi->dev, + "Invalid delay unit %d, should be SPI_DELAY_UNIT_SCK\n", + SPI_DELAY_UNIT_SCK); + return -EINVAL; + } + +- setup_dly = setup ? setup->value : 0; +- hold_dly = hold ? hold->value : 0; +- inactive_dly = inactive ? inactive->value : 0; +- +- setup_dly = min_t(u8, setup_dly, MAX_SETUP_HOLD_CYCLES); +- hold_dly = min_t(u8, hold_dly, MAX_SETUP_HOLD_CYCLES); ++ setup_dly = min_t(u8, setup->value, MAX_SETUP_HOLD_CYCLES); ++ hold_dly = min_t(u8, hold->value, MAX_SETUP_HOLD_CYCLES); + if (setup_dly && hold_dly) { + setup_hold = SPI_SETUP_HOLD(setup_dly - 1, hold_dly - 1); + spi_cs_timing = SPI_CS_SETUP_HOLD(tspi->spi_cs_timing1, +@@ -755,7 +751,7 @@ static int tegra_spi_set_hw_cs_timing(struct spi_device *spi) + } + } + +- inactive_cycles = min_t(u8, inactive_dly, MAX_INACTIVE_CYCLES); ++ inactive_cycles = min_t(u8, inactive->value, MAX_INACTIVE_CYCLES); + if (inactive_cycles) + inactive_cycles--; + cs_state = inactive_cycles ? 0 : 1; +-- +2.39.5 +