관련 소스: mm/damon/ 디렉토리 (core.c, reclaim.c, lru_sort.c, vaddr.c, paddr.c, ops-common.c, modules-common.c, sysfs.c)
관련 문서: 페이지 회수 (vmscan) · 페이지 Migration · 메모리 관리 개요
DAMON(Data Access Monitor)는 Linux 5.15에서 SeongJae Park이 도입한 메모리 접근 패턴 모니터링 프레임워크입니다. 물리 또는 가상 주소 공간을 샘플링 기반으로 모니터링하여 핫/콜드 영역을 구분하고, 접근 패턴에 기반한 자동화된 메모리 관리 작업(DAMOS: Data Access Monitoring-based Operation Scheme)을 수행합니다. 오버헤드가 1% 미만으로 낮아 프로덕션 시스템에서도 사용 가능합니다.
DAMON의 핵심 설계 원칙은 "정확한 모니터링은 불필요하고, 충분히 정확한 추정이면 된다"입니다. 전체 메모리를 정밀 추적하는 대신 샘플링과 영역(region)의 적응적 병합/분할을 통해 높은 정확도와 낮은 오버헤드를 동시에 달성합니다. 내장 모듈인 damon_reclaim(콜드 페이지 자동 회수)과 damon_lru_sort(LRU 리스트 자동 정렬)가 대표적인 활용 사례입니다.
도서관으로 비유하면, DAMON은 모든 책의 대출 기록을 일일이 세지 않고 자주 찾는 서가만 빠르게 표시해 우선 정리하는 방식에 가깝습니다. 현장에서는 damo 같은 사용자 공간 도구로 대상과 스킴을 다루고, DAMOS_MIGRATE_HOT/DAMOS_MIGRATE_COLD는 NUMA 티어링과 함께 핫 데이터 이동에 활용됩니다. 이런 흐름은 MGLRU나 메모리 티어링 문서와 같이 보면 더 또렷해집니다.
# 소스 파일 경로
mm/damon/core.c — 핵심 모니터링 루프, 영역 관리, DAMOS 로직
mm/damon/reclaim.c — DAMON 기반 페이지 회수 모듈
mm/damon/lru_sort.c — DAMON 기반 LRU 정렬 모듈
mm/damon/vaddr.c — 가상 주소 공간 모니터링 연산
mm/damon/paddr.c — 물리 주소 공간 모니터링 연산
mm/damon/ops-common.c — 공통 연산 (PTE young/clear, folio 처리)
mm/damon/sysfs.c — sysfs 인터페이스
mm/damon/modules-common.c — 모듈 공통 유틸리티
include/linux/damon.h — 핵심 자료구조 및 API 정의
# DAMON sysfs 인터페이스 확인
ls /sys/kernel/mm/damon/
# DAMON_RECLAIM 활성화
echo Y > /sys/module/damon_reclaim/parameters/enabled
# DAMON_RECLAIM 파라미터 확인
cat /sys/module/damon_reclaim/parameters/min_age
cat /sys/module/damon_reclaim/parameters/monitor_region_start
cat /sys/module/damon_reclaim/parameters/monitor_region_end
# DAMON_LRU_SORT 활성화 (커널 6.0+)
echo Y > /sys/module/damon_lru_sort/parameters/enabled
# kdamond 스레드 확인
ps aux | grep kdamond
# DAMON tracepoint 이벤트 확인
sudo perf list | grep damon
# DAMOS 통계 확인 (sysfs)
cat /sys/kernel/mm/damon/admin/kdamonds/nr_kdamonds
ls /sys/kernel/mm/damon/admin/kdamonds/0/contexts/
# 메모리 압력 확인 (PSI)
cat /proc/pressure/memory
# 물리 주소 영역 확인 (DAMON_RECLAIM 대상)
sudo cat /proc/iomem | head -20
모니터링 대상 메모리 영역을 나타냅니다. 각 영역은 시작/끝 주소, 접근 빈도, 나이(age)를 추적합니다.
/* include/linux/damon.h:76-86 */
struct damon_region {
struct damon_addr_range ar; /* 시작 주소 (inclusive), 끝 주소 (exclusive) */
unsigned long sampling_addr; /* 다음 접근 확인을 위한 샘플링 주소 */
unsigned int nr_accesses; /* 현재 집계 구간에서의 접근 횟수 */
unsigned int nr_accesses_bp; /* 접근 빈도 (basis point, 1/10000) */
struct list_head list; /* 형제 영역 연결용 리스트 */
unsigned int age; /* 접근 패턴 유지 시간 (집계 구간 단위) */
unsigned int last_nr_accesses; /* 이전 집계 구간의 접근 횟수 */
};
필드 설명:
ar: 영역의 물리/가상 주소 범위 [start, end)sampling_addr: 매 샘플링 구간마다 랜덤으로 선택되는 확인 대상 주소nr_accesses: 현재 aggr_interval 내에서 접근이 감지된 샘플링 횟수. aggr_interval마다 0으로 리셋됨nr_accesses_bp: nr_accesses를 basis point로 표현한 값. 이동 합(moving sum) 방식으로 업데이트되어, aggr_interval이 너무 길 때도 즉시 접근 정보를 얻을 수 있음age: 인접 영역 병합 시 가중평균으로 계산됨. 접근 패턴이 변하면 0으로 리셋모니터링 대상(프로세스의 가상 주소 공간 또는 물리 주소 공간 전체)을 나타냅니다.
/* include/linux/damon.h:105-111 */
struct damon_target {
struct pid *pid; /* 대상 프로세스의 PID (물리 주소는 NULL) */
unsigned int nr_regions; /* 이 대상의 영역 수 */
struct list_head regions_list; /* damon_region 연결 리스트 헤드 */
struct list_head list; /* 다른 대상과의 연결용 리스트 */
bool obsolete; /* commit 시 폐기 대상 표시 */
};
데이터 접근 모니터링 기반 동작 계획을 정의합니다. 접근 패턴 조건에 맞는 영역에 특정 작업을 수행하는 규칙입니다.
/* include/linux/damon.h:545-580 */
struct damos {
struct damos_access_pattern pattern; /* 대상 접근 패턴 (크기, 접근 빈도, 나이 범위) */
enum damos_action action; /* 적용할 동작 (PAGEOUT, COLD, HUGEPAGE 등) */
unsigned long apply_interval_us; /* 동작 적용 주기 (마이크로초) */
/* 내부 사용 필드 */
unsigned long next_apply_sis; /* 다음 적용 시점 (샘플링 구간 기준) */
bool walk_completed; /* DAMOS walk 완료 여부 */
bool core_filters_allowed; /* 코어 필터 통과 여부 */
/* 공개 필드 */
struct damos_quota quota; /* 자원 제한 (시간/크기) */
struct damos_watermarks wmarks; /* 워터마크 기반 자동 활성화/비활성화 */
struct list_head core_filters; /* 코어 레이어 필터 목록 */
struct list_head ops_filters; /* 연산 레이어 필터 목록 */
void *last_applied; /* 마지막 적용된 연산 대상 (중복 방지) */
struct damos_stat stat; /* 적용 통계 */
struct list_head list; /* 다른 스킴과의 연결용 리스트 */
};
모니터링 컨텍스트. 하나의 kdamond 스레드가 관리하는 최상위 객체입니다.
/* include/linux/damon.h:780 기준 핵심 필드 */
struct damon_ctx {
struct damon_attrs attrs; /* 모니터링 속성 (샘플/집계/연산 업데이트 주기) */
unsigned long passed_sample_intervals; /* 경과한 샘플링 구간 수 */
unsigned long next_aggregation_sis; /* 다음 집계 시점 */
unsigned long next_ops_update_sis; /* 다음 연산 업데이트 시점 */
/* kdamond 스레드 관리 */
struct task_struct *kdamond; /* 모니터링 스레드 */
struct mutex kdamond_lock;
/* DAMOS 호출 제어 */
struct list_head call_controls; /* damon_call 요청 목록 */
/* 대상 및 스킴 */
struct list_head adaptive_targets; /* damon_target 목록 */
struct list_head schemes; /* damos 스킴 목록 */
/* 연산 (가상/물리 주소별 구현) */
struct damon_operations ops;
};
DAMOS가 영역에 동작을 적용하기 위한 접근 패턴 필터입니다.
/* include/linux/damon.h:468-475 */
struct damos_access_pattern {
unsigned long min_sz_region; /* 최소 영역 크기 */
unsigned long max_sz_region; /* 최대 영역 크기 */
unsigned int min_nr_accesses; /* 최소 접근 빈도 */
unsigned int max_nr_accesses; /* 최대 접근 빈도 */
unsigned int min_age_region; /* 최소 나이 */
unsigned long max_age_region; /* 최대 나이 */
};
시스템 상태에 따른 DAMOS 스킴의 자동 활성화/비활성화를 제어합니다.
/* include/linux/damon.h:317-326 */
struct damos_watermarks {
enum damos_wmark_metric metric; /* 워터마크 메트릭 (FREE_MEM_RATE 등) */
unsigned long interval; /* 확인 주기 (마이크로초) */
unsigned long high; /* 상한선 — 초과 시 비활성화 */
unsigned long mid; /* 중간선 — 이하일 때 활성화 가능 */
unsigned long low; /* 하한선 — 미만 시 비활성화 */
bool activated; /* 현재 활성화 상태 */
};
DAMOS 동작의 공격성을 제어하는 자원 제한입니다.
/* include/linux/damon.h:258-285 */
struct damos_quota {
unsigned long reset_interval; /* 충전 리셋 주기 (밀리초) */
unsigned long ms; /* 최대 사용 가능 시간 (밀리초) */
unsigned long sz; /* 최대 적용 가능 크기 (바이트) */
struct list_head goals; /* 쿼터 자동 조절 목표 목록 */
unsigned long esz; /* 유효 크기 쿼터 (내부 계산) */
unsigned int weight_sz; /* 크기 가중치 (우선순위) */
unsigned int weight_nr_accesses; /* 접근 빈도 가중치 */
unsigned int weight_age; /* 나이 가중치 */
};
모니터링 대상 주소 공간 종류별 연산 구현체입니다.
/* include/linux/damon.h:643-656 */
struct damon_operations {
enum damon_ops_id id; /* 연산 세트 ID (VADDR, FVADDR, PADDR) */
void (*init)(struct damon_ctx *context); /* 초기화 */
void (*update)(struct damon_ctx *context); /* 대상 업데이트 */
void (*prepare_access_checks)(struct damon_ctx *context); /* 접근 검사 준비 */
unsigned int (*check_accesses)(struct damon_ctx *context); /* 접근 검사 실행 */
int (*get_scheme_score)(struct damon_ctx *context,
struct damon_target *t, struct damon_region *r,
struct damos *scheme);
unsigned long (*apply_scheme)(struct damon_ctx *context,
struct damon_target *t, struct damon_region *r,
struct damos *scheme, unsigned long *sz_filter_passed);
bool (*target_valid)(struct damon_target *t); /* 대상 유효성 검사 */
void (*cleanup_target)(struct damon_target *t); /* 대상 정리 */
};
DAMOS가 수행할 수 있는 동작 목록입니다.
/* include/linux/damon.h:135-147 */
enum damos_action {
DAMOS_WILLNEED, /* madvise(MADV_WILLNEED) — 사전 로드 */
DAMOS_COLD, /* madvise(MADV_COLD) — 콜드 표시 */
DAMOS_PAGEOUT, /* 페이지 회수 (swap out) */
DAMOS_HUGEPAGE, /* madvise(MADV_HUGEPAGE) — THP 유도 */
DAMOS_NOHUGEPAGE, /* madvise(MADV_NOHUGEPAGE) — THP 억제 */
DAMOS_LRU_PRIO, /* LRU 우선순위 상향 */
DAMOS_LRU_DEPRIO, /* LRU 우선순위 하향 */
DAMOS_MIGRATE_HOT, /* 핫 영역 마이그레이션 */
DAMOS_MIGRATE_COLD, /* 콜드 영역 마이그레이션 */
DAMOS_STAT, /* 통계만 기록 (동작 없음) */
NR_DAMOS_ACTIONS,
};
kdamond 스레드의 핵심 루프입니다. 샘플링 → 접근 검사 → 영역 병합/분할 → DAMOS 적용을 반복합니다.
/* mm/damon/core.c:2746-2850 */
static int kdamond_fn(void *data)
{
struct damon_ctx *ctx = data;
/* ... 초기화 ... */
while (!kdamond_need_stop(ctx)) {
/* 워터마크 기반 활성화 대기 */
if (kdamond_wait_activation(ctx))
break;
/* 1단계: 접근 검사 준비 (PTE dirty/young 비트 리셋 등) */
if (ctx->ops.prepare_access_checks)
ctx->ops.prepare_access_checks(ctx);
/* 샘플링 구간만큼 대기 */
kdamond_usleep(sample_interval);
ctx->passed_sample_intervals++;
/* 2단계: 접근 검사 (PTE dirty/young 비트 확인) */
if (ctx->ops.check_accesses)
max_nr_accesses = ctx->ops.check_accesses(ctx);
/* 3단계: 집계 구간 경과 시 인접 영역 병합 */
if (ctx->passed_sample_intervals >= next_aggregation_sis)
kdamond_merge_regions(ctx, max_nr_accesses / 10, sz_limit);
/* 4단계: DAMOS 스킴 적용 */
kdamond_call(ctx, false);
if (!list_empty(&ctx->schemes))
kdamond_apply_schemes(ctx);
/* 5단계: 집계 리셋 및 영역 분할 */
if (ctx->passed_sample_intervals >= next_aggregation_sis) {
kdamond_reset_aggregated(ctx);
kdamond_split_regions(ctx);
}
/* 6단계: 연산 업데이트 (VMA 변경 반영 등) */
if (ctx->passed_sample_intervals >= next_ops_update_sis) {
if (ctx->ops.update)
ctx->ops.update(ctx);
}
}
/* ... 정리 ... */
}
동작 흐름 요약:
1. prepare_access_checks: 샘플 주소의 PTE dirty/young 비트를 리셋
2. sample_interval만큼 대기
3. check_accesses: 비트가 설정되었으면 접근으로 기록
4. aggr_interval 경과 시 인접 영역 병합 (접근 빈도 유사)
5. DAMOS 스킴 적용 (패턴 매칭 + 필터 + 쿼터 검사)
6. 집계 리셋 + 너무 적은 영역은 분할
영역에 DAMOS 스킴의 동작을 적용하는 핵심 함수입니다.
/* mm/damon/core.c:1998-2030 */
static void damon_do_apply_schemes(struct damon_ctx *c,
struct damon_target *t,
struct damon_region *r)
{
struct damos *s;
damon_for_each_scheme(s, c) {
/* 적용 시점 확인 */
if (c->passed_sample_intervals < s->next_apply_sis)
continue;
/* 워터마크 활성화 확인 */
if (!s->wmarks.activated)
continue;
/* 쿼터 초과 확인 */
if (quota->esz && quota->charged_sz >= quota->esz)
continue;
/* 이전에 충전된 영역 스킵 */
if (damos_skip_charged_region(t, &r, s, c->min_region_sz))
continue;
/* 접근 패턴 매칭 */
if (damos_valid_target(c, t, r, s))
damos_apply_scheme(c, t, r, s);
}
}
판단 조건 순서: 시점 → 워터마크 → 쿼터 → 충전 이력 → 패턴 매칭 → 적용
영역이 DAMOS 스킴의 접근 패턴 조건을 만족하는지 확인합니다.
/* mm/damon/core.c:1678-1690 */
static bool __damos_valid_target(struct damon_region *r, struct damos *s)
{
unsigned long sz;
unsigned int nr_accesses = r->nr_accesses_bp / 10000;
sz = damon_sz_region(r);
return s->pattern.min_sz_region <= sz &&
sz <= s->pattern.max_sz_region &&
s->pattern.min_nr_accesses <= nr_accesses &&
nr_accesses <= s->pattern.max_nr_accesses &&
s->pattern.min_age_region <= r->age &&
r->age <= s->pattern.max_age_region;
}
인접한 영역 중 접근 빈도가 유사한 것을 병합하여 모니터링 오버헤드를 줄입니다.
/* mm/damon/core.c:2463-2481 */
static void kdamond_merge_regions(struct damon_ctx *c, unsigned int threshold,
unsigned long sz_limit)
{
struct damon_target *t;
unsigned int nr_regions;
unsigned int max_thres;
max_thres = c->attrs.aggr_interval /
(c->attrs.sample_interval ? c->attrs.sample_interval : 1);
do {
nr_regions = 0;
damon_for_each_target(t, c) {
damon_merge_regions_of(t, threshold, sz_limit);
nr_regions += damon_nr_regions(t);
}
threshold = max(1, threshold * 2);
} while (nr_regions > c->attrs.max_nr_regions &&
threshold / 2 < max_thres);
}
병합 기준: 인접 + nr_accesses 차이 ≤ threshold + 합산 크기 ≤ sz_limit. 영역 수가 max_nr_regions를 초과하면 threshold를 2배씩 올려가며 반복 병합.
시스템 메모리 상태에 따라 DAMOS 스킴의 활성화/비활성화를 결정합니다.
/* mm/damon/core.c:2615-2641 */
static unsigned long damos_wmark_wait_us(struct damos *scheme)
{
unsigned long metric;
if (damos_get_wmark_metric_value(scheme->wmarks.metric, &metric))
return 0;
/* high 초과 또는 low 미만 → 비활성화 */
if (metric > scheme->wmarks.high || scheme->wmarks.low > metric) {
scheme->wmarks.activated = false;
return scheme->wmarks.interval;
}
/* mid~high 구간이고 비활성 상태 → 대기 */
if ((scheme->wmarks.high >= metric && metric >= scheme->wmarks.mid) &&
!scheme->wmarks.activated)
return scheme->wmarks.interval;
scheme->wmarks.activated = true;
return 0;
}
워터마크 상태 다이어그램:
metric > high → 비활성화 (메모리 여유)
mid ≤ metric ≤ high → 비활성 상태 유지 (중간 영역)
low ≤ metric ≤ mid → 활성화 (메모리 압박 시작)
metric < low → 비활성화 (메모리 극심한 부족 → 다른 메커니즘 동작)
DAMON_RECLAIM 모듈이 사용하는 기본 회수 스킴을 구성합니다.
/* mm/damon/reclaim.c:163-189 */
static struct damos *damon_reclaim_new_scheme(void)
{
struct damos_access_pattern pattern = {
.min_sz_region = PAGE_SIZE, /* 최소 영역: 페이지 크기 */
.max_sz_region = ULONG_MAX, /* 최대 영역: 제한 없음 */
.min_nr_accesses = 0, /* 접근 없음 */
.max_nr_accesses = 0, /* 접근 없음 */
.min_age_region = min_age / /* 최소 나이 (집계 구간 환산) */
damon_reclaim_mon_attrs.aggr_interval,
.max_age_region = UINT_MAX,
};
return damon_new_scheme(
&pattern,
DAMOS_PAGEOUT, /* 콜드 페이지를 스왑아웃 */
0, /* 매 집계 구간마다 적용 */
&damon_reclaim_quota, /* 쿼터 적용 */
&damon_reclaim_wmarks, /* 워터마크 적용 */
NUMA_NO_NODE);
}
kdamond_fn() [kdamond 스레드 메인 루프]
├── kdamond_wait_activation()
│ └── damos_wmark_wait_us() ── 워터마크 기반 대기
├── ops.prepare_access_checks()
│ ├── [vaddr] damon_va_prepare_access_checks()
│ │ └── damon_ptep_mkold() ── PTE young 비트 리셋
│ └── [paddr] damon_pa_prepare_access_checks()
│ └── damon_pa_mkold() ── 페이지 young 비트 리셋
├── ops.check_accesses()
│ ├── [vaddr] damon_va_check_accesses()
│ │ └── damon_va_young() ── PTE dirty/young 확인
│ └── [paddr] damon_pa_check_accesses()
│ └── damon_pa_young() ── 페이지 young 확인
├── kdamond_merge_regions()
│ └── damon_merge_regions_of()
│ └── damon_merge_two_regions() ── 인접 영역 병합
├── kdamond_call() ── 외부 요청 처리 (damon_call)
├── kdamond_apply_schemes()
│ ├── damos_adjust_quota() ── 쿼터 유효 크기 계산
│ └── damon_do_apply_schemes()
│ ├── damos_valid_target() ── 패턴 매칭
│ │ └── __damos_valid_target() ── 크기/빈도/나이 조건
│ └── damos_apply_scheme()
│ ├── damos_core_filter_out() ── 코어 필터 적용
│ ├── ops.apply_scheme() ── 실제 동작 적용
│ │ ├── [paddr] damon_pa_apply_scheme()
│ │ │ └── damos_pa_filter_out() ── 필터 확인
│ │ └── [vaddr] damon_va_apply_scheme()
│ └── damos_update_stat() ── 통계 업데이트
├── kdamond_reset_aggregated() ── 집계 리셋
└── kdamond_split_regions() ── 영역 분할 (정확도 향상)
| 항목 | DAMON_OPS_VADDR | DAMON_OPS_PADDR |
|---|---|---|
| 대상 | 프로세스 가상 주소 공간 | 물리 주소 공간 전체 |
| 식별자 | `target->pid` | `target->pid = NULL` |
| 접근 검사 | PTE young/dirty 비트 | 페이지 young 비트 |
| 영역 초기화 | VMA 기반 3영역 분할 | 전체 물리 메모리 1영역 |
| 필터 지원 | ADDR, TARGET | ANON, MEMCG, ADDR, TARGET, YOUNG, ACTIVE, HUGEPAGE_SIZE, UNMAPPED |
| 대표 사용자 | 프로세스별 프로파일링 | DAMON_RECLAIM, DAMON_LRU_SORT |
| 구현 파일 | `mm/damon/vaddr.c` | `mm/damon/paddr.c` |
| 동작 | 설명 | 사용 예 |
|---|---|---|
| `DAMOS_PAGEOUT` | 콜드 페이지를 스왑으로 회수 | DAMON_RECLAIM |
| `DAMOS_LRU_PRIO` | LRU 리스트에서 우선순위 상향 | 핫 페이지 보호 |
| `DAMOS_LRU_DEPRIO` | LRU 리스트에서 우선순위 하향 | DAMON_LRU_SORT |
| `DAMOS_COLD` | `madvise(MADV_COLD)` 호출 | 콜드 페이지 표시 |
| `DAMOS_HUGEPAGE` | `madvise(MADV_HUGEPAGE)` 호출 | THP 유도 |
| `DAMOS_MIGRATE_HOT` | 핫 영역 마이그레이션 | NUMA 티어링 |
| `DAMOS_STAT` | 통계만 기록 | 프로파일링 |
| 메모리 상태 | metric 범위 | 스킴 동작 |
|---|---|---|
| 메모리 여유 | `metric > high` | 비활성화 |
| 중간 (이전 비활성) | `mid ≤ metric ≤ high` | 유지 (비활성) |
| 중간 (이전 활성) | `low ≤ metric ≤ mid` | 활성화 |
| 메모리 부족 | `metric < low` | 비활성화 |
| 메트릭 | 의미 | 대상 |
|---|---|---|
| `DAMOS_QUOTA_USER_INPUT` | 사용자 지정값 | 커스텀 피드백 |
| `DAMOS_QUOTA_SOME_MEM_PSI_US` | 시스템 수준 일부 메모리 PSI (마이크로초) | 전체 시스템 |
| `DAMOS_QUOTA_NODE_MEM_USED_BP` | 노드별 메모리 사용 비율 | 특정 NUMA 노드 |
| `DAMOS_QUOTA_NODE_MEM_FREE_BP` | 노드별 메모리 여유 비율 | 특정 NUMA 노드 |
| `DAMOS_QUOTA_NODE_MEMCG_USED_BP` | 컨테이너별 메모리 사용 비율 | 특정 memcg |
| `DAMOS_QUOTA_ACTIVE_MEM_BP` | 활성 LRU 메모리 비율 | LRU 밸런스 |
| 항목 | `DAMON_RECLAIM` | `DAMON_LRU_SORT` | 직접 DAMOS |
|---|---|---|---|
| 목적 | 콜드 페이지를 줄여 회수 압박 완화 | LRU 우선순위를 재배치 | 워크로드에 맞춘 정책 자동화 |
| 대표 액션 | `DAMOS_PAGEOUT` | `DAMOS_LRU_DEPRIO` / `DAMOS_LRU_PRIO` | `DAMOS_MIGRATE_HOT/COLD`, `DAMOS_HUGEPAGE` 등 |
| 설정 위치 | `/sys/module/damon_reclaim/parameters/*` | `/sys/module/damon_lru_sort/parameters/*` | `/sys/kernel/mm/damon/admin/kdamonds/*` |
| 함께 보는 지표 | PSI, `pgscan`, `pgsteal` | LRU 균형, 재참조율 | quota, watermarks, stat |
Documentation/mm/damon/