Files
threeeye bc1d038eac REVVL 6 Pro kernel bring-up: full working tree snapshot (2026-09-12)
Squashed backup commit (no shallow-clone history issues) of
xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream
commit 9eece662e) plus all fixes applied to get everpal_defconfig building
clean against a modern (GCC 15.2.0) toolchain -- see the previous commit
message on the lineage-23.2 branch (also in this repo history is not
available here since this is a squashed snapshot; full incremental history
lives in the local DL380 checkout and the upstream GitHub remote).

Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
2026-09-12 13:36:46 +00:00

363 lines
7.9 KiB
C

/*
* Copyright (C) 2020 MediaTek Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#undef pr_fmt
#define pr_fmt(fmt) "cache-ctl: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/sched.h>
#include <linux/cgroup-defs.h>
#include <linux/cgroup.h>
#include <linux/moduleparam.h>
#include <linux/sched.h>
#include <linux/debugfs.h>
#include <asm/sysreg.h>
#include "cache_ctrl.h"
#include <mt-plat/l3cc_common.h>
#ifdef CONFIG_MTK_TASK_TURBO
#include <mt-plat/turbo_common.h>
#endif
#define CREATE_TRACE_POINTS
#include "trace_cache_ctrl.h"
#define AID_SYSTEM 1000
DECLARE_PER_CPU(struct boost_groups, cpu_boost_groups);
static DEFINE_PER_CPU(bool, in_partition_control);
static int is_cache_ctrl_enabled;
static int ctl_min_score = 500; // default is service adj
module_param(ctl_min_score, int, 0600);
static unsigned int ctl_suppress_part = 1;
module_param(ctl_suppress_part, uint, 0600);
static int ctl_turbo_group = 1;
module_param(ctl_turbo_group, int, 0600);
unsigned int ctl_suppress_group = 1 << GROUP_BG; // default is BG for depress
module_param(ctl_suppress_group, uint, 0600);
static struct dentry *l3cc_debugfs;
struct partition_stats {
int state[CONFIG_NR_CPUS];
};
#define CLUSTERPARTCR_EL1 sys_reg(3, 0, 15, 4, 3)
#define CLUSTERTHREADSID_EL1 sys_reg(3, 0, 15, 4, 0)
static inline void cache_ctrl_write_partcr(u32 val)
{
write_sysreg_s(val, CLUSTERPARTCR_EL1);
isb();
}
static inline u32 cache_ctrl_read_partcr(void)
{
return read_sysreg_s(CLUSTERPARTCR_EL1);
}
static inline void cache_ctrl_write_threadsid(u32 val)
{
write_sysreg_s(val, CLUSTERTHREADSID_EL1);
/*
* prefer not to use isb(). Since cache
* limit is applied to next task, so
* it's no need to take effect immediately
*/
}
static inline u32 cache_ctrl_read_threadsid(void)
{
return read_sysreg_s(CLUSTERTHREADSID_EL1);
}
void dump_partition_config_per_cpu(void *info)
{
struct partition_stats *stats;
int cpu = smp_processor_id();
register int val asm("x3");
stats = (struct partition_stats *)info;
if (IS_ERR(stats) || !stats) {
pr_debug("%s: invalid info address\n", __func__);
return;
}
val = cache_ctrl_read_threadsid();
stats->state[cpu] = val;
}
static int l3cc_info_show(struct seq_file *m, void *unused)
{
struct partition_stats stats;
int partcr_val;
int i;
seq_puts(m, "dump L3 partition register.\n");
for (i = 0; i < nr_cpu_ids; i++) {
smp_call_function_single(i,
dump_partition_config_per_cpu, &stats, 1);
seq_printf(m, "\tCPU%d: group[%d]\n", i, stats.state[i]);
}
partcr_val = cache_ctrl_read_partcr();
seq_printf(m, "partcr=0x%x\n", partcr_val);
return 0;
}
static int l3cc_info_open(struct inode *inode, struct file *file)
{
return single_open(file, l3cc_info_show, inode->i_private);
}
static const struct file_operations l3cc_debug_fops = {
.owner = THIS_MODULE,
.open = l3cc_info_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static bool is_bw_congested;
static int qos_callback(struct notifier_block *nb,
unsigned long qos_status, void *v)
{
if (qos_status == QOS_BOUND_BW_FREE && is_bw_congested)
is_bw_congested = false;
else if (qos_status > QOS_BOUND_BW_FREE && !is_bw_congested)
is_bw_congested = true;
return NOTIFY_DONE;
}
static struct notifier_block nb = {
.notifier_call = qos_callback,
};
void config_partition(int gid)
{
/*
* per-cpu: CLUSTERTHREADSID_EL1
* group id:
* group 0: 0xf, full cache way
* group 1: 0x1, 0b0001 cache way
*/
cache_ctrl_write_threadsid(gid);
}
#if IS_ENABLED(CONFIG_SCHED_TUNE)
static bool group_active(unsigned int idx, struct boost_groups *bg)
{
if (bg->group[idx].tasks)
return true;
return false;
}
static bool important_group_active(int cpu)
{
struct boost_groups *bg;
unsigned int idx;
bg = &per_cpu(cpu_boost_groups, cpu);
for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx) {
if (ctl_suppress_group & (1 << idx))
continue;
if (group_active(idx, bg)) {
// trace_ca_apply_control(cpu, 5);
return true;
}
}
return false;
}
#else
static bool important_group_active(int cpu) {return false; }
#endif
static bool check_important_runnable(int cpu)
{
struct rq *rq = cpu_rq(cpu);
/* check if runnable task */
if (rq->cfs.h_nr_running > 2 &&
important_group_active(cpu))
return true;
return false;
}
static inline int get_stune_id(struct task_struct *task)
{
#if IS_ENABLED(CONFIG_SCHED_TUNE)
const int subsys_id = schedtune_cgrp_id;
struct cgroup *grp;
rcu_read_lock();
grp = task_cgroup(task, subsys_id);
rcu_read_unlock();
return grp->id;
#else
return 0;
#endif
}
static inline bool is_important(struct task_struct *task)
{
int grp_id = get_stune_id(task);
#ifdef CONFIG_MTK_TASK_TURBO
if (ctl_turbo_group && is_turbo_task(task))
return true;
#endif
if (ctl_suppress_group & (1 << grp_id))
return false;
return true;
}
/*
* reset cache control of the cpu.
*/
static inline void reset_cache_ctl(void)
{
if (__this_cpu_read(in_partition_control) == true) {
__this_cpu_write(in_partition_control, false);
config_partition(0);
}
}
static bool audit_next_task(struct task_struct *next)
{
int cpu = smp_processor_id();
/* important task, no need to control */
if (is_important(next))
return false;
/* DO NOT audit kernel threads */
if (!next->mm)
return false;
if (!fair_policy(next->policy))
return false;
/* Skip tasks of AID_SYSTEM */
if (from_kuid(current_user_ns(), task_euid(next)) == AID_SYSTEM)
return false;
/* Prefer not to suppress when memstall happened */
if (next->flags & PF_MEMSTALL)
return false;
/* prevent to depresss service of ta app */
if (next->signal->oom_score_adj <= ctl_min_score)
return false;
if (is_bw_congested)
return false;
if (check_important_runnable(cpu))
return false;
return true;
}
inline void restrict_next_task(struct task_struct *next)
{
int cpu = smp_processor_id();
if (!audit_next_task(next)) {
trace_skip_cache_control(next, is_bw_congested);
return;
}
/* perform to limit l3-cache use */
__this_cpu_write(in_partition_control, true);
config_partition(ctl_suppress_part);
trace_apply_cache_control(next, cpu, ctl_suppress_part);
}
void hook_ca_context_switch(struct rq *rq, struct task_struct *prev,
struct task_struct *next)
{
reset_cache_ctl();
if (is_cache_ctrl_enabled == 0)
return;
restrict_next_task(next);
}
EXPORT_SYMBOL(hook_ca_context_switch);
static int set_cache_control(const char *buf, const struct kernel_param *kp)
{
int ret = 0;
int val = 0;
ret = kstrtoint(buf, 0, &val);
if (ret < 0)
return ret;
if (val == is_cache_ctrl_enabled) {
pr_debug("No need to change config\n");
return 0;
}
pr_info("Changing state from %d to %d ...\n", is_cache_ctrl_enabled, val);
if (val == 0 || val == 1)
ret = param_set_int(buf, kp);
else
ret = -EINVAL;
if (ret < 0)
return ret;
pr_info("[Change complete] %s with mode %d\n",
is_cache_ctrl_enabled ? "enable":"disable",
is_cache_ctrl_enabled);
return 0;
}
struct kernel_param_ops cache_ctrl_enable_cb = {
.set = set_cache_control,
.get = param_get_int,
};
param_check_int(enabled, &is_cache_ctrl_enabled);
module_param_cb(enable, &cache_ctrl_enable_cb, &is_cache_ctrl_enabled, 0664);
__MODULE_PARM_TYPE(enabled, "int");
static int __init cache_ctrl_init(void)
{
register_qos_notifier(&nb);
cache_ctrl_write_partcr(0x8F);
l3cc_debugfs = debugfs_create_file("l3cc_info",
0444,
NULL,
NULL,
&l3cc_debug_fops);
return 0;
}
static void __exit cache_ctrl_exit(void)
{
unregister_qos_notifier(&nb);
}
module_init(cache_ctrl_init);
module_exit(cache_ctrl_exit);