mirror of
https://github.com/torvalds/linux.git
synced 2026-10-07 19:16:02 +02:00
mm/page_owner: add NUMA node filter
Add NUMA node filtering functionality to page_owner to allow filtering pages by specific NUMA node(s). This is useful for NUMA-aware memory allocation analysis and debugging. The filter supports flexible input formats: - Single node: nid=0 - Multiple nodes: nid=0,2,3 - Node range: nid=0-3 - Mixed format: nid=0,2-4,7 Example usage: # Using the page_owner_filter tool (recommended) ./page_owner_filter -n 0-3 ./page_owner_filter -m stack_handle -n 0,2-4,7 The implementation uses per-file-descriptor filter state stored in file->private_data, allowing each opener to have independent filter configuration. It uses nodemask_t for efficient multi-node filtering and nodelist_parse() for flexible input parsing. Node validity is verified using nodes_subset() to reject nodes without memory. Link: https://lore.kernel.org/20260707115411.1714314-3-zhen.ni@easystack.cn Signed-off-by: Zhen Ni <zhen.ni@easystack.cn> Tested-by: Zi Yan <ziy@nvidia.com> Acked-by: Zi Yan <ziy@nvidia.com> Reviewed-by: Vlastimil Babka (SUSE) <vbabka@kernel.org> Cc: Brendan Jackman <jackmanb@google.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Michal Hocko <mhocko@suse.com> Cc: Suren Baghdasaryan <surenb@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
parent
8570b65220
commit
bed8003612
|
|
@ -68,6 +68,8 @@ static const char * const page_owner_print_mode_strings[] = {
|
|||
|
||||
struct page_owner_filter_state {
|
||||
enum page_owner_print_mode print_mode;
|
||||
nodemask_t nid_filter;
|
||||
bool nid_filter_enabled;
|
||||
};
|
||||
|
||||
static bool page_owner_enabled __initdata;
|
||||
|
|
@ -803,6 +805,21 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
|
|||
if (!handle)
|
||||
goto ext_put_continue;
|
||||
|
||||
if (state->nid_filter_enabled) {
|
||||
int nid;
|
||||
memdesc_flags_t page_flags = READ_ONCE(page->flags);
|
||||
|
||||
/*
|
||||
* Bypass PF_POISONED_CHECK() in page_to_nid() to avoid
|
||||
* VM_BUG_ON when accessing poisoned pages.
|
||||
*/
|
||||
if (page_flags.f == PAGE_POISON_PATTERN)
|
||||
goto ext_put_continue;
|
||||
nid = memdesc_nid(page_flags);
|
||||
if (!node_isset(nid, state->nid_filter))
|
||||
goto ext_put_continue;
|
||||
}
|
||||
|
||||
/* Record the next PFN to read in the file offset */
|
||||
*ppos = pfn + 1;
|
||||
|
||||
|
|
@ -812,6 +829,7 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
|
|||
&page_owner_tmp, handle, state);
|
||||
ext_put_continue:
|
||||
page_ext_put(page_ext);
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -907,6 +925,8 @@ static int page_owner_open(struct inode *inode, struct file *file)
|
|||
return -ENOMEM;
|
||||
|
||||
state->print_mode = PAGE_OWNER_PRINT_STACK;
|
||||
nodes_clear(state->nid_filter);
|
||||
state->nid_filter_enabled = false;
|
||||
file->private_data = state;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -927,12 +947,17 @@ static ssize_t page_owner_write(struct file *file,
|
|||
int ret;
|
||||
struct page_owner_filter_state *state = file->private_data;
|
||||
enum page_owner_print_mode new_print_mode;
|
||||
nodemask_t new_nid_filter;
|
||||
bool new_nid_filter_enabled;
|
||||
|
||||
/*
|
||||
* Maximum input length for filter commands:
|
||||
* 32: print_mode command max length is 17 ("mode=stack_handle").
|
||||
* - 32: print_mode command max length is 17 ("mode=stack_handle")
|
||||
* with sufficient buffer
|
||||
* - 6 * MAX_NUMNODES: worst case for nid list
|
||||
* Worst case per node: ",NNNNN" (comma + 5-digit node number) = 6 bytes
|
||||
*/
|
||||
if (count > 32)
|
||||
if (count > 32 + 6 * MAX_NUMNODES)
|
||||
return -EINVAL;
|
||||
|
||||
kbuf = memdup_user_nul(buf, count);
|
||||
|
|
@ -942,6 +967,8 @@ static ssize_t page_owner_write(struct file *file,
|
|||
orig = kbuf;
|
||||
|
||||
new_print_mode = state->print_mode;
|
||||
new_nid_filter = state->nid_filter;
|
||||
new_nid_filter_enabled = state->nid_filter_enabled;
|
||||
|
||||
while ((token = strsep(&kbuf, " \t\n")) != NULL) {
|
||||
if (*token == '\0')
|
||||
|
|
@ -953,13 +980,36 @@ static ssize_t page_owner_write(struct file *file,
|
|||
if (ret < 0)
|
||||
goto out_free;
|
||||
new_print_mode = ret;
|
||||
} else if (!strncmp(token, "nid=", 4)) {
|
||||
ret = nodelist_parse(token + 4, new_nid_filter);
|
||||
if (ret < 0)
|
||||
goto out_free;
|
||||
|
||||
if (nodes_empty(new_nid_filter)) {
|
||||
ret = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/*
|
||||
* We want to filter memory allocations by numa nodes, so make sure
|
||||
* that the specified nodes have memory.
|
||||
*/
|
||||
if (!nodes_subset(new_nid_filter, node_states[N_MEMORY])) {
|
||||
ret = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
new_nid_filter_enabled = true;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
|
||||
/* Commit all filter changes */
|
||||
state->print_mode = new_print_mode;
|
||||
state->nid_filter = new_nid_filter;
|
||||
state->nid_filter_enabled = new_nid_filter_enabled;
|
||||
|
||||
ret = count;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user