19. KSM (Kernel Same-page Merging)
๊ฐ์ (Overview)
KSM(Kernel Same-page Merging)์ ์ปค๋์ด ์คํ ์ค์ธ ํ๋ก์ธ์ค๋ค์ ๋์ผํ ๋ด์ฉ์ ์ต๋ช ํ์ด์ง๋ฅผ ํ์งํ์ฌ ํ๋์ ๋ฌผ๋ฆฌ ํ์ด์ง๋ก ๋ณํฉ(merging)ํ๋ ๋ฉ๋ชจ๋ฆฌ ์ค๋ณต ์ ๊ฑฐ ๋ฉ์ปค๋์ฆ์ ๋๋ค. ๊ฐ์ํ ํ๊ฒฝ(KVM/QEMU)์์ ์ฌ๋ฌ ๊ฐ์ ๋จธ์ ์ด ๋์ผํ ์ปค๋์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์คํํ ๋, ๋๋ ๊ฐ์ ํ๋ก์ธ์ค๋ฅผ fork()ํ ํ COW๋ก ๋ถ๋ฆฌ๋์ง ์์ ํ์ด์ง๋ฅผ ๋ค์ ๋ณํฉํ ๋ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ํฌ๊ฒ ์ ๊ฐํ ์ ์์ต๋๋ค.
KSM์ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ปค๋ ์ค๋ ๋์ธ ksmd๊ฐ ์ฃผ๊ธฐ์ ์ผ๋ก ๋ชจ๋ VM_MERGEABLE ํ๋๊ทธ๊ฐ ์ค์ ๋ VMA๋ฅผ ์ค์บํ๊ณ , ํ์ด์ง ๋ด์ฉ์ ๋น๊ตํ์ฌ stable tree(์ฐ๊ธฐ ๋ณดํธ๋ KSM ํ์ด์ง)์ unstable tree(์ผ์์ ์ผ๋ก ๋ณด๊ด๋๋ ํ๋ณด ํ์ด์ง)์ ์ ๋ ฌํฉ๋๋ค. ๋์ผํ ํ์ด์ง๋ฅผ ์ฐพ์ผ๋ฉด write-protect ํ PTE๋ฅผ ๊ต์ฒดํ์ฌ ๋ฌผ๋ฆฌ ํ์ด์ง๋ฅผ ๊ณต์ ํฉ๋๋ค.
์ ํ๋ฆฌ์ผ์ด์
์ madvise(..., MADV_MERGEABLE)๋ก ๋ณํฉ ํ๋ณด๋ฅผ ๋ช
์ํ๊ณ , MADV_UNMERGEABLE๋ก ์ ์ธํฉ๋๋ค. ์ด์์๋ sysfs๋ก ksmd์ ์คํ ์ฌ๋ถ์ ์ค์บ ์๋๋ฅผ ์กฐ์ ํฉ๋๋ค.
๊ฐ์ ์ฌ๋ณธ ์ฌ๋ฌ ๋ถ๋ฅผ ํ ๊ถ์ผ๋ก ์ ๋ฆฌํด ๋๋จธ์ง๋ฅผ ๊ณต์ ํ๋ ๋์๊ด ์ฌ๋ณธ ๊ด๋ฆฌ์ ๋น์ทํฉ๋๋ค. ์ค์บ๊ณผ ๋น๊ต ๋น์ฉ์ด ์์ผ๋ฏ๋ก CPU ์ค๋ฒํค๋๊ฐ ์๊ธฐ๋ฉฐ, ํ์ํ VMA๋ง ๊ณจ๋ผ ์ผ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
์์ค ํ์ผ ๊ฒฝ๋ก
mm/ksm.c โ KSM ํต์ฌ ๊ตฌํ (์ค์บ, ๋น๊ต, ๋ณํฉ, stable/unstable tree)
mm/madvise.c โ MADV_MERGEABLE/MADV_UNMERGEABLE ์ฒ๋ฆฌ
include/linux/ksm.h โ KSM ์ธ๋ถ API ๋ฐ ์ธ๋ผ์ธ ํจ์
mm/mm_slot.h โ mm_slot ํด์ ํ
์ด๋ธ ๊ด๋ฆฌ
include/linux/mm.h โ struct mm_struct ๋ด ksm ๊ด๋ จ ํ๋
๋น ๋ฅธ ์ ๊ฒ ๋ช ๋ น
# KSM ์ค๋ ๋ ์ํ ํ์ธ
ps aux | grep ksmd
# KSM ๋ณํฉ ํต๊ณ (์ ์ฒด ์์คํ
)
cat /sys/kernel/mm/ksm/pages_shared # stable tree์ ๊ณ ์ KSM ํ์ด์ง ์
cat /sys/kernel/mm/ksm/pages_sharing # KSM ํ์ด์ง๋ฅผ ๊ณต์ ์ค์ธ ์ถ๊ฐ ๋งคํ ์
cat /sys/kernel/mm/ksm/pages_unshared # unstable tree์ ๋ฏธ๋ณํฉ ํ๋ณด ์
cat /sys/kernel/mm/ksm/pages_volatile # rmap_item ์ค ์ด๋ ํธ๋ฆฌ์๋ ์๋ ์
cat /sys/kernel/mm/ksm/pages_scanned # ์ด ์ค์บ๋ ํ์ด์ง ์
cat /sys/kernel/mm/ksm/full_scans # ์ ์ฒด ์ค์บ ์๋ฃ ํ์
cat /sys/kernel/mm/ksm/pages_skipped # smart scan์ผ๋ก ๊ฑด๋๋ด ํ์ด์ง ์
cat /sys/kernel/mm/ksm/ksm_zero_pages # zero page ๋ณํฉ ์
# KSM ์์ต ๊ณ์ฐ (์ ์ฝ ๋ฉ๋ชจ๋ฆฌ - ์ค๋ฒํค๋)
cat /sys/kernel/mm/ksm/general_profit
# KSM ์ค์บ ํ๋ผ๋ฏธํฐ ํ์ธ/์กฐ์
cat /sys/kernel/mm/ksm/pages_to_scan # ํ ๋ฒ์ ์ค์บํ ํ์ด์ง ์ (๊ธฐ๋ณธ 100)
cat /sys/kernel/mm/ksm/sleep_millisecs # ์ค์บ ๊ฐ ๋๊ธฐ ์๊ฐ (๊ธฐ๋ณธ 20ms)
cat /sys/kernel/mm/ksm/run # 0:์ ์ง, 1:๋ณํฉ, 2:๋ณํฉํด์
cat /sys/kernel/mm/ksm/merge_across_nodes # NUMA ๋
ธ๋ ๊ฐ ๋ณํฉ ํ์ฉ ์ฌ๋ถ
cat /sys/kernel/mm/ksm/use_zero_pages # zero page ๋ณํฉ ํ์ฑํ ์ฌ๋ถ
cat /sys/kernel/mm/ksm/smart_scan # ์ค๋งํธ ์ค์บ ํ์ฑํ ์ฌ๋ถ
cat /sys/kernel/mm/ksm/max_page_sharing # ํ๋์ KSM ํ์ด์ง ์ต๋ ๊ณต์ ์ (๊ธฐ๋ณธ 256)
# KSM advisor ํ์ธ/์กฐ์
cat /sys/kernel/mm/ksm/advisor_mode # none / scan-time
cat /sys/kernel/mm/ksm/advisor_max_cpu # advisor ์ต๋ CPU%
cat /sys/kernel/mm/ksm/advisor_target_scan_time # ๋ชฉํ ์ค์บ ์๊ฐ(์ด)
echo scan-time | sudo tee /sys/kernel/mm/ksm/advisor_mode
echo none | sudo tee /sys/kernel/mm/ksm/advisor_mode
# KSM ํ์ฑํ (sysfs๋ฅผ ํตํ ์ ์ด)
echo 1 | sudo tee /sys/kernel/mm/ksm/run # ksmd ์์
echo 0 | sudo tee /sys/kernel/mm/ksm/run # ksmd ์ค์ง
# ํ๋ก์ธ์ค๋ณ KSM ์ฌ์ฉ๋ ํ์ธ
cat /proc/<pid>/status | grep KSM
awk '/VmFlags/ && /mg/ {print}' /proc/<pid>/smaps # mergeable VMA ํ์ธ
# per-mm KSM ์ ๋ณด (/proc/<pid>/ksm_stat - ํด๋น ์ปค๋์์ ์ง์ ์)
cat /proc/<pid>/ksm_stat 2>/dev/null
# KSM ๊ด๋ จ ์ปค๋ ์ค์ ํ์ธ
zgrep KSM /proc/config.gz 2>/dev/null || grep KSM /boot/config-$(uname -r)
ํต์ฌ ์๋ฃ๊ตฌ์กฐ
struct ksm_stable_node
stable tree์ ๋ ธ๋๋ก, write-protect๋ KSM ํ์ด์ง๋ฅผ ๋ํ๋ ๋๋ค. ๋์ผํ ๋ด์ฉ์ ์ฌ๋ฌ ๋งคํ์ด ์์ ๋ chain/dup ๊ตฌ์กฐ๋ก ๊ด๋ฆฌ๋ฉ๋๋ค.
// mm/ksm.c:159-185
struct ksm_stable_node {
union {
struct rb_node node; /* stable tree ๋ด rb-tree ๋
ธ๋ */
struct { /* migration list์ ์์ ๋ */
struct list_head *head;
struct {
struct hlist_node hlist_dup; /* chain ๋ด dup ์ฐ๊ฒฐ */
struct list_head list; /* migrate_nodes ์ฐ๊ฒฐ */
};
};
};
struct hlist_head hlist; /* ์ด stable node๋ฅผ ์ฌ์ฉํ๋ rmap_item ๋ชฉ๋ก */
union {
unsigned long kpfn; /* KSM ํ์ด์ง์ ๋ฌผ๋ฆฌ ํ๋ ์ ๋ฒํธ */
unsigned long chain_prune_time; /* chain pruning ๋ง์ง๋ง ์๊ฐ */
};
#define STABLE_NODE_CHAIN -1024 /* chain ๋
ธ๋ ํ๋ณ ๋ง์ปค */
int rmap_hlist_len; /* hlist์ rmap_item ์ ๋๋ STABLE_NODE_CHAIN */
#ifdef CONFIG_NUMA
int nid; /* NUMA ๋
ธ๋ ID */
#endif
};
struct ksm_rmap_item
๊ฐ์ ์ฃผ์โ๋ฌผ๋ฆฌ ํ์ด์ง์ ์ญ๋ฐฉํฅ ๋งคํ(rmap) ํญ๋ชฉ์ ๋๋ค. unstable tree์ rb-tree ๋ ธ๋์ด๊ฑฐ๋ stable tree์ hlist ์์๋ก ์ฌ์ฉ๋ฉ๋๋ค.
// mm/ksm.c:201-221
struct ksm_rmap_item {
struct ksm_rmap_item *rmap_list; /* mm_slot์ ๋จ์ผ ์ฐ๊ฒฐ ๋ฆฌ์คํธ */
union {
struct anon_vma *anon_vma; /* stable tree์ ์์ ๋ anon_vma */
#ifdef CONFIG_NUMA
int nid; /* unstable tree์ ์์ ๋ NUMA node */
#endif
};
struct mm_struct *mm; /* ์ด rmap_item์ด ์ํ mm */
unsigned long address; /* ๊ฐ์ ์ฃผ์ + ํ๋๊ทธ ๋นํธ */
unsigned int oldchecksum; /* unstable tree์์ ์ด์ ์ฒดํฌ์ฌ */
rmap_age_t age; /* ์ค์บ ๋ฐ๋ณต ํ์ (์ค๋งํธ ์ค์บ์ฉ) */
rmap_age_t remaining_skips; /* ๋จ์ ๊ฑด๋๋ ํ์ */
union {
struct rb_node node; /* unstable tree ๋ด rb-tree ๋
ธ๋ */
struct { /* stable tree์์ ์ฌ์ฉ ์ */
struct ksm_stable_node *head;
struct hlist_node hlist;
};
};
};
// ์ฃผ์ ํ์ ๋นํธ ํ๋๊ทธ (mm/ksm.c:223-225)
#define SEQNR_MASK 0x0ff /* ๋ถ์์ ํธ๋ฆฌ seqnr */
#define UNSTABLE_FLAG 0x100 /* unstable tree ๋
ธ๋ */
#define STABLE_FLAG 0x200 /* stable tree์ rmap_item */
struct ksm_mm_slot
์ค์บ ์ปค์๊ฐ ํ์ฌ ์์นํ mm ์ ๋ณด๋ฅผ ๋ด์ต๋๋ค. ํด์ ํ ์ด๋ธ๋ก mmโmm_slot ๋น ๋ฅธ ์กฐํ๋ฅผ ์ง์ํฉ๋๋ค.
// mm/ksm.c:126-129
struct ksm_mm_slot {
struct mm_slot slot; /* ํด์ + ์ฐ๊ฒฐ ๋ฆฌ์คํธ ๊ด๋ฆฌ */
struct ksm_rmap_item *rmap_list; /* ์ด mm์ rmap_item ๋ชฉ๋ก ๋จธ๋ฆฌ */
};
// mm/mm_slot.h:15-19
struct mm_slot {
struct hlist_node hash; /* ํด์ ํ
์ด๋ธ ์ฐ๊ฒฐ */
struct list_head mm_node; /* mm ๋ชฉ๋ก ์ฐ๊ฒฐ */
struct mm_struct *mm; /* ๋์ mm_struct */
};
struct ksm_scan (์ค์บ ์ปค์)
์ ์ญ ์ค์บ ์ปค์๋ก, ํ์ฌ ์ด๋ค mm, ์ด๋ค ์ฃผ์, ์ด๋ค rmap_list๋ฅผ ์ค์บ ์ค์ธ์ง ์ถ์ ํฉ๋๋ค.
// mm/ksm.c:140-145
struct ksm_scan {
struct ksm_mm_slot *mm_slot; /* ํ์ฌ ์ค์บ ์ค์ธ mm_slot */
unsigned long address; /* ๋ค์ ์ค์บํ ๊ฐ์ ์ฃผ์ */
struct ksm_rmap_item **rmap_list; /* ๋ค์ rmap_item์ ๊ฐ๋ฆฌํค๋ ํฌ์ธํฐ */
unsigned long seqnr; /* ์๋ฃ๋ ์ ์ฒด ์ค์บ ํ์ */
};
struct advisor_ctx (KSM Advisor)
scan-time advisor๊ฐ ์ค์บ ์๋๋ฅผ ์ ์ํ์ผ๋ก ์กฐ์ ํ๊ธฐ ์ํ ์ปจํ ์คํธ์ ๋๋ค.
// mm/ksm.c:325-331
struct advisor_ctx {
ktime_t start_scan; /* ํ์ฌ ์ค์บ ์์ ์๊ฐ */
unsigned long scan_time; /* ์ด์ ์ค์บ ์์ ์๊ฐ */
unsigned long change; /* pages_to_scan ๋ณ๊ฒฝ ๋น์จ (EWMA) */
unsigned long long cpu_time; /* ์ด์ ์ค์บ์ ksmd CPU ์ฌ์ฉ ์๊ฐ */
};
์ ์ญ ํธ๋ฆฌ ๋ฐ ์ฃผ์ ๋ณ์
// mm/ksm.c:228-248 โ stable/unstable tree ๋ฃจํธ
static struct rb_root one_stable_tree[1] = { RB_ROOT };
static struct rb_root one_unstable_tree[1] = { RB_ROOT };
static struct rb_root *root_stable_tree = one_stable_tree;
static struct rb_root *root_unstable_tree = one_unstable_tree;
// mm/ksm.c:252-301 โ ์ฃผ์ ํต๊ณ ๋ณ์
static unsigned long ksm_pages_scanned; // ์ค์บ๋ ํ์ด์ง ์
static unsigned long ksm_pages_shared; // stable tree ๊ณ ์ KSM ํ์ด์ง ์
static unsigned long ksm_pages_sharing; // KSM ํ์ด์ง ๊ณต์ ๋งคํ ์
static unsigned long ksm_pages_unshared; // unstable tree ๋ฏธ๋ณํฉ ์
static unsigned long ksm_rmap_items; // ์ฌ์ฉ ์ค rmap_item ์
static unsigned long ksm_pages_skipped; // smart scan์ผ๋ก ๊ฑด๋๋ด ์
ํต์ฌ ํจ์
1. ksm_scan_thread() โ ksmd ๋ฉ์ธ ์ค๋ ๋
// mm/ksm.c:2801-2826
static int ksm_scan_thread(void *nothing)
์ญํ : ๋ฐฑ๊ทธ๋ผ์ด๋์์ ksm_do_scan()์ ๋ฐ๋ณต ํธ์ถํ๋ ์ปค๋ ์ค๋ ๋์
๋๋ค. set_user_nice(current, 5)๋ก ๋ฎ์ ์ฐ์ ์์๋ก ๋์ํ๋ฉฐ, KSM_RUN_MERGE ์ํ์ผ ๋๋ง ์ค์บ์ ์ํํฉ๋๋ค.
๋ถ๊ธฐ ๋ก์ง:
ksmd_should_run() โ ksm_run & KSM_RUN_MERGE && mm_list ๋น์ด์์ง ์์์ด๋ฉด ์ค์บ ์คํksm_thread_sleep_millisecs ๋์ ๋๊ธฐksm_thread_sleep_millisecs ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด ์ฆ์ ๊นจ์ด๋จ (ksm_iter_wait)2. ksm_do_scan() โ ์ค์บ ์์ปค
// mm/ksm.c:2780-2794
static void ksm_do_scan(unsigned int scan_npages)
์ญํ : scan_npages๋งํผ ํ์ด์ง๋ฅผ ์ค์บํฉ๋๋ค. ๊ฐ ๋ฐ๋ณต์์ scan_get_next_rmap_item()์ผ๋ก ๋ค์ ํ๋ณด ํ์ด์ง๋ฅผ ์ป๊ณ , cmp_and_merge_page()๋ก ๋น๊ต/๋ณํฉ์ ์๋ํฉ๋๋ค.
๋ถ๊ธฐ ๋ก์ง:
freezing(current)์ด๋ฉด ์ฆ์ ์ข
๋ฃ (๋๊ฒฐ ์ํ)rmap_item์ด NULL์ด๋ฉด ๋ ์ด์ ์ค์บํ ๊ฒ์ด ์์ โ ์ข
๋ฃput_page(page)๋ก ์ฐธ์กฐ ํด์ , ksm_pages_scanned++3. cmp_and_merge_page() โ ํต์ฌ ๋น๊ต/๋ณํฉ
// mm/ksm.c:2248-2386
static void cmp_and_merge_page(struct page *page, struct ksm_rmap_item *rmap_item)
์ญํ : ๋จ์ผ ํ์ด์ง์ stable tree ๊ฒ์, checksum ๋น๊ต, unstable tree ๊ฒ์/์ฝ์ , ๋ณํฉ์ ์ํํ๋ ํต์ฌ ํจ์์ ๋๋ค.
๋ถ๊ธฐ ๋ก์ง:
1. ์ด๋ฏธ stable tree์ ์์ผ๋ฉด โ is_page_sharing_candidate() ํ์ธ ํ ์กฐ๊ธฐ ๋ฐํ
2. checksum์ด ์ด์ ๊ณผ ๋ค๋ฅด๋ฉด โ ํ์ด์ง๊ฐ ๋น๋ฒํ ๋ณ๊ฒฝ๋จ โ unstable tree์๋ ๋ฃ์ง ์์
3. try_to_merge_with_zero_page() โ zero page ๋ณํฉ ๊ฐ๋ฅํ๋ฉด ๋ณํฉ ํ ๋ฐํ
4. stable_tree_search(page) โ stable tree์์ ๋์ผ ํ์ด์ง ์ฐพ๊ธฐ
- ์ฐพ์ผ๋ฉด โ try_to_merge_with_ksm_page()๋ก ๋ณํฉ ์๋
5. unstable tree์์ unstable_tree_search_insert() โ ๋์ผ ํ์ด์ง ์ฐพ์ผ๋ฉด
- try_to_merge_two_pages()๋ก ๋ ํ์ด์ง๋ฅผ ๋ณํฉ ํ stable tree์ ์ฝ์
4. stable_tree_search() โ stable tree ํ์
// mm/ksm.c:1825-2030
static struct folio *stable_tree_search(struct page *page)
์ญํ : stable tree์์ ์ค์บ ์ค์ธ ํ์ด์ง์ ๋์ผํ ๋ด์ฉ์ KSM ํ์ด์ง๋ฅผ ์ฐพ์ต๋๋ค. chain/dup ๊ตฌ์กฐ๋ฅผ ์ํํ๋ฉฐ memcmp_pages()๋ก ๋ด์ฉ์ ๋น๊ตํฉ๋๋ค.
๋ถ๊ธฐ ๋ก์ง:
ksm_get_folio()๊ฐ ์ ๊ฑฐ โ ๊ฒ์ ์ฌ์์memcmp_pages() == 0 โ ๋์ผ ํ์ด์ง ๋ฐ๊ฒฌ - is_page_sharing_candidate() โ ๊ณต์ ๊ฐ๋ฅํ๋ฉด folio ๋ฐํ
- ๊ณต์ ๋ถ๊ฐ โ chain ๋ด ๋ค๋ฅธ dup ํ์ ๋๋ NULL ๋ฐํ
5. unstable_tree_search_insert() โ unstable tree ๊ฒ์/์ฝ์
// mm/ksm.c:2133-2198
static struct ksm_rmap_item *unstable_tree_search_insert(
struct ksm_rmap_item *rmap_item,
struct page *page,
struct page **tree_pagep)
์ญํ : unstable tree์์ ๋์ผ ํ์ด์ง๋ฅผ ์ฐพ๊ฑฐ๋, ์์ผ๋ฉด ํ์ฌ rmap_item์ ์ฝ์ ํฉ๋๋ค. unstable tree๋ ๋งค ์ ์ฒด ์ค์บ ์๋ฃ ์ ๋ฆฌ์ ๋๋ฏ๋ก ์์ ํฉ๋๋ค.
๋ถ๊ธฐ ๋ก์ง:
memcmp_pages() == 0 โ ๋์ผ ํ์ด์ง ๋ฐ๊ฒฌ - NUMA ๋
ธ๋ ๋ถ์ผ์น ํ์ธ (!ksm_merge_across_nodes && ๋ค๋ฅธ ๋
ธ๋)
- *tree_pagep์ tree_page ์ค์ ํ tree_rmap_item ๋ฐํ
rmap_item->address |= UNSTABLE_FLAG | seqnr, ksm_pages_unshared++6. write_protect_page() โ ํ์ด์ง ์ฐ๊ธฐ ๋ณดํธ
// mm/ksm.c:1272-1361
static int write_protect_page(struct vm_area_struct *vma, struct folio *folio,
pte_t *orig_pte)
์ญํ : ๋ณํฉ ์ ์ ํ์ด์ง๋ฅผ ์ฐ๊ธฐ ๋ณดํธํฉ๋๋ค. dirty/clean ์ํ๊ฐ ์์ฌ ์์ผ๋ฉด PTE๋ฅผ ๋ค์ ๋ง์ถฐ์ผ ํ๋ฉฐ folio lock์ด ํ์ํฉ๋๋ค.
๋ถ๊ธฐ ๋ก์ง:
pte_write || pte_dirty || anon_exclusive || mm_tlb_flush_pending โ PTE ํด๋ฆฌ์ด ํ ์ฌ์ค์ folio_mapcount + 1 + swapped != folio_ref_count โ O_DIRECT ์งํ ์ค โ ์คํจanon_exclusive โ folio_try_share_anon_rmap_pte() ์คํจ ์ โ ์คํจ7. ksm_madvise() โ VMA ๋ณํฉ ํ์ฉ/ํด์
// mm/ksm.c:2975-3013
int ksm_madvise(struct vm_area_struct *vma, unsigned long start,
unsigned long end, int advice, vm_flags_t *vm_flags)
์ญํ : MADV_MERGEABLE์ MADV_UNMERGEABLE ์์ฒญ์ ์ฒ๋ฆฌํฉ๋๋ค. MADV_MERGEABLE์ __ksm_enter(mm)๋ก ํด๋น mm_struct๋ฅผ KSM ๊ด๋ฆฌ ๋์์ ๋ฃ๊ณ VM_MERGEABLE์ ์ผ๋ฉฐ, MADV_UNMERGEABLE์ ์ด๋ฏธ ๋ณํฉ๋ ํ์ด์ง๊ฐ ์์ผ๋ฉด break_ksm()์ผ๋ก ๋ถ๋ฆฌํ ๋ค ํ๋๊ทธ๋ฅผ ๋๋๋ค.
๋ถ๊ธฐ ๋ก์ง:
MADV_MERGEABLE โ vma_ksm_compatible() ํ์ธ, __ksm_enter(mm) ํ์ ์ ํธ์ถ, *vm_flags |= VM_MERGEABLEMADV_UNMERGEABLE โ VM_MERGEABLE๊ฐ ์์ผ๋ฉด ๋ฌด์, ์ต๋ช
VMA๋ฉด break_ksm(vma, start, end, true) ํ ํ๋๊ทธ ํด์ // mm/madvise.c:1416-1422
case MADV_MERGEABLE:
case MADV_UNMERGEABLE:
error = ksm_madvise(vma, range->start, range->end,
behavior, &new_flags);
if (error)
goto out;
break;
ํธ์ถ ํ๋ฆ
ksm_scan_thread() โ ksmd ๋ฉ์ธ ์ค๋ ๋
โโ ksm_do_scan(pages_to_scan) โ scan_npages๋งํผ ๋ฐ๋ณต
โโ scan_get_next_rmap_item(&page) โ ๋ค์ ํ๋ณด ํ์ด์ง/ rmap_item ํ๋
โโ walk_page_range_vma() โ VMA ์ํํ์ฌ ์ต๋ช
ํ์ด์ง ํ์
โโ get_next_rmap_item() โ rmap_item ํ ๋น/์กฐํ
โโ should_skip_rmap_item() โ smart scan: ๊ฑด๋๋ธ์ง ๊ฒฐ์
โโ cmp_and_merge_page(page, rmap_item) โ ํต์ฌ ๋น๊ต/๋ณํฉ
โโ calc_checksum() โ xxhash ๊ธฐ๋ฐ ์ฒดํฌ์ฌ ๊ณ์ฐ
โโ stable_tree_search(page) โ stable tree์์ ๋์ผ ํ์ด์ง ํ์
โ โโ memcmp_pages() โ ํ์ด์ง ๋ด์ฉ ๋ฐ์ดํธ ๋น๊ต
โ โโ chain_prune() โ stale chain/dup ์ ๋ฆฌ
โโ unstable_tree_search_insert() โ unstable tree ๊ฒ์/์ฝ์
โ โโ memcmp_pages() โ ํ์ด์ง ๋ด์ฉ ๋น๊ต
โโ try_to_merge_with_ksm_page() โ ๊ธฐ์กด KSM ํ์ด์ง์ ๋ณํฉ
โ โโ try_to_merge_one_page()
โ โโ write_protect_page() โ PTE ์ฐ๊ธฐ ๋ณดํธ
โ โโ replace_page() โ PTE๋ฅผ KSM ํ์ด์ง๋ก ๊ต์ฒด
โโ try_to_merge_two_pages() โ ๋ ๋ฏธ๋ณํฉ ํ์ด์ง ๋ณํฉ
โโ stable_tree_insert() โ stable tree์ ์ KSM ๋
ธ๋ ์ฝ์
โโ stable_tree_append() โ rmap_item์ stable node์ ์ฐ๊ฒฐ
์กฐ๊ฑด๋ณ ๋น๊ต
KSM ์ค์บ ๋ชจ๋ ๋น๊ต
| ํญ๋ชฉ | KSM_RUN_STOP (0) | KSM_RUN_MERGE (1) | KSM_RUN_UNMERGE (2) |
|---|---|---|---|
| ksmd ๋์ | ์ค์ง | ์ค์บ/๋ณํฉ ์ํ | ๋ณํฉ ํด์ ๋ง ์ํ |
| ํธ๋ฆฌ ๋์ | ๋ถ๋ณ | stable/unstable tree ๊ฐฑ์ | ๋ชจ๋ rmap_item์ unstable์์ ์ ๊ฑฐ |
| COW ๋ฐ๋ | ์์ | ๋ณํฉ ์ write-protect ๋ฐ์ | break_ksm()์ผ๋ก ๋ชจ๋ KSM ํ์ด์ง ๋ณํฉ ํด์ |
| ํธ๋ฆฌ๊ฑฐ | `echo 0 > run` | `echo 1 > run` | `echo 2 > run` |
Smart Scan vs ์ผ๋ฐ ์ค์บ
| ํญ๋ชฉ | Smart Scan (๊ธฐ๋ณธ ์ผ์ง) | ์ผ๋ฐ ์ค์บ (๋๋ฉด) |
|---|---|---|
| ๊ฑด๋๋ ๋ฉ์ปค๋์ฆ | age/remaining_skips๋ก ๋น๋ฒ ๋ณ๊ฒฝ ํ์ด์ง ์คํต | ๋ชจ๋ ํ์ด์ง ์ค์บ |
| ๊ฑด๋๋ ๊ธฐ์ค | age โฅ 3๋ถํฐ ์ ์ง์ ์คํต (1โ2โ4โ8ํ) | ์์ |
| pages_skipped | ์ฆ๊ฐ | 0 ์ ์ง |
| ๋์ | `folio_test_ksm()`์ด ์๋ ์ผ๋ฐ ์ต๋ช ํ์ด์ง | ๋ชจ๋ anonymous ํ์ด์ง |
| ์ฅ์ | ๋ถํ์ํ ์ค์บ ์ต์ํ, CPU ์ ์ฝ | ์์ ํ ์ค์บ ๋ณด์ฅ |
NUMA ๋ณํฉ ์ ์ฑ ๋น๊ต
| ํญ๋ชฉ | merge_across_nodes=1 (๊ธฐ๋ณธ) | merge_across_nodes=0 |
|---|---|---|
| stable/unstable tree | ์ ์ญ 1๊ฐ์ฉ | NUMA node๋ณ ๋ณ๋ tree |
| NUMA ์๊ฒฉ ๋ณํฉ | ํ์ฉ | ๋ถํ (๊ฐ์ ๋ ธ๋ ๋ด์์๋ง ๋ณํฉ) |
| ๋ฉ๋ชจ๋ฆฌ ์ ๊ฐ | ์ต๋ (์๊ฒฉ ํฌํจ) | locality ์ฐ์ |
| tree ๊ตฌ์กฐ | `root_stable_tree[0]`๋ง ์ฌ์ฉ | `root_stable_tree[nr_node_ids]` ๋ฐฐ์ด |
Advisor ๋ชจ๋ ๋น๊ต
| ํญ๋ชฉ | KSM_ADVISOR_NONE (๊ธฐ๋ณธ) | KSM_ADVISOR_SCAN_TIME |
|---|---|---|
| pages_to_scan ์ ์ด | ์๋ (sysfs) | ์๋ ์ ์ํ |
| ๋ชฉํ | ์์ | `ksm_advisor_target_scan_time` (๊ธฐ๋ณธ 200์ด) |
| CPU ์ ํ | ์์ | `KSM_ADVISOR_MIN_CPU` (10%) ~ `ksm_advisor_max_cpu` (70%) |
| EWMA ์ ์ฉ | ์์ | ์ค์บ ์๊ฐ ๋ณํ๋์ EWMA๋ก ํํ |
VMA ์ง์ ๊ฒฝ๋ก ๋น๊ต
| ํญ๋ชฉ | ์ ํ๋ฆฌ์ผ์ด์ `madvise` | ์ด์์ sysfs |
|---|---|---|
| ๋์ | ๊ฐ๋ณ VMA | ์ ์ฒด ksmd ์ ์ฑ |
| ํต์ฌ ๋์ | `MADV_MERGEABLE`๋ก `VM_MERGEABLE` ์ค์ , `MADV_UNMERGEABLE`๋ก ํด์ | `run`, `pages_to_scan`, `sleep_millisecs`, `advisor_mode` ์กฐ์ |
| ์ปค๋ ์ง์ ์ | `mm/madvise.c` โ `ksm_madvise()` | `mm/ksm.c` sysfs store ํจ์ |
| ํจ๊ณผ | ๋ณํฉ ํ๋ณด ์ง์ /ํด์ | ์ค์บ ์คํ, ์๋, CPU ์ํ ์ ์ด |
Stable Tree ๋ ธ๋ ์ ํ ๋น๊ต
| ํญ๋ชฉ | ์ผ๋ฐ stable_node | chain stable_node | dup stable_node |
|---|---|---|---|
| ์ญํ | KSM ํ์ด์ง 1๊ฐ๋ฅผ ๋ํ๋ | ๋์ผ ๋ด์ฉ์ ์ฌ๋ฌ KSM ํ์ด์ง ๊ทธ๋ฃน | chain ๋ด ๊ฐ๋ณ KSM ํ์ด์ง |
| rmap_hlist_len | 0 ~ ksm_max_page_sharing | STABLE_NODE_CHAIN (-1024) | 0 ~ ksm_max_page_sharing |
| hlist | rmap_item ์ฐ๊ฒฐ | dup stable_node ์ฐ๊ฒฐ | rmap_item ์ฐ๊ฒฐ |
| rb_tree | (rb_node ์ฌ์ฉ) | (rb_node ์ฌ์ฉ) | (rb_node ๋ฏธ์ฌ์ฉ, hlist_dup๋ก chain์ ์ฐ๊ฒฐ) |
KSM ์์ต ๊ณ์ฐ
KSM์ ๋ฉ๋ชจ๋ฆฌ ์ ๊ฐ ํจ๊ณผ๋ general_profit์ผ๋ก ์ธก์ ๋ฉ๋๋ค:
general_profit = (pages_sharing + ksm_zero_pages) ร PAGE_SIZE
- rmap_items ร sizeof(struct ksm_rmap_item)
๊ด๋ จ ๋ฌธ์
์ฐธ๊ณ : KSM Sysfs ์ธํฐํ์ด์ค ์์ฝ
| ํ์ผ | ์ฝ๊ธฐ/์ฐ๊ธฐ | ์ค๋ช | ๊ธฐ๋ณธ๊ฐ |
|---|---|---|---|
| `/sys/kernel/mm/ksm/run` | RW | 0:์ค์ง, 1:๋ณํฉ, 2:๋ณํฉํด์ | 0 |
| `/sys/kernel/mm/ksm/pages_to_scan` | RW | ํ ๋ฒ์ ์ค์บํ ํ์ด์ง ์ | 100 |
| `/sys/kernel/mm/ksm/sleep_millisecs` | RW | ์ค์บ ๊ฐ ๋๊ธฐ (ms) | 20 |
| `/sys/kernel/mm/ksm/merge_across_nodes` | RW | NUMA ๊ฐ ๋ณํฉ ํ์ฉ | 1 |
| `/sys/kernel/mm/ksm/use_zero_pages` | RW | zero page ๋ณํฉ | 0 |
| `/sys/kernel/mm/ksm/smart_scan` | RW | ์ค๋งํธ ์ค์บ | 1 |
| `/sys/kernel/mm/ksm/max_page_sharing` | RW | ์ต๋ ๊ณต์ ๋งคํ ์ | 256 |
| `/sys/kernel/mm/ksm/advisor_mode` | RW | none / scan-time | none |
| `/sys/kernel/mm/ksm/advisor_max_cpu` | RW | ์ต๋ CPU% | 70 |
| `/sys/kernel/mm/ksm/advisor_min_pages_to_scan` | RW | ์ต์ ์ค์บ ํ์ด์ง ์ | 500 |
| `/sys/kernel/mm/ksm/advisor_max_pages_to_scan` | RW | ์ต๋ ์ค์บ ํ์ด์ง ์ | 30000 |
| `/sys/kernel/mm/ksm/advisor_target_scan_time` | RW | ๋ชฉํ ์ค์บ ์๊ฐ (์ด) | 200 |
| `/sys/kernel/mm/ksm/pages_shared` | RO | ๊ณ ์ KSM ํ์ด์ง ์ | - |
| `/sys/kernel/mm/ksm/pages_sharing` | RO | ๊ณต์ ๋งคํ ์ | - |
| `/sys/kernel/mm/ksm/pages_unshared` | RO | ๋ฏธ๋ณํฉ ํ๋ณด ์ | - |
| `/sys/kernel/mm/ksm/pages_volatile` | RO | ์ด๋ ํธ๋ฆฌ์๋ ์๋ rmap_item | - |
| `/sys/kernel/mm/ksm/pages_scanned` | RO | ์ด ์ค์บ ํ์ด์ง ์ | - |
| `/sys/kernel/mm/ksm/pages_skipped` | RO | ๊ฑด๋๋ด ํ์ด์ง ์ | - |
| `/sys/kernel/mm/ksm/ksm_zero_pages` | RO | zero page ๋ณํฉ ์ | - |
| `/sys/kernel/mm/ksm/full_scans` | RO | ์ ์ฒด ์ค์บ ์๋ฃ ํ์ | - |
| `/sys/kernel/mm/ksm/general_profit` | RO | ๋ฉ๋ชจ๋ฆฌ ์์ต (bytes) | - |
| `/sys/kernel/mm/ksm/advisor_mode` | RW | none / scan-time | none |
| `/sys/kernel/mm/ksm/advisor_max_cpu` | RW | ์ต๋ CPU% | 70 |
| `/sys/kernel/mm/ksm/advisor_target_scan_time` | RW | ๋ชฉํ ์ค์บ ์๊ฐ (์ด) | 200 |