[커널 18차] 50주차
2022.05.10 18:18
git : https://github.com/iamroot18/5.10/commit/80afe12f1c731c4d2cfd490339b3487f7dd01e76
diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index b9e8696ea1c1..aad28e05df69 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -391,6 +391,10 @@ static inline int bitmap_intersects(const unsigned long *src1,
return __bitmap_intersects(src1, src2, nbits);
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - @src1이 @src2에 모두 포함되어있는지 판별한다.
+ */
static inline int bitmap_subset(const unsigned long *src1,
const unsigned long *src2, unsigned int nbits)
{
diff --git a/include/linux/mm.h b/include/linux/mm.h
index a099b72852eb..7f6d32cc6893 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2056,6 +2056,10 @@ static inline bool get_user_page_fast_only(unsigned long addr,
/*
* per-process(per-mm_struct) statistics.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - @member에 대한 실제 사용 phy memory를 구한다.
+ */
static inline unsigned long get_mm_counter(struct mm_struct *mm, int member)
{
long val = atomic_long_read(&mm->rss_stat.count[member]);
@@ -2109,6 +2113,10 @@ static inline int mm_counter(struct page *page)
return mm_counter_file(page);
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - 실제 할당되어 사용되는 page 수를 구한다.
+ */
static inline unsigned long get_mm_rss(struct mm_struct *mm)
{
return get_mm_counter(mm, MM_FILEPAGES) +
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index f6012515cde9..c79e9cbbd00b 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -539,6 +539,10 @@ struct mm_struct {
* @mm_count (which may then free the &struct mm_struct if
* @mm_count also drops to 0).
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - mm을 사용하고있는 process나 thread 수.
+ */
atomic_t mm_users;
/**
diff --git a/include/linux/sched/mm.h b/include/linux/sched/mm.h
index 96e70c5e7632..0823f393bdab 100644
--- a/include/linux/sched/mm.h
+++ b/include/linux/sched/mm.h
@@ -31,6 +31,10 @@ extern struct mm_struct *mm_alloc(void);
* See also <Documentation/vm/active_mm.rst> for an in-depth explanation
* of &mm_struct.mm_count vs &mm_struct.mm_users.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - @mm에 대한 refcnt(pin)를 증가시킨다.
+ */
static inline void mmgrab(struct mm_struct *mm)
{
atomic_inc(&mm->mm_count);
diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h
index e5f4ce622ee6..b21041b8a292 100644
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -218,6 +218,13 @@ struct signal_struct {
* oom
*/
bool oom_flag_origin;
+/*
+ * IAMROOT, 2022.05.07:
+ * - lagacy 방식으로 -17 ~ 15의 제어로 /proc/${pid}/oom_adj 으로 사용했으며
+ * 현재는 -1000 ~ 1000까지의 숫자로 /proc/${pid}/oom_score_adj으로 사용한다.
+ * (oom_adj_write(), oom_socre_adj_write() 참고)
+ * lagacy방식으로 사용할경우 현재 score로 변환하여 고쳐주긴한다.
+ */
short oom_score_adj; /* OOM kill score adjustment */
short oom_score_adj_min; /* OOM kill score adjustment min value.
* Only settable by CAP_SYS_RESOURCE. */
@@ -600,6 +607,10 @@ extern void flush_itimer_signals(void);
#define next_task(p) \
list_entry_rcu((p)->tasks.next, struct task_struct, tasks)
+/*
+ * IAMROOT, 2022.05.07:
+ * - init_task부터 모든 process를 iterate한다.
+ */
#define for_each_process(p) \
for (p = &init_task ; (p = next_task(p)) != &init_task ; )
@@ -615,13 +626,25 @@ extern bool current_is_single_threaded(void);
#define while_each_thread(g, t) \
while ((t = next_thread(t)) != g)
+/*
+ * IAMROOT, 2022.05.07:
+ * - signal을 공유하고 있는 list iteration.
+ */
#define __for_each_thread(signal, t) \
list_for_each_entry_rcu(t, &(signal)->thread_head, thread_node)
+/*
+ * IAMROOT, 2022.05.07:
+ * - p(process)에 대한 thread들을 iterate한다.
+ */
#define for_each_thread(p, t) \
__for_each_thread((p)->signal, t)
/* Careful: this is a double loop, 'break' won't work as expected. */
+/*
+ * IAMROOT, 2022.05.07:
+ * - 모든 process의 모든 thread를 순회한다.
+ */
#define for_each_process_thread(p, t) \
for_each_process(p) for_each_thread(p, t)
diff --git a/include/uapi/linux/mempolicy.h b/include/uapi/linux/mempolicy.h
index 046d0ccba4cd..4000010a9833 100644
--- a/include/uapi/linux/mempolicy.h
+++ b/include/uapi/linux/mempolicy.h
@@ -18,9 +18,25 @@
/* Policies */
enum {
MPOL_DEFAULT,
+/*
+ * IAMROOT, 2022.05.07:
+ * - 선호 node(반드시는 아님)
+ */
MPOL_PREFERRED,
+/*
+ * IAMROOT, 2022.05.07:
+ * - 특정 node에 memory를 할당할수있는.
+ */
MPOL_BIND,
+/*
+ * IAMROOT, 2022.05.07:
+ * - 2개 이상의 node에서 교대로 할당.
+ */
MPOL_INTERLEAVE,
+/*
+ * IAMROOT, 2022.05.07:
+ * - cpu가 있는 node에서만 할당.
+ */
MPOL_LOCAL,
MPOL_PREFERRED_MANY,
MPOL_MAX, /* always last member of enum */
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index 0255be5011e3..8a1eb08fee3c 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -3643,6 +3643,10 @@ EXPORT_SYMBOL_GPL(cpuset_mem_spread_node);
* to the other.
**/
+/*
+ * IAMROOT, 2022.05.07:
+ * - @tas1의 node가 @task2의 node에 속해있는지 확인한다.
+ */
int cpuset_mems_allowed_intersects(const struct task_struct *tsk1,
const struct task_struct *tsk2)
{
diff --git a/kernel/freezer.c b/kernel/freezer.c
index 45ab36ffd0e7..fd7227c0ff31 100644
--- a/kernel/freezer.c
+++ b/kernel/freezer.c
@@ -143,6 +143,10 @@ bool freeze_task(struct task_struct *p)
return true;
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - @p가 frozen 상태이면 wakeup.
+ */
void __thaw_task(struct task_struct *p)
{
unsigned long flags;
diff --git a/mm/internal.h b/mm/internal.h
index b61df0de0888..41a80aa78fdb 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -205,6 +205,24 @@ struct alloc_context {
*
* Assumption: *_mem_map is contiguous at least up to MAX_ORDER
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - buddy page를 찾는다.
+ * ex) order 0에 대한 pair page
+ * 3 -> 2
+ * 2 -> 3
+ * 1 -> 0
+ * 0 -> 1
+ * ex) order 1에 대한 pair page
+ * 7 -> X
+ * 6 -> 4
+ * 5 -> X
+ * 4 -> 6
+ * 3 -> X
+ * 2 -> 0
+ * 1 -> X
+ * 0 -> 2
+ */
static inline unsigned long
__find_buddy_pfn(unsigned long page_pfn, unsigned int order)
{
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 256e71297f39..72951f568571 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -1284,6 +1284,10 @@ static void invalidate_reclaim_iterators(struct mem_cgroup *dead_memcg)
*
* This function must not be called for the root memory cgroup.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - @memcg를 root로 tree구조로 iterate하며 @fn을 실행한다.
+ */
int mem_cgroup_scan_tasks(struct mem_cgroup *memcg,
int (*fn)(struct task_struct *, void *), void *arg)
{
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 9f648e5b91fc..d177e6063070 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -2012,6 +2012,10 @@ bool init_nodemask_of_mempolicy(nodemask_t *mask)
*
* Takes task_lock(tsk) to prevent freeing of its mempolicy.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - @task가 MPOL_BIND 일때 @mask node에 속해있는지의 여부를 확인한다.
+ */
bool mempolicy_in_oom_domain(struct task_struct *tsk,
const nodemask_t *mask)
{
@@ -2022,6 +2026,12 @@ bool mempolicy_in_oom_domain(struct task_struct *tsk,
return ret;
task_lock(tsk);
+
+/*
+ * IAMROOT, 2022.05.07:
+ * - task의 mempolicy를 가져온다. 해당 task가 bind policy라면 mempolicy에 속한 node가
+ * mask에 포함이 되있는지를 확인한다.
+ */
mempolicy = tsk->mempolicy;
if (mempolicy && mempolicy->mode == MPOL_BIND)
ret = nodes_intersects(mempolicy->nodes, *mask);
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index 989f35a2bbb1..7ec453aee4b2 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -52,6 +52,30 @@
#define CREATE_TRACE_POINTS
#include <trace/events/oom.h>
+/*
+ * IAMROOT, 2022.05.07:
+ * --- sysctl_panic_on_oom
+ *
+ * - /proc/sys/vm/panic_on_oom
+ * - Documentation 참고
+ * 0으로 설정하면 커널은 oom_killer라는 일부 악성 프로세스를 종료합니다.
+ * 일반적으로 oom_killer는 악성 프로세스를 제거할 수 있으며 시스템은
+ * 존속합니다.
+ *
+ * 1로 설정하면 메모리 부족이 발생할 때 커널이 패닉에 빠집니다. 그러나
+ * 프로세스가 mempolicy/cpusets에 의해 노드 사용을 제한하고 이러한 노드가
+ * 메모리 소진 상태가 되면 하나의 프로세스가 oom-killer에 의해 중단될 수 있다.
+ * 이 경우 패닉은 발생하지 않습니다. 다른 노드의 메모리가 비어 있을 수 있기
+ * 때문입니다. 이는 시스템 전체 상태가 아직 치명적이지 않을 수 있음을
+ * 의미합니다.
+ *
+ * 만약 이것이 2로 설정된다면, 위에서 언급한 것에도 커널은 강제적으로 패닉을
+ * 일으킨다. memcg 아래서 oom도 발생하며, 전체 시스템이 공황상태에 빠진다.
+ *
+ * --- sysctl_oom_kill_allocating_task
+ * oom이 일어날때 사용자가 oom kill을 할 task를 정할때 사용한다. 설정이 되면
+ * oom score를 할 필요없이 설정된 task로 oom kill을 한다.
+ */
int sysctl_panic_on_oom;
int sysctl_oom_kill_allocating_task;
int sysctl_oom_dump_tasks = 1;
@@ -68,6 +92,10 @@ DEFINE_MUTEX(oom_lock);
/* Serializes oom_score_adj and oom_score_adj_min updates */
DEFINE_MUTEX(oom_adj_mutex);
+/*
+ * IAMROOT, 2022.05.07:
+ * - oom controller에 memcg가 지정되있는지 확인한다.
+ */
static inline bool is_memcg_oom(struct oom_control *oc)
{
return oc->memcg != NULL;
@@ -86,6 +114,13 @@ static inline bool is_memcg_oom(struct oom_control *oc)
* This function is assuming oom-killer context and 'current' has triggered
* the oom-killer.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - @start process의 thread들을 iterate하며 oc->nodemask에 따라 같은 node영역을 가진
+ * thread를 찾는다.
+ * - 같은 node영역을 찾는다는 것은 현재 memory가 모자르는 node를 사용하는 다른 thread
+ * 를 찾아 해당 thread를 죽이면 node에 free memory가 생기므로 node를 찾는것이다.
+ */
static bool oom_cpuset_eligible(struct task_struct *start,
struct oom_control *oc)
{
@@ -98,6 +133,10 @@ static bool oom_cpuset_eligible(struct task_struct *start,
rcu_read_lock();
for_each_thread(start, tsk) {
+/*
+ * IAMROOT, 2022.05.07:
+ * - mask != null이면 mempolicy로 처리한다.
+ */
if (mask) {
/*
* If this is a mempolicy constrained oom, tsk's
@@ -111,8 +150,16 @@ static bool oom_cpuset_eligible(struct task_struct *start,
* This is not a mempolicy constrained oom, so only
* check the mems of tsk's cpuset.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - current의 allow node가 task allow node에 포함되있는지에 대한 여부를 확인한다.
+ */
ret = cpuset_mems_allowed_intersects(current, tsk);
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - node 영역이 겹치는 thread를 찾는순간 return true.
+ */
if (ret)
break;
}
@@ -156,6 +203,17 @@ struct task_struct *find_lock_task_mm(struct task_struct *p)
* order == -1 means the oom kill is required by sysrq, otherwise only
* for display purposes.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - sysrq요청인지(강제로 oom상태 확인. 사용자 요청) 확인한다.
+ * --- /proc/sys/kernel/sysrq
+ * - help(sudo echo "g" > /proc/sysrq-trigger)
+ * sysrq: HELP : loglevel(0-9) reboot(b) crash(c) terminate-all-tasks(e)
+ * memory-full-oom-kill(f) kill-all-tasks(i) thaw-filesystems(j) sak(k)
+ * show-backtrace-all-active-cpus(l) show-memory-usage(m) nice-all-RT-tasks(n)
+ * poweroff(o) show-registers(p) show-all-timers(q) unraw(r) sync(s)
+ * show-task-states(t) unmount(u) show-blocked-tasks(w) dump-ftrace-buffer(z)
+ */
static inline bool is_sysrq_oom(struct oom_control *oc)
{
return oc->order == -1;
@@ -201,6 +259,17 @@ static bool should_dump_unreclaim_slab(void)
* predictable as possible. The goal is to return the highest value for the
* task consuming the most memory to avoid subsequent oom failures.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - @p에 대해서 oom_score_adj 및 memory 사용등을 적용해 point를 계산한다.
+ *
+ * ---
+ * /proc/${pid}/oom_score 를 통해서 점수를 볼수있지만 proc_oom_score()함수를
+ * 참고하면 실제 user에서 볼때는 0 ~ 2000 값으로 변환을 하여 보여줄려고한다.
+ *
+ * ---
+ * proc_oom_score, proc_oom_adj_operations, proc_oom_score_adj_operations 참고
+ */
long oom_badness(struct task_struct *p, unsigned long totalpages)
{
long points;
@@ -218,6 +287,11 @@ long oom_badness(struct task_struct *p, unsigned long totalpages)
* unkillable or have been already oom reaped or the are in
* the middle of vfork
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - signal->oom_score_adj
+ * 각 thread들이 공유하고 있는 oom_score_adj.
+ */
adj = (long)p->signal->oom_score_adj;
if (adj == OOM_SCORE_ADJ_MIN ||
test_bit(MMF_OOM_SKIP, &p->mm->flags) ||
@@ -230,11 +304,26 @@ long oom_badness(struct task_struct *p, unsigned long totalpages)
* The baseline for the badness score is the proportion of RAM that each
* task's rss, pagetable and swap space use.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - 실제 사용 memory + swap한 memory + pgtable 사용 memory
+ */
points = get_mm_rss(p->mm) + get_mm_counter(p->mm, MM_SWAPENTS) +
mm_pgtables_bytes(p->mm) / PAGE_SIZE;
task_unlock(p);
/* Normalize to oom_score_adj units */
+/*
+ * IAMROOT, 2022.05.07:
+ * - adj * (totalpages / 1000) + point 값을 return한다.
+ * ex) points = 100, adj = 10, totalpages = 200000
+ * return = 10 * (200000 / 1000) + 100
+ * = 2100
+ *
+ * ex) points = 100, adj = 500, totalpages = 200000
+ * return = 500 * (200000 / 1000) + 100
+ * = 100100
+ */
adj *= totalpages / 1000;
points += adj;
@@ -251,6 +340,10 @@ static const char * const oom_constraint_text[] = {
/*
* Determine the type of allocation constraint.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - @oc에 따라 oom에 어떻게 동작할지를 정한다.
+ */
static enum oom_constraint constrained_alloc(struct oom_control *oc)
{
struct zone *zone;
@@ -285,6 +378,15 @@ static enum oom_constraint constrained_alloc(struct oom_control *oc)
* the page allocator means a mempolicy is in effect. Cpuset policy
* is enforced in get_page_from_freelist().
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - papgo
+ * 이것은 __GFP_THISNODE 할당이 아니므로 페이지 할당기에서 잘린 노드 마스크는
+ * mem 정책이 적용됨을 의미합니다. CPUet 정책은 get_page_from_freeelist()에
+ * 적용됩니다.
+ * - @oc->nodemask에 memory node가 전부 포함이 안되있으면 memory policy를 따른다.
+ * (numactl 설정에 따른다.)
+ */
if (oc->nodemask &&
!nodes_subset(node_states[N_MEMORY], *oc->nodemask)) {
oc->totalpages = total_swap_pages;
@@ -294,11 +396,24 @@ static enum oom_constraint constrained_alloc(struct oom_control *oc)
}
/* Check this allocation failure is caused by cpuset's wall function */
+/*
+ * IAMROOT, 2022.05.07:
+ * - zonelist를 iterate 하면서 해당 zone의 node가 memcg에서 허용한 node인지 확인한다.
+ * 허락을 안했다면 cpuset_limited가 true가된다.
+ * - memory node에 oc->nodemask가 전부 포함이 되었지만 일부 memcg에는 허락이
+ * 안됭상태가 발견되면 cpuset으로 적용하도록 한다.
+ */
for_each_zone_zonelist_nodemask(zone, z, oc->zonelist,
highest_zoneidx, oc->nodemask)
if (!cpuset_zone_allowed(zone, oc->gfp_mask))
cpuset_limited = true;
+/*
+ * IAMROOT, 2022.05.07:
+ * - 현재 task에 허락된 node에 대해서 totalpage를 구하고 CONSTRAINT_CPUSET으로
+ * return.
+ * - memcg에 허용되지 않은 node가 발견되엇으니 cpuset으로 적용하라는 의미이다.
+ */
if (cpuset_limited) {
oc->totalpages = total_swap_pages;
for_each_node_mask(nid, cpuset_current_mems_allowed)
@@ -308,11 +423,20 @@ static enum oom_constraint constrained_alloc(struct oom_control *oc)
return CONSTRAINT_NONE;
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - @task를 oom을 위해 평가한다.
+ * - select된 task는 oc->chosen에 갱신되고 해당 점수가 oc->chosen_points에 갱신된다.
+ */
static int oom_evaluate_task(struct task_struct *task, void *arg)
{
struct oom_control *oc = arg;
long points;
+/*
+ * IAMROOT, 2022.05.07:
+ * - 못죽이거나 memory 영향이 없는 경우엔 next로 처리한다.
+ */
if (oom_unkillable_task(task))
goto next;
@@ -326,6 +450,13 @@ static int oom_evaluate_task(struct task_struct *task, void *arg)
* the task has MMF_OOM_SKIP because chances that it would release
* any memory is quite low.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - papago
+ * 이 작업은 이미 메모리 예약에 액세스할 수 있으므로 중지되고 있습니다.
+ * 작업에 MMF_OOM_SKIP가 없는 한 다른 작업이 예약에 액세스할 수 있도록
+ * 허용하지 마십시오. 메모리를 방출할 가능성이 매우 낮기 때문입니다.
+ */
if (!is_sysrq_oom(oc) && tsk_is_oom_victim(task)) {
if (test_bit(MMF_OOM_SKIP, &task->signal->oom_mm->flags))
goto next;
@@ -336,12 +467,20 @@ static int oom_evaluate_task(struct task_struct *task, void *arg)
* If task is allocating a lot of memory and has been marked to be
* killed first if it triggers an oom, then select it.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - origin flag가 set되있으면 많은 memory를 사용중으로 예상하며, 첫번째 타겟으로한다.
+ */
if (oom_task_origin(task)) {
points = LONG_MAX;
goto select;
}
points = oom_badness(task, oc->totalpages);
+/*
+ * IAMROOT, 2022.05.07:
+ * - 이전에 저장된 최대 값과 비교한다. LONG_MIN이면 아에 선택안한다.
+ */
if (points == LONG_MIN || points < oc->chosen_points)
goto next;
@@ -364,10 +503,19 @@ static int oom_evaluate_task(struct task_struct *task, void *arg)
* Simple selection loop. We choose the process with the highest number of
* 'points'. In case scan was aborted, oc->chosen is set to -1.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - oom point가 가장 높은 process를 찾아서 @oc에 기록한다.
+ */
static void select_bad_process(struct oom_control *oc)
{
oc->chosen_points = LONG_MIN;
+/*
+ * IAMROOT, 2022.05.07:
+ * - memcg에서의 oom이면 memcg에서, 아니면 전체 process에서 oom_evaluate_task를
+ * 수행한다.
+ */
if (is_memcg_oom(oc))
mem_cgroup_scan_tasks(oc->memcg, oom_evaluate_task, oc);
else {
@@ -659,6 +807,10 @@ static int oom_reaper(void *unused)
return 0;
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - @task를 oom_reaper에 등록시키고 깨운다.
+ */
static void wake_oom_reaper(struct task_struct *tsk)
{
/* mm is already queued? */
@@ -697,16 +849,29 @@ static inline void wake_oom_reaper(struct task_struct *tsk)
* tsk->mm has to be non NULL and caller has to guarantee it is stable (either
* under task_lock or operate on the current).
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - @task의 flag 및 oom_mm을 설정하고. frozen일경우 wakeup시킨다.
+ */
static void mark_oom_victim(struct task_struct *tsk)
{
struct mm_struct *mm = tsk->mm;
WARN_ON(oom_killer_disabled);
+
+/*
+ * IAMROOT, 2022.05.07:
+ * - 이미 TIF_MEMDIE가 설정되있으면 return.
+ */
/* OOM killer might race with memcg OOM */
if (test_and_set_tsk_thread_flag(tsk, TIF_MEMDIE))
return;
/* oom_mm is bound to the signal struct life time. */
+/*
+ * IAMROOT, 2022.05.07:
+ * - oom_mm == NULL이면 mm으로 교체.
+ */
if (!cmpxchg(&tsk->signal->oom_mm, NULL, mm)) {
mmgrab(tsk->signal->oom_mm);
set_bit(MMF_OOM_VICTIM, &mm->flags);
@@ -782,6 +947,11 @@ bool oom_killer_disable(signed long timeout)
return true;
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - task가 free memory를 반환할수있는 상태인지 확인한다.
+ * signal group이 죽고 있거나 task가 죽고있는지 확인한다.
+ */
static inline bool __task_will_free_mem(struct task_struct *task)
{
struct signal_struct *sig = task->signal;
@@ -791,12 +961,24 @@ static inline bool __task_will_free_mem(struct task_struct *task)
* so the oom killer cannot assume that the process will promptly exit
* and release memory.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - papgo
+ * 코어 덤핑 프로세스는 exit_mm()에서 장기간 sleep할 수 있으므로, oom 킬러는
+ * 프로세스가 즉시 종료되고 메모리를 방출한다고 가정할 수 없다.
+ * - memory가 늦게 반환되니 false를 반환한다.
+ */
if (sig->flags & SIGNAL_GROUP_COREDUMP)
return false;
if (sig->flags & SIGNAL_GROUP_EXIT)
return true;
+/*
+ * IAMROOT, 2022.05.07:
+ * - thread group에 포함이 되지 않았고, 해당 task가 종료중이면 곧 종료중이다.
+ * return true.
+ */
if (thread_group_empty(task) && (task->flags & PF_EXITING))
return true;
@@ -810,6 +992,10 @@ static inline bool __task_will_free_mem(struct task_struct *task)
* Caller has to make sure that task->mm is stable (hold task_lock or
* it operates on the current).
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - @task가 죽어가면서 free memrory를 반환할수있는 상태인지 확인한다.
+ */
static bool task_will_free_mem(struct task_struct *task)
{
struct mm_struct *mm = task->mm;
@@ -821,9 +1007,18 @@ static bool task_will_free_mem(struct task_struct *task)
* exit_oom_victim. oom_reaper could have rescued that but do not rely
* on that for now. We can consider find_lock_task_mm in future.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - exit_mm이나 exit_oom_victim으로 task가 죽고 있는중인지 확인한다.
+ */
if (!mm)
return false;
+/*
+ * IAMROOT, 2022.05.07:
+ * - task가 종료되고있는데, memory를 반환할수있는 상태인지 확인한다.
+ * 종료되는 task임에도 불구하고 memory를 확보할수 없으면 return false.
+ */
if (!__task_will_free_mem(task))
return false;
@@ -831,6 +1026,10 @@ static bool task_will_free_mem(struct task_struct *task)
* This task has already been drained by the oom reaper so there are
* only small chances it will free some more
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - mm이 이미 oom중이니 return false.
+ */
if (test_bit(MMF_OOM_SKIP, &mm->flags))
return false;
@@ -843,6 +1042,12 @@ static bool task_will_free_mem(struct task_struct *task)
* b) the task is also reapable by the oom reaper.
*/
rcu_read_lock();
+/*
+ * IAMROOT, 2022.05.07:
+ * - 모든 process를 iterate한다.
+ * mm을 share 하고, 같은 thread group이 아닌 process를 찾아서
+ * __task_will_free_mem()으로 확인한다.
+ */
for_each_process(p) {
if (!process_shares_mm(p, mm))
continue;
@@ -890,6 +1095,13 @@ static void __oom_kill_process(struct task_struct *victim, const char *message)
*/
do_send_sig_info(SIGKILL, SEND_SIG_PRIV, victim, PIDTYPE_TGID);
mark_oom_victim(victim);
+/*
+ * IAMROOT, 2022.05.07:
+ * - ex)
+ * Out of memory: Killed process 2446 (gnome-shell) total-vm:5424564kB,
+ * anon-rss:255676kB, file-rss:100240kB, shmem-rss:38948kB, UID:1000
+ * pgtables:1496kB oom_score_adj:0
+ */
pr_err("%s: Killed process %d (%s) total-vm:%lukB, anon-rss:%lukB, file-rss:%lukB, shmem-rss:%lukB, UID:%u pgtables:%lukB oom_score_adj:%hd\n",
message, task_pid_nr(victim), victim->comm, K(mm->total_vm),
K(get_mm_counter(mm, MM_ANONPAGES)),
@@ -1002,8 +1214,17 @@ static void oom_kill_process(struct oom_control *oc, const char *message)
/*
* Determines whether the kernel must panic because of the panic_on_oom sysctl.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - sysctl_panic_on_oom과 oc->constraint에 따라 panic을 일으킨다.
+ */
static void check_panic_on_oom(struct oom_control *oc)
{
+/*
+ * IAMROOT, 2022.05.07:
+ * - sysctl_panic_on_oom == 0
+ * oom만 동작시킨다.
+ */
if (likely(!sysctl_panic_on_oom))
return;
if (sysctl_panic_on_oom != 2) {
@@ -1012,9 +1233,20 @@ static void check_panic_on_oom(struct oom_control *oc)
* does not panic for cpuset, mempolicy, or memcg allocation
* failures.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - sysctl_panic_on_oom == 1이면 mempolicy/cpusets, memcg인 상황에서는 oom에만
+ * 동작시킨다.
+ */
if (oc->constraint != CONSTRAINT_NONE)
return;
}
+
+/*
+ * IAMROOT, 2022.05.07:
+ * - sysctl_panic_on_oom == 2이거나 CONSTRAINT_NONE인 경우 무조건 panic을 일으킨다.
+ * - 사용자 요청이면 panic을 안한다.
+ */
/* Do not panic for oom kills triggered by sysrq */
if (is_sysrq_oom(oc))
return;
@@ -1054,6 +1286,11 @@ bool out_of_memory(struct oom_control *oc)
return false;
if (!is_memcg_oom(oc)) {
+/*
+ * IAMROOT, 2022.05.07:
+ * - oom_notify_list에 등록되있는 함수들을 호출한다. 해당 함수에서 free된게
+ * 있다면 return true.
+ */
blocking_notifier_call_chain(&oom_notify_list, 0, &freed);
if (freed > 0)
/* Got some memory back in the last second. */
@@ -1065,6 +1302,11 @@ bool out_of_memory(struct oom_control *oc)
* select it. The goal is to allow it to allocate so that it may
* quickly exit and free its memory.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - current가 종료중이고 memory를 반환할수있는 상태라면 oom관련 설정, frozen상태일
+ * 경우 wakepup, oom_reaper에 등록 및 oom_reaper wakepup등을 수행하고 return true.
+ */
if (task_will_free_mem(current)) {
mark_oom_victim(current);
wake_oom_reaper(current);
@@ -1078,6 +1320,13 @@ bool out_of_memory(struct oom_control *oc)
* ___GFP_DIRECT_RECLAIM to get here. But mem_cgroup_oom() has to
* invoke the OOM killer even if it is a GFP_NOFS allocation.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - OOM 킬러는 IO가 없는 회수를 보상하지 않습니다.
+ * pagefault_out_of_memory가 gfp 컨텍스트를 손실했기 때문에 0 마스크를 제외해야
+ * 합니다. 다른 모든 사용자는 __GFP_DIRECT_RECLAIM을 가지고 있어야 합니다.
+ * 그러나 mem_cgroup_oom()은 GFP_NOFS 할당인 경우에도 OOM 킬러를 호출해야 합니다.
+ */
if (oc->gfp_mask && !(oc->gfp_mask & __GFP_FS) && !is_memcg_oom(oc))
return true;
@@ -1090,10 +1339,21 @@ bool out_of_memory(struct oom_control *oc)
oc->nodemask = NULL;
check_panic_on_oom(oc);
+/*
+ * IAMROOT, 2022.05.07:
+ * - check_panic_on_oom()에서 panic이 안일어났다면 oom을 준비한다.
+ * - sysctl_oom_kill_allocating_task이 설정되있고, 아직 종료가 안됬으며, 죽일수가없는
+ * task이며
+ */
+
if (!is_memcg_oom(oc) && sysctl_oom_kill_allocating_task &&
current->mm && !oom_unkillable_task(current) &&
oom_cpuset_eligible(current, oc) &&
current->signal->oom_score_adj != OOM_SCORE_ADJ_MIN) {
+/*
+ * IAMROOT, 2022.05.07:
+ * - oom_score_adj가 OOM_SCORE_ADJ_MIN이 아닌경우 현재 task를 oom한다.
+ */
get_task_struct(current);
oc->chosen = current;
oom_kill_process(oc, "Out of memory (oom_kill_allocating_task)");
@@ -1103,6 +1363,10 @@ bool out_of_memory(struct oom_control *oc)
select_bad_process(oc);
/* Found nothing?!?! */
if (!oc->chosen) {
+/*
+ * IAMROOT, 2022.05.07:
+ * - oom 대상을 못찾았다. deadlock
+ */
dump_header(oc, NULL);
pr_warn("Out of memory and no killable processes...\n");
/*
@@ -1113,6 +1377,11 @@ bool out_of_memory(struct oom_control *oc)
if (!is_sysrq_oom(oc) && !is_memcg_oom(oc))
panic("System is deadlocked on memory\n");
}
+
+/*
+ * IAMROOT, 2022.05.07:
+ * - chosen != -1UL. abort가 아니라는뜻. 즉 oom 대상 process가 찾아졌다.
+ */
if (oc->chosen && oc->chosen != (void *)-1UL)
oom_kill_process(oc, !is_memcg_oom(oc) ? "Out of memory" :
"Memory cgroup out of memory");
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 62315b3b0b88..639f3388bf23 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -279,6 +279,11 @@ void pm_restrict_gfp_mask(void)
gfp_allowed_mask &= ~(__GFP_IO | __GFP_FS);
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - 절전모드면 gfp_allowed_mask에서 __GFP_IO, __GFP_FS가 제한된다.
+ * 즉 reclaim에 대해서 disk 이용 가능 여부를 확인한다.
+ */
bool pm_suspended_storage(void)
{
if ((gfp_allowed_mask & (__GFP_IO | __GFP_FS)) == (__GFP_IO | __GFP_FS))
@@ -850,6 +855,10 @@ static inline unsigned int order_to_pindex(int migratetype, int order)
return (MIGRATE_PCPTYPES * base) + migratetype;
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - pindex = order * MIGRATE_PCPTYPES + migratetype
+ */
static inline int pindex_to_order(unsigned int pindex)
{
int order = pindex / MIGRATE_PCPTYPES;
@@ -1461,6 +1470,10 @@ static const char *page_bad_reason(struct page *page, unsigned long flags)
return bad_reason;
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - bad page인지 확인.
+ */
static void check_free_page_bad(struct page *page)
{
bad_page(page,
@@ -1685,12 +1698,20 @@ static bool free_pcp_prepare(struct page *page, unsigned int order)
return free_pages_prepare(page, order, false, FPI_NONE);
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - bad page인지 확인.
+ */
static bool bulkfree_pcp_prepare(struct page *page)
{
return check_free_page(page);
}
#endif /* CONFIG_DEBUG_VM */
+/*
+ * IAMROOT, 2022.05.07:
+ * - @page의 buddy page를 prefetch한다.
+ */
static inline void prefetch_buddy(struct page *page)
{
unsigned long pfn = page_to_pfn(page);
@@ -1711,6 +1732,10 @@ static inline void prefetch_buddy(struct page *page)
* And clear the zone's pages_scanned counter, to hold off the "all pages are
* pinned" detection logic.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - @zone에 따라 pcp가 있는 cpu들의 pcp를 @count만큼 buddy로 보낸다.
+ */
static void free_pcppages_bulk(struct zone *zone, int count,
struct per_cpu_pages *pcp)
{
@@ -1738,6 +1763,10 @@ static void free_pcppages_bulk(struct zone *zone, int count,
* off fuller lists instead of spinning excessively around empty
* lists
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - 비어있지 않은 pcp list를 찾는다.
+ */
do {
batch_free++;
if (++pindex == NR_PCP_LISTS)
@@ -1758,6 +1787,10 @@ static void free_pcppages_bulk(struct zone *zone, int count,
nr_freed += 1 << order;
count -= 1 << order;
+/*
+ * IAMROOT, 2022.05.07:
+ * - bad page인지 확인.
+ */
if (bulkfree_pcp_prepare(page))
continue;
@@ -1765,6 +1798,10 @@ static void free_pcppages_bulk(struct zone *zone, int count,
page->index <<= NR_PCP_ORDER_WIDTH;
page->index |= order;
+/*
+ * IAMROOT, 2022.05.07:
+ * - pcp에서 빼서 index를 encode후 local list에 넣어놓는다.
+ */
list_add_tail(&page->lru, &head);
/*
@@ -1776,6 +1813,10 @@ static void free_pcppages_bulk(struct zone *zone, int count,
* avoid excessive prefetching due to large count, only
* prefetch buddy for the first pcp->batch nr of pages.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - buddy를 prefetch_nr만큼 prefetch한다.
+ */
if (prefetch_nr) {
prefetch_buddy(page);
prefetch_nr--;
@@ -1795,6 +1836,10 @@ static void free_pcppages_bulk(struct zone *zone, int count,
* Use safe version since after __free_one_page(),
* page->lru.next will not point to original list.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - local list에 모아놓은 page를 buddy로 돌려보낸다.
+ */
list_for_each_entry_safe(page, tmp, &head, lru) {
int mt = get_pcppage_migratetype(page);
@@ -3516,6 +3561,11 @@ static void reserve_highatomic_pageblock(struct page *page, struct zone *zone,
* If @force is true, try to unreserve a pageblock even though highatomic
* pageblock is exhausted.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - force == true일때는 1block이상, 아니면 2block이상 highatomic을 가진 zone
+ * 을 찾아 존재하면 @ac type으로 변경후 가져온다.
+ */
static bool unreserve_highatomic_pageblock(const struct alloc_context *ac,
bool force)
{
@@ -3533,6 +3583,10 @@ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac,
* Preserve at least one pageblock unless memory pressure
* is really high.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - 1block 초과로 reseve된 highatomic을 찾는다.
+ */
if (!force && zone->nr_reserved_highatomic <=
pageblock_nr_pages)
continue;
@@ -3552,6 +3606,10 @@ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac,
* from highatomic to ac->migratetype. So we should
* adjust the count once.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - fallback으로 가져올수도있어서 가져와고서도 한번 확인해본다.
+ */
if (is_migrate_highatomic_page(page)) {
/*
* It should never happen but changes to
@@ -3574,6 +3632,10 @@ static bool unreserve_highatomic_pageblock(const struct alloc_context *ac,
* of pageblocks that cannot be completely freed
* may increase.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - 요청 type으로 변경.
+ */
set_pageblock_migratetype(page, ac->migratetype);
ret = move_freepages_block(zone, page, ac->migratetype,
NULL);
@@ -3872,7 +3934,7 @@ void drain_zone_pages(struct zone *zone, struct per_cpu_pages *pcp)
*/
/*
* IAMROOT, 2022.04.09:
- * - pcplist를 drain시킨다.
+ * - pcplist를 bulk drain시킨다.(pcp를 buddy로 돌려보낸다.)
*/
static void drain_pages_zone(unsigned int cpu, struct zone *zone)
{
@@ -3957,6 +4019,12 @@ static void drain_local_pages_wq(struct work_struct *work)
* that need the guarantee that every CPU has drained can disable the
* optimizing racy check.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * @force_all_cpus true면 무조건 drain 시도.
+ * @zone NULL이면 모든 zone, 아니면 @zone에 대해서 수행한다.
+ * - 모든 cpu에 대하여 @zone 및 @force_all_cpus에 따라 pcp를 bulk drain한다.
+ */
static void __drain_all_pages(struct zone *zone, bool force_all_cpus)
{
int cpu;
@@ -3991,6 +4059,19 @@ static void __drain_all_pages(struct zone *zone, bool force_all_cpus)
* cpu to drain that CPU pcps and on_each_cpu_mask
* disables preemption as part of its processing
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - online cpu만큼 iterate를 하며 pcp에 page가 있는 cpu를 cpus_with_pcps cpumask에
+ * 기록한다.
+ * 1. force_all_cpus = true
+ * 모든 cpu에 대해서 set.
+ *
+ * 2. zone != NULL
+ * 해당 zone에 대한 pcp를 가진 모든 cpu.
+ *
+ * 3. zone == NULL
+ * cpu마다 모든 zone의 범위 에대해서 pcp가 있는 cpu.
+ */
for_each_online_cpu(cpu) {
struct per_cpu_pages *pcp;
struct zone *z;
@@ -4022,6 +4103,11 @@ static void __drain_all_pages(struct zone *zone, bool force_all_cpus)
cpumask_clear_cpu(cpu, &cpus_with_pcps);
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - cpus_with_pcps에 기록된 cpu를 iterate하며 worker thread를 동작시켜
+ * pcp를 drain한다.
+ */
for_each_cpu(cpu, &cpus_with_pcps) {
struct pcpu_drain *drain = per_cpu_ptr(&pcpu_drain, cpu);
@@ -4042,6 +4128,10 @@ static void __drain_all_pages(struct zone *zone, bool force_all_cpus)
*
* Note that this can be extremely slow as the draining happens in a workqueue.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - @zone에 대해여 pcp drain을 시도한다.
+ */
void drain_all_pages(struct zone *zone)
{
__drain_all_pages(zone, false);
@@ -5432,6 +5522,10 @@ __alloc_pages_may_oom(gfp_t gfp_mask, unsigned int order,
* Acquire the oom lock. If that fails, somebody else is
* making progress for us.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - cpu들이 동시에 oom처리하고잇는 경우를 확인한다.
+ */
if (!mutex_trylock(&oom_lock)) {
*did_some_progress = 1;
schedule_timeout_uninterruptible(1);
@@ -5445,6 +5539,10 @@ __alloc_pages_may_oom(gfp_t gfp_mask, unsigned int order,
* attempt shall not depend on __GFP_DIRECT_RECLAIM && !__GFP_NORETRY
* allocation which will never fail due to oom_lock already held.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - high watermark를 기준으로 할당을 시도한다.
+ */
page = get_page_from_freelist((gfp_mask | __GFP_HARDWALL) &
~__GFP_DIRECT_RECLAIM, order,
ALLOC_WMARK_HIGH|ALLOC_CPUSET, ac);
@@ -5452,6 +5550,10 @@ __alloc_pages_may_oom(gfp_t gfp_mask, unsigned int order,
goto out;
/* Coredumps can quickly deplete all memory reserves */
+/*
+ * IAMROOT, 2022.05.07:
+ * - core dump 중이면 out. 현재 task가 죽고 있다는것이다.
+ */
if (current->flags & PF_DUMPCORE)
goto out;
/* The OOM killer will not help higher order allocs */
@@ -5465,9 +5567,17 @@ __alloc_pages_may_oom(gfp_t gfp_mask, unsigned int order,
*
* The OOM killer may not free memory on a specific node.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - 현재 노드만 특정을 하거나 retry may fail이 존재하면 out.
+ */
if (gfp_mask & (__GFP_RETRY_MAYFAIL | __GFP_THISNODE))
goto out;
/* The OOM killer does not needlessly kill tasks for lowmem */
+/*
+ * IAMROOT, 2022.05.07:
+ * - DMA 요청이면 out.
+ */
if (ac->highest_zoneidx < ZONE_NORMAL)
goto out;
if (pm_suspended_storage())
@@ -5787,6 +5897,11 @@ EXPORT_SYMBOL_GPL(fs_reclaim_release);
#endif
/* Perform direct synchronous page reclaim */
+/*
+ * IAMROOT, 2022.05.07:
+ * @return reclaim 성공 page 개수
+ * - reclaim을 수행한다.
+ */
static unsigned long
__perform_reclaim(gfp_t gfp_mask, unsigned int order,
const struct alloc_context *ac)
@@ -5819,6 +5934,11 @@ __perform_reclaim(gfp_t gfp_mask, unsigned int order,
}
/* The really slow allocator path where we enter direct reclaim */
+/*
+ * IAMROOT, 2022.05.07:
+ * - reclaim을 실행한다. reclaim후에 buddy에서 할당이 실패했다면
+ * 1개 초과를 가진 highatomic에 대한 unreserve시도 및 pcp drain까지 수행한다.
+ */
static inline struct page *
__alloc_pages_direct_reclaim(gfp_t gfp_mask, unsigned int order,
unsigned int alloc_flags, const struct alloc_context *ac,
@@ -5831,6 +5951,10 @@ __alloc_pages_direct_reclaim(gfp_t gfp_mask, unsigned int order,
if (unlikely(!(*did_some_progress)))
return NULL;
+/*
+ * IAMROOT, 2022.05.07:
+ * - 회수를 했으니 buddy에서 가져와본다.
+ */
retry:
page = get_page_from_freelist(gfp_mask, order, alloc_flags, ac);
@@ -5839,8 +5963,16 @@ __alloc_pages_direct_reclaim(gfp_t gfp_mask, unsigned int order,
* pages are pinned on the per-cpu lists or in high alloc reserves.
* Shrink them and try again
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - reclaim했는데도 페이지 할당이 실패한경우 pcpu cache까지 한번 지워보고 시도한다.
+ */
if (!page && !drained) {
unreserve_highatomic_pageblock(ac, false);
+/*
+ * IAMROOT, 2022.05.07:
+ * - 모든 zone에 대해서 pcp drain을 시도한다.
+ */
drain_all_pages(NULL);
drained = true;
goto retry;
@@ -6036,6 +6168,14 @@ bool gfp_pfmemalloc_allowed(gfp_t gfp_mask)
*
* Returns true if a retry is viable or false to enter the oom path.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * @did_some_progress 이전 reclaim 성공여부
+ * - no_progress_loops를 갱신한다. 만약 한계를 초과하면 마지막 highatomic까지
+ * 사용한다.
+ * 그게 아니라면 min watermark와 비교를 하여 reclaim 가능여부를 결정한다.
+ * (미래 회수 가능한 memory까지 고려)
+ */
static inline bool
should_reclaim_retry(gfp_t gfp_mask, unsigned order,
struct alloc_context *ac, int alloc_flags,
@@ -6050,6 +6190,11 @@ should_reclaim_retry(gfp_t gfp_mask, unsigned order,
* their order will become available due to high fragmentation so
* always increment the no progress counter for them
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - 직전 reclaim에서 성공을 했고 order가 costly_order이내라면 no_progress_loops를
+ * 초기화한다.
+ */
if (did_some_progress && order <= PAGE_ALLOC_COSTLY_ORDER)
*no_progress_loops = 0;
else
@@ -6059,6 +6204,10 @@ should_reclaim_retry(gfp_t gfp_mask, unsigned order,
* Make sure we converge to OOM if we cannot make any progress
* several times in the row.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - no_progress_loops가 한계를 초과하면 마지막 남은 highatomic까지 사용해버린다.
+ */
if (*no_progress_loops > MAX_RECLAIM_RETRIES) {
/* Before OOM, exhaust highatomic_reserve */
return unreserve_highatomic_pageblock(ac, true);
@@ -6084,6 +6233,10 @@ should_reclaim_retry(gfp_t gfp_mask, unsigned order,
* Would the allocation succeed if we reclaimed all
* reclaimable pages?
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - min watermark에 대해서 available과 비교한다.
+ */
wmark = __zone_watermark_ok(zone, order, min_wmark,
ac->highest_zoneidx, alloc_flags, available);
trace_reclaim_retry_zone(z, order, reclaimable,
@@ -6095,6 +6248,11 @@ should_reclaim_retry(gfp_t gfp_mask, unsigned order,
* an IO to complete to slow down the reclaim and
* prevent from pre mature OOM
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - 이전 reclaim이 실패를 했어도 write중인 page가 reclaimable의 2배이상이면
+ * 0.1 sec만 쉬고 true return.
+ */
if (!did_some_progress) {
unsigned long write_pending;
@@ -6120,6 +6278,10 @@ should_reclaim_retry(gfp_t gfp_mask, unsigned order,
* looping without ever sleeping. Therefore we have to do a short sleep
* here rather than calling cond_resched().
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - current가 worker thread인 경우 interrupt무관 1 sec sleep
+ */
if (current->flags & PF_WQ_WORKER)
schedule_timeout_uninterruptible(1);
else
@@ -6408,9 +6570,16 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
if (page)
goto got_pg;
+/*
+ * IAMROOT, 2022.05.07:
+ * - 여기까지 왔다는건. reclaim을 시도해도 실패하고, highatomic 여유분 사용 및
+ * pcp drain을 했는데도 실패했다는것이다.
+ */
+
/* Try direct compaction and then allocating */
/*
* IAMROOT, 2022.04.09:
+ * - compact를 한번 실행해본다.
* - retry등을 할때는 compact_priority에 따라서 수행한다.
*/
page = __alloc_pages_direct_compact(gfp_mask, order, alloc_flags, ac,
@@ -6418,6 +6587,10 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
if (page)
goto got_pg;
+/*
+ * IAMROOT, 2022.05.07:
+ * - retry 금지를 확인한다.
+ */
/* Do not loop if specifically requested */
if (gfp_mask & __GFP_NORETRY)
goto nopage;
@@ -6426,9 +6599,17 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
* Do not retry costly high order allocations unless they are
* __GFP_RETRY_MAYFAIL
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - costly_order같은 경우엔 retry가 있어도 실패로 간주한다.
+ */
if (costly_order && !(gfp_mask & __GFP_RETRY_MAYFAIL))
goto nopage;
+/*
+ * IAMROOT, 2022.05.07:
+ * - no_progress_loops를 갱신하고 reclaim 재시도 여부를 확인한다.
+ */
if (should_reclaim_retry(gfp_mask, order, ac, alloc_flags,
did_some_progress > 0, &no_progress_loops))
goto retry;
@@ -6439,13 +6620,20 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
* implementation of the compaction depends on the sufficient amount
* of free memory (see __compaction_suitable)
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - compact retry 여부 확인을 하며 retries, priority--를 갱신한다.
+ */
if (did_some_progress > 0 &&
should_compact_retry(ac, order, alloc_flags,
compact_result, &compact_priority,
&compaction_retries))
goto retry;
-
+/*
+ * IAMROOT, 2022.05.07:
+ * - 현재 task에 사용 가능 noide가 변경되었는지 확인한다. 변경되었으면 재시도한다.
+ */
/* Deal with possible cpuset update races before we start OOM killing */
if (check_retry_cpuset(cpuset_mems_cookie, ac))
goto retry_cpuset;
diff --git a/mm/vmpressure.c b/mm/vmpressure.c
index 16a2700590cc..899654d1cc83 100644
--- a/mm/vmpressure.c
+++ b/mm/vmpressure.c
@@ -328,6 +328,8 @@ static void vmpressure_work_fn(struct work_struct *work)
* - 누적 scanned, reclaimed 에 따라 vmpressure를 monitoring하고있는 task들한테
* signal을 보낸다.
* - 누적 scanned가 vmpressure_win보다 작다면 누적만 시킨다.
+ * - android등에서 이 기능을 사용한다.(vmpressure, psi)
+ * app측에서 memory 상태등을 확인해서 안쓰는 app등을 죽이는 용도등
*/
void vmpressure(gfp_t gfp, struct mem_cgroup *memcg, bool tree,
unsigned long scanned, unsigned long reclaimed)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 7ff3b61d2764..f2b7d63343ba 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -940,6 +940,10 @@ static unsigned long shrink_slab_memcg(gfp_t gfp_mask, int nid,
*
* Returns the number of reclaimed slab objects.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - TODO
+ */
static unsigned long shrink_slab(gfp_t gfp_mask, int nid,
struct mem_cgroup *memcg,
int priority)
@@ -2769,6 +2773,11 @@ static int current_may_throttle(void)
* shrink_inactive_list() is a helper for shrink_node(). It returns the number
* of reclaimed pages
*/
+
+/*
+ * IAMROOT, 2022.05.07:
+ * - reclaim을 수행한다.
+ */
static unsigned long
shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec,
struct scan_control *sc, enum lru_list lru)
@@ -2856,6 +2865,18 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec,
* the flushers simply cannot keep up with the allocation
* rate. Nudge the flusher threads in case they are asleep.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - papago
+ * IO를 위해 대기열에 있지 않은 더러운 페이지가 검색되면 플러셔가 작업을 수행하지
+ * 않고 있음을 의미합니다. 메모리 압력이 더티 한계를 위반하고 더티 데이터가
+ * 만료되기 전에 더티 페이지를 LRU의 끝으로 밀어넣을 때 발생할 수 있습니다.
+ * 또한 쓰기가 아닌 모든 클린 캐시를 회수하는 메모리 압력으로 인해 dirty page의
+ * 비율이 증가할 때 발생할 수 있습니다. 그리고 어떤 경우에는, 플러셔가 단순히
+ * 할당 비율을 따라가지 못한다. 잠들어 있을 때를 대비해서 밀어라
+ * - unqueue
+ * dirty && !writeback.
+ */
if (stat.nr_unqueued_dirty == nr_taken)
wakeup_flusher_threads(WB_REASON_VMSCAN);
@@ -3129,6 +3150,26 @@ static unsigned long shrink_list(enum lru_list lru, unsigned long nr_to_scan,
* 1TB 101 10GB
* 10TB 320 32GB
*/
+
+/*
+ * IAMROOT, 2022.05.07:
+ * - papago
+ * inactive anon list는 VM이 너무 많은 작업을 수행할 필요가 없을 정도로 작아야
+ * 합니다.
+ *
+ * 비활성 파일 목록은 대부분의 메모리를 검사 방지 활성 목록에 설정된 workingsets에
+ * 남겨둘 수 있을 만큼 작아야 하지만, 전체 읽기 미리 실행된 창이 스레싱되지 않도록
+ * 충분히 커야 합니다.
+ *
+ * 또한 두 비활성 목록은 각 비활성 페이지가 회수되기 전에 다시 참조될 수 있을
+ * 정도로 충분히 커야 합니다.
+ *
+ * 이 작업이 실패하여 재장애가 관찰되면 비활성 목록이 증가합니다.
+ *
+ * inactive_ratio는 이 LRU의 ACTIVE 페이지와 ICTIVE 페이지의 목표 비율이며,
+ * 페이지아웃 코드에 의해 유지된다. 3의 inactive_ratio는 3을 의미한다.
+ * 페이지의 1 또는 25%가 비활성 목록에 보관됩니다.
+ */
static bool inactive_is_low(struct lruvec *lruvec, enum lru_list inactive_lru)
{
enum lru_list active_lru = inactive_lru + LRU_ACTIVE;
@@ -3140,11 +3181,27 @@ static bool inactive_is_low(struct lruvec *lruvec, enum lru_list inactive_lru)
active = lruvec_page_state(lruvec, NR_LRU_BASE + active_lru);
gb = (inactive + active) >> (30 - PAGE_SHIFT);
+
+/*
+ * IAMROOT, 2022.05.07:
+ * - int_sqrt(10 * gb) = root(10 * gb)
+ */
if (gb)
inactive_ratio = int_sqrt(10 * gb);
else
inactive_ratio = 1;
+/*
+ * IAMROOT, 2022.05.07:
+ * - 1GB 미만으로 차이나면 inactive < active
+ * - 1GB 이상 차이나면 inactive * root(10 * gb) < active
+ * ex) gb
+ * 1 inactive : active = 1 : 3
+ * 2 inactive : active = 1 : 4
+ * 3 inactive : active = 1 : 5
+ * 10 inactive : active = 1 : 10
+ * 16 inactive : active = 1 : 12
+ */
return inactive * inactive_ratio < active;
}
@@ -3453,6 +3510,11 @@ static void get_scan_count(struct lruvec *lruvec, struct scan_control *sc,
* Anonymous LRU management is a waste if there is
* ultimately no way to reclaim the memory.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - @pgdat에 대해서 anon page에 대해 page가 swap할 공간이 있거나
+ * demote가 가능한지 알아온다.
+ */
static bool can_age_anon_pages(struct pglist_data *pgdat,
struct scan_control *sc)
{
@@ -3464,6 +3526,10 @@ static bool can_age_anon_pages(struct pglist_data *pgdat,
return can_demote(pgdat->node_id, sc);
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - @lruvec에 대해서 reclaim을 수행한다.
+ */
static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
{
unsigned long nr[NR_LRU_LISTS];
@@ -3578,6 +3644,12 @@ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
* Recalculate the other LRU scan count based on its original
* scan target and the percentage scanning already complete
*/
+
+/*
+ * IAMROOT, 2022.05.07:
+ * - 위에서 정해진 lru의 반대(file <=> anon)의 nr을 산정한다. 최소 lru를 기준으로
+ * 반대측의 lru와 비율을 조정한다.
+ */
lru = (lru == LRU_FILE) ? LRU_BASE : LRU_FILE;
nr_scanned = targets[lru] - nr[lru];
nr[lru] = targets[lru] * (100 - percentage) / 100;
@@ -3597,6 +3669,12 @@ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
* Even if we did not try to evict anon pages at all, we want to
* rebalance the anon lru active/inactive ratio.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - anon page에 대해서 inactive가 active에 비해 비율이 적으면 deactive 시도를한다.
+ * SWAP_CLUSTER_MAX개씩만 reclaim을 하기때문에 anon page에 대해서만 조금더
+ * 신경 쓰는 개념.
+ */
if (can_age_anon_pages(lruvec_pgdat(lruvec), sc) &&
inactive_is_low(lruvec, LRU_INACTIVE_ANON))
shrink_active_list(SWAP_CLUSTER_MAX, lruvec,
@@ -3604,6 +3682,13 @@ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
}
/* Use reclaim/compaction for costly allocs or under memory pressure */
+
+/*
+ * IAMROOT, 2022.05.07:
+ * - @sc의 order와 priority에 대해서 reclaim중 compaction을 할지에 대해 정한다.
+ * - compaction은 order가 1이상이며 order가 costly_order보다 크거나 priority가 낮을때
+ * (4번째 시도 이상) true로 return한다.
+ */
static bool in_reclaim_compaction(struct scan_control *sc)
{
if (IS_ENABLED(CONFIG_COMPACTION) && sc->order &&
@@ -3621,6 +3706,11 @@ static bool in_reclaim_compaction(struct scan_control *sc)
* calls try_to_compact_pages() that it will have enough free pages to succeed.
* It will give up earlier than that if there is difficulty reclaiming pages.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - @sc에 대해서 reclaim을 더 해야되는건지를 결정한다.
+ * (모든 zone에 대해서 compaction을 못하는 상황에서만 reclaim 수행.)
+ */
static inline bool should_continue_reclaim(struct pglist_data *pgdat,
unsigned long nr_reclaimed,
struct scan_control *sc)
@@ -3643,6 +3733,10 @@ static inline bool should_continue_reclaim(struct pglist_data *pgdat,
* scan, but that approximation was wrong, and there were corner cases
* where always a non-zero amount of pages were scanned.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - 직전 reclaim에서 성공한게 하나도 없으면 false
+ */
if (!nr_reclaimed)
return false;
@@ -3662,6 +3756,10 @@ static inline bool should_continue_reclaim(struct pglist_data *pgdat,
}
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - 모든 zone이 COMPACT_SKIPPED이면 진입.
+ */
/*
* If we have not reclaimed enough pages for compaction and the
* inactive lists are large enough, continue reclaiming
@@ -3671,9 +3769,17 @@ static inline bool should_continue_reclaim(struct pglist_data *pgdat,
if (can_reclaim_anon_pages(NULL, pgdat->node_id, sc))
inactive_lru_pages += node_page_state(pgdat, NR_INACTIVE_ANON);
+/*
+ * IAMROOT, 2022.05.07:
+ * inactive file + swap가능한 anon > pages_for_compaction
+ */
return inactive_lru_pages > pages_for_compaction;
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - @sc->target_mem_cgroup의 @pgdat의 memcg들에 대해 reclaim을 수행한다.
+ */
static void shrink_node_memcgs(pg_data_t *pgdat, struct scan_control *sc)
{
struct mem_cgroup *target_memcg = sc->target_mem_cgroup;
@@ -3731,6 +3837,10 @@ static void shrink_node_memcgs(pg_data_t *pgdat, struct scan_control *sc)
} while ((memcg = mem_cgroup_iter(target_memcg, memcg, NULL)));
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - @pgdat에 대해 reclaim을 수행한다.
+ */
static void shrink_node(pg_data_t *pgdat, struct scan_control *sc)
{
struct reclaim_state *reclaim_state = current->reclaim_state;
@@ -3770,6 +3880,14 @@ static void shrink_node(pg_data_t *pgdat, struct scan_control *sc)
refaults = lruvec_page_state(target_lruvec,
WORKINGSET_ACTIVATE_ANON);
+
+/*
+ * IAMROOT, 2022.05.07:
+ * - refault != target refaults(snpashot_refaults() 참고)
+ * 새로운 workingset activate가 있는경우 deactive(밀어내기)를 하겠다는것.
+ * - incative_is_low
+ * inactive가 너무 적은경우 deactive를 하겠다는것.
+ */
if (refaults != target_lruvec->refaults[0] ||
inactive_is_low(target_lruvec, LRU_INACTIVE_ANON))
sc->may_deactivate |= DEACTIVATE_ANON;
@@ -3874,6 +3992,23 @@ static void shrink_node(pg_data_t *pgdat, struct scan_control *sc)
* immediate reclaim and stall if any are encountered
* in the nr_immediate check below.
*/
+
+/*
+ * IAMROOT, 2022.05.07:
+ * - papgo
+ * reclaim가 writeback에서 dirty page를 isolate하는 경우, 오래 지속되는 페이지
+ * 할당률이 페이지 laundering 속도를 초과하고 있음을 의미합니다. 영역 전체에 페이지
+ * 배포로 인해 조정 프로세스에서 전역 제한이 효과적이지 않거나 느린 backing device
+ * 가 많이 사용되고 있습니다. 유일한 옵션은 회수 컨텍스트에서 조정되는 것인데,
+ * 더티 프로세스가 balance_dirty_pages()가 관리하는 것과 동일한 방식으로
+ * 조정된다는 보장이 없기 때문에 이상적이지 않습니다.
+ *
+ * 노드에 PGDAT_WRITHBACK 플래그가 지정되면 kswapd는 즉시 회수 플래그가 지정된
+ * 페이지 아래의 페이지 수를 세고 아래의 nr_immediate 검사에서 해당 페이지 수가
+ * 발견되면 중지합니다.
+ *
+ * - backing device속도가 느리거나 요청등이 많은 이유로 node가 busy인것을 표시한다.
+ */
if (sc->nr.writeback && sc->nr.writeback == sc->nr.taken)
set_bit(PGDAT_WRITEBACK, &pgdat->flags);
@@ -3887,6 +4022,10 @@ static void shrink_node(pg_data_t *pgdat, struct scan_control *sc)
* implies that pages are cycling through the LRU
* faster than they are written so also forcibly stall.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - rotate이후에도 node가 너무 바쁘다. 현재 task(kswpad)를 0.1 sec sleep
+ */
if (sc->nr.immediate)
congestion_wait(BLK_RW_ASYNC, HZ/10);
}
@@ -3899,6 +4038,12 @@ static void shrink_node(pg_data_t *pgdat, struct scan_control *sc)
* Legacy memcg will stall in page writeback so avoid forcibly
* stalling in wait_iff_congested().
*/
+
+/*
+ * IAMROOT, 2022.05.07:
+ * - reclaimer가 kswpad이거나 writeback_throttling_sane을 지원하는 reclaimer일때
+ * congested상태(rotate중인 상황)이면 lruvec에다가 LRUVEC_CONGESTED를 set한다.
+ */
if ((current_is_kswapd() ||
(cgroup_reclaim(sc) && writeback_throttling_sane(sc))) &&
sc->nr.dirty && sc->nr.dirty == sc->nr.congested)
@@ -3910,6 +4055,12 @@ static void shrink_node(pg_data_t *pgdat, struct scan_control *sc)
* starts encountering unqueued dirty pages or cycling through
* the LRU too quickly.
*/
+
+/*
+ * IAMROOT, 2022.05.07:
+ * - direct reclaim이고, current_may_throttle상태, 절전모드가 아니고, lruvec이
+ * congested상태라면 0.1초를 쉰다.
+ */
if (!current_is_kswapd() && current_may_throttle() &&
!sc->hibernation_mode &&
test_bit(LRUVEC_CONGESTED, &target_lruvec->flags))
@@ -4000,6 +4151,10 @@ static inline bool compaction_ready(struct zone *zone, struct scan_control *sc)
* If a zone is deemed to be full of pinned pages then just give it a light
* scan then give up on it.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - @zonelist에 대해서 reclaim을 수행한다.
+ */
static void shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
{
struct zoneref *z;
@@ -4106,6 +4261,11 @@ static void shrink_zones(struct zonelist *zonelist, struct scan_control *sc)
sc->gfp_mask = orig_mask;
}
+/*
+ * IAMROOT, 2022.05.07:
+ * - @target_memcg, @pgdat에 대한 lruvec에 refault 통계값을 적용한다.
+ * refault가 변경됬는지에 대한 여부를 검사하는데 사용한다.
+ */
static void snapshot_refaults(struct mem_cgroup *target_memcg, pg_data_t *pgdat)
{
struct lruvec *target_lruvec;
@@ -4134,6 +4294,10 @@ static void snapshot_refaults(struct mem_cgroup *target_memcg, pg_data_t *pgdat)
* returns: 0, if no pages reclaimed
* else, the number of pages reclaimed
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - @zonelist에 대한 reclaim을 수행한다.
+ */
static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
struct scan_control *sc)
{
@@ -4211,6 +4375,15 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist,
* entire cgroup subtree up front, we assume the estimates are
* good, and retry with forcible deactivation if that fails.
*/
+/*
+ * IAMROOT, 2022.05.07:
+ * - papgo
+ * 노드의 메모리 구성을 기반으로 inactive:active 비율을 결정하지만 제한적인
+ * reclaim_idx 또는 memory.low cgroup 설정은 많은 양의 메모리를 회수할 수 없게
+ * 할 수 있습니다. 둘 다 매우 흔하지 않기 때문에 전체 cgroup 하위 트리에 대해
+ * 비용이 많이 드는 적격성 계산을 수행하는 대신 추정치가 양호하다고 가정하고
+ * 실패하면 강제 비활성화를 통해 재시도한다.
+ */
if (sc->skipped_deactivate) {
sc->priority = initial_priority;
sc->force_deactivate = 1;
@@ -4458,6 +4631,7 @@ static bool throttle_direct_reclaim(gfp_t gfp_mask, struct zonelist *zonelist,
/*
* IAMROOT, 2022.04.16:
+ * @return reclaim 한 page 개수
* - direct reclaim을 수행하는데, 만약 memory가 너무 적을 경우 throttle을 수행한다.
*/
unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
댓글 0
번호 | 제목 | 글쓴이 | 날짜 | 조회 수 |
---|---|---|---|---|
공지 | [공지] 스터디 정리 노트 공간입니다. | woos | 2016.05.14 | 623 |
125 | [커널 18차] 54주차 | kkr | 2022.06.04 | 81 |
124 | [커널 19차] 3주차 | 리턴 | 2022.06.04 | 215 |
123 | [커널 18차] 53주차 | kkr | 2022.05.29 | 93 |
122 | [커널 17차] 91주차 | ㅇㅇㅇ | 2022.05.28 | 64 |
121 | [커널 19차] 2주차 | 리턴 | 2022.05.28 | 169 |
120 | [커널 17차] 90주차 | ㅇㅇㅇ | 2022.05.22 | 149 |
119 | [커널 18차] 52주차 | kkr | 2022.05.21 | 123 |
118 | [커널 19차] 1주차 | 리턴 | 2022.05.16 | 455 |
117 | [커널 17차] 89주차 | ㅇㅇㅇ | 2022.05.15 | 63 |
116 | [커널 18차] 51주차 | kkr | 2022.05.14 | 159 |
» | [커널 18차] 50주차 | kkr | 2022.05.10 | 204 |
114 | [커널 17차] 88주차 | ㅇㅇㅇ | 2022.05.08 | 101 |
113 | [커널 19차] 0주차 - 오리엔테이션 | 리턴 | 2022.05.07 | 598 |
112 | [커널 17차] 86~87주차 | ㅇㅇㅇ | 2022.04.30 | 101 |
111 | [커널 17차] 84~85주차 | JSYoo5B | 2022.04.16 | 86 |
110 | [커널 17차] 83주차 | ㅇㅇㅇ | 2022.04.03 | 92 |
109 | [커널 17차] 82주차 | ㅇㅇㅇ | 2022.03.27 | 65 |
108 | [커널 17차] 81주차 | ㅇㅇㅇ | 2022.03.19 | 131 |
107 | [커널 17차] 77 ~ 80주차 | ㅇㅇㅇ | 2022.03.13 | 102 |
106 | [커널 17차] 76주차 [1] | ㅇㅇㅇ | 2022.02.19 | 139 |
.