OOM livelock problem Aug 3, 2009

2ff05b2b4eac2e63d345fc731ea151a060247f53

이 패치는 6월 16일 Google의 David에 의해 적용된 패치이다.
이 패치의 요는 다음과 같다.

1. oom_adj를 task_struct에서 mm_struct으로 옮긴다.
2. OOM의 livelock 상황을 막는다.

1.의 rationale은 쉽게 생각할 수 있다.
oom_adj의 값은 per-task의 member로는 의미가 없기 때문이다.
생각을 해보자. 특정 프로세스 A의 임의의 한 쓰레드의 oom_adj값을 -17(OOM_DISABLE)로 했다고 가정하자.
OOM killer의 입장에서 한 task를 kill해야 하는 경우,
특정 프로세스 A를 kill하는 것이 make sense한가? 당연히 No!.

왜냐하면 프로세스의 A가 포함하고 있는 쓰레드 가운데 한 녀석이라도
OOM_DISABLE이 설정되어 있다면, mm_struct 즉 해당 쓰레드가 사용하고 있는 주소 공간은
보존되어야 하기 때문이다. 주소 공간이 보존된다는 것은 OOM killer는 메모리를 회수하기위해
프로세스 A를 kill할 수 없다는 것이다.

이는 아래와 같이 oom_kill_task의 함수에서도 볼 수 있다.


366 /*
367 * Don't kill the process if any threads are set to OOM_DISABLE
368 */
369 do_each_thread(g, q) {
370 if (q->mm == mm && q->oomkilladj == OOM_DISABLE)
371 return 1;
372 } while_each_thread(g, q);
373


그러므로 oom_adj란 member는 task_struct의 member보다는 mm_struct의 member가 되는 것이
더 자연스러운 것이다.

2.에서 말한 livelock 상황은 위에서 언급한 문제로 인해 발생한다.
select_bad_process가 프로세스 A를 선택했다고 가정하자.
이전의 예와 같이 A는 OOM immutable한 쓰레드를 가지고 있다고 가정하면
oom_kill_process는 결국 retry하게 될 것이다.

530 retry:
531 /*
532 * Rambo mode: Shoot down a process and hope it solves whatever
533 * issues we may have.
534 */
535 p = select_bad_process(&points, NULL);
536
537 if (PTR_ERR(p) == -1UL)
538 return;
539
540 /* Found nothing?!?! Either we hang forever, or we panic. */
541 if (!p) {
542 read_unlock(&tasklist_lock);
543 panic("Out of memory and no killable processes...\n");
544 }
545
546 if (oom_kill_process(p, gfp_mask, order, points, NULL,
547 "Out of memory"))
548 goto retry;
549 }


이 상황은 OOM immuable 쓰레드가 종료될 때까지 계속 될 수 있으며, 해당 쓰레드가 언제 종료될지는
아무도 모른다. 그러므로 livelock 상황을 유발할 수 있었다.

하지만 이 패치가 또 다른 side effect을 만들어 내기 시작했다.
것은 다음 기회에.

0 개의 덧글: