๏ปฟ# 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์ ์๋ฆฝ๋๋ค.
/* 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 ๋ณต๊ท ์๋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);
}
ํธ์ถ ํ๋ฆ
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 Balloon | Hyper-V Balloon |
|---|---|---|
| ์์ค ํ์ผ | `drivers/virtio/virtio_balloon.c` | `drivers/hv/hv_balloon.c` |
| ์ฝ๋ฐฑ ํจ์ | `virtballoon_free_page_report()` | `hv_free_page_report()` |
| ๋ฑ๋ก ์ order | ARM64_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()` ํธ์ถ | REQUESTED | 2์ด ์ง์ฐ ์ํฌ ์์ฝ |
| REQUESTED | `__page_reporting_request()` ํธ์ถ | REQUESTED (์ ์ง) | ์ค๋ณต ์์ฝ ๋ฐฉ์ง |
| ACTIVE | ๋ณด๊ณ ์ฌ์ดํด ์๋ฃ + ์์ฒญ ์์ | IDLE | ์ํฌ ํ ์ ์ง |
| ACTIVE | ๋ณด๊ณ ์ฌ์ดํด ์๋ฃ + ์๋ก์ด ์์ฒญ | REQUESTED | 2์ด ํ ์ฌ์คํ |
| 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 ์์ฐ ์ด๊ณผ |