* [PATCH 1/8] mm/page_owner: Add PID filtering support
2026-08-28 3:13 [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering Zhen Ni
@ 2026-08-28 3:13 ` Zhen Ni
2026-08-28 3:13 ` [PATCH 2/8] mm/page_owner: Add TGID " Zhen Ni
` (7 subsequent siblings)
8 siblings, 0 replies; 16+ messages in thread
From: Zhen Ni @ 2026-08-28 3:13 UTC (permalink / raw)
To: Andrew Morton
Cc: David Hildenbrand, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-doc, linux-kernel,
Zhen Ni
Add PID filtering support. Users can filter page_owner output by process
IDs using the "pid=<pid_list>" command format. The filter supports up to
16 PIDs specified as a comma-separated list. PIDs are stored in sorted
order for efficient binary search matching during page owner iteration.
Signed-off-by: Zhen Ni <zhen.ni@easystack.cn>
---
mm/page_owner.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 83 insertions(+), 2 deletions(-)
diff --git a/mm/page_owner.c b/mm/page_owner.c
index fbbda7ba914b..33b9e12a019b 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -12,6 +12,8 @@
#include <linux/seq_file.h>
#include <linux/memcontrol.h>
#include <linux/sched/clock.h>
+#include <linux/bsearch.h>
+#include <linux/sort.h>
#include "page_alloc.h"
@@ -66,12 +68,24 @@ static const char * const page_owner_print_mode_strings[] = {
[PAGE_OWNER_PRINT_STACK_HANDLE] = "stack_handle",
};
+/* PID_MAX_LIMIT = 4,194,304 (7 decimal digits) */
+#define PID_MAX_DIGITS 7
+#define MAX_FILTER_PIDS 16
+
struct page_owner_filter_state {
enum page_owner_print_mode print_mode;
- nodemask_t nid_filter;
bool nid_filter_enabled;
+ bool proc_filter_enabled;
+ nodemask_t nid_filter;
+ int pid_count;
+ pid_t pid_list[MAX_FILTER_PIDS];
};
+static int cmp_pid_t(const void *a, const void *b)
+{
+ return *(pid_t *)a - *(pid_t *)b;
+}
+
static bool page_owner_enabled __initdata;
DEFINE_STATIC_KEY_FALSE(page_owner_inited);
@@ -820,6 +834,19 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
goto ext_put_continue;
}
+ if (state->proc_filter_enabled) {
+ bool proc_match = false;
+
+ proc_match = bsearch(&page_owner->pid,
+ state->pid_list,
+ state->pid_count,
+ sizeof(pid_t),
+ cmp_pid_t) != NULL;
+
+ if (!proc_match)
+ goto ext_put_continue;
+ }
+
/* Record the next PFN to read in the file offset */
*ppos = pfn + 1;
@@ -927,6 +954,7 @@ static int page_owner_open(struct inode *inode, struct file *file)
state->print_mode = PAGE_OWNER_PRINT_STACK;
nodes_clear(state->nid_filter);
state->nid_filter_enabled = false;
+ state->proc_filter_enabled = false;
file->private_data = state;
return 0;
}
@@ -937,6 +965,37 @@ static int page_owner_release(struct inode *inode, struct file *file)
return 0;
}
+static int parse_pid_t_list(const char *str, pid_t *list, int *count)
+{
+ char *str_copy, *token;
+ int i = 0;
+ unsigned int pid;
+ int ret = 0;
+
+ str_copy = kstrdup(str, GFP_KERNEL);
+ if (!str_copy)
+ return -ENOMEM;
+
+ while ((token = strsep(&str_copy, ",")) != NULL) {
+ if (*token == '\0')
+ continue;
+ if (i >= MAX_FILTER_PIDS) {
+ ret = -E2BIG;
+ goto out_free;
+ }
+ if (kstrtouint(token, 10, &pid) != 0) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+ list[i++] = (pid_t)pid;
+ }
+
+ *count = i;
+out_free:
+ kfree(str_copy);
+ return ret;
+}
+
static ssize_t page_owner_write(struct file *file,
const char __user *buf,
size_t count, loff_t *ppos)
@@ -949,6 +1008,9 @@ static ssize_t page_owner_write(struct file *file,
enum page_owner_print_mode new_print_mode;
nodemask_t new_nid_filter;
bool new_nid_filter_enabled;
+ bool new_proc_filter_enabled;
+ pid_t new_pid_list[MAX_FILTER_PIDS];
+ int new_pid_count = 0;
/*
* Maximum input length for filter commands:
@@ -956,8 +1018,10 @@ static ssize_t page_owner_write(struct file *file,
* with sufficient buffer
* - 6 * MAX_NUMNODES: worst case for nid list
* Worst case per node: ",NNNNN" (comma + 5-digit node number) = 6 bytes
+ * - For list filters: (digit+comma) * count + prefix
*/
- if (count > 32 + 6 * MAX_NUMNODES)
+ if (count > 32 + 6 * MAX_NUMNODES +
+ (PID_MAX_DIGITS + 1) * MAX_FILTER_PIDS + 4)
return -EINVAL;
kbuf = memdup_user_nul(buf, count);
@@ -969,6 +1033,11 @@ static ssize_t page_owner_write(struct file *file,
new_print_mode = state->print_mode;
new_nid_filter = state->nid_filter;
new_nid_filter_enabled = state->nid_filter_enabled;
+ new_proc_filter_enabled = state->proc_filter_enabled;
+ if (state->pid_count > 0) {
+ memcpy(new_pid_list, state->pid_list, sizeof(state->pid_list));
+ new_pid_count = state->pid_count;
+ }
while ((token = strsep(&kbuf, " \t\n")) != NULL) {
if (*token == '\0')
@@ -1000,6 +1069,10 @@ static ssize_t page_owner_write(struct file *file,
}
new_nid_filter_enabled = true;
+ } else if (!strncmp(token, "pid=", 4)) {
+ ret = parse_pid_t_list(token + 4, new_pid_list, &new_pid_count);
+ if (ret < 0)
+ goto out_free;
} else {
ret = -EINVAL;
goto out_free;
@@ -1010,6 +1083,14 @@ static ssize_t page_owner_write(struct file *file,
state->print_mode = new_print_mode;
state->nid_filter = new_nid_filter;
state->nid_filter_enabled = new_nid_filter_enabled;
+ state->proc_filter_enabled = new_pid_count > 0;
+ if (new_pid_count > 0) {
+ memcpy(state->pid_list, new_pid_list, sizeof(state->pid_list));
+ state->pid_count = new_pid_count;
+ }
+ if (state->pid_count > 1)
+ sort(state->pid_list, state->pid_count, sizeof(pid_t),
+ cmp_pid_t, NULL);
ret = count;
--
2.20.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 2/8] mm/page_owner: Add TGID filtering support
2026-08-28 3:13 [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering Zhen Ni
2026-08-28 3:13 ` [PATCH 1/8] mm/page_owner: Add PID filtering support Zhen Ni
@ 2026-08-28 3:13 ` Zhen Ni
2026-08-28 3:13 ` [PATCH 3/8] mm/page_owner: Add COMM filtering with wildcard support Zhen Ni
` (6 subsequent siblings)
8 siblings, 0 replies; 16+ messages in thread
From: Zhen Ni @ 2026-08-28 3:13 UTC (permalink / raw)
To: Andrew Morton
Cc: David Hildenbrand, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-doc, linux-kernel,
Zhen Ni
Extend filter to support thread group ID (TGID) filtering alongside
PID filtering. Reuses existing PID parsing and binary search
infrastructure with separate TGID list and count fields.
Signed-off-by: Zhen Ni <zhen.ni@easystack.cn>
---
mm/page_owner.c | 43 ++++++++++++++++++++++++++++++++++++-------
1 file changed, 36 insertions(+), 7 deletions(-)
diff --git a/mm/page_owner.c b/mm/page_owner.c
index 33b9e12a019b..077867e9e572 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -71,6 +71,7 @@ static const char * const page_owner_print_mode_strings[] = {
/* PID_MAX_LIMIT = 4,194,304 (7 decimal digits) */
#define PID_MAX_DIGITS 7
#define MAX_FILTER_PIDS 16
+#define MAX_FILTER_TGIDS 16
struct page_owner_filter_state {
enum page_owner_print_mode print_mode;
@@ -78,7 +79,9 @@ struct page_owner_filter_state {
bool proc_filter_enabled;
nodemask_t nid_filter;
int pid_count;
+ int tgid_count;
pid_t pid_list[MAX_FILTER_PIDS];
+ pid_t tgid_list[MAX_FILTER_TGIDS];
};
static int cmp_pid_t(const void *a, const void *b)
@@ -837,11 +840,19 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
if (state->proc_filter_enabled) {
bool proc_match = false;
- proc_match = bsearch(&page_owner->pid,
- state->pid_list,
- state->pid_count,
- sizeof(pid_t),
- cmp_pid_t) != NULL;
+ if (state->pid_count > 0)
+ proc_match = bsearch(&page_owner->pid,
+ state->pid_list,
+ state->pid_count,
+ sizeof(pid_t),
+ cmp_pid_t) != NULL;
+
+ if (!proc_match && state->tgid_count > 0)
+ proc_match = bsearch(&page_owner->tgid,
+ state->tgid_list,
+ state->tgid_count,
+ sizeof(pid_t),
+ cmp_pid_t) != NULL;
if (!proc_match)
goto ext_put_continue;
@@ -1010,7 +1021,9 @@ static ssize_t page_owner_write(struct file *file,
bool new_nid_filter_enabled;
bool new_proc_filter_enabled;
pid_t new_pid_list[MAX_FILTER_PIDS];
+ pid_t new_tgid_list[MAX_FILTER_TGIDS];
int new_pid_count = 0;
+ int new_tgid_count = 0;
/*
* Maximum input length for filter commands:
@@ -1021,7 +1034,8 @@ static ssize_t page_owner_write(struct file *file,
* - For list filters: (digit+comma) * count + prefix
*/
if (count > 32 + 6 * MAX_NUMNODES +
- (PID_MAX_DIGITS + 1) * MAX_FILTER_PIDS + 4)
+ (PID_MAX_DIGITS + 1) * MAX_FILTER_PIDS + 4 +
+ (PID_MAX_DIGITS + 1) * MAX_FILTER_TGIDS + 5)
return -EINVAL;
kbuf = memdup_user_nul(buf, count);
@@ -1038,6 +1052,10 @@ static ssize_t page_owner_write(struct file *file,
memcpy(new_pid_list, state->pid_list, sizeof(state->pid_list));
new_pid_count = state->pid_count;
}
+ if (state->tgid_count > 0) {
+ memcpy(new_tgid_list, state->tgid_list, sizeof(state->tgid_list));
+ new_tgid_count = state->tgid_count;
+ }
while ((token = strsep(&kbuf, " \t\n")) != NULL) {
if (*token == '\0')
@@ -1073,6 +1091,10 @@ static ssize_t page_owner_write(struct file *file,
ret = parse_pid_t_list(token + 4, new_pid_list, &new_pid_count);
if (ret < 0)
goto out_free;
+ } else if (!strncmp(token, "tgid=", 5)) {
+ ret = parse_pid_t_list(token + 5, new_tgid_list, &new_tgid_count);
+ if (ret < 0)
+ goto out_free;
} else {
ret = -EINVAL;
goto out_free;
@@ -1083,7 +1105,7 @@ static ssize_t page_owner_write(struct file *file,
state->print_mode = new_print_mode;
state->nid_filter = new_nid_filter;
state->nid_filter_enabled = new_nid_filter_enabled;
- state->proc_filter_enabled = new_pid_count > 0;
+ state->proc_filter_enabled = new_pid_count > 0 || new_tgid_count > 0;
if (new_pid_count > 0) {
memcpy(state->pid_list, new_pid_list, sizeof(state->pid_list));
state->pid_count = new_pid_count;
@@ -1091,6 +1113,13 @@ static ssize_t page_owner_write(struct file *file,
if (state->pid_count > 1)
sort(state->pid_list, state->pid_count, sizeof(pid_t),
cmp_pid_t, NULL);
+ if (new_tgid_count > 0) {
+ memcpy(state->tgid_list, new_tgid_list, sizeof(state->tgid_list));
+ state->tgid_count = new_tgid_count;
+ }
+ if (state->tgid_count > 1)
+ sort(state->tgid_list, state->tgid_count, sizeof(pid_t),
+ cmp_pid_t, NULL);
ret = count;
--
2.20.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 3/8] mm/page_owner: Add COMM filtering with wildcard support
2026-08-28 3:13 [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering Zhen Ni
2026-08-28 3:13 ` [PATCH 1/8] mm/page_owner: Add PID filtering support Zhen Ni
2026-08-28 3:13 ` [PATCH 2/8] mm/page_owner: Add TGID " Zhen Ni
@ 2026-08-28 3:13 ` Zhen Ni
2026-08-28 3:13 ` [PATCH 4/8] mm/page_owner: Refactor memcg handling for cgroup filter support Zhen Ni
` (5 subsequent siblings)
8 siblings, 0 replies; 16+ messages in thread
From: Zhen Ni @ 2026-08-28 3:13 UTC (permalink / raw)
To: Andrew Morton
Cc: David Hildenbrand, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-doc, linux-kernel,
Zhen Ni
Add process name (COMM) filtering to page_owner with glob-style wildcard
pattern matching support. Users can now filter page_owner output by
process names using flexible patterns.
Supported wildcards:
* : matches any sequence of characters
? : matches any single character
[abc]: matches any character in the set
[a-z]: matches any character in the range
Examples:
comm=python* : matches python, python3, python3.9, etc.
comm=*sh : matches bash, zsh, dash, etc.
Signed-off-by: Zhen Ni <zhen.ni@easystack.cn>
---
mm/page_owner.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 79 insertions(+), 3 deletions(-)
diff --git a/mm/page_owner.c b/mm/page_owner.c
index 077867e9e572..7cd533682054 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -14,6 +14,7 @@
#include <linux/sched/clock.h>
#include <linux/bsearch.h>
#include <linux/sort.h>
+#include <linux/glob.h>
#include "page_alloc.h"
@@ -72,6 +73,7 @@ static const char * const page_owner_print_mode_strings[] = {
#define PID_MAX_DIGITS 7
#define MAX_FILTER_PIDS 16
#define MAX_FILTER_TGIDS 16
+#define MAX_FILTER_COMMS 8
struct page_owner_filter_state {
enum page_owner_print_mode print_mode;
@@ -80,8 +82,10 @@ struct page_owner_filter_state {
nodemask_t nid_filter;
int pid_count;
int tgid_count;
+ int comm_count;
pid_t pid_list[MAX_FILTER_PIDS];
pid_t tgid_list[MAX_FILTER_TGIDS];
+ char comm_list[MAX_FILTER_COMMS][TASK_COMM_LEN];
};
static int cmp_pid_t(const void *a, const void *b)
@@ -854,6 +858,20 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
sizeof(pid_t),
cmp_pid_t) != NULL;
+ if (!proc_match && state->comm_count > 0) {
+ bool comm_match = false;
+ int i;
+
+ for (i = 0; i < state->comm_count; i++) {
+ if (glob_match(state->comm_list[i],
+ page_owner->comm)) {
+ comm_match = true;
+ break;
+ }
+ }
+ proc_match = comm_match;
+ }
+
if (!proc_match)
goto ext_put_continue;
}
@@ -1007,6 +1025,38 @@ static int parse_pid_t_list(const char *str, pid_t *list, int *count)
return ret;
}
+static int parse_comm_list(const char *str, char (*list)[TASK_COMM_LEN], int *count)
+{
+ char *str_copy, *token;
+ int i = 0;
+ int ret = 0;
+
+ str_copy = kstrdup(str, GFP_KERNEL);
+ if (!str_copy)
+ return -ENOMEM;
+
+ while ((token = strsep(&str_copy, ",")) != NULL) {
+ token = strstrip(token);
+ if (*token == '\0')
+ continue;
+ if (i >= MAX_FILTER_COMMS) {
+ ret = -E2BIG;
+ goto out_free;
+ }
+ strscpy(list[i], token, TASK_COMM_LEN);
+ i++;
+ }
+
+ *count = i;
+
+ if (i == 0)
+ ret = -EINVAL;
+
+out_free:
+ kfree(str_copy);
+ return ret;
+}
+
static ssize_t page_owner_write(struct file *file,
const char __user *buf,
size_t count, loff_t *ppos)
@@ -1022,8 +1072,10 @@ static ssize_t page_owner_write(struct file *file,
bool new_proc_filter_enabled;
pid_t new_pid_list[MAX_FILTER_PIDS];
pid_t new_tgid_list[MAX_FILTER_TGIDS];
+ char (*new_comm_list)[TASK_COMM_LEN] = NULL;
int new_pid_count = 0;
int new_tgid_count = 0;
+ int new_comm_count = 0;
/*
* Maximum input length for filter commands:
@@ -1035,12 +1087,19 @@ static ssize_t page_owner_write(struct file *file,
*/
if (count > 32 + 6 * MAX_NUMNODES +
(PID_MAX_DIGITS + 1) * MAX_FILTER_PIDS + 4 +
- (PID_MAX_DIGITS + 1) * MAX_FILTER_TGIDS + 5)
+ (PID_MAX_DIGITS + 1) * MAX_FILTER_TGIDS + 5 +
+ TASK_COMM_LEN * MAX_FILTER_COMMS + 5)
return -EINVAL;
+ new_comm_list = kmalloc_array(MAX_FILTER_COMMS, TASK_COMM_LEN, GFP_KERNEL);
+ if (!new_comm_list)
+ return -ENOMEM;
+
kbuf = memdup_user_nul(buf, count);
- if (IS_ERR(kbuf))
+ if (IS_ERR(kbuf)) {
+ kfree(new_comm_list);
return PTR_ERR(kbuf);
+ }
orig = kbuf;
@@ -1056,6 +1115,11 @@ static ssize_t page_owner_write(struct file *file,
memcpy(new_tgid_list, state->tgid_list, sizeof(state->tgid_list));
new_tgid_count = state->tgid_count;
}
+ if (state->comm_count > 0) {
+ memcpy(new_comm_list, state->comm_list,
+ state->comm_count * TASK_COMM_LEN);
+ new_comm_count = state->comm_count;
+ }
while ((token = strsep(&kbuf, " \t\n")) != NULL) {
if (*token == '\0')
@@ -1095,6 +1159,10 @@ static ssize_t page_owner_write(struct file *file,
ret = parse_pid_t_list(token + 5, new_tgid_list, &new_tgid_count);
if (ret < 0)
goto out_free;
+ } else if (!strncmp(token, "comm=", 5)) {
+ ret = parse_comm_list(token + 5, new_comm_list, &new_comm_count);
+ if (ret < 0)
+ goto out_free;
} else {
ret = -EINVAL;
goto out_free;
@@ -1105,7 +1173,9 @@ static ssize_t page_owner_write(struct file *file,
state->print_mode = new_print_mode;
state->nid_filter = new_nid_filter;
state->nid_filter_enabled = new_nid_filter_enabled;
- state->proc_filter_enabled = new_pid_count > 0 || new_tgid_count > 0;
+ state->proc_filter_enabled = new_pid_count > 0 ||
+ new_tgid_count > 0 ||
+ new_comm_count > 0;
if (new_pid_count > 0) {
memcpy(state->pid_list, new_pid_list, sizeof(state->pid_list));
state->pid_count = new_pid_count;
@@ -1120,10 +1190,16 @@ static ssize_t page_owner_write(struct file *file,
if (state->tgid_count > 1)
sort(state->tgid_list, state->tgid_count, sizeof(pid_t),
cmp_pid_t, NULL);
+ if (new_comm_count > 0) {
+ memcpy(state->comm_list, new_comm_list,
+ new_comm_count * TASK_COMM_LEN);
+ state->comm_count = new_comm_count;
+ }
ret = count;
out_free:
+ kfree(new_comm_list);
kfree(orig);
return ret;
}
--
2.20.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 4/8] mm/page_owner: Refactor memcg handling for cgroup filter support
2026-08-28 3:13 [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering Zhen Ni
` (2 preceding siblings ...)
2026-08-28 3:13 ` [PATCH 3/8] mm/page_owner: Add COMM filtering with wildcard support Zhen Ni
@ 2026-08-28 3:13 ` Zhen Ni
2026-08-28 3:13 ` [PATCH 5/8] mm/page_owner: Add memcg " Zhen Ni
` (4 subsequent siblings)
8 siblings, 0 replies; 16+ messages in thread
From: Zhen Ni @ 2026-08-28 3:13 UTC (permalink / raw)
To: Andrew Morton
Cc: David Hildenbrand, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-doc, linux-kernel,
Zhen Ni
Extract memcg information retrieval from printing logic to prepare for
cgroup filtering support. Introduce struct memcg_info to hold cgroup
data that can be reused for both display output and filtering
decisions.
No functional change - output behavior unchanged.
Signed-off-by: Zhen Ni <zhen.ni@easystack.cn>
---
mm/page_owner.c | 62 +++++++++++++++++++++++++++++++++++--------------
1 file changed, 44 insertions(+), 18 deletions(-)
diff --git a/mm/page_owner.c b/mm/page_owner.c
index 7cd533682054..09cfad10a7df 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -69,6 +69,13 @@ static const char * const page_owner_print_mode_strings[] = {
[PAGE_OWNER_PRINT_STACK_HANDLE] = "stack_handle",
};
+struct memcg_info {
+ char name[80];
+ bool is_slab;
+ bool is_objcg;
+ bool is_online;
+};
+
/* PID_MAX_LIMIT = 4,194,304 (7 decimal digits) */
#define PID_MAX_DIGITS 7
#define MAX_FILTER_PIDS 16
@@ -573,16 +580,13 @@ void pagetypeinfo_showmixedcount_print(struct seq_file *m,
#ifdef CONFIG_MEMCG
/*
- * Looking for memcg information and print it out
+ * Get memcg information from page
*/
-static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret,
- struct page *page)
+static void get_page_memcg_info(struct page *page, struct memcg_info *info)
{
unsigned long memcg_data;
struct obj_cgroup *objcg;
struct mem_cgroup *memcg;
- bool online;
- char name[80];
rcu_read_lock();
memcg_data = READ_ONCE(page->memcg_data);
@@ -590,8 +594,7 @@ static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret,
goto out_unlock;
if (memcg_data & MEMCG_DATA_OBJEXTS) {
- ret += scnprintf(kbuf + ret, count - ret,
- "Slab cache page\n");
+ info->is_slab = true;
goto out_unlock;
}
@@ -600,21 +603,38 @@ static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret,
if (!memcg)
goto out_unlock;
- online = css_is_online(&memcg->css);
- cgroup_name(memcg->css.cgroup, name, sizeof(name));
- ret += scnprintf(kbuf + ret, count - ret,
- "Charged %sto %smemcg %s\n",
- (memcg_data & MEMCG_DATA_KMEM) ? "(via objcg) " : "",
- online ? "" : "offline ",
- name);
+ info->is_objcg = (memcg_data & MEMCG_DATA_KMEM) != 0;
+ info->is_online = css_is_online(&memcg->css);
+ cgroup_name(memcg->css.cgroup, info->name, sizeof(info->name));
out_unlock:
rcu_read_unlock();
+}
+
+/*
+ * Print memcg information from memcg_info
+ */
+static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret,
+ const struct memcg_info *info)
+{
+ if (!info)
+ return ret;
+
+ if (info->is_slab)
+ ret += scnprintf(kbuf + ret, count - ret,
+ "Slab cache page\n");
+
+ if (info->name[0])
+ ret += scnprintf(kbuf + ret, count - ret,
+ "Charged %sto %smemcg %s\n",
+ info->is_objcg ? "(via objcg) " : "",
+ info->is_online ? "" : "offline ",
+ info->name);
return ret;
}
#else
static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret,
- struct page *page)
+ const struct memcg_info *info)
{
return ret;
}
@@ -624,7 +644,8 @@ static ssize_t
print_page_owner(char __user *buf, size_t count, unsigned long pfn,
struct page *page, struct page_owner *page_owner,
depot_stack_handle_t handle,
- struct page_owner_filter_state *state)
+ struct page_owner_filter_state *state,
+ const struct memcg_info *memcg_info)
{
int ret, pageblock_mt, page_mt;
char *kbuf;
@@ -674,7 +695,7 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn,
migrate_reason_names[page_owner->last_migrate_reason]);
}
- ret = print_page_owner_memcg(kbuf, count, ret, page);
+ ret = print_page_owner_memcg(kbuf, count, ret, memcg_info);
ret += snprintf(kbuf + ret, count - ret, "\n");
if (ret >= count)
@@ -777,6 +798,7 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
* user through copy_to_user() or GFP_KERNEL allocations.
*/
struct page_owner page_owner_tmp;
+ struct memcg_info memcg_info = {};
/*
* If the new page is in a new MAX_ORDER_NR_PAGES area,
@@ -876,13 +898,17 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
goto ext_put_continue;
}
+#ifdef CONFIG_MEMCG
+ get_page_memcg_info(page, &memcg_info);
+#endif
+
/* Record the next PFN to read in the file offset */
*ppos = pfn + 1;
page_owner_tmp = *page_owner;
page_ext_put(page_ext);
return print_page_owner(buf, count, pfn, page,
- &page_owner_tmp, handle, state);
+ &page_owner_tmp, handle, state, &memcg_info);
ext_put_continue:
page_ext_put(page_ext);
cond_resched();
--
2.20.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 5/8] mm/page_owner: Add memcg filter support
2026-08-28 3:13 [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering Zhen Ni
` (3 preceding siblings ...)
2026-08-28 3:13 ` [PATCH 4/8] mm/page_owner: Refactor memcg handling for cgroup filter support Zhen Ni
@ 2026-08-28 3:13 ` Zhen Ni
2026-08-28 3:13 ` [PATCH 6/8] tools/mm: Add PID/TGID/COMM filtering support to page_owner_filter Zhen Ni
` (3 subsequent siblings)
8 siblings, 0 replies; 16+ messages in thread
From: Zhen Ni @ 2026-08-28 3:13 UTC (permalink / raw)
To: Andrew Morton
Cc: David Hildenbrand, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-doc, linux-kernel,
Zhen Ni
Add memory cgroup filtering to page_owner to allow filtering pages by
their memcg path. This helps debug memory usage patterns for specific
cgroups. Users can now filter page_owner output to show only pages
belonging to a particular memory cgroup.
Collect cgroup path in memcg_info using cgroup_path() and store the
filter state in page_owner_filter_state. When the user sets memcg filter
via "memcg=<path>" command, compare each page's cgroup path against
the specified path and skip non-matching pages using strcmp.
Signed-off-by: Zhen Ni <zhen.ni@easystack.cn>
---
mm/page_owner.c | 79 +++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 74 insertions(+), 5 deletions(-)
diff --git a/mm/page_owner.c b/mm/page_owner.c
index 09cfad10a7df..21b82e927eaf 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -71,6 +71,7 @@ static const char * const page_owner_print_mode_strings[] = {
struct memcg_info {
char name[80];
+ char *path;
bool is_slab;
bool is_objcg;
bool is_online;
@@ -86,6 +87,7 @@ struct page_owner_filter_state {
enum page_owner_print_mode print_mode;
bool nid_filter_enabled;
bool proc_filter_enabled;
+ bool memcg_filter_enabled;
nodemask_t nid_filter;
int pid_count;
int tgid_count;
@@ -93,6 +95,7 @@ struct page_owner_filter_state {
pid_t pid_list[MAX_FILTER_PIDS];
pid_t tgid_list[MAX_FILTER_TGIDS];
char comm_list[MAX_FILTER_COMMS][TASK_COMM_LEN];
+ char *memcg_path;
};
static int cmp_pid_t(const void *a, const void *b)
@@ -582,12 +585,21 @@ void pagetypeinfo_showmixedcount_print(struct seq_file *m,
/*
* Get memcg information from page
*/
-static void get_page_memcg_info(struct page *page, struct memcg_info *info)
+static void get_page_memcg_info(struct page *page, struct memcg_info *info,
+ bool need_path)
{
unsigned long memcg_data;
struct obj_cgroup *objcg;
struct mem_cgroup *memcg;
+ if (need_path) {
+ info->path = kzalloc(PATH_MAX, GFP_KERNEL);
+ if (!info->path)
+ return;
+ } else {
+ info->path = NULL;
+ }
+
rcu_read_lock();
memcg_data = READ_ONCE(page->memcg_data);
if (!memcg_data || PageTail(page))
@@ -606,8 +618,14 @@ static void get_page_memcg_info(struct page *page, struct memcg_info *info)
info->is_objcg = (memcg_data & MEMCG_DATA_KMEM) != 0;
info->is_online = css_is_online(&memcg->css);
cgroup_name(memcg->css.cgroup, info->name, sizeof(info->name));
+ if (need_path)
+ cgroup_path(memcg->css.cgroup, info->path, PATH_MAX);
out_unlock:
rcu_read_unlock();
+ if (info->path && !info->path[0]) {
+ kfree(info->path);
+ info->path = NULL;
+ }
}
/*
@@ -776,6 +794,7 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
struct page_owner *page_owner;
depot_stack_handle_t handle;
struct page_owner_filter_state *state = file->private_data;
+ ssize_t ret;
if (!static_branch_unlikely(&page_owner_inited))
return -EINVAL;
@@ -899,7 +918,12 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
}
#ifdef CONFIG_MEMCG
- get_page_memcg_info(page, &memcg_info);
+ get_page_memcg_info(page, &memcg_info,
+ state->memcg_filter_enabled);
+ if (state->memcg_filter_enabled)
+ if (!memcg_info.path ||
+ strcmp(memcg_info.path, state->memcg_path) != 0)
+ goto ext_put_continue;
#endif
/* Record the next PFN to read in the file offset */
@@ -907,9 +931,12 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
page_owner_tmp = *page_owner;
page_ext_put(page_ext);
- return print_page_owner(buf, count, pfn, page,
+ ret = print_page_owner(buf, count, pfn, page,
&page_owner_tmp, handle, state, &memcg_info);
+ kfree(memcg_info.path);
+ return ret;
ext_put_continue:
+ kfree(memcg_info.path);
page_ext_put(page_ext);
cond_resched();
}
@@ -1016,7 +1043,10 @@ static int page_owner_open(struct inode *inode, struct file *file)
static int page_owner_release(struct inode *inode, struct file *file)
{
- kfree(file->private_data);
+ struct page_owner_filter_state *state = file->private_data;
+
+ kfree(state->memcg_path);
+ kfree(state);
return 0;
}
@@ -1096,9 +1126,11 @@ static ssize_t page_owner_write(struct file *file,
nodemask_t new_nid_filter;
bool new_nid_filter_enabled;
bool new_proc_filter_enabled;
+ bool new_memcg_filter_enabled;
pid_t new_pid_list[MAX_FILTER_PIDS];
pid_t new_tgid_list[MAX_FILTER_TGIDS];
char (*new_comm_list)[TASK_COMM_LEN] = NULL;
+ char *new_memcg_path;
int new_pid_count = 0;
int new_tgid_count = 0;
int new_comm_count = 0;
@@ -1114,16 +1146,24 @@ static ssize_t page_owner_write(struct file *file,
if (count > 32 + 6 * MAX_NUMNODES +
(PID_MAX_DIGITS + 1) * MAX_FILTER_PIDS + 4 +
(PID_MAX_DIGITS + 1) * MAX_FILTER_TGIDS + 5 +
- TASK_COMM_LEN * MAX_FILTER_COMMS + 5)
+ TASK_COMM_LEN * MAX_FILTER_COMMS + 5 +
+ PATH_MAX + 6)
return -EINVAL;
new_comm_list = kmalloc_array(MAX_FILTER_COMMS, TASK_COMM_LEN, GFP_KERNEL);
if (!new_comm_list)
return -ENOMEM;
+ new_memcg_path = kmalloc(PATH_MAX, GFP_KERNEL);
+ if (!new_memcg_path) {
+ kfree(new_comm_list);
+ return -ENOMEM;
+ }
+
kbuf = memdup_user_nul(buf, count);
if (IS_ERR(kbuf)) {
kfree(new_comm_list);
+ kfree(new_memcg_path);
return PTR_ERR(kbuf);
}
@@ -1146,6 +1186,9 @@ static ssize_t page_owner_write(struct file *file,
state->comm_count * TASK_COMM_LEN);
new_comm_count = state->comm_count;
}
+ new_memcg_filter_enabled = state->memcg_filter_enabled;
+ if (state->memcg_filter_enabled && state->memcg_path)
+ strscpy(new_memcg_path, state->memcg_path, PATH_MAX);
while ((token = strsep(&kbuf, " \t\n")) != NULL) {
if (*token == '\0')
@@ -1189,12 +1232,33 @@ static ssize_t page_owner_write(struct file *file,
ret = parse_comm_list(token + 5, new_comm_list, &new_comm_count);
if (ret < 0)
goto out_free;
+ } else if (!strncmp(token, "memcg=", 6)) {
+ if (token[6] == '\0') {
+ ret = -EINVAL;
+ goto out_free;
+ }
+ ret = strscpy(new_memcg_path, token + 6, PATH_MAX);
+ if (ret < 0)
+ goto out_free;
+ new_memcg_filter_enabled = true;
} else {
ret = -EINVAL;
goto out_free;
}
}
+ if (new_memcg_filter_enabled) {
+ if (!state->memcg_path) {
+ state->memcg_path = kzalloc(PATH_MAX, GFP_KERNEL);
+ if (!state->memcg_path) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ } else {
+ memset(state->memcg_path, 0, PATH_MAX);
+ }
+ }
+
/* Commit all filter changes */
state->print_mode = new_print_mode;
state->nid_filter = new_nid_filter;
@@ -1221,11 +1285,16 @@ static ssize_t page_owner_write(struct file *file,
new_comm_count * TASK_COMM_LEN);
state->comm_count = new_comm_count;
}
+ if (new_memcg_filter_enabled) {
+ strscpy(state->memcg_path, new_memcg_path, PATH_MAX);
+ state->memcg_filter_enabled = true;
+ }
ret = count;
out_free:
kfree(new_comm_list);
+ kfree(new_memcg_path);
kfree(orig);
return ret;
}
--
2.20.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 6/8] tools/mm: Add PID/TGID/COMM filtering support to page_owner_filter
2026-08-28 3:13 [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering Zhen Ni
` (4 preceding siblings ...)
2026-08-28 3:13 ` [PATCH 5/8] mm/page_owner: Add memcg " Zhen Ni
@ 2026-08-28 3:13 ` Zhen Ni
2026-08-28 3:13 ` [PATCH 7/8] tools/mm: Add memory cgroup " Zhen Ni
` (2 subsequent siblings)
8 siblings, 0 replies; 16+ messages in thread
From: Zhen Ni @ 2026-08-28 3:13 UTC (permalink / raw)
To: Andrew Morton
Cc: David Hildenbrand, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-doc, linux-kernel,
Zhen Ni
Add command-line options for filtering by process ID (PID), thread group
ID (TGID), and process name (COMM) to the page_owner_filter userspace tool.
New options:
-p, --pid PID_LIST : Process IDs (comma-separated, max 16)
-t, --tgid TGID_LIST : Thread Group IDs (comma-separated, max 16)
-c, --comm COMM_LIST : Process names (comma-separated, max 8)
Supports wildcards: * ? [a-z]
Usage examples:
page_owner_filter -p 1234,5678
page_owner_filter -c "python*"
page_owner_filter -n 0 -c kworker* -o output.txt
Signed-off-by: Zhen Ni <zhen.ni@easystack.cn>
---
tools/mm/page_owner_filter.c | 168 ++++++++++++++++++++++++++++++-----
1 file changed, 147 insertions(+), 21 deletions(-)
diff --git a/tools/mm/page_owner_filter.c b/tools/mm/page_owner_filter.c
index 1d1f0a38678a..9256ffad4939 100644
--- a/tools/mm/page_owner_filter.c
+++ b/tools/mm/page_owner_filter.c
@@ -21,22 +21,24 @@
#include <signal.h>
#define MAX_CMD_LEN 512
+#define TASK_COMM_LEN 16
static void usage(const char *prog)
{
fprintf(stderr, "Usage: %s [OPTIONS]\n", prog);
fprintf(stderr, "\nOptions:\n");
- fprintf(stderr, " -m, --mode MODE : print_mode (stack, handle, or stack_handle)\n");
- fprintf(stderr, " -n, --nid NID_LIST : NUMA node IDs (comma-separated or ranges)\n");
- fprintf(stderr, " -o, --output FILE : output file (default: stdout)\n");
- fprintf(stderr, " -h, --help : show this help message\n");
+ fprintf(stderr, " -m, --mode MODE : print_mode (stack, handle, stack_handle)\n");
+ fprintf(stderr, " -n, --nid NID_LIST : NUMA nodes (comma-separated or ranges)\n");
+ fprintf(stderr, " -p, --pid PID_LIST : Process IDs (comma-separated, max 16)\n");
+ fprintf(stderr, " -t, --tgid TGID_LIST : Thread Group IDs (comma-separated, max 16)\n");
+ fprintf(stderr, " -c, --comm COMM_LIST : Process names (comma-separated, max 8)\n");
+ fprintf(stderr, " Supports wildcards: * ? [a-z]\n");
+ fprintf(stderr, " -o, --output FILE : output file (default: stdout)\n");
+ fprintf(stderr, " -h, --help : show this help message\n");
fprintf(stderr, "\nExamples:\n");
- fprintf(stderr, " %s -m stack\n", prog);
- fprintf(stderr, " %s -m handle\n", prog);
- fprintf(stderr, " %s -m stack_handle\n", prog);
- fprintf(stderr, " %s -m stack -o output.txt\n", prog);
- fprintf(stderr, " %s -n 0,1,2\n", prog);
- fprintf(stderr, " %s -m stack -n 0\n", prog);
+ fprintf(stderr, " %s -m handle -o output.txt\n", prog);
+ fprintf(stderr, " %s -n 0,1 -c bash\n", prog);
+ fprintf(stderr, " %s -c \"python*\" -t 1\n", prog);
}
static int validate_mode(const char *mode)
@@ -132,6 +134,93 @@ static int validate_nid_list(const char *nid_list)
return 0;
}
+static int validate_pid_list(const char *pid_list)
+{
+ const char *p;
+ int count = 0;
+
+ if (!pid_list || strlen(pid_list) == 0)
+ return -1;
+
+ for (p = pid_list; *p; p++) {
+ if (*p == ',') {
+ count++;
+ continue;
+ }
+ if (!isdigit((unsigned char)*p)) {
+ fprintf(stderr,
+ "Error: Invalid character '%c' in pid_list (only digits allowed)\n",
+ *p);
+ return -1;
+ }
+ }
+
+ if (++count > 16) {
+ fprintf(stderr, "Error: Too many PIDs (max 16)\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int validate_tgid_list(const char *tgid_list)
+{
+ return validate_pid_list(tgid_list);
+}
+
+static int validate_comm_list(const char *comm_list)
+{
+ const char *p;
+ const char *comm_start;
+ int count = 0;
+ int comm_len = 0;
+
+ if (!comm_list || strlen(comm_list) == 0)
+ return -1;
+
+ comm_start = comm_list;
+ for (p = comm_list; *p; p++) {
+ if (*p == ',') {
+ /* Check COMM length before separator */
+ if (comm_len == 0) {
+ fprintf(stderr, "Error: Empty COMM in list\n");
+ return -1;
+ }
+ if (comm_len >= TASK_COMM_LEN) {
+ fprintf(stderr,
+ "Error: COMM too long (max %d chars)\n",
+ TASK_COMM_LEN - 1);
+ fprintf(stderr, " Near: %.15s...\n", comm_start);
+ return -1;
+ }
+ count++;
+ comm_len = 0;
+ comm_start = p + 1;
+ continue;
+ }
+ comm_len++;
+ }
+
+ /* Check last COMM */
+ if (comm_len == 0) {
+ fprintf(stderr, "Error: Empty COMM at end of list\n");
+ return -1;
+ }
+ if (comm_len >= TASK_COMM_LEN) {
+ fprintf(stderr, "Error: COMM too long (max %d chars)\n",
+ TASK_COMM_LEN - 1);
+ fprintf(stderr, " Near: %.15s...\n", comm_start);
+ return -1;
+ }
+
+ if (++count > 8) {
+ fprintf(stderr, "Error: Too many COMMs (max 8)\n");
+ return -1;
+ }
+
+ return 0;
+}
+
int main(int argc, char *argv[])
{
const char *output_file = NULL;
@@ -148,6 +237,9 @@ int main(int argc, char *argv[])
static struct option long_options[] = {
{"mode", required_argument, 0, 'm'},
{"nid", required_argument, 0, 'n'},
+ {"pid", required_argument, 0, 'p'},
+ {"tgid", required_argument, 0, 't'},
+ {"comm", required_argument, 0, 'c'},
{"output", required_argument, 0, 'o'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
@@ -174,7 +266,7 @@ int main(int argc, char *argv[])
return 1;
}
- while ((opt = getopt_long(argc, argv, "m:n:o:h", long_options, NULL)) != -1) {
+ while ((opt = getopt_long(argc, argv, "m:n:p:t:c:o:h", long_options, NULL)) != -1) {
int len;
switch (opt) {
@@ -206,6 +298,48 @@ int main(int argc, char *argv[])
cmd_len += len;
break;
}
+ case 'p': {
+ const char *pid_list = optarg;
+
+ if (validate_pid_list(pid_list) < 0)
+ return 1;
+ len = snprintf(filter_cmd + cmd_len, MAX_CMD_LEN - cmd_len,
+ "%spid=%s", cmd_len > 0 ? " " : "", pid_list);
+ if (len < 0 || cmd_len + len >= MAX_CMD_LEN) {
+ fprintf(stderr, "Error: Command too long\n");
+ return 1;
+ }
+ cmd_len += len;
+ break;
+ }
+ case 't': {
+ const char *tgid_list = optarg;
+
+ if (validate_tgid_list(tgid_list) < 0)
+ return 1;
+ len = snprintf(filter_cmd + cmd_len, MAX_CMD_LEN - cmd_len,
+ "%stgid=%s", cmd_len > 0 ? " " : "", tgid_list);
+ if (len < 0 || cmd_len + len >= MAX_CMD_LEN) {
+ fprintf(stderr, "Error: Command too long\n");
+ return 1;
+ }
+ cmd_len += len;
+ break;
+ }
+ case 'c': {
+ const char *comm_list = optarg;
+
+ if (validate_comm_list(comm_list) < 0)
+ return 1;
+ len = snprintf(filter_cmd + cmd_len, MAX_CMD_LEN - cmd_len,
+ "%scomm=%s", cmd_len > 0 ? " " : "", comm_list);
+ if (len < 0 || cmd_len + len >= MAX_CMD_LEN) {
+ fprintf(stderr, "Error: Command too long\n");
+ return 1;
+ }
+ cmd_len += len;
+ break;
+ }
case 'o':
output_file = optarg;
break;
@@ -220,7 +354,7 @@ int main(int argc, char *argv[])
/* At least one filter must be specified */
if (cmd_len == 0) {
- fprintf(stderr, "Error: At least one filter (-m or -n) must be specified\n\n");
+ fprintf(stderr, "Error: At least one filter must be specified\n\n");
usage(argv[0]);
return 1;
}
@@ -255,15 +389,7 @@ int main(int argc, char *argv[])
ret = write(fd, filter_cmd, strlen(filter_cmd));
if (ret < 0) {
- if (errno == EINVAL) {
- fprintf(stderr, "Error: Kernel rejected the filter command.\n");
- fprintf(stderr, "Possible causes:\n");
- fprintf(stderr, " - Kernel does not support per-fd filtering\n");
- fprintf(stderr, " - NUMA node has no memory\n");
- fprintf(stderr, " - Unknown reason\n");
- } else {
- perror("write filter command");
- }
+ perror("write filter command");
goto out;
}
--
2.20.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 7/8] tools/mm: Add memory cgroup filtering support to page_owner_filter
2026-08-28 3:13 [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering Zhen Ni
` (5 preceding siblings ...)
2026-08-28 3:13 ` [PATCH 6/8] tools/mm: Add PID/TGID/COMM filtering support to page_owner_filter Zhen Ni
@ 2026-08-28 3:13 ` Zhen Ni
2026-08-28 3:13 ` [PATCH 8/8] Documentation: page_owner: Document PID/TGID/COMM and cgroup filters Zhen Ni
2026-08-28 7:03 ` [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering Lorenzo Stoakes (ARM)
8 siblings, 0 replies; 16+ messages in thread
From: Zhen Ni @ 2026-08-28 3:13 UTC (permalink / raw)
To: Andrew Morton
Cc: David Hildenbrand, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-doc, linux-kernel,
Zhen Ni
Add filtering capability for page_owner to allow filtering by
memory cgroup path.
Filter page_owner output by cgroup path:
./page_owner_filter -g /
./page_owner_filter -g /user.slice
./page_owner_filter -g /user.slice -c systemd
Signed-off-by: Zhen Ni <zhen.ni@easystack.cn>
---
tools/mm/page_owner_filter.c | 56 ++++++++++++++++++++++++++++++++++--
1 file changed, 53 insertions(+), 3 deletions(-)
diff --git a/tools/mm/page_owner_filter.c b/tools/mm/page_owner_filter.c
index 9256ffad4939..1ffbe4387854 100644
--- a/tools/mm/page_owner_filter.c
+++ b/tools/mm/page_owner_filter.c
@@ -20,7 +20,7 @@
#include <getopt.h>
#include <signal.h>
-#define MAX_CMD_LEN 512
+#define MAX_CMD_LEN 2048
#define TASK_COMM_LEN 16
static void usage(const char *prog)
@@ -33,12 +33,13 @@ static void usage(const char *prog)
fprintf(stderr, " -t, --tgid TGID_LIST : Thread Group IDs (comma-separated, max 16)\n");
fprintf(stderr, " -c, --comm COMM_LIST : Process names (comma-separated, max 8)\n");
fprintf(stderr, " Supports wildcards: * ? [a-z]\n");
+ fprintf(stderr, " -g, --cgroup PATH : Memory cgroup path\n");
fprintf(stderr, " -o, --output FILE : output file (default: stdout)\n");
fprintf(stderr, " -h, --help : show this help message\n");
fprintf(stderr, "\nExamples:\n");
fprintf(stderr, " %s -m handle -o output.txt\n", prog);
fprintf(stderr, " %s -n 0,1 -c bash\n", prog);
- fprintf(stderr, " %s -c \"python*\" -t 1\n", prog);
+ fprintf(stderr, " %s -c \"python*\" -g user.slice\n", prog);
}
static int validate_mode(const char *mode)
@@ -221,6 +222,38 @@ static int validate_comm_list(const char *comm_list)
return 0;
}
+static int validate_cgroup_path(const char *path)
+{
+ char cgroup_path[512];
+ const char *input_path = path;
+ int is_cgroup_v2 = 0;
+
+ if (!path || strlen(path) == 0)
+ return -1;
+
+ if (path[0] == '/')
+ input_path++;
+
+ /* Check if v1 memory controller exists */
+ if (access("/sys/fs/cgroup/memory", F_OK) != 0)
+ is_cgroup_v2 = 1;
+
+ if (is_cgroup_v2)
+ snprintf(cgroup_path, sizeof(cgroup_path),
+ "/sys/fs/cgroup/%s/memory.stat", input_path);
+ else
+ snprintf(cgroup_path, sizeof(cgroup_path),
+ "/sys/fs/cgroup/memory/%s/memory.stat", input_path);
+
+ if (access(cgroup_path, F_OK) != 0) {
+ fprintf(stderr, "Error: Cgroup path '%s': not found or no memory controller\n",
+ path);
+ return -1;
+ }
+
+ return 0;
+}
+
int main(int argc, char *argv[])
{
const char *output_file = NULL;
@@ -240,6 +273,7 @@ int main(int argc, char *argv[])
{"pid", required_argument, 0, 'p'},
{"tgid", required_argument, 0, 't'},
{"comm", required_argument, 0, 'c'},
+ {"cgroup", required_argument, 0, 'g'},
{"output", required_argument, 0, 'o'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
@@ -266,7 +300,7 @@ int main(int argc, char *argv[])
return 1;
}
- while ((opt = getopt_long(argc, argv, "m:n:p:t:c:o:h", long_options, NULL)) != -1) {
+ while ((opt = getopt_long(argc, argv, "m:n:p:t:c:g:o:h", long_options, NULL)) != -1) {
int len;
switch (opt) {
@@ -340,6 +374,22 @@ int main(int argc, char *argv[])
cmd_len += len;
break;
}
+ case 'g': {
+ const char *cgroup_path = optarg;
+
+ if (validate_cgroup_path(cgroup_path) < 0)
+ return 1;
+ const char *path = (cgroup_path[0] == '/') ? cgroup_path + 1 : cgroup_path;
+
+ len = snprintf(filter_cmd + cmd_len, MAX_CMD_LEN - cmd_len,
+ "%smemcg=/%s", cmd_len > 0 ? " " : "", path);
+ if (len < 0 || cmd_len + len >= MAX_CMD_LEN) {
+ fprintf(stderr, "Error: Command too long\n");
+ return 1;
+ }
+ cmd_len += len;
+ break;
+ }
case 'o':
output_file = optarg;
break;
--
2.20.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH 8/8] Documentation: page_owner: Document PID/TGID/COMM and cgroup filters
2026-08-28 3:13 [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering Zhen Ni
` (6 preceding siblings ...)
2026-08-28 3:13 ` [PATCH 7/8] tools/mm: Add memory cgroup " Zhen Ni
@ 2026-08-28 3:13 ` Zhen Ni
2026-08-28 7:03 ` [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering Lorenzo Stoakes (ARM)
8 siblings, 0 replies; 16+ messages in thread
From: Zhen Ni @ 2026-08-28 3:13 UTC (permalink / raw)
To: Andrew Morton
Cc: David Hildenbrand, Lorenzo Stoakes, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-doc, linux-kernel,
Zhen Ni
Update page_owner.rst to document process and cgroup filtering
support in page_owner_filter tool. Add usage examples for PID, TGID,
COMM (with wildcard support), and cgroup filters along with their
respective limits.
Signed-off-by: Zhen Ni <zhen.ni@easystack.cn>
---
Documentation/mm/page_owner.rst | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/Documentation/mm/page_owner.rst b/Documentation/mm/page_owner.rst
index a6bd3fe6423a..c8a7e6c242c5 100644
--- a/Documentation/mm/page_owner.rst
+++ b/Documentation/mm/page_owner.rst
@@ -283,7 +283,7 @@ page_owner supports filtering output at the kernel level before reading,
which reduces the amount of data that needs to be processed in userspace.
The page_owner_filter tool provides a convenient interface for this filtering
-capability. It supports two types of filters:
+capability. It supports the following types of filters:
1. **print_mode filter**: Control what information is printed for each page
- ``stack``: Print full stack traces (default, compatible with existing usage)
@@ -300,6 +300,16 @@ capability. It supports two types of filters:
- Ranges: ``-n 0-3``
- Mixed format: ``-n 0,2-3,5``
+3. **Process filters**: Filter pages by process identifiers
+ - Filter by process ID: ``-p PID_LIST`` (comma-separated, max 16)
+ - Filter by thread group ID: ``-t TGID_LIST`` (comma-separated, max 16)
+ - Filter by task command name: ``-c COMM_LIST`` (comma-separated, max 8)
+ - Name matching supports wildcards: ``*``, ``?``, ``[a-z]``
+
+4. **Cgroup (memcg) filter**: Filter pages by memory cgroup
+ - Filter by cgroup path: ``-g CGROUP``
+ - Useful for containerized environments and multi-tenant systems
+
Usage examples::
# Filter by print mode
@@ -310,9 +320,19 @@ Usage examples::
./page_owner_filter -n 0
./page_owner_filter -n 0-3
+ # Filter by process
+ ./page_owner_filter -p 1234
+ ./page_owner_filter -t 1,2,3
+ ./page_owner_filter -c python*
+
+ # Filter by cgroup
+ ./page_owner_filter -g system.slice
+ ./page_owner_filter -g kubepods/besteffort/pod123
+
# Combined filters
./page_owner_filter -m stack -n 0,1,2
./page_owner_filter -m handle -n 0,2-3
+ ./page_owner_filter -g user.slice -c bash -n 0
# Save to file
./page_owner_filter -m handle -o filtered_output.txt
@@ -323,6 +343,9 @@ reduce output size by ~66% (84MB vs 244MB) and improve read performance by ~4.4x
compared to full stack output.
The NUMA node filter is useful for NUMA-aware memory allocation analysis and debugging.
+Process filters help isolate memory allocations for specific processes or tasks.
+The cgroup filter is essential for containerized environments where you need to
+analyze memory usage per container or service.
Behind the scenes, page_owner_filter opens /sys/kernel/debug/page_owner and
writes filter commands before reading the filtered output. The filtering uses
--
2.20.1
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering
2026-08-28 3:13 [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering Zhen Ni
` (7 preceding siblings ...)
2026-08-28 3:13 ` [PATCH 8/8] Documentation: page_owner: Document PID/TGID/COMM and cgroup filters Zhen Ni
@ 2026-08-28 7:03 ` Lorenzo Stoakes (ARM)
2026-08-28 18:36 ` Andrew Morton
8 siblings, 1 reply; 16+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-08-28 7:03 UTC (permalink / raw)
To: Zhen Ni
Cc: Andrew Morton, David Hildenbrand, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-doc, linux-kernel
On Fri, Aug 28, 2026 at 11:13:31AM +0800, Zhen Ni wrote:
> This patch series adds process and memory cgroup filtering support to
> page_owner. Following the previous series that introduced print_mode and
> NUMA node filters:
> https://lore.kernel.org/linux-mm/20260707115411.1714314-1-zhen.ni@easystack.cn/
>
> This series adds filtering capabilities to page_owner, allowing users to
> filter output by specific processes and memory cgroups. Users can now
> filter page_owner output by PID, TGID, COMM (with wildcard support), and
> memory cgroup path. This makes page_owner debugging more focused and
> efficient for tracking memory allocations in specific contexts.
The majority of this cover letter feels like it should have been
documentation put somewhere :)
I mean I like to be verbose, but this is slightly excessive :P
Remember that mm places cover letters in the first commit in merged
series...
Andrew - could we not do that here, or at least heavily truncate it?
[...]
> Test: Cgroup path exceeding PATH_MAX
> Generated path length: 4101
> ./page_owner_filter -g '<very long path>'
> Error: Cgroup path '/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa': not found or no memory controller
Also let's maybe refrain from >4000 char 'aaa...[rgh]'s, I think people get
the picture without needing all the chars :)
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering
2026-08-28 7:03 ` [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering Lorenzo Stoakes (ARM)
@ 2026-08-28 18:36 ` Andrew Morton
2026-09-01 6:34 ` zhen.ni
0 siblings, 1 reply; 16+ messages in thread
From: Andrew Morton @ 2026-08-28 18:36 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Zhen Ni, David Hildenbrand, Liam R . Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
Shuah Khan, Randy Dunlap, Brendan Jackman, Johannes Weiner,
Zi Yan, linux-mm, linux-doc, linux-kernel
On Fri, 28 Aug 2026 08:03:38 +0100 "Lorenzo Stoakes (ARM)" <ljs@kernel.org> wrote:
> On Fri, Aug 28, 2026 at 11:13:31AM +0800, Zhen Ni wrote:
> > This patch series adds process and memory cgroup filtering support to
> > page_owner. Following the previous series that introduced print_mode and
> > NUMA node filters:
> > https://lore.kernel.org/linux-mm/20260707115411.1714314-1-zhen.ni@easystack.cn/
> >
> > This series adds filtering capabilities to page_owner, allowing users to
> > filter output by specific processes and memory cgroups. Users can now
> > filter page_owner output by PID, TGID, COMM (with wildcard support), and
> > memory cgroup path. This makes page_owner debugging more focused and
> > efficient for tracking memory allocations in specific contexts.
>
> The majority of this cover letter feels like it should have been
> documentation put somewhere :)
yes please.
The only thing longer than the cover letter is the Sashiko report ;)
https://sashiko.dev/#/patchset/20260828031339.1270699-1-zhen.ni@easystack.cn
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering
2026-08-28 18:36 ` Andrew Morton
@ 2026-09-01 6:34 ` zhen.ni
2026-09-01 7:27 ` Lorenzo Stoakes (ARM)
0 siblings, 1 reply; 16+ messages in thread
From: zhen.ni @ 2026-09-01 6:34 UTC (permalink / raw)
To: Andrew Morton, Lorenzo Stoakes (ARM)
Cc: David Hildenbrand, Liam R . Howlett, Vlastimil Babka,
Mike Rapoport, Suren Baghdasaryan, Michal Hocko, Jonathan Corbet,
Shuah Khan, Randy Dunlap, Brendan Jackman, Johannes Weiner,
Zi Yan, linux-mm, linux-doc, linux-kernel
在 2026/8/29 02:36, Andrew Morton 写道:
> On Fri, 28 Aug 2026 08:03:38 +0100 "Lorenzo Stoakes (ARM)" <ljs@kernel.org> wrote:
>
>> On Fri, Aug 28, 2026 at 11:13:31AM +0800, Zhen Ni wrote:
>>> This patch series adds process and memory cgroup filtering support to
>>> page_owner. Following the previous series that introduced print_mode and
>>> NUMA node filters:
>>> https://lore.kernel.org/linux-mm/20260707115411.1714314-1-zhen.ni@easystack.cn/
>>>
>>> This series adds filtering capabilities to page_owner, allowing users to
>>> filter output by specific processes and memory cgroups. Users can now
>>> filter page_owner output by PID, TGID, COMM (with wildcard support), and
>>> memory cgroup path. This makes page_owner debugging more focused and
>>> efficient for tracking memory allocations in specific contexts.
>>
>> The majority of this cover letter feels like it should have been
>> documentation put somewhere :)
>
> yes please.
>
> The only thing longer than the cover letter is the Sashiko report ;)
>
> https://sashiko.dev/#/patchset/20260828031339.1270699-1-zhen.ni@easystack.cn
>
>
Hi Andrew, Lorenzo,
Thanks for the review.
I have analyzed all the Sashiko report findings. Some will be fixed in
the next version, and for the rest I propose not to fix them, with
reasons below. If there are no objections I will send v2 accordingly.
Will be fixed in the next version:
- mm/page_owner.c: drop the kstrdup() copy in parse_pid_t_list(); the
token will be parsed in place. This also fixes a leak on the success
path and a kfree() of an advanced (interior) pointer on the error
path. cmp_int() will replace plain subtraction in cmp_pid_t(), and
pid values exceeding PID_MAX_LIMIT will be rejected.
- mm/page_owner.c: PAGE_OWNER will select GLOB so glob_match() is
always linked in; parse_comm_list() will drop its kstrdup() copy the
same way.
- mm/page_owner.c: the cgroup path buffer will be allocated once per
read() outside the page_ext RCU read-side critical section instead of
per page inside get_page_memcg_info() (GFP_KERNEL allocations must
not sleep there). The memcg= parsing branch will be guarded by
CONFIG_MEMCG so kernels built without memcg reject the command
instead of silently enabling a filter that never matches.
- tools/mm/page_owner_filter.c: user-visible input errors (empty
-p/-t/-c/-g arguments) will print error messages instead of exiting
silently.
- Documentation: the wildcard pattern in the -c example will be quoted
to prevent shell glob expansion.
Proposed not to fix, by design:
1. Shared-fd concurrent read/write races (READ_ONCE around the pid
passed to bsearch, torn reads of pid/tgid lists, glob_match() racing
comm rewrites, concurrent write() leading to state->memcg_path
double-allocation): multi-threaded sharing of one page_owner fd is
not a designed use of this interface. The filters are per-fd state
meant to be configured once and then read, which is what the
page_owner_filter tool does. Adding locking to the read path would
put overhead into the per-page scan loop for no designed benefit.
This matches the semantics of the original filter introduction.
2. No "clear filter" support (empty pid=/tgid= lists partially clearing
proc filters, empty memcg=/nid= being rejected): write commands are
incremental -- "keep the unmentioned filters" -- and there is no
clear operation by design. To start over, close the fd and open a
fresh one; the page_owner_filter tool already works this way. This
also matches the semantics of the original filter introduction.
3. kcalloc() vs kmalloc_array() for new_comm_list: no consumer of the
list reads past strscpy()'s NUL terminator, so uninitialized bytes
are unreachable.
4. char cgroup_path[512] in validate_cgroup_path(): acknowledged that
the kernel side accepts paths up to PATH_MAX (4096). The userspace
check truncates an over-long path with snprintf() and then fails
access(), so it is rejected, never silently accepted. Bumping the
buffer to PATH_MAX would only serve pathological paths; realistic
cgroup paths are well under 100 bytes, so 512 wastes nothing in
practice.
If this plan looks reasonable I will send v2.
Thanks,
Zhen Ni
^ permalink raw reply [flat|nested] 16+ messages in thread* Re: [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering
2026-09-01 6:34 ` zhen.ni
@ 2026-09-01 7:27 ` Lorenzo Stoakes (ARM)
2026-09-01 8:01 ` zhen.ni
0 siblings, 1 reply; 16+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-01 7:27 UTC (permalink / raw)
To: zhen.ni
Cc: Andrew Morton, David Hildenbrand, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-doc, linux-kernel
On Tue, Sep 01, 2026 at 02:34:34PM +0800, zhen.ni wrote:
>
>
> 在 2026/8/29 02:36, Andrew Morton 写道:
> > On Fri, 28 Aug 2026 08:03:38 +0100 "Lorenzo Stoakes (ARM)" <ljs@kernel.org> wrote:
> >
> > > On Fri, Aug 28, 2026 at 11:13:31AM +0800, Zhen Ni wrote:
> > > > This patch series adds process and memory cgroup filtering support to
> > > > page_owner. Following the previous series that introduced print_mode and
> > > > NUMA node filters:
> > > > https://lore.kernel.org/linux-mm/20260707115411.1714314-1-zhen.ni@easystack.cn/
> > > >
> > > > This series adds filtering capabilities to page_owner, allowing users to
> > > > filter output by specific processes and memory cgroups. Users can now
> > > > filter page_owner output by PID, TGID, COMM (with wildcard support), and
> > > > memory cgroup path. This makes page_owner debugging more focused and
> > > > efficient for tracking memory allocations in specific contexts.
> > >
> > > The majority of this cover letter feels like it should have been
> > > documentation put somewhere :)
> >
> > yes please.
> >
> > The only thing longer than the cover letter is the Sashiko report ;)
> >
> > https://sashiko.dev/#/patchset/20260828031339.1270699-1-zhen.ni@easystack.cn
> >
> >
>
> Hi Andrew, Lorenzo,
>
> Thanks for the review.
>
> I have analyzed all the Sashiko report findings. Some will be fixed in
> the next version, and for the rest I propose not to fix them, with
> reasons below. If there are no objections I will send v2 accordingly.
>
> Will be fixed in the next version:
>
> - mm/page_owner.c: drop the kstrdup() copy in parse_pid_t_list(); the
> token will be parsed in place. This also fixes a leak on the success
> path and a kfree() of an advanced (interior) pointer on the error
> path. cmp_int() will replace plain subtraction in cmp_pid_t(), and
> pid values exceeding PID_MAX_LIMIT will be rejected.
> - mm/page_owner.c: PAGE_OWNER will select GLOB so glob_match() is
> always linked in; parse_comm_list() will drop its kstrdup() copy the
> same way.
> - mm/page_owner.c: the cgroup path buffer will be allocated once per
> read() outside the page_ext RCU read-side critical section instead of
> per page inside get_page_memcg_info() (GFP_KERNEL allocations must
> not sleep there). The memcg= parsing branch will be guarded by
> CONFIG_MEMCG so kernels built without memcg reject the command
> instead of silently enabling a filter that never matches.
> - tools/mm/page_owner_filter.c: user-visible input errors (empty
> -p/-t/-c/-g arguments) will print error messages instead of exiting
> silently.
> - Documentation: the wildcard pattern in the -c example will be quoted
> to prevent shell glob expansion.
>
> Proposed not to fix, by design:
>
> 1. Shared-fd concurrent read/write races (READ_ONCE around the pid
> passed to bsearch, torn reads of pid/tgid lists, glob_match() racing
> comm rewrites, concurrent write() leading to state->memcg_path
> double-allocation): multi-threaded sharing of one page_owner fd is
> not a designed use of this interface. The filters are per-fd state
> meant to be configured once and then read, which is what the
> page_owner_filter tool does. Adding locking to the read path would
> put overhead into the per-page scan loop for no designed benefit.
> This matches the semantics of the original filter introduction.
>
> 2. No "clear filter" support (empty pid=/tgid= lists partially clearing
> proc filters, empty memcg=/nid= being rejected): write commands are
> incremental -- "keep the unmentioned filters" -- and there is no
> clear operation by design. To start over, close the fd and open a
> fresh one; the page_owner_filter tool already works this way. This
> also matches the semantics of the original filter introduction.
>
> 3. kcalloc() vs kmalloc_array() for new_comm_list: no consumer of the
> list reads past strscpy()'s NUL terminator, so uninitialized bytes
> are unreachable.
>
> 4. char cgroup_path[512] in validate_cgroup_path(): acknowledged that
> the kernel side accepts paths up to PATH_MAX (4096). The userspace
> check truncates an over-long path with snprintf() and then fails
> access(), so it is rejected, never silently accepted. Bumping the
> buffer to PATH_MAX would only serve pathological paths; realistic
> cgroup paths are well under 100 bytes, so 512 wastes nothing in
> practice.
>
> If this plan looks reasonable I will send v2.
Sorry but this kind of 'summary', 'do you agree with the plan' email is not
acceptable.
You have your feedback, reply to people like a human being directly to them,
thank you very much.
At this point, based on past experience, I have to ask if you're using an LLM?
If so please disclose this as per kernel guidelines:
https://docs.kernel.org/process/coding-assistants.html
https://docs.kernel.org/process/generated-content.html
>
> Thanks,
> Zhen Ni
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering
2026-09-01 7:27 ` Lorenzo Stoakes (ARM)
@ 2026-09-01 8:01 ` zhen.ni
2026-09-01 8:10 ` Lorenzo Stoakes (ARM)
0 siblings, 1 reply; 16+ messages in thread
From: zhen.ni @ 2026-09-01 8:01 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Andrew Morton, David Hildenbrand, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-doc, linux-kernel
在 2026/9/1 15:27, Lorenzo Stoakes (ARM) 写道:
> On Tue, Sep 01, 2026 at 02:34:34PM +0800, zhen.ni wrote:
>>
>>
>> 在 2026/8/29 02:36, Andrew Morton 写道:
>>> On Fri, 28 Aug 2026 08:03:38 +0100 "Lorenzo Stoakes (ARM)" <ljs@kernel.org> wrote:
>>>
>>>> On Fri, Aug 28, 2026 at 11:13:31AM +0800, Zhen Ni wrote:
>>>>> This patch series adds process and memory cgroup filtering support to
>>>>> page_owner. Following the previous series that introduced print_mode and
>>>>> NUMA node filters:
>>>>> https://lore.kernel.org/linux-mm/20260707115411.1714314-1-zhen.ni@easystack.cn/
>>>>>
>>>>> This series adds filtering capabilities to page_owner, allowing users to
>>>>> filter output by specific processes and memory cgroups. Users can now
>>>>> filter page_owner output by PID, TGID, COMM (with wildcard support), and
>>>>> memory cgroup path. This makes page_owner debugging more focused and
>>>>> efficient for tracking memory allocations in specific contexts.
>>>>
>>>> The majority of this cover letter feels like it should have been
>>>> documentation put somewhere :)
>>>
>>> yes please.
>>>
>>> The only thing longer than the cover letter is the Sashiko report ;)
>>>
>>> https://sashiko.dev/#/patchset/20260828031339.1270699-1-zhen.ni@easystack.cn
>>>
>>>
>>
>> Hi Andrew, Lorenzo,
>>
>> Thanks for the review.
>>
>> I have analyzed all the Sashiko report findings. Some will be fixed in
>> the next version, and for the rest I propose not to fix them, with
>> reasons below. If there are no objections I will send v2 accordingly.
>>
>> Will be fixed in the next version:
>>
>> - mm/page_owner.c: drop the kstrdup() copy in parse_pid_t_list(); the
>> token will be parsed in place. This also fixes a leak on the success
>> path and a kfree() of an advanced (interior) pointer on the error
>> path. cmp_int() will replace plain subtraction in cmp_pid_t(), and
>> pid values exceeding PID_MAX_LIMIT will be rejected.
>> - mm/page_owner.c: PAGE_OWNER will select GLOB so glob_match() is
>> always linked in; parse_comm_list() will drop its kstrdup() copy the
>> same way.
>> - mm/page_owner.c: the cgroup path buffer will be allocated once per
>> read() outside the page_ext RCU read-side critical section instead of
>> per page inside get_page_memcg_info() (GFP_KERNEL allocations must
>> not sleep there). The memcg= parsing branch will be guarded by
>> CONFIG_MEMCG so kernels built without memcg reject the command
>> instead of silently enabling a filter that never matches.
>> - tools/mm/page_owner_filter.c: user-visible input errors (empty
>> -p/-t/-c/-g arguments) will print error messages instead of exiting
>> silently.
>> - Documentation: the wildcard pattern in the -c example will be quoted
>> to prevent shell glob expansion.
>>
>> Proposed not to fix, by design:
>>
>> 1. Shared-fd concurrent read/write races (READ_ONCE around the pid
>> passed to bsearch, torn reads of pid/tgid lists, glob_match() racing
>> comm rewrites, concurrent write() leading to state->memcg_path
>> double-allocation): multi-threaded sharing of one page_owner fd is
>> not a designed use of this interface. The filters are per-fd state
>> meant to be configured once and then read, which is what the
>> page_owner_filter tool does. Adding locking to the read path would
>> put overhead into the per-page scan loop for no designed benefit.
>> This matches the semantics of the original filter introduction.
>>
>> 2. No "clear filter" support (empty pid=/tgid= lists partially clearing
>> proc filters, empty memcg=/nid= being rejected): write commands are
>> incremental -- "keep the unmentioned filters" -- and there is no
>> clear operation by design. To start over, close the fd and open a
>> fresh one; the page_owner_filter tool already works this way. This
>> also matches the semantics of the original filter introduction.
>>
>> 3. kcalloc() vs kmalloc_array() for new_comm_list: no consumer of the
>> list reads past strscpy()'s NUL terminator, so uninitialized bytes
>> are unreachable.
>>
>> 4. char cgroup_path[512] in validate_cgroup_path(): acknowledged that
>> the kernel side accepts paths up to PATH_MAX (4096). The userspace
>> check truncates an over-long path with snprintf() and then fails
>> access(), so it is rejected, never silently accepted. Bumping the
>> buffer to PATH_MAX would only serve pathological paths; realistic
>> cgroup paths are well under 100 bytes, so 512 wastes nothing in
>> practice.
>>
>> If this plan looks reasonable I will send v2.
>
> Sorry but this kind of 'summary', 'do you agree with the plan' email is not
> acceptable.
>
> You have your feedback, reply to people like a human being directly to them,
> thank you very much.
>
> At this point, based on past experience, I have to ask if you're using an LLM?
> If so please disclose this as per kernel guidelines:
>
> https://docs.kernel.org/process/coding-assistants.html
> https://docs.kernel.org/process/generated-content.html
>
>>
>> Thanks,
>> Zhen Ni
>
> --
> Cheers, Lorenzo
>
>
Hi, Lorenzo
I honestly don't understand what is wrong here. I spent two full days
going through every single finding in the Sashiko report one by one,
checking each against the code. In fact I am already working on v2 and
testing the corresponding changes. What I don't understand is what
"this kind of 'summary' email is not acceptable" is supposed to mean.
If you disagree with any specific item, name it and we can discuss it
-- but rejecting the whole thing outright, with just two documentation
links and no specifics, is not something I can act on.
Thanks,
Zhen
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering
2026-09-01 8:01 ` zhen.ni
@ 2026-09-01 8:10 ` Lorenzo Stoakes (ARM)
2026-09-01 9:29 ` zhen.ni
0 siblings, 1 reply; 16+ messages in thread
From: Lorenzo Stoakes (ARM) @ 2026-09-01 8:10 UTC (permalink / raw)
To: zhen.ni
Cc: Andrew Morton, David Hildenbrand, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-doc, linux-kernel
On Tue, Sep 01, 2026 at 04:01:20PM +0800, zhen.ni wrote:
>
>
> 在 2026/9/1 15:27, Lorenzo Stoakes (ARM) 写道:
> > On Tue, Sep 01, 2026 at 02:34:34PM +0800, zhen.ni wrote:
> > >
> > >
> > > 在 2026/8/29 02:36, Andrew Morton 写道:
> > > > On Fri, 28 Aug 2026 08:03:38 +0100 "Lorenzo Stoakes (ARM)" <ljs@kernel.org> wrote:
> > > >
> > > > > On Fri, Aug 28, 2026 at 11:13:31AM +0800, Zhen Ni wrote:
> > > > > > This patch series adds process and memory cgroup filtering support to
> > > > > > page_owner. Following the previous series that introduced print_mode and
> > > > > > NUMA node filters:
> > > > > > https://lore.kernel.org/linux-mm/20260707115411.1714314-1-zhen.ni@easystack.cn/
> > > > > >
> > > > > > This series adds filtering capabilities to page_owner, allowing users to
> > > > > > filter output by specific processes and memory cgroups. Users can now
> > > > > > filter page_owner output by PID, TGID, COMM (with wildcard support), and
> > > > > > memory cgroup path. This makes page_owner debugging more focused and
> > > > > > efficient for tracking memory allocations in specific contexts.
> > > > >
> > > > > The majority of this cover letter feels like it should have been
> > > > > documentation put somewhere :)
> > > >
> > > > yes please.
> > > >
> > > > The only thing longer than the cover letter is the Sashiko report ;)
> > > >
> > > > https://sashiko.dev/#/patchset/20260828031339.1270699-1-zhen.ni@easystack.cn
> > > >
> > > >
> > >
> > > Hi Andrew, Lorenzo,
> > >
> > > Thanks for the review.
> > >
> > > I have analyzed all the Sashiko report findings. Some will be fixed in
> > > the next version, and for the rest I propose not to fix them, with
> > > reasons below. If there are no objections I will send v2 accordingly.
> > >
> > > Will be fixed in the next version:
> > >
> > > - mm/page_owner.c: drop the kstrdup() copy in parse_pid_t_list(); the
> > > token will be parsed in place. This also fixes a leak on the success
> > > path and a kfree() of an advanced (interior) pointer on the error
> > > path. cmp_int() will replace plain subtraction in cmp_pid_t(), and
> > > pid values exceeding PID_MAX_LIMIT will be rejected.
> > > - mm/page_owner.c: PAGE_OWNER will select GLOB so glob_match() is
> > > always linked in; parse_comm_list() will drop its kstrdup() copy the
> > > same way.
> > > - mm/page_owner.c: the cgroup path buffer will be allocated once per
> > > read() outside the page_ext RCU read-side critical section instead of
> > > per page inside get_page_memcg_info() (GFP_KERNEL allocations must
> > > not sleep there). The memcg= parsing branch will be guarded by
> > > CONFIG_MEMCG so kernels built without memcg reject the command
> > > instead of silently enabling a filter that never matches.
> > > - tools/mm/page_owner_filter.c: user-visible input errors (empty
> > > -p/-t/-c/-g arguments) will print error messages instead of exiting
> > > silently.
> > > - Documentation: the wildcard pattern in the -c example will be quoted
> > > to prevent shell glob expansion.
> > >
> > > Proposed not to fix, by design:
> > >
> > > 1. Shared-fd concurrent read/write races (READ_ONCE around the pid
> > > passed to bsearch, torn reads of pid/tgid lists, glob_match() racing
> > > comm rewrites, concurrent write() leading to state->memcg_path
> > > double-allocation): multi-threaded sharing of one page_owner fd is
> > > not a designed use of this interface. The filters are per-fd state
> > > meant to be configured once and then read, which is what the
> > > page_owner_filter tool does. Adding locking to the read path would
> > > put overhead into the per-page scan loop for no designed benefit.
> > > This matches the semantics of the original filter introduction.
> > >
> > > 2. No "clear filter" support (empty pid=/tgid= lists partially clearing
> > > proc filters, empty memcg=/nid= being rejected): write commands are
> > > incremental -- "keep the unmentioned filters" -- and there is no
> > > clear operation by design. To start over, close the fd and open a
> > > fresh one; the page_owner_filter tool already works this way. This
> > > also matches the semantics of the original filter introduction.
> > >
> > > 3. kcalloc() vs kmalloc_array() for new_comm_list: no consumer of the
> > > list reads past strscpy()'s NUL terminator, so uninitialized bytes
> > > are unreachable.
> > >
> > > 4. char cgroup_path[512] in validate_cgroup_path(): acknowledged that
> > > the kernel side accepts paths up to PATH_MAX (4096). The userspace
> > > check truncates an over-long path with snprintf() and then fails
> > > access(), so it is rejected, never silently accepted. Bumping the
> > > buffer to PATH_MAX would only serve pathological paths; realistic
> > > cgroup paths are well under 100 bytes, so 512 wastes nothing in
> > > practice.
> > >
> > > If this plan looks reasonable I will send v2.
> >
> > Sorry but this kind of 'summary', 'do you agree with the plan' email is not
> > acceptable.
> >
> > You have your feedback, reply to people like a human being directly to them,
> > thank you very much.
> >
> > At this point, based on past experience, I have to ask if you're using an LLM?
> > If so please disclose this as per kernel guidelines:
> >
> > https://docs.kernel.org/process/coding-assistants.html
> > https://docs.kernel.org/process/generated-content.html
> >
> > >
> > > Thanks,
> > > Zhen Ni
> >
> > --
> > Cheers, Lorenzo
> >
> >
> Hi, Lorenzo
>
> I honestly don't understand what is wrong here. I spent two full days
> going through every single finding in the Sashiko report one by one,
> checking each against the code. In fact I am already working on v2 and
> testing the corresponding changes. What I don't understand is what
> "this kind of 'summary' email is not acceptable" is supposed to mean.
>
> If you disagree with any specific item, name it and we can discuss it
> -- but rejecting the whole thing outright, with just two documentation
> links and no specifics, is not something I can act on.
Again you're failing to reply to kernel email in the usual style, and it's
on you to figure out how to do that, not me.
Reply, inline, to what people have said to you.
Do NOT ask them to read through your 'plan' document and give yet more of
their time to compensate for you not following basic kernel procedure.
I have done hundreds (>1,000?) hrs of review upstream and I have only seen
these kinds of 'summary - plan' emails since 2026.
I am only asking you to engage upstream as everybody else does.
Since you ignored it, I ask you again - have you used an LLM here? If so
follow kernel procedure as per the documentation I linked.
>
> Thanks,
> Zhen
--
Cheers, Lorenzo
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 0/8] mm/page_owner: Add PID/TGID/COMM and cgroup filtering
2026-09-01 8:10 ` Lorenzo Stoakes (ARM)
@ 2026-09-01 9:29 ` zhen.ni
0 siblings, 0 replies; 16+ messages in thread
From: zhen.ni @ 2026-09-01 9:29 UTC (permalink / raw)
To: Lorenzo Stoakes (ARM)
Cc: Andrew Morton, David Hildenbrand, Liam R . Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Jonathan Corbet, Shuah Khan, Randy Dunlap, Brendan Jackman,
Johannes Weiner, Zi Yan, linux-mm, linux-doc, linux-kernel
在 2026/9/1 16:10, Lorenzo Stoakes (ARM) 写道:
> Again you're failing to reply to kernel email in the usual style, and it's
> on you to figure out how to do that, not me.
>
> Reply, inline, to what people have said to you.
>
Understood - my previous mail was indeed a plan document rather than
inline replies.
Sorry for the noise.
> Do NOT ask them to read through your 'plan' document and give yet more of
> their time to compensate for you not following basic kernel procedure.
>
> I have done hundreds (>1,000?) hrs of review upstream and I have only seen
> these kinds of 'summary - plan' emails since 2026.
>
The timing has a simpler cause: Sashiko itself only started reviewing
patches in 2026.
I would normally prefer inline replies to review, but Sashiko did not
send any mail into this thread, so there is nothing to reply to
directly. Its findings are also heavily duplicated across patches, so
a grouped summary is the more appropriate form here.
> I am only asking you to engage upstream as everybody else does.
>
> Since you ignored it, I ask you again - have you used an LLM here? If so
> follow kernel procedure as per the documentation I linked.
No. Every patch hunk and every fix/no-fix decision is my own.
> --
> Cheers, Lorenzo
Thanks,
Zhen
^ permalink raw reply [flat|nested] 16+ messages in thread