๏ปฟ# List LRU (List-based Least Recently Used)

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

๊ฐœ์š” (Overview)

List LRU๋Š” ๋ฆฌ๋ˆ…์Šค ์ปค๋„์—์„œ ๋ฒ”์šฉ LRU(Least Recently Used) ์ธํ”„๋ผ๋ฅผ ์ œ๊ณตํ•˜๋Š” ํ•˜์œ„ ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค. dentry ์บ์‹œ, inode ์บ์‹œ, shmem ํŽ˜์ด์ง€ ๋“ฑ ๋‹ค์–‘ํ•œ ์ปค๋„ ์บ์‹œ ํ•ญ๋ชฉ๋“ค์„ LRU ์ˆœ์„œ๋กœ ๊ด€๋ฆฌํ•˜๊ณ , ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ ์‹œ ํšจ๊ณผ์ ์œผ๋กœ ํšŒ์ˆ˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. List LRU๋Š” struct list_head ๊ธฐ๋ฐ˜์˜ ๋‹จ์ผ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„๋˜๋ฉฐ, NUMA ๋…ธ๋“œ๋ณ„, ๋ฉ”๋ชจ๋ฆฌ cgroup๋ณ„๋กœ ๋…๋ฆฝ์ ์ธ ๋ฆฌ์ŠคํŠธ๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

์ด ์ธํ”„๋ผ์˜ ํ•ต์‹ฌ์€ shrinker์™€์˜ ํ†ตํ•ฉ์ž…๋‹ˆ๋‹ค. list_lru_walk_one()๊ณผ ๊ฐ™์€ ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ฝœ๋ฐฑ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ ํšŒ์ˆ˜ ๊ฐ€๋Šฅํ•œ ํ•ญ๋ชฉ์„ ๊ฒฉ๋ฆฌ(isolate)ํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. CONFIG_MEMCG๊ฐ€ ํ™œ์„ฑํ™”๋œ ์‹œ์Šคํ…œ์—์„œ๋Š” ๋ฉ”๋ชจ๋ฆฌ cgroup๋ณ„๋กœ ๋ณ„๋„์˜ LRU ๋ฆฌ์ŠคํŠธ๋ฅผ ํ• ๋‹นํ•˜์—ฌ ์ปจํ…Œ์ด๋„ˆ ๊ฐ„ ๋ฉ”๋ชจ๋ฆฌ ๊ฒฉ๋ฆฌ๋ฅผ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

page reclaim์—์„œ ๋งํ•˜๋Š” active/inactive anon/file LRU์™€๋Š” ์—ญํ• ์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค. List LRU๋Š” ํŽ˜์ด์ง€ ์ž์ฒด๋ฅผ ๊ด€๋ฆฌํ•˜์ง€ ์•Š๊ณ , dentry๋‚˜ inode์ฒ˜๋Ÿผ shrinker๊ฐ€ ํšŒ์ˆ˜ํ•  ์ปค๋„ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋ชจ์•„ ๋‘๋Š” ๋ณด์กฐ ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ shrink_slab() ๊ฒฝ๋กœ์—์„œ ํ˜ธ์ถœ๋˜์–ด ์Šฌ๋žฉ ๊ณ„์—ด ํšŒ์ˆ˜์™€ ํ•จ๊ป˜ ์›€์ง์ด๋ฉฐ, ํŽ˜์ด์ง€ ํšŒ์ˆ˜ LRU์™€๋Š” ๋Œ€์ƒ๊ณผ ๋‹จ์œ„๊ฐ€ ๊ตฌ๋ถ„๋ฉ๋‹ˆ๋‹ค.

์ผ์ƒ ๋น„์œ 

List LRU๋Š” ๋„์„œ๊ด€์—์„œ ์˜ค๋ž˜๋œ ์žก์ง€๋ฅผ ์ •๋ฆฌํ•˜๋Š” ์‹œ์Šคํ…œ๊ณผ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ์ฃผ์ œ(๋ฉ”๋ชจ๋ฆฌ cgroup)๋ณ„๋กœ ๋ณ„๋„์˜ ์„œ๊ฐ€(NUMA ๋…ธ๋“œ)๊ฐ€ ์žˆ๊ณ , ๊ฐ ์„œ๊ฐ€์—์„œ๋Š” ์ฝํžŒ ์ง€ ์˜ค๋ž˜๋œ ์žก์ง€๋ถ€ํ„ฐ ์ •๋ฆฌ ๋Œ€์ƒ์œผ๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ์ •๋ฆฌํ•  ๋•Œ๋Š” ๋จผ์ € ์žก์ง€๋ฅผ ๋นผ์„œ(isolate) ๋ชฉ๋ก์—์„œ ์ง€์šฐ๊ณ , ํ•„์š”ํ•˜๋ฉด ๋‹ค๋ฅธ ์„œ๊ฐ€๋กœ ์˜ฎ๊น๋‹ˆ๋‹ค.

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

mm/list_lru.c                    โ† LRU ํ•ต์‹ฌ ๋กœ์ง (618์ค„)
include/linux/list_lru.h         โ† ๊ตฌ์กฐ์ฒด ์ •์˜, API ์„ ์–ธ (289์ค„)
mm/slab.h                        โ† ๋‚ด๋ถ€ ํ—ฌํผ
mm/internal.h                    โ† set_shrinker_bit() ๋“ฑ
include/linux/memcontrol.h       โ† memcg ์—ฐ๋™

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

# 1. list_lru ๊ด€๋ จ ์ปค๋„ ์‹ฌ๋ณผ ํ™•์ธ
cat /proc/kallsyms | grep -E "list_lru_add|list_lru_del|list_lru_walk" | head -10

# 2. CONFIG_MEMCG ์ปดํŒŒ์ผ ์˜ต์…˜ ํ™•์ธ
grep CONFIG_MEMCG /boot/config-$(uname -r) 2>/dev/null || zcat /proc/config.gz 2>/dev/null | grep CONFIG_MEMCG

# 3. slab ์บ์‹œ ํ†ต๊ณ„์—์„œ dentry/inode ์บ์‹œ ํ™•์ธ (list_lru ์‚ฌ์šฉ์ฒ˜)
cat /proc/slabinfo | grep -E "dentry|inode_cache|shmem_inode_cache"

# 4. ๋ฉ”๋ชจ๋ฆฌ cgroup๋ณ„ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ํ™•์ธ
cat /sys/fs/cgroup/memory/memory.limit_in_bytes 2>/dev/null || cat /sys/fs/cgroup/memory.max 2>/dev/null

# 5. shrinker ๋“ฑ๋ก ์ •๋ณด ํ™•์ธ
cat /sys/kernel/mm/leak_debug/objects 2>/dev/null || echo "leak_debug not available"

# 6. /proc/vmstat์—์„œ ์บ์‹œ ํšŒ์ˆ˜ ๊ด€๋ จ ์นด์šดํ„ฐ
cat /proc/vmstat | grep -E "pgscan|pgsteal|nr_slab|nr_dentry|nr_inode"

# 7. list_lru ๊ด€๋ จ ๋ชจ๋“ˆ ์ •๋ณด
modinfo list_lru 2>/dev/null || echo "Built-in module"

# 8. memcg slab ํ†ต๊ณ„
cat /sys/fs/cgroup/memory/memory.stat 2>/dev/null | grep -E "slab|dentry|inode" | head -10

# 9. ์ปค๋„ ๋นŒ๋“œ ์˜ต์…˜์—์„œ list_lru ๊ด€๋ จ ํ™•์ธ
grep -r "CONFIG_LIST_LRU" /boot/config-$(uname -r) 2>/dev/null

# 10. list_lru ์‚ฌ์šฉ ์ฝ”๋“œ ์œ„์น˜ ์ถ”์ 
grep -rn "list_lru_add\|list_lru_del\|list_lru_walk" /usr/src/linux-*/fs/ 2>/dev/null | head -10

# 11. ํŽ˜์ด์ง€ ํšŒ์ˆ˜ ์••๋ ฅ๊ณผ ์—ฐ๊ณ„ ์ง€ํ‘œ ํ™•์ธ
cat /proc/vmstat | grep -E "pgscan|pgsteal|allocstall|kswapd"

# 12. ํ˜„์žฌ ๋ฉ”๋ชจ๋ฆฌ ์••๋ ฅ ํ™•์ธ
cat /proc/pressure/memory

# 13. ์Šค์™‘ ์ƒํƒœ ํ™•์ธ
swapon --show

# 14. ์Šค์™‘ ์ •์ฑ… ํ™•์ธ
cat /proc/sys/vm/swappiness

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

1. `struct list_lru_one` โ€” ๊ฐœ๋ณ„ LRU ๋ฆฌ์ŠคํŠธ

๊ฐ memcg + NUMA node ์กฐํ•ฉ์— ํ•˜๋‚˜์”ฉ ์กด์žฌํ•˜๋Š” ๊ธฐ๋ณธ LRU ๋ฆฌ์ŠคํŠธ์ž…๋‹ˆ๋‹ค.

// include/linux/list_lru.h:31-37
struct list_lru_one {
struct list_head    list;       // ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ ํ—ค๋“œ
// memcg reparenting ์ค‘ ์Œ์ˆ˜๊ฐ€ ๋  ์ˆ˜ ์žˆ์Œ
long                nr_items;   // ํ˜„์žฌ ๋ฆฌ์ŠคํŠธ์˜ ์•„์ดํ…œ ์ˆ˜
// ๋ชจ๋“  ํ•„๋“œ๋ฅผ ๋ณดํ˜ธํ•˜๋Š” ์Šคํ•€๋ฝ
spinlock_t          lock;
};

2. `struct list_lru_node` โ€” NUMA ๋…ธ๋“œ๋ณ„ LRU

NUMA ์‹œ์Šคํ…œ์—์„œ ๊ฐ ๋…ธ๋“œ๋งˆ๋‹ค ํ•˜๋‚˜์”ฉ ์กด์žฌํ•˜๋ฉฐ, ๋ฃจํŠธ cgroup์šฉ ๊ธ€๋กœ๋ฒŒ LRU์™€ ์นด์šดํ„ฐ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

// include/linux/list_lru.h:45-49
struct list_lru_node {
// ๋ฃจํŠธ cgroup์šฉ ๊ธ€๋กœ๋ฒŒ ๋ฆฌ์ŠคํŠธ
struct list_lru_one lru;
atomic_long_t       nr_items;   // ์ „์ฒด ์•„์ดํ…œ ์ˆ˜ (atomic)
} ____cacheline_aligned_in_smp;

3. `struct list_lru_memcg` โ€” memcg๋ณ„ LRU

๋ฉ”๋ชจ๋ฆฌ cgroup์ด ํ• ๋‹น๋  ๋•Œ๋งˆ๋‹ค ๋™์ ์œผ๋กœ ํ• ๋‹น๋˜๋Š” per-memcg LRU์ž…๋‹ˆ๋‹ค.

// include/linux/list_lru.h:39-43
struct list_lru_memcg {
struct rcu_head     rcu;        // RCU ํ•ด์ œ์šฉ
// ๋…ธ๋“œ๋ณ„ per-cgroup ๋ฆฌ์ŠคํŠธ (์œ ์—ฐํ•œ ๋ฐฐ์—ด)
struct list_lru_one node[];
};

4. `struct list_lru` โ€” ์ตœ์ƒ์œ„ LRU ๊ตฌ์กฐ์ฒด

List LRU์˜ ์ตœ์ƒ์œ„ ๊ตฌ์กฐ์ฒด๋กœ, ๋ชจ๋“  NUMA ๋…ธ๋“œ์™€ memcg ์ •๋ณด๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

// include/linux/list_lru.h:51-62
struct list_lru {
struct list_lru_node    *node;      // NUMA ๋…ธ๋“œ๋ณ„ LRU ๋ฐฐ์—ด
#ifdef CONFIG_MEMCG
struct list_head        list;       // memcg_list_lrus ์—ฐ๊ฒฐ์šฉ
int                     shrinker_id; // ์—ฐ๊ฒฐ๋œ shrinker ID
bool                    memcg_aware; // memcg ์ธ์‹ ์—ฌ๋ถ€
struct xarray           xa;         // memcg ID โ†’ list_lru_memcg ๋งคํ•‘
#endif
#ifdef CONFIG_LOCKDEP
struct lock_class_key   *key;       // lockdep ํด๋ž˜์Šค ํ‚ค
#endif
};

5. `enum lru_status` โ€” walk ์ฝœ๋ฐฑ ๋ฐ˜ํ™˜ ๊ฐ’

list_lru_walk_one()์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ƒํƒœ ๊ฐ’์ž…๋‹ˆ๋‹ค.

// include/linux/list_lru.h:19-29
enum lru_status {
LRU_REMOVED,        // ์•„์ดํ…œ์ด ๋ฆฌ์ŠคํŠธ์—์„œ ์ œ๊ฑฐ๋จ
LRU_REMOVED_RETRY,  // ์ œ๊ฑฐ๋จ, ์ž ๊ธˆ ํ•ด์ œ ํ›„ ๋‹ค์‹œ ์‹œ๋„
LRU_ROTATE,         // ์•„์ดํ…œ์ด ์ฐธ์กฐ๋จ, ๋‹ค์‹œ ์ˆœํšŒ
LRU_SKIP,           // ์•„์ดํ…œ ์ž ๊ธˆ ๋ถˆ๊ฐ€, ๊ฑด๋„ˆ๋œ€
LRU_RETRY,          // ์•„์ดํ…œ ํ•ด์ œ ๋ถˆ๊ฐ€, ๋‹ค์‹œ ์‹œ๋„
LRU_STOP,           // ์ˆœํšŒ ์ค‘์ง€
};

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

1. `list_lru_add()` / `list_lru_add_obj()`

LRU ๋ฆฌ์ŠคํŠธ์— ์•„์ดํ…œ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. list_lru_add()๋Š” ๋ช…์‹œ์ ์œผ๋กœ nid์™€ memcg๋ฅผ ์ง€์ •ํ•˜๊ณ , list_lru_add_obj()๋Š” ์•„์ดํ…œ์˜ ๋ฌผ๋ฆฌ ์ฃผ์†Œ์—์„œ ์ž๋™์œผ๋กœ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

// mm/list_lru.c:161-181
bool list_lru_add(struct list_lru *lru, struct list_head *item, int nid,
struct mem_cgroup *memcg)
{
struct list_lru_node *nlru = &lru->node[nid];
struct list_lru_one *l;

l = lock_list_lru_of_memcg(lru, nid, memcg, false, false);
if (!l)
return false;
if (list_empty(item)) {
list_add_tail(item, &l->list);
// ์ฒซ ๋ฒˆ์งธ ์š”์†Œ ์ถ”๊ฐ€ ์‹œ shrinker ๋น„ํŠธ ์„ค์ •
if (!l->nr_items++)
set_shrinker_bit(memcg, nid, lru_shrinker_id(lru));
unlock_list_lru(l, false);
atomic_long_inc(&nlru->nr_items);
return true;
}
unlock_list_lru(l, false);
return false;
}

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

  • lock_list_lru_of_memcg()๋กœ memcg๋ณ„ ๋ฆฌ์ŠคํŠธ ํš๋“ ์‹คํŒจ ์‹œ false ๋ฐ˜ํ™˜
  • ์•„์ดํ…œ์ด ์ด๋ฏธ ๋ฆฌ์ŠคํŠธ์— ์žˆ๋Š” ๊ฒฝ์šฐ(list_empty() ์‹คํŒจ) ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์Œ
  • ์ฒซ ๋ฒˆ์งธ ์š”์†Œ ์ถ”๊ฐ€ ์‹œ set_shrinker_bit()๋กœ shrinker์— ์•Œ๋ฆผ
  • 2. `list_lru_del()` / `list_lru_del_obj()`

    LRU ๋ฆฌ์ŠคํŠธ์—์„œ ์•„์ดํ…œ์„ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.

    // mm/list_lru.c:201-218
    bool list_lru_del(struct list_lru *lru, struct list_head *item, int nid,
    struct mem_cgroup *memcg)
    {
    struct list_lru_node *nlru = &lru->node[nid];
    struct list_lru_one *l;
    l = lock_list_lru_of_memcg(lru, nid, memcg, false, false);
    if (!l)
    return false;
    if (!list_empty(item)) {
    list_del_init(item);
    l->nr_items--;
    unlock_list_lru(l, false);
    atomic_long_dec(&nlru->nr_items);
    return true;
    }
    unlock_list_lru(l, false);
    return false;
    }

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

  • ๋ฆฌ์ŠคํŠธ์— ์—†๋Š” ์•„์ดํ…œ(list_empty()๊ฐ€ true์ธ ๊ฒฝ์šฐ)์€ ์‚ญ์ œํ•˜์ง€ ์•Š์Œ
  • list_del_init()์œผ๋กœ ์ œ๊ฑฐ ํ›„ ์ดˆ๊ธฐํ™”
  • 3. `__list_lru_walk_one()` โ€” ํ•ต์‹ฌ ์ˆœํšŒ ๋กœ์ง

    LRU ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํ•ญ๋ชฉ์„ ๊ฒฉ๋ฆฌํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

    // mm/list_lru.c:279-334
    static unsigned long
    __list_lru_walk_one(struct list_lru *lru, int nid, struct mem_cgroup *memcg,
    list_lru_walk_cb isolate, void *cb_arg,
    unsigned long *nr_to_walk, bool irq_off)
    {
    struct list_lru_node *nlru = &lru->node[nid];
    struct list_lru_one *l = NULL;
    struct list_head *item, *n;
    unsigned long isolated = 0;
    
    restart:
    l = lock_list_lru_of_memcg(lru, nid, memcg, irq_off, true);
    if (!l)
    return isolated;
    list_for_each_safe(item, n, &l->list) {
    enum lru_status ret;
    
    if (!*nr_to_walk)
    break;
    --*nr_to_walk;
    
    ret = isolate(item, l, cb_arg);
    switch (ret) {
    case LRU_RETRY:
    goto restart;
    case LRU_REMOVED_RETRY:
    fallthrough;
    case LRU_REMOVED:
    isolated++;
    atomic_long_dec(&nlru->nr_items);
    if (ret == LRU_REMOVED_RETRY)
    goto restart;
    break;
    case LRU_ROTATE:
    list_move_tail(item, &l->list);
    break;
    case LRU_SKIP:
    break;
    case LRU_STOP:
    goto out;
    default:
    BUG();
    }
    }
    unlock_list_lru(l, irq_off);
    out:
    return isolated;
    }

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

  • LRU_RETRY: ์ž ๊ธˆ ํ•ด์ œ ํ›„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ์ˆœํšŒ
  • LRU_REMOVED_RETRY: ์•„์ดํ…œ ์ œ๊ฑฐ ํ›„ ๋‹ค์‹œ ์ˆœํšŒ
  • LRU_REMOVED: ์•„์ดํ…œ ์ œ๊ฑฐ, ๋‹ค์Œ ํ•ญ๋ชฉ์œผ๋กœ ์ด๋™
  • LRU_ROTATE: ์•„์ดํ…œ์„ ๋ฆฌ์ŠคํŠธ ๋์œผ๋กœ ์ด๋™
  • LRU_SKIP: ๊ฑด๋„ˆ๋œ€
  • LRU_STOP: ์ˆœํšŒ ์ค‘์ง€
  • 4. `list_lru_walk_node()` โ€” ๋…ธ๋“œ ์ „์ฒด ์ˆœํšŒ

    ํŠน์ • NUMA ๋…ธ๋“œ์˜ ๋ชจ๋“  memcg์— ๋Œ€ํ•ด LRU ์ˆœํšŒ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

    // mm/list_lru.c:355-391
    unsigned long list_lru_walk_node(struct list_lru *lru, int nid,
    list_lru_walk_cb isolate, void *cb_arg,
    unsigned long *nr_to_walk)
    {
    long isolated = 0;
    
    isolated += list_lru_walk_one(lru, nid, NULL, isolate, cb_arg,
    nr_to_walk);
    
    #ifdef CONFIG_MEMCG
    if (*nr_to_walk > 0 && list_lru_memcg_aware(lru)) {
    struct list_lru_memcg *mlru;
    struct mem_cgroup *memcg;
    unsigned long index;
    
    xa_for_each(&lru->xa, index, mlru) {
    rcu_read_lock();
    memcg = mem_cgroup_from_private_id(index);
    if (!mem_cgroup_tryget(memcg)) {
    rcu_read_unlock();
    continue;
    }
    rcu_read_unlock();
    isolated += __list_lru_walk_one(lru, nid, memcg,
    isolate, cb_arg,
    nr_to_walk, false);
    mem_cgroup_put(memcg);
    
    if (*nr_to_walk <= 0)
    break;
    }
    }
    #endif
    
    return isolated;
    }

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

  • ๋จผ์ € ๋ฃจํŠธ memcg(NULL)์˜ LRU๋ฅผ ์ˆœํšŒ
  • memcg_aware์ธ ๊ฒฝ์šฐ Xarray๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ ๊ฐ memcg์˜ LRU๋ฅผ ์ˆœํšŒ
  • memcg ์ฐธ์กฐ ์นด์šดํŠธ ์‹คํŒจ ์‹œ ํ•ด๋‹น memcg๋Š” ๊ฑด๋„ˆ๋œ€
  • 5. `__list_lru_init()` / `list_lru_destroy()`

    List LRU์˜ ์ดˆ๊ธฐํ™”์™€ ์†Œ๋ฉธ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

    // mm/list_lru.c:574-600
    int __list_lru_init(struct list_lru *lru, bool memcg_aware, struct shrinker *shrinker)
    {
    int i;
    
    #ifdef CONFIG_MEMCG
    if (shrinker)
    lru->shrinker_id = shrinker->id;
    else
    lru->shrinker_id = -1;
    
    if (mem_cgroup_kmem_disabled())
    memcg_aware = false;
    #endif
    
    lru->node = kzalloc_objs(*lru->node, nr_node_ids);
    if (!lru->node)
    return -ENOMEM;
    
    for_each_node(i)
    init_one_lru(lru, &lru->node[i].lru);
    
    memcg_init_list_lru(lru, memcg_aware);
    list_lru_register(lru);
    
    return 0;
    }

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

    LRU ์ถ”๊ฐ€/์‚ญ์ œ ํ๋ฆ„

    list_lru_add_obj() / list_lru_del_obj()
    โ”œโ”€ virt_to_page() โ†’ page_to_nid() โ†’ NUMA ๋…ธ๋“œ ๊ฒฐ์ •
    โ”œโ”€ mem_cgroup_from_virt() โ†’ memcg ๊ฒฐ์ • (memcg_aware์ธ ๊ฒฝ์šฐ)
    โ””โ”€ list_lru_add() / list_lru_del()
    โ”œโ”€ lock_list_lru_of_memcg()
    โ”‚    โ”œโ”€ list_lru_from_memcg_idx() โ†’ memcg๋ณ„ list_lru_one ํš๋“
    โ”‚    โ””โ”€ lock_list_lru() โ†’ ์Šคํ•€๋ฝ ํš๋“
    โ”œโ”€ list_add_tail() / list_del_init()
    โ”œโ”€ set_shrinker_bit() (์ฒซ ์ถ”๊ฐ€ ์‹œ)
    โ””โ”€ unlock_list_lru()

    LRU ์ˆœํšŒ ํ๋ฆ„

    list_lru_walk_node()
    โ”œโ”€ list_lru_walk_one(NULL memcg)
    โ”‚    โ””โ”€ __list_lru_walk_one()
    โ”‚         โ”œโ”€ lock_list_lru_of_memcg()
    โ”‚         โ”œโ”€ list_for_each_safe() ์ˆœํšŒ
    โ”‚         โ”‚    โ”œโ”€ isolate() ์ฝœ๋ฐฑ ํ˜ธ์ถœ
    โ”‚         โ”‚    โ””โ”€ lru_status์— ๋”ฐ๋ฅธ ๋ถ„๊ธฐ
    โ”‚         โ””โ”€ unlock_list_lru()
    โ””โ”€ xa_for_each() (memcg_aware์ธ ๊ฒฝ์šฐ)
    โ””โ”€ __list_lru_walk_one() (๊ฐ memcg๋ณ„)

    shrinker ์—ฐ๋™ ํ๋ฆ„

    try_to_free_pages() / kswapd
    โ””โ”€ shrink_slab()
    โ””โ”€ list_lru_shrink_walk()
    โ””โ”€ list_lru_walk_one()
    โ””โ”€ __list_lru_walk_one()
    โ””โ”€ isolate() ์ฝœ๋ฐฑ โ†’ LRU_REMOVED / LRU_ROTATE / LRU_STOP

    list_lru_shrink_walk()๋Š” struct shrink_control์˜ nid, memcg, nr_to_scan์„ ๊ทธ๋Œ€๋กœ ๋ฐ›์•„ list_lru_walk_one()์œผ๋กœ ๋„˜๊น๋‹ˆ๋‹ค. ์ด ๋•Œ๋ฌธ์— ์Šฌ๋žฉ ํšŒ์ˆ˜๋Ÿ‰์€ ํŽ˜์ด์ง€ ํšŒ์ˆ˜๋Ÿ‰๊ณผ ๋”ฐ๋กœ ์›€์ง์ด์ง€๋งŒ, ๋ฉ”๋ชจ๋ฆฌ ์••๋ ฅ์ด ๋†’์„ ๋•Œ๋Š” ๋‘ ๊ฒฝ๋กœ๊ฐ€ ํ•จ๊ป˜ ๊ด€์ธก๋ฉ๋‹ˆ๋‹ค.

    ์ดˆ๊ธฐํ™”/์†Œ๋ฉธ ํ๋ฆ„

    __list_lru_init()
    โ”œโ”€ kzalloc_objs() โ†’ node ๋ฐฐ์—ด ํ• ๋‹น
    โ”œโ”€ for_each_node() โ†’ init_one_lru() ๊ฐ ๋…ธ๋“œ ์ดˆ๊ธฐํ™”
    โ”œโ”€ memcg_init_list_lru() โ†’ Xarray ์ดˆ๊ธฐํ™”
    โ””โ”€ list_lru_register() โ†’ memcg_list_lrus์— ์ถ”๊ฐ€
    
    list_lru_destroy()
    โ”œโ”€ list_lru_unregister() โ†’ memcg_list_lrus์—์„œ ์ œ๊ฑฐ
    โ”œโ”€ memcg_destroy_list_lru() โ†’ Xarray ํ•ญ๋ชฉ ํ•ด์ œ
    โ””โ”€ kfree(node)

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

    List LRU API ๋น„๊ต

    ํ•จ์ˆ˜์—ญํ• memcg ์ฒ˜๋ฆฌNUMA ์ฒ˜๋ฆฌ
    `list_lru_add()`์•„์ดํ…œ ์ถ”๊ฐ€๋ช…์‹œ์  memcg ์ง€์ •๋ช…์‹œ์  nid ์ง€์ •
    `list_lru_add_obj()`์•„์ดํ…œ ์ถ”๊ฐ€virt์—์„œ ์ž๋™ ๊ฒฐ์ •virt์—์„œ ์ž๋™ ๊ฒฐ์ •
    `list_lru_del()`์•„์ดํ…œ ์‚ญ์ œ๋ช…์‹œ์  memcg ์ง€์ •๋ช…์‹œ์  nid ์ง€์ •
    `list_lru_del_obj()`์•„์ดํ…œ ์‚ญ์ œvirt์—์„œ ์ž๋™ ๊ฒฐ์ •virt์—์„œ ์ž๋™ ๊ฒฐ์ •
    `list_lru_walk_one()`์ˆœํšŒ๋ช…์‹œ์  memcg๋ช…์‹œ์  nid
    `list_lru_walk_node()`๋…ธ๋“œ ์ „์ฒด ์ˆœํšŒ๋ชจ๋“  memcg ์ˆœํšŒ๋ช…์‹œ์  nid

    LRU Walk ์ฝœ๋ฐฑ ๋ฐ˜ํ™˜๊ฐ’ ๋™์ž‘

    ๋ฐ˜ํ™˜๊ฐ’๋™์ž‘์ž ๊ธˆ ์ƒํƒœ์žฌ์‹œ๋„
    `LRU_REMOVED`์•„์ดํ…œ ์ œ๊ฑฐ, ๋‹ค์Œ์œผ๋กœ ์ด๋™์œ ์ง€์•„๋‹˜
    `LRU_REMOVED_RETRY`์•„์ดํ…œ ์ œ๊ฑฐ, ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œํ•ด์ œ ํ›„ ์žฌํš๋“์˜ˆ
    `LRU_ROTATE`๋ฆฌ์ŠคํŠธ ๋์œผ๋กœ ์ด๋™์œ ์ง€์•„๋‹˜
    `LRU_SKIP`๊ฑด๋„ˆ๋œ€์œ ์ง€์•„๋‹˜
    `LRU_RETRY`์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œํ•ด์ œ ํ›„ ์žฌํš๋“์˜ˆ
    `LRU_STOP`์ˆœํšŒ ์ค‘์ง€์œ ์ง€์•„๋‹˜

    memcg ์ฒ˜๋ฆฌ ๋ฐฉ์‹ ๋น„๊ต

    ์กฐ๊ฑด๋™์ž‘
    `memcg_aware = true`Xarray์— memcg๋ณ„ list_lru_memcg ํ• ๋‹น
    `memcg_aware = false`๋ฃจํŠธ memcg์˜ list_lru_node.lru๋งŒ ์‚ฌ์šฉ
    memcg reparenting๋ถ€๋ชจ memcg์˜ LRU๋กœ ์•„์ดํ…œ ์ด๋™, src๋Š” `LONG_MIN`์œผ๋กœ ํ‘œ์‹œ
    memcg ํ•ด์ œRCU๋ฅผ ํ†ตํ•ด list_lru_memcg ์•ˆ์ „ํ•˜๊ฒŒ ํ•ด์ œ

    List LRU์™€ page reclaim LRU ๋น„๊ต

    ๊ตฌ๋ถ„๊ด€๋ฆฌ ๋Œ€์ƒ๋Œ€ํ‘œ ๊ฒฝ๋กœํšŒ์ˆ˜ ๋‹จ์œ„๊ด€์ฐฐ ์ง€ํ‘œ
    List LRUdentry, inode, shmem ๊ฐ™์€ ์ปค๋„ ์˜ค๋ธŒ์ ํŠธ`shrink_slab()` โ†’ `list_lru_shrink_walk()`์˜ค๋ธŒ์ ํŠธ ๊ฐœ์ˆ˜`list_lru_count_one()`, `list_lru_count_node()`
    page reclaim LRUanon/file ํŽ˜์ด์ง€`try_to_free_pages()` โ†’ `shrink_node()` โ†’ `shrink_lruvec()`ํŽ˜์ด์ง€ ํ”„๋ ˆ์ž„`pgscan`, `pgsteal`, `allocstall`, `kswapd`
    ์—ฐ๊ฒฐ์ shrinker๊ฐ€ drainํ•  ๋Œ€์ƒ์„ ์ œ๊ณต`list_lru_walk_node()`๊ฐ€ ์ฝœ๋ฐฑ ๊ธฐ๋ฐ˜ ์ˆœํšŒmemcg/nid ๋‹จ์œ„`cat /proc/pressure/memory`, `swapon --show`

    ๊ด€๋ จ ๋ฌธ์„œ

  • ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • SLUB ํ• ๋‹น์ž
  • Shrinker
  • VMA / mmap
  • ํŽ˜์ด์ง€ ํšŒ์ˆ˜
  • Memory Cgroup