๏ปฟ# Free Page Reporting

๊ฐœ์š” (Overview)

Free Page Reporting๋Š” ๊ฐ€์ƒํ™” ํ™˜๊ฒฝ์—์„œ ํ˜ธ์ŠคํŠธ(์„œ๋ฒ„)๊ฐ€ ๊ฒŒ์ŠคํŠธ VM์˜ ๋นˆ ํŽ˜์ด์ง€๋ฅผ ์ธ์ง€ํ•˜๊ณ  ํšŒ์ˆ˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค. ๊ฒŒ์ŠคํŠธ ์ปค๋„์ด "๋” ์ด์ƒ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํŽ˜์ด์ง€"๋ฅผ ํ˜ธ์ŠคํŠธ์— ์•Œ๋ฆฌ๋ฉด, ํ˜ธ์ŠคํŠธ๋Š” ํ•ด๋‹น ํŽ˜์ด์ง€๋ฅผ ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ œ๊ฑฐํ•˜์—ฌ ๋‹ค๋ฅธ VM์ด๋‚˜ ํ˜ธ์ŠคํŠธ ์ž์ฒด์— ์žฌํ• ๋‹นํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ณผ์ (overcommit) ํ™˜๊ฒฝ์—์„œ ์‹ค์ œ ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ์ค„์ด๊ณ , ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค.

์ด ์„œ๋ธŒ์‹œ์Šคํ…œ์€ Virtio Balloon ๋“œ๋ผ์ด๋ฒ„์™€ Hyper-V Balloon ๋“œ๋ผ์ด๋ฒ„๊ฐ€ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ฒŒ์ŠคํŠธ ์ปค๋„ ์ธก์—์„œ๋Š” page_reporting.c๊ฐ€ Buddy Allocator์˜ free list๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ ๋ณด๊ณ ๋˜์ง€ ์•Š์€(reported) ํŽ˜์ด์ง€๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ , scatterlist๋ฅผ ํ†ตํ•ด ๋“œ๋ผ์ด๋ฒ„ ์ฝœ๋ฐฑ(report())์œผ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๋“œ๋ผ์ด๋ฒ„๋Š” ํ•ด๋‹น ํŽ˜์ด์ง€๋ฅผ ํ˜ธ์ŠคํŠธ์— ์•Œ๋ฆฌ๊ณ , ์•Œ๋ฆผ์ด ์™„๋ฃŒ๋œ ํ›„ ํŽ˜์ด์ง€๋Š” Buddy Allocator๋กœ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค. CONFIG_PAGE_REPORTING ์ปค๋„ ์˜ต์…˜์œผ๋กœ ํ™œ์„ฑํ™”๋˜๋ฉฐ, page_reporting_order ๋ชจ๋“ˆ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ณด๊ณ ํ•  ํŽ˜์ด์ง€์˜ ์ตœ์†Œ order๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ผ์ƒ์ ์œผ๋กœ ๋ณด๋ฉด, ์ปค๋„์ด "์•„์ง ๋น„์–ด ์žˆ๋Š” ์ฑ…"์— ๋ฐ˜๋‚ฉ ์˜ˆ์ • ํ‘œ์‹์„ ๋ถ™์—ฌ ๋ฌถ์–ด ๋‘๊ณ , ์ผ์ • ์‹œ๊ฐ„ ๋™์•ˆ ๋ชจ์€ ๋’ค ํ•œ๊บผ๋ฒˆ์— ์ฐฝ๊ณ  ๋‹ด๋‹น์ž์—๊ฒŒ ๋„˜๊ธฐ๋Š” ์ž‘์—…์— ๊ฐ€๊น์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด ๊ธฐ๋Šฅ์€ ๋‹จ์ˆœํžˆ free list๋ฅผ ํ›‘๋Š” ๊ฒฝ๋กœ๊ฐ€ ์•„๋‹ˆ๋ผ, ๋ณด๊ณ ๋˜์ง€ ์•Š์€ ํŽ˜์ด์ง€๋ฅผ ๋ชจ์•„ ๋ฐฐ์น˜(batch)๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ์ง€์—ฐ ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ์ดํ•ดํ•˜๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค.

์†Œ์Šค ํŒŒ์ผ:

mm/page_reporting.c
mm/page_reporting.h          (๋‚ด๋ถ€ ํ—ค๋”)
include/linux/page_reporting.h  (๊ณต๊ฐœ API)
include/linux/page-flags.h      (PageReported ํ”Œ๋ž˜๊ทธ)
drivers/virtio/virtio_balloon.c (Virtio ์†Œ๋น„์ž)
drivers/hv/hv_balloon.c         (Hyper-V ์†Œ๋น„์ž)

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

# page_reporting ๋ชจ๋“ˆ ํŒŒ๋ผ๋ฏธํ„ฐ ํ™•์ธ
cat /sys/module/page_reporting/parameters/page_reporting_order 2>/dev/null || echo "๋ชจ๋“ˆ ๋ฏธ๋กœ๋“œ ๋˜๋Š” ๋น„ํ™œ์„ฑํ™”"

# ์ปค๋„ ์ปจํ”ผ๊ทธ ํ™•์ธ
grep CONFIG_PAGE_REPORTING /boot/config-$(uname -r) 2>/dev/null || zcat /proc/config.gz 2>/dev/null | grep CONFIG_PAGE_REPORTING

# Virtio Balloon ๋“œ๋ผ์ด๋ฒ„ ๋กœ๋“œ ์ƒํƒœ ํ™•์ธ
lsmod | grep virtio_balloon

# Hyper-V Balloon ๋“œ๋ผ์ด๋ฒ„ ๋กœ๋“œ ์ƒํƒœ ํ™•์ธ
lsmod | grep hv_balloon

# ๋™์  ํ‚ค ์ƒํƒœ ํ™•์ธ (page_reporting ํ™œ์„ฑํ™” ์—ฌ๋ถ€)
cat /sys/kernel/debug/dynamic_debug/control 2>/dev/null | grep page_reporting || echo "dynamic debug ๋ฏธ์ง€์›"

# Buddy Allocator free list ์ƒํƒœ (zone๋ณ„)
cat /proc/buddyinfo

# ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ํ˜„ํ™ฉ
free -h

# ๋ฉ”๋ชจ๋ฆฌ ํ•ต์‹ฌ ์ง€ํ‘œ
grep -E 'MemTotal|MemFree|MemAvailable|AnonPages|Cached' /proc/meminfo

# ํŽ˜์ด์ง€ ํดํŠธ์™€ ํšŒ์ˆ˜ ์ถ”์„ธ
grep -E 'pgfault|pgmajfault|pgscan|pgsteal' /proc/vmstat

# ๋Œ€ํ‘œ ์Šฌ๋žฉ ์บ์‹œ ํ™•์ธ
grep -E '^(kmalloc-|dentry|inode_cache|task_struct)' /proc/slabinfo

# Balloon ํ†ต๊ณ„ ํ™•์ธ (Virtio)
cat /proc/vmstat | grep -i balloon

# ๋ฉ”๋ชจ๋ฆฌ ์••๋ ฅ ํ™•์ธ (PSI)
cat /proc/pressure/memory

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

struct page_reporting_dev_info

๋“œ๋ผ์ด๋ฒ„๊ฐ€ page_reporting ์„œ๋ธŒ์‹œ์Šคํ…œ์— ๋“ฑ๋กํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์žฅ์น˜ ์ •๋ณด ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค.

/* include/linux/page_reporting.h:11-24 */
struct page_reporting_dev_info {
	/* ํŽ˜์ด์ง€๋ฅผ "reported" ์ƒํƒœ๋กœ ๋งŒ๋“œ๋Š” ๋“œ๋ผ์ด๋ฒ„ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ */
	int (*report)(struct page_reporting_dev_info *prdev,
		      struct scatterlist *sg, unsigned int nents);

	/* ์ง€์—ฐ๋œ ์ž‘์—…(delayed work) ๊ตฌ์กฐ์ฒด โ€” ๋น„๋™๊ธฐ ๋ณด๊ณ  ์ฒ˜๋ฆฌ */
	struct delayed_work work;

	/* ํ˜„์žฌ ๋ณด๊ณ  ์ƒํƒœ: IDLE(0), REQUESTED(1), ACTIVE(2) */
	atomic_t state;

	/* ๋ณด๊ณ ํ•  ํŽ˜์ด์ง€์˜ ์ตœ์†Œ order */
	unsigned int order;
};

์ƒํƒœ ์—ด๊ฑฐํ˜• (๋‚ด๋ถ€)

page_reporting.c ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์ƒํƒœ ๊ฐ’์ž…๋‹ˆ๋‹ค.

/* mm/page_reporting.c:53-57 */
enum {
	PAGE_REPORTING_IDLE = 0,      /* ์œ ํœด ์ƒํƒœ โ€” ๋ณด๊ณ  ์ž‘์—… ์—†์Œ */
	PAGE_REPORTING_REQUESTED,      /* ๋ณด๊ณ  ์š”์ฒญ๋จ โ€” ์›Œํฌ ํ ์˜ˆ์•ฝ ๋Œ€๊ธฐ */
	PAGE_REPORTING_ACTIVE          /* ๋ณด๊ณ  ์ง„ํ–‰ ์ค‘ โ€” ์‹ค์ œ ์ฒ˜๋ฆฌ ์ค‘ */
};

PAGE_REPORTING_CAPACITY

scatterlist์˜ ํฌ๊ธฐ โ€” ํ•œ ๋ฒˆ์— ๋“œ๋ผ์ด๋ฒ„์— ์ „๋‹ฌํ•  ์ตœ๋Œ€ ํŽ˜์ด์ง€ ์ˆ˜์ž…๋‹ˆ๋‹ค.

/* include/linux/page_reporting.h:9 */
#define PAGE_REPORTING_CAPACITY		32

PageReported ํ”Œ๋ž˜๊ทธ

Buddy Allocator์—์„œ ์ด๋ฏธ ๋ณด๊ณ ๋œ ํŽ˜์ด์ง€๋ฅผ ์ถ”์ ํ•˜๋Š” ํŽ˜์ด์ง€ ํ”Œ๋ž˜๊ทธ์ž…๋‹ˆ๋‹ค.

/* include/linux/page-flags.h:682-688 */
/*
* PageReported()๋Š” Buddy Allocator ๋‚ด๋ถ€์—์„œ ๋ณด๊ณ ๋œ ๋นˆ ํŽ˜์ด์ง€๋ฅผ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค.
* zone lock์œผ๋กœ ๋ณดํ˜ธํ•ด์•ผ ๋น„ํŠธ ์„ค์ •/ํ•ด์ œ ๊ฒฝ์Ÿ์„ ๋ง‰์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
*/
__PAGEFLAG(Reported, reported, PF_NO_COMPOUND)

page_reporting_notify_free โ€” ํ•ซ ๊ฒฝ๋กœ ์ง„์ž…์ 

__free_one_page()์—์„œ ํ˜ธ์ถœ๋˜๋Š” ์ธ๋ผ์ธ ํ•จ์ˆ˜๋กœ, free ํŽ˜์ด์ง€๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ page_reporting์— ์•Œ๋ฆฝ๋‹ˆ๋‹ค.

Free Page Reporting ์ž๋ฃŒ๊ตฌ์กฐ ๊ด€๊ณ„๋„
/* mm/page_reporting.h:33-45 */
static inline void page_reporting_notify_free(unsigned int order)
{
	/* __free_one_page()์˜ ํ•ซ ๊ฒฝ๋กœ์—์„œ ํ˜ธ์ถœ๋จ */
	if (!static_branch_unlikely(&page_reporting_enabled))
		return;

	/* page_reporting_order ๋ฏธ๋งŒ์ด๋ฉด ๋ฌด์‹œ */
	if (order < page_reporting_order)
		return;

	/* __page_reporting_notify() ํ˜ธ์ถœ (์•ฝ๊ฐ„์˜ ์˜ค๋ฒ„ํ—ค๋“œ ์žˆ์Œ) */
	__page_reporting_notify();
}

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

0. `__page_reporting_notify()`

RCU๋กœ ๋“ฑ๋ก๋œ ์žฅ์น˜๋ฅผ ์ฐพ์•„ ๋ณด๊ณ  ์š”์ฒญ์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

/* mm/page_reporting.c:87-102 */
void __page_reporting_notify(void)
{
	struct page_reporting_dev_info *prdev;

	/* RCU๋กœ pr_dev_info๋ฅผ ๋ณดํ˜ธํ•œ๋‹ค. */
	rcu_read_lock();
	prdev = rcu_dereference(pr_dev_info);
	if (likely(prdev))
		__page_reporting_request(prdev);

	rcu_read_unlock();
}

1. `__page_reporting_request()`

pr_dev_info์˜ ์›์ž์  ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๊ณ , ์œ ํœด ์ƒํƒœ์ด๋ฉด ์ง€์—ฐ๋œ ์›Œํฌ๋ฅผ ์˜ˆ์•ฝํ•ฉ๋‹ˆ๋‹ค.

/* mm/page_reporting.c:60-84 */
static void
__page_reporting_request(struct page_reporting_dev_info *prdev)
{
	unsigned int state;

	/* ์ด๋ฏธ ์š”์ฒญ ์ƒํƒœ์ด๋ฉด ๋ฌด์‹œ */
	state = atomic_read(&prdev->state);
	if (state == PAGE_REPORTING_REQUESTED)
		return;

	/* ์ด๋ฏธ ํ™œ์„ฑ ์ƒํƒœ(IDLE์ด ์•„๋‹Œ ๊ฒฝ์šฐ)์ด๋ฉด ๋ฌด์‹œ */
	state = atomic_xchg(&prdev->state, PAGE_REPORTING_REQUESTED);
	if (state != PAGE_REPORTING_IDLE)
		return;

	/* 2์ดˆ ์ง€์—ฐ ํ›„ ์›Œํฌ ์‹คํ–‰ โ€” ํ๊ฐ€ ์ถฉ๋ถ„ํžˆ ์Œ“์ด๋„๋ก ๋Œ€๊ธฐ */
	schedule_delayed_work(&prdev->work, PAGE_REPORTING_DELAY);
}

๋ถ„๊ธฐ ๋กœ์ง:

  • state == REQUESTED โ†’ ์ด๋ฏธ ์˜ˆ์•ฝ๋จ, ์ฆ‰์‹œ ๋ฐ˜ํ™˜
  • state != IDLE (ACTIVE) โ†’ ๋ณด๊ณ  ์ง„ํ–‰ ์ค‘, ์ถ”๊ฐ€ ์˜ˆ์•ฝ ๋ถˆํ•„์š”
  • state == IDLE โ†’ REQUESTED๋กœ ๋ณ€๊ฒฝ ํ›„ 2์ดˆ ์ง€์—ฐ ์›Œํฌ ์˜ˆ์•ฝ
  • 2. `page_reporting_cycle()`

    ํŠน์ • zone์˜ ํŠน์ • order/migratetype free list์—์„œ ๋ณด๊ณ ๋˜์ง€ ์•Š์€ ํŽ˜์ด์ง€๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  ๋“œ๋ผ์ด๋ฒ„์— ์ „๋‹ฌํ•˜๋Š” ํ•ต์‹ฌ ์‚ฌ์ดํด ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

    /* mm/page_reporting.c:145-257 */
    static int
    page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone,
    		     unsigned int order, unsigned int mt,
    		     struct scatterlist *sgl, unsigned int *offset)
    {
    	struct free_area *area = &zone->free_area[order];
    	struct list_head *list = &area->free_list[mt];
    	unsigned int page_len = PAGE_SIZE << order;
    	struct page *page, *next;
    	long budget;
    	int err = 0;
    
    	/* free area๊ฐ€ ๋น„์–ด ์žˆ์œผ๋ฉด ์ฒ˜๋ฆฌํ•  ๊ฒƒ์ด ์—†์œผ๋ฏ€๋กœ ์ด free_list๋Š” ๊ฑด๋„ˆ๋›ด๋‹ค. */
    	if (list_empty(list))
    		return err;
    
    	spin_lock_irq(&zone->lock);
    
    	/* ์˜ˆ์‚ฐ ๊ณ„์‚ฐ: free ํŽ˜์ด์ง€์˜ 1/16 + ์—ฌ์œ ๋ถ„ */
    	budget = DIV_ROUND_UP(area->nr_free, PAGE_REPORTING_CAPACITY * 16);
    
    	/* ๋ณด๊ณ ๋˜์ง€ ์•Š์€ ํŽ˜์ด์ง€๋ฅผ SG list์— ์ถ”๊ฐ€ํ•˜๋ฉฐ free list๋ฅผ ์ˆœํšŒํ•œ๋‹ค. */
    	list_for_each_entry_safe(page, next, list, lru) {
    		/* ์ด๋ฏธ ๋ณด๊ณ ๋œ ํŽ˜์ด์ง€๋Š” ๊ฑด๋„ˆ๋›ด๋‹ค. */
    		if (PageReported(page))
    			continue;
    
    		/* ์˜ˆ์‚ฐ์„ ๋ชจ๋‘ ์†Œ์ง„ํ•˜๋ฉด ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋Š” ์ƒํƒœ๋กœ ๋ฐ”๊พธ๊ณ  ๋น ์ ธ๋‚˜๊ฐ„๋‹ค. */
    		if (budget < 0) {
    			atomic_set(&prdev->state, PAGE_REPORTING_REQUESTED);
    			next = page;
    			break;
    		}
    
    		/* SG list์— ๋„ฃ์„ ๊ณต๊ฐ„์ด ์žˆ์œผ๋ฉด ํŽ˜์ด์ง€๋ฅผ ๊ฒฉ๋ฆฌํ•ด์„œ ๋‹ด๋Š”๋‹ค. */
    		if (*offset) {
    			if (!__isolate_free_page(page, order)) {
    				next = page;
    				break;
    			}
    
    			/* SG list์— ํŽ˜์ด์ง€๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. */
    			--(*offset);
    			sg_set_page(&sgl[*offset], page, page_len, 0);
    			continue;
    		}
    
    		/* ๋ณด๊ณ ๋˜์ง€ ์•Š์€ ์ฒซ ํŽ˜์ด์ง€๋ฅผ free list์˜ ๋จธ๋ฆฌ๋กœ ์˜ฎ๊ธด ๋’ค zone lock์„ ํ•ด์ œํ•œ๋‹ค. */
    		if (!list_is_first(&page->lru, list))
    			list_rotate_to_front(&page->lru, list);
    
    		/* ๋ณด๊ณ  ์ฒ˜๋ฆฌ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ธฐ ์ „์— lock์„ ํ•ด์ œํ•œ๋‹ค. */
    		spin_unlock_irq(&zone->lock);
    
    		/* ๋กœ์ปฌ ๋ฆฌ์ŠคํŠธ์— ์žˆ๋Š” ํŽ˜์ด์ง€๋“ค์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์‹œ์ž‘ํ•œ๋‹ค. */
    		err = prdev->report(prdev, sgl, PAGE_REPORTING_CAPACITY);
    
    		/* ์ „์ฒด ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ณด๊ณ ํ–ˆ์œผ๋ฏ€๋กœ offset์„ ์žฌ์„ค์ •ํ•œ๋‹ค. */
    		*offset = PAGE_REPORTING_CAPACITY;
    
    		/* report ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ๋ฐ˜์˜ํ•ด budget์„ ๊ฐฑ์‹ ํ•œ๋‹ค. */
    		budget--;
    
    		/* zone lock์„ ๋‹ค์‹œ ์žก๊ณ  ์ฒ˜๋ฆฌ๋ฅผ ์ด์–ด๊ฐ„๋‹ค. */
    		spin_lock_irq(&zone->lock);
    
    		/* SG list์—์„œ ๋ณด๊ณ ๋œ ํŽ˜์ด์ง€๋ฅผ ๋น„์šด๋‹ค. */
    		page_reporting_drain(prdev, sgl, PAGE_REPORTING_CAPACITY, !err);
    
    		/* lock์„ ๋†“๋Š” ๋™์•ˆ ์ด์ „ next๋Š” ์œ ํšจํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ next๋ฅผ free list์˜ ์ฒซ ์—”ํŠธ๋ฆฌ๋กœ ๋‹ค์‹œ ๋งž์ถ˜๋‹ค. */
    		next = list_first_entry(list, struct page, lru);
    
    		/* ์—๋Ÿฌ๊ฐ€ ์žˆ์œผ๋ฉด ์ค‘๋‹จํ•œ๋‹ค. */
    		if (err)
    			break;
    	}
    
    	/* ๋‚จ์€ ํŽ˜์ด์ง€๊ฐ€ ์žˆ์œผ๋ฉด free list์˜ ๋จธ๋ฆฌ๋กœ ๋Œ๋ฆฐ๋‹ค. */
    	if (!list_entry_is_head(next, list, lru) && !list_is_first(&next->lru, list))
    		list_rotate_to_front(&next->lru, list);
    
    	spin_unlock_irq(&zone->lock);
    
    	return err;
    }

    ๋ถ„๊ธฐ ๋กœ์ง:

  • PageReported(page) โ†’ ์ด๋ฏธ ๋ณด๊ณ ๋จ, ๊ฑด๋„ˆ๋œ€
  • budget < 0 โ†’ ํ•ด๋‹น free list ์ฒ˜๋ฆฌ ์˜ˆ์‚ฐ ์ดˆ๊ณผ, ์ƒํƒœ๋ฅผ REQUESTED๋กœ ๋ณต๊ท€
  • *offset > 0 โ†’ SG list์— ๊ณต๊ฐ„ ์žˆ์Œ, __isolate_free_page()๋กœ ํŽ˜์ด์ง€ ๊ฒฉ๋ฆฌ ํ›„ SG list์— ์ถ”๊ฐ€
  • *offset == 0 โ†’ SG list ๊ฐ€๋“ ์ฐธ, lock ํ•ด์ œ ํ›„ report() ์ฝœ๋ฐฑ ํ˜ธ์ถœ
  • 3. `page_reporting_process_zone()`

    ๋‹จ์ผ zone์„ ๋Œ€์ƒ์œผ๋กœ ๋ชจ๋“  order/migratetype ์กฐํ•ฉ์— ๋Œ€ํ•ด ๋ณด๊ณ  ์‚ฌ์ดํด์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

    /* mm/page_reporting.c:259-305 */
    static int
    page_reporting_process_zone(struct page_reporting_dev_info *prdev,
    			    struct scatterlist *sgl, struct zone *zone)
    {
    	unsigned int order, mt, leftover, offset = PAGE_REPORTING_CAPACITY;
    	unsigned long watermark;
    	int err = 0;
    
    	/* ๋ณด๊ณ ๊ฐ€ ์ง„ํ–‰๋  ์ˆ˜ ์žˆ๋„๋ก ์ตœ์†Œ watermark๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค. */
    	watermark = low_wmark_pages(zone) +
    		    (PAGE_REPORTING_CAPACITY << page_reporting_order);
    
    	/* ์ž์œ  ํŽ˜์ด์ง€๊ฐ€ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์œผ๋ฉด ์š”์ฒญ์„ ์ทจ์†Œํ•œ๋‹ค. */
    	if (!zone_watermark_ok(zone, 0, watermark, 0, ALLOC_CMA))
    		return err;
    
    	/* ๋ชจ๋“  order/migratetype ์กฐํ•ฉ์„ ์ˆœํšŒํ•œ๋‹ค. */
    	for (order = page_reporting_order; order < NR_PAGE_ORDERS; order++) {
    		for (mt = 0; mt < MIGRATE_TYPES; mt++) {
    			/* isolate free list๋Š” ๊ฑด๋„ˆ๋›ด๋‹ค. */
    			if (is_migrate_isolate(mt))
    				continue;
    			err = page_reporting_cycle(prdev, zone, order, mt,
    						   sgl, &offset);
    			if (err)
    				return err;
    		}
    	}
    
    	/* ๋‚จ์€ ํŽ˜์ด์ง€๋ฅผ ๋ณด๊ณ ํ•œ๋‹ค. */
    	leftover = PAGE_REPORTING_CAPACITY - offset;
    	if (leftover) {
    		sgl = &sgl[offset];
    		err = prdev->report(prdev, sgl, leftover);
    
    		/* ๋‚จ์€ ํŽ˜์ด์ง€๋ฅผ ๋ชจ๋‘ ๋ณด๊ณ ํ•œ ๋’ค free list๋กœ ๋Œ๋ ค๋ณด๋‚ธ๋‹ค. */
    		spin_lock_irq(&zone->lock);
    		page_reporting_drain(prdev, sgl, leftover, !err);
    		spin_unlock_irq(&zone->lock);
    	}
    	return err;
    }

    4. `page_reporting_process()`

    ์ง€์—ฐ ์›Œํฌ ํ•ธ๋“ค๋Ÿฌ โ€” ๋ชจ๋“  zone์„ ์ˆœํšŒํ•˜๋ฉฐ ๋ณด๊ณ  ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

    /* mm/page_reporting.c:307-347 */
    static void page_reporting_process(struct work_struct *work)
    {
    	struct delayed_work *d_work = to_delayed_work(work);
    	struct page_reporting_dev_info *prdev =
    		container_of(d_work, struct page_reporting_dev_info, work);
    	int err = 0, state = PAGE_REPORTING_ACTIVE;
    
    	/* ์ƒํƒœ๋ฅผ ACTIVE๋กœ ๋ณ€๊ฒฝ โ€” ๋ณด๊ณ  ์ง„ํ–‰ ์ค‘์ž„์„ ํ‘œ์‹œ */
    	atomic_set(&prdev->state, state);
    
    	/* SG list ํ• ๋‹น */
    	sgl = kmalloc_objs(*sgl, PAGE_REPORTING_CAPACITY);
    	if (!sgl)
    		goto err_out;
    	sg_init_table(sgl, PAGE_REPORTING_CAPACITY);
    
    	/* ๋ชจ๋“  zone ์ˆœํšŒ */
    	for_each_zone(zone) {
    		err = page_reporting_process_zone(prdev, sgl, zone);
    		if (err)
    			break;
    	}
    
    	kfree(sgl);
    err_out:
    	/* IDLE๋กœ ๋ณต๊ท€ ์‹œ๋„ โ€” ๊ทธ ์‚ฌ์ด ์ƒˆ ์š”์ฒญ์ด ๋“ค์–ด์™”์œผ๋ฉด REQUESTED ์œ ์ง€ */
    	state = atomic_cmpxchg(&prdev->state, state, PAGE_REPORTING_IDLE);
    	if (state == PAGE_REPORTING_REQUESTED)
    		schedule_delayed_work(&prdev->work, PAGE_REPORTING_DELAY);
    }

    ๋ถ„๊ธฐ ๋กœ์ง:

  • atomic_cmpxchg๋กœ IDLE ๋ณต๊ท€ ์‹œ๋„
  • ๋ณด๊ณ  ์ง„ํ–‰ ์ค‘ ์ƒˆ๋กœ์šด ์š”์ฒญ์ด ๋“ค์–ด์™€ state๊ฐ€ REQUESTED๋กœ ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ โ†’ ์ถ”๊ฐ€ 2์ดˆ ์ง€์—ฐ ํ›„ ์žฌ์‹คํ–‰
  • 5. `page_reporting_register()` / `page_reporting_unregister()`

    ๋“œ๋ผ์ด๋ฒ„์˜ ๋“ฑ๋ก/ํ•ด์ œ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

    /* mm/page_reporting.c:352-417 (ํ•ต์‹ฌ) */
    int page_reporting_register(struct page_reporting_dev_info *prdev)
    {
    	mutex_lock(&page_reporting_mutex);
    
    	/* ์ด๋ฏธ ๋“ฑ๋ก๋œ ์žฅ์น˜๊ฐ€ ์žˆ์œผ๋ฉด -EBUSY */
    	if (rcu_dereference_protected(pr_dev_info,
    				lockdep_is_held(&page_reporting_mutex))) {
    		err = -EBUSY;
    		goto err_out;
    	}
    
    	/* order ๊ฒฐ์ •: ํŒŒ๋ผ๋ฏธํ„ฐ โ†’ ๋“œ๋ผ์ด๋ฒ„ โ†’ pageblock_order ์ˆœ */
    	if (page_reporting_order == -1) {
    		if (prdev->order > 0 && prdev->order <= MAX_PAGE_ORDER)
    			page_reporting_order = prdev->order;
    		else
    			page_reporting_order = pageblock_order;
    	}
    
    	/* ์ƒํƒœ ๋ฐ ์›Œํฌ ์ดˆ๊ธฐํ™” */
    	atomic_set(&prdev->state, PAGE_REPORTING_IDLE);
    	INIT_DELAYED_WORK(&prdev->work, &page_reporting_process);
    
    	/* ์ดˆ๊ธฐ ๋ณด๊ณ  ์š”์ฒญ */
    	__page_reporting_request(prdev);
    
    	/* RCU๋กœ ์ „์—ญ ํฌ์ธํ„ฐ ํ• ๋‹น */
    	rcu_assign_pointer(pr_dev_info, prdev);
    
    	/* static key ํ™œ์„ฑํ™” โ€” ํ•ซ ๊ฒฝ๋กœ ์ฒดํฌ ํ™œ์„ฑํ™” */
    	if (!static_key_enabled(&page_reporting_enabled)) {
    		static_branch_enable(&page_reporting_enabled);
    		pr_info("Free page reporting enabled\n");
    	}
    
    	mutex_unlock(&page_reporting_mutex);
    	return err;
    }
    
    void page_reporting_unregister(struct page_reporting_dev_info *prdev)
    {
    	mutex_lock(&page_reporting_mutex);
    
    	if (prdev == rcu_dereference_protected(pr_dev_info,
    				lockdep_is_held(&page_reporting_mutex))) {
    		/* RCU ํ•ด์ œ ํ›„ ๋™๊ธฐํ™” */
    		RCU_INIT_POINTER(pr_dev_info, NULL);
    		synchronize_rcu();
    		/* ์ง€์—ฐ๋œ ์ž‘์—… ์ทจ์†Œ ๋ฐ ๋™๊ธฐ ๋Œ€๊ธฐ */
    		cancel_delayed_work_sync(&prdev->work);
    	}
    
    	mutex_unlock(&page_reporting_mutex);
    }

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

    Free Page Reporting ํ˜ธ์ถœ ํ๋ฆ„
    Buddy: __free_one_page()
    โ””โ”€ page_reporting_notify_free(order)           [mm/page_reporting.h:33]
    โ””โ”€ __page_reporting_notify()              [mm/page_reporting.c:87]
    โ””โ”€ __page_reporting_request(prdev)   [mm/page_reporting.c:60]
    โ””โ”€ schedule_delayed_work()      [2์ดˆ ์ง€์—ฐ ์˜ˆ์•ฝ]
    โ””โ”€ page_reporting_process() [mm/page_reporting.c:307]
    โ””โ”€ page_reporting_process_zone() [mm/page_reporting.c:259]
    โ”œโ”€ zone_watermark_ok() ์ฒดํฌ
    โ””โ”€ page_reporting_cycle()   [mm/page_reporting.c:145]
    โ”œโ”€ __isolate_free_page() โ†’ SG list ์ˆ˜์ง‘
    โ”œโ”€ prdev->report()       โ†’ ๋“œ๋ผ์ด๋ฒ„ ์ฝœ๋ฐฑ
    โ””โ”€ page_reporting_drain() โ†’ free list ๋ฐ˜ํ™˜

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

    ๋“œ๋ผ์ด๋ฒ„๋ณ„ report ์ฝœ๋ฐฑ ๋น„๊ต

    ํ•ญ๋ชฉVirtio BalloonHyper-V Balloon
    ์†Œ์Šค ํŒŒ์ผ`drivers/virtio/virtio_balloon.c``drivers/hv/hv_balloon.c`
    ์ฝœ๋ฐฑ ํ•จ์ˆ˜`virtballoon_free_page_report()``hv_free_page_report()`
    ๋“ฑ๋ก ์‹œ orderARM64_64K: 5, ๊ธฐ๋ณธ: ๋ฏธ์„ค์ •0 (ํŒŒ๋ผ๋ฏธํ„ฐ ์‚ฌ์šฉ)
    ๋™์ž‘ํŽ˜์ด์ง€๋ฅผ VIRTIO_BALLOON_S_GUEST_MEM ํ•€์œผ๋กœ ์•Œ๋ฆผHyper-V Hypercall๋กœ GPA ๋ฒ”์œ„ ์ „์†ก
    ์ตœ๋Œ€ ์šฉ๋Ÿ‰`PAGE_REPORTING_CAPACITY` (32)`HV_MEMORY_HINT_MAX_GPA_PAGE_RANGES`

    ์ƒํƒœ ์ „์ด ๋น„๊ต

    ํ˜„์žฌ ์ƒํƒœ์ด๋ฒคํŠธ๋‹ค์Œ ์ƒํƒœ๋™์ž‘
    IDLE`__page_reporting_request()` ํ˜ธ์ถœREQUESTED2์ดˆ ์ง€์—ฐ ์›Œํฌ ์˜ˆ์•ฝ
    REQUESTED`__page_reporting_request()` ํ˜ธ์ถœREQUESTED (์œ ์ง€)์ค‘๋ณต ์˜ˆ์•ฝ ๋ฐฉ์ง€
    ACTIVE๋ณด๊ณ  ์‚ฌ์ดํด ์™„๋ฃŒ + ์š”์ฒญ ์—†์ŒIDLE์›Œํฌ ํ ์ •์ง€
    ACTIVE๋ณด๊ณ  ์‚ฌ์ดํด ์™„๋ฃŒ + ์ƒˆ๋กœ์šด ์š”์ฒญREQUESTED2์ดˆ ํ›„ ์žฌ์‹คํ–‰
    ACTIVE`__page_reporting_request()` ํ˜ธ์ถœACTIVE (์œ ์ง€)์ด๋ฏธ ์ง„ํ–‰ ์ค‘

    free list ์ˆœํšŒ ๋Œ€์ƒ ๋น„๊ต

    ์กฐ๊ฑด์ฒ˜๋ฆฌ ์—ฌ๋ถ€์„ค๋ช…
    `PageReported(page)`๊ฑด๋„ˆ๋œ€์ด๋ฏธ ํ˜ธ์ŠคํŠธ์— ๋ณด๊ณ ๋œ ํŽ˜์ด์ง€
    `is_migrate_isolate(mt)`๊ฑด๋„ˆ๋œ€๊ฒฉ๋ฆฌ๋œ migratetype (Compaction ์‚ฌ์šฉ)
    `order < page_reporting_order`ํ•ด๋‹น ์—†์Œ์ตœ์†Œ order ๋ฏธ๋งŒ์€ ๋ณด๊ณ  ๋Œ€์ƒ ์•„๋‹˜
    `!zone_watermark_ok()`ํ•ด๋‹น zone ๊ฑด๋„ˆ๋œ€๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์‹œ ๋ณด๊ณ  ๋ถˆ๊ฐ€
    `budget < 0`์ˆœํšŒ ์ค‘๋‹จfree list ์˜ˆ์‚ฐ ์ดˆ๊ณผ

    ๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • Buddy Allocator
  • VMA / mmap
  • Virtio Balloon
  • DMA Pool