From 6754b27a37e04c912c83f175ceeca86a4052ffc2 Mon Sep 17 00:00:00 2001 From: fadlyas07 Date: Sun, 10 May 2026 04:10:20 +0000 Subject: [PATCH 01/13] fixup! BACKPORT: kbuild: check the minimum assembler version in Kconfig Since minimum assembler version checking is not required in 5.10, it should not be required in 4.19 either. Change-Id: I333454a3dffb4798295adf9b3038ebc9acda948e --- scripts/as-version.sh | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/scripts/as-version.sh b/scripts/as-version.sh index 1a21495e9ff0..532270bd4b7e 100755 --- a/scripts/as-version.sh +++ b/scripts/as-version.sh @@ -51,12 +51,9 @@ set -- $(LC_ALL=C "$@" -Wa,--version -c -x assembler /dev/null -o /dev/null 2>/d IFS=' ' set -- $1 -min_tool_version=$(dirname $0)/min-tool-version.sh - if [ "$1" = GNU -a "$2" = assembler ]; then shift $(($# - 1)) version=$1 - min_version=$($min_tool_version binutils) name=GNU else echo "$orig_args: unknown assembler invoked" >&2 @@ -68,15 +65,5 @@ fi version=${version%-*} cversion=$(get_canonical_version $version) -min_cversion=$(get_canonical_version $min_version) - -if [ "$cversion" -lt "$min_cversion" ]; then - echo >&2 "***" - echo >&2 "*** Assembler is too old." - echo >&2 "*** Your $name assembler version: $version" - echo >&2 "*** Minimum $name assembler version: $min_version" - echo >&2 "***" - exit 1 -fi echo $name $cversion From bf39effde585ceaa8a00b09d3a0823daa05649d0 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Tue, 20 Sep 2022 18:41:17 +0530 Subject: [PATCH 02/13] BACKPORT: msm: kgsl: Avoid unmap after kgsl system suspend Smmu driver cannot safely handle unmap request after smmu device is system suspended. So ensure kgsl doesn't initiate an unmap request after kgsl system suspend as smmu device suspend happens after gpu's. Kgsl does unmap from the following paths: 1. Userspace unmap calls 2. Mementry workqueue 3. During reclaim (1) is not a concern as userspace will be collapse before driver suspend. So we need to ensure that mementry/events workqueues are flushed and we don't participate in reclaim to take care off (2) & (3) before kgsl system suspend completes. [mkbestas]: Ignore kgsl_reclaim parts that don't exist in 5.4 Change-Id: Ibe2c8f5a90fd4d8d4cf212f17c04c52873738e36 Signed-off-by: Akhil P Oommen --- drivers/gpu/msm/adreno.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 3a898033c178..aa5589b7bf54 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1743,6 +1743,12 @@ static int adreno_pm_suspend(struct device *dev) status = ops->pm_suspend(adreno_dev); mutex_unlock(&device->mutex); + if (status) + return status; + + flush_workqueue(device->events_wq); + flush_workqueue(kgsl_driver.mem_workqueue); + return status; } From e97ce5ceb5f8f8f33a492e62936eb6eae52b09e6 Mon Sep 17 00:00:00 2001 From: Pankaj Gupta Date: Fri, 6 Dec 2024 13:06:02 +0530 Subject: [PATCH 03/13] msm: kgsl: Use kthread instead of workqueue for event work Currently a workqueue is being used to process the event work. In certain scenarios like when most of CPU cores are busy, there can be a significant delay between the actual timestamp retire event and when the work is processed by the events workqueue as workqueues cannot have RT priority. Hence use kthread instead of workqueue for event work. Change-Id: Ib1ec7fa1ec3a133d03104c9a029dcc4c06180609 Signed-off-by: Puranam V G Tejaswi Signed-off-by: Pankaj Gupta --- drivers/gpu/msm/adreno.c | 2 +- drivers/gpu/msm/kgsl.c | 26 +++++++++++--------------- drivers/gpu/msm/kgsl.h | 4 ++-- drivers/gpu/msm/kgsl_device.h | 3 ++- drivers/gpu/msm/kgsl_events.c | 15 ++++++++------- 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index aa5589b7bf54..a37aa033ed98 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1746,7 +1746,7 @@ static int adreno_pm_suspend(struct device *dev) if (status) return status; - flush_workqueue(device->events_wq); + kthread_flush_worker(device->events_worker); flush_workqueue(kgsl_driver.mem_workqueue); return status; diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 0699fdadade3..328ea61b46a8 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -686,11 +686,11 @@ int kgsl_context_init(struct kgsl_device_private *dev_priv, if (id == -ENOSPC) { /* * Before declaring that there are no contexts left try - * flushing the event workqueue just in case there are + * flushing the event worker just in case there are * detached contexts waiting to finish */ - flush_workqueue(device->events_wq); + kthread_flush_worker(device->events_worker); id = _kgsl_get_context_id(device); } @@ -4526,15 +4526,16 @@ int kgsl_device_platform_probe(struct kgsl_device *device) device->pwrctrl.interrupt_num = status; disable_irq(device->pwrctrl.interrupt_num); - device->events_wq = alloc_workqueue("kgsl-events", - WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS | WQ_HIGHPRI, 0); + device->events_worker = kthread_create_worker(0, "kgsl-events"); - if (!device->events_wq) { - dev_err(device->dev, "Failed to allocate events workqueue\n"); - status = -ENOMEM; + if (IS_ERR(device->events_worker)) { + status = PTR_ERR(device->events_worker); + dev_err(device->dev, "Failed to create events worker ret=%d\n", status); goto error_pwrctrl_close; } + sched_set_fifo(device->events_worker->task); + /* This can return -EPROBE_DEFER */ status = kgsl_mmu_probe(device); if (status != 0) @@ -4559,10 +4560,8 @@ int kgsl_device_platform_probe(struct kgsl_device *device) return 0; error_pwrctrl_close: - if (device->events_wq) { - destroy_workqueue(device->events_wq); - device->events_wq = NULL; - } + if (!IS_ERR(device->events_worker)) + kthread_destroy_worker(device->events_worker); kgsl_pwrctrl_close(device); error: @@ -4572,10 +4571,7 @@ error: void kgsl_device_platform_remove(struct kgsl_device *device) { - if (device->events_wq) { - destroy_workqueue(device->events_wq); - device->events_wq = NULL; - } + kthread_destroy_worker(device->events_worker); kgsl_device_snapshot_close(device); diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 4698aacf94cf..e86051f9aa57 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -295,7 +295,7 @@ typedef void (*kgsl_event_func)(struct kgsl_device *, struct kgsl_event_group *, * @priv: Private data passed to the callback function * @node: List node for the kgsl_event_group list * @created: Jiffies when the event was created - * @work: Work struct for dispatching the callback + * @work: kthread_work struct for dispatching the callback * @result: KGSL event result type to pass to the callback * group: The event group this event belongs to */ @@ -307,7 +307,7 @@ struct kgsl_event { void *priv; struct list_head node; unsigned int created; - struct work_struct work; + struct kthread_work work; int result; struct kgsl_event_group *group; }; diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index 3d3cfdab5128..0b4a6c01d1d0 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2002,2007-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef __KGSL_DEVICE_H #define __KGSL_DEVICE_H @@ -293,7 +294,7 @@ struct kgsl_device { struct kgsl_pwrscale pwrscale; int reset_counter; /* Track how many GPU core resets have occurred */ - struct workqueue_struct *events_wq; + struct kthread_worker *events_worker; /* Number of active contexts seen globally for this device */ int active_context_count; diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c index 891c662ea339..674a3dc13e2c 100644 --- a/drivers/gpu/msm/kgsl_events.c +++ b/drivers/gpu/msm/kgsl_events.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2011-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -21,17 +22,17 @@ static inline void signal_event(struct kgsl_device *device, { list_del(&event->node); event->result = result; - queue_work(device->events_wq, &event->work); + kthread_queue_work(device->events_worker, &event->work); } /** * _kgsl_event_worker() - Work handler for processing GPU event callbacks - * @work: Pointer to the work_struct for the event + * @work: Pointer to the kthread_work for the event * - * Each event callback has its own work struct and is run on a event specific - * workqeuue. This is the worker that queues up the event callback function. + * Each event callback has its own kthread_work struct and is run on a event specific + * worker thread. This is the worker that queues up the event callback function. */ -static void _kgsl_event_worker(struct work_struct *work) +static void _kgsl_event_worker(struct kthread_work *work) { struct kgsl_event *event = container_of(work, struct kgsl_event, work); int id = KGSL_CONTEXT_ID(event->context); @@ -268,7 +269,7 @@ int kgsl_add_event(struct kgsl_device *device, struct kgsl_event_group *group, event->created = jiffies; event->group = group; - INIT_WORK(&event->work, _kgsl_event_worker); + kthread_init_work(&event->work, _kgsl_event_worker); trace_kgsl_register_event(KGSL_CONTEXT_ID(context), timestamp, func); @@ -283,7 +284,7 @@ int kgsl_add_event(struct kgsl_device *device, struct kgsl_event_group *group, if (timestamp_cmp(retired, timestamp) >= 0) { event->result = KGSL_EVENT_RETIRED; - queue_work(device->events_wq, &event->work); + kthread_queue_work(device->events_worker, &event->work); spin_unlock(&group->lock); return 0; } From 726d78ef8a242dae247ee372f1633e2d54bad3de Mon Sep 17 00:00:00 2001 From: Jeyaprabu J Date: Tue, 22 Mar 2022 12:24:47 +0530 Subject: [PATCH 04/13] msm: kgsl: Fix UBSAN warnings Fix possible division by zero error. Change-Id: I64eb6a5ee1247dafea6701b8244eefab1c40eea6 Signed-off-by: Jeyaprabu J --- drivers/gpu/msm/adreno_a6xx_rpmh.c | 6 +++++- drivers/gpu/msm/governor_gpubw_mon.c | 7 ++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/msm/adreno_a6xx_rpmh.c b/drivers/gpu/msm/adreno_a6xx_rpmh.c index 0594304d64ee..c7a4b3297e45 100644 --- a/drivers/gpu/msm/adreno_a6xx_rpmh.c +++ b/drivers/gpu/msm/adreno_a6xx_rpmh.c @@ -120,6 +120,7 @@ static void tcs_cmd_data(struct bcm *bcms, int count, u32 ab, u32 ib, u32 *data) { int i; + u64 total_width; for (i = 0; i < count; i++) { bool valid = true; @@ -145,8 +146,11 @@ static void tcs_cmd_data(struct bcm *bcms, int count, u32 ab, u32 ib, /* Multiple the bandwidth by the width of the connection */ avg = ((u64) ab) * bcms[i].width; + total_width = bcms[i].buswidth * bcms[i].channels; + /* And then divide by the total width across channels */ - do_div(avg, bcms[i].buswidth * bcms[i].channels); + if (total_width) + do_div(avg, (u32) total_width); peak = ((u64) ib) * bcms[i].width; do_div(peak, bcms[i].buswidth); diff --git a/drivers/gpu/msm/governor_gpubw_mon.c b/drivers/gpu/msm/governor_gpubw_mon.c index fabcd56b3776..af437abf107e 100644 --- a/drivers/gpu/msm/governor_gpubw_mon.c +++ b/drivers/gpu/msm/governor_gpubw_mon.c @@ -101,7 +101,7 @@ static int devfreq_gpubw_get_target(struct devfreq *df, int act_level; int norm_max_cycles; int norm_cycles; - int wait_active_percent; + int wait_active_percent = 0; int gpu_percent; /* * Normalized AB should at max usage be the gpu_bimc frequency in MHz. @@ -132,8 +132,9 @@ static int devfreq_gpubw_get_target(struct devfreq *df, (unsigned int) priv->bus.total_time; norm_cycles = (unsigned int)(priv->bus.ram_time + priv->bus.ram_wait) / (unsigned int) priv->bus.total_time; - wait_active_percent = (100 * (unsigned int)priv->bus.ram_wait) / - (unsigned int) priv->bus.ram_time; + if (priv->bus.ram_time) + wait_active_percent = (100 * (unsigned int)priv->bus.ram_wait) / + (unsigned int) priv->bus.ram_time; gpu_percent = (100 * (unsigned int)priv->bus.gpu_time) / (unsigned int) priv->bus.total_time; From ae1efd457bdf513bc37586d703666f3aab985649 Mon Sep 17 00:00:00 2001 From: Abhishek Shah Date: Mon, 16 Aug 2021 16:47:52 +0530 Subject: [PATCH 05/13] devfreq: governor_bw_hwmon: fix deadlock warning due to state_lock usage lockdep is detecting possible circular locking dependency due to state_lock mutex as shown below: Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(devfreq_list_lock); lock(state_lock#2); lock(devfreq_list_lock); lock(state_lock#2); *** DEADLOCK *** Below is partial call stacks (in reverse order) showing relevant locking paths: Call stack for CPU0: devfreq_bw_hwmon_ev_handler+0x4c/0x5e0 [may acquire &state_lock] devfreq_add_device+0x418/0x538 [may acquire &devfreq_list_lock] devfreq_add_icc+0x41c/0x528 devfreq_icc_probe+0x20/0x30 Call stack for CPU1: devfreq_add_governor+0x3c/0x260 [may acquire &devfreq_list_lock] register_bw_hwmon+0x1e8/0x248 [may acquire &state_lock] bimc_bwmon_driver_probe+0x310/0x408 Practically, this race is not possible, since devfreq_add_device first tries to find the governor, and only if it is succeeds, devfreq_bw_hwmon_ev_handler is called. And the governor would be found only if devfreq_add_governor has added it priorly. We have mechanism(initcall_level) in place to make sure that governor is added before devfreq_add_device happens. In attempt to quiet the lockdep warning, below fix is adopted: Since state_lock mutex has different purpose, introduce a new event_handle_lock mutex for devfreq_bw_hwmon_ev_handler to avoid this warning. Change-Id: I53c4514aa2357bf39e9405683f5e73ada0160ba5 Signed-off-by: Abhishek Shah --- drivers/devfreq/governor_bw_hwmon.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/devfreq/governor_bw_hwmon.c b/drivers/devfreq/governor_bw_hwmon.c index 2acdb0a3fe5a..261b40843ff3 100644 --- a/drivers/devfreq/governor_bw_hwmon.c +++ b/drivers/devfreq/governor_bw_hwmon.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2021, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "bw-hwmon: " fmt @@ -80,6 +80,8 @@ static DEFINE_MUTEX(list_lock); static int use_cnt; static DEFINE_MUTEX(state_lock); +static DEFINE_MUTEX(event_handle_lock); + #define show_attr(name) \ static ssize_t name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ @@ -865,7 +867,7 @@ static int devfreq_bw_hwmon_ev_handler(struct devfreq *df, struct hwmon_node *node; struct bw_hwmon *hw; - mutex_lock(&state_lock); + mutex_lock(&event_handle_lock); switch (event) { case DEVFREQ_GOV_START: @@ -939,7 +941,7 @@ static int devfreq_bw_hwmon_ev_handler(struct devfreq *df, } out: - mutex_unlock(&state_lock); + mutex_unlock(&event_handle_lock); return ret; } From 63898d53781294a24459bd0ce8fe4d869fadf1aa Mon Sep 17 00:00:00 2001 From: Abhishek Shah Date: Mon, 16 Aug 2021 17:02:43 +0530 Subject: [PATCH 06/13] devfreq: governor_memlat: avoid deadlock due to cpu_grp->mons_lock usage lockdep is detecting possible circular locking dependency due to cpu_grp->mons_lock mutex as shown below: Chain exists of: &cpu_grp->mons_lock --> state_lock#3 --> devfreq_list_lock Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(devfreq_list_lock); lock(state_lock#3); lock(devfreq_list_lock); lock(&cpu_grp->mons_lock); *** DEADLOCK *** Below is partial call stacks (in reverse order) showing relevant locking paths: Call stack for CPU0: start_hwmon+0x6c/0x5d0 [may acquire &cpu_grp->mons_lock] devfreq_memlat_ev_handler+0x2f4/0x3f8 devfreq_add_device+0x418/0x538 [may acquire devfreq_list_lock] devfreq_add_icc+0x41c/0x528 devfreq_icc_probe+0x20/0x30 Call stack for CPU1: devfreq_add_governor+0x3c/0x260 [may acquire devfreq_list_lock] register_memlat+0x84/0x100 memlat_mon_probe+0x414/0x550 [may acquire &cpu_grp->mons_lock] arm_memlat_mon_driver_probe+0x120/0x3b8 Practically, this race is not possible, since devfreq_add_device first tries to find the governor, and only if it is succeeds, devfreq_memlat_ev_handler is called. And the governor would be found only if devfreq_add_governor has added it priorly. We have mechanism(initcall_level) in place to make sure that governor is added before devfreq_add_device happens. In attempt to quiet the lockdep warning, below fix is adopted: cpu_grp gets allocated by memlat_cpu_grp_probe, but it is used by its multiple memlat_mon children's memlat_mon_probe. cpu_grp->mons_lock is used to prevent race between them for access to cpu_grp and members. Use a new lock - cpu_grp->init_mons_lock - for the probe routine, and continue using cpu_grp->mons_lock in other routines. Change-Id: Ie59c28de0a40914fb120a305067ef0fe504a4455 Signed-off-by: Abhishek Shah --- drivers/devfreq/arm-memlat-mon.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/devfreq/arm-memlat-mon.c b/drivers/devfreq/arm-memlat-mon.c index 4fc8f76918e4..7cf6d701a496 100644 --- a/drivers/devfreq/arm-memlat-mon.c +++ b/drivers/devfreq/arm-memlat-mon.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2022, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "arm-memlat-mon: " fmt @@ -126,6 +126,7 @@ struct memlat_mon { * @mons: All of the memlat_mon structs representing * the different voters who share this cpu_grp. * @mons_lock: A lock used to protect the @mons. + * @init_mons_lock: A lock used to protect the @mons in probe routine. */ struct memlat_cpu_grp { cpumask_t cpus; @@ -143,6 +144,7 @@ struct memlat_cpu_grp { unsigned int num_active_mons; struct memlat_mon *mons; struct mutex mons_lock; + struct mutex init_mons_lock; spinlock_t mon_active_lock; }; @@ -851,6 +853,7 @@ static int memlat_cpu_grp_probe(struct platform_device *pdev) return -ENOMEM; mutex_init(&cpu_grp->mons_lock); + mutex_init(&cpu_grp->init_mons_lock); spin_lock_init(&cpu_grp->mon_active_lock); cpu_grp->update_ms = DEFAULT_UPDATE_MS; @@ -886,7 +889,7 @@ static int memlat_mon_probe(struct platform_device *pdev, bool is_compute) return -ENODEV; } - mutex_lock(&cpu_grp->mons_lock); + mutex_lock(&cpu_grp->init_mons_lock); mon = &cpu_grp->mons[cpu_grp->num_inited_mons]; spin_lock_irqsave(&cpu_grp->mon_active_lock, flags); mon->is_active = false; @@ -1003,7 +1006,7 @@ static int memlat_mon_probe(struct platform_device *pdev, bool is_compute) cpu_grp->num_inited_mons++; unlock_out: - mutex_unlock(&cpu_grp->mons_lock); + mutex_unlock(&cpu_grp->init_mons_lock); return ret; } From d8ea382c8ab40dedc14cbbbdf7a75787eaed2690 Mon Sep 17 00:00:00 2001 From: Abhishek Shah Date: Mon, 16 Aug 2021 17:18:33 +0530 Subject: [PATCH 07/13] devfreq: govener_memlat: fix cpu_hotplug_lock recursive lock warning Possible unsafe locking scenario: CPU0 ---- lock(cpu_hotplug_lock.rw_sem); lock(cpu_hotplug_lock.rw_sem); *** DEADLOCK *** Below code under start_hwmon may cause this recursive locking. get_online_cpus(); for_each_cpu(cpu, cpu_possible_mask) { if (!cpumask_test_cpu(cpu, cpu_online_mask)) per_cpu(cpu_is_hp, cpu) = true; } ret = memlat_event_cpu_hp_init(); put_online_cpus(); get_online_cpus() acquires cpu_hotplug_lock.rw_sem lock. Then memlat_event_cpu_hp_init() -> __cpuhp_setup_state() tries to acquire the same lock again. Use cpuslocked version of __cpuhp_setup_state() to avoid this warning. Change-Id: Ied9fe53d02c74816f38c1efe954cea91f9831cc7 Signed-off-by: Abhishek Shah --- drivers/devfreq/arm-memlat-mon.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/devfreq/arm-memlat-mon.c b/drivers/devfreq/arm-memlat-mon.c index 7cf6d701a496..1ab4a88e4fa0 100644 --- a/drivers/devfreq/arm-memlat-mon.c +++ b/drivers/devfreq/arm-memlat-mon.c @@ -519,11 +519,15 @@ static int memlat_event_hotplug_going_down(unsigned int cpu) return ret; } +/* + * Note: We must be holding cpus_read_lock() before calling this function + * since we are using cpuslocked version of function inside it + */ static int memlat_event_cpu_hp_init(void) { int ret = 0; - ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, + ret = cpuhp_setup_state_nocalls_cpuslocked(CPUHP_AP_ONLINE_DYN, "MEMLAT_EVENT", memlat_event_hotplug_coming_up, memlat_event_hotplug_going_down); From 01674892a63155a3921757ef403ae49503c54887 Mon Sep 17 00:00:00 2001 From: Abhijeet Dharmapurikar Date: Fri, 15 Mar 2019 12:01:03 -0700 Subject: [PATCH 08/13] devfreq: Allow zero values in opp table commit ab8f58ad72c4d1abe59216362ddb8bfa428c9071 ("PM / devfreq: Set min/max_freq when adding the devfreq device") introduces a change where an error in finding the ceil or floor is returned as a zero frequency. This return of zero is treated as error condition from the callsites. It is perfectly valid for a device to have a zero frequency, usually it denotes a power collapse. This valid return of zero frequency ends up being treated as an error condition. Fix it. Change-Id: I5eb6fa622d6fe09207746b96dce05d0b58b233dc Signed-off-by: Abhijeet Dharmapurikar Signed-off-by: Shreyas K K --- drivers/devfreq/devfreq.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 88afaed2cb61..fe0bb7a79dee 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -70,28 +70,28 @@ static struct devfreq *find_device_devfreq(struct device *dev) return ERR_PTR(-ENODEV); } -static unsigned long find_available_min_freq(struct devfreq *devfreq) +static long find_available_min_freq(struct devfreq *devfreq) { struct dev_pm_opp *opp; - unsigned long min_freq = 0; + long min_freq = 0; opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &min_freq); if (IS_ERR(opp)) - min_freq = 0; + min_freq = PTR_ERR(opp); else dev_pm_opp_put(opp); return min_freq; } -static unsigned long find_available_max_freq(struct devfreq *devfreq) +static long find_available_max_freq(struct devfreq *devfreq) { struct dev_pm_opp *opp; - unsigned long max_freq = ULONG_MAX; + long max_freq = LONG_MAX; opp = dev_pm_opp_find_freq_floor(devfreq->dev.parent, &max_freq); if (IS_ERR(opp)) - max_freq = 0; + max_freq = PTR_ERR(opp); else dev_pm_opp_put(opp); @@ -552,18 +552,23 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, { struct devfreq *devfreq = container_of(nb, struct devfreq, nb); int err = -EINVAL; + long freq; mutex_lock(&devfreq->lock); - devfreq->scaling_min_freq = find_available_min_freq(devfreq); - if (!devfreq->scaling_min_freq) + freq = find_available_min_freq(devfreq); + if (freq < 0) { + devfreq->scaling_min_freq = 0; goto out; + } + devfreq->scaling_min_freq = freq; - devfreq->scaling_max_freq = find_available_max_freq(devfreq); - if (!devfreq->scaling_max_freq) { + freq = find_available_max_freq(devfreq); + if (freq < 0) { devfreq->scaling_max_freq = ULONG_MAX; goto out; } + devfreq->scaling_max_freq = freq; err = update_devfreq(devfreq); @@ -616,6 +621,7 @@ struct devfreq *devfreq_add_device(struct device *dev, struct devfreq *devfreq; struct devfreq_governor *governor; int err = 0; + long freq; if (!dev || !profile || !governor_name) { dev_err(dev, "%s: Invalid parameters.\n", __func__); @@ -660,21 +666,21 @@ struct devfreq *devfreq_add_device(struct device *dev, mutex_lock(&devfreq->lock); } - devfreq->scaling_min_freq = find_available_min_freq(devfreq); - if (!devfreq->scaling_min_freq) { + freq = find_available_min_freq(devfreq); + if (freq < 0) { mutex_unlock(&devfreq->lock); err = -EINVAL; goto err_dev; } - devfreq->min_freq = devfreq->scaling_min_freq; + devfreq->min_freq = devfreq->scaling_min_freq = freq; - devfreq->scaling_max_freq = find_available_max_freq(devfreq); - if (!devfreq->scaling_max_freq) { + freq = find_available_max_freq(devfreq); + if (freq < 0) { mutex_unlock(&devfreq->lock); err = -EINVAL; goto err_dev; } - devfreq->max_freq = devfreq->scaling_max_freq; + devfreq->max_freq = devfreq->scaling_max_freq = freq; devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev); atomic_set(&devfreq->suspend_count, 0); From 6024e3974b24192524112c5ad894c8fec902eb3a Mon Sep 17 00:00:00 2001 From: Shreyas K K Date: Thu, 11 Nov 2021 09:55:35 +0530 Subject: [PATCH 09/13] devfreq: Fix PM callbacks to support zero frequency It is perfectly valid for a device to have a zero frequency, usually it denotes a power collapse. To support this, fix the PM suspend callback to allow the suspend_freq to be zero. Change-Id: Id10f95b59b219e82e0695f80361aa6b35cdc5137 Signed-off-by: Shreyas K K --- drivers/devfreq/devfreq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index fe0bb7a79dee..b48f465ea19b 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -318,7 +318,7 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq, devfreq->previous_freq = new_freq; - if (devfreq->suspend_freq) + if (devfreq->suspend_freq >= 0) devfreq->resume_freq = new_freq; return err; @@ -912,7 +912,7 @@ int devfreq_suspend_device(struct devfreq *devfreq) return ret; } - if (devfreq->suspend_freq) { + if (devfreq->suspend_freq >= 0) { mutex_lock(&devfreq->lock); ret = devfreq_set_target(devfreq, devfreq->suspend_freq, 0); mutex_unlock(&devfreq->lock); From 0192aaa6cd9377b91b8022acf33a45fbd9adc3f0 Mon Sep 17 00:00:00 2001 From: Shreyas K K Date: Fri, 26 Nov 2021 00:35:03 +0530 Subject: [PATCH 10/13] devfreq: Fix suspend callback for non-zero min_freq With commit commit 921884ca1498 ("devfreq: Fix PM callbacks to support zero frequency"), suspend_freq of zero is considered valid. However, the devfreq_set_target is called with zero frequency. To avoid this, do not call devfreq_set_target if the suspend_freq is less than the minimun allowed frequency. Change-Id: I506165d28395eb67138ae2dac53e842357809bbb Signed-off-by: Shreyas K K --- drivers/devfreq/devfreq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index b48f465ea19b..81709add4b0e 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -318,7 +318,7 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq, devfreq->previous_freq = new_freq; - if (devfreq->suspend_freq >= 0) + if (devfreq->suspend_freq >= devfreq->scaling_min_freq) devfreq->resume_freq = new_freq; return err; @@ -912,7 +912,7 @@ int devfreq_suspend_device(struct devfreq *devfreq) return ret; } - if (devfreq->suspend_freq >= 0) { + if (devfreq->suspend_freq >= devfreq->scaling_min_freq) { mutex_lock(&devfreq->lock); ret = devfreq_set_target(devfreq, devfreq->suspend_freq, 0); mutex_unlock(&devfreq->lock); From 8c29510d4456fd1b54ba0482450c7dd7f2dd08ce Mon Sep 17 00:00:00 2001 From: Vedant Yevale Date: Fri, 22 Aug 2025 14:32:54 +0530 Subject: [PATCH 11/13] disp: msm: dsi: Nullify display modes after kfree The 'display->modes' pointer must be set to NULL immediately after calling kfree on it to prevent potential double-free vulnerabilities or use-after-free issues. This change ensures robust memory management by clearing the pointer to previously freed memory, aligning with best practices for kernel memory deallocation. Change-Id: I4fd939bc6ee5f3d9c506f0c311a9400f366e7fd2 Signed-off-by: Vedant Yevale (cherry picked from commit 1d3c5a477ea8f6aca1158209e1b87209c21c61f5) --- techpack/display/msm/dsi/dsi_display.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/techpack/display/msm/dsi/dsi_display.c b/techpack/display/msm/dsi/dsi_display.c index 291dcb9732f2..482ff87bc4bf 100644 --- a/techpack/display/msm/dsi/dsi_display.c +++ b/techpack/display/msm/dsi/dsi_display.c @@ -7168,8 +7168,10 @@ exit: rc = 0; error: - if (rc) + if (rc) { kfree(display->modes); + display->modes = NULL; + } mutex_unlock(&display->display_lock); return rc; From 3a757c1357fadea06aba96c5dafd96aa031d0cd1 Mon Sep 17 00:00:00 2001 From: Alexander Winkowski Date: Sun, 24 May 2026 16:08:26 +0000 Subject: [PATCH 12/13] disp: msm: Fix division by zero during ESD recovery During ESD recovery, num_mixers becomes 0 for a few moments. [ 34.758513] ================================================================================ [ 34.758519] UBSAN: division-overflow in ../techpack/display/msm/sde/sde_crtc.h:522:32 [ 34.758521] division by zero [ 34.758524] CPU: 6 PID: 1039 Comm: SDM_EventThread Tainted: G S 5.4.302~positron/4565e293 #17 [ 34.758525] Hardware name: Qualcomm Technologies, Inc. Blair QRD NOPMI (DT) [ 34.758526] Call trace: [ 34.758531] dump_backtrace+0x0/0x298 [ 34.758532] __dump_stack+0x20/0x28 [ 34.758533] dump_stack+0x74/0x9c [ 34.758535] ubsan_epilogue+0xc/0x48 [ 34.758538] __ubsan_handle_divrem_overflow+0x184/0x198 [ 34.758540] _sde_crtc_setup_lm_bounds+0x300/0x318 [ 34.758541] sde_crtc_atomic_check+0x260/0x253c [ 34.758543] drm_atomic_helper_check_planes+0x1b0/0x224 [ 34.758544] sde_kms_atomic_check+0xb4/0x2f4 [ 34.758545] drm_atomic_check_only+0x410/0x778 [ 34.758546] drm_mode_atomic_ioctl+0xb24/0x1020 [ 34.758548] drm_ioctl_kernel+0x21c/0x2c0 [ 34.758549] drm_ioctl+0x2d0/0x434 [ 34.758551] do_vfs_ioctl+0x9b4/0xf9c [ 34.758552] __arm64_sys_ioctl+0x128/0x144 [ 34.758553] el0_svc_common+0xdc/0x158 [ 34.758554] el0_svc+0x8/0x700 [ 34.758555] ================================================================================ Fixes: 4fef803aff96 ("disp: msm: sde: increase max number of mixers to 4") Change-Id: Idb60e329ffb47d842bb0b4a16b3d086cc4ec16bc Signed-off-by: Alexander Winkowski --- techpack/display/msm/sde/sde_crtc.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/techpack/display/msm/sde/sde_crtc.h b/techpack/display/msm/sde/sde_crtc.h index 75fb998ed06d..3a9e4b68eb0b 100644 --- a/techpack/display/msm/sde/sde_crtc.h +++ b/techpack/display/msm/sde/sde_crtc.h @@ -519,7 +519,8 @@ static inline int sde_crtc_get_mixer_width(struct sde_crtc *sde_crtc, if (cstate->num_ds_enabled) mixer_width = cstate->ds_cfg[0].lm_width; else - mixer_width = mode->hdisplay / sde_crtc->num_mixers; + mixer_width = sde_crtc->num_mixers ? + mode->hdisplay / sde_crtc->num_mixers : mode->hdisplay; return mixer_width; } From c404d8ddf71bd422dc670a884ecd838b29ce3c3f Mon Sep 17 00:00:00 2001 From: Alexander Winkowski Date: Thu, 14 May 2026 19:56:04 +0000 Subject: [PATCH 13/13] disp: msm: Avoid UB in VBIF register shift calculation Left-shifting a 32-bit integer by 32 bits or more results in UB. The common values for vbif_xin_id[] are {10, 11} which means reg_shift becomes 40/44. For IDs >= 8, the shift is meant to be relative to the second 32-bit register, not to the first. Fix this issue by masking the ID so that it will properly describe the intended shift. Change-Id: Icd530a3bb7cfd9d087e2aecc763d24f775e20466 Signed-off-by: Alexander Winkowski --- techpack/display/rotator/sde_rotator_r3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/techpack/display/rotator/sde_rotator_r3.c b/techpack/display/rotator/sde_rotator_r3.c index b1e2f15a4b84..f36c0ce6ce7c 100644 --- a/techpack/display/rotator/sde_rotator_r3.c +++ b/techpack/display/rotator/sde_rotator_r3.c @@ -1419,14 +1419,14 @@ static void sde_hw_rotator_vbif_rt_setting(void) for (j = 0; j < MAX_XIN; j++) { reg_high = ((mdata->vbif_xin_id[j] & 0x8) >> 3) * 4 + (i * 8); - reg_shift = mdata->vbif_xin_id[j] * 4; + reg_shift = (mdata->vbif_xin_id[j] & 0x7) * 4; reg_val = SDE_VBIF_READ(mdata, MMSS_VBIF_NRT_VBIF_QOS_RP_REMAP_000 + reg_high); reg_val_lvl = SDE_VBIF_READ(mdata, MMSS_VBIF_NRT_VBIF_QOS_LVL_REMAP_000 + reg_high); - mask = 0x7 << (mdata->vbif_xin_id[j] * 4); + mask = 0x7 << reg_shift; vbif_qos = mdata->vbif_nrt_qos[i];