๏ปฟ# Virtio Balloon

๊ด€๋ จ ์†Œ์Šค: mm/balloon.c, drivers/virtio/virtio_balloon.c, include/linux/balloon.h, include/uapi/linux/virtio_balloon.h

๊ฐœ์š” (Overview)

Virtio Balloon์€ ๊ฐ€์ƒ๋จธ์‹ ์—์„œ ํ˜ธ์ŠคํŠธ(ํ•˜์ดํผ๋ฐ”์ด์ €)์™€ ๊ฒŒ์ŠคํŠธ ๊ฐ„์— ๋™์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค. ๊ฒŒ์ŠคํŠธ OS๋Š” "balloon"์„ inflateํ•˜์—ฌ ์‚ฌ์šฉ ์ค‘์ธ ํŽ˜์ด์ง€๋ฅผ ํ˜ธ์ŠคํŠธ์—๊ฒŒ ๋ฐ˜ํ™˜ํ•˜๊ณ , deflateํ•˜์—ฌ ํ˜ธ์ŠคํŠธ๋กœ๋ถ€ํ„ฐ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์—ฌ๋Ÿฌ ๊ฐ€์ƒ๋จธ์‹  ๊ฐ„ ๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฒ„์ปค๋ฐ‹์ด ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค.

์ปค๋„์€ mm/balloon.c์— ๊ณต์šฉ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ, drivers/virtio/virtio_balloon.c์— Virtio ๋“œ๋ผ์ด๋ฒ„ ๊ตฌํ˜„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ณต์šฉ ์ธํ„ฐํŽ˜์ด์Šค๋Š” ํŽ˜์ด์ง€ ํ• ๋‹น, enqueue/dequeue, ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ๋‹ด๋‹นํ•˜๋ฉฐ, ๋“œ๋ผ์ด๋ฒ„ ์ธก์€ virtqueue๋ฅผ ํ†ตํ•ด ํ˜ธ์ŠคํŠธ์™€ ํ†ต์‹ ํ•ฉ๋‹ˆ๋‹ค. Linux 7.0์—์„œ๋Š” Free Page Hint, Free Page Reporting, OOM ์‹œ ์ž๋™ deflate ๋“ฑ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

Virtio Balloon ์ž๋ฃŒ๊ตฌ์กฐ ๊ด€๊ณ„๋„
Virtio Balloon ํ˜ธ์ถœ ํ๋ฆ„๋„

์†Œ์Šค ํŒŒ์ผ ๊ฒฝ๋กœ

mm/balloon.c                              โ† ๊ณต์šฉ balloon ์ธํ„ฐํŽ˜์ด์Šค
drivers/virtio/virtio_balloon.c           โ† Virtio balloon ๋“œ๋ผ์ด๋ฒ„
include/linux/balloon.h                   โ† balloon_dev_info ์ •์˜
include/uapi/linux/virtio_balloon.h       โ† feature ํ”Œ๋ž˜๊ทธ, ํ†ต๊ณ„ ์ •์˜

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

# balloon ํŽ˜์ด์ง€ ์ˆ˜ ํ™•์ธ
cat /proc/ballooninfo

# Virtio balloon ๋ชจ๋“ˆ ์ƒํƒœ ํ™•์ธ
lsmod | grep virtio_balloon

# virtio ๋””๋ฐ”์ด์Šค ์ •๋ณด ํ™•์ธ
ls /sys/class/misc/balloon*

# ๊ฒŒ์ŠคํŠธ ๋ฉ”๋ชจ๋ฆฌ ํ†ต๊ณ„ (balloon ํ†ต๊ณ„)
dmesg | grep -i balloon

# Free Page Reporting ์ƒํƒœ ํ™•์ธ
cat /sys/kernel/mm/page_reporting/reporting

# OOM ์•Œ๋ฆผ ์„ค์ • ํ™•์ธ (deflate_on_oom)
dmesg | grep "deflate on oom"

# ๋ฉ”๋ชจ๋ฆฌ ์••๋ ฅ ํ™•์ธ (balloon inflate/deflate ํŠธ๋ฆฌ๊ฑฐ)
grep -i balloon /proc/vmstat

# balloon ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ™œ์„ฑํ™” ํ™•์ธ
grep BALLOON /proc/vmstat

# virtio ๋””๋ฐ”์ด์Šค ๋“œ๋ผ์ด๋ฒ„ ์ •๋ณด
lspci -v | grep -i virtio

# ๊ฒŒ์ŠคํŠธ์—์„œ ํ˜ธ์ŠคํŠธ ํ†ต์‹  ์ƒํƒœ (virtqueue)
dmesg | grep "virtio.*balloon"

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

struct balloon_dev_info

include/linux/balloon.h:53 โ€” ๋ชจ๋“  balloon ๋””๋ฐ”์ด์Šค์˜ ๊ณต์šฉ ํŽ˜์ด์ง€ ๊ด€๋ฆฌ ๊ตฌ์กฐ์ฒด

struct balloon_dev_info {
unsigned long isolated_pages;    /* ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์œ„ํ•ด ๊ฒฉ๋ฆฌ๋œ ํŽ˜์ด์ง€ ์ˆ˜ */
struct list_head pages;           /* ํ˜ธ์ŠคํŠธ์—๊ฒŒ ํ• ๋‹น๋œ ํŽ˜์ด์ง€ ๋ชฉ๋ก (LRU) */
int (*migratepage)(struct balloon_dev_info *, struct page *newpage,
struct page *page, enum migrate_mode mode);  /* ํŽ˜์ด์ง€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ฝœ๋ฐฑ */
bool adjust_managed_page_count;  /* managed_page_count ์กฐ์ • ์—ฌ๋ถ€ */
};
  • isolated_pages: Compaction ์Šค๋ ˆ๋“œ๊ฐ€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์œ„ํ•ด ๊ฒฉ๋ฆฌํ•œ balloon ํŽ˜์ด์ง€ ์ˆ˜
  • pages: balloon_page_insert()๋กœ ์ถ”๊ฐ€๋˜๋Š” ํŽ˜์ด์ง€๋“ค์˜ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ
  • migratepage: ๋“œ๋ผ์ด๋ฒ„๋ณ„ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ฝœ๋ฐฑ (virtio๋Š” virtballoon_migratepage)
  • adjust_managed_page_count: OOM deflate ์‹œ adjust_managed_page_count ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ์นด์šดํ„ฐ๋ฅผ ์กฐ์ •
  • struct virtio_balloon

    drivers/virtio/virtio_balloon.c:55 โ€” Virtio balloon ๋“œ๋ผ์ด๋ฒ„์˜ ํ•ต์‹ฌ ๊ตฌ์กฐ์ฒด

    struct virtio_balloon {
    struct virtio_device *vdev;
    struct virtqueue *inflate_vq, *deflate_vq, *stats_vq, *free_page_vq;
    
    struct workqueue_struct *balloon_wq;        /* Free Page Hint ์ „์šฉ ์›Œํฌํ */
    struct work_struct report_free_page_work;    /* Free Page Reporting ์ž‘์—… */
    struct work_struct update_balloon_stats_work; /* ํ†ต๊ณ„ ์—…๋ฐ์ดํŠธ ์ž‘์—… */
    struct work_struct update_balloon_size_work;  /* ํฌ๊ธฐ ์กฐ์ • ์ž‘์—… */
    
    spinlock_t stop_update_lock;
    bool stop_update;
    unsigned long config_read_bitmap;
    
    struct list_head free_page_list;     /* Free Page Hint์šฉ ํ• ๋‹น๋œ ํŽ˜์ด์ง€ ๋ชฉ๋ก */
    spinlock_t free_page_list_lock;
    unsigned long num_free_page_blocks;  /* free_page_list์˜ ๋ธ”๋ก ์ˆ˜ */
    
    u32 cmd_id_received_cache;   /* ํ˜ธ์ŠคํŠธ๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ cmd_id ์บ์‹œ */
    __virtio32 cmd_id_active;    /* ํ˜„์žฌ ํ™œ์„ฑํ™”๋œ cmd_id */
    __virtio32 cmd_id_stop;      /* ์ •์ง€ ์‹ ํ˜ธ cmd_id */
    
    wait_queue_head_t acked;     /* ํ˜ธ์ŠคํŠธ ack ๋Œ€๊ธฐ ํ */
    unsigned int num_pages;      /* ํ˜„์žฌ balloon์— ์žˆ๋Š” ํŽ˜์ด์ง€ ์ˆ˜ */
    struct balloon_dev_info vb_dev_info;  /* ๊ณต์šฉ balloon ๋””๋ฐ”์ด์Šค ์ •๋ณด */
    
    struct mutex balloon_lock;   /* inflate/deflate ๋™๊ธฐํ™” ๋ฎคํ…์Šค */
    unsigned int num_pfns;       /* ํ˜ธ์ŠคํŠธ์— ์•Œ๋ ค์ค„ PFN ๋ฐฐ์—ด ๊ธธ์ด */
    __virtio32 pfns[VIRTIO_BALLOON_ARRAY_PFNS_MAX];  /* PFN ๋ฐฐ์—ด (์ตœ๋Œ€ 256๊ฐœ) */
    
    struct virtio_balloon_stat stats[VIRTIO_BALLOON_S_NR]; /* ๋ฉ”๋ชจ๋ฆฌ ํ†ต๊ณ„ */
    
    struct shrinker *shrinker;           /* Free Page Hint์šฉ shrinker */
    struct notifier_block oom_nb;        /* OOM ์•Œ๋ฆผ ๋ธ”๋ก */
    
    struct virtqueue *reporting_vq;      /* Free Page Reporting virtqueue */
    struct page_reporting_dev_info pr_dev_info;  /* page reporting ๋””๋ฐ”์ด์Šค ์ •๋ณด */
    
    spinlock_t wakeup_lock;
    bool processing_wakeup_event;
    u32 wakeup_signal_mask;
    };

    virtio_balloon_config (ํ˜ธ์ŠคํŠธ โ†” ๊ฒŒ์ŠคํŠธ ๊ณต์œ )

    include/uapi/linux/virtio_balloon.h:46 โ€” ํ˜ธ์ŠคํŠธ์™€ ๊ฒŒ์ŠคํŠธ๊ฐ€ ๊ณต์œ ํ•˜๋Š” ์„ค์ • ๊ตฌ์กฐ์ฒด

    struct virtio_balloon_config {
    __le32 num_pages;  /* ํ˜ธ์ŠคํŠธ๊ฐ€ ๊ฒŒ์ŠคํŠธ์—๊ฒŒ ๋ฐ˜ํ™˜์„ ์š”์ฒญํ•œ ํŽ˜์ด์ง€ ์ˆ˜ */
    __le32 actual;     /* ๊ฒŒ์ŠคํŠธ๊ฐ€ ์‹ค์ œ๋กœ balloon์— ๋„ฃ์€ ํŽ˜์ด์ง€ ์ˆ˜ */
    union {
    __le32 free_page_hint_cmd_id;     /* Free Page Hint ๋ช…๋ น ID */
    __le32 free_page_report_cmd_id;   /* deprecated ๋ณ„์นญ */
    };
    __le32 poison_val;  /* PAGE_POISON ๊ฐ’ */
    };

    virtio_balloon_stat (๋ฉ”๋ชจ๋ฆฌ ํ†ต๊ณ„)

    include/uapi/linux/virtio_balloon.h:126 โ€” ํ˜ธ์ŠคํŠธ์—๊ฒŒ ์ „์†ก๋˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ํ†ต๊ณ„ ๊ตฌ์กฐ์ฒด

    struct virtio_balloon_stat {
    __virtio16 tag;  /* ํ†ต๊ณ„ ํƒ€์ž… (VIRTIO_BALLOON_S_* ์ƒ์ˆ˜) */
    __virtio64 val;  /* ํ†ต๊ณ„ ๊ฐ’ */
    } __attribute__((packed));

    virtio_balloon_feature ํ”Œ๋ž˜๊ทธ

    include/uapi/linux/virtio_balloon.h:34 โ€” ์ง€์› ๊ธฐ๋Šฅ ๋น„ํŠธ๋งต

    #define VIRTIO_BALLOON_F_MUST_TELL_HOST  0  /* ํŽ˜์ด์ง€ ๋ฐ˜ํ™˜ ์ „ ํ˜ธ์ŠคํŠธ์—๊ฒŒ ์•Œ๋ฆผ ํ•„์š” */
    #define VIRTIO_BALLOON_F_STATS_VQ        1  /* ๋ฉ”๋ชจ๋ฆฌ ํ†ต๊ณ„ virtqueue ์ง€์› */
    #define VIRTIO_BALLOON_F_DEFLATE_ON_OOM   2  /* OOM ๋ฐœ์ƒ ์‹œ ์ž๋™ balloon ํ•ด์ œ */
    #define VIRTIO_BALLOON_F_FREE_PAGE_HINT   3  /* Free Page Hint virtqueue ์ง€์› */
    #define VIRTIO_BALLOON_F_PAGE_POISON      4  /* ํŽ˜์ด์ง€ ํฌ์ด์ฆˆ๋‹ ์‚ฌ์šฉ */
    #define VIRTIO_BALLOON_F_REPORTING        5  /* Free Page Reporting virtqueue ์ง€์› */

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

    1. balloon_page_alloc

    mm/balloon.c:147 โ€” balloon์— ์‚ฌ์šฉํ•  ์ƒˆ ํŽ˜์ด์ง€๋ฅผ ํ• ๋‹น

    struct page *balloon_page_alloc(void)
    {
    gfp_t gfp_flags = __GFP_NOMEMALLOC | __GFP_NORETRY | __GFP_NOWARN;
    
    if (IS_ENABLED(CONFIG_BALLOON_MIGRATION))
    gfp_flags |= GFP_HIGHUSER_MOVABLE;  /* ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฐ€๋Šฅํ•œ ๊ณ ์œ  ์‚ฌ์šฉ์ž ์˜์—ญ */
    else
    gfp_flags |= GFP_HIGHUSER;          /* ์ผ๋ฐ˜ ๊ณ ์œ  ์‚ฌ์šฉ์ž ์˜์—ญ */
    
    return alloc_page(gfp_flags);
    }
  • ์—ญํ• : Buddy Allocator์—์„œ ํŽ˜์ด์ง€๋ฅผ ํ• ๋‹นํ•˜๋˜, balloon์ด๋ฏ€๋กœ ์‹คํŒจํ•ด๋„ ์žฌ์‹œ๋„ํ•˜์ง€ ์•Š์Œ
  • ๋ถ„๊ธฐ ๋กœ์ง: CONFIG_BALLOON_MIGRATION์ด ํ™œ์„ฑํ™”๋˜๋ฉด GFP_HIGHUSER_MOVABLE๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŽ˜์ด์ง€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฐ€๋Šฅ
  • 2. balloon_page_list_enqueue

    mm/balloon.c:75 โ€” ํŽ˜์ด์ง€ ๋ชฉ๋ก์„ balloon์— ์ถ”๊ฐ€

    size_t balloon_page_list_enqueue(struct balloon_dev_info *b_dev_info,
    struct list_head *pages)
    {
    struct page *page, *tmp;
    unsigned long flags;
    size_t n_pages = 0;
    
    spin_lock_irqsave(&balloon_pages_lock, flags);
    list_for_each_entry_safe(page, tmp, pages, lru) {
    list_del(&page->lru);
    balloon_page_enqueue_one(b_dev_info, page);  /* ํŽ˜์ด์ง€ ์‚ฝ์ž… + ์นด์šดํ„ฐ ์ฆ๊ฐ€ */
    n_pages++;
    }
    spin_unlock_irqrestore(&balloon_pages_lock, flags);
    return n_pages;
    }
  • ์—ญํ• : ํ• ๋‹น๋œ ํŽ˜์ด์ง€๋“ค์„ balloon_dev_info->pages ๋ชฉ๋ก์— ์ถ”๊ฐ€
  • ๋ถ„๊ธฐ ๋กœ์ง: balloon_page_enqueue_one์—์„œ adjust_managed_page_count ํ”Œ๋ž˜๊ทธ์— ๋”ฐ๋ผ managed page count๋ฅผ ์กฐ์ •ํ•˜๊ณ , NR_BALLOON_PAGES ๋…ธ๋“œ ์ƒํƒœ๋ฅผ ์ฆ๊ฐ€์‹œํ‚ด
  • 3. balloon_page_list_dequeue

    mm/balloon.c:111 โ€” balloon์—์„œ ํŽ˜์ด์ง€๋ฅผ ์ œ๊ฑฐํ•˜์—ฌ ๋ฐ˜ํ™˜

    size_t balloon_page_list_dequeue(struct balloon_dev_info *b_dev_info,
    struct list_head *pages, size_t n_req_pages)
    {
    struct page *page, *tmp;
    unsigned long flags;
    size_t n_pages = 0;
    
    spin_lock_irqsave(&balloon_pages_lock, flags);
    list_for_each_entry_safe(page, tmp, &b_dev_info->pages, lru) {
    if (n_pages == n_req_pages)
    break;
    list_del(&page->lru);
    if (b_dev_info->adjust_managed_page_count)
    adjust_managed_page_count(page, 1);   /* managed count ๋ณต์› */
    balloon_page_finalize(page);               /* ํŽ˜์ด์ง€ ์ •๋ฆฌ */
    __count_vm_event(BALLOON_DEFLATE);
    list_add(&page->lru, pages);
    dec_node_page_state(page, NR_BALLOON_PAGES);
    n_pages++;
    }
    spin_unlock_irqrestore(&balloon_pages_lock, flags);
    return n_pages;
    }
  • ์—ญํ• : balloon ๋ชฉ๋ก์—์„œ ์š”์ฒญ๋œ ์ˆ˜๋งŒํผ ํŽ˜์ด์ง€๋ฅผ ๊บผ๋‚ด ๋ฐ˜ํ™˜
  • ๋ถ„๊ธฐ ๋กœ์ง: compaction์œผ๋กœ ์ธํ•ด ๊ฒฉ๋ฆฌ๋œ ํŽ˜์ด์ง€๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์–ด ์š”์ฒญ ์ˆ˜๋ณด๋‹ค ์ ๊ฒŒ ๋ฐ˜ํ™˜๋  ์ˆ˜ ์žˆ์Œ
  • 4. fill_balloon (virtio)

    drivers/virtio/virtio_balloon.c:242 โ€” ํ˜ธ์ŠคํŠธ์—๊ฒŒ inflate ์š”์ฒญ (๊ฒŒ์ŠคํŠธ โ†’ ํ˜ธ์ŠคํŠธ)

    static unsigned int fill_balloon(struct virtio_balloon *vb, size_t num)
    {
    unsigned int num_allocated_pages;
    struct page *page, *next;
    unsigned int num_pfns;
    LIST_HEAD(pages);
    
    num = min(num, ARRAY_SIZE(vb->pfns));  /* ํ•œ ๋ฒˆ์— ์ตœ๋Œ€ 256๊ฐœ */
    
    for (num_pfns = 0; num_pfns < num;
    num_pfns += VIRTIO_BALLOON_PAGES_PER_PAGE) {
    page = balloon_page_alloc();
    if (!page) {
    msleep(200);  /* ํ• ๋‹น ์‹คํŒจ ์‹œ 200ms ๋Œ€๊ธฐ ํ›„ ์žฌ์‹œ๋„ */
    break;
    }
    list_add(&page->lru, &pages);
    }
    
    mutex_lock(&vb->balloon_lock);
    vb->num_pfns = 0;
    list_for_each_entry_safe(page, next, &pages, lru) {
    list_del(&page->lru);
    balloon_page_enqueue(&vb->vb_dev_info, page);  /* balloon ๋ชฉ๋ก์— ์ถ”๊ฐ€ */
    set_page_pfns(vb, vb->pfns + vb->num_pfns, page);
    vb->num_pages += VIRTIO_BALLOON_PAGES_PER_PAGE;
    vb->num_pfns += VIRTIO_BALLOON_PAGES_PER_PAGE;
    }
    
    num_allocated_pages = vb->num_pfns;
    if (vb->num_pfns != 0)
    tell_host(vb, vb->inflate_vq);  /* ํ˜ธ์ŠคํŠธ์—๊ฒŒ PFN ๋ชฉ๋ก ์ „์†ก */
    mutex_unlock(&vb->balloon_lock);
    return num_allocated_pages;
    }
  • ์—ญํ• : ํ˜ธ์ŠคํŠธ์—๊ฒŒ ๋ฐ˜ํ™˜ํ•  ํŽ˜์ด์ง€๋ฅผ ํ• ๋‹นํ•˜๊ณ  PFN ๋ชฉ๋ก์„ virtqueue๋กœ ์ „์†ก
  • ๋ถ„๊ธฐ ๋กœ์ง: ํŽ˜์ด์ง€ ํ• ๋‹น ์‹คํŒจ ์‹œ 200ms ๋Œ€๊ธฐ ํ›„ ๋ถ€๋ถ„ ํ• ๋‹น์œผ๋กœ ์ง„ํ–‰, balloon_lock ๋ฎคํ…์Šค๋กœ ๋™์‹œ์„ฑ ๋ณดํ˜ธ
  • 5. leak_balloon (virtio)

    drivers/virtio/virtio_balloon.c:301 โ€” ํ˜ธ์ŠคํŠธ์—์„œ deflate ์š”์ฒญ (ํ˜ธ์ŠคํŠธ โ†’ ๊ฒŒ์ŠคํŠธ)

    static unsigned int leak_balloon(struct virtio_balloon *vb, size_t num)
    {
    unsigned int num_freed_pages;
    struct page *page;
    struct balloon_dev_info *vb_dev_info = &vb->vb_dev_info;
    LIST_HEAD(pages);
    
    num = min(num, ARRAY_SIZE(vb->pfns));
    mutex_lock(&vb->balloon_lock);
    num = min(num, (size_t)vb->num_pages);  /* ํ˜„์žฌ balloon ํŽ˜์ด์ง€ ์ˆ˜ ์ œํ•œ */
    
    for (vb->num_pfns = 0; vb->num_pfns < num;
    vb->num_pfns += VIRTIO_BALLOON_PAGES_PER_PAGE) {
    page = balloon_page_dequeue(vb_dev_info);
    if (!page)
    break;
    set_page_pfns(vb, vb->pfns + vb->num_pfns, page);
    list_add(&page->lru, &pages);
    vb->num_pages -= VIRTIO_BALLOON_PAGES_PER_PAGE;
    }
    
    num_freed_pages = vb->num_pfns;
    if (vb->num_pfns != 0)
    tell_host(vb, vb->deflate_vq);  /* ํ˜ธ์ŠคํŠธ์—๊ฒŒ ํ•ด์ œ ์•Œ๋ฆผ */
    release_pages_balloon(vb, &pages);    /* ํŽ˜์ด์ง€ ํ•ด์ œ (put_page) */
    mutex_unlock(&vb->balloon_lock);
    return num_freed_pages;
    }
  • ์—ญํ• : balloon์—์„œ ํŽ˜์ด์ง€๋ฅผ ๊บผ๋‚ด ์‹ค์ œ๋กœ ๋ฉ”๋ชจ๋ฆฌ๋กœ ๋ฐ˜ํ™˜
  • ๋ถ„๊ธฐ ๋กœ์ง: MUST_TELL_HOST ๊ธฐ๋Šฅ์ด ์žˆ์œผ๋ฉด ๋ฐ˜๋“œ์‹œ virtqueue๋ฅผ ํ†ตํ•ด ํ˜ธ์ŠคํŠธ์—๊ฒŒ ๋จผ์ € ์•Œ๋ ค์•ผ ํ•จ

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

    Inflate ํ๋ฆ„ (๊ฒŒ์ŠคํŠธ โ†’ ํ˜ธ์ŠคํŠธ ๋ฉ”๋ชจ๋ฆฌ ๋ฐ˜ํ™˜)

    virtballoon_changed
    โ””โ†’ start_update_balloon_size
    โ””โ†’ queue_work(update_balloon_size_work)
    โ””โ†’ update_balloon_size_func
    โ””โ†’ towards_target (์ฐจ์ด ๊ณ„์‚ฐ)
    โ””โ†’ fill_balloon (diff > 0)
    โ”œโ†’ balloon_page_alloc (GFP_HIGHUSER_MOVABLE)
    โ”œโ†’ balloon_page_enqueue โ†’ balloon_page_insert
    โ”‚    โ”œโ†’ __SetPageOffline
    โ”‚    โ”œโ†’ SetPageMovableOps (CONFIG_BALLOON_MIGRATION)
    โ”‚    โ””โ†’ list_add(&page->lru, &balloon->pages)
    โ”œโ†’ set_page_pfns (PFN ๋ณ€ํ™˜: page โ†’ balloon PFN)
    โ””โ†’ tell_host โ†’ virtqueue_add_outbuf โ†’ virtqueue_kick
    โ””โ†’ wait_event(vb->acked, virtqueue_get_buf)

    Deflate ํ๋ฆ„ (ํ˜ธ์ŠคํŠธ โ†’ ๊ฒŒ์ŠคํŠธ ๋ฉ”๋ชจ๋ฆฌ ๋ฐ˜ํ™˜)

    update_balloon_size_func
    โ””โ†’ towards_target (์ฐจ์ด ๊ณ„์‚ฐ)
    โ””โ†’ leak_balloon (diff < 0)
    โ”œโ†’ balloon_page_dequeue โ†’ balloon_page_list_dequeue
    โ”‚    โ”œโ†’ list_del(&page->lru)
    โ”‚    โ”œโ†’ adjust_managed_page_count (์นด์šดํ„ฐ ๋ณต์›)
    โ”‚    โ””โ†’ balloon_page_finalize โ†’ set_page_private(0)
    โ”œโ†’ set_page_pfns
    โ”œโ†’ tell_host โ†’ deflate_vq
    โ””โ†’ release_pages_balloon โ†’ put_page (์‹ค์ œ ํ•ด์ œ)

    Free Page Hint ํ๋ฆ„ (๋นˆ ํŽ˜์ด์ง€๋ฅผ ํ˜ธ์ŠคํŠธ์—๊ฒŒ ํžŒํŠธ)

    virtballoon_changed
    โ””โ†’ virtio_balloon_queue_free_page_work
    โ””โ†’ queue_work(report_free_page_work)
    โ””โ†’ report_free_page_func
    โ”œโ†’ cmd_id_received == DONE?
    โ”‚    โ””โ†’ return_free_pages_to_mm (๋ชจ๋“  free page ๋ฐ˜ํ™˜)
    โ””โ†’ cmd_id_received != STOP && ๋ณ€๊ฒฝ๋จ?
    โ””โ†’ virtio_balloon_report_free_page
    โ”œโ†’ send_cmd_id_start (์‹œ์ž‘ ๋ช…๋ น ์ „์†ก)
    โ”œโ†’ send_free_pages
    โ”‚    โ””โ†’ loop: get_free_page_and_send
    โ”‚         โ”œโ†’ alloc_pages (free page block ํ• ๋‹น)
    โ”‚         โ”œโ†’ virtqueue_add_inbuf (host์—๊ฒŒ ์ „๋‹ฌ)
    โ”‚         โ””โ†’ list_add(free_page_list)
    โ””โ†’ send_cmd_id_stop (์ •์ง€ ๋ช…๋ น ์ „์†ก)

    OOM ์‹œ deflate ํ๋ฆ„

    OOM ๋ฐœ์ƒ
    โ””โ†’ virtio_balloon_oom_notify
    โ””โ†’ leak_balloon(vb, 256)  /* ์ตœ๋Œ€ 256 ํŽ˜์ด์ง€ ํ•ด์ œ */
    โ””โ†’ update_balloon_size (ํ˜ธ์ŠคํŠธ์—๊ฒŒ ์ƒˆ ํฌ๊ธฐ ์•Œ๋ฆผ)

    ํŽ˜์ด์ง€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ๋ฆ„ (CONFIG_BALLOON_MIGRATION)

    Compaction ์Šค๋ ˆ๋“œ
    โ””โ†’ balloon_page_isolate (๊ฒฉ๋ฆฌ)
    โ”œโ†’ balloon_page_device (page_private๋กœ ๋””๋ฐ”์ด์Šค ํ™•์ธ)
    โ”œโ†’ list_del (๋ชฉ๋ก์—์„œ ์ œ๊ฑฐ)
    โ””โ†’ isolated_pages++
    โ””โ†’ balloon_page_migrate (๋งˆ์ด๊ทธ๋ ˆ์ด์…˜)
    โ”œโ†’ b_dev_info->migratepage โ†’ virtballoon_migratepage
    โ”‚    โ”œโ†’ mutex_trylock (๋ฝ ํš๋“ ์‹คํŒจ ์‹œ -EAGAIN)
    โ”‚    โ”œโ†’ 1๋‹จ๊ณ„: newpage inflate (tell_host via inflate_vq)
    โ”‚    โ””โ†’ 2๋‹จ๊ณ„: old page deflate (tell_host via deflate_vq)
    โ”œโ†’ balloon_page_insert (์ƒˆ ํŽ˜์ด์ง€๋ฅผ balloon ๋ชฉ๋ก์— ์ถ”๊ฐ€)
    โ””โ†’ balloon_page_finalize โ†’ put_page (์ด์ „ ํŽ˜์ด์ง€ ํ•ด์ œ)

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

    feature ํ”Œ๋ž˜๊ทธ๋ณ„ ๋™์ž‘

    ํ”Œ๋ž˜๊ทธ๋น„ํŠธ๋™์ž‘๋“œ๋ผ์ด๋ฒ„์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ
    MUST_TELL_HOST0ํŽ˜์ด์ง€ ๋ฐ˜ํ™˜ ์ „ ํ˜ธ์ŠคํŠธ์—๊ฒŒ ๋ฐ˜๋“œ์‹œ ์•Œ๋ฆผdeflate ์‹œ virtqueue ํ†ต์‹  ํ•„์ˆ˜
    STATS_VQ1๋ฉ”๋ชจ๋ฆฌ ํ†ต๊ณ„ ์ „์†ก virtqueue ์ง€์›stats_vq ์ƒ์„ฑ, ํ†ต๊ณ„ ์ˆ˜์ง‘ ์›Œํฌํ
    DEFLATE_ON_OOM2OOM ์‹œ ์ž๋™์œผ๋กœ balloon ํ•ด์ œoom_nb ๋“ฑ๋ก, ์ตœ๋Œ€ 256 ํŽ˜์ด์ง€ ํ•ด์ œ
    FREE_PAGE_HINT3Free Page Hint virtqueue ์ง€์›free_page_vq, shrinker, ์ „์šฉ ์›Œํฌํ
    PAGE_POISON4ํŽ˜์ด์ง€ ํฌ์ด์ฆˆ๋‹ ์‚ฌ์šฉpoison_val์„ ํ˜ธ์ŠคํŠธ์—๊ฒŒ ์„ค์ •
    REPORTING5Free Page Reporting virtqueue ์ง€์›reporting_vq, page_reporting_dev_info

    inflate vs deflate ๋น„๊ต

    ํ•ญ๋ชฉfill_balloon (inflate)leak_balloon (deflate)
    ๋ฐฉํ–ฅ๊ฒŒ์ŠคํŠธ โ†’ ํ˜ธ์ŠคํŠธ (๋ฉ”๋ชจ๋ฆฌ ๋ฐ˜ํ™˜)ํ˜ธ์ŠคํŠธ โ†’ ๊ฒŒ์ŠคํŠธ (๋ฉ”๋ชจ๋ฆฌ ๋ณต์›)
    ํ• ๋‹น/ํ•ด์ œ`balloon_page_alloc` (ํ• ๋‹น)`balloon_page_dequeue` (ํ•ด์ œ)
    virtqueue`inflate_vq``deflate_vq`
    ํŽ˜์ด์ง€ ์นด์šดํ„ฐ`num_pages` ์ฆ๊ฐ€`num_pages` ๊ฐ์†Œ
    managed count`adjust_managed_page_count(page, -1)``adjust_managed_page_count(page, 1)`
    VM ์ด๋ฒคํŠธ`BALLOON_INFLATE``BALLOON_DEFLATE`
    ๋…ธ๋“œ ์ƒํƒœ`NR_BALLOON_PAGES` ์ฆ๊ฐ€`NR_BALLOON_PAGES` ๊ฐ์†Œ
    ์‹คํŒจ ์ฒ˜๋ฆฌํ• ๋‹น ์‹คํŒจ ์‹œ 200ms ๋Œ€๊ธฐ ํ›„ ๋ถ€๋ถ„ ์ง„ํ–‰dequeue ์‹คํŒจ ์‹œ BUG() (๋น„์ •์ƒ ์ƒํƒœ)

    Free Page Hint vs Free Page Reporting ๋น„๊ต

    ํ•ญ๋ชฉFree Page HintFree Page Reporting
    virtqueue`free_page_vq``reporting_vq`
    ํ• ๋‹น ๋‹จ์œ„`VIRTIO_BALLOON_HINT_BLOCK_ORDER` (MAX_PAGE_ORDER)`page_reporting_dev_info.order`
    ๋™์ž‘ ๋ฐฉ์‹๋Šฅ๋™์ ์œผ๋กœ free page๋ฅผ ํ• ๋‹นํ•˜์—ฌ ํ˜ธ์ŠคํŠธ์—๊ฒŒ ์ „๋‹ฌํ˜ธ์ŠคํŠธ๊ฐ€ ์š”์ฒญํ•œ ํŽ˜์ด์ง€๋ฅผ ์ง์ ‘ ๋ณด๊ณ 
    cmd_idSTART โ†’ STOP ๋ช…๋ น ๊ตํ™˜๋ถˆํ•„์š”
    shrinker`virtio_balloon_shrinker_scan/count`๋ถˆํ•„์š”
    ์›Œํฌํ`balloon_wq` (์ „์šฉ)`page_reporting_dev_info.report` ์ฝœ๋ฐฑ

    ํŽ˜์ด์ง€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ƒํƒœ

    ์ƒํƒœpage ํ”Œ๋ž˜๊ทธpage_privateisolation
    ์ผ๋ฐ˜ balloon ํŽ˜์ด์ง€`PageOffline=true`, `PageMovableOps=true`balloon_dev_info ํฌ์ธํ„ฐ`isolated_pages=0`
    ๊ฒฉ๋ฆฌ๋œ ํŽ˜์ด์ง€๋™์ผ๋™์ผ`list_del` + `isolated_pages++`
    ํ•ด์ œ๋œ ํŽ˜์ด์ง€`PageOffline` ์œ ์ง€ (sticky)0 (clear)์—†์Œ

    ๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • Buddy Allocator
  • Compaction
  • Migration
  • Free Page Reporting
  • Mempool