๏ปฟ# Workingset (์์ ์งํฉ ๊ฐ์ง)
๊ด๋ จ ์์ค:mm/workingset.c,mm/filemap.c,mm/vmscan.c,mm/swap.c,mm/memcontrol.c,include/linux/swap.h,include/linux/mmzone.h,include/linux/mm_inline.h
๊ฐ์ (Overview)
Workingset์ ํ์ด์ง๊ฐ ๋ฉ๋ชจ๋ฆฌ์์ ์ ๊ฑฐ๋ ํ ์งง์ ์๊ฐ ๋ด์ ์ฌ์ฌ์ฉ๋๋์ง(refault)๋ฅผ ์ถ์ ํ์ฌ, ๋ฉ๋ชจ๋ฆฌ ํ์ ๊ฒฐ์ ์ ํ์ฉํ๋ ๋ฉ์ปค๋์ฆ์ด๋ค. ํต์ฌ ์์ด๋์ด๋ "shadow entry"๋ก, folio๊ฐ ์ ๊ฑฐ๋ ๋ xarray์ ํด๋น ์ฌ๋กฏ์ ์ ๊ฑฐ ์์ ์ ํ์์คํฌํ(๋น๊ฑฐ์ฃผ ์ฐ๋ น)๋ฅผ ์ ์ฅํ๊ณ , ๋์ค์ ๊ฐ์ folio๊ฐ ๋ค์ fault๋๋ฉด ๊ทธ ์๊ฐ ๊ฐ๊ฒฉ(refault distance)์ ๊ณ์ฐํ์ฌ working set ๊ฒฝ์ ์ํฉ์ ํ๋จํ๋ค. refault distance๊ฐ ํ์ฌ ํ์ฑ+๋นํ์ฑ ์บ์ ํฌ๊ธฐ ์ด๋ด์ด๋ฉด ํด๋น folio๋ ํ์ฑ ๋ชฉ๋ก์ผ๋ก ํ์ฑํ๋์ด ๋ณดํธ๋๋ค.
์ผ์ ๋น์ : ๋์๊ด ๋น์ ๋ก ์ค๋ช ํ๋ฉด, workingset์ "์ต๊ทผ์ ๋น๋ ค๊ฐ ์ฑ "์ ์ถ์ ํ๋ ์์คํ ์ด๋ค. ๋นํ์ฑ ๋ชฉ๋ก์ "์์ ์ง์ด๋", ํ์ฑ ๋ชฉ๋ก์ "์์ฃผ ๋น๋ฆฌ๋ ์ฑ ์ง์ด๋"์ด๋ค. ์๋ก์ด ์ฑ ์ด ๋ค์ด์ค๋ฉด ์์ ์ง์ด๋ ๋์ ๋์ด๊ณ , ์ค๋๋ ์ฑ ์ ๋ฐ๋ ค๋ ํด์ถ๋๋ค. ์ด๋ค ์ฑ ์ด ํด์ถ๋ ํ ๋ค์ ๋น๋ ค๊ฐ๋ฉด(refault), ์ด์ ๋์ฌ ๊ธฐ๋ก(shadow entry)์ ํ์ธํ์ฌ "์ด ์ฑ ์ด ์ผ๋ง๋ ์์ฃผ ๋น๋ ค์ก๋์ง" ํ๋จํ๋ค. ์ถฉ๋ถํ ์์ฃผ ๋น๋ ค์ง ์ฑ ์ ์์ฃผ ๋น๋ฆฌ๋ ์ฑ ์ง์ด๋๋ก ์น๊ฒฉ๋๋ค.
Linux 7.0์์๋ ๋ ๊ฐ์ง ๊ตฌํ ๊ฒฝ๋ก๊ฐ ๊ณต์กดํ๋ค: (1) ๊ธฐ์กด Double CLOCK ์๊ณ ๋ฆฌ์ฆ(MGLRU ๋ฏธ์ฌ์ฉ ์)๊ณผ (2) MGLRU(CONFIG_LRU_GEN) ๊ธฐ๋ฐ ์ธ๋๋ณ ์ถ์ . Double CLOCK์ ๋นํ์ฑ/ํ์ฑ ๋ชฉ๋ก์์ ๊ฐ๊ฐ์ eviction/activation ์นด์ดํฐ๋ฅผ lruvec->nonresident_age๋ก ๊ด๋ฆฌํ๊ณ , MGLRU๋ lrugen->evicted[hist][type][tier]์ lrugen->refaulted[hist][type][tier] ๋ฐฐ์ด๋ก ์ธ๋๋ณ๋ก ์ถ์ ํ๋ค. ๋ํ shadow entry๊ฐ ๊ณผ๋ํ๊ฒ ๋์ ๋์ง ์๋๋ก shadow_nodes list_lru์ shrinker๋ก shadow ๋
ธ๋๋ฅผ ๊ด๋ฆฌํ๋ค.
ํต์ฌ ์์ค ํ์ผ:
mm/workingset.c โ workingset ๊ฐ์ง ์ ์ฒด ๊ตฌํ (826์ค)mm/filemap.c โ workingset_refault() ์ฌ์ง์
์ (filemap_add_folio())mm/vmscan.c โ workingset_eviction() ํธ์ถ ์ง์ (shrink_folio_list())mm/swap.c โ workingset_activation() ํธ์ถ ์ง์ (folio_mark_accessed())mm/memcontrol.c โ WORKINGSET_* vmstat/cgroup ํต๊ณ ์ด๋ฆinclude/linux/swap.h โ workingset_eviction(), workingset_refault() ๋ฑ ์ ์ธ (line 314-320)include/linux/mmzone.h โ lruvec.nonresident_age, WORKINGSET_* ํต๊ณ enum (line 192-202, 681)include/linux/mm_inline.h โ lru_gen_enabled(), lru_gen_in_fault(), folio_lru_refs() ๋ฑ MGLRU ํฌํผ๋น ๋ฅธ ์ ๊ฒ ๋ช ๋ น
# workingset ๊ด๋ จ ์ปค๋ ์ฌ๋ณผ ํ์ธ
cat /proc/kallsyms | grep -E 'workingset_eviction|workingset_refault|workingset_test_recent|workingset_age_nonresident|workingset_activation'
# workingset ํต๊ณ ํ์ธ (๋
ธ๋๋ณ)
cat /proc/vmstat | grep -E 'workingset_|nr_workingset'
# shadow entry ํต๊ณ
cat /proc/vmstat | grep -E 'workingset_nodes|workingset_refault|workingset_activate|workingset_restore|workingset_nodereclaim'
# MGLRU ํ์ฑํ ์ฌ๋ถ ํ์ธ
cat /sys/kernel/mm/lru_gen/enabled
# memcg๋ณ workingset ํต๊ณ ํ์ธ
cat /sys/fs/cgroup/<cgroup>/memory.stat | grep -E 'workingset_refault|workingset_activate|workingset_restore|workingset_nodereclaim'
# memcg ์ด๋ฒคํธ ํ์ธ
cat /sys/fs/cgroup/<cgroup>/memory.events
# ํ์ด์ง ํดํธ/๋ฉ๋ชจ๋ฆฌ ์๋ฐ ์งํ ํ์ธ
cat /proc/vmstat | grep -E 'pgfault|pgmajfault|allocstall|workingset_'
# PSI ๋ฉ๋ชจ๋ฆฌ ์๋ ฅ ํ์ธ
cat /proc/pressure/memory
# LRU_GEN ๊ด๋ จ ํต๊ณ ํ์ธ
cat /proc/vmstat | grep -E 'nr_lru_gen|nr_inactive|nr_active'
# workingset refault ๋น๋ ๋ชจ๋ํฐ๋ง (์ค์๊ฐ)
watch -n 1 'cat /proc/vmstat | grep workingset'
# ํน์ ํ๋ก์ธ์ค์ workingset ํฌ๊ธฐ ํ์ธ
cat /proc/<pid>/smaps | grep -E 'Referenced|Anonymous|File'
# shadow entry ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ํ์ธ
cat /proc/meminfo | grep -E 'Slab|SReclaimable|SUnreclaim'
# MGLRU ์ธ๋๋ณ ํต๊ณ ํ์ธ
cat /sys/kernel/mm/lru_gen/stats
# memcg๋ณ nonresident_age ํ์ธ
cat /sys/fs/cgroup/<cgroup>/memory.stat | grep -E 'workingset_refault|workingset_activate|workingset_restore|workingset_nodereclaim'
ํต์ฌ ์๋ฃ๊ตฌ์กฐ
shadow entry โ ์ ๊ฑฐ ์์ ์ ๋ณด ์ ์ฅ
shadow entry๋ xarray์ value ์ํธ๋ฆฌ๋ก, folio ์ ๊ฑฐ ์ ๋ฉ๋ชจ๋ฆฌ ์บ์ ์ฌ๋กฏ์ ์ ์ฅ๋๋ค. pack_shadow()๋ก ์ธ์ฝ๋ฉํ๊ณ unpack_shadow()๋ก ๋์ฝ๋ฉํ๋ค.
// mm/workingset.c:199-228
static void *pack_shadow(int memcgid, pg_data_t *pgdat, unsigned long eviction,
bool workingset)
{
eviction &= EVICTION_MASK;
eviction = (eviction << MEM_CGROUP_ID_SHIFT) | memcgid;
eviction = (eviction << NODES_SHIFT) | pgdat->node_id;
eviction = (eviction << WORKINGSET_SHIFT) | workingset;
return xa_mk_value(eviction);
}
static void unpack_shadow(void *shadow, int *memcgidp, pg_data_t **pgdat,
unsigned long *evictionp, bool *workingsetp)
{
int memcgid, nid;
bool workingset;
unsigned long entry = xa_to_value(shadow);
workingset = entry & ((1UL << WORKINGSET_SHIFT) - 1);
entry >>= WORKINGSET_SHIFT;
nid = entry & ((1UL << NODES_SHIFT) - 1);
entry >>= NODES_SHIFT;
memcgid = entry & ((1UL << MEM_CGROUP_ID_SHIFT) - 1);
entry >>= MEM_CGROUP_ID_SHIFT;
*memcgidp = memcgid;
*pgdat = NODE_DATA(nid);
*evictionp = entry;
*workingsetp = workingset;
}
EVICTION_SHIFT: BITS_PER_LONG - BITS_PER_XA_VALUE + WORKINGSET_SHIFT + NODES_SHIFT + MEM_CGROUP_ID_SHIFT โ ํ์์คํฌํ์ ์ฌ์ฉ ๊ฐ๋ฅํ ๋นํธ ์ ๊ฒฐ์ bucket_order: ๋ฉ๋ชจ๋ฆฌ ํฌ๊ธฐ๊ฐ ํ์์คํฌํ ๋นํธ ๋ฒ์๋ฅผ ์ด๊ณผํ๋ฉด ํ์ ๋นํธ๋ฅผ ์๋ผ coarser granularity๋ก ์ฒ๋ฆฌlruvec โ LRU ๋ฒกํฐ์ ๋น๊ฑฐ์ฃผ ์ฐ๋ น
// include/linux/mmzone.h:670-698
struct lruvec {
struct list_head lists[NR_LRU_LISTS]; // ๋นํ์ฑ/ํ์ฑ LRU ๋ชฉ๋ก (anon/file/unevictable)
spinlock_t lru_lock; // memcg๋ณ LRU ์ ๊ธ
unsigned long anon_cost; // anon LRU ํ์ ๋น์ฉ ์ถ์
unsigned long file_cost; // file LRU ํ์ ๋น์ฉ ์ถ์
atomic_long_t nonresident_age; // ๋น๊ฑฐ์ฃผ ์ํธ๋ฆฌ ์ฐ๋ น (ํต์ฌ!)
unsigned long refaults[ANON_AND_FILE]; // ์ด์ ํ์ ์ฌ์ดํด ์ refault ์
unsigned long flags;
#ifdef CONFIG_LRU_GEN
struct lru_gen_folio lrugen; // MGLRU ์ธ๋๋ณ folio ๊ด๋ฆฌ
#endif
#ifdef CONFIG_MEMCG
struct pglist_data *pgdat; // ์ด lruvec์ด ์ํ NUMA ๋
ธ๋
#endif
};
nonresident_age: workingset_age_nonresident()์์ atomic_long_add(nr_pages)๋ก ์ฆ๊ฐ. folio ์ ๊ฑฐ/ํ์ฑํ ์์ ์ ์ด ๊ฐ์ ํ์์คํฌํ๋ก ์ ์ฅWORKINGSET_* ํต๊ณ enum
// include/linux/mmzone.h:192-202
WORKINGSET_NODES, // shadow entry๋ฅผ ํฌํจํ๋ xarray ๋
ธ๋ ์
WORKINGSET_REFAULT_BASE,
WORKINGSET_REFAULT_ANON = WORKINGSET_REFAULT_BASE, // ์ต๋ช
refault ์
WORKINGSET_REFAULT_FILE, // ํ์ผ refault ์
WORKINGSET_ACTIVATE_BASE,
WORKINGSET_ACTIVATE_ANON = WORKINGSET_ACTIVATE_BASE, // refault๋ก ํ์ฑํ๋ ์ต๋ช
ํ์ด์ง ์
WORKINGSET_ACTIVATE_FILE, // refault๋ก ํ์ฑํ๋ ํ์ผ ํ์ด์ง ์
WORKINGSET_RESTORE_BASE,
WORKINGSET_RESTORE_ANON = WORKINGSET_RESTORE_BASE, // workingset์ผ๋ก ๋ณต์๋ ์ต๋ช
ํ์ด์ง ์
WORKINGSET_RESTORE_FILE, // workingset์ผ๋ก ๋ณต์๋ ํ์ผ ํ์ด์ง ์
WORKINGSET_NODERECLAIM, // shrinker์ ์ํด ํ์๋ shadow ๋
ธ๋ ์
MGLRU ์ธ๋๋ณ ์ถ์ ๊ตฌ์กฐ (CONFIG_LRU_GEN)
// mm/workingset.c:232-258 (lru_gen_eviction ๋ด๋ถ)
// MGLRU์์ folio ์ ๊ฑฐ ์ shadow entry ์์ฑ
static void *lru_gen_eviction(struct folio *folio)
{
int hist;
unsigned long token;
unsigned long min_seq;
struct lruvec *lruvec;
struct lru_gen_folio *lrugen;
int type = folio_is_file_lru(folio); // ์ต๋ช
(0) ๋๋ ํ์ผ(1)
int delta = folio_nr_pages(folio);
int refs = folio_lru_refs(folio); // ์ ๊ทผ ํ์ ์ฐธ์กฐ
bool workingset = folio_test_workingset(folio);
int tier = lru_tier_from_refs(refs, workingset); // ์ฐธ์กฐ ํ์์์ tier๋ก ๋ณํ
struct mem_cgroup *memcg = folio_memcg(folio);
struct pglist_data *pgdat = folio_pgdat(folio);
BUILD_BUG_ON(LRU_GEN_WIDTH + LRU_REFS_WIDTH > BITS_PER_LONG - EVICTION_SHIFT);
lruvec = mem_cgroup_lruvec(memcg, pgdat);
lrugen = &lruvec->lrugen;
min_seq = READ_ONCE(lrugen->min_seq[type]);
token = (min_seq << LRU_REFS_WIDTH) | max(refs - 1, 0); // ์ํ์ค + ์ฐธ์กฐ ํ์ ๊ฒฐํฉ
hist = lru_hist_from_seq(min_seq);
atomic_long_add(delta, &lrugen->evicted[hist][type][tier]); // ์ธ๋๋ณ ์ถ์ถ ์นด์ดํธ ์ฆ๊ฐ
return pack_shadow(mem_cgroup_private_id(memcg), pgdat, token, workingset);
}
ํต์ฌ ํจ์
workingset_eviction() โ folio ์ ๊ฑฐ ์ shadow entry ์์ฑ
// mm/workingset.c:381-404
void *workingset_eviction(struct folio *folio, struct mem_cgroup *target_memcg)
์ญํ : folio๊ฐ ๋ฉ๋ชจ๋ฆฌ์์ ์ ๊ฑฐ๋ ๋ shadow entry๋ฅผ ์์ฑํ์ฌ xarray์ ์ ์ฅํ๋ค.
๋ถ๊ธฐ ๋ก์ง:
1. lru_gen_enabled() โ lru_gen_eviction() ํธ์ถ (MGLRU ๊ฒฝ๋ก)
2. MGLRU ๋ฏธ์ฌ์ฉ ์: lruvec->nonresident_age๋ฅผ ์ฝ์ด bucket_order๋ก ์ ๋ ฌ ํ pack_shadow()๋ก ์ธ์ฝ๋ฉ
3. folio_test_workingset(folio) ํ๋๊ทธ๋ฅผ shadow entry์ ํฌํจ โ ์ด์ ์ workingset์ผ๋ก ํ์ฑํ๋์๋์ง ์ถ์
workingset_refault() โ refault ์ workingset ํ๋จ ๋ฐ ํ์ฑํ
// mm/workingset.c:534-583
void workingset_refault(struct folio *folio, void *shadow)
์ญํ : ์ด์ ์ ์ ๊ฑฐ๋ folio๊ฐ ๋ค์ fault๋๋ฉด refault distance๋ฅผ ๊ณ์ฐํ๊ณ , ์ ์ ํ๋ฉด active list๋ก ํ์ฑํํ๋ค.
๋ถ๊ธฐ ๋ก์ง:
1. lru_gen_enabled() โ lru_gen_refault() ํธ์ถ (MGLRU ๊ฒฝ๋ก)
2. workingset_test_recent() ํธ์ถ โ shadow entry๊ฐ ์ต๊ทผ ๊ฒ์ธ์ง ํ๋จ
3. workingset_test_recent()๊ฐ true์ด๋ฉด:
- folio_set_active(folio) โ active list๋ก ํ์ฑํ
- workingset_age_nonresident() โ ๋น๊ฑฐ์ฃผ ์ฐ๋ น ์ฆ๊ฐ
- workingset ํ๋๊ทธ๊ฐ ์์ผ๋ฉด: folio_set_workingset(), lru_note_cost_refault() ํธ์ถ
4. false์ด๋ฉด ์๋ฌด ๋์ ์์ด ๋ฐํ
workingset_test_recent() โ refault distance ๊ณ์ฐ ๋ฐ ํ๋จ
// mm/workingset.c:418-523
bool workingset_test_recent(void *shadow, bool file, bool *workingset, bool flush)
์ญํ : shadow entry๋ฅผ ๋์ฝ๋ฉํ๊ณ , ํ์ฌ nonresident_age์ ๋น๊ตํ์ฌ refault distance๊ฐ working set ํฌ๊ธฐ ์ด๋ด์ธ์ง ํ๋จํ๋ค.
๋ถ๊ธฐ ๋ก์ง:
1. lru_gen_enabled() โ lru_gen_test_recent() ํธ์ถ
2. unpack_shadow()๋ก memcgid, node, eviction ํ์์คํฌํ ์ถ์ถ
3. mem_cgroup_from_private_id()๋ก eviction ์์ ์ memcg ์กฐํ (์ญ์ ๋์์ ์ ์์)
4. mem_cgroup_flush_stats_ratelimited()๋ก ํต๊ณ ๊ฐฑ์
5. refault_distance = (refault - eviction) & EVICTION_MASK ๊ณ์ฐ
6. workingset_size ๊ณ์ฐ:
- file ํ์ด์ง์ธ ๊ฒฝ์ฐ: NR_ACTIVE_FILE๋ง ๊ธฐ๋ณธ ํฌํจ
- anon ํ์ด์ง์ธ ๊ฒฝ์ฐ: NR_INACTIVE_FILE๋ ํฌํจ
- swap ์ฌ์ฉ ๊ฐ๋ฅ ์: NR_ACTIVE_ANON์ ์ถ๊ฐํ๊ณ , file ํ์ด์ง์ธ ๊ฒฝ์ฐ NR_INACTIVE_ANON๋ ํฌํจ
7. refault_distance <= workingset_size ๋ฐํ โ ์ด ์กฐ๊ฑด์ด true์ด๋ฉด ํ์ฑํ ๋์
workingset_age_nonresident() โ ๋น๊ฑฐ์ฃผ ์ฐ๋ น ์ฆ๊ฐ
// mm/workingset.c:355-371
void workingset_age_nonresident(struct lruvec *lruvec, unsigned long nr_pages)
์ญํ : ๋ฉ๋ชจ๋ฆฌ ๋ด LRU๊ฐ aging๋ ๋ ๋น๊ฑฐ์ฃผ ์ํธ๋ฆฌ๋ ํจ๊ป aging์ํจ๋ค. memcg ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ๋ฐ๋ผ ์์ memcg๊น์ง ๋ชจ๋ ์ฆ๊ฐ์ํจ๋ค.
๋ถ๊ธฐ ๋ก์ง:
do-while ๋ฃจํ: parent_lruvec(lruvec)๊ฐ NULL์ด ๋ ๋๊น์ง ๋ฐ๋ณตnonresident_age์ nr_pages๋ฅผ ๋ํจworkingset_activation() โ accessed folio์ ๋น๊ฑฐ์ฃผ ์ฐ๋ น ๋ฐ์
// mm/workingset.c:589-596
void workingset_activation(struct folio *folio)
{
if (mem_cgroup_disabled() || folio_memcg_charged(folio))
workingset_age_nonresident(folio_lruvec(folio), folio_nr_pages(folio));
}
folio_mark_accessed()์์ active ์ ํ์ด ์ผ์ด๋ ๋ ํจ๊ป ํธ์ถ๋์ด, LRU ์ด๋๊ณผ nonresident age ์ฆ๊ฐ๋ฅผ ๋ง์ถฐ ์ค๋ค.
workingset_update_node() โ shadow ๋ ธ๋ LRU ๊ด๋ฆฌ
// mm/workingset.c:613-638
void workingset_update_node(struct xa_node *node)
์ญํ : xarray ๋ ธ๋๊ฐ shadow entry๋ง ํฌํจํ๋์ง ์ถ์ ํ์ฌ, ๊ณผ๋ํ๊ฒ ๋์ ๋๋ฉด shrinker๋ก ํ์ํ ์ ์๋๋ก ํ๋ค.
๋ถ๊ธฐ ๋ก์ง:
1. node->count && node->count == node->nr_values โ ๋ชจ๋ ๊ฐ์ด shadow entry
- list_empty(&node->private_list)์ด๋ฉด: list_lru_add_obj()๋ก shadow ๋
ธ๋ LRU์ ์ถ๊ฐ
2. ๊ทธ ์ธ (์ผ๋ฐ ํ์ด์ง ํฌํจ ๋๋ ํด์ ์ค):
- list_empty()๊ฐ ์๋๋ฉด: list_lru_del_obj()๋ก LRU์์ ์ ๊ฑฐ
ํธ์ถ ํ๋ฆ
1. folio ์ ๊ฑฐ ๊ฒฝ๋ก
vmscan.c: shrink_folio_list()
โโ workingset_eviction(folio, memcg)
โโ [MGLRU] lru_gen_eviction(folio)
โ โโ pack_shadow() โ xa_mk_value()
โโ [CLOCK] atomic_long_read(nonresident_age) โ pack_shadow()
โโ folio->mapping->i_pages[xarray]์ shadow entry ์ ์ฅ
2. folio refault ๊ฒฝ๋ก
mm/filemap.c: filemap_add_folio()
โโ __filemap_add_folio(..., &shadow)
โโ shadow && !(gfp & __GFP_WRITE)
โ โโ workingset_refault(folio, shadow)
โ โโ [MGLRU] lru_gen_refault(folio, shadow)
โ โ โโ lru_gen_test_recent() โ abs_diff(max_seq, token) < MAX_NR_GENS
โ โ โโ lrugen->refaulted[hist][type][tier] ์ฆ๊ฐ
โ โ โโ folio_set_workingset() + mod_lruvec_state()
โ โโ [CLOCK] workingset_test_recent()
โ โโ unpack_shadow() โ memcgid, node, eviction
โ โโ mem_cgroup_from_private_id() โ eviction ์์ memcg ์กฐํ
โ โโ refault_distance = (refault - eviction) & EVICTION_MASK
โ โโ workingset_size = NR_ACTIVE + NR_INACTIVE (+ swap)
โ โโ refault_distance <= workingset_size ?
โ โโ true: folio_set_active() + workingset_age_nonresident()
โ โโ false: ์๋ฌด ๋์ ์์
โโ folio_add_lru(folio)
mm/swap.c: folio_mark_accessed()
โโ workingset_activation(folio)
3. shadow ๋ ธ๋ ํ์ ๊ฒฝ๋ก
kswapd / direct reclaim
โโ scan_shadow_nodes(shrinker, sc)
โโ list_lru_shrink_walk_irq(&shadow_nodes, shadow_lru_isolate)
โโ xa_trylock(&mapping->i_pages)
โโ spin_trylock(&mapping->host->i_lock)
โโ xa_delete_node(node, workingset_update_node)
โโ WORKINGSET_NODERECLAIM ํต๊ณ ์ฆ๊ฐ
์กฐ๊ฑด๋ณ ๋น๊ต
Double CLOCK vs MGLRU workingset ๋์
| ํญ๋ชฉ | Double CLOCK (`!CONFIG_LRU_GEN`) | MGLRU (`CONFIG_LRU_GEN`) |
|---|---|---|
| **์ ๊ฑฐ ์** | `lruvec->nonresident_age`์์ ์ฝ์ | `lrugen->min_seq[type]` + `refs`๋ฅผ token์ผ๋ก ์ฌ์ฉ |
| **refault ํ์ ** | `refault_distance <= workingset_size` ์์ | `abs_diff(max_seq, token >> LRU_REFS_WIDTH) < MAX_NR_GENS` |
| **์ธ๋ถ ์ถ์ ** | ์ ์ฒด nonresident_age ์นด์ดํฐ ํ๋ | `evicted[hist][type][tier]` 3์ฐจ์ ๋ฐฐ์ด |
| **tier ๋ถ๋ฅ** | ์์ (workingset ํ๋๊ทธ๋ง) | `lru_tier_from_refs(refs, workingset)`์ผ๋ก 4๋จ๊ณ |
| **์ต๊ทผ ํ์ ** | refault ๊ฑฐ๋ฆฌ์ ๋ฉ๋ชจ๋ฆฌ ํฌ๊ธฐ ๋น๊ต | ์ํ์ค ๊ธฐ๋ฐ ์ต๊ทผ์ฑ ๋น๊ต |
refault ๊ฒฐ๊ณผ๋ณ ๋์
| ์กฐ๊ฑด | ๋์ | ์ค๋ช |
|---|---|---|
| shadow ์์ | pass | ์ด์ ์ ๊ฑฐ ๊ธฐ๋ก ์์ |
| `workingset_test_recent()` = false | pass | ๋๋ฌด ์ค๋์ ์ ๊ฑฐ, ๊ด๋ จ ์์ |
| recent = true, workingset = false | `folio_set_active()` | ๋นํ์ฑ ๋ชฉ๋ก์์ ํ์ฑ์ผ๋ก ์น๊ฒฉ |
| recent = true, workingset = true | `folio_set_active()` + `folio_set_workingset()` + `lru_note_cost_refault()` | ์ด์ ์ active์๋ ํ์ด์ง ๋ณต์, ๋น์ฉ ์ฆ๊ฐ ๊ธฐ๋ก |
shadow entry ํ์์คํฌํ ์ ๋ฐ๋
| ์กฐ๊ฑด | `bucket_order` | ์ค๋ช |
|---|---|---|
| ๋ฉ๋ชจ๋ฆฌ โค ํ์์คํฌํ ๋นํธ ๋ฒ์ | 0 | ์ ์ฒด ์ ๋ฐ๋ ์ ์ง |
| ๋ฉ๋ชจ๋ฆฌ > ํ์์คํฌํ ๋นํธ ๋ฒ์ | `max_order - timestamp_bits` | ํ์ ๋นํธ ์๋ผ coarsening |
anon vs file ํ์ด์ง์ refault distance ๊ณ์ฐ
| ํ์ด์ง ์ ํ | workingset_size์ ํฌํจ |
|---|---|
| file ํ์ด์ง | `NR_ACTIVE_FILE` + (swap ์ฌ์ฉ ์ `NR_ACTIVE_ANON` + `NR_INACTIVE_ANON`) |
| anon ํ์ด์ง | `NR_ACTIVE_FILE` + `NR_INACTIVE_FILE` + (swap ์ฌ์ฉ ์ `NR_ACTIVE_ANON`) |