Files
android/drivers/misc/mediatek/pidmap/pidmap.c
T
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

426 lines
10 KiB
C

/*
* Copyright (C) 2018 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.
*/
#ifdef CONFIG_MTK_PID_MAP
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <mt-plat/mtk_pidmap.h>
/*
* The best way favor performance to know task name is
* keeping pid and task name mapping in a one-way table.
*
* Other options will suffer performance, for example,
* using find_task_by_vpid() w/ rcu read lock, or
* maintaining mapping in an rb-tree.
*
* Besides, static memory is chosen for pid map because
* static memory layout is required for some exception
* handling flow, e.g., hwt or hw reboot.
*/
#define TAG "[PIDMAP]"
static DEFINE_SPINLOCK(mtk_pidmap_lock);
static char __rcu *mtk_pidmap;
static bool mtk_pidmap_enable;
static int mtk_pidmap_proc_dump_mode;
static int mtk_pidmap_max_pid;
static struct proc_dir_entry *mtk_pidmap_proc_entry;
static bool mtk_pidmap_allocate(void)
{
char *pidmap = NULL;
char *old_pidmap = NULL;
pidmap = kmalloc(PIDMAP_AEE_BUF_SIZE, GFP_KERNEL);
if (!pidmap)
return false;
memset(pidmap, 0, PIDMAP_AEE_BUF_SIZE);
spin_lock(&mtk_pidmap_lock);
old_pidmap = rcu_dereference_protected(mtk_pidmap,
lockdep_is_held(&mtk_pidmap_lock));
rcu_assign_pointer(mtk_pidmap, pidmap);
spin_unlock(&mtk_pidmap_lock);
synchronize_rcu();
kfree(old_pidmap);
return true;
}
static void mtk_pidmap_reset(void)
{
char *pidmap;
rcu_read_lock();
pidmap = rcu_dereference(mtk_pidmap);
if (pidmap)
memset(pidmap, 0, PIDMAP_AEE_BUF_SIZE);
rcu_read_unlock();
}
static void mtk_pidmap_free(void)
{
char *old_pidmap = NULL;
spin_lock(&mtk_pidmap_lock);
old_pidmap = rcu_dereference_protected(mtk_pidmap,
lockdep_is_held(&mtk_pidmap_lock));
rcu_assign_pointer(mtk_pidmap, NULL);
spin_unlock(&mtk_pidmap_lock);
synchronize_rcu();
kfree(old_pidmap);
}
static void mtk_pidmap_init_map(void)
{
/*
* now pidmap is designed for keeping
* maximum 32768 pids in 512 KB buffer.
*/
bool rs = mtk_pidmap_allocate();
if (rs) {
mtk_pidmap_proc_dump_mode = PIDMAP_PROC_DUMP_RAW;
mtk_pidmap_max_pid = PIDMAP_AEE_BUF_SIZE / PIDMAP_ENTRY_SIZE;
mtk_pidmap_enable = true;
} else {
pr_info(TAG " init: fail to init pidmap\n");
mtk_pidmap_enable = false;
}
}
static void mtk_pidmap_destroy_map(void)
{
mtk_pidmap_free();
mtk_pidmap_enable = false;
mtk_pidmap_max_pid = 0;
}
/*
* mtk_pidmap_update
* insert or update new mapping between pid and task name.
*
* task: current task
*/
void mtk_pidmap_update(struct task_struct *task)
{
char *pidmap = NULL;
char *name;
int len;
pid_t pid;
rcu_read_lock();
pidmap = rcu_dereference(mtk_pidmap);
if (unlikely(!pidmap)) {
pr_debug(TAG " update: pid map is not ready\n");
goto out;
}
pid = task->pid;
if (unlikely((pid < 1) || (pid > mtk_pidmap_max_pid)))
goto out;
/*
* part 1, get current task's name.
*
* this could be lockless because the specific offset
* will be updated by its task only.
*/
name = pidmap + ((pid - 1) * PIDMAP_ENTRY_SIZE);
/* copy task name */
memcpy(name, task->comm, PIDMAP_TASKNAME_SIZE);
*(name + PIDMAP_TASKNAME_SIZE) = '\0';
/* clear garbage tail chars to help parsers */
len = strlen(name);
if (len > PIDMAP_TASKNAME_SIZE)
len = PIDMAP_TASKNAME_SIZE;
memset(name + len, 0, PIDMAP_TASKNAME_SIZE - len);
/* part 2, copy thread group ID as hex format */
name += PIDMAP_TASKNAME_SIZE;
*(name + 0) = (char)(task->tgid & 0xFF);
*(name + 1) = (char)(task->tgid >> 8);
out:
rcu_read_unlock();
}
static void mtk_pidmap_seq_dump_disable(struct seq_file *seq)
{
seq_puts(seq, "<PID Map>\n");
seq_puts(seq, "PIDMAP is disabled, please use \"echo 2 > pidmap\" to enable it\n");
seq_puts(seq, "\n<Configuration>\n");
seq_puts(seq, "echo 0 > pidmap: Dump raw pidmap (default, for AEE DB)\n");
seq_puts(seq, "echo 1 > pidmap: Dump readable pidmap\n");
seq_puts(seq, "echo 2 > pidmap: Reset or Enable pidmap\n");
seq_puts(seq, "echo 3 > pidmap: Disable pidmap\n");
}
static void mtk_pidmap_seq_dump_readable(char **buff, unsigned long *size,
struct seq_file *seq)
{
char *pidmap;
int i, active_pid;
pid_t tgid;
char *name;
char name_tmp[TASK_COMM_LEN + 1] = {0};
seq_puts(seq, "<PID Map>\n");
seq_puts(seq, "PID\tTGID\tTask Name\n");
/*
* pid map shall be protected for dump, however
* we ignore locking here to favor performance.
*/
active_pid = 0;
rcu_read_lock();
pidmap = rcu_dereference(mtk_pidmap);
if (unlikely(!pidmap)) {
pr_debug(TAG " dump readable: pid map is not ready\n");
goto out;
}
for (i = 0; i < PIDMAP_ENTRY_CNT; i++) {
name = pidmap + (i * PIDMAP_ENTRY_SIZE);
if (name[0]) {
/* get task name */
memset(name_tmp, 0, PIDMAP_TASKNAME_SIZE);
memcpy(name_tmp, name, PIDMAP_TASKNAME_SIZE);
/* get tgid */
name += PIDMAP_TASKNAME_SIZE;
tgid = (pid_t)name[0] +
((pid_t)name[1] << 8);
seq_printf(seq, "%d\t%d\t%s\n",
i + 1, tgid, name_tmp);
active_pid++;
}
}
out:
rcu_read_unlock();
seq_puts(seq, "\n<Information>\n");
seq_printf(seq, "Total PIDs: %d\n", active_pid);
seq_printf(seq, "Entry size: %d bytes\n",
PIDMAP_ENTRY_SIZE);
seq_printf(seq, " - Task name size: %d bytes\n",
(int)PIDMAP_TASKNAME_SIZE);
seq_printf(seq, " - TGID size: %d bytes\n",
(int)PIDMAP_TGID_SIZE);
seq_printf(seq, "Total Buffer Size: %d bytes\n",
PIDMAP_AEE_BUF_SIZE + PIDMAP_PROC_CMD_BUF_SIZE);
seq_printf(seq, "mtk_pidmap address: 0x%p\n", pidmap);
seq_puts(seq, "\n<Configuration>\n");
seq_puts(seq, "echo 0 > pidmap: Dump raw pidmap (default, for AEE DB)\n");
seq_puts(seq, "echo 1 > pidmap: Dump readable pidmap\n");
seq_puts(seq, "echo 2 > pidmap: Reset or Enable pidmap\n");
seq_puts(seq, "echo 3 > pidmap: Disable pidmap\n");
}
static void mtk_pidmap_seq_dump_raw(struct seq_file *seq)
{
int i;
char *pidmap;
/*
* pid map shall be protected for dump, however
* we ignore locking here to favor performance.
*
* Notice: be aware that you shall NOT read this seq_file
* in Windows environment because Windows will automatically
* add Carriage Return 0Dh ('\r') while you output 0Ah ('\n')
* unexpectedly.
*
* 0Ah may be existed for TGID, and you'll get an incorrect
* PID map outputs.
*/
rcu_read_lock();
pidmap = rcu_dereference(mtk_pidmap);
if (unlikely(!pidmap)) {
pr_debug(TAG " dump raw: pid map is not ready\n");
rcu_read_unlock();
return;
}
for (i = 0; i < PIDMAP_AEE_BUF_SIZE; i++)
seq_putc(seq, pidmap[i]);
rcu_read_unlock();
}
static int mtk_pidmap_seq_show(struct seq_file *seq, void *v)
{
if (!mtk_pidmap_enable) {
mtk_pidmap_seq_dump_disable(seq);
return 0;
}
if (mtk_pidmap_proc_dump_mode == PIDMAP_PROC_DUMP_READABLE)
mtk_pidmap_seq_dump_readable(NULL, NULL, seq);
else if (mtk_pidmap_proc_dump_mode == PIDMAP_PROC_DUMP_RAW)
mtk_pidmap_seq_dump_raw(seq);
return 0;
}
void get_pidmap_aee_buffer(unsigned long *vaddr, unsigned long *size)
{
char *pidmap;
rcu_read_lock();
pidmap = rcu_dereference(mtk_pidmap);
if (unlikely(pidmap == NULL)) {
pr_info(TAG " aee buffer: pid map is not ready\n");
if (vaddr)
*vaddr = 0;
if (size)
*size = 0;
} else {
/* retrun start location */
/* FIXME pidmap is exposed to external module without rcu read lock. */
if (vaddr)
*vaddr = (unsigned long) pidmap;
/* return valid buffer size */
if (size)
*size = PIDMAP_AEE_BUF_SIZE;
}
rcu_read_unlock();
}
EXPORT_SYMBOL(get_pidmap_aee_buffer);
static ssize_t mtk_pidmap_proc_write(struct file *file, const char *buf,
size_t count, loff_t *data)
{
int ret;
char cmd[PIDMAP_PROC_CMD_BUF_SIZE] = {0};
if (count == 0)
goto err;
else if (count > PIDMAP_PROC_CMD_BUF_SIZE)
count = PIDMAP_PROC_CMD_BUF_SIZE;
ret = copy_from_user(cmd, buf, count);
if (ret < 0)
goto err;
if (cmd[0] == '0') {
mtk_pidmap_proc_dump_mode = PIDMAP_PROC_DUMP_RAW;
pr_info(TAG " dump mode: raw\n");
} else if (cmd[0] == '1') {
mtk_pidmap_proc_dump_mode = PIDMAP_PROC_DUMP_READABLE;
pr_info(TAG " dump mode: readable\n");
} else if (cmd[0] == '2') {
if (mtk_pidmap_enable) {
mtk_pidmap_reset();
pr_info(TAG " reset pidmap\n");
} else {
mtk_pidmap_allocate();
mtk_pidmap_enable = true;
pr_info(TAG " enable pidmap\n");
}
} else if (cmd[0] == '3') {
if (mtk_pidmap_enable) {
mtk_pidmap_free();
mtk_pidmap_enable = false;
pr_info(TAG " disable pidmap\n");
} else
pr_info(TAG " pidmap has been disabled");
} else
goto err;
goto out;
err:
pr_info(TAG " invalid arg: 0x%x\n", cmd[0]);
return -1;
out:
return count;
}
static int mtk_pidmap_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, mtk_pidmap_seq_show, inode->i_private);
}
static const struct file_operations mtk_pidmap_proc_fops = {
.open = mtk_pidmap_proc_open,
.write = mtk_pidmap_proc_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int mtk_pidmap_proc_init(void)
{
kuid_t uid;
kgid_t gid;
uid = make_kuid(&init_user_ns, 0);
gid = make_kgid(&init_user_ns, 1001);
mtk_pidmap_proc_entry = proc_create("pidmap",
PIDMAP_PROC_PERM, NULL,
&mtk_pidmap_proc_fops);
if (mtk_pidmap_proc_entry)
proc_set_user(mtk_pidmap_proc_entry, uid, gid);
else
pr_info(TAG " failed to create /proc/pidmap\n");
return 0;
}
static int __init mtk_pidmap_init(void)
{
mtk_pidmap_proc_init();
mtk_pidmap_init_map();
return 0;
}
static void __exit mtk_pidmap_exit(void)
{
mtk_pidmap_destroy_map();
proc_remove(mtk_pidmap_proc_entry);
}
module_init(mtk_pidmap_init);
module_exit(mtk_pidmap_exit);
#else /* CONFIG_MTK_PID_MAP */
void get_pidmap_aee_buffer(unsigned long *vaddr, unsigned long *size)
{
/* return valid buffer size */
if (size)
*size = 0;
}
EXPORT_SYMBOL(get_pidmap_aee_buffer);
#endif /* CONFIG_MTK_PID_MAP */
MODULE_AUTHOR("Stanley Chu <stanley.chu@mediatek.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PID Map");