DAMON (Data Access MONitor)
리눅스 커널의 DAMON(Data Access MONitor) 서브시스템을 심층 분석합니다. 리전(region) 기반 적응적 샘플링으로 오버헤드(Overhead)를 최소화하면서 메모리 접근 패턴을 모니터링하고, DAMOS(Data Access-aware Memory Operation Schemes)를 통해 콜드 페이지(Page) 회수, THP 프로모션, LRU 정렬, NUMA 마이그레이션을 자동으로 수행하는 구조를 커널 소스(mm/damon/)와 최신 공식 문서 기준으로 분석합니다. kdamond 커널 스레드(Kernel Thread), 최신 sysfs/admin 인터페이스, intervals_goal 자동 튜닝, DAMON_STAT, damo 유저스페이스 도구, MGLRU 비교, 파라미터 튜닝까지 포괄합니다.
핵심 요약
- 저오버헤드 접근 모니터링 -- PTE 접근 비트 샘플링으로 전체 페이지 스캔 없이 메모리 접근 패턴을 추적합니다. 일반적으로 CPU 오버헤드 1% 미만.
- 적응적 리전 -- 접근 빈도가 비슷한 연속 페이지를 하나의 리전으로 병합하고, 패턴이 달라지면 자동으로 분할합니다. min_nr_regions~max_nr_regions 범위 내에서 동적 조절.
- DAMOS 자동 정책 -- 모니터링 결과에 기반해 pageout, hugepage collapse, LRU 우선순위(Priority) 조정 등의 액션을 자동 수행합니다.
- 다중 Operations Set -- vaddr(가상 주소 공간(Address Space)), fvaddr(고정 가상 주소(Virtual Address)), paddr(물리 주소(Physical Address)) 세 가지 모니터링 대상을 지원합니다.
- sysfs 인터페이스 --
/sys/kernel/mm/damon/을 통해 유저스페이스에서 전체 DAMON 설정을 제어할 수 있습니다.
단계별 이해
- PTE 접근 비트 이해
CPU가 메모리 페이지에 접근하면 하드웨어가 자동으로 PTE의 Access Bit를 설정합니다. DAMON은 이 비트를 주기적으로 확인하고 리셋하여 접근 여부를 감지합니다. - 리전 모델 파악
연속된 주소 범위를 리전으로 묶고, 각 리전의 접근 횟수(nr_accesses)와 나이(age)를 추적하는 구조를 이해합니다. - 샘플링/집계 주기 구분
sampling_interval(샘플링 주기)과 aggr_interval(집계 주기)의 관계와 리전 분할/병합 타이밍을 파악합니다. - DAMOS 스킴 설정
접근 빈도와 나이 조건에 매칭되는 리전에 대해 어떤 액션을 수행할지 정의합니다. - 실전 적용과 모니터링
damo 도구로 워크로드를 프로파일링(Profiling)하고, sysfs로 DAMOS 스킴을 설정하여 메모리 효율을 최적화합니다.
mm/damon/core.c (핵심 모니터링 로직), mm/damon/ops-common.c (공통 연산), mm/damon/vaddr.c (가상 주소 모니터링), mm/damon/paddr.c (물리 주소 모니터링), mm/damon/sysfs.c (sysfs 인터페이스), mm/damon/reclaim.c (프로액티브 회수), mm/damon/lru_sort.c (LRU 정렬), mm/damon/stat.c (단순 통계 모듈).
SeongJae Park이 DAMON의 주요 개발자이며, v5.15에서 커널에 최초 머지되었습니다.
DAMON 개요
DAMON(Data Access MONitor)은 리눅스 커널 v5.15에서 도입된 데이터 접근 모니터링 프레임워크입니다. 기존의 메모리 관리 메커니즘(LRU, kswapd)이 모든 페이지를 균일하게 처리하는 것과 달리, DAMON은 실제 접근 패턴을 정밀하게 추적하여 데이터 기반 메모리 최적화를 가능하게 합니다.
왜 DAMON이 필요한가
| 기존 메커니즘 | 한계 | DAMON의 해결 |
|---|---|---|
| LRU 리스트 | 접근 빈도 구분 불가 (active/inactive 이진 분류) | nr_accesses로 접근 빈도 정량화 |
| kswapd/직접 회수(Direct Reclaim) | 메모리 압박 시에만 동작 (사후 대응) | DAMOS로 사전 예방적 회수 가능 |
| /proc/PID/pagemap | 전체 페이지 테이블 순회 필요 (고비용) | 리전 기반 샘플링으로 O(regions) 비용 |
| perf mem | 하드웨어 PMU 의존, 오프라인 분석 | 커널 내장, 실시간(Real-time) 자동 대응 |
| idle page tracking | 사용자 도구 폴링(Polling) 필요, DAMOS 부재 | 모니터링 + 자동 액션 통합 |
소스 트리 구조
mm/damon/
├── core.c # 핵심: kdamond, 리전 관리, 샘플링/집계 루프
├── ops-common.c # Operations Set 공통 헬퍼
├── vaddr.c # vaddr operations: PTE 접근 비트 기반
├── paddr.c # paddr operations: 물리 주소 기반
├── sysfs.c # /sys/kernel/mm/damon/ 인터페이스
├── sysfs-schemes.c # DAMOS sysfs 스킴 관리
├── dbgfs.c # debugfs 인터페이스 (레거시)
├── reclaim.c # DAMON_RECLAIM 모듈
├── lru_sort.c # DAMON_LRU_SORT 모듈
└── stat.c # DAMON_STAT 모듈
최신 커널 체크포인트
kernel.org에는 stable 6.19.11(2026-04-02 게시)과 mainline 7.0-rc7(2026-04-05 게시)가 올라와 있습니다. 최신 DAMON 문서는 기존의 "리전 기반 샘플링 + DAMOS" 설명을 넘어서, 자동 튜닝된 모니터링 간격, 계층 분리된 필터, 세밀한 스킴 통계, 가중치 기반 다중 마이그레이션 목적지, DAMON_STAT 같은 운영 편의 기능을 공식 인터페이스로 제공합니다.
| 영역 | 최신 공식 문서에서 확인되는 항목 | 실무 의미 |
|---|---|---|
| kdamond 제어 | refresh_ms, update_tuned_intervals, update_schemes_effective_quotas, update_schemes_tried_regions | 주기적 갱신과 원샷 디버깅을 분리하여 자동화 스크립트를 단순화할 수 있습니다. |
| 모니터링 품질 | intervals_goal/access_bp, aggrs, min_sample_us, max_sample_us | 고정 5ms/100ms에 머물지 않고 워크로드별로 샘플링/집계 간격을 자동 조정할 수 있습니다. |
| DAMOS 제어 | quotas/goals/*/target_metric,target_value,current_value,nid,path, effective_bytes | PSI뿐 아니라 NUMA 노드, memcg, active/inactive 비율을 목표로 한 피드백 제어가 가능합니다. |
| 필터링 | core_filters/, ops_filters/, filters/ | 필터 평가 순서를 명시적으로 제어할 수 있고, 신규 구성에서는 core_filters와 ops_filters를 쓰는 편이 안전합니다. |
| 마이그레이션 | dests/nr_dests, 0/id, 0/weight | migrate_hot/migrate_cold 액션에 단일 target_nid가 아니라 여러 목적지를 가중치로 줄 수 있습니다. |
| 관찰 결과 | stats/nr_snapshots,max_nr_snapshots, tried_regions/, DAMON_STAT | 스킴이 실제로 어디까지 시도되었는지와 시스템 전체 유휴 시간 분위수를 더 쉽게 수집할 수 있습니다. |
아키텍처
DAMON은 4계층 아키텍처로 구성됩니다: kdamond(커널 스레드) → context(모니터링 컨텍스트) → target(모니터링 대상) → region(주소 범위). 이 계층 구조가 DAMON의 유연성과 확장성을 제공합니다.
/* include/linux/damon.h - 핵심 자료 구조 관계 (단순화) */
struct damon_region {
unsigned long ar.start; /* 리전 시작 주소 */
unsigned long ar.end; /* 리전 끝 주소 */
unsigned int nr_accesses; /* 집계 주기 내 접근 횟수 */
unsigned int age; /* nr_accesses 불변 지속 집계 횟수 */
struct list_head list; /* target 내 리전 연결 리스트 */
};
struct damon_target {
struct pid *pid; /* vaddr: 모니터링 대상 PID */
unsigned int nr_regions; /* 현재 리전 수 */
struct list_head regions_list; /* damon_region 연결 리스트 */
struct list_head list; /* context 내 target 연결 리스트 */
};
struct damon_ctx {
struct damon_attrs attrs; /* 샘플링/집계/갱신 주기 */
struct damon_operations ops; /* operations set (vaddr/paddr) */
struct list_head adaptive_targets; /* damon_target 리스트 */
struct list_head schemes; /* DAMOS 스킴 리스트 */
struct damon_callback callback; /* 사용자 콜백 */
};
설명
damon_region은 연속된 주소 범위의 접근 패턴을 추적하는 기본 단위입니다.
nr_accesses는 하나의 집계 주기(aggr_interval) 동안 접근이 감지된 샘플링 횟수이며,
age는 nr_accesses 값이 변하지 않은 채 지속된 집계 주기 수입니다.
damon_target은 프로세스(Process)(vaddr) 또는 물리 주소 범위(paddr)를 나타내며, 내부에 리전 리스트를 유지합니다.
damon_ctx가 모니터링의 전체 설정(Operations Set, DAMOS 스킴, 콜백(Callback))을 보유합니다.
리전 기반 적응적 샘플링
DAMON의 핵심 혁신은 리전 기반 적응적 샘플링(Region-based Adaptive Sampling)입니다. 모든 페이지를 순회하는 대신, 각 리전에서 하나의 랜덤 페이지만 샘플링하여 접근 여부를 확인합니다.
샘플링 알고리즘
DAMON의 모니터링 루프는 세 가지 시간 단위로 동작합니다:
| 파라미터 | 기본값 | 역할 |
|---|---|---|
sample_interval | 5ms | 각 리전에서 랜덤 페이지 1개의 PTE 접근 비트 확인 주기 |
aggr_interval | 100ms | nr_accesses 집계 및 DAMOS 스킴 적용 주기 |
update_interval | 60s | 모니터링 대상 리전 초기화/갱신 주기 (VMA 변경 반영) |
리전 분할과 병합
DAMON은 각 aggr_interval 종료 시 리전을 동적으로 분할/병합하여 정밀도를 자동 조절합니다:
- 분할(Split) -- 리전 크기가 크고 내부 접근 패턴이 불균일하면 2~3개로 분할합니다. 새 리전의 nr_accesses는 부모의 값을 상속받습니다.
- 병합(Merge) -- 인접한 두 리전의 nr_accesses 차이가
max_nr_regions기반 임계값 이하이면 병합합니다.
/* mm/damon/core.c - damon_merge_regions_of() (단순화) */
static void damon_merge_regions_of(
struct damon_target *t,
unsigned int threshold, /* nr_accesses 차이 임계값 */
unsigned long sz_limit) /* 병합 후 최대 크기 */
{
struct damon_region *r, *prev = NULL;
damon_for_each_region(r, t) {
if (prev && prev->ar.end == r->ar.start &&
abs(prev->nr_accesses - r->nr_accesses) <= threshold &&
damon_sz_region(prev) + damon_sz_region(r) <= sz_limit) {
prev->ar.end = r->ar.end;
prev->nr_accesses =
(prev->nr_accesses + r->nr_accesses) / 2;
damon_destroy_region(r, t);
} else {
prev = r;
}
}
}
설명
인접한 두 리전의nr_accesses 차이가 임계값 이하이고, 병합 후 크기가 제한 이내이면 하나로 합칩니다.
이 과정으로 접근 패턴이 균일한 대규모 영역은 하나의 리전으로 합쳐져 오버헤드가 줄고,
패턴이 변하는 경계 지점은 분할되어 정밀도가 높아집니다.
샘플링 오버헤드 분석
| 파라미터 | 워크로드 300MB RSS | 일반 페이지 스캔 | DAMON 리전 샘플링 |
|---|---|---|---|
| 모니터링 대상 페이지 수 | 76,800 | 76,800 PTE 확인 | ~100 리전 × 1 PTE = ~100 |
| 샘플링 당 비용 | - | O(페이지 수) | O(리전 수) |
| CPU 오버헤드 | - | ~5-10% | <1% |
| 정밀도 | - | 정확 | 통계적 추정 (95%+) |
min_nr_regions=10, max_nr_regions=1000이 기본값입니다. 리전 수가 많을수록 정밀하지만 오버헤드가 증가합니다. 대부분의 워크로드에서 기본값이 적절하며, 매우 큰 워크로드(수십 GB RSS)에서는 max_nr_regions=2000까지 늘릴 수 있습니다.
Operations Set
DAMON은 Operations Set 추상화를 통해 다양한 주소 공간 타입을 지원합니다. 각 Operations Set은 리전 초기화, 접근 비트 확인, DAMOS 액션 실행 등의 콜백을 구현합니다.
| Operations Set | 대상 | 접근 감지 방식 | 용도 |
|---|---|---|---|
vaddr | 프로세스 가상 주소 공간 | PTE Access Bit | 특정 프로세스의 메모리 접근 패턴 분석 |
fvaddr | 고정 가상 주소 범위 | PTE Access Bit | 사용자 지정 주소 범위 모니터링 |
paddr | 물리 주소 공간 | rmap을 통한 PTE 확인 | 시스템 전체 메모리 패턴, DAMON_RECLAIM |
/* include/linux/damon.h - Operations Set 인터페이스 */
struct damon_operations {
enum damon_ops_id id;
/* 모니터링 대상 리전 초기화 */
void (*init)(struct damon_ctx *ctx);
/* 모니터링 대상 갱신 (VMA 변경 반영) */
void (*update)(struct damon_ctx *ctx);
/* 리전의 접근 준비 (Access Bit 클리어) */
void (*prepare_access_checks)(struct damon_ctx *ctx);
/* 리전의 접근 확인 (Access Bit 읽기) */
unsigned int (*check_accesses)(struct damon_ctx *ctx);
/* 리셋 (집계 완료 후) */
void (*reset_aggregated)(struct damon_ctx *ctx);
/* DAMOS 액션 적용 (pageout, hugepage 등) */
unsigned long (*apply_scheme)(struct damon_ctx *ctx,
struct damon_target *t,
struct damon_region *r,
struct damos *s);
/* 스킴 스코어 계산 (우선순위 결정) */
int (*get_scheme_score)(struct damos *s,
struct damon_region *r);
};
설명
damon_operations는 Strategy 패턴으로 구현되어, 동일한 모니터링 로직에 다른 주소 공간 구현을 끼워넣을 수 있습니다.
prepare_access_checks()에서 PTE 접근 비트를 클리어하고, 다음 check_accesses()에서 비트가 다시 설정되었는지 확인합니다.
vaddr은 mm_struct의 페이지 테이블을 직접 워크하고, paddr은 folio_get_anon_vma() + rmap_walk()로 물리 페이지의 매핑(Mapping)을 역추적(Backtrace)합니다.
vaddr 동작 상세
/* mm/damon/vaddr.c - PTE Access Bit 확인 (단순화) */
static void damon_va_check_access(
struct damon_ctx *ctx,
struct damon_target *t,
struct damon_region *r,
struct mm_struct *mm)
{
unsigned long addr;
pte_t *pte;
/* 리전 내 랜덤 주소 선택 */
addr = r->sampling_addr;
/* 페이지 테이블 워크로 PTE 획득 */
pte = damon_get_pte(mm, addr);
if (!pte)
return;
/* Access Bit 확인 */
if (pte_young(*pte)) {
r->nr_accesses++;
/* Access Bit 클리어 (다음 확인을 위해) */
pte_mkold(*pte);
}
}
paddr 동작 상세
paddr Operations Set은 물리 주소 공간을 직접 모니터링합니다. 물리 페이지에 대한 접근 여부를 확인하려면 rmap(reverse mapping)을 통해 해당 물리 페이지를 매핑하는 모든 PTE를 역추적해야 합니다.
/* mm/damon/paddr.c - 물리 주소 접근 확인 (단순화) */
static bool damon_pa_young(unsigned long paddr,
unsigned long *folio_sz)
{
struct folio *folio;
bool young = false;
folio = damon_get_folio(PHYS_PFN(paddr));
if (!folio)
return false;
*folio_sz = folio_size(folio);
/* 1. folio 자체의 reference bit 확인 */
if (folio_test_young(folio) ||
!folio_test_idle(folio)) {
young = true;
goto out;
}
/* 2. rmap으로 모든 매핑의 PTE 확인 */
if (folio_mapped(folio)) {
struct damon_young_walk_private pvt = {
.young = false,
.folio_sz = folio_sz,
};
rmap_walk(folio, &damon_young_rwc);
young = pvt.young;
}
out:
folio_put(folio);
return young;
}
설명
paddr ops에서 접근 확인은 vaddr보다 복잡합니다.
물리 페이지가 여러 프로세스에 공유 매핑되어 있을 수 있으므로, rmap_walk()으로 모든 역방향 매핑의 PTE를 검사해야 합니다.
하나라도 Access Bit가 설정되어 있으면 해당 물리 페이지는 "accessed"로 판정합니다.
이 과정이 비용이 더 들지만, 시스템 전체의 물리 메모리(Physical Memory) 접근 패턴을 파악할 수 있는 장점이 있습니다.
DAMON_RECLAIM이 paddr ops를 사용하는 이유가 바로 이것입니다.
fvaddr Operations Set
fvaddr(fixed virtual address)는 vaddr의 변형으로, VMA 변경에 따른 리전 재초기화를 하지 않습니다. 사용자가 지정한 고정 주소 범위만 모니터링하므로, 특정 메모리 영역(예: 공유 메모리 세그먼트, mmap 파일)의 접근 패턴을 추적할 때 유용합니다.
# fvaddr 사용 예시: 특정 mmap 영역만 모니터링
echo fvaddr > .../contexts/0/operations
# 대상 프로세스 설정
echo 1 > .../contexts/0/targets/nr_targets
echo $(pidof my-app) > .../contexts/0/targets/0/pid_target
# 초기 리전 수동 설정 (mmap 영역 주소)
echo 1 > .../contexts/0/targets/0/regions/nr_regions
echo 0x7f0000000000 > .../contexts/0/targets/0/regions/0/start
echo 0x7f0100000000 > .../contexts/0/targets/0/regions/0/end
콜백 기반 모니터링
DAMON은 모니터링 루프의 주요 지점에서 콜백 함수를 호출할 수 있습니다. 이를 통해 커널 내부 모듈이나 BPF 프로그램이 DAMON의 모니터링 데이터를 활용할 수 있습니다.
/* include/linux/damon.h - 콜백 구조체 */
struct damon_callback {
void *private;
/* 각 샘플링 주기 후 호출 */
int (*after_wmarks_check)(struct damon_ctx *ctx);
/* 각 집계 주기 후 호출 */
int (*after_aggregation)(struct damon_ctx *ctx);
/* kdamond 종료 전 호출 */
void (*before_terminate)(struct damon_ctx *ctx);
};
설명
after_aggregation은 가장 자주 사용되는 콜백입니다. 이 시점에서 모든 리전의 nr_accesses가 확정되어 있으므로 접근 패턴 분석에 적합합니다.
콜백이 0이 아닌 값을 반환하면 kdamond가 모니터링을 중단합니다. 이를 통해 특정 조건(예: 충분한 데이터 수집)에서 자동 종료가 가능합니다.
DAMON_RECLAIM과 DAMON_LRU_SORT는 이 콜백 메커니즘을 사용하지 않고 DAMOS를 직접 활용합니다.
| 콜백 | 호출 시점 | 활용 예시 |
|---|---|---|
after_wmarks_check | 워터마크(Watermark) 확인 후 | 모니터링 시작/중지 제어 |
after_aggregation | 집계 완료 후 | 접근 패턴 기록, 히트맵 생성, BPF 프로그램 |
before_terminate | kdamond 종료 전 | 리소스 정리, 최종 통계 출력 |
DAMOS (Data Access-aware Memory Operation Schemes)
DAMOS는 DAMON의 모니터링 결과를 자동으로 메모리 관리 액션에 연결하는 프레임워크입니다. "접근 빈도가 N 이하이고 age가 M 이상인 리전에 대해 pageout을 수행하라"와 같은 선언적 정책을 정의할 수 있습니다.
/* include/linux/damon.h - DAMOS 스킴 구조체 (단순화) */
struct damos {
/* 패턴 매칭 조건 */
struct damos_access_pattern pattern; /* min/max nr_accesses, age, size */
/* 수행할 액션 */
enum damos_action action; /* DAMOS_PAGEOUT, DAMOS_HUGEPAGE 등 */
/* 적용 제한 */
struct damos_quota quota; /* time/size 쿼터 */
/* 활성화 조건 */
struct damos_watermarks wmarks; /* free memory 기반 활성화 */
/* 필터 리스트 */
struct list_head filters; /* damos_filter 연결 리스트 */
/* 통계 */
struct damos_stat stat; /* 적용 횟수/크기 */
};
DAMOS 액션
DAMOS가 지원하는 메모리 관리 액션은 다음과 같습니다:
| 액션 | 커널 버전 | 설명 | 주요 용도 |
|---|---|---|---|
DAMOS_PAGEOUT | v5.16+ | 매칭된 리전의 페이지를 스왑(Swap)/디스크로 방출 | 콜드 페이지 프로액티브 회수 |
DAMOS_HUGEPAGE | v5.16+ | 매칭된 리전을 THP로 프로모션 (khugepaged 힌트) | 핫 페이지 THP 통합 |
DAMOS_NOHUGEPAGE | v5.16+ | 매칭된 리전의 THP 프로모션 억제 | 콜드/랜덤 접근 영역 THP 방지 |
DAMOS_LRU_PRIO | v6.0+ | LRU 리스트에서 우선순위 상향 (활성화) | 핫 페이지 보호 |
DAMOS_LRU_DEPRIO | v6.0+ | LRU 리스트에서 우선순위 하향 (비활성화) | 콜드 페이지 빠른 회수 대상화 |
DAMOS_STAT | v5.16+ | 통계만 수집, 실제 액션 없음 | 모니터링 전용, 정책 시뮬레이션 |
DAMOS_MIGRATE_HOT | v6.9+ | 핫 페이지를 빠른 NUMA 노드로 이동 | NUMA 티어링 프로모션 |
DAMOS_MIGRATE_COLD | v6.9+ | 콜드 페이지를 느린 NUMA 노드로 이동 | NUMA 티어링 디모션 |
/* mm/damon/paddr.c - pageout 액션 구현 (단순화) */
static unsigned long damon_pa_pageout(
struct damon_region *r)
{
unsigned long addr, applied = 0;
for (addr = r->ar.start; addr < r->ar.end; addr += PAGE_SIZE) {
struct folio *folio = damon_get_folio(PHYS_PFN(addr));
if (!folio)
continue;
/* 이미 회수 불가능하면 스킵 */
if (folio_test_unevictable(folio))
goto next;
/* 회수 리스트에 추가 */
list_add(&folio->lru, &folio_list);
applied += folio_nr_pages(folio);
next:
folio_put(folio);
}
/* 일괄 회수 수행 */
reclaim_pages(&folio_list);
return applied * PAGE_SIZE;
}
설명
DAMOS_PAGEOUT은 매칭된 리전의 모든 페이지를 순회하며 reclaim_pages()를 호출합니다.
이때 folio_test_unevictable()로 mlock된 페이지를 건너뛰고, anonymous 페이지는 스왑으로, file-backed 페이지는 디스크로 방출합니다.
paddr Operations Set에서는 물리 주소를 직접 사용하므로 rmap 역추적이 필요하지만, vaddr에서는 VMA를 통해 직접 처리합니다.
DAMOS 필터
DAMOS 필터는 패턴 매칭을 통과한 리전 중에서 특정 조건의 페이지를 추가로 포함하거나 제외합니다. 최신 공식 문서 기준으로 필터는 core_filters와 ops_filters 두 계층으로 나뉘며, 공통 계층 필터가 먼저 평가되고 그 다음에 Operations Set별 필터가 평가됩니다.
| 필터 계층 | 필터 타입 | 설명 | 예시 용도 |
|---|---|---|---|
core_filters | addr | 주소 범위 기준 포함/제외 | 힙, mmap 구간, 특정 PFN 범위만 타겟팅 |
core_filters | target | 특정 모니터링 대상 인덱스만 포함/제외 | 다중 프로세스 중 하나만 정책 적용 |
ops_filters | anon, active | 익명 페이지(Anonymous Page), active LRU 기준 포함/제외 | 파일 캐시는 남기고 anonymous만 회수, active 페이지 보호 |
ops_filters | memcg | 특정 memcg 경로의 페이지만 포함/제외 | 특정 컨테이너(Container)의 메모리만 최적화 |
ops_filters | young, unmapped | 최근 접근 여부, 매핑 여부 기준 포함/제외 | 아직 따뜻한 페이지와 매핑 끊긴 페이지를 분리 |
ops_filters | hugepage_size | Huge Page 크기 범위 기준 필터링 | 2MB THP만 디모션하거나 1GB HugeTLB는 건너뛰기 |
/* include/linux/damon.h - 필터 구조체 (단순화) */
struct damos_filter {
enum damos_filter_type type;
bool matching; /* 필터 조건이 일치해야 하는지 */
bool allow; /* true: 허용, false: 제외 */
union {
unsigned short memcg_id;
struct damon_addr_range addr_range;
int target_idx;
struct {
unsigned long min;
unsigned long max;
} hugepage_sz;
};
struct list_head list;
};
설명
matching은 "필터가 어떤 조건을 검사할지"를, allow는 "매칭 시 허용할지 제외할지"를 표현합니다.
평가 순서는 core_filters → ops_filters이며, 각 그룹 안에서는 첫 번째 일치 필터가 결과를 결정합니다.
아무 필터에도 일치하지 않았을 때의 기본 동작은 마지막 필터의 allow 값에 따라 달라지므로, "항상 기본 포함"이라고 가정하면 안 됩니다.
# 최신 sysfs 방식: core_filters와 ops_filters를 분리해서 설정
echo 1 > .../schemes/0/core_filters/nr_filters
echo target > .../schemes/0/core_filters/0/type
echo Y > .../schemes/0/core_filters/0/matching
echo Y > .../schemes/0/core_filters/0/allow
echo 0 > .../schemes/0/core_filters/0/target_idx
echo 1 > .../schemes/0/ops_filters/nr_filters
echo memcg > .../schemes/0/ops_filters/0/type
echo Y > .../schemes/0/ops_filters/0/matching
echo N > .../schemes/0/ops_filters/0/allow
echo /kubepods.slice > .../schemes/0/ops_filters/0/memcg_path
filters 디렉토리도 존재하지만, 최신 설계 문서는 core_filters와 ops_filters를 분리해 쓰는 편이 평가 순서를 분명히 한다고 설명합니다. 신규 정책은 가능하면 이 두 디렉토리로 작성하세요.
DAMOS 쿼터와 우선순위
DAMOS 쿼터는 스킴이 한 번의 집계 주기에서 소비할 수 있는 시간과 크기를 제한합니다. 쿼터가 초과되면 나머지 리전은 다음 주기로 연기됩니다. 이때 우선순위(prioritization)가 어떤 리전을 먼저 처리할지 결정합니다.
/* include/linux/damon.h - 쿼터 구조체 */
struct damos_quota {
unsigned long ms; /* 시간 제한 (밀리초, 0=무제한) */
unsigned long sz; /* 크기 제한 (바이트, 0=무제한) */
unsigned long reset_interval_ms; /* 쿼터 리셋 주기 */
/* 우선순위 가중치 */
unsigned int weight_sz; /* 리전 크기 가중치 */
unsigned int weight_nr_accesses; /* 접근 횟수 가중치 */
unsigned int weight_age; /* 나이 가중치 */
/* 자동 튜닝 목표 */
struct damos_quota_goal *goals; /* v6.8+: 자동 쿼터 조절 */
/* 내부 상태 */
unsigned long charged_sz;
unsigned long charged_from;
unsigned long esz; /* 유효 쿼터 크기 */
};
damos_quota_goal을 설정하면 DAMON이 목표 메트릭(예: PSI some 값 5% 이하)을 달성하도록 쿼터를 자동으로 조절합니다. 메모리 압박이 심하면 쿼터를 늘리고, 안정화되면 줄이는 피드백 루프입니다.
sysfs 인터페이스
DAMON의 주요 인터페이스는 /sys/kernel/mm/damon/admin/ 디렉토리입니다. 커널 v5.18에서 도입되어 debugfs 인터페이스를 대체합니다.
/sys/kernel/mm/damon/admin/
├── kdamonds/
│ ├── nr_kdamonds # kdamond 스레드 수 설정
│ └── 0/
│ ├── state # on/off/commit/update_* 명령
│ ├── pid # kdamond 스레드 PID (읽기 전용(Read-Only))
│ ├── refresh_ms # 주기적 자동 갱신 주기
│ └── contexts/
│ ├── nr_contexts
│ └── 0/
│ ├── avail_operations # 현재 커널이 지원하는 ops 목록
│ ├── operations # vaddr/fvaddr/paddr
│ ├── addr_unit # paddr 계열 주소 단위
│ ├── monitoring_attrs/
│ │ ├── intervals/
│ │ │ ├── sample_us # 샘플링 주기 (마이크로초)
│ │ │ ├── aggr_us # 집계 주기
│ │ │ ├── update_us # 갱신 주기
│ │ │ └── intervals_goal/
│ │ │ ├── access_bp
│ │ │ ├── aggrs
│ │ │ ├── min_sample_us
│ │ │ └── max_sample_us
│ │ └── nr_regions/
│ │ ├── min # 최소 리전 수
│ │ └── max # 최대 리전 수
│ ├── targets/
│ │ ├── nr_targets
│ │ └── 0/
│ │ ├── pid_target # 대상 PID (vaddr)
│ │ ├── obsolete_target
│ │ └── regions/ # 초기 리전 (선택)
│ └── schemes/
│ ├── nr_schemes
│ └── 0/
│ ├── action # pageout/hugepage/...
│ ├── apply_interval_us
│ ├── target_nid
│ ├── access_pattern/
│ ├── quotas/ # goals/, effective_bytes 포함
│ ├── watermarks/
│ ├── core_filters/
│ ├── ops_filters/
│ ├── filters/
│ ├── dests/
│ ├── stats/ # nr_snapshots 포함
│ └── tried_regions/
sysfs 설정 워크플로
# 1. kdamond 생성
echo 1 > /sys/kernel/mm/damon/admin/kdamonds/nr_kdamonds
# 2. context 생성 및 operations 설정
echo 1 > /sys/kernel/mm/damon/admin/kdamonds/0/contexts/nr_contexts
echo vaddr > /sys/kernel/mm/damon/admin/kdamonds/0/contexts/0/operations
# 3. 모니터링 파라미터 설정
echo 5000 > .../contexts/0/monitoring_attrs/intervals/sample_us # 5ms
echo 100000 > .../contexts/0/monitoring_attrs/intervals/aggr_us # 100ms
echo 60000000 > .../contexts/0/monitoring_attrs/intervals/update_us # 60s
echo 4 > .../contexts/0/monitoring_attrs/intervals/intervals_goal/access_bp
echo 20 > .../contexts/0/monitoring_attrs/intervals/intervals_goal/aggrs
echo 5000 > .../contexts/0/monitoring_attrs/intervals/intervals_goal/min_sample_us
echo 200000 > .../contexts/0/monitoring_attrs/intervals/intervals_goal/max_sample_us
# 4. 모니터링 대상 설정
echo 1 > .../contexts/0/targets/nr_targets
echo $(pidof my-app) > .../contexts/0/targets/0/pid_target
# 5. DAMOS 스킴 설정 (콜드 페이지 회수)
echo 1 > .../contexts/0/schemes/nr_schemes
echo pageout > .../contexts/0/schemes/0/action
echo 0 > .../contexts/0/schemes/0/access_pattern/nr_accesses/min
echo 0 > .../contexts/0/schemes/0/access_pattern/nr_accesses/max
echo 5 > .../contexts/0/schemes/0/access_pattern/age/min
# 6. 커밋 및 시작
echo commit > /sys/kernel/mm/damon/admin/kdamonds/0/state
echo on > /sys/kernel/mm/damon/admin/kdamonds/0/state
echo 1000 > /sys/kernel/mm/damon/admin/kdamonds/0/refresh_ms # 1초마다 자동 갱신
# 7. 통계 확인
echo update_schemes_stats > /sys/kernel/mm/damon/admin/kdamonds/0/state
echo update_schemes_effective_quotas > /sys/kernel/mm/damon/admin/kdamonds/0/state
echo update_schemes_tried_regions > /sys/kernel/mm/damon/admin/kdamonds/0/state
cat .../contexts/0/schemes/0/stats/nr_applied
cat .../contexts/0/schemes/0/stats/sz_applied
cat .../contexts/0/schemes/0/quotas/effective_bytes
cat .../contexts/0/schemes/0/tried_regions/total_bytes
설명
sysfs 인터페이스는 DAMON의 전체 설정을 디렉토리 계층 구조로 노출합니다.state 파일에 commit을 쓰면 현재 sysfs 설정이 실제 DAMON 설정에 반영되고,
on을 쓰면 kdamond 스레드(Thread)가 시작됩니다.
update_schemes_stats, update_schemes_effective_quotas, update_schemes_tried_regions, update_tuned_intervals 같은 명령으로 최신 상태를 개별적으로 갱신할 수 있습니다.
최신 sysfs에서 놓치기 쉬운 확장점
| 경로 | 의미 | 운영 팁 |
|---|---|---|
kdamonds/0/refresh_ms | 통계, tuned interval, tried regions를 주기적으로 갱신합니다. | 관찰 전용 서버에서는 1000~5000ms 정도로 두면 폴링 스크립트를 단순화할 수 있습니다. |
contexts/0/avail_operations | 현재 커널이 노출하는 Operations Set 목록입니다. | 배포판별로 fvaddr 지원 여부가 다를 수 있으므로 스크립트에서 먼저 확인하는 편이 안전합니다. |
contexts/0/addr_unit | 주소를 페이지 단위로 쓸지 바이트 단위로 쓸지 표현합니다. | paddr 자동화에서 단위를 잘못 가정하면 target/dests 계산이 어긋날 수 있습니다. |
.../intervals/intervals_goal/* | 목표 접근 비율에 맞춰 sample/aggr 간격을 자동 조정합니다. | 워크로드가 들쭉날쭉한 서버에서는 고정 interval보다 이쪽이 더 안정적입니다. |
.../schemes/0/core_filters, ops_filters | 필터를 공통 계층과 ops 계층으로 분리합니다. | 신규 구성에서는 통합 filters보다 이 두 디렉토리를 우선 사용하세요. |
.../schemes/0/dests | 복수 마이그레이션 목적지를 가중치로 지정합니다. | CXL, DRAM, PMEM이 섞인 환경에서 단일 target_nid보다 유연합니다. |
.../schemes/0/stats/nr_snapshots | 스킴 통계 스냅샷 보존 개수를 확인합니다. | 장시간 실험에서는 max_nr_snapshots와 함께 봐야 오래된 통계가 잘리는 시점을 알 수 있습니다. |
.../schemes/0/tried_regions | 최근 시도된 리전의 주소, 접근 횟수, 나이, 필터 통과 크기를 노출합니다. | 왜 어떤 리전이 실제 pageout 대상이 되었는지 역추적할 때 유용합니다. |
debugfs 인터페이스 (레거시)
초기 DAMON은 /sys/kernel/debug/damon/ debugfs 인터페이스를 사용했습니다. 최신 공식 문서는 debugfs를 deprecated로 분류하며, 신규 스크립트와 운영 자동화는 sysfs 인터페이스로 작성할 것을 권고합니다.
# debugfs 인터페이스 (레거시 - 신규 사용 비권장)
/sys/kernel/debug/damon/
├── attrs # "sample_us aggr_us update_us min_nr max_nr" 형식
├── target_ids # 대상 PID 목록 (공백 구분)
├── schemes # DAMOS 스킴 (한 줄에 하나씩)
└── monitor_on # "on"/"off"
# 예시: debugfs로 모니터링 시작
echo "5000 100000 60000000 10 1000" > /sys/kernel/debug/damon/attrs
echo "$(pidof my-app)" > /sys/kernel/debug/damon/target_ids
echo "on" > /sys/kernel/debug/damon/monitor_on
/sys/kernel/mm/damon/admin/)를 사용하세요.
debugfs에서 sysfs로 마이그레이션
기존 debugfs 기반 설정을 sysfs로 마이그레이션할 때 참고하는 대응표입니다. debugfs는 하나의 파일에 여러 값을 공백으로 구분하여 쓰는 반면, sysfs는 디렉토리 계층으로 각 값을 개별 파일에 저장합니다.
| debugfs 경로/형식 | sysfs 경로 | 비고 |
|---|---|---|
attrs (5개 값 한 줄) | contexts/0/monitoring_attrs/intervals/ + nr_regions/ | sample_us, aggr_us, update_us, min, max 각각 분리 |
target_ids (PID 목록) | contexts/0/targets/0/pid_target | sysfs는 타겟별 디렉토리 분리 |
schemes (한 줄 스킴) | contexts/0/schemes/0/ 하위 | action, access_pattern, quotas, watermarks, filters 각각 분리 |
monitor_on | kdamonds/0/state | on/off 대신 on/off/commit/update_schemes_stats |
# [기존] debugfs 방식 - 단일 파일에 모든 파라미터
echo "5000 100000 60000000 10 1000" > /sys/kernel/debug/damon/attrs
echo "$(pidof my-app)" > /sys/kernel/debug/damon/target_ids
echo "on" > /sys/kernel/debug/damon/monitor_on
# [신규] sysfs 방식 - 계층적 디렉토리 구조
DAMON_BASE="/sys/kernel/mm/damon/admin/kdamonds/0"
CTX="${DAMON_BASE}/contexts/0"
# 모니터링 파라미터 (attrs의 5개 값을 개별 파일로)
echo 5000 > ${CTX}/monitoring_attrs/intervals/sample_us
echo 100000 > ${CTX}/monitoring_attrs/intervals/aggr_us
echo 60000000 > ${CTX}/monitoring_attrs/intervals/update_us
echo 10 > ${CTX}/monitoring_attrs/nr_regions/min
echo 1000 > ${CTX}/monitoring_attrs/nr_regions/max
# 타겟 PID 설정
echo 1 > ${CTX}/targets/nr_targets
echo $(pidof my-app) > ${CTX}/targets/0/pid_target
# 설정 반영 후 시작
echo commit > ${DAMON_BASE}/state
echo on > ${DAMON_BASE}/state
설명
debugfs의attrs 파일은 "sample_us aggr_us update_us min_nr max_nr" 5개 값을 한 줄에 작성하는 방식이었습니다.
sysfs에서는 이 값들이 monitoring_attrs/intervals/와 monitoring_attrs/nr_regions/ 하위에 각각 독립 파일로 분리되어 개별적으로 읽고 쓸 수 있습니다.
monitor_on에 해당하는 state 파일은 commit(설정 반영), on(시작), off(중지), update_schemes_stats(통계 갱신) 등 다양한 명령을 지원합니다.
sysfs 인터페이스는 debugfs보다 파일 수가 많지만, 원자적(atomic) 설정 변경과 통계 조회가 가능하다는 장점이 있습니다.
CONFIG_DAMON_DBGFS=y일 때만 사용 가능하며, 최신 문서에서도 deprecated 상태로 표시됩니다.
(2) debugfs schemes 파일은 한 줄에 모든 스킴 파라미터를 공백으로 나열하므로 필터, 쿼터 목표 등 최신 기능을 설정할 수 없습니다.
(3) debugfs와 sysfs를 동시에 사용하면 설정이 충돌할 수 있으므로, 반드시 하나의 인터페이스만 사용해야 합니다.
(4) 기존 debugfs 기반 스크립트는 위의 대응표를 참고하여 sysfs 기반으로 순차적으로 전환하세요.
DAMON 기반 프로액티브 회수
DAMON_RECLAIM은 DAMON의 가장 대표적인 활용 사례로, 콜드 페이지를 메모리 압박 없이 사전에 회수합니다. 기존 kswapd가 워터마크 기반으로 사후 대응하는 것과 달리, DAMON_RECLAIM은 접근 패턴을 분석하여 실제로 사용되지 않는 페이지만 선별적으로 회수합니다.
# DAMON_RECLAIM 활성화
echo Y > /sys/module/damon_reclaim/parameters/enabled
# 기본 파라미터 확인
cat /sys/module/damon_reclaim/parameters/sample_interval # 5000 (us)
cat /sys/module/damon_reclaim/parameters/aggr_interval # 100000 (us)
cat /sys/module/damon_reclaim/parameters/min_age # 200
# 서버 환경: 콜드 임계값을 낮추고 쿼터를 넉넉하게
echo 100 > /sys/module/damon_reclaim/parameters/min_age # 10초 미접근 시 콜드
echo 20 > /sys/module/damon_reclaim/parameters/quota_ms # 20ms/주기 처리
echo 256000000 > /sys/module/damon_reclaim/parameters/quota_sz # 256MB/주기
# 모니터링 통계 확인
cat /sys/module/damon_reclaim/parameters/nr_reclaimed # 회수된 페이지 수
cat /sys/module/damon_reclaim/parameters/bytes_reclaimed # 회수된 바이트 수
DAMON LRU Sort
DAMON_LRU_SORT(v6.0+)는 LRU 리스트의 페이지 순서를 DAMON의 접근 패턴 데이터 기반으로 재정렬합니다. 기존 LRU는 페이지 폴트(Page Fault)와 참조 시점에만 순서가 갱신되지만, DAMON_LRU_SORT는 실제 접근 빈도를 반영하여 보다 정확한 LRU 순서를 유지합니다.
| 동작 | 조건 | LRU 효과 |
|---|---|---|
| 핫 페이지 프로모션 | nr_accesses > hot_threshold | LRU active 리스트 head로 이동 |
| 콜드 페이지 디모션 | nr_accesses < cold_threshold, age > min_age | LRU inactive 리스트 tail로 이동 |
# DAMON_LRU_SORT 활성화
echo Y > /sys/module/damon_lru_sort/parameters/enabled
# 핫/콜드 임계값 설정
echo 5 > /sys/module/damon_lru_sort/parameters/hot_thres_access_freq # 접근 빈도 5 이상이면 핫
echo 200 > /sys/module/damon_lru_sort/parameters/cold_min_age # 200 주기 이상 미접근이면 콜드
# 쿼터 설정 (LRU 재정렬 비용 제한)
echo 10 > /sys/module/damon_lru_sort/parameters/quota_ms
# 통계 확인
cat /sys/module/damon_lru_sort/parameters/nr_lru_sort_hot # 핫 프로모션 횟수
cat /sys/module/damon_lru_sort/parameters/nr_lru_sort_cold # 콜드 디모션 횟수
damo 유저스페이스 도구
damo는 DAMON의 공식 유저스페이스 CLI 도구입니다. Python으로 작성되어 있으며, 모니터링 설정, 데이터 수집, 시각화, DAMOS 테스트를 간편하게 수행할 수 있습니다.
# 설치
pip3 install damo
# 프로세스 모니터링 (vaddr)
sudo damo start $(pidof my-app)
# 시스템 전체 모니터링 (paddr)
sudo damo start --ops paddr
# 접근 패턴 기록 (10초)
sudo damo record $(pidof my-app) --duration 10s -o access.json
# 히트맵 시각화
sudo damo report heats --input access.json
# 워킹셋 크기 분석
sudo damo report wss --input access.json
# 출력 예시:
# percentile working_set_size
# 0 23.4MB
# 25 45.2MB
# 50 67.8MB
# 75 89.3MB
# 100 156.7MB
# 핫/콜드 분포 리포트
sudo damo report nr_regions --input access.json
# DAMOS 시뮬레이션 (실제 액션 없이 예상 결과만 확인)
sudo damo schemes --action stat \
--access_rate 0 0 \
--age 200 max \
$(pidof my-app)
# DAMOS 적용
sudo damo schemes --action pageout \
--access_rate 0 0 \
--age 200 max \
--quotas 10ms 128M \
$(pidof my-app)
설명
damo record는 DAMON 모니터링을 시작하고 접근 패턴 데이터를 JSON 파일로 저장합니다.
damo report heats는 주소 범위 × 시간 축의 히트맵을 터미널에 출력하여 핫/콜드 영역을 시각적으로 확인할 수 있습니다.
damo report wss는 워킹셋(실제 사용 중인 메모리) 크기의 분포를 보여줍니다.
damo schemes --action stat은 실제 회수 없이 매칭되는 리전의 크기만 확인하여 정책을 사전 검증할 수 있습니다.
실전 활용 사례
서버 환경: 대규모 메모리 최적화
# 시나리오: 256GB RAM 서버, memcached + MySQL 혼합 워크로드
# 목표: 사용되지 않는 memcached 캐시 페이지를 사전 회수
# DAMON_RECLAIM 설정 (물리 주소 전체 모니터링)
echo 5000 > /sys/module/damon_reclaim/parameters/sample_interval
echo 200000 > /sys/module/damon_reclaim/parameters/aggr_interval
echo 300 > /sys/module/damon_reclaim/parameters/min_age # 60초 콜드 기준
echo 50 > /sys/module/damon_reclaim/parameters/quota_ms
echo 512000000 > /sys/module/damon_reclaim/parameters/quota_sz # 512MB/주기
echo Y > /sys/module/damon_reclaim/parameters/enabled
모바일/임베디드 환경
# 시나리오: 8GB RAM 스마트폰, 메모리 제약 환경
# 목표: 백그라운드 앱의 콜드 페이지 적극 회수
# 짧은 집계 주기 + 낮은 콜드 기준
echo 5000 > /sys/module/damon_reclaim/parameters/sample_interval
echo 50000 > /sys/module/damon_reclaim/parameters/aggr_interval # 50ms
echo 100 > /sys/module/damon_reclaim/parameters/min_age # 5초 콜드
echo 5 > /sys/module/damon_reclaim/parameters/quota_ms # 낮은 쿼터
echo Y > /sys/module/damon_reclaim/parameters/enabled
컨테이너(Container) 환경: cgroup 필터
# 시나리오: Kubernetes Pod별 DAMOS 정책 분리
# sysfs 인터페이스로 memcg 필터 설정
# 특정 cgroup의 메모리만 회수 대상으로 지정
MEMCG_PATH="/sys/fs/cgroup/my-pod"
# DAMOS 스킴에 memcg 필터 추가
echo 1 > .../schemes/0/ops_filters/nr_filters
echo memcg > .../schemes/0/ops_filters/0/type
echo Y > .../schemes/0/ops_filters/0/matching
echo Y > .../schemes/0/ops_filters/0/allow
echo ${MEMCG_PATH} > .../schemes/0/ops_filters/0/memcg_path
Redis/Memcached 인메모리 캐시 최적화
Redis나 Memcached 같은 인메모리 캐시(In-memory Cache) 서버는 대량의 데이터를 메모리에 적재하지만, 실제로는 상당 부분이 콜드 상태입니다. 만료된 키(Expired Key), 거의 조회되지 않는 값, TTL이 남았지만 접근이 없는 항목 등이 RSS를 불필요하게 증가시킵니다. DAMON으로 캐시 적중률(Hit Rate)에 영향 없이 콜드 페이지를 사전 회수할 수 있습니다.
# 시나리오: 64GB Redis 인스턴스, RSS 48GB 중 실제 핫 데이터 ~30GB
# 목표: 콜드 anonymous 페이지만 선별적으로 pageout하여 RSS 절감
# ── 1단계: DAMON_RECLAIM 파라미터 설정 ──
echo 5000 > /sys/module/damon_reclaim/parameters/sample_interval # 5ms
echo 200000 > /sys/module/damon_reclaim/parameters/aggr_interval # 200ms
echo 600 > /sys/module/damon_reclaim/parameters/min_age # 120초 콜드 기준
echo 30 > /sys/module/damon_reclaim/parameters/quota_ms # 주기당 최대 30ms
echo 256000000 > /sys/module/damon_reclaim/parameters/quota_sz # 주기당 256MB
# ── 2단계: anon 필터 (파일 캐시는 커널 페이지 캐시가 관리) ──
DAMON_CTX="/sys/kernel/mm/damon/admin/kdamonds/0/contexts/0"
echo 1 > ${DAMON_CTX}/schemes/0/ops_filters/nr_filters
echo anon > ${DAMON_CTX}/schemes/0/ops_filters/0/type
echo Y > ${DAMON_CTX}/schemes/0/ops_filters/0/matching
echo Y > ${DAMON_CTX}/schemes/0/ops_filters/0/allow # anon만 포함
echo Y > /sys/module/damon_reclaim/parameters/enabled
min_age를 120초 이상으로 보수적으로 설정해도 상당량의 콜드 페이지를 회수할 수 있습니다. 일반적으로 RSS 20~40% 절감, 캐시 적중률 변화 1% 미만을 기대할 수 있습니다.
# 검증: 캐시 성능 + RSS 변화 모니터링
redis-cli info stats | grep "keyspace_hits\|keyspace_misses"
ps -o rss= -p $(pidof redis-server) | awk '{print $1/1024"MB"}'
cat /sys/module/damon_reclaim/parameters/nr_reclaimed
Java/JVM 힙 메모리 최적화
JVM(Java Virtual Machine) 힙의 올드 제너레이션(Old/Tenured Generation)에는 장기 생존하지만 거의 접근되지 않는 콜드 객체가 상당수 존재합니다. DAMON의 vaddr 오퍼레이션으로 특정 JVM 프로세스를 모니터링하면, GC가 관리하지 못하는 페이지 수준의 콜드 영역을 식별하고 회수할 수 있습니다.
min_age를 GC 주기의 2~3배 이상으로 충분히 길게 설정해야 합니다.
# 시나리오: -Xmx32g JVM, Old Gen 24GB 중 활성 ~15GB
JAVA_PID=$(pidof java)
CTX="/sys/kernel/mm/damon/admin/kdamonds/0/contexts/0"
echo vaddr > ${CTX}/operations
echo 1 > ${CTX}/targets/nr_targets
echo ${JAVA_PID} > ${CTX}/targets/0/pid_target
# 보수적 파라미터 (GC 주기 고려)
echo 10000 > ${CTX}/monitoring_attrs/intervals/sample_us # 10ms
echo 500000 > ${CTX}/monitoring_attrs/intervals/aggr_us # 500ms
# 콜드 판정: 250초 이상 미접근 (GC Full 주기 대비 충분히 길게)
echo 1 > ${CTX}/schemes/nr_schemes
echo pageout > ${CTX}/schemes/0/action
echo 0 > ${CTX}/schemes/0/access_pattern/nr_accesses/max
echo 500 > ${CTX}/schemes/0/access_pattern/age/min
echo 20 > ${CTX}/schemes/0/quotas/ms
echo 128000000 > ${CTX}/schemes/0/quotas/bytes
jstat -gcutil로 GC 간격을 측정한 후 해당 간격의 2~3배로 설정하세요.
데이터베이스 버퍼(Buffer) 풀 최적화
MySQL(InnoDB)이나 PostgreSQL의 버퍼 풀(Buffer Pool)은 mmap()으로 매핑된 file-backed 페이지입니다. DAMON으로 콜드 버퍼 풀 페이지를 식별하여 다른 프로세스에 메모리를 환원할 수 있습니다.
fsync() 비용이 발생합니다. 데이터베이스 체크포인트(Checkpoint) 주기와 조율하여 더티 페이지 비율을 낮게 유지하세요.
# MySQL InnoDB buffer pool 32GB, file-backed 콜드 페이지만 회수
echo 5000 > /sys/module/damon_reclaim/parameters/sample_interval
echo 200000 > /sys/module/damon_reclaim/parameters/aggr_interval
echo 900 > /sys/module/damon_reclaim/parameters/min_age # 180초 콜드
echo 20 > /sys/module/damon_reclaim/parameters/quota_ms # 낮은 쿼터
# anon 필터 allow=false → file-backed 페이지만 타겟
DAMON_CTX="/sys/kernel/mm/damon/admin/kdamonds/0/contexts/0"
echo 1 > ${DAMON_CTX}/schemes/0/ops_filters/nr_filters
echo anon > ${DAMON_CTX}/schemes/0/ops_filters/0/type
echo Y > ${DAMON_CTX}/schemes/0/ops_filters/0/matching
echo N > ${DAMON_CTX}/schemes/0/ops_filters/0/allow # anon 제외=file만
echo Y > /sys/module/damon_reclaim/parameters/enabled
# 버퍼 풀 히트율 모니터링
mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';"
innodb_buffer_pool_dump_pct로 핫 페이지 목록을 유지하여 DAMON 회수 후에도 빠른 워밍을 보장합니다. PostgreSQL은 shared_buffers가 OS 페이지 캐시(Page Cache)와 이중 캐싱되므로 file-backed 회수가 특히 효과적입니다. 초기에는 quota_ms를 10~20으로 보수적으로 시작하고, 히트율 99%+ 유지를 확인한 후 점진적으로 증가시키세요.
Meta TMO (Transparent Memory Offloading)
Meta(Facebook)의 TMO(Transparent Memory Offloading)는 DAMON을 프로덕션에 대규모로 적용한 대표적인 사례입니다. 수백만 대의 서버에서 운영되며, DAMON을 사용하여 콜드 메모리를 zswap으로 오프로딩(Offloading)합니다.
TMO 아키텍처
| 측정 항목 | TMO 없음 | TMO 적용 | 개선 |
|---|---|---|---|
| 서버당 DRAM 사용 | 100% | 70~80% | 20~30% 절약 |
| 워크로드 성능 (p99 지연(Latency)) | 기준 | ±2% 이내 | 성능 유지 |
| OOM 빈도 | 기준 | 50% 감소 | 안정성 향상 |
| CPU 오버헤드 | 0% | 0.5~1% | 미미 |
TMO 스타일 DAMON sysfs 설정
Meta TMO와 유사한 구성을 DAMON sysfs 인터페이스를 통해 직접 설정하는 방법입니다. 물리 주소 모니터링(paddr)으로 콜드 페이지를 탐지하고 PAGEOUT 액션으로 zswap에 오프로딩합니다.
#!/bin/bash
# TMO 스타일 DAMON 설정: 콜드 페이지를 zswap으로 오프로딩
DAMON_BASE="/sys/kernel/mm/damon/admin/kdamonds/0"
CTX="${DAMON_BASE}/contexts/0"
SCHEME="${CTX}/schemes/0"
# 1. zswap 활성화 (zstd 압축, zsmalloc 할당자)
echo 1 > /sys/module/zswap/parameters/enabled
echo zstd > /sys/module/zswap/parameters/compressor
echo zsmalloc > /sys/module/zswap/parameters/zpool
echo 30 > /sys/module/zswap/parameters/max_pool_percent
# 2. paddr 오퍼레이션(물리 주소 전체 모니터링) 설정
echo 1 > ${DAMON_BASE}/../nr_kdamonds
echo 1 > ${DAMON_BASE}/contexts/nr_contexts
echo paddr > ${CTX}/operations
# 3. 모니터링 파라미터: TMO 권장값
echo 5000 > ${CTX}/monitoring_attrs/intervals/sample_us # 5ms 샘플링
echo 100000 > ${CTX}/monitoring_attrs/intervals/aggr_us # 100ms 집계
echo 1000000 > ${CTX}/monitoring_attrs/intervals/update_us # 1초 리전 갱신
echo 10 > ${CTX}/monitoring_attrs/nr_regions/min
echo 1000 > ${CTX}/monitoring_attrs/nr_regions/max
# 4. DAMOS PAGEOUT 스킴: 콜드 페이지 오프로딩
echo 1 > ${CTX}/schemes/nr_schemes
echo pageout > ${SCHEME}/action
# 접근 패턴: nr_accesses=0 (미접근), age >= 200 집계주기(20초)
echo 0 > ${SCHEME}/access_pattern/sz/min
echo 0 > ${SCHEME}/access_pattern/sz/max # 0=무제한
echo 0 > ${SCHEME}/access_pattern/nr_accesses/min
echo 0 > ${SCHEME}/access_pattern/nr_accesses/max # 접근 없음
echo 200 > ${SCHEME}/access_pattern/age/min # 최소 20초 미접근
echo 0 > ${SCHEME}/access_pattern/age/max # 0=무제한
# 5. 쿼터 설정: I/O 폭주 방지
echo 10 > ${SCHEME}/quotas/ms # 집계 주기당 10ms
echo 128000000 > ${SCHEME}/quotas/bytes # 주기당 128MB
echo 1000 > ${SCHEME}/quotas/reset_interval_ms # 1초마다 리셋
# 6. 워터마크: 메모리 여유 시 비활성화
echo free_mem_rate > ${SCHEME}/watermarks/metric
echo 5000000 > ${SCHEME}/watermarks/interval_us # 5초마다 확인
echo 500 > ${SCHEME}/watermarks/high # 50% 이상: 비활성
echo 400 > ${SCHEME}/watermarks/mid # 40% 이하: 활성
echo 200 > ${SCHEME}/watermarks/low # 20% 이하: 적극적
# 7. 설정 반영 및 시작
echo commit > ${DAMON_BASE}/state
echo on > ${DAMON_BASE}/state
echo "TMO 스타일 DAMON 설정 완료"
설명
TMO의 핵심은paddr 오퍼레이션으로 시스템 전체 물리 메모리를 모니터링하고, PAGEOUT 액션으로 콜드 페이지를 스왑 아웃하는 것입니다.
zswap이 활성화되어 있으면 스왑 아웃된 페이지가 먼저 압축 캐시에 저장되므로, 재접근 시 디스크 I/O 없이 메모리에서 직접 압축 해제됩니다.
쿼터(quotas/ms, quotas/bytes)는 한 번에 너무 많은 페이지를 회수하여 I/O 폭주가 발생하는 것을 방지합니다.
워터마크(watermarks/)는 메모리 여유가 충분할 때 불필요한 회수를 자동으로 비활성화합니다.
PSI 피드백 루프 모니터링
TMO에서 핵심적인 역할을 하는 PSI(Pressure Stall Information) 피드백 루프의 상태를 모니터링하는 방법입니다. DAMOS Quota Goal과 연동하여 메모리 압박에 따라 쿼터가 자동 조절되는 과정을 관찰할 수 있습니다.
#!/bin/bash
# PSI 피드백 루프 모니터링 스크립트
SCHEME="/sys/kernel/mm/damon/admin/kdamonds/0/contexts/0/schemes/0"
# PSI 기반 자동 쿼터 튜닝 설정 (v6.8+)
echo 1 > ${SCHEME}/quotas/goals/nr_goals
echo some_mem_psi_us > ${SCHEME}/quotas/goals/0/target_metric
echo 10000 > ${SCHEME}/quotas/goals/0/target_value # PSI some 10ms 이내 목표
# 주기적 모니터링
while true; do
# DAMOS 통계 갱신 요청
echo update_schemes_stats > /sys/kernel/mm/damon/admin/kdamonds/0/state
# 메모리 PSI 확인
PSI_SOME=$(awk '/some/{print $2}' /proc/pressure/memory)
# DAMOS 적용 통계
TRIED=$(cat ${SCHEME}/stats/sz_tried)
APPLIED=$(cat ${SCHEME}/stats/sz_applied)
QUOTA_EXCEED=$(cat ${SCHEME}/stats/qt_exceeds)
echo "[$(date +%H:%M:%S)] PSI_some=${PSI_SOME} tried=${TRIED} applied=${APPLIED} qt_exceeds=${QUOTA_EXCEED}"
# zswap 상태 확인
ZSWAP_STORED=$(cat /sys/kernel/debug/zswap/stored_pages)
ZSWAP_POOL=$(cat /sys/kernel/debug/zswap/pool_total_size)
echo " zswap_pages=${ZSWAP_STORED} pool_size=$((ZSWAP_POOL/1024/1024))MB"
sleep 5
done
설명
update_schemes_stats를 state 파일에 쓰면 DAMOS 통계가 갱신됩니다.
sz_tried는 스킴 조건에 매칭되어 시도된 총 바이트, sz_applied는 실제 적용(회수)된 바이트입니다.
qt_exceeds는 쿼터 초과로 스킵된 횟수로, 이 값이 지속적으로 높다면 쿼터를 늘리거나 PSI 목표를 완화해야 합니다.
PSI some 값이 목표(10ms)를 초과하면 DAMON이 쿼터를 자동으로 늘려 더 많은 콜드 페이지를 회수하고, 목표 이하로 떨어지면 쿼터를 줄여 불필요한 회수를 방지합니다.
DAMON vs MGLRU
DAMON과 MGLRU(Multi-Gen LRU)는 모두 메모리 접근 패턴을 활용하지만, 접근 방식과 목적이 다릅니다. 두 메커니즘은 상호 보완적으로 동시에 사용할 수 있습니다.
| 특성 | DAMON | MGLRU |
|---|---|---|
| 도입 버전 | v5.15 | v6.1 |
| 주요 목적 | 데이터 접근 모니터링 + 자동 최적화 | LRU 알고리즘 근본 개선 |
| 동작 시점 | 항상 (프로액티브) | 메모리 회수(Memory Reclaim) 시 (리액티브) |
| 정밀도 | 리전 단위 (통계적 추정) | 페이지 단위 (정확) |
| 사용자 제어 | 세밀한 정책 정의 가능 | 제한적 (min_ttl_ms 등) |
| THP 최적화 | DAMOS_HUGEPAGE/NOHUGEPAGE | 내장 (folio 기반 에이징) |
| NUMA 지원 | MIGRATE_HOT/COLD (v6.9+) | lru_gen 내장 NUMA 밸런싱 |
| 동시 사용 | 가능 -- DAMON의 LRU_PRIO/DEPRIO가 MGLRU 세대 이동을 보완 | |
DAMON과 MGLRU 동시 활성화 설정
DAMON과 MGLRU는 독립적으로 동작하므로 동시에 활성화할 수 있습니다. MGLRU가 리액티브 회수를 담당하고, DAMON이 프로액티브 회수와 추가 최적화를 수행하는 구성입니다.
#!/bin/bash
# DAMON + MGLRU 동시 활성화 설정
# === MGLRU 활성화 (리액티브 LRU 에이징 개선) ===
# MGLRU 상태 확인
cat /sys/kernel/mm/lru_gen/enabled
# 0x0007 = 모든 기능 활성화 (y=세대 에이징, +page table 스캔, +블룸 필터)
# MGLRU 활성화 (아직 비활성이라면)
echo 7 > /sys/kernel/mm/lru_gen/enabled
# min_ttl_ms: 최소 생존 시간 (0=제한 없음, 1000=최소 1초 보장)
echo 1000 > /sys/kernel/mm/lru_gen/min_ttl_ms
# === DAMON_RECLAIM 활성화 (프로액티브 콜드 페이지 회수) ===
# MGLRU와 함께 사용 시 min_age를 다소 높게 설정하여
# MGLRU가 처리하지 못한 장기 미접근 페이지만 DAMON이 회수
echo 5000 > /sys/module/damon_reclaim/parameters/sample_interval # 5ms
echo 100000 > /sys/module/damon_reclaim/parameters/aggr_interval # 100ms
echo 300 > /sys/module/damon_reclaim/parameters/min_age # 30초 미접근
echo 10 > /sys/module/damon_reclaim/parameters/quota_ms # 10ms/주기
echo 128000000 > /sys/module/damon_reclaim/parameters/quota_sz # 128MB/주기
# 워터마크: 메모리 여유 시 DAMON 비활성
echo 500 > /sys/module/damon_reclaim/parameters/wmarks_high
echo 400 > /sys/module/damon_reclaim/parameters/wmarks_mid
echo 200 > /sys/module/damon_reclaim/parameters/wmarks_low
echo Y > /sys/module/damon_reclaim/parameters/enabled
# === 상태 확인 ===
echo "--- MGLRU 상태 ---"
cat /sys/kernel/mm/lru_gen/enabled
cat /sys/kernel/debug/lru_gen # 세대별 페이지 분포 (debugfs)
echo "--- DAMON_RECLAIM 상태 ---"
cat /sys/module/damon_reclaim/parameters/enabled
cat /sys/module/damon_reclaim/parameters/nr_reclaimed
cat /sys/module/damon_reclaim/parameters/bytes_reclaimed_regions
설명
MGLRU의enabled 값 7(0x0007)은 세대 에이징(bit 0), 페이지 테이블 스캔(bit 1), 블룸 필터(bit 2)를 모두 활성화합니다.
min_ttl_ms는 세대의 최소 생존 시간으로, 너무 빨리 에이징되는 것을 방지합니다.
DAMON_RECLAIM의 min_age를 기본값(200)보다 높게(300) 설정하면, MGLRU가 이미 처리한 비교적 짧은 미접근 페이지는 건드리지 않고 장기 콜드 페이지만 DAMON이 사전 회수합니다.
sysfs/procfs 설정 비교
| 설정 항목 | DAMON (sysfs) | MGLRU (sysfs/debugfs) |
|---|---|---|
| 활성화 | /sys/module/damon_reclaim/parameters/enabled | /sys/kernel/mm/lru_gen/enabled |
| 최소 미접근 시간 | min_age (집계 주기 단위) | min_ttl_ms (밀리초 단위) |
| 회수 제한 | quota_ms, quota_sz | 해당 없음 (커널 자동 조절) |
| 워터마크 | wmarks_high/mid/low (퍼밀) | 해당 없음 (kswapd 워터마크 사용) |
| 모니터링 통계 | nr_reclaimed, schemes/0/stats/ | /sys/kernel/debug/lru_gen |
| 커널 CONFIG | CONFIG_DAMON_RECLAIM | CONFIG_LRU_GEN |
워크로드별 선택 가이드
| 워크로드 유형 | 권장 구성 | 이유 |
|---|---|---|
| 범용 서버 (웹, API) | MGLRU만 | 리액티브 회수만으로 충분하며, 추가 오버헤드 불필요 |
| 메모리 집약 (DB, 캐시) | MGLRU + DAMON | DAMON이 장기 콜드 페이지를 사전 회수하여 OOM 예방 |
| 대규모 클라우드 (컨테이너) | MGLRU + DAMON + memcg 필터 | cgroup별 차별화된 정책, 프로액티브 메모리 절약 |
| NUMA 다계층 메모리 | MGLRU + DAMON MIGRATE | MGLRU는 노드 내 에이징, DAMON은 노드 간 마이그레이션 |
| 임베디드/저전력 | DAMON만 | MGLRU의 페이지 테이블 전체 스캔이 부담되는 환경 |
| THP 최적화 필요 | MGLRU + DAMON HUGEPAGE | MGLRU는 folio 에이징, DAMON은 핫 리전 THP 프로모션 |
DAMON과 NUMA 티어링
커널 v6.9에서 도입된 DAMOS_MIGRATE_HOT/DAMOS_MIGRATE_COLD 액션은 DAMON의 접근 패턴 데이터를 NUMA 메모리 티어링에 활용합니다. CXL 메모리, HBM, PMEM 등 이기종 메모리 계층에서 데이터 배치를 최적화합니다.
# NUMA 티어링 예시: DRAM(Node 0) ↔ CXL(Node 1)
# 스킴 1: 콜드 페이지를 CXL로 디모션
echo migrate_cold > .../schemes/0/action
echo 0 > .../schemes/0/access_pattern/nr_accesses/min
echo 0 > .../schemes/0/access_pattern/nr_accesses/max
echo 100 > .../schemes/0/access_pattern/age/min
echo 1 > .../schemes/0/target_nid # CXL node
# 스킴 2: 핫 페이지를 DRAM으로 프로모션
echo migrate_hot > .../schemes/1/action
echo 15 > .../schemes/1/access_pattern/nr_accesses/min
echo 20 > .../schemes/1/access_pattern/nr_accesses/max
echo 0 > .../schemes/1/target_nid # DRAM node
ftrace/tracepoint 디버깅(Debugging)
DAMON은 ftrace tracepoint를 통해 내부 동작을 디버깅할 수 있습니다.
# 사용 가능한 DAMON tracepoint 확인
ls /sys/kernel/debug/tracing/events/damon/
# damon_aggregated -- 리전별 집계 결과
# tracepoint 활성화
echo 1 > /sys/kernel/debug/tracing/events/damon/damon_aggregated/enable
# 추적 시작
echo 1 > /sys/kernel/debug/tracing/tracing_on
# (DAMON 모니터링 실행 후)
cat /sys/kernel/debug/tracing/trace
# 출력 예시:
# kdamond.0-1234 [002] target_id=42 nr_regions=85
# region: start=0x7f0000000 end=0x7f0100000
# nr_accesses=18 age=0
# region: start=0x7f0100000 end=0x7f0800000
# nr_accesses=0 age=45
# 추적 중지
echo 0 > /sys/kernel/debug/tracing/tracing_on
# perf로 DAMON 이벤트 수집
perf record -e damon:damon_aggregated -a --duration 10
# BPF를 통한 DAMON 데이터 가공
bpftrace -e 'tracepoint:damon:damon_aggregated {
printf("region %lx-%lx: acc=%u age=%u\n",
args->start, args->end,
args->nr_accesses, args->age);
}'
cat /sys/kernel/mm/damon/admin/kdamonds/0/pid가 유효한 PID를 반환하는지 확인 (kdamond 실행 중)dmon_aggregatedtracepoint에 리전 데이터가 출력되는지 확인 (모니터링 동작 중)schemes/0/stats/nr_tried가 0이 아닌지 확인 (스킴 매칭이 발생하는지)schemes/0/stats/nr_applied가 0이면 쿼터/워터마크/필터 조건 확인
파라미터 튜닝 가이드
DAMON의 효과는 파라미터 설정에 크게 좌우됩니다. 워크로드 특성에 맞는 최적 파라미터를 찾는 것이 중요합니다.
| 파라미터 | 보수적 (저부하) | 기본값 (권장) | 공격적 (고정밀) | 튜닝 지침 |
|---|---|---|---|---|
sample_interval | 10ms | 5ms | 1ms | 짧을수록 정밀하나 CPU 사용 증가 |
aggr_interval | 200ms | 100ms | 50ms | 짧을수록 DAMOS 반응 빠름 |
update_interval | 120s | 60s | 10s | VMA 변경이 잦으면 짧게 |
min_nr_regions | 10 | 10 | 10 | 보통 기본값 유지 |
max_nr_regions | 100 | 1000 | 5000 | RSS 크기에 비례하여 조절 |
min_age (RECLAIM) | 500 | 200 | 50 | 워크로드의 재접근 주기 고려 |
quota_ms | 5 | 10 | 50 | CPU 여유에 맞게 |
체계적 튜닝 방법론
# Step 1: 기본값으로 워크로드 프로파일링
sudo damo record $(pidof my-app) --duration 60s -o baseline.json
# Step 2: 워킹셋 분포 확인
sudo damo report wss --input baseline.json
# → 워킹셋 대비 RSS가 2배 이상이면 DAMON_RECLAIM 효과적
# Step 3: DAMOS 시뮬레이션으로 회수 가능량 확인
sudo damo schemes --action stat \
--access_rate 0 0 \
--age 200 max \
$(pidof my-app)
# → stat 결과가 RSS의 20% 이상이면 회수 효과 기대
# Step 4: 보수적으로 시작하여 점진적 강화
echo 500 > /sys/module/damon_reclaim/parameters/min_age
echo 5 > /sys/module/damon_reclaim/parameters/quota_ms
echo Y > /sys/module/damon_reclaim/parameters/enabled
# Step 5: 모니터링하며 min_age, quota_ms 조절
watch -n 5 "cat /sys/module/damon_reclaim/parameters/nr_reclaimed; \
cat /proc/vmstat | grep -E 'pgscan|pgsteal'"
커널 빌드 옵션
# DAMON 관련 커널 설정 (make menuconfig)
# Memory Management options → Data Access Monitoring
CONFIG_DAMON=y # DAMON 코어 프레임워크
CONFIG_DAMON_VADDR=y # vaddr Operations Set
CONFIG_DAMON_PADDR=y # paddr Operations Set
CONFIG_DAMON_SYSFS=y # sysfs 인터페이스 (/sys/kernel/mm/damon/)
CONFIG_DAMON_DBGFS=n # debugfs 인터페이스 (레거시, 비권장)
CONFIG_DAMON_RECLAIM=y # DAMON 기반 프로액티브 회수
CONFIG_DAMON_LRU_SORT=y # DAMON 기반 LRU 정렬
# 의존성
CONFIG_MMU=y # 필수: MMU 지원
CONFIG_PAGE_IDLE_FLAG=y # paddr ops에 필요
# 선택적 (디버깅)
CONFIG_DAMON_KUNIT_TEST=n # DAMON 커널 유닛 테스트
| CONFIG 옵션 | 설명 | 기본값 | 프로덕션 권장 |
|---|---|---|---|
CONFIG_DAMON | 코어 프레임워크 | n | y |
CONFIG_DAMON_VADDR | 프로세스별 모니터링 | n | y |
CONFIG_DAMON_PADDR | 시스템 전체 모니터링 | n | y |
CONFIG_DAMON_SYSFS | sysfs 인터페이스 | n | y |
CONFIG_DAMON_RECLAIM | 프로액티브 회수 | n | y |
CONFIG_DAMON_LRU_SORT | LRU 재정렬 | n | 선택적 |
CONFIG_DAMON_DBGFS | debugfs (레거시) | n | n |
내부 자료 구조
DAMON의 핵심 동작을 이해하려면 kdamond 메인 루프의 실행 흐름을 추적해야 합니다.
/* mm/damon/core.c - kdamond 메인 루프 (대폭 단순화) */
static int kdamond_fn(void *data)
{
struct damon_ctx *ctx = data;
/* 리전 초기화 */
ctx->ops.init(ctx);
while (!kthread_should_stop() && ctx->kdamond) {
/* 워터마크 확인 -- 조건 불충족 시 슬립 */
if (kdamond_wait_activation(ctx))
continue;
/* Access Bit 클리어 (다음 체크 준비) */
ctx->ops.prepare_access_checks(ctx);
/* sample_interval 대기 */
kdamond_usleep(ctx->attrs.sample_interval);
/* Access Bit 확인 → nr_accesses 갱신 */
ctx->ops.check_accesses(ctx);
/* aggr_interval 도달 시 */
if (kdamond_aggregate_interval(ctx)) {
damon_merge_regions_of(ctx);
damon_do_apply_schemes(ctx); /* DAMOS 적용 */
kdamond_call_after_aggregation(ctx);
damon_reset_aggregated(ctx);
damon_split_regions_of(ctx);
}
/* update_interval 도달 시 */
if (kdamond_update_interval(ctx))
ctx->ops.update(ctx);
}
ctx->callback.before_terminate(ctx);
return 0;
}
설명
kdamond_fn은 kdamond 커널 스레드의 메인 함수입니다.
매 sample_interval마다 prepare_access_checks()로 Access Bit를 클리어하고, 다음 주기에 check_accesses()로 확인합니다.
aggr_interval에 도달하면 리전 병합, DAMOS 스킴 적용, 콜백 호출, 리전 분할을 순서대로 수행합니다.
update_interval에 도달하면 VMA 변경을 반영하여 리전을 재초기화합니다.
kdamond_wait_activation()은 워터마크 조건을 확인하여, 메모리가 충분하면 모니터링을 일시 중단합니다.
/* mm/damon/core.c - damon_split_regions_of() (단순화) */
static void damon_split_regions_of(
struct damon_ctx *ctx,
struct damon_target *t)
{
struct damon_region *r, *next;
unsigned int nr_new_regs = 0;
damon_for_each_region_safe(r, next, t) {
if (t->nr_regions >= ctx->attrs.max_nr_regions)
break;
/* 리전 크기가 2 * min_region_sz 이상이면 분할 */
if (damon_sz_region(r) > 2 * min_region_sz) {
struct damon_region *new;
unsigned long mid = r->ar.start + damon_sz_region(r) / 2;
new = damon_new_region(mid, r->ar.end);
new->nr_accesses = r->nr_accesses;
new->age = r->age;
r->ar.end = mid;
damon_insert_region(new, r, next, t);
nr_new_regs++;
}
}
}
설명
damon_split_regions_of()는 집계 주기(aggr_interval)마다 호출되어, 크기가 큰 리전을 절반으로 분할합니다.
분할 조건은 리전 크기가 2 * min_region_sz 이상이고, 전체 리전 수가 max_nr_regions 미만인 경우입니다.
분할 시 새 리전은 원본의 nr_accesses와 age 값을 상속받습니다.
이는 분할 직후에도 접근 패턴 정보가 유지되어 DAMOS 스킴 매칭이 올바르게 동작하기 위함입니다.
병합(merge)이 동질적인 리전을 합쳐 오버헤드를 줄이는 역할이라면, 분할(split)은 접근 패턴이 다른 영역을 세분화하여 정밀도를 높이는 역할을 합니다.
/* mm/damon/core.c - damon_do_apply_schemes() (단순화) */
static void damon_do_apply_schemes(
struct damon_ctx *ctx)
{
struct damos *s;
damon_for_each_scheme(s, ctx) {
struct damon_target *t;
/* 워터마크 확인 */
if (!damos_valid_wmarks(s))
continue;
/* 쿼터 리셋 확인 */
damos_check_quota_reset(s);
damon_for_each_target(t, ctx) {
struct damon_region *r;
damon_for_each_region(r, t) {
/* 패턴 매칭 */
if (!damos_access_pattern_match(s, r))
continue;
/* 필터 확인 */
if (damos_filter_out(s, t, r))
continue;
/* 쿼터 확인 */
if (damos_quota_exceeded(s))
break;
/* 액션 적용 */
sz = ctx->ops.apply_scheme(ctx, t, r, s);
s->stat.nr_applied++;
s->stat.sz_applied += sz;
}
}
}
}
설명
damon_do_apply_schemes()는 DAMOS의 핵심 실행 함수로, 등록된 모든 스킴을 순회하며 조건에 맞는 리전에 액션을 적용합니다.
실행 흐름은 4단계 필터링을 거칩니다:
- 워터마크 확인:
damos_valid_wmarks()가 현재 메모리 상태(빈 메모리 비율)를 워터마크와 비교합니다. 빈 메모리가high이상이면 스킴을 건너뛰고,low미만이면 적극 적용합니다. - 패턴 매칭:
damos_access_pattern_match()가 리전의nr_accesses,age, 크기를 스킴의 min/max 범위와 비교합니다. - 필터 확인:
damos_filter_out()이 anon/file, memcg, 주소 범위 등의 필터 조건을 평가하여 불필요한 페이지를 제외합니다. - 쿼터 확인:
damos_quota_exceeded()가 시간/크기 쿼터 초과 여부를 확인하여 과도한 자원 소비를 방지합니다.
ctx->ops.apply_scheme()은 Operations Set에 등록된 구현체를 호출하며, paddr의 경우 damon_pa_apply_scheme()이 pageout, lru_prio, stat 등의 액션을 수행합니다.
통계(nr_applied, sz_applied)는 sysfs의 stats/ 디렉토리를 통해 사용자 공간(User Space)에서 확인할 수 있습니다.
damon_attrs 구조체(Struct)
/* include/linux/damon.h */
struct damon_attrs {
unsigned long sample_interval; /* 마이크로초 단위 */
unsigned long aggr_interval;
unsigned long ops_update_interval;
unsigned long min_nr_regions;
unsigned long max_nr_regions;
};
damos_watermarks 구조체
/* include/linux/damon.h - 워터마크 */
struct damos_watermarks {
enum damos_wmark_metric metric; /* DAMOS_WMARK_FREE_MEM_RATE */
unsigned long interval; /* 확인 주기 (마이크로초) */
unsigned long high; /* 높은 워터마크 (퍼밀, 1/1000) */
unsigned long mid; /* 중간 워터마크 */
unsigned long low; /* 낮은 워터마크 */
};
설명
워터마크는 퍼밀(permil, 1/1000) 단위로 설정합니다.high=500이면 빈 메모리가 전체의 50% 이상일 때 DAMOS 스킴을 비활성화합니다.
low=200이면 빈 메모리가 20% 미만일 때 적극적으로 스킴을 적용합니다.
mid는 현재 상태를 유지하는 구간입니다 (히스테리시스).
성능 영향 분석
| 측정 항목 | DAMON 비활성 | DAMON 기본값 | DAMON + RECLAIM | 비고 |
|---|---|---|---|---|
| CPU 오버헤드 | 0% | 0.5~0.8% | 0.8~1.2% | 워크로드/RSS 크기에 따라 변동 |
| 메모리 사용 | 0 | ~200KB | ~300KB | 리전 메타데이터 + 스킴 구조체 |
| TLB miss 증가 | 0% | <0.1% | <0.5% | Access Bit 클리어에 의한 추가 TLB miss |
| 메모리 절약 | - | 모니터링만 | 10~30% RSS 감소 | 콜드 페이지 회수 효과 |
sample_interval이 매우 짧으면(1ms 미만) TLB thrashing이 발생하여 오히려 성능이 저하될 수 있습니다. 프로덕션에서는 반드시 벤치마크를 통해 최적 값을 확인하세요.
perf를 이용한 DAMON 오버헤드 측정
perf 도구를 사용하여 kdamond 커널 스레드의 CPU 사용률과 TLB miss 증가량을 정량적으로 측정할 수 있습니다.
# kdamond 스레드 PID 확인
KDAMOND_PID=$(pgrep -x kdamond)
echo "kdamond PID: ${KDAMOND_PID}"
# 1. kdamond의 CPU 사용률 프로파일링 (30초간)
perf stat -p ${KDAMOND_PID} -e task-clock,context-switches,page-faults -- sleep 30
# 2. kdamond 함수별 CPU 시간 분석
perf record -p ${KDAMOND_PID} -g -- sleep 10
perf report --sort comm,dso,symbol
# 3. DAMON 활성화 전후 TLB miss 비교
# (먼저 DAMON 비활성 상태에서 베이스라인 측정)
perf stat -e dTLB-load-misses,dTLB-store-misses,iTLB-load-misses \
-a -- sleep 30
# (DAMON 활성화 후 동일 측정)
echo Y > /sys/module/damon_reclaim/parameters/enabled
perf stat -e dTLB-load-misses,dTLB-store-misses,iTLB-load-misses \
-a -- sleep 30
# 4. DAMON 관련 커널 함수 호출 빈도 측정
perf stat -e probe:damon_do_apply_schemes,probe:kdamond_fn \
-a -- sleep 10 2>&1 || \
echo "프로브 미설정 시: perf probe -a damon_do_apply_schemes 실행 필요"
설명
perf stat -p ${KDAMOND_PID}는 kdamond 스레드의 CPU 시간을 직접 측정합니다. task-clock 이벤트는 해당 스레드가 실제로 사용한 CPU 시간(밀리초)을 보여줍니다.
perf record -g는 콜 그래프를 포함한 상세 프로파일을 수집하여, damon_do_apply_schemes(), damon_pa_check_accesses() 등 어떤 DAMON 함수가 가장 많은 시간을 소비하는지 확인할 수 있습니다.
TLB miss 비교는 DAMON의 Access Bit 클리어가 실제로 얼마나 TLB 성능에 영향을 미치는지 정량적으로 보여줍니다.
bpftrace로 DAMON 이벤트 추적
bpftrace를 사용하면 DAMON의 내부 이벤트를 실시간으로 추적할 수 있습니다. 리전 분할/병합 빈도, DAMOS 액션 실행 횟수, 쿼터 초과 등을 모니터링합니다.
# 1. DAMON 트레이스포인트 확인
cat /sys/kernel/debug/tracing/available_events | grep damon
# 2. bpftrace: DAMOS 액션 실행 추적
bpftrace -e '
tracepoint:damon:damos_before_apply {
@actions[str(args->action)] = count();
@sz_sum = sum(args->sz);
}
interval:s:5 {
printf("--- DAMOS actions (5s window) ---\n");
print(@actions); clear(@actions);
printf("Total bytes tried: %lld\n", @sz_sum);
@sz_sum = 0;
}'
# 3. bpftrace: kdamond 실행 주기 및 지연 측정
bpftrace -e '
kprobe:kdamond_fn {
@start[tid] = nsecs;
}
kretprobe:kdamond_fn /@start[tid]/ {
@latency_us = hist((nsecs - @start[tid]) / 1000);
delete(@start[tid]);
}'
# 4. bpftrace: 리전 분할/병합 빈도 추적
bpftrace -e '
kprobe:damon_split_region_at {
@splits = count();
}
kprobe:damon_merge_two {
@merges = count();
}
interval:s:10 {
printf("10s: splits=%lld merges=%lld\n", @splits, @merges);
@splits = 0; @merges = 0;
}'
설명
tracepoint:damon:damos_before_apply는 DAMOS 스킴이 리전에 적용되기 직전에 발생하는 트레이스포인트입니다.
args->action은 적용된 액션(pageout, hugepage 등), args->sz는 대상 리전의 크기를 나타냅니다.
kdamond_fn은 kdamond 커널 스레드의 메인 루프 함수로, 실행 지연을 측정하면 모니터링 주기가 정상적으로 유지되는지 확인할 수 있습니다.
리전 분할/병합 빈도가 과도하게 높다면 min_nr_regions/max_nr_regions 범위를 조정하거나 aggr_interval을 늘려야 합니다.
벤치마크 실행 및 결과 해석
DAMON의 성능 영향을 정확히 파악하려면 체계적인 벤치마크가 필요합니다. 다음은 DAMON 활성화 전후를 비교하는 벤치마크 절차입니다.
#!/bin/bash
# DAMON 성능 벤치마크 스크립트
RESULTS_DIR="/tmp/damon-bench"
mkdir -p ${RESULTS_DIR}
run_benchmark() {
local label=$1
echo "=== ${label} 벤치마크 시작 ==="
# 캐시 드롭 후 측정 (공정한 비교)
sync; echo 3 > /proc/sys/vm/drop_caches
# 1. 메모리 집약 워크로드: stress-ng
perf stat -o ${RESULTS_DIR}/${label}-stress.txt \
-e task-clock,dTLB-load-misses,page-faults \
-- stress-ng --vm 2 --vm-bytes 1G --timeout 60s
# 2. 실제 워크로드 시뮬레이션: redis-benchmark
redis-benchmark -t set,get -n 1000000 -d 256 \
> ${RESULTS_DIR}/${label}-redis.txt 2>&1
# 3. 메모리 사용량 스냅샷
cat /proc/meminfo | grep -E "MemFree|MemAvailable|SwapCached|AnonPages|Mapped" \
> ${RESULTS_DIR}/${label}-meminfo.txt
# 4. vmstat 스냅샷 (페이지 폴트, 스왑 활동)
vmstat 1 10 > ${RESULTS_DIR}/${label}-vmstat.txt
}
# A. DAMON 비활성 베이스라인
echo N > /sys/module/damon_reclaim/parameters/enabled 2>/dev/null
sleep 5
run_benchmark "baseline"
# B. DAMON 기본값 활성화
echo 5000 > /sys/module/damon_reclaim/parameters/sample_interval
echo 100000 > /sys/module/damon_reclaim/parameters/aggr_interval
echo 200 > /sys/module/damon_reclaim/parameters/min_age
echo 10 > /sys/module/damon_reclaim/parameters/quota_ms
echo Y > /sys/module/damon_reclaim/parameters/enabled
sleep 5
run_benchmark "damon-default"
# C. 결과 비교
echo "=== 결과 비교 ==="
echo "--- TLB miss 비교 ---"
grep "dTLB-load-misses" ${RESULTS_DIR}/*-stress.txt
echo "--- 메모리 절약 ---"
diff ${RESULTS_DIR}/baseline-meminfo.txt ${RESULTS_DIR}/damon-default-meminfo.txt
echo "--- DAMON 회수 통계 ---"
cat /sys/module/damon_reclaim/parameters/nr_reclaimed
설명
벤치마크는 캐시를 드롭한 후 실행하여 공정한 비교를 보장합니다.stress-ng --vm은 메모리를 집중적으로 할당/해제하는 워크로드로, DAMON의 TLB miss 영향을 가장 잘 보여줍니다.
redis-benchmark는 실제 서비스와 유사한 메모리 접근 패턴을 생성하여 DAMON의 콜드 페이지 탐지 정확도를 검증합니다.
결과 해석 시 핵심 지표는 (1) dTLB-load-misses 증가율이 1% 미만인지, (2) MemAvailable이 증가했는지 (콜드 페이지 회수 효과), (3) page-faults가 과도하게 증가하지 않았는지입니다.
커널 내부 DAMON API
커널 모듈(Kernel Module)에서 DAMON을 프로그래밍적으로 사용하는 주요 API입니다.
/* mm/damon/core.c - 주요 API */
/* DAMON context 생성/해제 */
struct damon_ctx *damon_new_ctx(void);
void damon_destroy_ctx(struct damon_ctx *ctx);
/* Operations Set 등록 */
int damon_select_ops(struct damon_ctx *ctx,
enum damon_ops_id id);
/* 모니터링 대상 추가 */
struct damon_target *damon_new_target(void);
void damon_add_target(struct damon_ctx *ctx,
struct damon_target *t);
/* 리전 추가 */
struct damon_region *damon_new_region(
unsigned long start, unsigned long end);
void damon_add_region(struct damon_region *r,
struct damon_target *t);
/* DAMOS 스킴 추가 */
struct damos *damon_new_scheme(
struct damos_access_pattern *pattern,
enum damos_action action,
unsigned long apply_interval_us,
struct damos_quota *quota,
struct damos_watermarks *wmarks);
void damon_set_schemes(struct damon_ctx *ctx,
struct damos **schemes,
ssize_t nr_schemes);
/* 모니터링 시작/중지 */
int damon_start(struct damon_ctx **ctxs,
int nr_ctxs, bool exclusive);
int damon_stop(struct damon_ctx **ctxs, int nr_ctxs);
설명
커널 모듈에서 DAMON을 사용하려면damon_new_ctx()로 컨텍스트를 생성하고, damon_select_ops()로 Operations Set을 선택한 뒤, damon_add_target()으로 모니터링 대상을 추가합니다.
DAMOS 스킴을 설정하려면 damon_new_scheme()으로 스킴을 생성하여 damon_set_schemes()로 등록합니다.
damon_start()를 호출하면 kdamond 커널 스레드가 생성되어 모니터링을 시작합니다.
DAMON_RECLAIM과 DAMON_LRU_SORT가 이 API의 대표적인 사용 사례입니다.
안티패턴과 주의사항
안티패턴 1: 과도한 샘플링
# 잘못된 설정: sample_interval이 너무 짧음
echo 100 > .../monitoring_attrs/intervals/sample_us # 0.1ms!
echo 5000 > .../nr_regions/max # 5000 리전!
# → CPU 오버헤드 5%+, TLB thrashing 발생
# 올바른 설정: 기본값 또는 보수적 설정
echo 5000 > .../monitoring_attrs/intervals/sample_us # 5ms (기본값)
echo 1000 > .../nr_regions/max # 1000 리전 (기본값)
안티패턴 2: 쿼터 없는 DAMOS
# 잘못된 설정: 쿼터 없이 pageout 스킴 활성화
echo pageout > .../schemes/0/action
echo 0 > .../schemes/0/quotas/ms # 무제한 시간!
echo 0 > .../schemes/0/quotas/bytes # 무제한 크기!
# → 한 번에 수 GB를 회수하여 I/O 폭주
# 올바른 설정: 항상 쿼터 설정
echo 10 > .../schemes/0/quotas/ms # 10ms/주기
echo 128000000 > .../schemes/0/quotas/bytes # 128MB/주기
안티패턴 3: min_age가 너무 낮은 RECLAIM
# 잘못된 설정: 1초만 미접근이면 회수
echo 10 > /sys/module/damon_reclaim/parameters/min_age
# → 버스트 워크로드에서 곧 재접근될 페이지까지 회수
# → page fault 증가 → 성능 저하
# 올바른 설정: 워크로드의 재접근 패턴 고려
echo 200 > /sys/module/damon_reclaim/parameters/min_age # 20초 (기본값)
DAMON vs 기존 메커니즘 종합 비교
| 특성 | DAMON | idle page tracking | perf mem | /proc/PID/pagemap |
|---|---|---|---|---|
| 커널 내장 | v5.15+ | v4.3+ | v2.6.31+ | v2.6.25+ |
| 자동 액션 | DAMOS | 없음 | 없음 | 없음 |
| 오버헤드 | <1% CPU | ~5% (풀스캔) | 1~3% (PMU 샘플링) | 높음 (PT 순회) |
| 정밀도 | 리전 단위 | 페이지 단위 | 주소 단위 (PMU) | 페이지 단위 |
| 하드웨어 의존 | PTE Access Bit | PTE Idle Bit | PMU (IBS/PEBS) | PTE |
| 실시간 대응 | 가능 (DAMOS) | 불가 (폴링) | 불가 (오프라인) | 불가 (폴링) |
| NUMA 최적화 | v6.9+ | 불가 | 가능 (분석) | 불가 |
시나리오별 메커니즘 선택 가이드
| 사용 시나리오 | 권장 메커니즘 | 이유 |
|---|---|---|
| 프로덕션 메모리 최적화 | DAMON + DAMOS | 자동 액션(회수/프로모션)과 쿼터로 안정적 운영 가능 |
| 원샷 메모리 프로파일링 | idle page tracking | 정확한 페이지 단위 접근 여부 확인, 일회성 분석에 적합 |
| NUMA 접근 패턴 분석 | perf mem | PMU 기반 로컬/리모트 접근 비율 측정, numastat 보완 |
| 특정 프로세스 메모리 맵(Memory Map)핑 | /proc/PID/pagemap | 가상→물리 매핑, RSS/WSS 정밀 측정 |
| 콜드 페이지 자동 회수 | DAMON_RECLAIM | 유일하게 자동 PAGEOUT을 지원하는 저오버헤드 메커니즘 |
| 개발/디버깅 단계 | perf mem + damo | 시각적 히트맵으로 접근 패턴 파악 후 DAMON 정책 설계 |
idle page tracking vs DAMON 코드 비교
idle page tracking은 /sys/kernel/mm/page_idle/bitmap을 통해 페이지별 idle 비트를 설정/확인하는 방식입니다. DAMON과의 접근 방식 차이를 코드로 비교합니다.
# === idle page tracking 방식 ===
# 1단계: 대상 프로세스의 모든 PFN 수집
TARGET_PID=1234
python3 -c "
import struct, os
pagemap = open(f'/proc/{os.environ[\"PID\"]}/pagemap', 'rb')
maps = open(f'/proc/{os.environ[\"PID\"]}/maps', 'r')
pfns = []
for line in maps:
parts = line.split()
start, end = [int(x, 16) for x in parts[0].split('-')]
for vaddr in range(start, end, 4096):
pagemap.seek(vaddr // 4096 * 8)
entry = struct.unpack('Q', pagemap.read(8))[0]
if entry & (1 << 63): # present
pfns.append(entry & ((1 << 55) - 1))
# 2단계: idle 비트 설정 (전체 PFN 순회)
idle_bitmap = open('/sys/kernel/mm/page_idle/bitmap', 'r+b')
for pfn in pfns:
idle_bitmap.seek(pfn // 64 * 8)
idle_bitmap.write(struct.pack('Q', 1 << (pfn % 64)))
print(f'Set idle bit for {len(pfns)} pages')
" PID=${TARGET_PID}
# 3단계: 일정 시간 대기 후 idle 비트 확인
sleep 30
# 4단계: 여전히 idle인 페이지 = 콜드 페이지
# (동일 PFN 목록을 다시 순회하여 idle 비트 확인)
# === DAMON 방식 ===
# 동일한 목적을 달성하지만 코드가 훨씬 간단
DAMON_BASE="/sys/kernel/mm/damon/admin/kdamonds/0"
CTX="${DAMON_BASE}/contexts/0"
# 리전 기반 샘플링으로 자동 모니터링 (전체 PFN 순회 불필요)
echo vaddr > ${CTX}/operations
echo 1 > ${CTX}/targets/nr_targets
echo ${TARGET_PID} > ${CTX}/targets/0/pid_target
echo 5000 > ${CTX}/monitoring_attrs/intervals/sample_us
echo 100000 > ${CTX}/monitoring_attrs/intervals/aggr_us
echo commit > ${DAMON_BASE}/state
echo on > ${DAMON_BASE}/state
# 30초 후 결과 확인
sleep 30
echo update_schemes_stats > ${DAMON_BASE}/state
# damo를 사용하면 리전별 접근 빈도를 시각적으로 확인 가능
damo report access --input /sys/kernel/mm/damon/admin
설명
idle page tracking은 모든 페이지의 PFN을 수집하고, idle 비트를 하나씩 설정하고, 대기 후 다시 확인하는 3단계 과정이 필요합니다. 대상 프로세스의 RSS가 클수록 순회해야 할 PFN 수가 선형으로 증가하여 오버헤드가 급증합니다(O(페이지 수)). 반면 DAMON은 주소 공간을 리전으로 묶어 리전당 하나의 랜덤 페이지만 샘플링하므로 O(리전 수)의 일정한 오버헤드를 유지합니다. 다만 idle page tracking은 페이지 단위의 정확한 결과를 제공하고, DAMON은 리전 단위의 통계적 추정 결과를 제공한다는 정밀도 차이가 있습니다.자동 쿼터 튜닝 (DAMOS Quota Goal)
최신 공식 문서 기준의 DAMOS Quota Goal은 사용자가 명시적 쿼터 값을 고정하는 대신, 목표 메트릭을 지정하면 DAMON이 effective_bytes와 유효 시간 쿼터를 자동으로 조절하는 피드백 루프입니다. 이제 목표는 PSI 하나에만 머무르지 않고, NUMA 노드 메모리 사용률과 memcg 경로별 사용률까지 지정할 수 있습니다.
/* include/linux/damon.h - 쿼터 목표 구조체 */
struct damos_quota_goal {
enum damos_quota_goal_metric metric;
/* DAMOS_QUOTA_USER_INPUT: 사용자 직접 입력 */
/* DAMOS_QUOTA_SOME_MEM_PSI_US: PSI some 값 */
/* DAMOS_QUOTA_NODE_MEM_USED_BP / _FREE_BP */
/* DAMOS_QUOTA_NODE_MEMCG_USED_BP / _FREE_BP */
/* DAMOS_QUOTA_ACTIVE_MEM_BP / _INACTIVE_MEM_BP */
unsigned long target_value; /* 목표 값 */
unsigned long current_value; /* 현재 값 (커널이 갱신) */
int nid; /* NUMA 노드 기반 목표일 때 사용 */
char *path; /* memcg 경로 기반 목표일 때 사용 */
struct list_head list;
};
설명
DAMOS_QUOTA_SOME_MEM_PSI_US는 메모리 PSI(Pressure Stall Information)의 some 값(마이크로초)을 목표로 합니다.
예를 들어 target_value=10000이면 DAMON은 PSI some이 10ms를 넘지 않도록 쿼터를 조절합니다.
최신 문서는 여기에 더해 특정 NUMA 노드의 사용률, 특정 memcg 경로의 사용률, active/inactive 메모리 비율도 목표로 둘 수 있다고 설명합니다.
여러 goal을 동시에 두면 DAMON은 각 goal의 현재 값을 갱신한 뒤 가장 적절한 값으로 유효 쿼터를 계산합니다.
| target_metric | 의미 | 추가 입력 |
|---|---|---|
user_input | 사용자 공간이 current_value를 직접 넣는 방식입니다. | 주기적으로 값을 밀어 넣는 외부 제어 루프가 필요합니다. |
some_mem_psi_us | 메모리 PSI some 지연시간을 목표로 합니다. | 추가 입력 없음 |
node_mem_used_bp, node_mem_free_bp | 특정 NUMA 노드의 사용/유휴 비율을 목표로 합니다. | nid |
node_memcg_used_bp, node_memcg_free_bp | 특정 memcg가 노드별로 소비하는 비율을 목표로 합니다. | nid, path |
active_mem_bp, inactive_mem_bp | active/inactive LRU 비율을 목표로 합니다. | 추가 입력 없음 |
자동 튜닝 알고리즘
DAMON의 자동 쿼터 튜닝은 비례 제어(proportional control) 기반으로 동작합니다:
/* mm/damon/core.c - 쿼터 자동 조절 (단순화) */
static void damos_adjust_quota(
struct damos *s)
{
struct damos_quota *q = &s->quota;
struct damos_quota_goal *g;
unsigned long score;
list_for_each_entry(g, &q->goals, list) {
/* 현재 메트릭 수집 */
damos_quota_goal_update(g);
/* 목표 대비 비율 계산 */
if (g->current_value < g->target_value) {
/* 여유 있음 → 쿼터 축소 */
score = g->current_value * 10000 / g->target_value;
} else {
/* 압박 상태 → 쿼터 확대 */
score = g->current_value * 10000 / g->target_value;
}
}
/* 유효 쿼터 조절 (최소/최대 제한 적용) */
q->esz = q->esz * score / 10000;
q->esz = clamp(q->esz, DAMOS_QUOTA_MIN, q->sz);
}
# sysfs에서 자동 쿼터 튜닝 설정
# PSI some 5ms 이하를 목표로 자동 조절
echo 1 > .../schemes/0/quotas/goals/nr_goals
echo some_mem_psi_us > .../schemes/0/quotas/goals/0/target_metric
echo 5000 > .../schemes/0/quotas/goals/0/target_value
# 초기 쿼터는 보수적으로 설정 (자동 튜닝이 조절)
echo 5 > .../schemes/0/quotas/ms
echo 64000000 > .../schemes/0/quotas/bytes
echo commit_schemes_quota_goals > .../kdamonds/0/state
echo update_schemes_effective_quotas > .../kdamonds/0/state
cat .../schemes/0/quotas/effective_bytes
# memcg + NUMA 기준 목표 예시
echo 1 > .../schemes/0/quotas/goals/nr_goals
echo node_memcg_used_bp > .../schemes/0/quotas/goals/0/target_metric
echo 650 > .../schemes/0/quotas/goals/0/target_value
echo 1 > .../schemes/0/quotas/goals/0/nid
echo /kubepods.slice/pod123 > .../schemes/0/quotas/goals/0/path
echo commit_schemes_quota_goals > .../kdamonds/0/state
DAMON_STAT 경량 모니터링 모듈
최신 관리자 문서는 DAMON_STAT을 "시스템 전체 물리 메모리 접근 패턴을 위한 정적이고 단순한 모듈"로 설명합니다. 이 모듈은 DAMOS 액션을 수행하지 않고, 추정 메모리 대역폭(Bandwidth)과 메모리 유휴 시간 분위수를 바로 읽을 수 있게 해 줍니다. 운영자가 "정책 적용" 전에 시스템 상태를 낮은 비용으로 관찰할 때 유용합니다.
| 항목 | 설명 | 운영 의미 |
|---|---|---|
estimated_memory_bandwidth | 추정 메모리 대역폭입니다. | 메모리 집약 구간과 유휴 구간을 빠르게 구분할 수 있습니다. |
memory_idle_ms_percentiles | 메모리 유휴 시간의 분위수입니다. | 콜드 데이터가 얼마나 오래 방치되는지 분포로 볼 수 있습니다. |
| 자동 간격 튜닝 | 기본적으로 관찰된 접근 이벤트 비율을 약 4% 수준으로 맞추도록 interval을 조절합니다. | 정확도와 오버헤드 사이 균형을 자동으로 유지합니다. |
| 오버헤드 목표 | 관리자 문서는 몇몇 프로덕션 서버에서 단일 CPU의 0.x% 수준 시간만 사용한다고 설명합니다. | 상시 켜 두는 관찰용 모듈로 쓰기 좋습니다. |
# DAMON_STAT 활성화와 결과 확인
cat /sys/module/damon_stat/parameters/enabled
echo Y > /sys/module/damon_stat/parameters/enabled
cat /sys/module/damon_stat/parameters/aggr_interval_us
cat /sys/module/damon_stat/parameters/min_sample_us
cat /sys/module/damon_stat/parameters/max_sample_us
cat /sys/module/damon_stat/parameters/estimated_memory_bandwidth
cat /sys/module/damon_stat/parameters/memory_idle_ms_percentiles
DAMON_STAT으로 "이 서버에 실제로 긴 유휴 꼬리가 있는가"를 확인하면 불필요한 정책 실험을 줄일 수 있습니다.
접근 패턴 분석 기법
DAMON의 모니터링 데이터를 효과적으로 분석하면 워크로드의 메모리 사용 특성을 정밀하게 파악할 수 있습니다.
워킹셋 크기 분석
워킹셋(Working Set Size)은 특정 시간 동안 실제로 접근된 메모리 양입니다. DAMON의 nr_accesses > 0인 리전의 크기 합이 워킹셋의 근사치입니다.
# damo로 워킹셋 분석
sudo damo record $(pidof redis-server) --duration 120s -o redis.json
# 시간대별 워킹셋 변화 확인
sudo damo report wss --input redis.json --sortby time
# 워킹셋 분포 히스토그램
sudo damo report wss --input redis.json --percentiles 10 25 50 75 90 95 99
# 출력 예시:
# percentile wss
# 10 128MB (최소 활성)
# 25 256MB (1사분위)
# 50 512MB (중앙값)
# 75 1.2GB (3사분위)
# 90 1.8GB (p90)
# 95 2.1GB (p95)
# 99 2.4GB (p99)
# → RSS 4GB 중 약 50%가 일상적으로 사용되지 않음
# → DAMON_RECLAIM으로 ~2GB 회수 가능
핫/콜드 메모리 분류
| 분류 | nr_accesses 범위 | 비율 (일반적) | 최적 처리 |
|---|---|---|---|
| 핫 (Hot) | aggr/sample의 80%+ | 10~30% | THP 프로모션, DRAM 고정 |
| 웜 (Warm) | aggr/sample의 20~80% | 20~40% | LRU active 유지 |
| 쿨 (Cool) | aggr/sample의 1~20% | 10~20% | LRU inactive 이동 |
| 콜드 (Cold) | 0 (age > threshold) | 20~50% | 프로액티브 회수, CXL 디모션 |
# 핫/콜드 분포를 bpftrace로 실시간 확인
bpftrace -e '
tracepoint:damon:damon_aggregated {
@hot = count(args->nr_accesses >= 15);
@warm = count(args->nr_accesses >= 5 && args->nr_accesses < 15);
@cool = count(args->nr_accesses >= 1 && args->nr_accesses < 5);
@cold = count(args->nr_accesses == 0);
@total_cold_bytes = sum(args->nr_accesses == 0 ?
(args->end - args->start) : 0);
}
interval:s:10 {
printf("hot=%d warm=%d cool=%d cold=%d cold_bytes=%lld\n",
@hot, @warm, @cool, @cold, @total_cold_bytes);
clear(@hot); clear(@warm); clear(@cool); clear(@cold);
clear(@total_cold_bytes);
}'
접근 빈도 히스토그램
# DAMON 리전 데이터를 사용한 접근 빈도 히스토그램 생성
bpftrace -e '
tracepoint:damon:damon_aggregated {
@accesses = lhist(args->nr_accesses, 0, 20, 1);
@sizes[args->nr_accesses] =
sum(args->end - args->start);
}
END {
printf("\n--- 접근 빈도별 메모리 크기 분포 ---\n");
print(@sizes);
}'
프로덕션 운영 플레이북
단계 1: 프로파일링 (관측만)
# 1주일간 워크로드 프로파일링
# stat 액션으로 실제 회수 없이 데이터만 수집
# sysfs 설정
echo 1 > /sys/kernel/mm/damon/admin/kdamonds/nr_kdamonds
echo 1 > .../kdamonds/0/contexts/nr_contexts
echo paddr > .../kdamonds/0/contexts/0/operations
# 모니터링 파라미터
echo 5000 > .../contexts/0/monitoring_attrs/intervals/sample_us
echo 100000 > .../contexts/0/monitoring_attrs/intervals/aggr_us
echo 60000000 > .../contexts/0/monitoring_attrs/intervals/update_us
# stat 스킴 (콜드 페이지 크기만 측정)
echo 1 > .../contexts/0/schemes/nr_schemes
echo stat > .../contexts/0/schemes/0/action
echo 0 > .../contexts/0/schemes/0/access_pattern/nr_accesses/min
echo 0 > .../contexts/0/schemes/0/access_pattern/nr_accesses/max
echo 200 > .../contexts/0/schemes/0/access_pattern/age/min
# 시작
echo commit > .../kdamonds/0/state
echo on > .../kdamonds/0/state
# 매일 통계 수집 (cron에 등록)
echo update_schemes_stats > .../kdamonds/0/state
DATE=$(date +%Y%m%d)
echo "$DATE nr_tried=$(cat .../schemes/0/stats/nr_tried) \
sz_tried=$(cat .../schemes/0/stats/sz_tried)" >> /var/log/damon-stats.log
단계 2: 스테이징 환경 테스트
# 프로파일링 결과 기반으로 보수적 DAMOS 적용
# DAMON_RECLAIM으로 간편 시작
echo 300 > /sys/module/damon_reclaim/parameters/min_age # 30초 콜드
echo 5 > /sys/module/damon_reclaim/parameters/quota_ms # 5ms (보수적)
echo 64000000 > /sys/module/damon_reclaim/parameters/quota_sz # 64MB
echo 500 > /sys/module/damon_reclaim/parameters/wmarks_high # 50%
echo 400 > /sys/module/damon_reclaim/parameters/wmarks_mid # 40%
echo 200 > /sys/module/damon_reclaim/parameters/wmarks_low # 20%
echo Y > /sys/module/damon_reclaim/parameters/enabled
# 성능 지표 모니터링 (A/B 비교)
while true; do
echo "$(date) \
reclaimed=$(cat /sys/module/damon_reclaim/parameters/nr_reclaimed) \
pgscan=$(grep pgscan_direct /proc/vmstat | awk '{print $2}') \
pgfault=$(grep pgfault /proc/vmstat | awk '{print $2}') \
psi_mem=$(cat /proc/pressure/memory | head -1)"
sleep 60
done >> /var/log/damon-perf.log
단계 3: 프로덕션 점진적 롤아웃
# 카나리 배포: 전체 호스트의 5%에 먼저 적용
# 1주일 모니터링 후 문제없으면 25% → 50% → 100%
# 프로덕션 최종 설정 (예시)
echo 200 > /sys/module/damon_reclaim/parameters/min_age
echo 10 > /sys/module/damon_reclaim/parameters/quota_ms
echo 128000000 > /sys/module/damon_reclaim/parameters/quota_sz
# 자동 쿼터 튜닝 활성화 (v6.8+)
# → PSI 기반으로 자동 조절하므로 수동 튜닝 부담 감소
# 롤백 절차
echo N > /sys/module/damon_reclaim/parameters/enabled
# → 즉시 회수 중단, 기존 페이지에 영향 없음
모니터링 알림 설정
# Prometheus + Grafana 연동 (node_exporter custom collector)
# /var/lib/node_exporter/textfile/damon.prom
cat <<'EOF' > /usr/local/bin/damon-metrics.sh
#!/bin/bash
METRICS=/var/lib/node_exporter/textfile/damon.prom
{
echo "# HELP damon_reclaim_nr_reclaimed Total pages reclaimed by DAMON"
echo "# TYPE damon_reclaim_nr_reclaimed counter"
echo "damon_reclaim_nr_reclaimed $(cat /sys/module/damon_reclaim/parameters/nr_reclaimed 2>/dev/null || echo 0)"
echo "# HELP damon_reclaim_bytes_reclaimed Total bytes reclaimed"
echo "# TYPE damon_reclaim_bytes_reclaimed counter"
echo "damon_reclaim_bytes_reclaimed $(cat /sys/module/damon_reclaim/parameters/bytes_reclaimed 2>/dev/null || echo 0)"
} > ${METRICS}.tmp
mv ${METRICS}.tmp ${METRICS}
EOF
chmod +x /usr/local/bin/damon-metrics.sh
# cron: 1분마다 메트릭 수집
echo "* * * * * root /usr/local/bin/damon-metrics.sh" > /etc/cron.d/damon-metrics
- DAMON 활성화 전후 애플리케이션 지연(p50/p99) 비교
- page fault 비율(
/proc/vmstatpgfault, pgmajfault) 증가 여부 확인 - PSI 메모리(
/proc/pressure/memory) 악화 여부 모니터링 - 스왑 사용량 급증 여부 확인 (DAMOS pageout → 스왑 I/O)
- 워터마크 설정이 환경에 적합한지 (충분한 히스테리시스)
- 롤백(Rollback) 자동화 스크립트 준비 (알림 → 자동 비활성화)
Android/ChromeOS에서의 DAMON
DAMON은 모바일/ChromeOS 환경에서 특히 효과적입니다. 제한된 메모리에서 멀티태스킹 성능을 최적화하기 위해 다음과 같이 활용됩니다.
Android LMKD 대체/보완
| 특성 | Android LMKD | DAMON + DAMOS |
|---|---|---|
| 메모리 회수 방식 | 프로세스 킬 (전체 메모리 반환) | 콜드 페이지만 선택적 회수 |
| 사용자 경험 | 앱 재시작(Reboot) 필요 | 앱 유지, 백그라운드 메모리만 회수 |
| 접근 패턴 활용 | 없음 (RSS/oom_score만) | 실제 접근 빈도 기반 |
| 오버헤드 | 킬 시 순간 비용 | 상시 ~0.5% CPU |
| 적합한 상황 | 심각한 메모리 부족 | 일상적 메모리 최적화 |
# Android 환경 DAMON 설정 예시
# 백그라운드 앱의 콜드 페이지를 zram으로 스왑
# 짧은 주기, 공격적 콜드 기준
echo 5000 > /sys/module/damon_reclaim/parameters/sample_interval
echo 50000 > /sys/module/damon_reclaim/parameters/aggr_interval # 50ms
echo 60 > /sys/module/damon_reclaim/parameters/min_age # 3초 콜드
echo 3 > /sys/module/damon_reclaim/parameters/quota_ms # 낮은 CPU 쿼터
echo 32000000 > /sys/module/damon_reclaim/parameters/quota_sz # 32MB/주기
# 프리 메모리 15% 이하에서만 동작
echo 200 > /sys/module/damon_reclaim/parameters/wmarks_high # 20%
echo 150 > /sys/module/damon_reclaim/parameters/wmarks_mid # 15%
echo 100 > /sys/module/damon_reclaim/parameters/wmarks_low # 10%
echo Y > /sys/module/damon_reclaim/parameters/enabled
ChromeOS 메모리 관리
Google의 ChromeOS는 DAMON을 탭 관리에 활용합니다. 백그라운드 탭의 렌더러 프로세스에 대해 DAMON으로 접근 패턴을 모니터링하고, 완전히 콜드한 탭의 메모리를 사전 회수하여 활성 탭의 성능을 보장합니다.
DAMON vs Idle Page Tracking 상세 비교
idle page tracking(/sys/kernel/mm/page_idle/bitmap)은 커널 v4.3에서 도입된 페이지 단위 접근 추적 메커니즘입니다. DAMON과 유사한 목적이지만 구현 방식과 성능 특성이 크게 다릅니다.
# idle page tracking 사용법 (비교용)
# 1. 모든 페이지를 idle로 마킹
echo 1 > /sys/kernel/mm/page_idle/bitmap # 전체 페이지 idle 설정
# 2. 일정 시간 대기 (워크로드 실행)
sleep 60
# 3. 여전히 idle인 페이지 확인
python3 -c "
import struct, os
with open('/sys/kernel/mm/page_idle/bitmap', 'rb') as f:
idle_count = 0
total_count = 0
while True:
data = f.read(8)
if not data: break
val = struct.unpack('Q', data)[0]
for i in range(64):
total_count += 1
if val & (1 << i):
idle_count += 1
print(f'Idle pages: {idle_count}/{total_count} '
f'({idle_count*4/1024:.0f}MB / {total_count*4/1024:.0f}MB)')
"
| 비교 항목 | DAMON | idle page tracking |
|---|---|---|
| 샘플링 단위 | 리전 (랜덤 1 페이지) | 모든 페이지 (비트맵(Bitmap)) |
| 자동화 | 커널 내부 자동 루프 | 유저스페이스 폴링 필요 |
| 오버헤드 (1GB RSS) | ~100 PTE 확인/주기 | ~256K PTE 확인/주기 |
| 접근 빈도 정보 | nr_accesses (다단계) | idle/non-idle (이진) |
| 자동 액션 | DAMOS (pageout, hugepage 등) | 없음 (외부 도구 필요) |
| 필터링 | addr/target + anon/active/memcg/young/unmapped/hugepage_size | 없음 |
| NUMA 티어링 | MIGRATE_HOT/COLD | 없음 |
BPF와 DAMON 통합
BPF(Berkeley Packet Filter)를 활용하면 DAMON의 tracepoint 데이터를 실시간으로 가공하거나, 커스텀 정책 로직을 구현할 수 있습니다.
/* BPF 프로그램: DAMON 데이터 기반 커스텀 히트맵 */
/* damon_heatmap.bpf.c */
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 10000);
__type(key, __u64); /* 리전 시작 주소 (4KB 정렬) */
__type(value, __u32); /* 누적 접근 횟수 */
} access_map SEC(".maps");
SEC("tracepoint/damon/damon_aggregated")
int handle_aggregated(struct trace_event_raw_damon_aggregated *ctx)
{
__u64 start = ctx->start;
__u32 accesses = ctx->nr_accesses;
__u32 *val;
/* 4KB 정렬 키로 변환 */
start &= ~(0xFFFULL);
val = bpf_map_lookup_elem(&access_map, &start);
if (val) {
*val += accesses;
} else {
bpf_map_update_elem(&access_map, &start,
&accesses, BPF_ANY);
}
return 0;
}
설명
이 BPF 프로그램은damon_aggregated tracepoint에 attach되어, 각 리전의 접근 횟수를 BPF 해시(Hash)맵에 누적합니다.
유저스페이스 도구가 이 맵을 주기적으로 읽어 히트맵을 생성하거나, 특정 임계값 초과 시 알림을 발생시킬 수 있습니다.
현재 공식 문서 기준으로 BPF는 주로 tracepoint 기반 관찰에 활용되며, 실제 메모리 조작은 기본 DAMOS 액션(pageout, hugepage, migrate_hot 등)이 담당합니다.
# BPF 프로그램 로드 및 히트맵 출력
sudo bpftool prog load damon_heatmap.bpf.o /sys/fs/bpf/damon_heatmap
sudo bpftool prog attach pinned /sys/fs/bpf/damon_heatmap tracepoint damon damon_aggregated
# 10초 후 맵 덤프
sleep 10
sudo bpftool map dump pinned /sys/fs/bpf/access_map | \
sort -t: -k2 -n -r | head -20
# → 가장 핫한 주소 영역 Top 20
트러블슈팅 가이드
DAMON이 동작하지 않는 경우
# 1. 커널 설정 확인
zcat /proc/config.gz | grep DAMON
# CONFIG_DAMON=y 확인, 없으면 커널 재빌드 필요
# 2. sysfs 존재 확인
ls /sys/kernel/mm/damon/admin/
# 없으면 CONFIG_DAMON_SYSFS=y 필요
# 3. kdamond 상태 확인
cat /sys/kernel/mm/damon/admin/kdamonds/0/state
# "on"이 아니면 시작되지 않음
cat /sys/kernel/mm/damon/admin/kdamonds/0/pid
# PID가 없으면 kdamond가 실행 중이 아님
# 4. commit 누락 확인 (가장 흔한 실수)
echo commit > /sys/kernel/mm/damon/admin/kdamonds/0/state
# → sysfs 설정 변경 후 반드시 commit 필요!
# 5. 대상 PID 유효성 확인
cat .../contexts/0/targets/0/pid_target
ls /proc/$(cat .../contexts/0/targets/0/pid_target)
# 프로세스가 이미 종료되었으면 모니터링 불가
DAMOS가 회수하지 않는 경우
# 1. 스킴 통계 확인
echo update_schemes_stats > .../kdamonds/0/state
echo "nr_tried=$(cat .../schemes/0/stats/nr_tried)"
echo "sz_tried=$(cat .../schemes/0/stats/sz_tried)"
echo "nr_applied=$(cat .../schemes/0/stats/nr_applied)"
echo "sz_applied=$(cat .../schemes/0/stats/sz_applied)"
echo "qt_exceeds=$(cat .../schemes/0/stats/qt_exceeds)"
# nr_tried=0: 패턴 매칭 실패 → access_pattern 조건 완화
# nr_tried>0, nr_applied=0: 필터/쿼터/워터마크 차단
# qt_exceeds>0: 쿼터 초과 → quota_ms/quota_sz 증가
# 2. 워터마크 확인 (free memory)
free -m
# free가 wmarks_high 이상이면 스킴 비활성화 상태
# 3. min_age 확인
# min_age=200, aggr_interval=100ms → 20초 이상 미접근 필요
# 워크로드가 20초 내에 모든 메모리를 재접근하면 콜드 리전 없음
# 4. 필터 확인
cat .../schemes/0/core_filters/0/type
cat .../schemes/0/ops_filters/0/type
cat .../schemes/0/ops_filters/0/allow
# ops memcg/anon 필터가 allow=N이면 해당 페이지는 제외
오버헤드가 높은 경우
# kdamond CPU 사용률 확인
top -p $(cat /sys/kernel/mm/damon/admin/kdamonds/0/pid)
# CPU 1% 이상이면 파라미터 완화
echo 10000 > .../monitoring_attrs/intervals/sample_us # 5ms → 10ms
echo 500 > .../monitoring_attrs/nr_regions/max # 1000 → 500
echo commit > .../kdamonds/0/state
# TLB miss 증가 확인
perf stat -e dTLB-load-misses,dTLB-store-misses -a sleep 10
# DAMON 활성화 전후 비교
systemd 서비스 연동
DAMON_RECLAIM을 시스템 부팅 시 자동으로 활성화하려면 systemd 서비스 유닛을 작성합니다.
# /etc/systemd/system/damon-reclaim.service
[Unit]
Description=DAMON Proactive Memory Reclaim
After=multi-user.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -c '\
echo 5000 > /sys/module/damon_reclaim/parameters/sample_interval; \
echo 100000 > /sys/module/damon_reclaim/parameters/aggr_interval; \
echo 200 > /sys/module/damon_reclaim/parameters/min_age; \
echo 10 > /sys/module/damon_reclaim/parameters/quota_ms; \
echo 128000000 > /sys/module/damon_reclaim/parameters/quota_sz; \
echo 500 > /sys/module/damon_reclaim/parameters/wmarks_high; \
echo 400 > /sys/module/damon_reclaim/parameters/wmarks_mid; \
echo 200 > /sys/module/damon_reclaim/parameters/wmarks_low; \
echo Y > /sys/module/damon_reclaim/parameters/enabled'
ExecStop=/bin/bash -c '\
echo N > /sys/module/damon_reclaim/parameters/enabled'
[Install]
WantedBy=multi-user.target
# 서비스 등록 및 시작
sudo systemctl daemon-reload
sudo systemctl enable damon-reclaim.service
sudo systemctl start damon-reclaim.service
# 상태 확인
sudo systemctl status damon-reclaim.service
cat /sys/module/damon_reclaim/parameters/enabled # Y
cat /sys/module/damon_reclaim/parameters/nr_reclaimed
damon_reclaim.enabled=Y damon_reclaim.min_age=200 damon_reclaim.quota_ms=10
버전별 주요 변경사항
| 커널 버전 | 변경사항 | 관련 CONFIG | 관련 git hash |
|---|---|---|---|
| v5.15 | DAMON 코어 프레임워크 최초 머지, region-based sampling, vaddr/paddr, debugfs | CONFIG_DAMON | core 2224d8485492 |
| v5.16 | DAMOS 도입, DAMON_RECLAIM, 워터마크 기반 프로액티브 회수 | CONFIG_DAMONCONFIG_DAMON_RECLAIM | core 1f366e421c8fvaddr 6dea8add4d28dbgfs af122dd8f3c0reclaim 43b0536cb471wmarks-core ee801b7dd782wmarks-dbgfs ae666a6dddfd |
| v5.17 | DAMON_RECLAIM 회수 통계 추가 | CONFIG_DAMON_RECLAIM | stat 60e52e7c46a1 |
| v5.18 | sysfs 인터페이스(/sys/kernel/mm/damon/admin/) 도입, virtual/physical monitoring, DAMOS 제어 | CONFIG_DAMON_SYSFS | stub c951cd3b8901vaddr a61ea561c871paddr 2031b14ea757DAMOS 7e84b1f8212aquotas 9bbb820a5bd5wmarks 1b32234ab087stats 0ac32b8affb5 |
| v5.19 | 고정 가상 주소 범위(fvaddr) 모니터링 | CONFIG_DAMON_SYSFS | vaddr de6d01542a5csysfs b82434471cd2 |
| v6.0 | DAMON_LRU_SORT, LRU_PRIO/LRU_DEPRIO 액션 | CONFIG_DAMON_LRU_SORT | module 40e983cca927LRU_PRIO 8cdcc532268dLRU_DEPRIO 99cdc2cd180a |
| v6.2 | reclaim/lru_sort enable/disable 동기화, 워터마크 기반 제어 정리 | CONFIG_DAMON_RECLAIMCONFIG_DAMON_LRU_SORT | reclaim 04e98764befalru_sort 7a034fbba336 |
| v6.3 | DAMOS 필터 프레임워크 (anon, memcg, addr) 및 sysfs 필터 디렉터리 | - | core 98def236f63cpaddr 18250e78f9c7filters-dir ac35264b9e88filter-dir 7ee161f18b5dconnect 472e2b70eda6scheme-filters 29cbb9a13f05 |
| v6.6 | target type DAMOS filter 추가 | - | core 17e7c724d3c2 |
| v6.7 | DAMOS apply interval 설정 | - | sysfs a2a9f68e358f |
| v6.8 | 목표 기반 피드백 쿼터 자동 튜닝 | - | core 9294a037c015 |
| v6.9 | 메모리 PSI 기반 쿼터 self-tuning (DAMON_RECLAIM) | CONFIG_DAMON_RECLAIM | reclaim 7ce55f8ffded |
| v6.11 | MIGRATE_HOT/MIGRATE_COLD 액션, NUMA 티어링 | - | MIGRATE_HOT b696722d784fMIGRATE_COLD b51820ebea65 |
| v6.15 | monitoring intervals auto-tuning, core_filters/ops_filters | - | struct 1eb3471bf574core f04b0fedbe71dirs db2e76ceb40bcommit 968cbea1bb0eops-field ab82e57981d0paddr ac7b094bf4d6 |
| v6.17 | refresh_ms, dests, DAMON_STAT | CONFIG_DAMON_STAT | refresh_ms b907494768e5refresh_ms-core d809a7c64ba8dests-field aabc85ee33c8dests-dir 2cd0bf85a203dests-commit cbc4eea4ffb5DAMON_STAT 369c415e6073 |
| v7.0-rc7 | young page filters, active/inactive 비율 quota goal | - | young-filter 303dbb1f08cfquota-goal 4835e2871321sysfs-goal fbec8a1e4fa4lru_sort-goal 40d98d31cd70 |
v5.15: DAMON 최초 머지
커널 v5.15(2021년 10월)에서 SeongJae Park이 개발한 DAMON이 Andrew Morton의 mm 트리를 통해 최초로 머지되었습니다. 이 버전에서는 코어 프레임워크(mm/damon/core.c), 가상/물리 주소 오퍼레이션(vaddr.c, paddr.c), debugfs 인터페이스가 포함되었습니다. 리전 기반 적응적 샘플링 알고리즘으로 기존 idle page tracking 대비 5~10배 낮은 오버헤드를 달성했습니다. 최초 머지 커밋은 2224d8485492입니다.
v5.16: DAMOS와 DAMON_RECLAIM
v5.16에서 DAMON은 "관찰"만 하는 프레임워크를 넘어 실제 메모리 정책(Memory Policy)을 수행하는 단계로 확장되었습니다. DAMOS 코어는 1f366e421c8f로 들어왔고, vaddr/dbgfs 지원은 6dea8add4d28, af122dd8f3c0로 이어졌습니다. 같은 릴리스에서 DAMON_RECLAIM도 43b0536cb471로 도입되었고, 워터마크 기반 활성화는 ee801b7dd782, ae666a6dddfd에서 정리되었습니다.
v5.18: sysfs 인터페이스 도입
v5.18(2022년 5월)에서 sysfs 인터페이스가 도입되면서 DAMON의 사용성이 크게 개선되었습니다. debugfs는 단일 파일에 여러 파라미터를 공백으로 나열하는 방식이었지만, sysfs는 /sys/kernel/mm/damon/admin/ 아래 계층적 디렉토리 구조로 각 설정을 개별 파일에 저장합니다. 이를 통해 원자적 설정 변경, 실시간 통계 조회, 다중 kdamond/컨텍스트 관리가 가능해졌습니다. 최소 뼈대는 c951cd3b8901이며, virtual/paddr/DAMOS 연결은 a61ea561c871, 2031b14ea757, 7e84b1f8212a에서 순차적으로 붙었습니다.
v6.0: DAMON_LRU_SORT
v6.0(2022년 10월)에서 DAMON_LRU_SORT 모듈이 추가되었습니다. 기존 DAMON_RECLAIM이 콜드 페이지를 회수(PAGEOUT)하는 데 초점을 맞춘 반면, DAMON_LRU_SORT는 핫 페이지의 LRU 우선순위를 올리고(LRU_PRIO) 콜드 페이지의 우선순위를 내려(LRU_DEPRIO) LRU 리스트의 정확도를 개선합니다. 이는 MGLRU와 상호 보완적으로 동작합니다. 대표 해시는 모듈 도입 40e983cca927, 액션 추가 8cdcc532268d, 99cdc2cd180a입니다.
v6.3+: DAMOS 필터 프레임워크
v6.3(2023년 4월)부터 DAMOS 필터가 도입되어 스킴 적용 대상을 세밀하게 제어할 수 있게 되었습니다. 최초 코어/물리 주소 지원은 98def236f63c, 18250e78f9c7이며, sysfs 디렉터리 구조는 ac35264b9e88부터 이어졌습니다. 이 시점의 기본 필터는 anon, memcg, addr였고, target 필터는 v6.6, young 페이지 필터는 mainline v7.0-rc7에서 추가되었습니다.
v6.15: auto-tuning과 filter 레이어 분리
v6.15에서는 모니터링 간격 자체를 workload 특성에 맞게 조절하는 intervals auto-tuning이 1eb3471bf574, f04b0fedbe71로 들어왔습니다. 동시에 core_filters/ops_filters 디렉터리와 커밋 경로가 db2e76ceb40b, 968cbea1bb0e로 정리되어, 공통 필터와 ops 전용 필터를 명시적으로 분리할 수 있게 되었습니다.
v6.17: 운영 제어면 고도화
v6.17은 최근 문서에서 눈에 띄는 운영 기능이 대거 정리된 릴리스입니다. refresh_ms는 b907494768e5, d809a7c64ba8로 들어왔고, 다중 마이그레이션 목적지인 dests는 aabc85ee33c8, 2cd0bf85a203, cbc4eea4ffb5로 정리되었습니다. 시스템 전체 유휴 시간 분포를 저비용으로 보는 DAMON_STAT은 369c415e6073에서 처음 도입되었습니다.
debugfs에서 sysfs 마이그레이션 타임라인
현재 공식 문서는 debugfs 인터페이스를 deprecated로 명시하고 있으며, 신규 배포판 스크립트와 운영 자동화는 sysfs 기준으로 작성할 것을 권고합니다. 특히 quota goal, tried regions, filter layer 분리 같은 최신 기능은 sysfs에서 훨씬 자연스럽게 표현됩니다.
/sys/kernel/mm/damon/admin/ 경로로 순차 전환하는 편이 안전합니다.
현재 상태
최신 DAMON 문서를 기준으로 보면, 요즘의 핵심 변화는 "새 액션이 생길 가능성"보다 운영 제어면이 더 정교해졌다는 점입니다. 같은 pageout 정책이라도 이제는 tuned interval, quota goal, tried regions, weighted destination, DAMON_STAT를 조합해 훨씬 세밀하게 운영할 수 있습니다.
| 현재 확인되는 항목 | 공식 인터페이스 | 실무 의미 |
|---|---|---|
| 자동 모니터링 간격 조정 | monitoring_attrs/intervals/intervals_goal/ | 워크로드가 바뀌어도 "관찰된 접근 비율"을 기준으로 정확도와 오버헤드를 자동 균형화합니다. |
| 정교한 피드백 제어 | quotas/goals/, effective_bytes | PSI, 노드 사용률, memcg 경로 사용률을 목표로 pageout 또는 tiering 강도를 조절할 수 있습니다. |
| 필터 계층 분리 | core_filters/, ops_filters/ | 정책 의도를 더 분명히 표현하고 디버깅 시 평가 순서를 추적하기 쉽습니다. |
| 다중 목적지 마이그레이션 | dests/0/id, dests/0/weight | CXL, DRAM, PMEM이 혼합된 환경에서 단순 1:1 노드 이동보다 현실적인 배치가 가능합니다. |
| 행동 결과 역추적 | stats/nr_snapshots, tried_regions/ | "왜 회수했는가"뿐 아니라 "실제로 어디까지 시도했는가"를 사후 분석할 수 있습니다. |
| 경량 관찰 모듈 | DAMON_STAT | 정책 적용 전 시스템 전체 idle 분포와 대역폭을 저비용으로 확인할 수 있습니다. |
damo 도구 저장소를 함께 보는 편이 좋습니다.
참고자료
권장 읽기 순서
- Getting Started -- 처음 한 번 읽어 전체 사용 흐름과
damo실습 감각을 잡기에 좋습니다 - Design -- DAMON 코어 구조와 DAMOS를 내부 관점에서 이해하기에 가장 중요합니다
- Detailed Usages -- 최신 sysfs/admin 트리와 실제 제어 명령을 익히기에 좋습니다
- API Reference -- 자료 구조와 함수 이름을 소스와 대조해 볼 때 필요합니다
- FAQ -- 페이지 단위 정밀도, vaddr/paddr 범위 같은 자주 헷갈리는 지점을 빠르게 정리해 줍니다
- DAMON_RECLAIM, DAMON_LRU_SORT, DAMON_STAT -- 목적별 운영 모듈을 이해할 때 읽으면 됩니다
공식 릴리스와 관리자 문서
- kernel.org 릴리스 페이지 -- 현재 stable과 mainline 상태를 확인하는 출발점입니다
- DAMON Admin Guide -- 관리자 관점의 입문과 운영 문서 모음입니다
- Getting Started -- 커널 설정, 준비,
damo설치 절차를 안내합니다 - Snapshot Data Access Patterns -- 일회성 스냅샷 관찰의 최소 경로를 보여 줍니다
- Recording Data Access Patterns -- 장시간 기록 수집 절차를 설명합니다
- Visualizing Recorded Patterns -- 수집한 결과를 시각화하는 흐름을 설명합니다
- Data Access Pattern Aware Memory Management -- 관찰에서 정책 적용으로 넘어가는 최소 예시입니다
- Detailed Usages -- 최신 sysfs/admin 트리 전체를 설명합니다
- sysfs Interface --
refresh_ms,state,schemes,targets구조를 볼 때 가장 중요합니다 - Tracepoints for Monitoring Results -- ftrace, perf, BPF 추적을 붙일 때 직접 참고할 수 있습니다
- DAMON-based Reclamation -- 프로액티브 회수 모듈의 동기, 동작, 파라미터를 설명합니다
- DAMON_RECLAIM Module Parameters -- 운영 중 조정해야 할 파라미터를 빠르게 찾기 좋습니다
- DAMON_RECLAIM Example -- 즉시 실험 가능한 예시입니다
- DAMON-based LRU-lists Sorting -- LRU 정렬 기반 보호/디모션 기법을 설명합니다
- DAMON_LRU_SORT Module Parameters -- LRU 정렬 파라미터 확인용입니다
- Data Access Monitoring Results Stat -- 경량 관찰 모듈 전체 설명입니다
- Monitoring Accuracy and Overhead -- 정확도와 오버헤드 균형을 이해할 때 중요합니다
공식 설계 문서와 API/FAQ
- DAMON Design Documentation -- 설계 문서 모음의 입구입니다
- Design -- execution model, operations set, core logics, modules를 자세히 설명합니다
- Execution Model and Data Structures --
damon_ctx,damon_target,damon_region관계를 이해할 때 유용합니다 - Operations Set Layer -- vaddr, fvaddr, paddr 차이를 파고들 때 필요합니다
- Core Logics -- 리전 병합/분할, 샘플링, 집계 로직을 따라가기 좋습니다
- Modules -- reclaim, lru_sort, stat 같은 모듈 배치를 요약해서 보여 줍니다
- API Reference -- 커널 내부 API 레퍼런스입니다
- API Structures -- 핵심 구조체 필드를 바로 확인할 수 있습니다
- API Functions -- 함수 이름으로 소스를 역추적할 때 편합니다
- FAQ -- 자주 혼동되는 설계 질문을 정리합니다
- Does DAMON support virtual memory only? -- paddr/fvaddr 범위와 한계를 확인할 수 있습니다
- Can I simply monitor page granularity? -- 리전 기반 추정과 페이지 단위 정밀도 사이의 trade-off를 이해하는 데 중요합니다
- Monitoring Intervals Auto-tuning Example --
intervals_goal기반 자동 튜닝 예시입니다 - DAMON Maintainer Entry Profile -- 기여 흐름, 리뷰 리듬, 메일링 툴, 커뮤니티 미팅 정보를 모아 둔 공식 페이지입니다
- Review Cadence -- 패치 리뷰가 어떤 리듬으로 돌아가는지 확인할 수 있습니다
- Mailing Tool -- 메일링 리스트 기반 기여에 필요한 도구 정보를 설명합니다
- Community Meetup -- DAMON 커뮤니티 미팅 정보를 제공합니다
공식 소스 트리
- include/linux/damon.h -- 핵심 자료 구조와 enum 정의입니다
- mm/damon/core.c -- kdamond 루프, region 적응, quota 처리의 중심 구현입니다
- mm/damon/ops-common.c -- Operations Set 공통 헬퍼입니다
- mm/damon/vaddr.c -- 가상 주소 모니터링 구현입니다
- mm/damon/paddr.c -- 물리 주소 모니터링과 DAMOS pageout/lru_sort 경로를 담습니다
- mm/damon/sysfs.c -- sysfs/admin 인터페이스 구현입니다
- mm/damon/sysfs-schemes.c -- schemes, filters, goals, dests sysfs 매핑 구현입니다
- mm/damon/dbgfs.c -- 레거시 debugfs 인터페이스를 이해하거나 마이그레이션할 때 참고할 수 있습니다
- mm/damon/reclaim.c -- DAMON_RECLAIM 모듈 구현입니다
- mm/damon/lru_sort.c -- DAMON_LRU_SORT 모듈 구현입니다
- mm/damon/stat.c -- DAMON_STAT 모듈 구현입니다
문서 원본과 문서 기여용 링크
- Documentation/admin-guide/mm/damon/ -- 관리자 문서 원본 디렉터리입니다
- Documentation/mm/damon/ -- 설계, API, FAQ, maintainer profile 원본 디렉터리입니다
- design.rst -- 설계 문서 원본입니다
- api.rst -- API 문서 원본입니다
- faq.rst -- FAQ 원본입니다
- maintainer-profile.rst -- 유지보수자 문서 원본입니다
공식 도구와 커뮤니티
- damo -- 공식 시작 문서에서 안내하는 사용자 공간 도구 저장소입니다
- damo README -- 설치, 개요, 기본 하위 명령을 빠르게 훑기에 좋습니다
- damo USAGE -- 실제 하위 명령과 옵션을 상세히 정리한 사용 문서입니다
- damo REPORTING -- 기록, 보고서, 시각화 흐름을 이해하는 데 유용합니다
- damo Features Deprecation Schedule -- 도구 인터페이스 변화와 호환성 경로를 확인할 수 있습니다
- lore.kernel.org/damon/ -- 최신 RFC, 리뷰, 회귀 논의를 추적할 수 있는 메일링 리스트 아카이브입니다
- SCM Trees -- 어떤 트리와 브랜치를 기준으로 패치가 흘러가는지 설명합니다
- Submit Checklist Addendum -- 패치 제출 전에 확인할 항목을 정리합니다
- Key Cycle Dates -- 머지 윈도와 리뷰 타이밍 감각을 잡기에 좋습니다