๏ปฟ# Page Owner โ€” ํŽ˜์ด์ง€ ํ• ๋‹น ์ถ”์ 

๊ด€๋ จ ์†Œ์Šค: mm/page_owner.c, include/linux/page_owner.h, include/linux/page_ext.h, tools/mm/page_owner_sort.c

๊ฐœ์š” (Overview)

Page Owner๋Š” ์ปค๋„์˜ ํŽ˜์ด์ง€ ํ• ๋‹น์ž๋ฅผ ์ถ”์ ํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋‚˜ ๋ฉ”๋ชจ๋ฆฌ ๊ณผ๋‹ค ์‚ฌ์šฉ ์›์ธ์„ ์ง„๋‹จํ•˜๋Š” ๋””๋ฒ„๊ทธ ์„œ๋ธŒ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค. ๊ฐ ํŽ˜์ด์ง€๊ฐ€ ํ• ๋‹น๋  ๋•Œ ํ˜ธ์ถœ ์Šคํƒ, GFP ํ”Œ๋ž˜๊ทธ, ํ”„๋กœ์„ธ์Šค ID ๋“ฑ์˜ ์ •๋ณด๋ฅผ page_ext ํ™•์žฅ ๋ฉ”๋ชจ๋ฆฌ์— ๊ธฐ๋กํ•˜๊ณ , ํ•ด์ œ ์‹œ ํ•ด์ œ ์Šคํƒ๋„ ํ•จ๊ป˜ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด cat /sys/kernel/debug/page_owner๋กœ ํ˜„์žฌ ํ• ๋‹น๋œ ๋ชจ๋“  ํŽ˜์ด์ง€์˜ ํ• ๋‹น ์ฃผ์ฒด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Page Owner๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋น„ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์œผ๋ฉฐ, ๋ถ€ํŒ… ์ปค๋งจ๋“œ๋ผ์ธ์— page_owner=on์„ ์ถ”๊ฐ€ํ•˜์—ฌ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค. ๋น„ํ™œ์„ฑํ™” ์‹œ์—๋Š” hotpath์— 2๊ฐœ์˜ unlikely ๋ถ„๊ธฐ๋งŒ ์ถ”๊ฐ€๋˜์–ด ์„ฑ๋Šฅ ์˜ํ–ฅ์ด ๋ฏธ๋ฏธํ•ฉ๋‹ˆ๋‹ค. ํ™œ์„ฑํ™” ์‹œ์—๋„ page_ext ๋ฉ”๋ชจ๋ฆฌ์™€ stack_depot ์Šคํƒ ์ €์žฅ์†Œ๋งŒ ์ถ”๊ฐ€๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์†Œ์Šค ํŒŒ์ผ:
mm/page_owner.c              โ† ํ•ต์‹ฌ ๊ตฌํ˜„ (ํ• ๋‹น ์ถ”์ , debugfs ์ธํ„ฐํŽ˜์ด์Šค)
include/linux/page_owner.h   โ† ์™ธ๋ถ€ API (set_page_owner, reset_page_owner ๋“ฑ)
include/linux/page_ext.h     โ† page_ext ํ™•์žฅ ๋ฉ”์ปค๋‹ˆ์ฆ˜
tools/mm/page_owner_sort.c   โ† ์‚ฌ์šฉ์ž็ฉบ้—ด ์ •๋ ฌ ๋„๊ตฌ

๋น ๋ฅธ ์ ๊ฒ€ ๋ช…๋ น

# 1. page_owner ํ™œ์„ฑํ™” ์ƒํƒœ ํ™•์ธ (๋ถ€ํŒ… ์‹œ)
cat /proc/cmdline | grep page_owner

# 2. ํŽ˜์ด์ง€ ํ• ๋‹น ์ •๋ณด ์ถœ๋ ฅ
cat /sys/kernel/debug/page_owner

# 3. ์Šคํƒ ๊ธฐ๋ฐ˜ ์ •๋ ฌ ์ถœ๋ ฅ (ํ• ๋‹น ๋งŽ์€ ์ˆœ)
cat /sys/kernel/debug/page_owner_stacks/show_stacks

# 4. ํ•ธ๋“ค ๊ธฐ๋ฐ˜ ์ถœ๋ ฅ (๋น ๋ฅธ ์š”์•ฝ)
cat /sys/kernel/debug/page_owner_stacks/show_handles

# 5. ์นด์šดํŠธ ์ž„๊ณ„๊ฐ’ ์„ค์ • ํ›„ ํ•„ํ„ฐ๋ง
echo 7000 > /sys/kernel/debug/page_owner_stacks/count_threshold
cat /sys/kernel/debug/page_owner_stacks/show_stacks

# 6. ์‚ฌ์šฉ์ž็ฉบ้—ด ์ •๋ ฌ ๋„๊ตฌ ์‹คํ–‰
cd tools/mm && make page_owner_sort
cat /sys/kernel/debug/page_owner > page_owner_full.txt
./page_owner_sort page_owner_full.txt sorted_page_owner.txt

# 7. ๋ฉ”๋ชจ๋ฆฌ ๊ธฐ์ค€ ์ •๋ ฌ (-m)
./page_owner_sort -m page_owner_full.txt sorted_by_mem.txt

# 8. ํŠน์ • ํ”„๋กœ์„ธ์Šค ํ•„ํ„ฐ๋ง (--pid)
./page_owner_sort --pid=1234 page_owner_full.txt filtered.txt

# 9. ํŠน์ • ํ”„๋กœ์„ธ์Šค ๊ทธ๋ฃน ํ•„ํ„ฐ๋ง (--tgid)
./page_owner_sort --tgid=100,200 page_owner_full.txt filtered_tgids.txt

# 10. ํŠน์ • ๋ช…๋ น์–ด ํ•„ํ„ฐ๋ง (--name)
./page_owner_sort --name sshd,nginx page_owner_full.txt filtered_names.txt

# 11. ํŠน์ • ํ•„๋“œ๋งŒ ์ถœ๋ ฅ (--cull)
./page_owner_sort --cull=stacktrace,pid page_owner_full.txt culled.txt

# 12. ์ปค์Šคํ…€ ์ •๋ ฌ (--sort)
./page_owner_sort --sort=-n,+pid page_owner_full.txt sorted_custom.txt

์ž๋ฃŒ๊ตฌ์กฐ ๊ด€๊ณ„๋„

Page Owner ์ž๋ฃŒ๊ตฌ์กฐ ๊ด€๊ณ„๋„

ํ˜ธ์ถœ ํ๋ฆ„

Page Owner ํ˜ธ์ถœ ํ๋ฆ„

ํ•ต์‹ฌ ์ž๋ฃŒ๊ตฌ์กฐ

struct page_owner โ€” ํŽ˜์ด์ง€ ํ• ๋‹น ์ถ”์  ์ •๋ณด

page_owner ๊ตฌ์กฐ์ฒด๋Š” page_ext์— ํฌํ•จ๋˜์–ด ๊ฐ ํŽ˜์ด์ง€๋งˆ๋‹ค ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

/* mm/page_owner.c:24-37 */
struct page_owner {
unsigned short order;              /* ํ• ๋‹น๋œ ํŽ˜์ด์ง€ order (2^order๊ฐœ) */
short last_migrate_reason;         /* ๋งˆ์ง€๋ง‰ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‚ฌ์œ  (-1 = ์—†์Œ) */
gfp_t gfp_mask;                   /* ํ• ๋‹น ์‹œ ์‚ฌ์šฉ๋œ GFP ํ”Œ๋ž˜๊ทธ */
depot_stack_handle_t handle;       /* ํ• ๋‹น ์‹œ ์Šคํƒ ํ•ธ๋“ค (stack_depot) */
depot_stack_handle_t free_handle;  /* ํ•ด์ œ ์‹œ ์Šคํƒ ํ•ธ๋“ค */
u64 ts_nsec;                       /* ํ• ๋‹น ์‹œ์  (๋‚˜๋…ธ์ดˆ) */
u64 free_ts_nsec;                  /* ํ•ด์ œ ์‹œ์  (๋‚˜๋…ธ์ดˆ) */
char comm[TASK_COMM_LEN];          /* ํ• ๋‹นํ•œ ํ”„๋กœ์„ธ์Šค ๋ช…๋ น์–ด (16๋ฐ”์ดํŠธ) */
pid_t pid;                         /* ํ• ๋‹นํ•œ ํ”„๋กœ์„ธ์Šค PID */
pid_t tgid;                        /* ํ• ๋‹นํ•œ ํ”„๋กœ์„ธ์Šค TGID */
pid_t free_pid;                    /* ํ•ด์ œํ•œ ํ”„๋กœ์„ธ์Šค PID */
pid_t free_tgid;                   /* ํ•ด์ œํ•œ ํ”„๋กœ์„ธ์Šค TGID */
};

struct page_ext โ€” ํŽ˜์ด์ง€ ํ™•์žฅ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ

page_owner ๋ฐ์ดํ„ฐ๋Š” page_ext ๊ตฌ์กฐ์ฒด์— ํฌํ•จ๋˜์–ด ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. page_ext๋Š” sparse memory ์‹œ์Šคํ…œ์—์„œ ๊ฐ ํŽ˜์ด์ง€์— ๋Œ€ํ•ด ํ• ๋‹น๋˜๋Š” ํ™•์žฅ ๋ฉ”๋ชจ๋ฆฌ ๋ธ”๋ก์ž…๋‹ˆ๋‹ค.

/* include/linux/page_ext.h:52-54 */
struct page_ext {
unsigned long flags;  /* PAGE_EXT_OWNER, PAGE_EXT_OWNER_ALLOCATED ๋“ฑ ๋น„ํŠธ ํ”Œ๋ž˜๊ทธ */
};

page_ext ํ”Œ๋ž˜๊ทธ โ€” ํ• ๋‹น ์ƒํƒœ ์ถ”์ 

/* include/linux/page_ext.h:36-43 */
enum page_ext_flags {
PAGE_EXT_OWNER,              /* ์ด ํŽ˜์ด์ง€์˜ page_owner ์ •๋ณด๊ฐ€ ์œ ํšจํ•œ์ง€ */
PAGE_EXT_OWNER_ALLOCATED,    /* ํ˜„์žฌ ํ• ๋‹น ์ค‘์ธ์ง€ (0 = ํ•ด์ œ๋จ) */
};

struct page_ext_operations โ€” page_ext ํด๋ผ์ด์–ธํŠธ ์—ฐ์‚ฐ

/* include/linux/page_ext.h:25-31 */
struct page_ext_operations {
size_t offset;               /* page_ext ๋‚ด ํด๋ผ์ด์–ธํŠธ ๋ฐ์ดํ„ฐ ์˜คํ”„์…‹ */
size_t size;                 /* ํด๋ผ์ด์–ธํŠธ ๋ฐ์ดํ„ฐ ํฌ๊ธฐ */
bool (*need)(void);          /* page_ext๊ฐ€ ํ•„์š”ํ•œ์ง€ ์—ฌ๋ถ€ */
void (*init)(void);          /* page_ext ํ• ๋‹น ํ›„ ์ดˆ๊ธฐํ™” ์ฝœ๋ฐฑ */
bool need_shared_flags;      /* ๊ณต์œ  ํ”Œ๋ž˜๊ทธ ํ•„๋“œ ์‚ฌ์šฉ ์—ฌ๋ถ€ */
};

struct stack โ€” ์Šคํƒ ๋ ˆ์ฝ”๋“œ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ

ํ• ๋‹น๋œ ์Šคํƒ ๋ ˆ์ฝ”๋“œ๋“ค์„ ์ˆœํšŒํ•˜๊ธฐ ์œ„ํ•œ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ ๋…ธ๋“œ์ž…๋‹ˆ๋‹ค.

/* mm/page_owner.c:39-42 */
struct stack {
struct stack_record *stack_record;  /* stack_depot ์Šคํƒ ๋ ˆ์ฝ”๋“œ ํฌ์ธํ„ฐ */
struct stack *next;                 /* ๋‹ค์Œ ์Šคํƒ ๋…ธ๋“œ */
};

stack_print_ctx โ€” debugfs ์ถœ๋ ฅ ์ปจํ…์ŠคํŠธ

show_stacks, show_handles ๋“ฑ debugfs ํŒŒ์ผ ์ฝ๊ธฐ ์‹œ ์ถœ๋ ฅ ํ”Œ๋ž˜๊ทธ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

/* mm/page_owner.c:52-55 */
struct stack_print_ctx {
struct stack *stack;  /* ํ˜„์žฌ ์ˆœํšŒ ์ค‘์ธ ์Šคํƒ ๋…ธ๋“œ */
u8 flags;             /* STACK_PRINT_FLAG_STACK | PAGES | HANDLE ์กฐํ•ฉ */
};

page_owner ์ดˆ๊ธฐํ™” ๋ ˆ์ง€์Šคํ„ฐ

page_owner_ops๋Š” page_ext์— ํŽ˜์ด์ง€ ์ถ”์  ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ™์ด๋Š” ๋“ฑ๋ก ์ •๋ณด์ž…๋‹ˆ๋‹ค. ๋ถ€ํŒ… ์ธ์ž๋กœ ๊ธฐ๋Šฅ์„ ์ผœ๋ฉด ์ดˆ๊ธฐํ™” ๋ฃจํ‹ด์—์„œ ๋”๋ฏธ/์‹คํŒจ/์ดˆ๊ธฐ ์Šคํƒ์„ ์ค€๋น„ํ•˜๊ณ , ์ดํ›„ page_owner_inited static key๋ฅผ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค.

/* mm/page_owner.c:93-148 */
static __init bool need_page_owner(void)
{
return page_owner_enabled;
}

static __init void init_page_owner(void)
{
if (!page_owner_enabled)
return;

register_dummy_stack();
register_failure_stack();
register_early_stack();
init_early_allocated_pages();
dummy_stack.stack_record = __stack_depot_get_stack_record(dummy_handle);
failure_stack.stack_record = __stack_depot_get_stack_record(failure_handle);
if (dummy_stack.stack_record)
refcount_set(&dummy_stack.stack_record->count, 1);
if (failure_stack.stack_record)
refcount_set(&failure_stack.stack_record->count, 1);
dummy_stack.next = &failure_stack;
stack_list = &dummy_stack;
static_branch_enable(&page_owner_inited);
}

struct page_ext_operations page_owner_ops = {
.size = sizeof(struct page_owner),
.need = need_page_owner,
.init = init_page_owner,
.need_shared_flags = true,
};

early_param("page_owner", early_page_owner_param)๊ฐ€ page_owner_enabled๋ฅผ ์„ธ์šฐ๊ณ , page_owner=on์ด๋ฉด stack_depot_request_early_init()๊นŒ์ง€ ๋จผ์ € ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.


ํ•ต์‹ฌ ํ•จ์ˆ˜

__set_page_owner() โ€” ํŽ˜์ด์ง€ ํ• ๋‹น ์‹œ owner ์ •๋ณด ๊ธฐ๋ก

/* mm/page_owner.c:335-346 */
noinline void __set_page_owner(struct page *page, unsigned short order,
gfp_t gfp_mask)
{
u64 ts_nsec = local_clock();
depot_stack_handle_t handle;

handle = save_stack(gfp_mask);  /* ํ˜„์žฌ ํ˜ธ์ถœ ์Šคํƒ ์ €์žฅ */
/* page_owner ํ•„๋“œ์— ํ• ๋‹น ์ •๋ณด ๊ธฐ๋ก */
__update_page_owner_handle(page, handle, order, gfp_mask, -1,
ts_nsec, current->pid, current->tgid,
current->comm);
inc_stack_record_count(handle, gfp_mask, 1 << order);  /* ์Šคํƒ๋ณ„ ํŽ˜์ด์ง€ ์ˆ˜ ์ฆ๊ฐ€ */
}

์—ญํ• : ํŽ˜์ด์ง€ ํ• ๋‹น ์‹œ ํ˜ธ์ถœ ์Šคํƒ๊ณผ ํ”„๋กœ์„ธ์Šค ์ •๋ณด๋ฅผ page_ext์— ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.

__reset_page_owner() โ€” ํŽ˜์ด์ง€ ํ•ด์ œ ์‹œ owner ์ •๋ณด ์—…๋ฐ์ดํŠธ

/* mm/page_owner.c:298-333 */
void __reset_page_owner(struct page *page, unsigned short order)
{
struct page_ext *page_ext;
depot_stack_handle_t handle;
depot_stack_handle_t alloc_handle;
struct page_owner *page_owner;
u64 free_ts_nsec = local_clock();  /* ํ•ด์ œ ์‹œ์  ๊ธฐ๋ก */

page_ext = page_ext_get(page);
if (unlikely(!page_ext))
return;

page_owner = get_page_owner(page_ext);
alloc_handle = page_owner->handle;
page_ext_put(page_ext);

/* ํ•ด์ œ ์‹œ ์Šคํƒ ์ €์žฅ (GFP_NOWAIT๋กœ ์žฌ๊ท€ ๋ฐฉ์ง€) */
handle = save_stack(__GFP_NOWARN);
__update_page_owner_free_handle(page, handle, order, current->pid,
current->tgid, free_ts_nsec);

/* ์ดˆ๊ธฐ ํ• ๋‹น ํŽ˜์ด์ง€(early_handle)๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ refcount ๊ฐ์†Œ */
if (alloc_handle != early_handle)
dec_stack_record_count(alloc_handle, 1 << order);
}

์—ญํ• : ํŽ˜์ด์ง€ ํ•ด์ œ ์‹œ ํ•ด์ œ ์Šคํƒ๊ณผ ํƒ€์ž„์Šคํƒฌํ”„๋ฅผ ๊ธฐ๋กํ•˜๊ณ , ์Šคํƒ๋ณ„ ํ• ๋‹น ํŽ˜์ด์ง€ ์ˆ˜๋ฅผ ๊ฐ์†Œ์‹œํ‚ต๋‹ˆ๋‹ค.

save_stack() โ€” ํ˜ธ์ถœ ์Šคํƒ์„ stack_depot์— ์ €์žฅ

/* mm/page_owner.c:155-172 */
static noinline depot_stack_handle_t save_stack(gfp_t flags)
{
unsigned long entries[PAGE_OWNER_STACK_DEPTH];
depot_stack_handle_t handle;
unsigned int nr_entries;

/* ์žฌ๊ท€ ๋ฐฉ์ง€: page_owner ๋‚ด๋ถ€ ํ• ๋‹น ์‹œ ์Šคํƒ ์ €์žฅ ์Šคํ‚ต */
if (current->in_page_owner)
return dummy_handle;

set_current_in_page_owner();  /* ์žฌ๊ท€ ํ”Œ๋ž˜๊ทธ ์„ค์ • */
nr_entries = stack_trace_save(entries, ARRAY_SIZE(entries), 2);  /* 2๋‹จ๊ณ„ ํ”„๋ ˆ์ž„ ์Šคํ‚ต */
handle = stack_depot_save(entries, nr_entries, flags);
if (!handle)
handle = failure_handle;  /* ์ €์žฅ ์‹คํŒจ ์‹œ ๋”๋ฏธ ํ•ธ๋“ค ์‚ฌ์šฉ */
unset_current_in_page_owner();

return handle;
}

์—ญํ• : ํ˜„์žฌ CPU์˜ ํ˜ธ์ถœ ์Šคํƒ์„ stack_depot์— ์ €์žฅํ•˜๊ณ  ํ•ธ๋“ค์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. page_owner ์ฝ”๋“œ ๋‚ด๋ถ€์—์„œ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ• ๋‹น๋˜๋Š” ์žฌ๊ท€ ์ƒํ™ฉ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

read_page_owner() โ€” debugfs์—์„œ ํŽ˜์ด์ง€ ํ• ๋‹น ์ •๋ณด ์ฝ๊ธฐ

/* mm/page_owner.c:659-755 (ํ•ต์‹ฌ ๋กœ์ง) */
static ssize_t read_page_owner(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
/* pfn ์œ„์น˜๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜์—ฌ ํ• ๋‹น๋œ ํŽ˜์ด์ง€ ํƒ์ƒ‰ */
for (; pfn < max_pfn; pfn++) {
/* Buddy ํŽ˜์ด์ง€๋Š” ์Šคํ‚ต */
if (PageBuddy(page)) {
unsigned long freepage_order = buddy_order_unsafe(page);
if (freepage_order <= MAX_PAGE_ORDER)
pfn += (1UL << freepage_order) - 1;
continue;
}
/* page_owner ํ”Œ๋ž˜๊ทธ ํ™•์ธ */
if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags))
goto ext_put_continue;
if (!test_bit(PAGE_EXT_OWNER_ALLOCATED, &page_ext->flags))
goto ext_put_continue;
/* ๊ณ ์œ  order์˜ ์ฒซ ํŽ˜์ด์ง€๋งŒ ์ถœ๋ ฅ (์ค‘๋ณต ๋ฐฉ์ง€) */
if (!IS_ALIGNED(pfn, 1 << page_owner->order))
goto ext_put_continue;
/* print_page_owner()๋กœ ์‚ฌ์šฉ์ž็ฉบ้—ด์— ๋ณต์‚ฌ */
return print_page_owner(buf, count, pfn, page, &page_owner_tmp, handle);
}
return 0;
}

์—ญํ• : /sys/kernel/debug/page_owner ํŒŒ์ผ read ์‹œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ์œ ํšจํ•œ ํ• ๋‹น ํŽ˜์ด์ง€๋ฅผ ์ฐพ์•„ ์ •๋ณด๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

/* mm/page_owner.c:668-749 */
if (!static_branch_unlikely(&page_owner_inited))
return -EINVAL;

page = NULL;
if (*ppos == 0)
pfn = min_low_pfn;
else
pfn = *ppos;

/* ์œ ํšจํ•œ PFN ๋˜๋Š” MAX_ORDER_NR_PAGES ๊ตฌ๊ฐ„ ์‹œ์ž‘์œผ๋กœ ์ด๋™ */
while (!pfn_valid(pfn) && (pfn & (MAX_ORDER_NR_PAGES - 1)) != 0)
pfn++;

for (; pfn < max_pfn; pfn++) {
/* ์ƒˆ MAX_ORDER_NR_PAGES ๊ตฌ๊ฐ„์ด ๋น„์–ด ์žˆ์œผ๋ฉด ๊ฑด๋„ˆ๋œ€ */
if ((pfn & (MAX_ORDER_NR_PAGES - 1)) == 0 && !pfn_valid(pfn)) {
pfn += MAX_ORDER_NR_PAGES - 1;
continue;
}

page = pfn_to_page(pfn);
if (PageBuddy(page)) {
unsigned long freepage_order = buddy_order_unsafe(page);

if (freepage_order <= MAX_PAGE_ORDER)
pfn += (1UL << freepage_order) - 1;
continue;
}

page_ext = page_ext_get(page);
if (unlikely(!page_ext))
continue;

if (!test_bit(PAGE_EXT_OWNER, &page_ext->flags))
goto ext_put_continue;
if (!test_bit(PAGE_EXT_OWNER_ALLOCATED, &page_ext->flags))
goto ext_put_continue;

page_owner = get_page_owner(page_ext);
if (!IS_ALIGNED(pfn, 1 << page_owner->order))
goto ext_put_continue;

handle = READ_ONCE(page_owner->handle);
if (!handle)
goto ext_put_continue;

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

ext_put_continue:
page_ext_put(page_ext);
}

return 0;

print_page_owner() โ€” ํŽ˜์ด์ง€ ํ• ๋‹น ์ •๋ณด ํฌ๋งทํŒ…

/* mm/page_owner.c:547-603 */
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)
{
/* ํ• ๋‹น ์ •๋ณด: order, GFP, PID, TGID, comm, ํƒ€์ž„์Šคํƒฌํ”„ */
ret = scnprintf(kbuf, count,
"Page allocated via order %u, mask %#x(%pGg), pid %d, tgid %d (%s), ts %llu ns\n",
page_owner->order, page_owner->gfp_mask,
&page_owner->gfp_mask, page_owner->pid,
page_owner->tgid, page_owner->comm,
page_owner->ts_nsec);

/* ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ •๋ณด */
if (page_owner->last_migrate_reason != -1) {
ret += scnprintf(kbuf + ret, count - ret,
"Page has been migrated, last migrate reason: %s\n",
migrate_reason_names[page_owner->last_migrate_reason]);
}

/* memcg ์ถฉ์ „ ์ •๋ณด */
ret = print_page_owner_memcg(kbuf, count, ret, page);
}

์—ญํ• : ํŽ˜์ด์ง€ ํ• ๋‹น ์ •๋ณด๋ฅผ human-readable ํ˜•ํƒœ๋กœ ํฌ๋งทํŒ…ํ•˜์—ฌ ์‚ฌ์šฉ์ž็ฉบ้—ด ๋ฒ„ํผ์— ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.

/* mm/page_owner.c:555-603 */
count = min_t(size_t, count, PAGE_SIZE);
kbuf = kmalloc(count, GFP_KERNEL);
if (!kbuf)
return -ENOMEM;

ret = scnprintf(kbuf, count,
"Page allocated via order %u, mask %#x(%pGg), pid %d, tgid %d (%s), ts %llu ns\n",
page_owner->order, page_owner->gfp_mask,
&page_owner->gfp_mask, page_owner->pid,
page_owner->tgid, page_owner->comm,
page_owner->ts_nsec);

pageblock_mt = get_pageblock_migratetype(page);
page_mt  = gfp_migratetype(page_owner->gfp_mask);
ret += scnprintf(kbuf + ret, count - ret,
"PFN 0x%lx type %s Block %lu type %s Flags %pGp\n",
pfn,
migratetype_names[page_mt],
pfn >> pageblock_order,
migratetype_names[pageblock_mt],
&page->flags);

ret += stack_depot_snprint(handle, kbuf + ret, count - ret, 0);
if (ret >= count)
goto err;

if (page_owner->last_migrate_reason != -1) {
ret += scnprintf(kbuf + ret, count - ret,
"Page has been migrated, last migrate reason: %s\n",
migrate_reason_names[page_owner->last_migrate_reason]);
}

ret = print_page_owner_memcg(kbuf, count, ret, page);
ret += snprintf(kbuf + ret, count - ret, "\n");
if (ret >= count)
goto err;

if (copy_to_user(buf, kbuf, ret))
ret = -EFAULT;

kfree(kbuf);
return ret;

err:
kfree(kbuf);
return -ENOMEM;

ํ˜ธ์ถœ ํ๋ฆ„ ํ…์ŠคํŠธ ์š”์•ฝ

ํ• ๋‹น ๊ฒฝ๋กœ:
__alloc_pages()
โ†’ set_page_owner()            โ† inline wrapper (page_owner.h)
โ†’ __set_page_owner()        โ† ์‹ค์ œ ๊ตฌํ˜„ (page_owner.c:335)
โ†’ save_stack()            โ† ํ˜ธ์ถœ ์Šคํƒ ์ €์žฅ (page_owner.c:155)
โ†’ stack_trace_save()    โ† ์ปค๋„ ์Šคํƒ ํŠธ๋ ˆ์ด์Šค ์ˆ˜์ง‘
โ†’ stack_depot_save()    โ† stack_depot์— ์ €์žฅ
โ†’ __update_page_owner_handle()  โ† page_ext์— ํ•„๋“œ ๊ธฐ๋ก (page_owner.c:244)
โ†’ inc_stack_record_count()      โ† ์Šคํƒ๋ณ„ ํŽ˜์ด์ง€ ์ˆ˜ ์ฆ๊ฐ€ (page_owner.c:206)

ํ•ด์ œ ๊ฒฝ๋กœ:
__free_pages()
โ†’ reset_page_owner()          โ† inline wrapper
โ†’ __reset_page_owner()      โ† ์‹ค์ œ ๊ตฌํ˜„ (page_owner.c:298)
โ†’ save_stack()            โ† ํ•ด์ œ ์Šคํƒ ์ €์žฅ
โ†’ __update_page_owner_free_handle()  โ† ํ•ด์ œ ์ •๋ณด ๊ธฐ๋ก (page_owner.c:273)
โ†’ dec_stack_record_count()           โ† ์Šคํƒ๋ณ„ ํŽ˜์ด์ง€ ์ˆ˜ ๊ฐ์†Œ (page_owner.c:231)

์ฝ๊ธฐ ๊ฒฝ๋กœ:
cat /sys/kernel/debug/page_owner
โ†’ read_page_owner()           โ† debugfs read ํ•ธ๋“ค๋Ÿฌ (page_owner.c:659)
โ†’ pfn ์ˆœํšŒ (Buddy/Reserved ์Šคํ‚ต)
โ†’ print_page_owner()        โ† ์ •๋ณด ํฌ๋งทํŒ… (page_owner.c:547)
โ†’ stack_depot_snprint()   โ† ์Šคํƒ ํŠธ๋ ˆ์ด์Šค ์ถœ๋ ฅ
โ†’ print_page_owner_memcg()  โ† memcg ์ •๋ณด ์ถœ๋ ฅ (page_owner.c:511)
โ†’ copy_to_user()          โ† ์‚ฌ์šฉ์ž็ฉบ้—ด์œผ๋กœ ๋ณต์‚ฌ

์Šคํƒ ์ˆœํšŒ ๊ฒฝ๋กœ:
cat /sys/kernel/debug/page_owner_stacks/show_stacks
โ†’ page_owner_stack_open()     โ† seq_file ์˜คํ”ˆ (page_owner.c:936)
โ†’ stack_start()               โ† stack_list ํ—ค๋“œ๋ถ€ํ„ฐ ์ˆœํšŒ ์‹œ์ž‘ (page_owner.c:855)
โ†’ stack_print()               โ† ๊ฐ ์Šคํƒ์˜ ํ• ๋‹น ํŽ˜์ด์ง€ ์ˆ˜ ์ถœ๋ ฅ (page_owner.c:892)
โ†’ stack_next()                โ† ๋‹ค์Œ ์Šคํƒ์œผ๋กœ ์ด๋™ (page_owner.c:878)

์กฐ๊ฑด๋ณ„ ๋น„๊ต

ํŽ˜์ด์ง€ Owner ๋น„๊ต ํ‘œ

๋น„๊ต ํ•ญ๋ชฉ๋น„ํ™œ์„ฑํ™” ์‹œํ™œ์„ฑํ™” ์‹œ
**hotpath ๋ถ„๊ธฐ**์—†์Œ2๊ฐœ unlikely (jump label ํŒจ์น˜๋กœ NOP)
**page_ext ์‚ฌ์šฉ**์—†์ŒํŽ˜์ด์ง€๋‹น `sizeof(struct page_owner)` ์ถ”๊ฐ€
**stack_depot ์‚ฌ์šฉ**์—†์Œ๊ณ ์œ  ์Šคํƒ๋‹น ์Šคํƒ ๋ ˆ์ฝ”๋“œ ์ €์žฅ
**์„ฑ๋Šฅ ์˜ํ–ฅ**์—†์Œ๋ฏธ๋ฏธ (jump label ๋น„ํ™œ์„ฑํ™” ์‹œ NOP)
**๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฒ„ํ—ค๋“œ**์—†์Œpage_ext + stack_depot ํ• ๋‹น

์ •๋ณด ์ถœ๋ ฅ ์œ ํ˜• ๋น„๊ต

์ถœ๋ ฅ ์ธํ„ฐํŽ˜์ด์Šค๋‚ด์šฉ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค
`/sys/kernel/debug/page_owner`์ „์ฒด ํŽ˜์ด์ง€๋ณ„ ์ƒ์„ธ ์ •๋ณดํŠน์ • ํŽ˜์ด์ง€ ํ• ๋‹น ์›์ธ ์ถ”์ 
`show_stacks`์Šคํƒ๋ณ„ ํ• ๋‹น ํŽ˜์ด์ง€ ์ˆ˜ + ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๋ฉ”๋ชจ๋ฆฌ ๋งŽ์ด ์“ฐ๋Š” ์ฝ”๋“œ ์‹๋ณ„
`show_handles`์Šคํƒ ํ•ธ๋“ค ๋ฒˆํ˜ธ + ํŽ˜์ด์ง€ ์ˆ˜๋งŒ๋น ๋ฅธ ์š”์•ฝ, ๋ชจ๋‹ˆํ„ฐ๋ง
`show_stacks_handles`์Šคํƒ ํŠธ๋ ˆ์ด์Šค + ํ•ธ๋“ค ๋ฒˆํ˜ธ์Šคํƒ-ํ•ธ๋“ค ๋งค์นญ
`count_threshold`์ž„๊ณ„๊ฐ’ ์„ค์ • (show_stacks ํ•„ํ„ฐ)๋Œ€๋Ÿ‰ ํ• ๋‹น๋งŒ ํ•„ํ„ฐ๋ง

page_owner_sort ์ •๋ ฌ ์˜ต์…˜ ๋น„๊ต

์˜ต์…˜์ •๋ ฌ ๊ธฐ์ค€๊ธฐ๋ณธ ์ •๋ ฌ ๋ฐฉํ–ฅ์‚ฌ์šฉ ์˜ˆ
`-t`ํ• ๋‹น ํšŸ์ˆ˜ (default)๋‚ด๋ฆผ์ฐจ์ˆœ๊ฐ€์žฅ ๋งŽ์€ ํ• ๋‹น ํŒจํ„ด
`-m`์ด ๋ฉ”๋ชจ๋ฆฌ ํŽ˜์ด์ง€ ์ˆ˜๋‚ด๋ฆผ์ฐจ์ˆœ๊ฐ€์žฅ ๋งŽ์€ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ
`-a`ํ• ๋‹น ํƒ€์ž„์Šคํƒฌํ”„์˜ค๋ฆ„์ฐจ์ˆœ์˜ค๋ž˜๋œ ํ• ๋‹น๋ถ€ํ„ฐ
`-r`ํ•ด์ œ ํƒ€์ž„์Šคํƒฌํ”„์˜ค๋ฆ„์ฐจ์ˆœ์ตœ๊ทผ ํ•ด์ œ๋ถ€ํ„ฐ
`-p`PID์˜ค๋ฆ„์ฐจ์ˆœํ”„๋กœ์„ธ์Šค๋ณ„ ๊ทธ๋ฃนํ•‘
`-P`TGID์˜ค๋ฆ„์ฐจ์ˆœํ”„๋กœ์„ธ์Šค ๊ทธ๋ฃน๋ณ„
`-n`๋ช…๋ น์–ด ์ด๋ฆ„์˜ค๋ฆ„์ฐจ์ˆœ๋ช…๋ น์–ด๋ณ„ ๊ทธ๋ฃนํ•‘
`-s`์Šคํƒ ํŠธ๋ ˆ์ด์Šค์˜ค๋ฆ„์ฐจ์ˆœ๋™์ผ ์Šคํƒ ๊ทธ๋ฃนํ•‘

stack_depot ํ•ธ๋“ค ์ƒํƒœ ๋น„๊ต

์ƒํƒœ์˜๋ฏธ์ฒ˜๋ฆฌ ๋ฐฉ์‹
`handle == 0`์Šคํƒ ์ €์žฅ ์‹คํŒจ์ถœ๋ ฅ ์‹œ "stack trace missing" ํ‘œ์‹œ
`handle == dummy_handle`์ดˆ๊ธฐํ™” ์ „ ๋˜๋Š” ์žฌ๊ท€ ํ• ๋‹นpage_owner ๋‚ด๋ถ€ ํ• ๋‹น ์‹œ ๋ฐ˜ํ™˜
`handle == failure_handle`stack_depot_save ์‹คํŒจfallback ํ•ธ๋“ค
`handle == early_handle`์ดˆ๊ธฐ ํ• ๋‹น ํŽ˜์ด์ง€refcount ์ฆ๊ฐ ์ƒ๋žต
์ •์ƒ ํ•ธ๋“ค์œ ํšจํ•œ ์Šคํƒ ๋ ˆ์ฝ”๋“œstack_depot_snprint๋กœ ์ถœ๋ ฅ

๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • Buddy Allocator
  • SLUB ํ• ๋‹น์ž
  • ๋””๋ฒ„๊ทธ ๋„๊ตฌ
  • Migration
  • Memory Cgroup