๏ปฟ# 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
์๋ฃ๊ตฌ์กฐ ๊ด๊ณ๋
ํธ์ถ ํ๋ฆ
ํต์ฌ ์๋ฃ๊ตฌ์กฐ
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๋ก ์ถ๋ ฅ |