Memory Cgroup (memcg)
๊ฐ์ (Overview)
Memory Cgroup๋ cgroup v2์ ๋ฉ๋ชจ๋ฆฌ ์ปจํธ๋กค๋ฌ๋ก, ํ๋ก์ธ์ค ๊ทธ๋ฃน๋ณ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ๊ณ์ (charge), ์ ํ(limit), ํ์(reclaim)ํ๋ ์ปค๋ ์๋ธ์์คํ
์
๋๋ค. ์ปจํ
์ด๋ ํ๊ฒฝ(Docker, Kubernetes)์์ ๋ฉ๋ชจ๋ฆฌ ๊ฒฉ๋ฆฌ(Isolation)์ ํต์ฌ ๋๊ตฌ์ด๋ฉฐ, memory.max(ํ๋ ์ ํ), memory.high(์ํํธ ์ ํ/throttle), memory.min/memory.low(๋ณดํธ ์์ค)๋ฅผ ํตํด ์ ์ฐํ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. ๊ฐ memcg๋ ๋
๋ฆฝ์ ์ธ LRU ๋ฆฌ์คํธ, ํ์ ์ ์ฑ
, OOM Killer ๋ฒ์๋ฅผ ๊ฐ์ง๋ฉฐ, cgroup ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ํตํด ๋ถ๋ชจ-์์ ๊ฐ ์์ ๋ฐฐ๋ถ์ด ๊ฐ๋ฅํฉ๋๋ค.
์ปค๋ ๋ด๋ถ์์ memcg๋ ํ์ด์ง ํ ๋น ์ page_counter๋ฅผ ํตํด ๊ณ์ ์ด ์ด๋ฃจ์ด์ง๋ฉฐ, ํ๊ณ ์ด๊ณผ ์ ์ง์ ํ์(direct reclaim) ๋๋ ์ ์ ๋ชจ๋ ๋ณต๊ท ์ throttle(__mem_cgroup_handle_over_high)์ด ๋ฐ์ํฉ๋๋ค. cgroup v1๊ณผ v2 ๋ชจ๋ ์ง์ํ๋ฉฐ, v1์์๋ memory.limit_in_bytes ๋ฑ ๋ ๊ฑฐ์ ์ธํฐํ์ด์ค๋ฅผ, v2์์๋ memory.max/memory.high/memory.low/memory.min์ ์ฌ์ฉํฉ๋๋ค.
์ผ์ ๋น์ ๋ก ๋ณด๋ฉด memcg๋ ๋ํ ๊ฑด๋ฌผ์ ์ธต๋ณ ์ ๋ ฅ ๊ณ๋๊ธฐ์ ์ฐจ๋จ๊ธฐ์ ๊ฐ๊น์ต๋๋ค. ๊ฐ ์ธต์ ์๊ธฐ ๊ณ๋๊ธฐ(memory.current)๋ก ์ฌ์ฉ๋์ ๋ณด๊ณ , ๊ด๋ฆฌ์๋ ์ ๋ ๋ณด์ฅ๋(memory.min), ๊ฐ๋ฅํ๋ฉด ์ง์ผ ์ค ๋ณดํธ๋(memory.low), ๊ณผ์ด์ ๋ง๋ ๊ฒฝ๊ณ ์ (memory.high), ๋์ผ๋ฉด ์ฐจ๋จ๋๋ ์ต๋์น(memory.max)๋ฅผ ์ค์ ํฉ๋๋ค. ์ปจํ
์ด๋๋ ๋ณ๋ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ๋งต์ ๊ฐ๋ ๊ฒ์ด ์๋๋ผ ํธ์คํธ ์ปค๋์ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ๋งต์ ๊ณต์ ํ๋ฉด์ ์ด ๊ณ๋๊ธฐ์ ์ฐจ๋จ๊ธฐ๋ง ๋ฐ๋ก ์ ์ฉ๋ฐ์ต๋๋ค.
// ์์ค ํ์ผ ๊ฒฝ๋ก
mm/memcontrol.c // ๋ฉ๋ชจ๋ฆฌ ์ปจํธ๋กค๋ฌ ํต์ฌ ๊ตฌํ (5679์ค)
mm/memcontrol-v1.c // cgroup v1 ๋ ๊ฑฐ์ ์ธํฐํ์ด์ค (2243์ค)
include/linux/memcontrol.h // ํต์ฌ ๊ตฌ์กฐ์ฒด ์ ์ (1939์ค)
include/linux/page_counter.h // ํ์ด์ง ์นด์ดํฐ ๊ธฐ๋ฐ ๊ณ์
๋น ๋ฅธ ์ ๊ฒ ๋ช ๋ น
# 1. ๋ฉ๋ชจ๋ฆฌ cgroup ์ ์ฒด ํํฉ
cat /proc/cgroups | grep memory
# 2. ํ์ฌ ๋ฉ๋ชจ๋ฆฌ cgroup ๊ณ์ธต ๊ตฌ์กฐ (cgroup v2)
ls /sys/fs/cgroup/
cat /sys/fs/cgroup/memory.current # ํ์ฌ ์ฌ์ฉ๋ (๋ฐ์ดํธ)
cat /sys/fs/cgroup/memory.max # ํ๋ ์ ํ
cat /sys/fs/cgroup/memory.high # ์ํํธ ์ ํ
cat /sys/fs/cgroup/memory.stat # ์์ธ ํต๊ณ
# 3. ํ๋ก์ธ์ค์ memcg ์์ ํ์ธ
cat /proc/$$/cgroup # ํ์ฌ ์
ธ์ cgroup ๊ฒฝ๋ก
cat /proc/$$/status | grep -i "voluntary_ctxt\|nonvoluntary_ctxt"
# 4. ๋ฉ๋ชจ๋ฆฌ ์ด๋ฒคํธ ํ์ธ (OOM, high ๋ฑ)
cat /sys/fs/cgroup/memory.events
# oom: memcg OOM ๋ฐ์ ํ์
# oom_kill: OOM killer์ ์ํด ์ข
๋ฃ๋ ํ๋ก์ธ์ค ์
# high: memory.high ์ด๊ณผ๋ก ํ์ ์๋๋ ํ์
# 5. memcg๋ณ ๋ฉ๋ชจ๋ฆฌ ์๋ ฅ ํ์ธ (PSI)
cat /proc/pressure/memory
cat /sys/fs/cgroup/memory.pressure # cgroup๋ณ PSI (์ปค๋ 6.1+)
# 6. ๋ฉ๋ชจ๋ฆฌ cgroup ํต๊ณ (v1 ๋ ๊ฑฐ์)
cat /sys/fs/cgroup/memory/memory.stat 2>/dev/null | head -20
# 7. memcg OOM ๊ทธ๋ฃน ํฌ ์ค์ ํ์ธ
cat /sys/fs/cgroup/memory.oom.group # 0 ๋๋ 1
# 8. ์ค์ ์ฌ์ฉ๋ ํ์ธ
cat /sys/fs/cgroup/memory.swap.current # ํ์ฌ ์ค์ ์ฌ์ฉ๋
cat /sys/fs/cgroup/memory.swap.max # ์ค์ ํ๋ ์ ํ
# 9. cgroup v2 ๋ฉ๋ชจ๋ฆฌ ์ ํ ํ
์คํธ์ฉ ๊ทธ๋ฃน ์์ฑ
sudo mkdir -p /sys/fs/cgroup/myapp
echo 512M | sudo tee /sys/fs/cgroup/myapp/memory.max
echo 400M | sudo tee /sys/fs/cgroup/myapp/memory.high
echo 256M | sudo tee /sys/fs/cgroup/myapp/memory.low
echo 128M | sudo tee /sys/fs/cgroup/myapp/memory.min
# 10. ํ๋ก์ธ์ค๋ฅผ ํ
์คํธ ๊ทธ๋ฃน์ ์ฐ๊ฒฐ
# PID์๋ ๊ด์ฐฐํ ๋์ ํ๋ก์ธ์ค ๋ฒํธ๋ฅผ ๋ฃ์ต๋๋ค.
PID=12345
echo $PID | sudo tee /sys/fs/cgroup/myapp/cgroup.procs
cat /sys/fs/cgroup/myapp/memory.current
cat /sys/fs/cgroup/myapp/memory.stat | head -30
cat /sys/fs/cgroup/myapp/memory.events
# 11. ๊ณ์ธต ์ ์ฒด์ ํต์ฌ ๋ฉ๋ชจ๋ฆฌ ํ์ผ ํ๊ธฐ
grep -R . /sys/fs/cgroup/memory.{current,max,high,low,min,events,numa_stat,oom.group} 2>/dev/null
# 12. cgroup v2 ์ปจํธ๋กค๋ฌ ํ์ฑํ ์ํ ํ์ธ
cat /sys/fs/cgroup/cgroup.controllers
cat /sys/fs/cgroup/cgroup.subtree_control
ํต์ฌ ์๋ฃ๊ตฌ์กฐ
struct mem_cgroup
๋ฉ๋ชจ๋ฆฌ ์ปจํธ๋กค๋ฌ์ ํต์ฌ ๊ตฌ์กฐ์ฒด. ๊ฐ cgroup์ ํ๋์ฉ ์์ฑ๋๋ฉฐ, ํ์ด์ง ๊ณ์ , ํ๊ณ ์ค์ , ํต๊ณ, ํ์ ์ดํฐ๋ ์ดํฐ ๋ฑ์ ํฌํจํฉ๋๋ค.
// include/linux/memcontrol.h:190-325
struct mem_cgroup {
struct cgroup_subsys_state css;
/* memcg ๋น๊ณต๊ฐ ID. cgroup๋ณด๋ค ์ค๋ ์ฌ๋ ์ค๋ธ์ ํธ ์๋ณ์ ์ฌ์ฉ */
struct mem_cgroup_private_id id;
/* ๊ณ์ ๋์ ์์ */
struct page_counter memory; /* v1๊ณผ v2 ๋ชจ๋ ์ฌ์ฉ */
union {
struct page_counter swap; /* v2 ์ ์ฉ */
struct page_counter memsw; /* v1 ์ ์ฉ */
};
/* ๋ฑ๋ก๋ ๋ก์ปฌ ํผํฌ ๊ฐ์์ */
struct list_head memory_peaks;
struct list_head swap_peaks;
spinlock_t peaks_lock;
/* ์ธํฐ๋ฝํธ ๊ณ์ ์ ๋ฒ์ ๊ฐ์ */
struct work_struct high_work;
#ifdef CONFIG_ZSWAP
unsigned long zswap_max;
/* ์ด memcg์ ํ์ด์ง๊ฐ zswap์์ swap์ผ๋ก ๋๋๋ ค ์ฐ์ด๋ ๊ฒ์ ์ ์ด */
bool zswap_writeback;
#endif /* CONFIG_ZSWAP */
/* vmpressure ์๋ฆผ */
struct vmpressure vmpressure;
/* OOM killer๊ฐ ํ ํ์คํฌ๋ฅผ ์ฃฝ์ผ ๋ ์์ ํ์คํฌ ์ ์ฒด๋ฅผ ์ฃฝ์ผ์ง ์ฌ๋ถ */
bool oom_group;
int swappiness;
/* memory.events์ memory.events.local */
struct cgroup_file events_file;
struct cgroup_file events_local_file;
/* memory.swap.events ํธ๋ค */
struct cgroup_file swap_events_file;
/* memory.stat */
struct memcg_vmstats *vmstats;
/* memory.events */
atomic_long_t memory_events[MEMCG_NR_MEMORY_EVENTS];
atomic_long_t memory_events_local[MEMCG_NR_MEMORY_EVENTS];
#ifdef CONFIG_MEMCG_NMI_SAFETY_REQUIRES_ATOMIC
/* NMI ์ปจํ
์คํธ์ MEMCG_KMEM */
atomic_t kmem_stat;
#endif /* CONFIG_MEMCG_NMI_SAFETY_REQUIRES_ATOMIC */
/*
* ์์ผ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ๋ฅผ ์ํ ํ์ ์๋ ฅ ํํธ. ์์ผ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ณ๋
* ๊ณ์ /์ถฉ์ ํ๋ ๋ ๊ฑฐ์ cgroup ๋ชจ๋์์๋ ์ฌ์ฉํ๋ฉด ์ ๋๋ค.
*/
u64 socket_pressure;
#if BITS_PER_LONG < 64
seqlock_t socket_pressure_seqlock;
#endif
int kmemcg_id;
/*
* objcg reparenting ๊ณผ์ ์์ memcg->objcg๋ ์ง์์ง๋ค.
* memcg->orig_objcg๋ memcg ์์ ๊ฐ ๋๋ ๋๊น์ง ์๋ objcg ํฌ์ธํฐ์
* ์ฐธ์กฐ๋ฅผ ๋ณด์กดํ๋ค.
*/
struct obj_cgroup __rcu *objcg;
struct obj_cgroup *orig_objcg;
/* objcg_lock์ผ๋ก ๋ณดํธ๋๋ ์์ objcg ๋ชฉ๋ก */
struct list_head objcg_list;
struct memcg_vmstats_percpu __percpu *vmstats_percpu;
#ifdef CONFIG_CGROUP_WRITEBACK
struct list_head cgwb_list;
struct wb_domain cgwb_domain;
struct memcg_cgwb_frn cgwb_frn[MEMCG_CGWB_FRN_CNT];
#endif /* CONFIG_CGROUP_WRITEBACK */
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
struct deferred_split deferred_split_queue;
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
#ifdef CONFIG_LRU_GEN_WALKS_MMU
/* memcg๋ณ mm_struct ๋ชฉ๋ก */
struct lru_gen_mm_list mm_list;
#endif /* CONFIG_LRU_GEN_WALKS_MMU */
#ifdef CONFIG_MEMCG_V1
/* ๋ ๊ฑฐ์ ์๋น์ ์งํฅ ์นด์ดํฐ */
struct page_counter kmem; /* v1 ์ ์ฉ */
struct page_counter tcpmem; /* v1 ์ ์ฉ */
struct memcg1_events_percpu __percpu *events_percpu;
unsigned long soft_limit;
/* memcg_oom_lock์ผ๋ก ๋ณดํธ */
bool oom_lock;
int under_oom;
/* OOM-Killer ๋นํ์ฑํ */
int oom_kill_disable;
/* ์๊ณ๊ฐ ๋ฐฐ์ด ๋ณดํธ */
struct mutex thresholds_lock;
/* ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์๊ณ๊ฐ. RCU๋ก ๋ณดํธ */
struct mem_cgroup_thresholds thresholds;
/* ๋ฉ๋ชจ๋ฆฌ+์ค์ ์ฌ์ฉ๋ ์๊ณ๊ฐ. RCU๋ก ๋ณดํธ */
struct mem_cgroup_thresholds memsw_thresholds;
/* oom notifier event fd์ฉ */
struct list_head oom_notify;
/* ๋ ๊ฑฐ์ TCP ๋ฉ๋ชจ๋ฆฌ ๊ณ์ */
bool tcpmem_active;
int tcpmem_pressure;
/* ์ฌ์ฉ์ ๊ณต๊ฐ์ด ์์ ํ๋ ค๋ ์ด๋ฒคํธ ๋ชฉ๋ก */
struct list_head event_list;
spinlock_t event_list_lock;
#endif /* CONFIG_MEMCG_V1 */
struct mem_cgroup_per_node *nodeinfo[];
};
ํต์ฌ ํ๋ ์ค๋ช :
| ํ๋ | ์ญํ |
|---|---|
| `css` | cgroup ์๋ธ์์คํ ์ํ โ ๋ถ๋ชจ/์์ ๊ด๊ณ, ์จ๋ผ์ธ ์ํ ๊ด๋ฆฌ |
| `memory` | `page_counter` ๊ธฐ๋ฐ ํ์ด์ง ๊ณ์ โ `memory.max`/`memory.high`์ ๋ด๋ถ ๊ตฌํ |
| `swap` / `memsw` | ์ค์ ๊ณ์ โ v2๋ `swap` ๋ ๋ฆฝ, v1์ `memsw` ํฉ์ฐ |
| `vmstats` / `vmstats_percpu` | ๋ฉ๋ชจ๋ฆฌ ํต๊ณ โ `memory.stat` ์ถ๋ ฅ์ ์์ฒ |
| `memory_events` | OOM, high ์ด๊ณผ ๋ฑ์ ์ด๋ฒคํธ ์นด์ดํฐ โ `memory.events` ์ถ๋ ฅ |
| `oom_group` | `memory.oom.group=1` ์ค์ ์ memcg ๋ด ๋ชจ๋ ํ๋ก์ธ์ค๋ฅผ ํ ๋ฒ์ OOM ํฌ |
| `swappiness` | memcg๋ณ ์ค์ ๊ฒฝํฅ โ ๊ธฐ๋ณธ๊ฐ 60, 0~200 ๋ฒ์ |
| `nodeinfo[]` | per-NUMA-node ์ ๋ณด โ LRU ๋ฒกํฐ, ํ์ ์ดํฐ๋ ์ดํฐ ํฌํจ |
struct page_counter
memcg์ memory, swap, v1 memsw๋ ๋ชจ๋ page_counter๋ก ๊ตฌํ๋ฉ๋๋ค. usage๋ ํ์ฌ ์ฌ์ฉ๋, min/low/high/max๋ cgroup v2 ๋ฉ๋ชจ๋ฆฌ ํ์ผ์ ๋ด๋ถ ๊ฐ์
๋๋ค.
// include/linux/page_counter.h:10-44
struct page_counter {
/*
* v2์์ 'usage'๊ฐ ๋ค๋ฅธ ํ๋์ ์บ์๋ผ์ธ์ ๊ณต์ ํ์ง ์๊ฒ ํ๋ค.
* memcg->memory.usage๋ struct mem_cgroup์์ ์์ฃผ ๊ฐฑ์ ๋๋ ํ๋๋ค.
*/
atomic_long_t usage;
unsigned long failcnt; /* v1 ์ ์ฉ ํ๋ */
CACHELINE_PADDING(_pad1_);
/* ์ ํจ memory.min๊ณผ memory.min ์ฌ์ฉ๋ ์ถ์ */
unsigned long emin;
atomic_long_t min_usage;
atomic_long_t children_min_usage;
/* ์ ํจ memory.low์ memory.low ์ฌ์ฉ๋ ์ถ์ */
unsigned long elow;
atomic_long_t low_usage;
atomic_long_t children_low_usage;
unsigned long watermark;
/* ์ต๊ทผ cgroup v2 ์ฌ์ค์ watermark */
unsigned long local_watermark;
/* ์ฝ๊ธฐ๊ฐ ๋ง์ ํ๋๋ฅผ ๋ณ๋ ์บ์๋ผ์ธ์ ๋๋ค. */
CACHELINE_PADDING(_pad2_);
bool protection_support;
bool track_failcnt;
unsigned long min;
unsigned long low;
unsigned long high;
unsigned long max;
struct page_counter *parent;
} ____cacheline_internodealigned_in_smp;
struct mem_cgroup_per_node
NUMA ๋ ธ๋๋ณ memcg ์ ๋ณด๋ฅผ ๋ด๋นํฉ๋๋ค.
// include/linux/memcontrol.h:88-123
struct mem_cgroup_per_node {
/* ์ฝ๊ธฐ ์ ์ฉ์ ๊ฐ๊น์ด ํ๋๋ ์์ชฝ์ ๋๋ค. */
struct mem_cgroup *memcg; /* ์ญ์ฐธ์กฐ, container_of๋ฅผ ์ธ ์ ์์ */
struct lruvec_stats_percpu __percpu *lruvec_stats_percpu;
struct lruvec_stats *lruvec_stats;
struct shrinker_info __rcu *shrinker_info;
#ifdef CONFIG_MEMCG_V1
/*
* v1 ์ ์ฉ ํ๋๋ฅผ ์ค๊ฐ ๋ฒํผ์ฒ๋ผ ๋ฐฐ์นํด ์ฝ๊ธฐ ์์ฃผ ํ๋์ ์์ฃผ ๊ฐฑ์ ๋๋
* ํ๋์ ์บ์๋ผ์ธ ๊ณต์ ๊ฒฝํฉ์ ์ค์ธ๋ค.
*/
struct rb_node tree_node; /* RB tree ๋
ธ๋ */
unsigned long usage_in_excess;/* ์ํํธ ํ๊ณ ์ด๊ณผ๋ */
bool on_tree;
#else
CACHELINE_PADDING(_pad1_);
#endif
/* ์์ฃผ ๊ฐฑ์ ๋๋ ํ๋๋ ๋์ชฝ์ ๋๋ค. */
struct lruvec lruvec;
CACHELINE_PADDING(_pad2_);
unsigned long lru_zone_size[MAX_NR_ZONES][NR_LRU_LISTS];
struct mem_cgroup_reclaim_iter iter;
#ifdef CONFIG_MEMCG_NMI_SAFETY_REQUIRES_ATOMIC
/* NMI ์ปจํ
์คํธ์ฉ slab ํต๊ณ */
atomic_t slab_reclaimable;
atomic_t slab_unreclaimable;
#endif
};
struct obj_cgroup
์ปค๋ ๋ฉ๋ชจ๋ฆฌ(slab) ๊ณ์ ์ ์ํ ์ค๋ธ์ ํธ ์ปจํธ๋กค๋ฌ. memcg์์ slab ์ค๋ธ์ ํธ๋ฅผ ๊ฐ๋ณ์ ์ผ๋ก ๊ณ์ ํ ๋ ์ฌ์ฉ๋ฉ๋๋ค.
// include/linux/memcontrol.h:174-182
struct obj_cgroup {
struct percpu_ref refcnt;
struct mem_cgroup *memcg;
atomic_t nr_charged_bytes;
union {
struct list_head list; /* objcg_lock์ผ๋ก ๋ณดํธ */
struct rcu_head rcu;
};
};
struct memcg_vmstats / memcg_vmstats_percpu
๋ฉ๋ชจ๋ฆฌ ํต๊ณ๋ฅผ ๊ด๋ฆฌํ๋ ๊ตฌ์กฐ์ฒด. per-CPU ๋ก์ปฌ ์นด์ดํฐ์ aggregate ์นด์ดํฐ๋ฅผ ๋ถ๋ฆฌํ์ฌ ์ฑ๋ฅ์ ์ต์ ํํฉ๋๋ค.
// mm/memcontrol.c:504-538
struct memcg_vmstats_percpu {
/* ๋ง์ง๋ง flush ์ดํ ํต๊ณ ๊ฐฑ์ ํ์ */
unsigned int stats_updates;
/* memcg_rstat_updated() ๋น ๋ฅธ ์ํ๋ฅผ ์ํ ์บ์ ํฌ์ธํฐ */
struct memcg_vmstats_percpu __percpu *parent_pcpu;
struct memcg_vmstats *vmstats;
/* ์ ํ๋๋ memcg_rstat_updated()์ฉ ๋จ์ผ ์บ์๋ผ์ธ ์์ ๋ค์ด๊ฐ์ผ ํ๋ค. */
/* ๋ก์ปฌ CPU์ cgroup์ ํ์ด์ง ์ํ ๋ฐ ์ด๋ฒคํธ */
long state[MEMCG_VMSTAT_SIZE];
unsigned long events[NR_MEMCG_EVENTS];
/* lockless ์ํฅ ์ ํ๋ฅผ ์ํ delta ๊ณ์ฐ */
long state_prev[MEMCG_VMSTAT_SIZE];
unsigned long events_prev[NR_MEMCG_EVENTS];
} ____cacheline_aligned;
struct memcg_vmstats {
/* ์ง๊ณ๋ CPU์ ํ์ ํธ๋ฆฌ ํ์ด์ง ์ํ ๋ฐ ์ด๋ฒคํธ */
long state[MEMCG_VMSTAT_SIZE];
unsigned long events[NR_MEMCG_EVENTS];
/* ๊ณ์ธตํํ์ง ์์ CPU ์ง๊ณ ํ์ด์ง ์ํ ๋ฐ ์ด๋ฒคํธ */
long state_local[MEMCG_VMSTAT_SIZE];
unsigned long events_local[NR_MEMCG_EVENTS];
/* tree ์ ํ ์ค์ธ ์์ ์นด์ดํธ */
long state_pending[MEMCG_VMSTAT_SIZE];
unsigned long events_pending[NR_MEMCG_EVENTS];
/* ๋ง์ง๋ง flush ์ดํ ํต๊ณ ๊ฐฑ์ ํ์ */
atomic_t stats_updates;
};
memcg ์ด๋ฒคํธ/ํต๊ณ enum
// include/linux/memcontrol.h:34-57
/* ๋ฒ์ฉ ๋
ธ๋ ํ์ด์ง ์ํ ์์ ์น๋ cgroup๋ณ ํ์ด์ง ์ํ */
enum memcg_stat_item {
MEMCG_SWAP = NR_VM_NODE_STAT_ITEMS,
MEMCG_SOCK,
MEMCG_PERCPU_B,
MEMCG_VMALLOC,
MEMCG_KMEM,
MEMCG_ZSWAP_B,
MEMCG_ZSWAPPED,
MEMCG_NR_STAT,
};
enum memcg_memory_event {
MEMCG_LOW,
MEMCG_HIGH,
MEMCG_MAX,
MEMCG_OOM,
MEMCG_OOM_KILL,
MEMCG_OOM_GROUP_KILL,
MEMCG_SWAP_HIGH,
MEMCG_SWAP_MAX,
MEMCG_SWAP_FAIL,
MEMCG_SOCK_THROTTLED,
MEMCG_NR_MEMORY_EVENTS,
};
ํต์ฌ ํจ์
1. mem_cgroup_css_alloc (cgroup ์์ฑ)
// mm/memcontrol.c:3822-3873
static struct cgroup_subsys_state * __ref
mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
{
struct mem_cgroup *parent = mem_cgroup_from_css(parent_css);
struct mem_cgroup *memcg, *old_memcg;
bool memcg_on_dfl = cgroup_subsys_on_dfl(memory_cgrp_subsys);
old_memcg = set_active_memcg(parent);
memcg = mem_cgroup_alloc(parent);
set_active_memcg(old_memcg);
if (IS_ERR(memcg))
return ERR_CAST(memcg);
page_counter_set_high(&memcg->memory, PAGE_COUNTER_MAX);
memcg1_soft_limit_reset(memcg);
#ifdef CONFIG_ZSWAP
memcg->zswap_max = PAGE_COUNTER_MAX;
WRITE_ONCE(memcg->zswap_writeback, true);
#endif
page_counter_set_high(&memcg->swap, PAGE_COUNTER_MAX);
if (parent) {
WRITE_ONCE(memcg->swappiness, mem_cgroup_swappiness(parent));
page_counter_init(&memcg->memory, &parent->memory, memcg_on_dfl);
page_counter_init(&memcg->swap, &parent->swap, false);
#ifdef CONFIG_MEMCG_V1
memcg->memory.track_failcnt = !memcg_on_dfl;
WRITE_ONCE(memcg->oom_kill_disable, READ_ONCE(parent->oom_kill_disable));
page_counter_init(&memcg->kmem, &parent->kmem, false);
page_counter_init(&memcg->tcpmem, &parent->tcpmem, false);
#endif
} else {
init_memcg_stats();
init_memcg_events();
page_counter_init(&memcg->memory, NULL, true);
page_counter_init(&memcg->swap, NULL, false);
#ifdef CONFIG_MEMCG_V1
page_counter_init(&memcg->kmem, NULL, false);
page_counter_init(&memcg->tcpmem, NULL, false);
#endif
root_mem_cgroup = memcg;
return &memcg->css;
}
if (memcg_on_dfl && !cgroup_memory_nosocket)
static_branch_inc(&memcg_sockets_enabled_key);
if (!cgroup_memory_nobpf)
static_branch_inc(&memcg_bpf_enabled_key);
return &memcg->css;
}
์ญํ : ์ memcg๋ฅผ ์์ฑํ๊ณ ์ด๊ธฐํํฉ๋๋ค. page_counter๋ฅผ ๋ถ๋ชจ์ ์ฐ๊ฒฐํ๊ณ , ํต๊ณ ๊ตฌ์กฐ์ฒด๋ฅผ ํ ๋นํฉ๋๋ค.
๋ถ๊ธฐ ๋ก์ง:
parent == NULL โ ๋ฃจํธ memcg ์์ฑ (ํต๊ณ ์ด๊ธฐํ, root_mem_cgroup ์ค์ )parent != NULL โ ์์ memcg ์์ฑ (๋ถ๋ชจ์ page_counter์ ์ฐ๊ฒฐ, swappiness ์์)cgroup_subsys_on_dfl(memory_cgrp_subsys) โ v2 ๋ชจ๋์์ ์์ผ/BPF ํค ํ์ฑํ2. try_charge_memcg (ํ์ด์ง ๊ณ์ )
// mm/memcontrol.c:2355-2548
static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
unsigned int nr_pages)
์ญํ : folio ํ ๋น ์ memcg์ ํ์ด์ง๋ฅผ ๊ณ์ (charge)ํฉ๋๋ค. ํ๊ณ ์ด๊ณผ ์ ํ์, OOM, force charge ๋ฑ์ ๋ถ๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ํํฉ๋๋ค.
๋ถ๊ธฐ ๋ก์ง:
1. consume_stock() โ per-CPU stock์์ ์ฆ์ ๊ณ์ ์ฑ๊ณต ์ ๋ฆฌํด
2. page_counter_try_charge(&memcg->memory) โ ํ๊ณ ๋ด ๊ณ์ ์ฑ๊ณต โ done_restock
3. ํ๊ณ ์ด๊ณผ ์:
- PF_MEMALLOC ํ๋๊ทธ โ ์ฆ์ force charge (goto force)
- task_in_memcg_oom() โ OOM ์ค์ด๋ฉด nomem
- gfpflags_allow_blocking() ๋ถ๊ฐ โ nomem
- try_to_free_mem_cgroup_pages() โ ์ง์ ํ์ ์๋
- ํ์ ํ margin ํ๋ณด ๊ฐ๋ฅํ๋ฉด retry
- mem_cgroup_oom() โ memcg OOM killer ๋ฐ๋
4. done_restock: stock ์์ฌ ๋ถ๋ ์ถฉ์ + memory.high ์ด๊ณผ ์ __mem_cgroup_handle_over_high() ์์ฝ
3. __mem_cgroup_handle_over_high (high ์ด๊ณผ ์ throttle)
// mm/memcontrol.c:2265-2353
void __mem_cgroup_handle_over_high(gfp_t gfp_mask)
์ญํ : memory.high ์ด๊ณผ ์ ํ ๋น์๋ฅผ throttleํฉ๋๋ค. ์ง์ ํ์ + schedule_timeout_killable๋ก ์ง์ฐ์ ์ ์ฉํฉ๋๋ค.
๋ถ๊ธฐ ๋ก์ง:
1. task_is_dying() โ ์ข
๋ฃ ์ค์ด๋ฉด ์ฆ์ ๋ฆฌํด
2. reclaim_high() โ ํ์ ์๋ (์ฒซ ๋ฒ์งธ๋ nr_pages, ์ดํ SWAP_CLUSTER_MAX)
3. ํ์ ์ฑ๊ณต or ์ฌ์๋ ๊ฐ๋ฅ โ retry_reclaim
4. calculate_high_delay() โ ์ด๊ณผ๋์ ๋น๋กํ ์ง์ฐ ์๊ฐ ๊ณ์ฐ
5. penalty_jiffies <= HZ / 100 โ ์ง์ฐ์ด ๋๋ฌด ์ ์ผ๋ฉด ์คํต
6. schedule_timeout_killable() โ ์ ์ ๋ชจ๋ ๋ณต๊ท ์ ์ค์ throttle
4. charge_memcg / __mem_cgroup_charge (folio ๊ณ์ ์ง์ ์ )
// mm/memcontrol.c:4739-4765
static int charge_memcg(struct folio *folio, struct mem_cgroup *memcg,
gfp_t gfp)
{
int ret;
ret = try_charge(memcg, gfp, folio_nr_pages(folio));
if (ret)
goto out;
css_get(&memcg->css);
commit_charge(folio, memcg);
memcg1_commit_charge(folio, memcg);
out:
return ret;
}
int __mem_cgroup_charge(struct folio *folio, struct mm_struct *mm, gfp_t gfp)
{
struct mem_cgroup *memcg;
int ret;
memcg = get_mem_cgroup_from_mm(mm);
ret = charge_memcg(folio, memcg, gfp);
css_put(&memcg->css);
return ret;
}
์ญํ : folio๋ฅผ memcg์ ๊ณ์ ํ๋ ์์ API. __mem_cgroup_charge๋ mm์์ memcg๋ฅผ ์ฐพ์ charge_memcg๋ฅผ ํธ์ถํฉ๋๋ค.
ํ๋ฆ:
__mem_cgroup_charge()
โโ get_mem_cgroup_from_mm(mm) // mm์ memcg ํ๋
โโ charge_memcg(folio, memcg, gfp)
โโ try_charge(memcg, gfp, nr_pages) // ๊ณ์ ์๋
โโ css_get(&memcg->css) // ์ฐธ์กฐ ์นด์ดํธ ์ฆ๊ฐ
โโ commit_charge(folio, memcg) // folio->memcg_data ์ค์
โโ memcg1_commit_charge(folio, memcg) // v1 ์ ์ฉ ํ์ฒ๋ฆฌ
โโ css_put(&memcg->css)
5. mem_cgroup_css_offline (cgroup ์ ๊ฑฐ)
// mm/memcontrol.c:3919-3939
static void mem_cgroup_css_offline(struct cgroup_subsys_state *css)
{
struct mem_cgroup *memcg = mem_cgroup_from_css(css);
memcg1_css_offline(memcg);
page_counter_set_min(&memcg->memory, 0);
page_counter_set_low(&memcg->memory, 0);
zswap_memcg_offline_cleanup(memcg);
memcg_offline_kmem(memcg);
reparent_deferred_split_queue(memcg);
reparent_shrinker_deferred(memcg);
wb_memcg_offline(memcg);
lru_gen_offline_memcg(memcg);
drain_all_stock(memcg);
mem_cgroup_private_id_put(memcg);
}
์ญํ : memcg๊ฐ ์คํ๋ผ์ธ๋ ๋ ๋ฆฌ์์ค๋ฅผ ์ ๋ฆฌํฉ๋๋ค. ๋ถ๋ชจ์๊ฒ ๋ฆฌ์์ค๋ฅผ ์์ํ๊ณ , ๊ด๋ จ ์์ปค๋ฅผ ๋๋ ์ธํฉ๋๋ค.
ํ๋ฆ:
mem_cgroup_css_offline()
โโ memcg1_css_offline(memcg) // v1 ๋ ๊ฑฐ์ ์ ๋ฆฌ
โโ page_counter_set_min/low(&memcg->memory, 0) // ๋ณดํธ ์ ๊ฑฐ
โโ zswap_memcg_offline_cleanup(memcg) // zswap ์ ๋ฆฌ
โโ memcg_offline_kmem(memcg) // ์ปค๋ ๋ฉ๋ชจ๋ฆฌ ์ ๋ฆฌ
โโ reparent_deferred_split_queue(memcg) // THP ๋ถํ ํ ์์
โโ reparent_shrinker_deferred(memcg) // shrinker ์์
โโ wb_memcg_offline(memcg) // ์ฐ๊ธฐ ๋๋๋ฆผ ์ ๋ฆฌ
โโ lru_gen_offline_memcg(memcg) // MGLRU ์ ๋ฆฌ
โโ drain_all_stock(memcg) // per-CPU stock ๋๋ ์ธ
โโ mem_cgroup_private_id_put(memcg) // ID ํด์
6. memory_files (cgroup v2 ํ์ผ ๋ฑ๋ก)
// mm/memcontrol.c:4624-4696
static struct cftype memory_files[] = {
{
.name = "current",
.flags = CFTYPE_NOT_ON_ROOT,
.read_u64 = memory_current_read,
},
{
.name = "peak",
.flags = CFTYPE_NOT_ON_ROOT,
.open = peak_open,
.release = peak_release,
.seq_show = memory_peak_show,
.write = memory_peak_write,
},
{
.name = "min",
.flags = CFTYPE_NOT_ON_ROOT,
.seq_show = memory_min_show,
.write = memory_min_write,
},
{
.name = "low",
.flags = CFTYPE_NOT_ON_ROOT,
.seq_show = memory_low_show,
.write = memory_low_write,
},
{
.name = "high",
.flags = CFTYPE_NOT_ON_ROOT,
.seq_show = memory_high_show,
.write = memory_high_write,
},
{
.name = "max",
.flags = CFTYPE_NOT_ON_ROOT,
.seq_show = memory_max_show,
.write = memory_max_write,
},
{
.name = "events",
.flags = CFTYPE_NOT_ON_ROOT,
.file_offset = offsetof(struct mem_cgroup, events_file),
.seq_show = memory_events_show,
},
{
.name = "events.local",
.flags = CFTYPE_NOT_ON_ROOT,
.file_offset = offsetof(struct mem_cgroup, events_local_file),
.seq_show = memory_events_local_show,
},
{
.name = "stat",
.seq_show = memory_stat_show,
},
#ifdef CONFIG_NUMA
{
.name = "numa_stat",
.seq_show = memory_numa_stat_show,
},
#endif
{
.name = "oom.group",
.flags = CFTYPE_NOT_ON_ROOT | CFTYPE_NS_DELEGATABLE,
.seq_show = memory_oom_group_show,
.write = memory_oom_group_write,
},
{
.name = "reclaim",
.flags = CFTYPE_NS_DELEGATABLE,
.write = memory_reclaim,
},
{ } /* ์ข
๋ฃ */
};
// mm/memcontrol.c:4698-4714
struct cgroup_subsys memory_cgrp_subsys = {
.css_alloc = mem_cgroup_css_alloc,
.css_online = mem_cgroup_css_online,
.css_offline = mem_cgroup_css_offline,
.css_released = mem_cgroup_css_released,
.css_free = mem_cgroup_css_free,
.css_reset = mem_cgroup_css_reset,
.css_rstat_flush = mem_cgroup_css_rstat_flush,
.attach = mem_cgroup_attach,
.fork = mem_cgroup_fork,
.exit = mem_cgroup_exit,
.dfl_cftypes = memory_files,
#ifdef CONFIG_MEMCG_V1
.legacy_cftypes = mem_cgroup_legacy_files,
#endif
.early_init = 0,
};
์ญํ : memory.current, memory.min, memory.low, memory.high, memory.max, memory.events, memory.numa_stat, memory.oom.group, memory.reclaim ๊ฐ์ cgroup v2 ํ์ผ์ memory controller์ ์ฐ๊ฒฐํฉ๋๋ค. ์ด ๋ฐฐ์ด์ memory_cgrp_subsys.dfl_cftypes๋ก ๋ฑ๋ก๋์ด ๋ฃจํธ๊ฐ ์๋ cgroup์์ ์ ์ด ํ์ผ๋ก ๋ณด์
๋๋ค.
ํธ์ถ ํ๋ฆ
folio ๊ณ์ ์ง์ ๊ฒฝ๋ก
ํ์ผ ์บ์: filemap_add_folio() [mm/filemap.c:949-969]
โโ mem_cgroup_charge(folio, NULL, gfp)
โโ __mem_cgroup_charge(folio, mm, gfp)
์ต๋ช
fault: alloc_anon_folio() [mm/memory.c:5179-5209]
โโ vma_alloc_folio(gfp, order, vma, addr)
โโ mem_cgroup_charge(folio, vma->vm_mm, gfp)
โโ __mem_cgroup_charge(folio, mm, gfp)
๊ณตํต ๊ณ์ : __mem_cgroup_charge() [mm/memcontrol.c:4755-4765]
โโ get_mem_cgroup_from_mm(mm)
โ โโ mem_cgroup_from_task(mm->owner)
โโ charge_memcg(folio, memcg, gfp)
โโ try_charge(memcg, gfp, folio_nr_pages(folio))
โ โโ try_charge_memcg(memcg, gfp_mask, nr_pages)
โ โโ consume_stock() [๋น ๋ฅธ ๊ฒฝ๋ก]
โ โโ page_counter_try_charge(&memcg->memory) [๊ณ์ ์๋]
โ โโ try_to_free_mem_cgroup_pages() [์ง์ ํ์]
โ โโ mem_cgroup_oom() [memcg OOM]
โ โโ done_restock [memory.high / memory.swap.high ํ์ธ]
โโ css_get(&memcg->css)
โโ commit_charge(folio, memcg)
โ โโ folio->memcg_data = (unsigned long)memcg
โโ memcg1_commit_charge(folio, memcg)
memcg OOM ๊ฒฝ๋ก
try_charge_memcg() โ mem_cgroup_oom()
โโ memcg_memory_event(memcg, MEMCG_OOM)
โโ memcg1_oom_prepare(memcg, &locked)
โโ mem_cgroup_out_of_memory(memcg, mask, order)
โโ mutex_lock_killable(&oom_lock)
โโ mem_cgroup_margin(memcg) ์ฌํ์ธ
โโ out_of_memory(&oc)
โโ oom_kill_process()
โโ mem_cgroup_get_oom_group(victim, oom_domain)
folio ํด์ ์ uncharge ๊ฒฝ๋ก
folio_memcg_free()
โโ __mem_cgroup_uncharge(folio)
โโ uncharge_folio(folio, &ug)
โโ folio_memcg_kmem() โ objcg ๊ฒฝ๋ก
โโ __folio_memcg() โ ์ผ๋ฐ ๋ฉ๋ชจ๋ฆฌ ๊ฒฝ๋ก
โโ uncharge_batch(ug)
โโ memcg_uncharge(memcg, nr_pages)
โโ page_counter_uncharge(&memcg->memory, nr_pages)
์กฐ๊ฑด๋ณ ๋น๊ต
memcg v1 vs v2 ์ฃผ์ ์ฐจ์ด
| ํญ๋ชฉ | cgroup v1 | cgroup v2 |
|---|---|---|
| ํ๊ณ ์ค์ | `memory.limit_in_bytes` | `memory.max` |
| ์ํํธ ํ๊ณ | `memory.soft_limit_in_bytes` | `memory.high` |
| ๋ณดํธ ์์ค | ์์ | `memory.min`, `memory.low` |
| ์ค์ ๊ณ์ | `memory.memsw.limit_in_bytes` (ํฉ์ฐ) | `memory.swap.max` (๋ ๋ฆฝ) |
| ์ปค๋ ๋ฉ๋ชจ๋ฆฌ | `memory.kmem.limit_in_bytes` | ๊ธฐ๋ณธ ๊ณ์ (๋ณ๋ ์ ํ ์์) |
| ์์ผ ๋ฉ๋ชจ๋ฆฌ | `memory.kmem.tcp.limit_in_bytes` | ๊ธฐ๋ณธ ๊ณ์ |
| OOM ์ ์ด | `memory.oom_control` | `memory.oom.group` |
| ์ด๋ฒคํธ | `memory.oom_control`์ `oom_kill` | `memory.events`์ `oom_kill` |
| ํต๊ณ | `memory.stat` (๋ค์ ์์ด) | `memory.stat` (ํ์ฅ) |
| PSI | ์์ | `memory.pressure` |
cgroup v2 ๋ณดํธ/์ ํ ํ์ผ
memory.min <= memory.low <= memory.high <= memory.max ์์๋ก ์ดํดํ๋ฉด ์ด์ ์ ์ฑ
์ ์ก๊ธฐ ์ฝ์ต๋๋ค. ๋ถ๋ชจ cgroup์ ์ค์ ํ๊ณ๊ฐ ์ต์ข
์ํ์ด๋ฏ๋ก, ์์ cgroup์ ์ค์ ํฉ๊ณ๊ฐ ๋ถ๋ชจ๋ณด๋ค ํฌ๊ฒ ์กํ ์๋ ์์ด๋ ์ค์ ์ฌ์ฉ๋์ ๋ถ๋ชจ์ ๊ณ์ ๊ณผ ์ ํ์ ํจ๊ป ํต๊ณผํด์ผ ํฉ๋๋ค.
| ํ์ผ | ๋ด๋ถ ํ๋ | ์๋ฏธ | ์ด๊ณผ ๋๋ ๋ถ์กฑ ์ ๋์ |
|---|---|---|---|
| `memory.min` | `memcg->memory.min` | ์ ๋ ๋ณดํธ๋ | ๋ณดํธ ๋ฒ์ ์๋์ ๋ฉ๋ชจ๋ฆฌ๋ ์์น์ ์ผ๋ก ํ์ํ์ง ์์ |
| `memory.low` | `memcg->memory.low` | ๋ ธ๋ ฅ ๊ธฐ๋ฐ ๋ณดํธ๋ | ์ ์ญ ์๋ฐ์ด ๊ฐํ๋ฉด ํ์๋ ์ ์์ง๋ง ์ฐ์ ์์๊ฐ ๋ฎ์ |
| `memory.high` | `memcg->memory.high` | throttle ๊ฒฝ๊ณ ์ | ์ด๊ณผ ์ ํ์ ์ ๋, ์ ์ ๋ชจ๋ ๋ณต๊ท ๊ฒฝ๋ก์์ ์ง์ฐ ์ ์ฉ |
| `memory.max` | `memcg->memory.max` | ํ๋ ์ ํ | ํ์ ์คํจ ์ memcg OOM ๊ฒฝ๋ก๋ก ์ง์ |
| `memory.current` | `memcg->memory.usage` | ํ์ฌ ์ฌ์ฉ๋ | `page_counter_read(&memcg->memory)` ๊ธฐ๋ฐ ์ถ๋ ฅ |
| `memory.events` | `memory_events[]` | low/high/max/OOM ์ด๋ฒคํธ | ์ด๋ฒคํธ ํ์ผ ์๋ฆผ๊ณผ ์ด์ ๋ชจ๋ํฐ๋ง์ ์ฌ์ฉ |
์ด์ ๊ณ์ธต ์์
| cgroup | ๋ํ ์ค์ | ํด์ |
|---|---|---|
| `system.slice` | `memory.max=4G`, `memory.high=3.5G`, `memory.low=1G` | ์์คํ ์๋น์ค๊ฐ ์ต์ 1G ๋ณดํธ๋ฅผ ๋ฐ๋ 3.5G๋ถํฐ ์๋ ์ ํ์ ๋ฐ์ |
| `app.slice` | `memory.max=8G`, `memory.high=7G`, `memory.swap.max=2G` | ์ ํ๋ฆฌ์ผ์ด์ RSS์ page cache๋ฅผ ์ ํํ๊ณ swap ์ฌ์ฉ๋์ ๋ณ๋ ๊ด๋ฆฌ |
| `guest.slice` | `memory.max=16G`, `memory.oom.group=1` | VM ๋๋ ์ปจํ ์ด๋ ๋ฌถ์์ ํ๋์ OOM ๋๋ฉ์ธ์ผ๋ก ์ทจ๊ธ |
memcg ํ์ ๊ฒฝ๋ก ๋น๊ต
| ๊ฒฝ๋ก | ํธ๋ฆฌ๊ฑฐ | ๋์ |
|---|---|---|
| ์ง์ ํ์ (direct reclaim) | `try_charge_memcg()`์์ ํ๊ณ ์ด๊ณผ | `try_to_free_mem_cgroup_pages()` ํธ์ถ |
| ์ ์ ๋ชจ๋ ๋ณต๊ท ์ throttle | `memory.high` ์ด๊ณผ, `done_restock`์์ ๊ฐ์ง | `__mem_cgroup_handle_over_high()` โ `schedule_timeout_killable()` |
| periodic flush | 2์ด๋ง๋ค deferred work | `flush_memcg_stats_dwork()` โ ํต๊ณ ๋๊ธฐํ |
| per-CPU stock drain | memcg ์คํ๋ผ์ธ ์ | `drain_all_stock()` โ ๋ชจ๋ CPU์ stock ๋น์ฐ๊ธฐ |
OOM ๋ฐ์ ์กฐ๊ฑด
| ์กฐ๊ฑด | memory.max ์ด๊ณผ ์ ๋์ |
|---|---|
| ์ผ๋ฐ ํ ๋น (`GFP_KERNEL`) | ํ์ ์๋ โ ์คํจ ์ memcg OOM killer ๋ฐ๋ |
| `__GFP_NOFAIL` ํ ๋น | ํ๊ณ๋ฅผ ์ผ์์ ์ผ๋ก ์ด๊ณผํ์ฌ force charge |
| `__GFP_HIGH` ํ ๋น | force charge ํ์ฉ (์ปค๋ ๋ด๋ถ ๊ธด๊ธ ํ ๋น) |
| `PF_MEMALLOC` ํ๋๊ทธ | ์ฆ์ force charge (์์คํ ์ ์ฒด ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ์ค) |
| `oom_kill_disable=1` (v1) | OOM killer ๋นํ์ฑํ, ํ์๋ง ์๋ |
์ค์ผ์ค๋ฌ ์ฐ๋: high throttle ์ง์ฐ ๊ณ์ฐ
penalty_jiffies = calculate_high_delay(memcg, nr_pages, max_overage)
+ calculate_high_delay(memcg, nr_pages, swap_max_overage)
| ์ด๊ณผ ๋น์จ | ์์ ์ง์ฐ | ํจ๊ณผ |
|---|---|---|
| ์ฝ๊ฐ ์ด๊ณผ (<= HZ/100) | 0 (์คํต) | ํ๋ก์ธ์ค์ ์ํฅ ์์ |
| ์ค๊ฐ ์ด๊ณผ | ์๋ฐฑ ms | ์ ์ง์ ์๋ ์ ํ |
| ํฌ๊ฒ ์ด๊ณผ | ์ต๋ `MEMCG_MAX_HIGH_DELAY_JIFFIES` | ๊ฐํ throttle |
memcg ํต๊ณ flush ๋ฉ์ปค๋์ฆ
์ปค๋์ memcg ํต๊ณ๋ฅผ ํจ์จ์ ์ผ๋ก ๋๊ธฐํํ๊ธฐ ์ํด 2-tier flush ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํฉ๋๋ค:
1. Per-CPU ๋ก์ปฌ ์
๋ฐ์ดํธ: memcg_vmstats_percpu์ ์ฆ์ ๊ธฐ๋ก (lockless)
2. Periodic flush: 2์ด๋ง๋ค flush_memcg_stats_dwork()๊ฐ aggregate๋ก ๋ณํฉ
3. Reader-side flush: MEMCG_CHARGE_BATCH * nr_cpus ์ด์ ์
๋ฐ์ดํธ ์ ๋๊ธฐ flush
Per-CPU state ์
๋ฐ์ดํธ
โโ memcg_rstat_updated()
โโ stats_updates >= MEMCG_CHARGE_BATCH โ vmstats->stats_updates์ atomic_add
Reader-side flush
โโ memcg_vmstats_needs_flush(vmstats) โ true ์ css_rstat_flush()