이 코드는 어떤 문제를 푸나요?
빈 페이지 수가 충분해도 고차 order의 연속 블록은 없을 수 있습니다. compaction은 이동 가능한 페이지를 옮겨 빈 공간을 모읍니다. 하지만 이동할 수 없는 페이지가 막고 있다면 같은 시도를 계속하는 데 비용만 들 수 있어 재시도를 잠시 미루기도 합니다.
읽을 범위: v6.18.37 · mm/compaction.c · compaction_deferred 141–157행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.
먼저 알아둘 개념
외부 단편화
총 빈 용량은 충분하지만 요청한 크기의 연속 구간이 없는 상태입니다. 회수로 빈 페이지 수를 늘리는 것과 compaction으로 배치를 바꾸는 것은 다른 일입니다.
defer shift
재시도 간격의 기준을 2의 거듭제곱으로 만드는 값입니다. 시간 단위 sleep 길이가 아니라 판단 횟수를 기반으로 한 지연입니다.
order별 실패 기록
작은 요청은 성공할 수 있는데 큰 요청에서 실패한 것일 수 있습니다. 실패한 크기 이상에만 같은 지연 정책을 적용하는 이유입니다.
처음 읽을 때
8페이지 중 빈 곳이 짝수 위치에만 있는 예를 그려 보세요. 빈 페이지 4개와 연속 4페이지 블록 1개가 다르다는 것을 먼저 확인한 뒤 재시도 조건을 읽으시면 됩니다.
더 깊이 살펴볼 때
compaction_deferred의 true가 실패 결과가 아니라 이번 시도 생략 판단임을 구분하세요. compact_considered의 포화 처리와 order 비교가 서로 다른 목적을 갖습니다.
그림으로 보는 변화
compact_defer_shift=2여서 defer_limit=1UL<<2=4, compact_considered=0에서 시작합니다. 매번 같은 zone과 지연 대상 order ≥ compact_order_failed로 호출하고, 사이에 상태를 바꾸는 다른 함수는 없다고 가정합니다. 원문 141–157행을 숫자로 펼친 예입니다.
considered=0. 초·밀리초 타이머가 아닙니다.
카운터를 1씩 올린 뒤 true: 이번 compaction을 미룹니다.
카운터를 4에 고정하고 false: 이 함수는 더 미루지 않습니다.
다시 증가를 검사해도 4로 되돌려 false를 반환합니다. 별도 상태 변경 전까지 같습니다.
작은 order는 별도 경로입니다. order < compact_order_failed면 카운터를 올리기 전에 false로 돌아옵니다. 어느 false든 여기서는 “이 도우미가 지연하지 않음”일 뿐, 실제 페이지 이동이나 할당 성공을 보장하지 않습니다.
| 호출 | 판단 직전 → 반환 뒤 considered | 반환 | 근거 |
|---|---|---|---|
| 1 | 0 → 1 | true · 미룸 | 1 < 4 |
| 2 | 1 → 2 | true · 미룸 | 2 < 4 |
| 3 | 2 → 3 | true · 미룸 | 3 < 4 |
| 4 | 3 → 4 | false · 미루지 않음 | 4 ≥ 4, 한도에 고정 |
| 5 이후 | 4 → 잠시 5 → 4 | false · 미루지 않음 | 한도에 다시 고정 |
| 별도 비교: 더 작은 order | 기존 값 → 그대로 | false · 미루지 않음 | order < compact_order_failed에서 조기 반환; 위 순차 호출에 포함하지 않음 |
지연 상태를 새로 잡는 defer_compaction():126–138행은 considered를 0으로 돌리고 shift를 증가시키되 최대 6으로 제한합니다. 따라서 위 숫자는 shift가 2로 유지되는 동안만 적용됩니다. 실제 지연 판단과 다른 함수의 호출 경로는 이 예시에서 생략했습니다.

1. 연속 블록 필요
빈 페이지 수와 연속성 별도 확인
화살표는 정책 판단 순서입니다. 실제 페이지 이동은 compact_zone 등의 다른 함수가 수행합니다.
2. 이전 실패와 요청 크기 비교
작은 order는 지연 대상에서 제외 가능
더 작은 요청까지 같은 실패로 간주하지 않습니다.
3. 시도 횟수 기준으로 재시도
considered가 limit 미만이면 미루고 도달하면 허용
화살표는 호출 사이에 누적되는 카운터 변화를 뜻합니다. 이 함수 안에서 잠드는 타이머가 아닙니다.
compaction_deferred를 한 줄씩 읽기
줄 번호는 v6.18.37 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.
static bool compaction_deferred(struct zone *zone, int order)
{
unsigned long defer_limit = 1UL << zone->compact_defer_shift;
if (order < zone->compact_order_failed)
return false;
/* Avoid possible overflow */
if (++zone->compact_considered >= defer_limit) {
zone->compact_considered = defer_limit;
return false;
}
trace_mm_compaction_deferred(zone, order);
return true;
}static bool compaction_deferred(struct zone *zone, int order)zone에서 해당 order 요청의 compaction을 이번에는 미룰지 판단합니다. 반환 true의 의미를 함수 이름과 함께 읽으셔야 합니다.
unsigned long defer_limit = 1UL << zone->compact_defer_shift;지연 shift에서 판단 횟수 한도를 계산합니다. shift=3이면 한도는 8입니다. 카운터가 0에서 시작하고 지연 대상 order만 반복 호출하면 앞의 7회는 미루고 8번째에는 미루지 않습니다. 8초를 뜻하지 않습니다.
if (order < zone->compact_order_failed)이번 요청이 이전에 실패했다고 기록된 최소 order보다 작은지 봅니다.
return false;작은 요청은 실패 기록의 지연 대상에서 빼고 지금 시도할 수 있도록 false를 반환합니다.
if (++zone->compact_considered >= defer_limit) {고차 요청에 대해 검토 횟수를 하나 늘리고 한도에 도달했는지 확인합니다.
zone->compact_considered = defer_limit;한도에 도달한 카운터를 그 값에 고정하여 계속 증가할 때 발생할 수 있는 overflow를 피합니다.
return false;기다린 횟수가 충분하므로 이번에는 compaction을 미루지 않습니다.
trace_mm_compaction_deferred(zone, order);이번 시도를 지연한 사실을 trace에 남겨 지연 정책의 영향을 관찰할 수 있게 합니다.
return true;이번에는 compaction을 생략하도록 true를 반환합니다. 실제 이동 성공 여부를 반환하는 함수가 아닙니다.
함께 생각해 볼 질문
compaction은 전체 빈 페이지 수를 늘리나요?
주목적은 이동을 통해 빈 공간을 연속으로 모으는 것입니다. 사용하지 않는 내용을 버려 공간을 얻는 reclaim과 구분합니다.
true를 반환하면 압축에 성공한 것인가요?
아닙니다. compaction_deferred의 true는 이번 compaction을 미루겠다는 뜻입니다.
작은 order에서 false를 즉시 반환하는 이유는 무엇인가요?
기록된 실패 크기보다 작은 요청은 만족할 수 있으므로 큰 요청의 실패 때문에 같이 지연시키지 않습니다.
출처와 읽은 범위
Linux stable v6.18.37 · mm/compaction.c
그림의 단계 구분에는 문C 블로그의 compaction, isolation, alloc_pages slowpath 글에서 확인한 관련 절·그림 구성만 참고했습니다. 세 글은 Linux v5.0 기준입니다. 위 호출 횟수와 반환값은 보존된 v6.18.37 mm/compaction.c로 검증했으며 문C 그림·본문을 복제하지 않았습니다.
해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고
