Merge remote-tracking branch 'sm8350/lineage-20' into lineage-23.2

* sm8350/lineage-20:
  disp: msm: Avoid UB in VBIF register shift calculation
  disp: msm: Fix division by zero during ESD recovery
  disp: msm: dsi: Nullify display modes after kfree
  devfreq: Fix suspend callback for non-zero min_freq
  devfreq: Fix PM callbacks to support zero frequency
  devfreq: Allow zero values in opp table
  devfreq: govener_memlat: fix cpu_hotplug_lock recursive lock warning
  devfreq: governor_memlat: avoid deadlock due to cpu_grp->mons_lock usage
  devfreq: governor_bw_hwmon: fix deadlock warning due to state_lock usage
  msm: kgsl: Fix UBSAN warnings
  msm: kgsl: Use kthread instead of workqueue for event work
  BACKPORT: msm: kgsl: Avoid unmap after kgsl system suspend
  fixup! BACKPORT: kbuild: check the minimum assembler version in Kconfig

Change-Id: I2f722ad0f53673157f841eaf0ef032e4f5b1703d
This commit is contained in:
Michael Bestas 2026-05-27 19:31:50 +03:00
commit 103491ac60
No known key found for this signature in database
14 changed files with 85 additions and 71 deletions

View file

@ -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;
};
@ -517,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);
@ -851,6 +857,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 +893,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 +1010,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;
}

View file

@ -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);
@ -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 >= devfreq->scaling_min_freq)
devfreq->resume_freq = new_freq;
return err;
@ -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);
@ -906,7 +912,7 @@ int devfreq_suspend_device(struct devfreq *devfreq)
return ret;
}
if (devfreq->suspend_freq) {
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);

View file

@ -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;
}

View file

@ -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;
kthread_flush_worker(device->events_worker);
flush_workqueue(kgsl_driver.mem_workqueue);
return status;
}

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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;
};

View file

@ -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;

View file

@ -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 <linux/debugfs.h>
@ -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;
}

View file

@ -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

View file

@ -7836,8 +7836,10 @@ exit:
rc = 0;
error:
if (rc)
if (rc) {
kfree(display->modes);
display->modes = NULL;
}
mutex_unlock(&display->display_lock);
return rc;

View file

@ -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;
}

View file

@ -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];