๏ปฟ# Buddy Allocator (ํŽ˜์ด์ง€ ํ• ๋‹น์ž)

Linux 7.0 ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๋ถ„์„ ์‹œ๋ฆฌ์ฆˆ

๊ฐœ์š” (Overview)

Buddy Allocator๋Š” ๋ฆฌ๋ˆ…์Šค ์ปค๋„์˜ ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์„ ๋‹ด๋‹นํ•˜๋Š” ํ•ต์‹ฌ ํ•˜์œ„ ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค. ์‹œ์Šคํ…œ ๋ถ€ํŒ… ์‹œ memblock ํ• ๋‹น์ž๊ฐ€ ํ™•๋ณดํ•œ ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ํ”„๋ ˆ์ž„์„ Buddy Allocator๊ฐ€ ๊ด€๋ฆฌํ•˜๋ฉฐ, ์ดํ›„ ๋ชจ๋“  ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ํ• ๋‹น์€ ์ด ํ• ๋‹น์ž๋ฅผ ํ†ตํ•ด ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค. kmalloc()๊ณผ ๊ฐ™์€ slab ํ• ๋‹น์ž๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ Buddy Allocator์˜ alloc_pages()๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

Buddy ์‹œ์Šคํ…œ์˜ ํ•ต์‹ฌ ์•„์ด๋””์–ด๋Š” 2์˜ ๊ฑฐ๋“ญ์ œ๊ณฑ(order) ํฌ๊ธฐ ๋ธ”๋ก์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐ order์˜ ๋นˆ ๋ธ”๋ก์€ ํ•ด๋‹น migratetype๋ณ„ free list๋กœ ๊ด€๋ฆฌ๋˜๋ฉฐ, ์š”์ฒญ order๋ณด๋‹ค ํฐ ๋ธ”๋ก์ด ์žˆ์œผ๋ฉด expand()๋กœ ๋ถ„ํ• (split)ํ•˜๊ณ , ๋ฐ˜๋Œ€๋กœ ๋ฐ˜ํ™˜ ์‹œ ์ธ์ ‘ buddy ๋ธ”๋ก์ด ์ž์œ ๋กญ๋ฉด ๋ณ‘ํ•ฉ(coalesce)ํ•˜์—ฌ ๋” ํฐ ๋ธ”๋ก์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์™ธ๋ถ€ ๋‹จํŽธํ™”๋ฅผ ์™„ํ™”ํ•ฉ๋‹ˆ๋‹ค.

์ผ์ƒ ๋น„์œ 

Buddy Allocator๋Š” ํฐ ์ฃผ์ฐจ์žฅ์„ ํ•ญ์ƒ 1์นธ, 2์นธ, 4์นธ, 8์นธ์ฒ˜๋Ÿผ 2์˜ ๊ฑฐ๋“ญ์ œ๊ณฑ ํฌ๊ธฐ ๊ตฌ์—ญ์œผ๋กœ๋งŒ ๋‚˜๋ˆ„๋Š” ๊ด€๋ฆฌ์ž์™€ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. 2์นธ์งœ๋ฆฌ ๊ณต๊ฐ„์ด ํ•„์š”ํ•œ๋ฐ ๋น„์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด 4์นธ์งœ๋ฆฌ ๊ตฌ์—ญ์„ ๋ฐ˜์œผ๋กœ ์ชผ๊ฐœ ํ•œ์ชฝ์„ ๋‚ด์ฃผ๊ณ , ๋‚˜๋จธ์ง€ 2์นธ์€ ๋‹ค์‹œ ๋นˆ ๊ตฌ์—ญ ๋ชฉ๋ก์— ๋ณด๊ด€ํ•ฉ๋‹ˆ๋‹ค.

๋ฐ˜๋Œ€๋กœ ์ฐจ๊ฐ€ ๋น ์ ธ ๋‘ ์ธ์ ‘ ๊ตฌ์—ญ์ด ๋ชจ๋‘ ๋น„๋ฉด ๋‘˜์„ ๋‹ค์‹œ ํ•ฉ์ณ ๋” ํฐ ๊ตฌ์—ญ์œผ๋กœ ๋ณต์›ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ทœ์น™ ๋•๋ถ„์— ์ธ์ ‘ํ•œ ์ง(buddy)์„ ๋น ๋ฅด๊ฒŒ ์ฐพ์„ ์ˆ˜ ์žˆ๊ณ , ์ž‘์€ ๋นˆ ๊ตฌ์—ญ์ด ํฉ์–ด์ ธ ํฐ ์—ฐ์† ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋ชป ๋งŒ๋“œ๋Š” ์™ธ๋ถ€ ๋‹จํŽธํ™”๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ํ• ๋‹น์ž๋Š” ์‚ฌ์šฉ์ž ๊ณต๊ฐ„์˜ mmap()์ด๋‚˜ malloc()์„ ์ง์ ‘ ์ฒ˜๋ฆฌํ•˜์ง€๋Š” ์•Š์ง€๋งŒ, ๊ทธ ์•„๋ž˜์—์„œ handle_mm_fault() โ†’ do_anonymous_page() / do_wp_page() โ†’ alloc_pages()๋กœ ์ด์–ด์ง€๋Š” ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ์—ฐ๊ฒฐ์„ ๋ฐ›์ณ ์ค๋‹ˆ๋‹ค. ์ต๋ช… ํŽ˜์ด์ง€์˜ ์ฒซ ์ ‘๊ทผ์€ demand paging์ด๊ณ , fork() ๋’ค private mapping์˜ ์“ฐ๊ธฐ fault๋Š” COW๋กœ ๋ถ„๊ธฐํ•œ ๋’ค ๊ฒฐ๊ตญ ๊ฐ™์€ Buddy ๊ฒฝ๋กœ์—์„œ ํŽ˜์ด์ง€๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค.

์†Œ์Šค ํŒŒ์ผ ๊ฒฝ๋กœ:
mm/page_alloc.c           โ† ํ• ๋‹น/ํ•ด์ œ ํ•ต์‹ฌ ๋กœ์ง (7856์ค„)
include/linux/mmzone.h    โ† struct zone, free_area, pglist_data ์ •์˜
include/linux/mm_types.h  โ† struct page ์ •์˜
mm/internal.h             โ† ๋‚ด๋ถ€ ํ—ฌํผ ํ•จ์ˆ˜, GFP ๋งˆ์Šคํฌ ์ƒ์ˆ˜

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

# ์‹œ์Šคํ…œ์˜ ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ ๋ ˆ์ด์•„์›ƒ ํ™•์ธ
cat /proc/pagetypeinfo

# ๊ธฐ๋ณธ ํŽ˜์ด์ง€ ํฌ๊ธฐ ํ™•์ธ
getconf PAGE_SIZE

# ๊ฐ ์กด์˜ ํ”„๋ฆฌ ํŽ˜์ด์ง€ ํ†ต๊ณ„
cat /proc/vmstat | grep -E "nr_free_pages|nr_free_cma"

# zone๋ณ„ ์›Œํ„ฐ๋งˆํฌ ๋ฐ free area ์ •๋ณด
cat /proc/zoneinfo | head -100

# Buddy ํ”„๋ฆฌ ๋ฆฌ์ŠคํŠธ ๋ถ„ํฌ (order๋ณ„ ํ”„๋ฆฌ ํŽ˜์ด์ง€ ์ˆ˜)
cat /proc/buddyinfo

# ๋ฉ”๋ชจ๋ฆฌ ๋‹จํŽธํ™” ์ƒํƒœ ํ™•์ธ
cat /proc/extfraginfo

# per-CPU ํŽ˜์ด์ง€ ๋ฆฌ์ŠคํŠธ(PCP) ์ƒํƒœ ํ™•์ธ
cat /proc/vmstat | grep pcp

# Slab ์บ์‹œ์™€ Buddy์˜ ์ƒํ˜ธ์ž‘์šฉ ํ™•์ธ
cat /proc/slabinfo | head -5

# ํŽ˜์ด์ง€ ํดํŠธ์™€ COW ์ง•ํ›„ ํ™•์ธ
ps -o pid,minflt,majflt -p $$

# ํ˜„์žฌ ์‹œ์Šคํ…œ์˜ ๋ฉ”๋ชจ๋ฆฌ ๋…ธ๋“œ/์กด ๊ตฌ์กฐ
lstopo 2>/dev/null || numactl --hardware

์ถ”๊ฐ€ ์ง„๋‹จ ์ˆ˜์ง‘ ๋ช…๋ น

# ์ „์ฒด ๋ฉ”๋ชจ๋ฆฌ์™€ page allocator ์ฆ์ƒ ๋™์‹œ ์ˆ˜์ง‘
cat /proc/meminfo | head -30

# ๋ฌผ๋ฆฌ/๊ฐ€์ƒ ๋ฉ”๋ชจ๋ฆฌ ๋งต ํ™•์ธ
cat /proc/iomem
sudo cat /proc/vmallocinfo | head -20

# page allocation failure, OOM, compaction ๊ด€๋ จ ์ปค๋„ ๋กœ๊ทธ ํ™•์ธ
dmesg | grep -Ei "page allocation failure|oom|compact|kswapd" | tail -50

# compaction, reclaim, allocation stall ์นด์šดํ„ฐ ํ™•์ธ
cat /proc/vmstat | grep -E "compact|allocstall|pgscan|pgsteal|pgalloc|pgfree"

# PSI ๋ฉ”๋ชจ๋ฆฌ ์••๋ ฅ ํ™•์ธ (์ปค๋„ 4.20+)
cat /proc/pressure/memory

# zoneinfo์—์„œ PCP pageset๊ณผ ์›Œํ„ฐ๋งˆํฌ ์ฃผ๋ณ€ ์ •๋ณด ํ™•์ธ
cat /proc/zoneinfo | grep -E "Node|zone|pagesets|count:|high:|batch:|low|min|high" | head -120

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

1. `struct page` โ€” ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ์„ค๋ช…์ž

๋ชจ๋“  ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€ ํ”„๋ ˆ์ž„์—๋Š” ํ•˜๋‚˜์˜ struct page๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. Buddy Allocator์—์„œ ์‚ฌ์šฉ๋˜๋Š” ํ•„๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

// include/linux/mm_types.h:79-190
struct page {
memdesc_flags_t flags;      // ํŽ˜์ด์ง€ ์ƒํƒœ ๋น„ํŠธ (PG_buddy, PG_head ๋“ฑ)
union {
struct {
union {
struct list_head lru;       // LRU ๋ฆฌ์ŠคํŠธ
struct list_head buddy_list; // โ† Buddy free list ์—ฐ๊ฒฐ
struct list_head pcp_list;   // โ† PCP ๋ฆฌ์ŠคํŠธ ์—ฐ๊ฒฐ
struct llist_node pcp_llist; // โ† NMI-safe PCP ๋ฆฌ์ŠคํŠธ
};
struct address_space *mapping;
union {
pgoff_t __folio_index;
unsigned long share;
};
unsigned long private;  // Buddy ์‹œ์Šคํ…œ์—์„œ order ์ €์žฅ (PageBuddy์ผ ๋•Œ)
};
// ... ์ƒ๋žต
};
union {
unsigned int page_type;  // typed folio์šฉ
atomic_t _mapcount;      // ๋งคํ•‘ ์นด์šดํŠธ
};
atomic_t _refcount;          // ์ฐธ์กฐ ์นด์šดํŠธ
// ...
};

ํ•ต์‹ฌ ํฌ์ธํŠธ:

  • buddy_list: free ์ƒํƒœ์˜ ํŽ˜์ด์ง€๊ฐ€ free_area์˜ free_list์— ์—ฐ๊ฒฐ๋˜๋Š” ๋ฆฌ์ŠคํŠธ ํ—ค๋“œ
  • private: PageBuddy(page)๊ฐ€ ์„ค์ •๋œ ๊ฒฝ์šฐ page_private(page)๋กœ order ๊ฐ’ ์ €์žฅ
  • flags.f: PG_buddy, PG_head, PG_tail ๋น„ํŠธ๋กœ compound page/buddy ์ƒํƒœ ์ถ”์ 
  • 2. `struct free_area` โ€” order๋ณ„ free ๋ฆฌ์ŠคํŠธ

    // include/linux/mmzone.h:138-141
    struct free_area {
    struct list_head    free_list[MIGRATE_TYPES];  // migratetype๋ณ„ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ
    unsigned long       nr_free;                    // ์ด ํ”„๋ฆฌ ๋ธ”๋ก ์ˆ˜
    };

    ๊ฐ zone์€ NR_PAGE_ORDERS(๋ณดํ†ต 11, order 0~10)๊ฐœ์˜ free_area ๋ฐฐ์—ด์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค. ๊ฐ free_area๋Š” MIGRATE_TYPES๊ฐœ์˜ free list๋ฅผ ๊ฐ€์ง€๋ฉฐ, ๊ฐ list๋Š” ํ•ด๋‹น migratetype๊ณผ order์˜ ํ”„๋ฆฌ ๋ธ”๋ก๋“ค์„ buddy_list๋กœ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

    3. `struct zone` โ€” ๋ฉ”๋ชจ๋ฆฌ ์กด

    // include/linux/mmzone.h:879-1060
    struct zone {
    // ์›Œํ„ฐ๋งˆํฌ
    unsigned long _watermark[NR_WMARK];   // MIN, LOW, HIGH, PROMO
    unsigned long watermark_boost;
    
    // ์กด ์˜์—ญ ์ •๋ณด
    unsigned long zone_start_pfn;
    atomic_long_t managed_pages;    // buddy๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ํŽ˜์ด์ง€ ์ˆ˜
    unsigned long spanned_pages;
    unsigned long present_pages;
    
    // ํ”„๋ฆฌ ์—์–ด๋ฆฌ์–ด ๋ฐฐ์—ด (ํ•ต์‹ฌ!)
    struct free_area free_area[NR_PAGE_ORDERS];
    
    // ๋™์‹œ์„ฑ ์ œ์–ด
    spinlock_t lock;                // free_area ๋ณดํ˜ธ
    
    // NUMA ๊ด€๋ จ
    struct pglist_data *zone_pgdat;
    struct per_cpu_pages __percpu *per_cpu_pageset;  // PCP
    
    // ํ†ต๊ณ„
    atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS];
    };

    ํ•ต์‹ฌ ํฌ์ธํŠธ:

  • free_area[NR_PAGE_ORDERS]: order 0๋ถ€ํ„ฐ MAX_PAGE_ORDER๊นŒ์ง€์˜ free area ๋ฐฐ์—ด
  • lock: free_area ์ ‘๊ทผ ์‹œ ํ•„์š” (zone->lock)
  • _watermark[]: ํ• ๋‹น ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ํŒ๋‹จ ๊ธฐ์ค€ (MIN/LOW/HIGH)
  • per_cpu_pageset: per-CPU ์บ์‹œ ํŽ˜์ด์ง€ ๋ฆฌ์ŠคํŠธ (PCP)
  • 4. `struct pglist_data` (pg_data_t) โ€” NUMA ๋…ธ๋“œ

    // include/linux/mmzone.h:1381-1498
    typedef struct pglist_data {
    struct zone node_zones[MAX_NR_ZONES];        // ์ด ๋…ธ๋“œ์˜ ์กด ๋ฐฐ์—ด
    struct zonelist node_zonelists[MAX_ZONELISTS]; // ํ• ๋‹น ์ˆœ์„œ ๋ชฉ๋ก
    int nr_zones;
    
    unsigned long node_start_pfn;
    unsigned long node_present_pages;
    unsigned long node_spanned_pages;
    int node_id;
    
    // kswapd ๊ด€๋ จ
    wait_queue_head_t kswapd_wait;
    struct task_struct *kswapd;
    int kswapd_order;
    // ...
    } pg_data_t;

    5. `struct per_cpu_pages` (PCP) โ€” Per-CPU ํŽ˜์ด์ง€ ์บ์‹œ

    // include/linux/mmzone.h:744-760
    struct per_cpu_pages {
    spinlock_t lock;
    int count;          // ํ˜„์žฌ ๋ฆฌ์ŠคํŠธ์˜ ํŽ˜์ด์ง€ ์ˆ˜
    int high;           // high ์›Œํ„ฐ๋งˆํฌ (์ด ๋„˜์œผ๋ฉด ๋“œ๋ ˆ์ธ)
    int high_min;
    int high_max;
    int batch;          // buddy์—์„œ ๊ฐ€์ ธ์˜ฌ/๋ณด๋‚ผ ๋ฐฐ์น˜ ํฌ๊ธฐ
    u8 alloc_factor;    // allocate ์‹œ batch ์Šค์ผ€์ผ๋ง ํŒฉํ„ฐ
    short free_count;   // ์—ฐ์† free ์นด์šดํŠธ
    struct list_head lists[NR_PCP_LISTS];  // migratetype ร— order ๋ฆฌ์ŠคํŠธ
    };

    ํ•ต์‹ฌ ํฌ์ธํŠธ:

  • NR_PCP_LISTS = MIGRATE_PCPTYPES ร— (PAGE_ALLOC_COSTLY_ORDER + 1) + NR_PCP_THP
  • order 0~3 ร— Unmovable/Movable/Reclaimable + THP 2๊ฐœ = 14๊ฐœ ๋ฆฌ์ŠคํŠธ
  • high: ์ด ๊ฐ’ ์ดˆ๊ณผ ์‹œ zone lock ํ•˜์—์„œ buddy๋กœ ๋ฐ˜ํ™˜
  • batch: ํ•œ ๋ฒˆ์— buddy์—์„œ ๊ฐ€์ ธ์˜ฌ/๋ณด๋‚ผ ํŽ˜์ด์ง€ ์ˆ˜
  • 6. Migratetype

    // include/linux/mmzone.h:64-90
    enum migratetype {
    MIGRATE_UNMOVABLE,    // ์ด๋™ ๋ถˆ๊ฐ€ (kernel allocations)
    MIGRATE_MOVABLE,      // ์ด๋™ ๊ฐ€๋Šฅ (user pages)
    MIGRATE_RECLAIMABLE,  // ํšŒ์ˆ˜ ๊ฐ€๋Šฅ (inode cache ๋“ฑ)
    MIGRATE_PCPTYPES,     // PCP lists์˜ ์ˆ˜ (3)
    MIGRATE_HIGHATOMIC = MIGRATE_PCPTYPES,
    MIGRATE_CMA,          // CMA ์˜์—ญ (CONFIG_CMA)
    MIGRATE_ISOLATE,      // ๊ฒฉ๋ฆฌ (CONFIG_MEMORY_ISOLATION)
    MIGRATE_TYPES
    };

    Fallback ์ˆœ์„œ:

    // mm/page_alloc.c:1951-1955
    static int fallbacks[MIGRATE_PCPTYPES][MIGRATE_PCPTYPES - 1] = {
    [MIGRATE_UNMOVABLE]   = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE   },
    [MIGRATE_MOVABLE]     = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE },
    [MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE,   MIGRATE_MOVABLE   },
    };

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

    1. `__alloc_pages_noprof()` โ€” ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น ๋ฉ”์ธ ์—”ํŠธ๋ฆฌ

    // mm/page_alloc.c:5279-5289
    struct page *__alloc_pages_noprof(gfp_t gfp, unsigned int order,
    int preferred_nid, nodemask_t *nodemask)
    {
    struct page *page;
    
    page = __alloc_frozen_pages_noprof(gfp, order, preferred_nid, nodemask);
    if (page)
    set_page_refcounted(page);
    return page;
    }

    ์—ญํ• : alloc_pages() ๋งคํฌ๋กœ์˜ ์ตœ์ข… ๊ตฌํ˜„. frozen ํŽ˜์ด์ง€๋ฅผ ํ• ๋‹นํ•œ ํ›„ refcount=1๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

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

  • __alloc_frozen_pages_noprof() ํ˜ธ์ถœ โ†’ ์‹คํŒจ ์‹œ NULL ๋ฐ˜ํ™˜
  • 2. `__alloc_frozen_pages_noprof()` โ€” ํ• ๋‹น ํ•ต์‹ฌ ๋กœ์ง

    // mm/page_alloc.c:5214-5277
    struct page *__alloc_frozen_pages_noprof(gfp_t gfp, unsigned int order,
    int preferred_nid, nodemask_t *nodemask)
    {
    struct alloc_context ac = { };
    unsigned int alloc_flags = ALLOC_WMARK_LOW;
    gfp_t alloc_gfp;
    
    if (WARN_ON_ONCE_GFP(order > MAX_PAGE_ORDER, gfp))
    return NULL;
    
    gfp &= gfp_allowed_mask;
    gfp = current_gfp_context(gfp);
    alloc_gfp = gfp;
    
    if (!prepare_alloc_pages(gfp, order, preferred_nid, nodemask, &ac,
    &alloc_gfp, &alloc_flags))
    return NULL;
    
    alloc_flags |= alloc_flags_nofragment(zonelist_zone(ac.preferred_zoneref), gfp);
    
    /* 1์ฐจ ์‹œ๋„: fast path */
    page = get_page_from_freelist(alloc_gfp, order, alloc_flags, &ac);
    if (likely(page))
    goto out;
    
    alloc_gfp = gfp;
    ac.spread_dirty_pages = false;
    
    /* 2์ฐจ ์‹œ๋„: slow path (direct reclaim, OOM ๋“ฑ) */
    page = __alloc_pages_slowpath(alloc_gfp, order, &ac);
    
    out:
    if (memcg_kmem_online() && (gfp & __GFP_ACCOUNT) && page &&
    unlikely(__memcg_kmem_charge_page(page, gfp, order) != 0)) {
    free_frozen_pages(page, order);
    page = NULL;
    }
    
    trace_mm_page_alloc(page, order, alloc_gfp, ac.migratetype);
    kmsan_alloc_page(page, order, alloc_gfp);
    
    return page;
    }

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

    1. order > MAX_PAGE_ORDER โ†’ ๊ฒฝ๊ณ  ํ›„ NULL ๋ฐ˜ํ™˜

    2. prepare_alloc_pages() ์‹คํŒจ โ†’ NULL ๋ฐ˜ํ™˜

    3. get_page_from_freelist() ์„ฑ๊ณต โ†’ ๋ฐ”๋กœ ๋ฐ˜ํ™˜ (fast path)

    4. ์‹คํŒจ โ†’ __alloc_pages_slowpath() ์ง„์ž… (slow path)

    - kswapd wake, direct reclaim, compaction, OOM ์ˆœ์„œ๋กœ ์‹œ๋„

    3. `get_page_from_freelist()` โ€” ์กด ์ˆœํšŒ ๋ฐ ํ• ๋‹น

    // mm/page_alloc.c:3808-4003
    static struct page *
    get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,
    const struct alloc_context *ac)
    {
    struct zoneref *z;
    struct zone *zone;
    
    retry:
    no_fallback = alloc_flags & ALLOC_NOFRAGMENT;
    z = ac->preferred_zoneref;
    
    // zonelist ์ˆœํšŒ
    for_next_zone_zonelist_nodemask(zone, z, ac->highest_zoneidx,
    ac->nodemask) {
    // cpuset ์ฒดํฌ
    // dirty limit ์ฒดํฌ
    // fragmentation avoidance ์ฒดํฌ
    
    check_alloc_wmark:
    // ์›Œํ„ฐ๋งˆํฌ ๊ฒ€์‚ฌ
    mark = wmark_pages(zone, alloc_flags & ALLOC_WMARK_MASK);
    if (!zone_watermark_fast(zone, order, mark,
    ac->highest_zoneidx, alloc_flags,
    gfp_mask)) {
    // watermarks ๋ฏธ์ถฉ์กฑ ์‹œ node_reclaim ์‹œ๋„
    continue;
    }
    
    try_this_zone:
    // ์‹ค์ œ ํ• ๋‹น ์‹œ๋„
    page = rmqueue(zonelist_zone(ac->preferred_zoneref), zone, order,
    gfp_mask, alloc_flags, ac->migratetype);
    if (page) {
    prep_new_page(page, order, gfp_mask, alloc_flags);
    if (unlikely(alloc_flags & ALLOC_HIGHATOMIC))
    reserve_highatomic_pageblock(page, order, zone);
    return page;
    }
    }
    
    // fallback: NOFRAGMENT ํ•ด์ œ ํ›„ ์žฌ์‹œ๋„
    if (no_fallback && !defrag_mode) {
    alloc_flags &= ~ALLOC_NOFRAGMENT;
    goto retry;
    }
    return NULL;
    }

    ์ƒ์œ„ page fault ์—ฐ๊ฒฐ

    page fault
    โ””โ”€ handle_mm_fault()      [mm/memory.c:6589]
    โ”œโ”€ do_anonymous_page() [mm/memory.c:5217]  โ† ์ฒซ ์ ‘๊ทผ์˜ ์ต๋ช… ํŽ˜์ด์ง€
    โ””โ”€ do_wp_page()        [mm/memory.c:4149]  โ† COW / write-protect fault
    โ””โ”€ alloc_page(GFP_KERNEL)
    // mm/memory.c:6589-6624
    vm_fault_t handle_mm_fault(struct vm_area_struct *vma, unsigned long address,
    unsigned int flags, struct pt_regs *regs)
    {
    ...
    if (unlikely(is_vm_hugetlb_page(vma)))
    ret = hugetlb_fault(vma->vm_mm, vma, address, flags);
    else
    ret = __handle_mm_fault(vma, address, flags);
    ...
    }
    // mm/memory.c:5217-5267
    static vm_fault_t do_anonymous_page(struct vm_fault *vmf)
    {
    ...
    if (!(vmf->flags & FAULT_FLAG_WRITE) &&
    !mm_forbids_zeropage(vma->vm_mm)) {
    ...
    entry = pte_mkspecial(pfn_pte(my_zero_pfn(vmf->address),
    vma->vm_page_prot));
    ...
    }
    ...
    folio = alloc_anon_folio(vmf);
    ...
    }
    // mm/memory.c:4149-4241
    static vm_fault_t do_wp_page(struct vm_fault *vmf)
    {
    ...
    if (folio && folio_test_anon(folio) &&
    (PageAnonExclusive(vmf->page) || wp_can_reuse_anon_folio(folio, vma))) {
    ...
    wp_page_reuse(vmf, folio);
    return 0;
    }
    ...
    return wp_page_copy(vmf);
    }

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

    1. zonelist ์ˆœํšŒ: preferred zone โ†’ fallback zone ์ˆœ

    2. ๊ฐ zone๋งˆ๋‹ค:

    - cpuset ํ—ˆ์šฉ ์—ฌ๋ถ€

    - dirty limit ์ดˆ๊ณผ ์—ฌ๋ถ€

    - ์›Œํ„ฐ๋งˆํฌ ์ถฉ์กฑ ์—ฌ๋ถ€ (fast check โ†’ full check)

    - watermarks ๋ฏธ์ถฉ์กฑ ์‹œ: _deferred_grow_zone() ๋˜๋Š” node_reclaim() ์‹œ๋„

    3. rmqueue() ์„ฑ๊ณต ์‹œ: prep_new_page() ํ›„ ๋ฐ˜ํ™˜

    4. ๋ชจ๋“  zone ์‹คํŒจ ์‹œ: NOFRAGMENT ํ”Œ๋ž˜๊ทธ ํ•ด์ œ ํ›„ retry

    4. `__rmqueue()` โ€” free list์—์„œ ํŽ˜์ด์ง€ ์ œ๊ฑฐ

    // mm/page_alloc.c:2477-2540
    static __always_inline struct page *
    __rmqueue(struct zone *zone, unsigned int order, int migratetype,
    unsigned int alloc_flags, enum rmqueue_mode *mode)
    {
    // CMA ๋ฐธ๋Ÿฐ์‹ฑ: CMA free๊ฐ€ 50% ์ดˆ๊ณผ ์‹œ CMA์—์„œ ๋จผ์ € ํ• ๋‹น
    if (alloc_flags & ALLOC_CMA &&
    zone_page_state(zone, NR_FREE_CMA_PAGES) >
    zone_page_state(zone, NR_FREE_PAGES) / 2) {
    page = __rmqueue_cma_fallback(zone, order);
    if (page) return page;
    }
    
    // fallback ๋ชจ๋“œ ์ˆœ์ฐจ ์‹œ๋„
    switch (*mode) {
    case RMQUEUE_NORMAL:
    page = __rmqueue_smallest(zone, order, migratetype);  // 1์ˆœ์œ„
    if (page) return page;
    fallthrough;
    case RMQUEUE_CMA:
    page = __rmqueue_cma_fallback(zone, order);            // 2์ˆœ์œ„
    if (page) { *mode = RMQUEUE_CMA; return page; }
    fallthrough;
    case RMQUEUE_CLAIM:
    page = __rmqueue_claim(zone, order, migratetype, alloc_flags); // 3์ˆœ์œ„
    if (page) { *mode = RMQUEUE_NORMAL; return page; }
    fallthrough;
    case RMQUEUE_STEAL:
    if (!(alloc_flags & ALLOC_NOFRAGMENT)) {
    page = __rmqueue_steal(zone, order, migratetype);  // 4์ˆœ์œ„
    if (page) { *mode = RMQUEUE_STEAL; return page; }
    }
    }
    return NULL;
    }

    ๋ถ„๊ธฐ ๋กœ์ง (4๋‹จ๊ณ„ fallback):

    1. NORMAL: ๊ฐ™์€ migratetype์˜ free list์—์„œ ์ง์ ‘ ํ• ๋‹น

    2. CMA: CMA free list์—์„œ ํ• ๋‹น (MOVABLE๋งŒ)

    3. CLAIM: ๋‹ค๋ฅธ migratetype์˜ free list์—์„œ ํŽ˜์ด์ง€๋ธ”๋ก์„ claim (migratetype ๋ณ€๊ฒฝ)

    4. STEAL: ๋‹ค๋ฅธ migratetype์—์„œ ํŽ˜์ด์ง€๋ฅผ steal (๋‹จํŽธํ™” ํ—ˆ์šฉ ์‹œ)

    5. `__rmqueue_smallest()` โ€” ์ตœ์†Œ order์—์„œ ํ• ๋‹น

    // mm/page_alloc.c:1919-1942
    struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,
    int migratetype)
    {
    unsigned int current_order;
    struct free_area *area;
    struct page *page;
    
    // ์š”์ฒญ order๋ถ€ํ„ฐ MAX_PAGE_ORDER๊นŒ์ง€ ์ƒ์œ„ order ํƒ์ƒ‰
    for (current_order = order; current_order < NR_PAGE_ORDERS; ++current_order) {
    area = &(zone->free_area[current_order]);
    page = get_page_from_free_area(area, migratetype);
    if (!page)
    continue;
    
    // ํฐ ๋ธ”๋ก์„ ํ•„์š” order๋กœ ๋ถ„ํ• 
    page_del_and_expand(zone, page, order, current_order, migratetype);
    return page;
    }
    return NULL;
    }

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

  • order ~ MAX_PAGE_ORDER ๋ฒ”์œ„์—์„œ free ๋ธ”๋ก ํƒ์ƒ‰
  • ํฐ ๋ธ”๋ก ๋ฐœ๊ฒฌ ์‹œ page_del_and_expand()๋กœ ๋ถ„ํ•  ํ›„ ๋ฐ˜ํ™˜
  • 6. `expand()` โ€” ๋ธ”๋ก ๋ถ„ํ•  (Split)

    // mm/page_alloc.c:1732-1758
    static inline unsigned int expand(struct zone *zone, struct page *page, int low,
    int high, int migratetype)
    {
    unsigned int size = 1 << high;
    unsigned int nr_added = 0;
    
    while (high > low) {
    high--;
    size >>= 1;
    
    // guard page ์„ค์ • ์‹œ skip
    if (set_page_guard(zone, &page[size], high))
    continue;
    
    // ์ƒ์œ„ half๋ฅผ free list์— ์ถ”๊ฐ€
    __add_to_free_list(&page[size], zone, high, migratetype, false);
    set_buddy_order(&page[size], high);
    nr_added += size;
    }
    return nr_added;
    }

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

  • high โ†’ low๊นŒ์ง€ ๋ฐ˜๋ณต
  • ๊ฐ ๋‹จ๊ณ„์—์„œ ์ƒ์œ„ half๋ฅผ free list์— ์ถ”๊ฐ€
  • guard page( CONFIG_DEBUG_PAGEALLOC) ์„ค์ • ์‹œ ํ•ด๋‹น ๋ธ”๋ก skip
  • 7. `__free_one_page()` โ€” ํŽ˜์ด์ง€ ๋ฐ˜ํ™˜ ๋ฐ ๋ฒ„๋”” ๋ณ‘ํ•ฉ

    // mm/page_alloc.c:978-1064
    static inline void __free_one_page(struct page *page,
    unsigned long pfn, struct zone *zone, unsigned int order,
    int migratetype, fpi_t fpi_flags)
    {
    unsigned long buddy_pfn = 0;
    unsigned long combined_pfn;
    
    account_freepages(zone, 1 << order, migratetype);
    
    // order < MAX_PAGE_ORDER๊นŒ์ง€ ๋ฒ„๋”” ๋ณ‘ํ•ฉ ์‹œ๋„
    while (order < MAX_PAGE_ORDER) {
    int buddy_mt = migratetype;
    
    // compaction capture ์ฒดํฌ
    if (compaction_capture(capc, page, order, migratetype)) {
    account_freepages(zone, -(1 << order), migratetype);
    return;
    }
    
    // ์ธ์ ‘ ๋ฒ„๋”” ์ฐพ๊ธฐ
    buddy = find_buddy_page_pfn(page, pfn, order, &buddy_pfn);
    if (!buddy)
    goto done_merging;
    
    // pageblock_order ์ด์ƒ์—์„œ๋Š” migratetype ์ผ์น˜ ํ™•์ธ
    if (unlikely(order >= pageblock_order)) {
    buddy_mt = get_pfnblock_migratetype(buddy, buddy_pfn);
    if (migratetype != buddy_mt &&
    (!migratetype_is_mergeable(migratetype) ||
    !migratetype_is_mergeable(buddy_mt)))
    goto done_merging;
    }
    
    // ๋ฒ„๋””๋ฅผ free list์—์„œ ์ œ๊ฑฐํ•˜๊ณ  ๋ณ‘ํ•ฉ
    __del_page_from_free_list(buddy, zone, order, buddy_mt);
    combined_pfn = buddy_pfn & pfn;
    page = page + (combined_pfn - pfn);
    pfn = combined_pfn;
    order++;
    }
    
    done_merging:
    set_buddy_order(page, order);
    __add_to_free_list(page, zone, order, migratetype, to_tail);
    }

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

    1. compaction capture: compaction ์ค‘์ด๋ฉด ํ•ด๋‹น ํŽ˜์ด์ง€๋ฅผ ์บก์ฒ˜

    2. find_buddy_page_pfn(): ์ธ์ ‘ ๋ฒ„๋”” ํŽ˜์ด์ง€ ๊ฒ€์ƒ‰

    3. order โ‰ฅ pageblock_order: migratetype ํ˜ธํ™˜์„ฑ ํ™•์ธ ํ›„ ๋ณ‘ํ•ฉ

    4. ๋ฒ„๋”” ๋ณ‘ํ•ฉ: combined_pfn = buddy_pfn & pfn์œผ๋กœ ๋ณ‘ํ•ฉ๋œ base pfn ๊ณ„์‚ฐ

    5. ๋ณ‘ํ•ฉ๋œ ๋ธ”๋ก์„ free list์— ์ถ”๊ฐ€

    8. `__alloc_pages_slowpath()` โ€” Slow Path (ํ• ๋‹น ์‹คํŒจ ์‹œ ๋Œ€์‘)

    // mm/page_alloc.c:4710-4994
    static inline struct page *
    __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
    struct alloc_context *ac)
    {
    bool can_direct_reclaim = gfp_mask & __GFP_DIRECT_RECLAIM;
    bool can_compact = can_direct_reclaim && gfp_compaction_allowed(gfp_mask);
    bool nofail = gfp_mask & __GFP_NOFAIL;
    const bool costly_order = order > PAGE_ALLOC_COSTLY_ORDER;
    struct page *page = NULL;
    unsigned int alloc_flags;
    unsigned long did_some_progress;
    enum compact_priority compact_priority;
    enum compact_result compact_result;
    int compaction_retries;
    int no_progress_loops;
    
    restart:
    compaction_retries = 0;
    no_progress_loops = 0;
    compact_result = COMPACT_SKIPPED;
    compact_priority = DEF_COMPACT_PRIORITY;
    
    // costly/high-order ํ• ๋‹น ์‹œ compaction ์šฐ์„  ์‹œ๋„
    if (can_compact && (costly_order || (order > 0 &&
    ac->migratetype != MIGRATE_MOVABLE))) {
    compact_first = true;
    compact_priority = INIT_COMPACT_PRIORITY;
    }
    
    alloc_flags = gfp_to_alloc_flags(gfp_mask, order);
    
    retry:
    // kswapd wake
    if (alloc_flags & ALLOC_KSWAPD)
    wake_all_kswapds(order, gfp_mask, ac);
    
    // ์žฌ์‹œ๋„: watermarks ์žฌ๊ฒ€์‚ฌ
    page = get_page_from_freelist(gfp_mask, order, alloc_flags, ac);
    if (page)
    goto got_pg;
    
    // pfmemalloc ํ”Œ๋ž˜๊ทธ ํ™•์ธ (๊ธด๊ธ‰ ํ• ๋‹น)
    reserve_flags = __gfp_pfmemalloc_flags(gfp_mask);
    if (reserve_flags) {
    alloc_flags = gfp_to_alloc_flags_cma(gfp_mask, reserve_flags) |
    (alloc_flags & ALLOC_KSWAPD);
    if (can_retry_reserves) {
    can_retry_reserves = false;
    goto retry;
    }
    }
    
    // ์ง์ ‘ ํšŒ์ˆ˜ ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฉด ์‹คํŒจ
    if (!can_direct_reclaim)
    goto nopage;
    
    // ์ง์ ‘ ํšŒ์ˆ˜ ์‹œ๋„
    if (!compact_first) {
    page = __alloc_pages_direct_reclaim(gfp_mask, order, alloc_flags,
    ac, &did_some_progress);
    if (page)
    goto got_pg;
    }
    
    // ์ง์ ‘ compaction ์‹œ๋„
    page = __alloc_pages_direct_compact(gfp_mask, order, alloc_flags, ac,
    compact_priority, &compact_result);
    if (page)
    goto got_pg;
    
    // compact_first ํ›„ retry
    if (compact_first) {
    compact_first = false;
    goto retry;
    }
    
    // ํšŒ์ˆ˜/compaction ์žฌ์‹œ๋„ ๊ฒ€์‚ฌ
    if (should_reclaim_retry(gfp_mask, order, ac, alloc_flags,
    did_some_progress > 0, &no_progress_loops))
    goto retry;
    
    if (did_some_progress > 0 && can_compact &&
    should_compact_retry(ac, order, alloc_flags,
    compact_result, &compact_priority,
    &compaction_retries))
    goto retry;
    
    // NOFRAGMENT ํ•ด์ œ ํ›„ ์žฌ์‹œ๋„
    if (defrag_mode && (alloc_flags & ALLOC_NOFRAGMENT)) {
    alloc_flags &= ~ALLOC_NOFRAGMENT;
    goto retry;
    }
    
    // OOM Killer ๋ฐœ๋™
    page = __alloc_pages_may_oom(gfp_mask, order, ac, &did_some_progress);
    if (page)
    goto got_pg;
    
    // NOFAIL ์ฒ˜๋ฆฌ
    if (unlikely(nofail)) {
    page = __alloc_pages_cpuset_fallback(gfp_mask, order,
    ALLOC_MIN_RESERVE, ac);
    if (page)
    goto got_pg;
    cond_resched();
    goto retry;
    }
    
    nopage:
    warn_alloc(gfp_mask, ac->nodemask,
    "page allocation failure: order:%u", order);
    got_pg:
    return page;
    }

    ๋ถ„๊ธฐ ๋กœ์ง (๋‹จ๊ณ„๋ณ„ ๋Œ€์‘):

    1. kswapd wake: ๋ฐฑ๊ทธ๋ผ์šด๋“œ ํšŒ์ˆ˜ ํŠธ๋ฆฌ๊ฑฐ

    2. watermark ์žฌ๊ฒ€์‚ฌ: ALLOC_MIN_RESERVE ์ ์šฉ ํ›„ ์žฌ์‹œ๋„

    3. pfmemalloc: ๊ธด๊ธ‰ ์‹œ ALLOC_NO_WATERMARKS ์ ์šฉ

    4. ์ง์ ‘ ํšŒ์ˆ˜: try_to_free_pages() โ†’ LRU ํŽ˜์ด์ง€ ํšŒ์ˆ˜

    5. ์ง์ ‘ compaction: ๋ฉ”๋ชจ๋ฆฌ ์กฐ๊ฐ ๋ชจ์Œ์œผ๋กœ ํฐ ๋ธ”๋ก ํ™•๋ณด

    6. ํšŒ์ˆ˜/compaction ์žฌ์‹œ๋„: MAX_RECLAIM_RETRIES(16ํšŒ)๊นŒ์ง€ ๋ฐ˜๋ณต

    7. NOFRAGMENT ํ•ด์ œ: ๋‹จํŽธํ™” ํ—ˆ์šฉ ํ›„ ์žฌ์‹œ๋„

    8. OOM Killer: out_of_memory() โ†’ ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ

    9. NOFAIL: ๋ฌดํ•œ ์žฌ์‹œ๋„ (์กฐ๊ฑด๋ถ€)

    9. `__free_frozen_pages()` โ€” PCP๋ฅผ ํ†ตํ•œ ํŽ˜์ด์ง€ ๋ฐ˜ํ™˜

    // mm/page_alloc.c:2964-3020
    static void __free_frozen_pages(struct page *page, unsigned int order,
    fpi_t fpi_flags)
    {
    unsigned long UP_flags;
    struct per_cpu_pages *pcp;
    struct zone *zone;
    unsigned long pfn = page_to_pfn(page);
    int migratetype;
    
    // PCP ํ—ˆ์šฉ order๊ฐ€ ์•„๋‹ˆ๋ฉด buddy๋กœ ์ง์ ‘ ๋ฐ˜ํ™˜
    if (!pcp_allowed_order(order)) {
    __free_pages_ok(page, order, fpi_flags);
    return;
    }
    
    if (!__free_pages_prepare(page, order, fpi_flags))
    return;
    
    // ISOLATE ํƒ€์ž…์€ ์ฆ‰์‹œ free_one_page()
    zone = page_zone(page);
    migratetype = get_pfnblock_migratetype(page, pfn);
    if (unlikely(migratetype >= MIGRATE_PCPTYPES)) {
    if (unlikely(is_migrate_isolate(migratetype))) {
    free_one_page(zone, page, pfn, order, fpi_flags);
    return;
    }
    migratetype = MIGRATE_MOVABLE;
    }
    
    // PCP์— ์ถ”๊ฐ€
    pcp = pcp_spin_trylock(zone->per_cpu_pageset, UP_flags);
    if (pcp) {
    free_frozen_page_commit(zone, pcp, page, migratetype,
    order, fpi_flags, &UP_flags);
    pcp_spin_unlock(pcp, UP_flags);
    } else {
    // PCP lock ์‹คํŒจ ์‹œ buddy๋กœ ์ง์ ‘ ๋ฐ˜ํ™˜
    free_one_page(zone, page, pfn, order, fpi_flags);
    }
    }

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

    1. PCP ํ—ˆ์šฉ order ์ดˆ๊ณผ โ†’ __free_pages_ok() (buddy ์ง์ ‘ ๋ฐ˜ํ™˜)

    2. ISOLATE ํƒ€์ž… โ†’ free_one_page() (์ฆ‰์‹œ buddy ๋ฐ˜ํ™˜)

    3. PCP lock ์„ฑ๊ณต โ†’ free_frozen_page_commit() (PCP์— ์ถ”๊ฐ€)

    4. PCP lock ์‹คํŒจ โ†’ free_one_page() (buddy ์ง์ ‘ ๋ฐ˜ํ™˜)

    10. `free_frozen_page_commit()` โ€” PCP ๋ฐ˜ํ™˜ ๋ฐ ๋“œ๋ ˆ์ธ

    // mm/page_alloc.c:2859-2959
    static bool free_frozen_page_commit(struct zone *zone,
    struct per_cpu_pages *pcp, struct page *page, int migratetype,
    unsigned int order, fpi_t fpi_flags, unsigned long *UP_flags)
    {
    int high, batch;
    int to_free, to_free_batched;
    int pindex;
    
    // PCP ํ• ๋‹น ํŒฉํ„ฐ ์ ˆ๋ฐ˜์œผ๋กœ ๊ฐ์†Œ
    pcp->alloc_factor >>= 1;
    
    // PCP ๋ฆฌ์ŠคํŠธ์— ์ถ”๊ฐ€
    pindex = order_to_pindex(migratetype, order);
    list_add(&page->pcp_list, &pcp->lists[pindex]);
    pcp->count += 1 << order;
    
    // high-order ํŽ˜์ด์ง€ ๋“œ๋ ˆ์ธ ๊ฒ€์‚ฌ
    if (order && order <= PAGE_ALLOC_COSTLY_ORDER) {
    free_high = (pcp->free_count >= (batch + pcp->high_min / 2) &&
    (pcp->flags & PCPF_PREV_FREE_HIGH_ORDER) &&
    (!(pcp->flags & PCPF_FREE_HIGH_BATCH) ||
    pcp->count >= batch));
    pcp->flags |= PCPF_PREV_FREE_HIGH_ORDER;
    } else if (pcp->flags & PCPF_PREV_FREE_HIGH_ORDER) {
    pcp->flags &= ~PCPF_PREV_FREE_HIGH_ORDER;
    }
    
    // PCP high ์ดˆ๊ณผ ์‹œ ๋“œ๋ ˆ์ธ
    high = nr_pcp_high(pcp, zone, batch, free_high);
    if (pcp->count < high)
    return true;
    
    // ์ดˆ๊ณผ๋ถ„์„ buddy๋กœ ๋ฐ˜ํ™˜
    to_free = nr_pcp_free(pcp, batch, high, free_high);
    while (to_free > 0 && pcp->count > 0) {
    to_free_batched = min(to_free, batch);
    free_pcppages_bulk(zone, to_free_batched, pcp, pindex);
    to_free -= to_free_batched;
    }
    
    // ZONE_BELOW_HIGH ํ•ด์ œ ๊ฒ€์‚ฌ
    if (test_bit(ZONE_BELOW_HIGH, &zone->flags) &&
    zone_watermark_ok(zone, 0, high_wmark_pages(zone),
    ZONE_MOVABLE, 0)) {
    clear_bit(ZONE_BELOW_HIGH, &zone->flags);
    }
    
    return true;
    }

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

    1. alloc_factor ์ ˆ๋ฐ˜์œผ๋กœ ๊ฐ์†Œ (ํ• ๋‹น ๋นˆ๋„ ์–ต์ œ)

    2. PCP ๋ฆฌ์ŠคํŠธ์— ํŽ˜์ด์ง€ ์ถ”๊ฐ€

    3. high-order ํŽ˜์ด์ง€ ์—ฐ์† ๋“œ๋ ˆ์ธ ๊ฒ€์‚ฌ

    4. PCP high ์ดˆ๊ณผ ์‹œ free_pcppages_bulk()๋กœ buddy์— ๋ฐ˜ํ™˜

    5. ์กด ์›Œํ„ฐ๋งˆํฌ ์ถฉ์กฑ ์‹œ ZONE_BELOW_HIGH ํ”Œ๋ž˜๊ทธ ํ•ด์ œ


    ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ๊ณ„์ธต

    Buddy Allocator ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ๊ณ„์ธต

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

    Buddy Allocator ํ• ๋‹น/ํ•ด์ œ ํ๋ฆ„

    Slow Path ์ƒ์„ธ ํ๋ฆ„

    __alloc_pages_slowpath() ํ๋ฆ„

    PCP (Per-CPU Pages) ํ๋ฆ„

    PCP ํ• ๋‹น/๋ฐ˜ํ™˜ ํ๋ฆ„
    alloc_pages(gfp, order)
    โ””โ”€ __alloc_pages_noprof()              [mm/page_alloc.c:5279]
    โ””โ”€ __alloc_frozen_pages_noprof()   [mm/page_alloc.c:5214]
    โ”œโ”€ prepare_alloc_pages()
    โ”œโ”€ get_page_from_freelist()    [mm/page_alloc.c:3808]  โ† Fast Path
    โ”‚   โ””โ”€ for_each_zone_zonelist_nodemask()
    โ”‚       โ”œโ”€ zone_watermark_fast()
    โ”‚       โ””โ”€ rmqueue()
    โ”‚           โ””โ”€ __rmqueue()     [mm/page_alloc.c:2477]
    โ”‚               โ”œโ”€ __rmqueue_smallest()   โ† 1์ˆœ์œ„
    โ”‚               โ”‚   โ””โ”€ get_page_from_free_area()
    โ”‚               โ”‚   โ””โ”€ page_del_and_expand()
    โ”‚               โ”‚       โ””โ”€ expand()       โ† ๋ถ„ํ• 
    โ”‚               โ”œโ”€ __rmqueue_cma_fallback() โ† 2์ˆœ์œ„
    โ”‚               โ”œโ”€ __rmqueue_claim()       โ† 3์ˆœ์œ„
    โ”‚               โ””โ”€ __rmqueue_steal()       โ† 4์ˆœ์œ„
    โ”‚   โ””โ”€ prep_new_page()
    โ””โ”€ __alloc_pages_slowpath()    โ† Slow Path (fast ์‹คํŒจ ์‹œ)
    โ”œโ”€ wake_all_kswapds()       โ† kswapd ๋ฐฑ๊ทธ๋ผ์šด๋“œ ํšŒ์ˆ˜ ํŠธ๋ฆฌ๊ฑฐ
    โ”œโ”€ __alloc_pages_direct_reclaim()  โ† ์ง์ ‘ ํšŒ์ˆ˜
    โ”‚   โ”œโ”€ __perform_reclaim()
    โ”‚   โ”‚   โ””โ”€ try_to_free_pages()
    โ”‚   โ””โ”€ get_page_from_freelist()
    โ”œโ”€ __alloc_pages_direct_compact()  โ† ์ง์ ‘ compaction
    โ””โ”€ __alloc_pages_may_oom()         โ† OOM Killer ๋ฐœ๋™
    
    free_pages(addr, order)
    โ””โ”€ __free_pages()                      [mm/page_alloc.c:5367]
    โ””โ”€ ___free_pages()
    โ””โ”€ __free_frozen_pages()       [mm/page_alloc.c:2964]
    โ”œโ”€ pcp_allowed_order() ์ฒดํฌ
    โ”‚   โ””โ”€ ์ดˆ๊ณผ ์‹œ โ†’ __free_pages_ok() (buddy ์ง์ ‘ ๋ฐ˜ํ™˜)
    โ”œโ”€ get_pfnblock_migratetype()
    โ”‚   โ””โ”€ ISOLATE โ†’ free_one_page() (์ฆ‰์‹œ ๋ฐ˜ํ™˜)
    โ””โ”€ pcp_spin_trylock()
    โ”œโ”€ ์„ฑ๊ณต โ†’ free_frozen_page_commit()  โ† PCP์— ์ถ”๊ฐ€
    โ”‚   โ”œโ”€ pcp->alloc_factor ์ ˆ๋ฐ˜์œผ๋กœ ๊ฐ์†Œ
    โ”‚   โ”œโ”€ PCP ๋ฆฌ์ŠคํŠธ์— ์ถ”๊ฐ€
    โ”‚   โ”œโ”€ PCP high ์ดˆ๊ณผ ์‹œ โ†’ free_pcppages_bulk()
    โ”‚   โ””โ”€ ์กด ์›Œํ„ฐ๋งˆํฌ ์ถฉ์กฑ ์‹œ โ†’ ZONE_BELOW_HIGH ํ•ด์ œ
    โ””โ”€ ์‹คํŒจ โ†’ free_one_page() (buddy ์ง์ ‘ ๋ฐ˜ํ™˜)
    โ””โ”€ __free_one_page()   [mm/page_alloc.c:978]
    โ””โ”€ while (order < MAX_PAGE_ORDER)
    โ”œโ”€ find_buddy_page_pfn()
    โ”œโ”€ __del_page_from_free_list()
    โ””โ”€ __add_to_free_list()  โ† ๋ณ‘ํ•ฉ ํ›„ ์ถ”๊ฐ€

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

    ํ• ๋‹น ํ”Œ๋ž˜๊ทธ (GFP)๋ณ„ ๋™์ž‘

    GFP ํ”Œ๋ž˜๊ทธ์›Œํ„ฐ๋งˆํฌdirect reclaimcompactionOOMPCP ์‚ฌ์šฉ
    `GFP_KERNEL`LOWOOOO
    `GFP_ATOMIC`MIN (์˜ˆ์•ฝ ์ ‘๊ทผ)XXXX
    `GFP_NOWAIT`LOWXXXO
    `GFP_NOIO`LOWO (I/O ์ œ์™ธ)OOO
    `GFP_NOFS`LOWO (FS ์ œ์™ธ)OOO
    `GFP_DMA`MINOOOO

    Migratetype Fallback ์ˆœ์„œ

    ์š”์ฒญ ํƒ€์ž…1์ฐจ fallback2์ฐจ fallback
    UNMOVABLERECLAIMABLEMOVABLE
    MOVABLERECLAIMABLEUNMOVABLE
    RECLAIMABLEUNMOVABLEMOVABLE

    Zone Watermark ๋™์ž‘

    ์กฐ๊ฑด๋™์ž‘
    free โ‰ฅ HIGH์ •์ƒ ํ• ๋‹น ๊ฐ€๋Šฅ
    LOW โ‰ค free < HIGHkswapd wake (background reclaim)
    MIN โ‰ค free < LOWdirect reclaim ๋ฐœ์ƒ ๊ฐ€๋Šฅ
    free < MINOOM ๋˜๋Š” ํ• ๋‹น ์‹คํŒจ

    ์™ธ๋ถ€/๋‚ด๋ถ€ ๋‹จํŽธํ™” ๋น„๊ต

    ์ข…๋ฅ˜์›์ธ๋Œ€ํ‘œ ์ง€ํ‘œ์ปค๋„ ๋Œ€์‘
    ์™ธ๋ถ€ ๋‹จํŽธํ™”free ๊ณต๊ฐ„์ด ์ž‘์€ ์กฐ๊ฐ์œผ๋กœ ํฉ์–ด์ง`/proc/buddyinfo`, `/proc/extfraginfo`buddy ๋ณ‘ํ•ฉ, compaction
    ๋‚ด๋ถ€ ๋‹จํŽธํ™”์š”์ฒญ๋ณด๋‹ค ํฐ ๋ธ”๋ก์„ ํ• ๋‹น`/proc/slabinfo`, `kmalloc-*` ์‚ฌ์šฉ๋ฅ SLUB size class, cache reuse

    Buddy ๊ด€๋ จ API ๊ณ„์ธต

    API๋ฐ˜ํ™˜ ํ˜•ํƒœ๋ฌผ๋ฆฌ ์—ฐ์†์„ฑ๋‚ด๋ถ€ ๊ฒฝ๋กœ์ฃผ ์šฉ๋„
    `alloc_page(gfp)``struct page *`1 page ์—ฐ์†`alloc_pages(gfp, 0)`๋‹จ์ผ ํŽ˜์ด์ง€ ํ• ๋‹น
    `alloc_pages(gfp, order)``struct page *``2^order` pages ์—ฐ์†Buddy ์ง์ ‘๊ณ ์ฐจ ํŽ˜์ด์ง€/compound page ๊ธฐ๋ฐ˜
    `__get_free_pages(gfp, order)``unsigned long` ์ฃผ์†Œ`2^order` pages ์—ฐ์†`alloc_pages()` + `page_address()`์ปค๋„ ์„ ํ˜• ์ฃผ์†Œ๊ฐ€ ํ•„์š”ํ•œ ์ €์ˆ˜์ค€ ๊ฒฝ๋กœ
    `kmalloc(size, gfp)``void *`๋ฌผ๋ฆฌ ์—ฐ์†SLUB โ†’ Buddy์ž‘์€ ์ปค๋„ ์˜ค๋ธŒ์ ํŠธ
    `vmalloc(size)``void *`๋ฌผ๋ฆฌ ๋ถˆ์—ฐ์†, ๊ฐ€์ƒ ์—ฐ์†ํŽ˜์ด์ง€๋ณ„ Buddy โ†’ vmapํฐ ๋ฒ„ํผ, ๋ฌผ๋ฆฌ ์—ฐ์† ๋ถˆํ•„์š”

    GFP ์ปจํ…์ŠคํŠธ ์ œ์•ฝ

    ํ˜ธ์ถœ ์ปจํ…์ŠคํŠธ๊ถŒ์žฅ GFP์Šฌ๋ฆฝ ๊ฐ€๋ŠฅBuddy ๋™์ž‘์ƒ ์˜๋ฏธ
    ์ผ๋ฐ˜ ํ”„๋กœ์„ธ์Šค ์ปจํ…์ŠคํŠธ`GFP_KERNEL`Odirect reclaim, compaction, OOM๊นŒ์ง€ ์ง„์ž… ๊ฐ€๋Šฅ
    ์ธํ„ฐ๋ŸฝํŠธ/hardirq`GFP_ATOMIC`X์˜ˆ์•ฝ ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ์€ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์‹คํŒจ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Œ
    softirq/timer`GFP_ATOMIC`Xreclaim์œผ๋กœ ์ž ๋“ค ์ˆ˜ ์—†์œผ๋ฏ€๋กœ fast/๊ธด๊ธ‰ ๊ฒฝ๋กœ ์œ„์ฃผ
    spinlock ๋ณด์œ  ์ค‘`GFP_ATOMIC`X`GFP_KERNEL` ์‚ฌ์šฉ ์‹œ scheduling while atomic ์œ„ํ—˜
    block I/O ๋‚ด๋ถ€`GFP_NOIO`OํšŒ์ˆ˜๋Š” ๊ฐ€๋Šฅํ•˜์ง€๋งŒ I/O ์žฌ๊ท€๋ฅผ ๋ง‰์Œ
    ํŒŒ์ผ์‹œ์Šคํ…œ ๋‚ด๋ถ€`GFP_NOFS`OFS ์žฌ์ง„์ž…์„ ๋ง‰๊ณ  ํšŒ์ˆ˜ ๋ฒ”์œ„๋ฅผ ์ œํ•œ

    ์ฆ์ƒ๋ณ„ ์ง„๋‹จ ํ‘œ

    ์ฆ์ƒ์šฐ์„  ์ ๊ฒ€Buddy ๊ด€์  ํ•ด์„ํ›„์† ์กฐ์น˜
    `page allocation failure: order:N``dmesg`, `/proc/buddyinfo`์ด free๋Š” ์žˆ์–ด๋„ ํ•ด๋‹น order ์ด์ƒ์˜ ์—ฐ์† ๋ธ”๋ก ๋ถ€์กฑcompaction ์ƒํƒœ, THP/CMA ์š”์ฒญ, migratetype ์˜ค์—ผ ํ™•์ธ
    ๊ณ ์ฐจ order๊ฐ€ ๊ณ„์† 0`/proc/buddyinfo`, `/proc/extfraginfo`์™ธ๋ถ€ ๋‹จํŽธํ™” ๋˜๋Š” MOVABLE ๋ถ€์กฑ`compact_*` vmstat, `compact_memory`, THP defrag ์ •์ฑ… ํ™•์ธ
    `allocstall` ์ฆ๊ฐ€`/proc/vmstat`fast path ์‹คํŒจ ํ›„ direct reclaim ๋นˆ๋ฒˆreclaim ๋ณ‘๋ชฉ, working set, memcg limit ํ™•์ธ
    PCP count ๊ณผ๋Œ€/๋ถˆ๊ท ํ˜•`/proc/zoneinfo` pagesetsCPU๋ณ„ ์บ์‹œ์— free page๊ฐ€ ๋จธ๋ฌผ๋Ÿฌ zone free list ๊ฐ€์‹œ์„ฑ์ด ๋‚ฎ์Œdrain ๋™์ž‘, CPU hotplug, zone pressure ํ™•์ธ
    OOM ์ „ `kswapd` ๊ณผํ™œ์„ฑ`dmesg`, PSI, `pgscan/pgsteal`watermark ์•„๋ž˜๋กœ ๋–จ์–ด์ ธ ํšŒ์ˆ˜์™€ ํ• ๋‹น์ด ๊ฒฝํ•ฉslab/page cache ์ฆ๊ฐ€, swap/zswap, memcg ์ด๋ฒคํŠธ ํ™•์ธ

    Linux 7.0 ์›๋ฌธ ์ƒ์„ธ

    Buddy PFN ๊ณ„์‚ฐ๊ณผ ๊ฒ€์ฆ

    buddy_pfn = pfn ^ (1 << order) ๊ณต์‹์€ mm/internal.h์— ๊ทธ๋Œ€๋กœ ๊ตฌํ˜„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ™์€ order์—์„œ ํ•œ ๋น„ํŠธ๋งŒ ๋’ค์ง‘์œผ๋ฉด ๊ฐ™์€ ์ƒ์œ„ ๋ธ”๋ก์„ ๊ณต์œ ํ•˜๋Š” ์ธ์ ‘ buddy๋ฅผ O(1)์— ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    // mm/internal.h:755-759
    static inline unsigned long
    __find_buddy_pfn(unsigned long page_pfn, unsigned int order)
    {
    	return page_pfn ^ (1 << order);
    }
    // mm/internal.h:775-788
    static inline struct page *find_buddy_page_pfn(struct page *page,
    			unsigned long pfn, unsigned int order, unsigned long *buddy_pfn)
    {
    	unsigned long __buddy_pfn = __find_buddy_pfn(pfn, order);
    	struct page *buddy;
    
    	buddy = page + (__buddy_pfn - pfn);
    	if (buddy_pfn)
    		*buddy_pfn = __buddy_pfn;
    
    	if (page_is_buddy(page, buddy, order))
    		return buddy;
    	return NULL;
    }

    ๋ถ„ํ• (split) ์›๋ฌธ

    expand()๋Š” ์ƒ์œ„ order ๋ธ”๋ก์„ ๋‚ฎ์€ order๋กœ ๋‚ด๋ฆฌ๋ฉด์„œ ๋’ค์ชฝ half๋ฅผ free list์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. set_page_guard()๊ฐ€ true์ด๋ฉด debug guard page๋กœ ๋‚จ๊ธฐ๊ณ  free list ์ถ”๊ฐ€๋ฅผ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค.

    // mm/page_alloc.c:1732-1758
    static inline unsigned int expand(struct zone *zone, struct page *page, int low,
    				  int high, int migratetype)
    {
    	unsigned int size = 1 << high;
    	unsigned int nr_added = 0;
    
    	while (high > low) {
    		high--;
    		size >>= 1;
    		VM_BUG_ON_PAGE(bad_range(zone, &page[size]), &page[size]);
    
    		/*
    		 * Mark as guard pages (or page), that will allow to
    		 * merge back to allocator when buddy will be freed.
    		 * Corresponding page table entries will not be touched,
    		 * pages will stay not present in virtual address space
    		 */
    		if (set_page_guard(zone, &page[size], high))
    			continue;
    
    		__add_to_free_list(&page[size], zone, high, migratetype, false);
    		set_buddy_order(&page[size], high);
    		nr_added += size;
    	}
    
    	return nr_added;
    }

    ๋ณ‘ํ•ฉ(coalescing) ์›๋ฌธ

    ํ•ด์ œ ๊ฒฝ๋กœ๋Š” ๊ฐ™์€ order์˜ buddy๊ฐ€ free์ธ์ง€ ํ™•์ธํ•˜๊ณ , ๋ณ‘ํ•ฉ ๊ฐ€๋Šฅํ•œ migratetype์ด๋ฉด ๊ธฐ์กด buddy๋ฅผ free list์—์„œ ์ œ๊ฑฐํ•œ ๋’ค order๋ฅผ ํ•˜๋‚˜์”ฉ ์˜ฌ๋ฆฝ๋‹ˆ๋‹ค.

    // mm/page_alloc.c:998-1059
    	while (order < MAX_PAGE_ORDER) {
    		int buddy_mt = migratetype;
    
    		if (compaction_capture(capc, page, order, migratetype)) {
    			account_freepages(zone, -(1 << order), migratetype);
    			return;
    		}
    
    		buddy = find_buddy_page_pfn(page, pfn, order, &buddy_pfn);
    		if (!buddy)
    			goto done_merging;
    
    		if (unlikely(order >= pageblock_order)) {
    			/*
    			 * We want to prevent merge between freepages on pageblock
    			 * without fallbacks and normal pageblock. Without this,
    			 * pageblock isolation could cause incorrect freepage or CMA
    			 * accounting or HIGHATOMIC accounting.
    			 */
    			buddy_mt = get_pfnblock_migratetype(buddy, buddy_pfn);
    
    			if (migratetype != buddy_mt &&
    			    (!migratetype_is_mergeable(migratetype) ||
    			     !migratetype_is_mergeable(buddy_mt)))
    				goto done_merging;
    		}
    
    		/*
    		 * Our buddy is free or it is CONFIG_DEBUG_PAGEALLOC guard page,
    		 * merge with it and move up one order.
    		 */
    		if (page_is_guard(buddy))
    			clear_page_guard(zone, buddy, order);
    		else
    			__del_page_from_free_list(buddy, zone, order, buddy_mt);
    
    		if (unlikely(buddy_mt != migratetype)) {
    			/*
    			 * Match buddy type. This ensures that an
    			 * expand() down the line puts the sub-blocks
    			 * on the right freelists.
    			 */
    			change_pageblock_range(buddy, order, migratetype);
    		}
    
    		combined_pfn = buddy_pfn & pfn;
    		page = page + (combined_pfn - pfn);
    		pfn = combined_pfn;
    		order++;
    	}
    
    done_merging:
    	set_buddy_order(page, order);
    
    	if (fpi_flags & FPI_TO_TAIL)
    		to_tail = true;
    	else if (is_shuffle_order(order))
    		to_tail = shuffle_pick_tail();
    	else
    		to_tail = buddy_merge_likely(pfn, buddy_pfn, page, order);
    
    	__add_to_free_list(page, zone, order, migratetype, to_tail);

    PCP ํ• ๋‹น ์›๋ฌธ

    Linux 7.0์˜ rmqueue()๋Š” pcp_allowed_order(order)์ด๋ฉด ๋จผ์ € per-CPU list๋ฅผ ์‹œ๋„ํ•˜๊ณ , ์‹คํŒจํ•˜๊ฑฐ๋‚˜ PCP ๋Œ€์ƒ order๊ฐ€ ์•„๋‹ˆ๋ฉด buddy ๊ฒฝ๋กœ(rmqueue_buddy)๋กœ ๋‚ด๋ ค๊ฐ‘๋‹ˆ๋‹ค.

    // mm/page_alloc.c:3336-3363
    struct page *__rmqueue_pcplist(struct zone *zone, unsigned int order,
    			int migratetype,
    			unsigned int alloc_flags,
    			struct per_cpu_pages *pcp,
    			struct list_head *list)
    {
    	struct page *page;
    
    	do {
    		if (list_empty(list)) {
    			int batch = nr_pcp_alloc(pcp, zone, order);
    			int alloced;
    
    			alloced = rmqueue_bulk(zone, order,
    					batch, list,
    					migratetype, alloc_flags);
    
    			pcp->count += alloced << order;
    			if (unlikely(list_empty(list)))
    				return NULL;
    		}
    
    		page = list_first_entry(list, struct page, pcp_list);
    		list_del(&page->pcp_list);
    		pcp->count -= 1 << order;
    	} while (check_new_pages(page, order));
    
    	return page;
    }
    // mm/page_alloc.c:3417-3425
    	if (likely(pcp_allowed_order(order))) {
    		page = rmqueue_pcplist(preferred_zone, zone, order,
    				       migratetype, alloc_flags);
    		if (likely(page))
    			goto out;
    	}
    
    	page = rmqueue_buddy(preferred_zone, zone, order, alloc_flags,
    							migratetype);

    Slow path ํ•ต์‹ฌ ๋ถ„๊ธฐ ์›๋ฌธ

    slow path๋Š” ๋จผ์ € ์กฐ์ •๋œ alloc_flags๋กœ ๋‹ค์‹œ fast path๋ฅผ ์‹œ๋„ํ•˜๊ณ , ์‹คํŒจํ•˜๋ฉด direct reclaim, direct compaction, ์žฌ์‹œ๋„ ํŒ๋‹จ, OOM, __GFP_NOFAIL ์ฒ˜๋ฆฌ ์ˆœ์„œ๋กœ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

    // mm/page_alloc.c:4796-4807
    retry:
    	/* ๋ฃจํ”„ํ•˜๋Š” ๋™์•ˆ kswapd๊ฐ€ ์‹ค์ˆ˜๋กœ ์ž ๋“ค์ง€ ์•Š๋„๋ก ๋ณด์žฅ */
    	if (alloc_flags & ALLOC_KSWAPD)
    		wake_all_kswapds(order, gfp_mask, ac);
    
    	/*
    	 * The adjusted alloc_flags might result in immediate success, so try
    	 * that first
    	 */
    	page = get_page_from_freelist(gfp_mask, order, alloc_flags, ac);
    	if (page)
    		goto got_pg;
    // mm/page_alloc.c:4844-4856
    	/* ์ง์ ‘ ํšŒ์ˆ˜๋ฅผ ์‹œ๋„ํ•œ ํ›„ ํ• ๋‹น */
    	if (!compact_first) {
    		page = __alloc_pages_direct_reclaim(gfp_mask, order, alloc_flags,
    							ac, &did_some_progress);
    		if (page)
    			goto got_pg;
    	}
    
    	/* ์ง์ ‘ ์ปดํŒฉ์…˜์„ ์‹œ๋„ํ•œ ํ›„ ํ• ๋‹น */
    	page = __alloc_pages_direct_compact(gfp_mask, order, alloc_flags, ac,
    					compact_priority, &compact_result);
    	if (page)
    		goto got_pg;
    // mm/page_alloc.c:4906-4925
    	if (should_reclaim_retry(gfp_mask, order, ac, alloc_flags,
    				 did_some_progress > 0, &no_progress_loops))
    		goto retry;
    
    	/*
    	 * It doesn't make any sense to retry for the compaction if the order-0
    	 * reclaim is not able to make any progress because the current
    	 * implementation of the compaction depends on the sufficient amount
    	 * of free memory (see __compaction_suitable)
    	 */
    	if (did_some_progress > 0 && can_compact &&
    			should_compact_retry(ac, order, alloc_flags,
    				compact_result, &compact_priority,
    				&compaction_retries))
    		goto retry;
    
    	/* ํšŒ์ˆ˜/์ปดํŒฉ์…˜์œผ๋กœ ๋Œ€์ฒด๋ฅผ ๋ฐฉ์ง€ํ•˜์ง€ ๋ชปํ•จ */
    	if (defrag_mode && (alloc_flags & ALLOC_NOFRAGMENT)) {
    		alloc_flags &= ~ALLOC_NOFRAGMENT;
    		goto retry;
    	}
    // mm/page_alloc.c:4936-4939
    	/* ํšŒ์ˆ˜์— ์‹คํŒจํ•จ, ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ ์‹œ์ž‘ */
    	page = __alloc_pages_may_oom(gfp_mask, order, ac, &did_some_progress);
    	if (page)
    		goto got_pg;
    // mm/page_alloc.c:4966-4987
    	if (unlikely(nofail)) {
    		/*
    		 * Lacking direct_reclaim we can't do anything to reclaim memory,
    		 * we disregard these unreasonable nofail requests and still
    		 * return NULL
    		 */
    		if (!can_direct_reclaim)
    			goto fail;
    
    		/*
    		 * Help non-failing allocations by giving some access to memory
    		 * reserves normally used for high priority non-blocking
    		 * allocations but do not use ALLOC_NO_WATERMARKS because this
    		 * could deplete whole memory reserves which would just make
    		 * the situation worse.
    		 */
    		page = __alloc_pages_cpuset_fallback(gfp_mask, order, ALLOC_MIN_RESERVE, ac);
    		if (page)
    			goto got_pg;
    
    		cond_resched();
    		goto retry;
    	}

    ๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • SLUB ํ• ๋‹น์ž
  • Memblock ํ• ๋‹น์ž
  • ํŽ˜์ด์ง€ ํšŒ์ˆ˜ (vmscan)
  • Compaction
  • CMA
  • OOM Killer