CXL (Compute Express Link) 메모리

CXL은 PCIe 물리 계층 위에 구축된 캐시 코히런트 인터커넥트로, CPU와 가속기/메모리 확장 장치 간 고속 연결을 제공합니다. CXL 3.0/3.1 프로토콜(io/cache/mem) 상세, Type 1/2/3 장치 아키텍처, HDM 디코더, 커널 드라이버 계층(drivers/cxl/), 리전 생성, 메모리 티어링, NUMA 노드 통합, CXL 보안/PMU, 포이즌 관리, QEMU 에뮬레이션까지 리눅스 커널 CXL 서브시스템의 모든 것을 다룹니다.

전제 조건: PCI/PCIe, NUMA, 고급 메모리 관리 문서를 먼저 읽으세요. CXL은 PCIe 기반 프로토콜이며, 커널에서 NUMA 노드로 노출되므로 두 주제의 기초 이해가 필요합니다.
일상 비유: CXL은 확장 서랍장과 비슷합니다. 책상(CPU)에 붙이는 서랍장(CXL 메모리)으로, 책상 위 공간(DDR)이 부족할 때 서랍에서 꺼내 씁니다. 서랍은 책상보다 조금 느리지만 공간이 훨씬 크고, 같은 집 안(캐시 코히런트)이라 내용이 항상 최신 상태입니다.

핵심 요약

  • 3개 서브프로토콜 — CXL.io(PCIe 호환), CXL.cache(디바이스에서 CPU 캐시), CXL.mem(CPU에서 디바이스 메모리)
  • 3가지 장치 유형 — Type1(캐시만), Type2(캐시+메모리, GPU), Type3(메모리만, 확장기)
  • NUMA 노드로 노출 — CXL 메모리는 별도 NUMA 노드로 추가되어 numactl로 제어
  • 캐시 코히런시 — CPU 캐시와 CXL 장치 캐시 간 자동 일관성 유지
  • memory_tier 프레임워크 — DDR > CXL > PMEM 계층에서 자동 demotion/promotion
  • HDM 디코더 — Host-managed Device Memory 디코더가 HPA를 DPA로 매핑
  • CXL 3.0/3.1 — Back-Invalidate, 멀티헤드, 패브릭 스위칭, GFD(Global Fabric Attached Memory) 지원
  • cxl-cli + ndctl — 사용자 공간 CXL 관리 도구

단계별 이해

  1. CXL 프로토콜 3종 차이 파악
    CXL.io/cache/mem이 각각 어떤 역할인지 이해합니다.
  2. Type1/2/3 장치 용도 구분
    AI 가속기(Type2)와 메모리 확장기(Type3)의 차이를 파악합니다.
  3. HDM 디코더 구조 학습
    HPA(Host Physical Address)에서 DPA(Device Physical Address)로의 매핑 과정을 이해합니다.
  4. 커널 drivers/cxl/ 구조 탐색
    cxl_core, cxl_port, cxl_mem 모듈의 역할을 이해합니다.
  5. 리전 생성과 NUMA 노드 확인 실습
    CXL 장치가 lscpu/numactl에 어떻게 나타나는지 확인합니다.
  6. memory_tier 정책 설정
    자동 demotion/promotion 정책을 조정하는 방법을 익힙니다.

CXL 개요

CXL(Compute Express Link)은 인텔이 주도하고 AMD, ARM, Nvidia, 삼성, SK하이닉스 등이 참여하는 캐시 코히런트 인터커넥트 표준입니다. PCIe 물리 계층 위에 구축되어, 기존 PCIe 장치와 동일한 슬롯을 사용하면서 CPU 캐시 계층과 통합된 메모리 액세스를 제공합니다.

CXL 버전 히스토리: CXL 1.0(2019, PCIe 5.0)에서 시작하여 CXL 1.1(PCIe 5.0, FLIT 모드 도입), CXL 2.0(스위칭/풀링), CXL 3.0(2022, PCIe 6.0, 256B FLIT, Back-Invalidate, 패브릭 연결), CXL 3.1(2024, 보안 강화, 포트 기반 라우팅, GFD)로 진화하고 있습니다. 리눅스 커널 6.2 이상에서 CXL 2.0 핵심 기능, 6.6 이상에서 CXL 3.0 일부를 지원합니다.
특성PCIe (일반)CXL
캐시 코히런시없음 (소프트웨어 관리)하드웨어 자동 보장
메모리 공유DMA 전용Load/Store 직접 접근
지연시간수십 us (PCIe TLP)200~400ns (DDR 유사)
NUMA 통합불가NUMA 노드로 노출
FLIT 모드68B FLIT (PCIe 6.0)256B FLIT (CXL 3.0)
코히런시 프로토콜없음Snoop 기반 MESI 확장
물리 인터페이스PCIe 슬롯동일 PCIe 슬롯 사용
최대 대역폭256 GB/s (PCIe 6.0 x16)동일 (물리 계층 공유)

CXL 버전별 비교

항목CXL 1.1CXL 2.0CXL 3.0CXL 3.1
PCIe 기반PCIe 5.0PCIe 5.0PCIe 6.0PCIe 6.0
FLIT 크기68B68B256B256B
대역폭 (x16)~128 GB/s~128 GB/s~256 GB/s~256 GB/s
스위칭지원 안함CXL 스위치패브릭 스위치패브릭+포트 라우팅
풀링지원 안함단일 호스트 풀링다중 호스트 공유향상된 GFD
Back-Invalidate없음없음지원 (BI)지원 (BI+)
보안IDE (기본)IDEIDE + CMAIDE + CMA + TSP
커널 지원 시작Linux 5.14Linux 6.0Linux 6.6+개발 중
CXL 1.0/1.1 2019~2020 PCIe 5.0 68B FLIT CXL 2.0 2021 스위칭/풀링 MLD(Multi-LD) CXL 3.0 2022 PCIe 6.0, 256B FLIT Back-Invalidate CXL 3.1 2024 TSP 보안 GFD, 포트 라우팅 Linux 5.14+ 기본 CXL 버스 Linux 6.0~6.5 리전, DAX, NUMA Linux 6.6+ PMU, 포이즌, TSP Linux 6.10+ 3.0 패브릭 (진행 중) CXL 규격 진화와 리눅스 커널 지원 타임라인 CXL 컨소시엄은 2022년 PCIe-SIG로 이관 (CXL 3.0부터 PCIe 표준 통합)

CXL 서브프로토콜

CXL은 3개의 서브프로토콜로 구성되며, 장치 유형에 따라 일부 또는 전체를 구현합니다. 이 3개 프로토콜은 동일한 PCIe 물리 계층 위에서 FLIT(Flow Control Unit) 기반으로 다중화됩니다.

CPU (Host) 측 CPU Cache (L3) + Home Agent 캐시 코히런시 엔진 — Snoop 필터, 디렉터리 CPU DDR5 메모리 (로컬 DRAM) CXL 링크 계층 (FLIT 다중화) CXL.io PCIe TLP 설정/IRQ/DMA CXL.cache D2H Req/Rsp H2D Rsp/Data CXL.mem M2S Req/RwD S2M NDR/DRS ARB/MUX (프로토콜 다중화기) PCIe 6.0 물리 계층 (64 GT/s PAM4) 256B FLIT + FEC (CRC-32) CXL 장치 (Device) 측 Type3: 메모리 확장기 DDR5 DRAM / HBM3 / LPDDR5 / CXL DRAM Type2: GPU / 가속기 (캐시+메모리) CXL Device 링크 계층 CXL.io Config / IRQ Mailbox CXL.cache 캐시 스누핑 DCOH 엔진 CXL.mem HDM 디코더 메모리 컨트롤러 PCIe 6.0 물리 계층 (동일) 256B FLIT + FEC (CRC-32) CXL 링크 CXL.io(녹색), CXL.cache(주황), CXL.mem(빨강) 3개 프로토콜이 ARB/MUX를 통해 FLIT으로 다중화

CXL.io 프로토콜 상세

CXL.io는 PCIe TLP(Transaction Layer Packet)와 동일한 프로토콜입니다. CXL 장치의 설정 공간(Configuration Space), MSI/MSI-X 인터럽트, DMA 전송, 그리고 CXL 메일박스(Mailbox) 통신에 사용됩니다. 모든 CXL 장치(Type1/2/3)는 CXL.io를 반드시 구현해야 합니다.

# CXL.io를 통한 장치 설정 공간 읽기
lspci -vvv -d ::0502  # CXL Type3 장치 (Class 0502)
# Capabilities: [xx] CXL cap  (DVSEC)
#   CXLCtl: Cache- IO+ Mem+ ...  ← 프로토콜 활성 상태
#   CXLSta: Viral- ← 바이러스 전파 상태

# DVSEC (Designated Vendor Specific Extended Capability) 확인
lspci -vvv | grep -A 20 "CXL"
# DVSEC ID 0: CXL Device Register Locator
# DVSEC ID 1: CXL Function DVSEC
# DVSEC ID 2: CXL Port DVSEC
# DVSEC ID 7: CXL Range Lock
# DVSEC ID 8: CXL GPF for Port

CXL.cache 프로토콜 상세

CXL.cache는 장치가 호스트 CPU의 메모리를 캐시할 때 사용하는 프로토콜입니다. Device-to-Host(D2H) 방향으로 Request/Response 채널이, Host-to-Device(H2D) 방향으로 Response/Data/Snoop 채널이 운용됩니다. MESI 프로토콜의 확장으로, 장치 캐시의 일관성을 하드웨어적으로 보장합니다.

채널방향메시지 유형설명
D2H ReqDevice → HostRdCurr, RdOwn, RdShared, ItoMWr, WrInv장치가 호스트 메모리 읽기/쓰기 요청
D2H RspDevice → HostWritePull, GO_WritePull장치가 스누프에 대한 응답
D2H DataDevice → Host캐시 라인 데이터스누프 결과 데이터 전달
H2D Req (Snoop)Host → DeviceSnpData, SnpInv, SnpCur호스트가 장치 캐시 스누핑
H2D RspHost → DeviceGO-I, GO-S, GO-E, GO-M, WritePull호스트 요청 완료 응답
H2D DataHost → Device캐시 라인 데이터요청된 데이터 전달

CXL.mem 프로토콜 상세

CXL.mem은 호스트 CPU가 CXL 장치의 메모리에 직접 Load/Store로 접근할 때 사용합니다. Master-to-Subordinate(M2S) 방향으로 Request/Read-Write-Data 채널이, Subordinate-to-Master(S2M) 방향으로 NDR(No Data Response)/DRS(Data Response) 채널이 운용됩니다.

채널방향메시지 유형설명
M2S ReqHost → DeviceMemRd, MemWr, MemRdData호스트 메모리 읽기/쓰기 요청
M2S RwDHost → DeviceMemRd, MemWr + Data쓰기 데이터 포함 요청
S2M NDRDevice → HostCmp, Cmp-S, Cmp-E완료 응답 (데이터 없음)
S2M DRSDevice → HostMemData읽기 데이터 응답
CXL 3.0 Back-Invalidate(BI): CXL 3.0에서 추가된 Back-Invalidate 채널은 Device-to-Host 방향으로 장치가 호스트 캐시의 특정 주소를 무효화할 수 있게 합니다. 이를 통해 여러 호스트가 하나의 CXL 메모리를 공유할 때 캐시 일관성을 유지합니다. BI는 메모리 풀링/공유 시나리오에서 핵심적인 역할을 합니다.
/* CXL 3.0 FLIT 구조 (256바이트) */
/*
 * 256B FLIT 레이아웃:
 * ┌────────────────────────────────────────┐
 * │ Header (2B)  : 프로토콜 ID + 슬롯 맵   │
 * │ Slot 0 (16B) : CXL.io / cache / mem    │
 * │ Slot 1 (16B) : CXL.io / cache / mem    │
 * │ Slot 2 (16B) : CXL.io / cache / mem    │
 * │ Slot 3 (16B) : CXL.io / cache / mem    │
 * │ Data (186B)  : 캐시 라인 데이터         │
 * │ CRC (4B)     : FEC 보호                 │
 * └────────────────────────────────────────┘
 * 하나의 FLIT에 여러 프로토콜 메시지를 동시 전송
 */

Type1 / Type2 / Type3 장치 비교

Type 1 CXL.io + CXL.cache 스마트 NIC / FPGA 자체 캐시 있음 자체 메모리 없음 (HDM 불필요) 캐시 코히런시만 필요한 네트워크/보안 가속기 예: Intel IPU E2000 Type 2 CXL.io + CXL.cache + CXL.mem GPU / AI 가속기 자체 캐시 + VRAM 모두 CPU와 GPU 캐시 동기화 HMM ZONE_DEVICE Coherent 포인터 공유 가능 예: AMD MI300, 향후 GPU Type 3 CXL.io + CXL.mem 메모리 확장기 (DIMM/E3.S) 자체 캐시 없음 (HDM 관리) 대용량 메모리 (수 TB) AI 모델 가중치 저장 LLM KV 캐시 풀링 예: 삼성 CMM, SK hynix CMS
특성Type 1Type 2Type 3
CXL.io지원지원지원
CXL.cache지원지원지원 안함
CXL.mem지원 안함지원 (HDM-DB)지원 (HDM-H)
HDM 유형없음HDM-DB (Device-Bias)HDM-H (Host-Only)
호스트 캐싱가능가능가능
디바이스 캐싱가능가능불가
대표 장치SmartNIC, FPGAGPU, AI 가속기메모리 확장기
NUMA 노드없음있음 (VRAM)있음
커널 드라이버cxl_pcicxl_pci + HMMcxl_mem + DAX
CDAT (Coherent Device Attribute Table): CXL Type3 장치는 CDAT를 통해 자신의 메모리 지연(latency)과 대역폭(bandwidth) 특성을 커널에 알립니다. 커널은 이 정보를 HMAT(Heterogeneous Memory Attribute Table)로 통합하여 memory_tier 프레임워크에 반영합니다. 결과적으로 커널 NUMA 밸런서가 CXL 메모리를 적절한 tier에 배치하고 자동으로 demotion/promotion을 수행합니다. CDAT는 CXL.io 메일박스를 통해 DOE(Data Object Exchange) 프로토콜로 전달됩니다.

HDM 디코더와 주소 변환

HDM(Host-managed Device Memory) 디코더는 CXL 메모리의 핵심 하드웨어 컴포넌트입니다. CPU가 발행하는 HPA(Host Physical Address)를 장치의 DPA(Device Physical Address)로 변환하며, 멀티 장치 인터리브를 지원합니다. CXL 토폴로지의 각 레벨(루트, 스위치, 엔드포인트)에 디코더가 존재합니다.

HDM 디코더 계층 구조 — HPA에서 DPA 변환 CPU (HPA 발행) CFMWS (CXL Fixed Memory Window) ACPI CEDT에서 정의, HPA 범위 지정 Root Decoder (cxl_root_decoder) 인터리브 방식, 대상 포트 결정 Switch Decoder 0 포트 라우팅 Switch Decoder 1 포트 라우팅 직접 연결 (스위치 없음) 루트 → 엔드포인트 Endpoint Decoder (mem0) HPA → DPA 최종 변환 Endpoint Decoder (mem1) HPA → DPA 최종 변환 Endpoint Decoder (mem2) HPA → DPA 최종 변환 DPA (DRAM 0) DPA (DRAM 1) DPA (DRAM 2)
/* drivers/cxl/core/hdm.c — HDM 디코더 구조 */

/* CXL 루트 디코더: CFMWS HPA 범위를 루트 포트에 매핑 */
struct cxl_root_decoder {
    struct cxl_switch_decoder cxlsd;
    struct cxl_dport        *cxlsd.target[CXL_DECODER_MAX_INTERLEAVE];
    resource_size_t          res;      /* CFMWS HPA 범위 */
};

/* CXL 스위치 디코더: 스위치 포트 라우팅 */
struct cxl_switch_decoder {
    struct cxl_decoder       cxld;
    int                      nr_targets;
    struct cxl_dport        *target[];
};

/* CXL 엔드포인트 디코더: HPA → DPA 최종 변환 */
struct cxl_endpoint_decoder {
    struct cxl_decoder       cxld;
    resource_size_t          dpa_res;   /* DPA 시작 주소 */
    resource_size_t          skip;      /* 인터리브 스킵 바이트 */
    enum cxl_decoder_mode    mode;      /* RAM 또는 PMEM */
    enum cxl_decoder_state   state;     /* 커밋 상태 */
};

/* 공통 디코더 기본 구조 */
struct cxl_decoder {
    struct device            dev;
    int                      id;
    struct range            hpa_range;        /* Host Physical Address 범위 */
    int                      interleave_ways;  /* 인터리브 배수 (1,2,4,8,16) */
    int                      interleave_granularity; /* 인터리브 단위 (256B~16KB) */
    enum cxl_decoder_type   target_type;      /* accelerator 또는 expander */
    unsigned long            flags;
};

인터리브 매핑 방식

CXL 디코더는 인터리브(interleave)를 통해 여러 CXL 장치에 메모리 접근을 분산시킵니다. 인터리브 방식(ways)과 단위(granularity)에 따라 HPA의 특정 비트가 대상 장치를 결정합니다.

# 디코더 인터리브 설정 확인
cat /sys/bus/cxl/devices/decoder0.0/interleave_ways      # 2 (2-way 인터리브)
cat /sys/bus/cxl/devices/decoder0.0/interleave_granularity # 256 (256바이트 단위)

# HPA → DPA 변환 예시 (2-way, 256B granularity)
# HPA 비트[8] 이 0이면 → mem0 (DPA = HPA[63:9] || HPA[7:0])
# HPA 비트[8] 이 1이면 → mem1 (DPA = HPA[63:9] || HPA[7:0])

# 인터리브 방식별 대역폭 예상
# 1-way: ~50 GB/s (단일 장치)
# 2-way: ~100 GB/s (2개 장치 병렬)
# 4-way: ~200 GB/s (4개 장치 병렬)
# 8-way: ~256 GB/s (PCIe 6.0 x16 최대)
인터리브 제약: 인터리브 ways는 반드시 2의 거듭제곱(1, 2, 4, 8, 16)이어야 합니다. 인터리브 granularity는 최소 256바이트에서 최대 16KB까지 설정 가능합니다. 인터리브에 참여하는 모든 CXL 장치는 동일한 용량이어야 하며, 인터리브 구성 후 변경하려면 리전을 삭제하고 재생성해야 합니다.

커널 드라이버 구조 (drivers/cxl/)

Linux 5.14부터 drivers/cxl/에 CXL 드라이버가 추가되었습니다. 계층화된 모듈 구조로 CXL 포트 토폴로지, 메모리 장치, 영역(region)을 관리합니다. 드라이버는 cxl_bus라는 전용 버스를 사용하며, 각 레벨의 장치가 이 버스에 등록됩니다.

CXL 커널 드라이버 모듈 아키텍처 사용자 공간 cxl-cli (ndctl) daxctl numactl / libnuma /sys/bus/cxl/ + /sys/bus/dax/ + /sys/devices/system/node/ cxl_core.ko CXL 버스, 리전, 디코더, 포트, memdev, PMU, 포이즌, CDAT 파싱 cxl_acpi.ko CEDT/CFMWS 파싱 cxl_port.ko 포트/스위치 토폴로지 cxl_mem.ko CXL 메모리 장치 cxl_pci.ko PCIe 장치 바인딩 ACPI 서브시스템 device core DAX / memory-tiers PCI 서브시스템 CXL 하드웨어: 루트 포트 + 스위치 + 엔드포인트 (Type1/2/3 장치)

드라이버 모듈 계층

# CXL 드라이버 계층 (lsmod로 확인)
lsmod | grep cxl
# cxl_acpi      — ACPI CEDT 테이블 파싱, CFMWS 등록
# cxl_port      — CXL 포트 (스위치, 루트 포트) 관리
# cxl_core      — CXL 코어 — 리전, 디코더, memdev, PMU
# cxl_mem       — CXL.mem 메모리 장치 드라이버
# cxl_pci       — PCIe 기반 CXL 장치 바인딩
# cxl_pmem      — CXL PMEM (비휘발성 메모리) 지원

# CXL 장치 확인
ls /sys/bus/cxl/devices/
# mem0     — CXL 메모리 장치 (Type3)
# port1    — CXL 루트 포트
# decoder0.0 — 루트 디코더
# endpoint2 — CXL 엔드포인트
# region0  — CXL 리전 (활성화된 경우)
# root0    — CXL 루트 (ACPI)

# 모듈 의존성 확인
modinfo cxl_mem | grep depends
# depends: cxl_core,cxl_port

# CXL 장치 상세 정보
cat /sys/bus/cxl/devices/mem0/firmware_version
cat /sys/bus/cxl/devices/mem0/payload_max
cat /sys/bus/cxl/devices/mem0/serial

CXL 메일박스 프로토콜

CXL 메일박스(Mailbox)는 호스트와 CXL 장치 간의 명령/응답 통신 채널입니다. CXL.io를 통해 MMIO 레지스터에 접근하며, 장치 초기화, 건강 상태 조회, 포이즌 관리, 보안 명령 등을 처리합니다.

/* drivers/cxl/core/mbox.c — 메일박스 명령 전송 */

struct cxl_mbox_cmd {
    u16  opcode;         /* 명령 코드 */
    u16  return_code;    /* 응답 코드 */
    size_t size_in;      /* 입력 페이로드 크기 */
    size_t size_out;     /* 출력 페이로드 크기 */
    void   *payload_in;  /* 입력 데이터 */
    void   *payload_out; /* 출력 데이터 */
};

/* 주요 메일박스 명령 (opcode) */
#define CXL_MBOX_OP_IDENTIFY          0x4000  /* 장치 식별 */
#define CXL_MBOX_OP_GET_HEALTH_INFO   0x4200  /* 건강 상태 */
#define CXL_MBOX_OP_GET_POISON        0x4300  /* 포이즌 목록 */
#define CXL_MBOX_OP_INJECT_POISON     0x4301  /* 포이즌 주입 */
#define CXL_MBOX_OP_CLEAR_POISON      0x4302  /* 포이즌 제거 */
#define CXL_MBOX_OP_GET_SCAN_MEDIA    0x4304  /* 미디어 스캔 */
#define CXL_MBOX_OP_SET_SHUTDOWN      0x4500  /* 정상 종료 */
#define CXL_MBOX_OP_GET_SEC_STATE     0x4600  /* 보안 상태 */

/* 메일박스 전송 함수 */
int cxl_internal_send_cmd(struct cxl_memdev_state *mds,
                          struct cxl_mbox_cmd *cmd)
{
    /* 1. 메일박스 잠금 획득 */
    mutex_lock(&mds->mbox_mutex);
    /* 2. 페이로드 레지스터에 입력 데이터 쓰기 */
    memcpy_toio(mds->payload_regs, cmd->payload_in, cmd->size_in);
    /* 3. 명령 레지스터에 opcode + 크기 쓰기 */
    writeq(FIELD_PREP(CXLDEV_MBOX_CMD_COMMAND_MASK, cmd->opcode) |
           FIELD_PREP(CXLDEV_MBOX_CMD_PAYLOAD_LENGTH_MASK, cmd->size_in),
           mds->regs.mbox + CXLDEV_MBOX_CMD_OFFSET);
    /* 4. 도어벨 레지스터 쓰기 (전송 시작) */
    writel(1, mds->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET);
    /* 5. 완료 대기 (폴링 또는 인터럽트) */
    cxl_pci_mbox_wait_for_doorbell(mds);
    mutex_unlock(&mds->mbox_mutex);
    return 0;
}

리전 생성과 관리

CXL 리전(Region)은 하나 이상의 CXL 메모리 장치를 묶어 연속된 HPA 범위로 노출하는 논리적 단위입니다. 리전이 생성되면 DAX(Direct Access) 장치로 등록되고, 이를 system-ram 모드로 전환하면 NUMA 노드의 메모리로 사용할 수 있습니다.

CXL 리전 생성에서 NUMA 노드 등록까지 1. 장치 발견 cxl_pci 바인딩 DVSEC 파싱 2. 토폴로지 구성 cxl_port 열거 디코더 프로그래밍 3. 리전 생성 cxl create-region HPA 범위 할당 4. DAX 장치 dax0.0 생성 /dev/dax0.0 5. system-ram 모드 daxctl reconfigure-device --mode=system-ram 6. NUMA 노드 등록 /sys/devices/system/node/nodeN memory_tier 프레임워크 연동 7. 메모리 사용 가능 numactl --membind=N 자동 demotion/promotion 핵심 명령어 흐름 cxl list -M 장치 확인 cxl_pci.ko cxl list -P 포트 확인 cxl_port.ko cxl create-region 리전 생성 cxl_core.ko daxctl reconfigure DAX 전환 dax_cxl.ko numactl -H NUMA 확인 mm/memory-tiers.c
# 전체 리전 생성 워크플로

# 1단계: CXL 장치 발견 확인
cxl list -M
# [{"memdev":"mem0","ram_size":"256.00 GiB","serial":"0x1234..."}]

# 2단계: 디코더 확인
cxl list -D -d root
# [{"decoder":"decoder0.0","interleave_ways":1,...}]

# 3단계: 리전 생성 (1-way, 단일 장치)
cxl create-region -m mem0 -d decoder0.0 -w 1 -g 256 -s 256G
# 결과: region0 생성됨

# 4단계: DAX 장치 확인
daxctl list
# [{"chardev":"dax0.0","size":"256.00 GiB","mode":"devdax"}]

# 5단계: system-ram 모드로 전환 (NUMA 노드 추가)
daxctl reconfigure-device --mode=system-ram dax0.0
# [{"chardev":"dax0.0","size":"256.00 GiB","mode":"system-ram",
#   "movable":true}]

# 6단계: NUMA 노드 확인
numactl --hardware
# available: 3 nodes (0,1,2)
# node 2: CXL Type3 메모리 (256 GB)
# node 2 cpus:          (비어있음 — CPU 없는 메모리 전용 노드)
# node 2 size: 262144 MB

# 인터리브 리전 생성 예시 (2-way)
cxl create-region -m mem0 -m mem1 -d decoder0.0 -w 2 -g 256 -s 512G
/* drivers/cxl/core/region.c — CXL 리전 생성 핵심 */

struct cxl_region {
    struct device              dev;
    int                        id;
    enum cxl_decoder_mode      mode;      /* CXL_DECODER_RAM 또는 CXL_DECODER_PMEM */
    enum cxl_decoder_type      type;
    struct cxl_region_params   params;
    struct cxl_pmem_region     *cxlr_pmem;
    unsigned long              flags;
    struct resource            *res;       /* HPA 리소스 */
};

struct cxl_region_params {
    enum cxl_config_state     state;
    int                        interleave_ways;
    int                        interleave_granularity;
    resource_size_t            res_size;       /* 리전 크기 */
    struct cxl_endpoint_decoder *targets[CXL_DECODER_MAX_INTERLEAVE];
    int                        nr_targets;     /* 타겟 수 */
    resource_size_t            cache_size;
    uuid_t                     uuid;
};

/* 리전 커밋 — 디코더 프로그래밍 */
static int cxl_region_attach(struct cxl_region *cxlr,
                              struct cxl_endpoint_decoder *cxled,
                              int pos)
{
    /* 1. 엔드포인트 디코더를 리전 타겟에 추가 */
    cxlr->params.targets[pos] = cxled;
    /* 2. 인터리브 설정 검증 */
    if (cxled->cxld.interleave_ways != cxlr->params.interleave_ways)
        return -EINVAL;
    /* 3. 모든 타겟이 설정되면 디코더 커밋 */
    if (++cxlr->params.nr_targets == cxlr->params.interleave_ways)
        return cxl_region_decode_commit(cxlr);
    return 0;
}

메모리 티어링 프레임워크

Linux 6.1+의 memory_tier 프레임워크는 HMAT/CDAT 기반 메모리 지연 정보를 이용하여 DDR, CXL, PMEM 계층 간 자동 demotion/promotion을 수행합니다. CXL 메모리가 NUMA 노드로 등록되면, 해당 노드의 성능 특성에 따라 자동으로 적절한 티어에 배치됩니다.

메모리 티어 계층과 Demotion/Promotion 흐름 Tier 0: DDR5 (로컬 DRAM) 지연: ~80ns / 대역폭: ~200 GB/s NUMA node 0,1 / 가장 빠른 메모리 Demotion Promotion Tier 1: CXL Type3 지연: ~300ns / 대역폭: ~50-100 GB/s NUMA node 2 / 대용량 확장 메모리 Demotion Promotion Tier 2: PMEM / CXL PMEM 지연: ~500ns+ / 비휘발성 NUMA node 3 / 또는 Swap 대상 Hot 페이지 (자주 접근) Warm 페이지 (간헐 접근) Cold 페이지 (거의 미접근) NUMA fault promotion 트리거 kswapd demotion 트리거 memory_tier 프레임워크가 HMAT/CDAT 지연 정보 기반으로 자동 티어 배치

memory_tier sysfs 인터페이스

# 메모리 티어 구조 확인
ls /sys/devices/virtual/memory_tiering/
# memory_tier0 — DDR5 (가장 빠름, adistance: 기본값)
# memory_tier1 — CXL Type3 (중간, adistance 기반)
# memory_tier2 — PMEM/Optane (느림)

# 각 티어의 NUMA 노드 확인
cat /sys/devices/virtual/memory_tiering/memory_tier0/nodelist  # 0,1
cat /sys/devices/virtual/memory_tiering/memory_tier1/nodelist  # 2

# 추상 거리(abstract distance) 확인 — 티어 결정 기준
# HMAT 지연 기반으로 계산됨
cat /sys/devices/system/node/node0/access0/initiators/read_latency   # 80 (ns)
cat /sys/devices/system/node/node2/access0/initiators/read_latency   # 300 (ns)

# NUMA 밸런싱 활성화 (자동 demotion/promotion)
echo 1 > /proc/sys/kernel/numa_balancing

# demotion 활성화
echo true > /sys/kernel/mm/numa/demotion_enabled

# perf로 메모리 지연 프로파일링
perf stat -e mem_load_retired.l3_miss \
          -e mem_load_retired.local_dram \
          -e mem_load_retired.remote_dram \
          -p $(pidof my_app) -- sleep 5

Demotion / Promotion 커널 내부

/* mm/memory-tiers.c — 메모리 티어 관리 */

struct memory_tier {
    struct list_head     list;       /* 전역 티어 리스트 */
    struct device        dev;
    nodemask_t           lower_tier_mask; /* 하위 티어 노드들 */
    int                  adistance_start; /* 추상 거리 시작 */
};

/* 추상 거리(adistance)로 티어 결정 */
/* DRAM: 기본 MEMTIER_ADISTANCE_DRAM (512)  */
/* CXL:  HMAT 지연 기반 계산 (~680~800)      */
/* PMEM: MEMTIER_ADISTANCE_PMEM (1024)       */

/* mm/vmscan.c — 페이지 demotion 흐름 */
static bool can_demote(int nid, struct scan_control *sc)
{
    if (!numa_demotion_enabled)
        return false;
    /* nid에서 더 낮은 tier 노드가 있는지 확인 */
    return next_demotion_node(nid) != NUMA_NO_NODE;
}

/* demotion 경로: kswapd → shrink_folio_list → demote_folio_list */
static unsigned int demote_folio_list(
    struct list_head *demote_folios,
    struct pglist_data *pgdat)
{
    int target_nid = next_demotion_node(pgdat->node_id);
    /* migrate_pages()로 DDR → CXL 노드로 이동 */
    return migrate_pages(demote_folios, alloc_demote_folio,
                          NULL, target_nid, MIGRATE_ASYNC, MR_DEMOTION, NULL);
}

/* promotion 경로: NUMA fault → do_numa_page → migrate_misplaced_folio */
/* CPU가 CXL 메모리의 페이지에 접근하면 NUMA fault 발생 */
/* 접근 빈도가 높은 페이지는 DDR로 promotion */
Demotion/Promotion 튜닝: 실전에서는 /proc/sys/kernel/numa_balancing_scan_delay_ms(기본 1000ms)와 /proc/sys/kernel/numa_balancing_scan_size_mb(기본 256MB)를 조정하여 promotion 빈도를 제어합니다. AI 워크로드에서는 scan_delay를 500ms로 줄여 hot 페이지의 promotion 속도를 높이는 것이 효과적입니다.

NUMA 통합과 numactl 활용

CXL 메모리는 커널이 부팅 시 또는 핫플러그 시 별도 NUMA 노드로 인식합니다. 일반 NUMA API(numactl, libnuma, move_pages)로 CXL 노드에 메모리를 할당하거나 특정 프로세스를 바인딩할 수 있습니다. CXL NUMA 노드는 CPU가 없는 메모리 전용 노드(CPU-less node)로 생성됩니다.

# CXL NUMA 노드 확인
numactl --hardware
# available: 3 nodes (0,1,2)
# node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11
# node 0 size: 131072 MB
# node 1 cpus: 12 13 14 15 16 17 18 19 20 21 22 23
# node 1 size: 131072 MB
# node 2 cpus:          ← CPU 없음 (CXL 메모리 전용)
# node 2 size: 262144 MB
# node distances:
#   node   0   1   2
#     0:  10  20  30    ← CXL 노드(2)까지 거리 30
#     1:  20  10  30
#     2:  30  30  10

# AI 추론을 CXL 메모리 노드에 바인딩 (모델 가중치를 CXL에)
numactl --membind=2 --cpunodebind=0 python llm_inference.py

# 인터리브 모드 (DDR + CXL 분산 할당)
numactl --interleave=0,2 ./my_application

# 선호 노드 설정 (DDR 우선, 부족 시 CXL 사용)
numactl --preferred=0 --membind=0,2 ./my_application

# 메모리 노드 간 마이그레이션
migratepages $(pidof llm_server) 0 2  # node0 → node2 이동

# NUMA 통계 모니터링
numastat -p $(pidof llm_server)
numastat -m  # 시스템 전체 NUMA 메모리 사용 현황

# HMAT 정보 확인
cat /sys/devices/system/node/node2/access0/initiators/read_latency
cat /sys/devices/system/node/node2/access0/initiators/read_bandwidth
cat /sys/devices/system/node/node2/access0/initiators/write_latency
cat /sys/devices/system/node/node2/access0/initiators/write_bandwidth
/* libnuma를 이용한 CXL 노드 프로그래밍 */
#include <numa.h>
#include <numaif.h>

void allocate_on_cxl(size_t size) {
    int cxl_node = 2;  /* CXL NUMA 노드 */

    /* 방법 1: numa_alloc_onnode */
    void *ptr = numa_alloc_onnode(size, cxl_node);
    if (!ptr)
        perror("CXL 노드 할당 실패");

    /* 방법 2: mbind (기존 매핑을 CXL로 이동) */
    unsigned long nodemask = 1UL << cxl_node;
    mbind(ptr, size, MPOL_BIND, &nodemask,
          sizeof(nodemask) * 8, MPOL_MF_MOVE);

    /* 방법 3: move_pages (특정 페이지 이동) */
    int nodes[1] = { cxl_node };
    void *pages[1] = { ptr };
    int status[1];
    move_pages(0, 1, pages, nodes, status, MPOL_MF_MOVE);
}

CXL 보안 (IDE, CMA, TSP)

CXL은 PCIe 링크 위에서 동작하므로 물리적 공격(프로빙, 스니핑)에 노출될 수 있습니다. 이를 방어하기 위해 IDE(Integrity and Data Encryption), CMA(CXL Memory Authentication), TSP(TEE Security Protocol) 등의 보안 메커니즘을 제공합니다.

보안 메커니즘CXL 버전보호 대상방식
IDE (Integrity and Data Encryption)CXL 2.0+링크 데이터AES-256-GCM 암호화 + 무결성 태그
CMA (CXL Memory Authentication)CXL 3.0+메모리 데이터ECC + MAC (Message Authentication Code)
TSP (TEE Security Protocol)CXL 3.1+TEE 데이터하드웨어 격리 + 암호화된 메모리 영역
Secure EraseCXL 2.0+장치 데이터메일박스 명령으로 안전 삭제
SanitizeCXL 2.0+장치 데이터암호화 키 파기 + 미디어 덮어쓰기
/* drivers/cxl/core/mbox.c — CXL 보안 명령 */

/* 보안 상태 조회 */
#define CXL_MBOX_OP_GET_SEC_STATE     0x4600
#define CXL_MBOX_OP_SET_PASSPHRASE    0x4601
#define CXL_MBOX_OP_DISABLE_PASSPHRASE 0x4602
#define CXL_MBOX_OP_UNLOCK            0x4603
#define CXL_MBOX_OP_FREEZE_SECURITY   0x4604
#define CXL_MBOX_OP_PASSPHRASE_SECURE_ERASE 0x4605

/* 보안 상태 비트 필드 */
struct cxl_get_security_output {
    __le32 flags;
    /* bit 0: user passphrase set */
    /* bit 1: master passphrase set */
    /* bit 2: locked */
    /* bit 3: frozen */
    /* bit 4: user passphrase attempts remaining (3bit) */
} __packed;
# CXL 보안 상태 확인
cxl list -m mem0 -i
# "security":"disabled"  ← 보안 비활성
# "security":"locked"    ← 잠김 (패스프레이즈 필요)

# 보안 설정 (cxl-cli)
cxl set-passphrase mem0 --type user --passphrase MYPASS123
cxl lock mem0
cxl unlock mem0 --passphrase MYPASS123

# 안전 삭제
cxl sanitize mem0 --passphrase MYPASS123
# 또는 Crypto Erase (키만 파기)
cxl sanitize mem0 --crypto-erase
CXL 보안 주의사항: IDE 암호화는 PCIe 링크 레벨에서만 보호합니다. 호스트 CPU가 복호화한 후의 메모리(L3 캐시, DDR)는 보호되지 않습니다. 완전한 보호가 필요하면 CXL 3.1 TSP와 CPU TEE(Intel TDX, AMD SEV)를 함께 사용해야 합니다. 또한 CXL 장치가 잠긴 상태에서 패스프레이즈 입력 시도 횟수가 소진되면 영구 잠금될 수 있으니 주의하세요.

CXL PMU (성능 모니터링 유닛)

CXL 3.0부터 정의된 CXL PMU는 CXL 링크의 성능 카운터를 제공합니다. 리눅스 커널 6.6+에서 perf 도구를 통해 CXL 트래픽, 지연, 대역폭 등을 모니터링할 수 있습니다. PMU는 CXL 장치의 MMIO 레지스터 공간에 매핑되며, perf 서브시스템에 등록됩니다.

# CXL PMU 장치 확인
ls /sys/bus/event_source/devices/ | grep cxl
# cxl_pmu_mem0  — mem0 장치의 PMU

# 사용 가능한 이벤트 확인
ls /sys/bus/event_source/devices/cxl_pmu_mem0/events/
# cxl_rx_clock_ticks    — CXL 수신 클럭 틱
# cxl_tx_clock_ticks    — CXL 전송 클럭 틱
# cxl_h2d_req           — Host→Device 요청 수
# cxl_d2h_rsp           — Device→Host 응답 수
# cxl_d2h_data          — Device→Host 데이터 전송
# cxl_cache_miss        — CXL 캐시 미스

# perf를 이용한 CXL 성능 모니터링
perf stat -e cxl_pmu_mem0/cxl_h2d_req/ \
          -e cxl_pmu_mem0/cxl_d2h_rsp/ \
          -e cxl_pmu_mem0/cxl_d2h_data/ \
          -- sleep 10

# 실시간 모니터링
perf stat -e cxl_pmu_mem0/cxl_rx_clock_ticks/ \
          -I 1000  # 1초마다 출력

# CXL 대역폭 계산
# 대역폭(GB/s) = (cxl_d2h_data * 64B) / 측정_시간(s) / 1e9
/* drivers/cxl/core/pmu.c — CXL PMU 드라이버 */

struct cxl_pmu {
    struct device    dev;
    void __iomem     *base;          /* PMU 레지스터 기본 주소 */
    int              index;          /* PMU 인덱스 */
    u64              counter_width;  /* 카운터 비트 폭 */
    int              num_counters;   /* 카운터 수 */
};

/* PMU 레지스터 레이아웃 (CXL 3.0 Spec 8.2.7) */
#define CXL_PMU_CAP            0x00  /* PMU 기능 */
#define CXL_PMU_OVERFLOW_STS   0x08  /* 오버플로우 상태 */
#define CXL_PMU_FREEZE         0x10  /* 카운터 정지 */
#define CXL_PMU_COUNTER_CFG(n) (0x100 + (n) * 0x10)
#define CXL_PMU_COUNTER_VAL(n) (0x108 + (n) * 0x10)

/* perf 서브시스템 연동 */
static struct pmu cxl_pmu_perf = {
    .task_ctx_nr  = perf_invalid_context,
    .event_init   = cxl_pmu_event_init,
    .add          = cxl_pmu_event_add,
    .del          = cxl_pmu_event_del,
    .start        = cxl_pmu_event_start,
    .stop         = cxl_pmu_event_stop,
    .read         = cxl_pmu_event_read,
};

포이즌 관리 (Poison Management)

CXL 메모리에서 ECC 복구 불가능한 에러가 발생하면 해당 주소에 "포이즌(poison)" 마크가 설정됩니다. 커널은 이러한 포이즌 주소를 추적하고, 사용자 공간에 알리며, 필요 시 복구 또는 격리 작업을 수행합니다. CXL 메일박스의 Get Poison List, Inject Poison, Clear Poison 명령을 통해 관리합니다.

CXL 포이즌 감지 및 처리 흐름 ECC 에러 감지 CXL DRAM 비트 에러 포이즌 마크 DPA에 poison 플래그 이벤트 로그 CXL Event Record 커널 처리 MCE / cxl_pci_irq 페이지 오프라인 memory_failure() 프로세스 알림 SIGBUS / kill_procs() cxl_trace 이벤트 trace_cxl_poison() 복구/격리 Clear Poison (메일박스) / Scan Media / 페이지 교체
# 포이즌 목록 조회
cxl list -m mem0 --poison
# {"memdev":"mem0","poison":[
#   {"offset":"0x1000000","length":"0x40","source":"Internal"}]}

# 포이즌 주입 (테스트용, 디버그 빌드 전용)
echo 0x1000000 > /sys/bus/cxl/devices/mem0/inject_poison

# 포이즌 제거
echo 0x1000000 > /sys/bus/cxl/devices/mem0/clear_poison

# 미디어 스캔 (전체 DRAM 검사)
cxl scan-media mem0

# CXL 이벤트 로그 확인
cxl list -m mem0 -H  # 건강 상태 이벤트

# 커널 트레이스로 포이즌 이벤트 모니터링
echo 1 > /sys/kernel/debug/tracing/events/cxl/cxl_poison/enable
cat /sys/kernel/debug/tracing/trace_pipe
# cxl_poison: memdev=mem0 host_addr=0x... dpa=0x... length=64 source=Internal

# MCE (Machine Check Exception) 로그 확인
dmesg | grep -i "memory error\|cxl\|poison"

AI/ML 활용 사례

CXL 메모리는 DDR보다 저렴하게 대용량 메모리를 확보할 수 있어 AI 워크로드에 이상적입니다. 특히 LLM(대형 언어 모델) 추론 서버에서 KV 캐시 확장, 모델 가중치 저장, 임베딩 인덱스 상주에 효과적입니다.

AI 워크로드CXL 활용 패턴기대 효과지연 민감도
LLM 추론 (Llama-70B)KV 캐시 → CXL 노드배치 크기 8배 증가중간
LLM 학습그래디언트 체크포인트 → CXLGPU OOM 감소낮음
임베딩 데이터베이스인덱스 전체 → CXL (TB급)SSD 없이 상주 가능낮음
RLHF 리플레이 버퍼경험 데이터 → CXL메모리 효율 3배낮음
멀티모달 캐싱이미지/비디오 피처 캐시 → CXL재처리 비용 절감중간
RAG (검색 증강 생성)벡터 DB 인덱스 → CXL검색 지연 수 ms 이내중간

LLM KV 캐시 CXL 배치 예시

# vLLM / SGLang의 CXL KV 캐시 설정 예시
import ctypes
import os

# libnuma로 CXL 노드에 KV 캐시 버퍼 할당
def alloc_kv_cache_on_cxl(size_bytes, cxl_numa_node=2):
    libnuma = ctypes.CDLL("libnuma.so.1")
    ptr = libnuma.numa_alloc_onnode(size_bytes, cxl_numa_node)
    if ptr == 0:
        raise MemoryError("CXL 노드에 메모리 할당 실패")
    return ptr

# PyTorch 텐서를 CXL 주소에 래핑
import torch
kv_cache_ptr = alloc_kv_cache_on_cxl(64 * 1024 ** 3)  # 64 GB on CXL
kv_tensor = torch.frombuffer(
    (ctypes.c_byte * 64 * 1024 ** 3).from_address(kv_cache_ptr),
    dtype=torch.float16)

# CXL 메모리 정책: Hot KV 엔트리는 DDR, Cold는 CXL
class TieredKVCache:
    def __init__(self, ddr_size_gb=16, cxl_size_gb=64,
                 cxl_node=2, ddr_node=0):
        self.ddr_cache = alloc_on_node(ddr_size_gb * 1024**3, ddr_node)
        self.cxl_cache = alloc_on_node(cxl_size_gb * 1024**3, cxl_node)
        self.access_count = {}  # 접근 빈도 추적

    def put(self, key, value):
        # 신규 항목은 CXL에 배치, 접근 빈도에 따라 DDR로 promote
        if self.access_count.get(key, 0) > 10:
            return self._store_ddr(key, value)
        return self._store_cxl(key, value)

성능 측정과 지연시간 비교

메모리 유형읽기 지연 (랜덤)대역폭 (seq read)용량비용 ($/GB)
DDR5 (로컬)~80ns~200 GB/s (6400MT/s x 8ch)수십 GB ~ 수 TB$3~5
DDR5 (원격 NUMA)~160ns~100 GB/s위와 동일$3~5
CXL Type3 (CXL 1.1)~300~500ns~50~100 GB/s (PCIe 5.0 x16)수 TB$1~2
CXL Type3 (CXL 3.0)~200~300ns~256 GB/s (PCIe 6.0 x16)수십 TB$1~2
Intel Optane PMEM~300~400ns~40 GB/s수 TB$2~4 (단종)
NVMe SSD~70~100us~14 GB/s (PCIe 5.0 x4)수 TB$0.1~0.3

성능 측정 도구

# Intel MLC로 CXL 지연 및 대역폭 측정
mlc --latency_matrix        # NUMA 노드 간 지연 행렬
mlc --bandwidth_matrix      # NUMA 노드 간 대역폭 행렬
mlc --loaded_latency -d0 -T  # 부하 시 지연 곡선

# STREAM 벤치마크 (CXL 노드 지정)
numactl --membind=2 ./stream       # CXL 노드에서 실행
numactl --membind=0 ./stream       # DDR 노드에서 비교 실행

# lmbench로 lat_mem_rd (랜덤 읽기 지연)
numactl --membind=2 lat_mem_rd -t -P1 512m
# 결과 예시:
# Stride=64 → ~80ns (DDR), ~300ns (CXL)

# Intel PCM CXL 모니터링
pcm-pcie -cxl              # CXL 대역폭 실시간 모니터링
pcm-latency                # 메모리 지연 분석
pcm-memory                 # 메모리 대역폭 채널별 분석

# perf mem 트레이싱
perf mem record -a -- sleep 10
perf mem report --sort=mem,dso

# bpftrace로 CXL 접근 패턴 분석
bpftrace -e 'tracepoint:cxl:cxl_aer_uncorrectable_error { printf("CXL UE: %s\n", args->memdev); }'
성능 비교 벤치마크 방법론: CXL 대 DDR 성능 비교 시, 반드시 동일 CPU 코어에서 numactl --membind로 메모리 노드만 변경하여 측정하세요. numactl --cpunodebind=0 --membind=0 vs numactl --cpunodebind=0 --membind=2 비교가 정확합니다. CPU를 변경하면 L3 캐시 차이가 결과에 영향을 미칩니다.

QEMU CXL 에뮬레이션

실제 CXL 하드웨어 없이도 QEMU를 통해 CXL 환경을 에뮬레이션할 수 있습니다. QEMU 7.2+ 에서 CXL Type3 장치, 스위치, 멀티 루트 포트를 에뮬레이션합니다. CXL 드라이버 개발, 리전 관리 스크립트 테스트, 메모리 티어링 실험에 유용합니다.

QEMU CXL 에뮬레이션 토폴로지 예시 QEMU 가상 머신 CXL Host Bridge (ACPI CEDT 생성) CXL Root Port 0 CXL Type3 (mem0) 256 MB 에뮬레이션 메모리 CXL Root Port 1 CXL Type3 (mem1) 512 MB 에뮬레이션 메모리 CXL Root Port 2 CXL 스위치 (선택적 토폴로지) CFMWS: 0x4080000000 ~ 0x40FFFFFFFF (ACPI CEDT로 노출)
# QEMU CXL 에뮬레이션 실행 (Type3 장치 2개)
qemu-system-x86_64 \
  -machine q35,cxl=on \
  -m 4G,maxmem=8G,slots=8 \
  -smp 4 \
  -cpu host -enable-kvm \
  # CXL 호스트 브리지
  -object memory-backend-file,id=cxl-mem0,share=on,mem-path=/tmp/cxlmem0,size=256M \
  -object memory-backend-file,id=cxl-mem1,share=on,mem-path=/tmp/cxlmem1,size=512M \
  -object memory-backend-file,id=cxl-lsa0,share=on,mem-path=/tmp/cxllsa0,size=256M \
  -object memory-backend-file,id=cxl-lsa1,share=on,mem-path=/tmp/cxllsa1,size=256M \
  # CXL 호스트 브리지 (CFMWS 윈도우 정의)
  -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \
  -cxl-fixed-memory-window targets.0=cxl.1,size=4G \
  # CXL 루트 포트
  -device cxl-rp,port=0,bus=cxl.1,id=root_port0,chassis=0,slot=2 \
  -device cxl-rp,port=1,bus=cxl.1,id=root_port1,chassis=0,slot=3 \
  # CXL Type3 메모리 장치
  -device cxl-type3,bus=root_port0,volatile-memdev=cxl-mem0,lsa=cxl-lsa0,id=cxl-vmem0 \
  -device cxl-type3,bus=root_port1,volatile-memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-vmem1 \
  # 게스트 커널 이미지
  -kernel /boot/vmlinuz-6.6.0 \
  -initrd /boot/initrd.img-6.6.0 \
  -append "root=/dev/sda1 console=ttyS0 cxl_acpi.dyndbg=+p" \
  -drive file=guest.qcow2,format=qcow2 \
  -nographic

# 게스트 내부에서 CXL 장치 확인
lspci | grep -i cxl
# 00:0c.0 PCI bridge: Intel CXL Root Port
# 01:00.0 CXL: Memory Expander

dmesg | grep -i cxl
# cxl_acpi: ACPI0017: found 1 CFMWS instance
# cxl_pci 0000:01:00.0: CXL: bound mem0
# cxl_port: found port root0

cxl list -M
# [{"memdev":"mem0","ram_size":"256.00 MiB"},
#  {"memdev":"mem1","ram_size":"512.00 MiB"}]
QEMU CXL 디버깅 팁: 커널 부팅 옵션에 cxl_acpi.dyndbg=+p cxl_core.dyndbg=+p cxl_pci.dyndbg=+p를 추가하면 CXL 드라이버의 동적 디버그 메시지를 모두 활성화할 수 있습니다. 또한 QEMU의 -device cxl-type3num-dc-regions=N 옵션을 추가하면 Dynamic Capacity(동적 용량) 기능도 테스트할 수 있습니다.

진단 도구 (cxl-cli / ndctl)

# cxl-cli 설치 (ndctl 패키지에 포함)
apt install ndctl  # Ubuntu
dnf install ndctl  # RHEL/Fedora

# CXL 장치 목록
cxl list
# [{"memdev":"mem0","pmem_size":"256.00 GiB","ram_size":"0.00 GiB"}]

# CXL 포트 토폴로지 조회 (전체 정보)
cxl list -BEIMPDRu
# -B: bus  -E: endpoint  -I: initiator  -M: memdev
# -P: port  -D: decoder  -R: region  -u: uuid

# 특정 memdev 상세 정보
cxl list -m mem0 -vi
# {  "memdev":"mem0",
#    "ram_size":"256.00 GiB",
#    "health":{"maintenance_needed":false,
#              "temperature":35,
#              "dirty_shutdowns":0,
#              "volatile_err_cnt":0},
#    "serial":"0x1234567890abcdef",
#    "firmware_version":"1.0.0" }

# CXL 리전(region) 관리
cxl create-region -m mem0 -d decoder0.0     # 생성
cxl enable-region region0                    # 활성화
cxl disable-region region0                   # 비활성화
cxl destroy-region region0                   # 삭제

# 리전을 DAXCTL로 온라인 (NUMA 노드에 메모리 추가)
daxctl reconfigure-device --mode=system-ram dax0.0
daxctl reconfigure-device --mode=devdax dax0.0  # 원복

# 메모리가 NUMA 노드로 추가됐는지 확인
numactl --hardware
cat /proc/meminfo | grep MemTotal

# CXL 장치 이벤트 로그 확인
cxl list -e -m mem0  # 에러 이벤트 조회

# 건강 상태 모니터링
cxl list -m mem0 -H
# "health":{"life_used_percent":5,"temperature":42,
#           "dirty_shutdowns":0,"correctable_errors":12}
명령설명주요 옵션
cxl listCXL 자원 목록-M(memdev), -P(port), -D(decoder), -R(region)
cxl create-regionCXL 리전 생성-m(memdev), -d(decoder), -w(ways), -g(granularity)
cxl destroy-regionCXL 리전 삭제-f(force)
cxl enable-region리전 활성화리전 이름
cxl disable-region리전 비활성화리전 이름
cxl set-passphrase보안 패스프레이즈 설정--type(user/master)
cxl sanitize안전 삭제--crypto-erase
daxctl reconfigure-deviceDAX 장치 모드 전환--mode=system-ram / --mode=devdax
daxctl online-memoryDAX 메모리 온라인장치 이름

커널 소스 가이드

파일 / 디렉토리설명주요 함수/구조체
drivers/cxl/CXL 드라이버 루트
drivers/cxl/core/region.cCXL 리전 생성, NUMA 등록cxl_region_attach()
drivers/cxl/core/port.cCXL 포트 토폴로지 관리cxl_port_setup()
drivers/cxl/core/hdm.cHDM 디코더 관리cxl_decoder, cxl_hdm_decode_init()
drivers/cxl/core/mbox.c메일박스 프로토콜cxl_internal_send_cmd()
drivers/cxl/core/pmu.cCXL PMU 드라이버cxl_pmu
drivers/cxl/core/trace.hCXL 트레이스 이벤트trace_cxl_poison()
drivers/cxl/pci.cPCIe CXL 장치 바인딩cxl_pci_probe()
drivers/cxl/mem.cCXL 메모리 장치 드라이버cxl_mem_probe()
drivers/cxl/acpi.cACPI CEDT 파싱cxl_acpi_probe()
drivers/cxl/cxlmem.hCXL 메모리 구조체 정의cxl_memdev_state
drivers/dax/cxl.cCXL DAX 드라이버cxl_dax_region_probe()
drivers/acpi/numa/hmat.cHMAT 파싱hmat_parse_locality()
mm/memory-tiers.cmemory_tier 프레임워크memory_tier, next_demotion_node()
include/linux/cxl.hCXL 공개 API 헤더
include/linux/memory-tiers.hmemory_tier API
tools/testing/cxl/CXL 단위 테스트cxl_mock_*

커널 설정 (Kconfig)

# CXL 지원
CONFIG_CXL_BUS=y           # CXL 버스 (기본)
CONFIG_CXL_PCI=y           # PCIe CXL 장치
CONFIG_CXL_ACPI=y          # ACPI CEDT 기반 CXL 발견
CONFIG_CXL_PMEM=y          # CXL PMEM 지원
CONFIG_CXL_MEM=y           # CXL 메모리 장치
CONFIG_CXL_PORT=y          # CXL 포트 관리
CONFIG_CXL_REGION=y        # CXL 리전 지원
CONFIG_CXL_REGION_INVALIDATION_TEST=n # 운영 환경에서는 비활성

# 메모리 티어링
CONFIG_MEMORY_TIER_DEFAULT_DRAM_PERF_VALUES=y
CONFIG_NUMA_BALANCING=y    # NUMA 자동 밸런싱

# DAX (직접 접근)
CONFIG_DEV_DAX=y
CONFIG_DEV_DAX_CXL=y      # CXL DAX 장치
CONFIG_DEV_DAX_KMEM=y      # DAX를 system-ram으로

# CXL PMU
CONFIG_CXL_PMU=y           # CXL 성능 카운터

# 디버깅
CONFIG_CXL_VERBOSE_DEBUG=y # 상세 디버그 메시지
CONFIG_DEBUG_CXL=y         # CXL 디버그 모드

CXL 프로브 흐름 상세

/* drivers/cxl/pci.c — CXL PCI 프로브 흐름 */
static int cxl_pci_probe(struct pci_dev *pdev,
                          const struct pci_device_id *id)
{
    struct cxl_memdev_state *mds;
    struct cxl_dev_state *cxlds;
    int rc;

    /* 1. CXL 레지스터 매핑 (DVSEC에서 레지스터 위치 파싱) */
    rc = cxl_pci_setup_regs(pdev, CXL_REGLOC_RBI_MEMDEV, &map);

    /* 2. 메모리 장치 상태 초기화 */
    mds = cxl_memdev_state_create(&pdev->dev);
    cxlds = &mds->cxlds;

    /* 3. 메일박스 초기화 */
    rc = cxl_pci_setup_mailbox(mds);

    /* 4. Identify 명령으로 장치 정보 수집 */
    rc = cxl_dev_state_identify(mds);
    /* mds->total_bytes: 총 메모리 용량 */
    /* mds->volatile_only_bytes: 휘발성 메모리 용량 */
    /* mds->persistent_only_bytes: 비휘발성 메모리 용량 */

    /* 5. CDAT 테이블 읽기 (DOE 프로토콜) */
    rc = cxl_enumerate_cmds(mds);
    rc = cxl_pci_doe_init(cxlds);

    /* 6. CXL memdev 등록 (/sys/bus/cxl/devices/memN) */
    return devm_cxl_add_memdev(&pdev->dev, cxlds);
}

ACPI 테이블과 펌웨어 인터페이스

CXL 장치 발견과 메모리 특성 전달은 ACPI 테이블을 통해 이루어집니다. CEDT(CXL Early Discovery Table)는 CXL 호스트 브리지와 CFMWS를 정의하고, SRAT(System Resource Affinity Table)은 NUMA 노드 소속을, HMAT(Heterogeneous Memory Attribute Table)은 지연/대역폭 특성을 제공합니다.

ACPI 테이블CXL 관련 역할커널 처리 파일
CEDT (CXL Early Discovery Table)CXL 호스트 브리지, CFMWS 정의drivers/cxl/acpi.c
CFMWS (CXL Fixed Memory Window)CXL 메모리의 HPA 범위 지정drivers/cxl/acpi.c
SRAT (System Resource Affinity)CXL 메모리의 NUMA 노드 소속drivers/acpi/numa/srat.c
HMAT (Heterogeneous Memory Attribute)지연/대역폭 특성, 캐시 정보drivers/acpi/numa/hmat.c
CDAT (Coherent Device Attribute)장치별 메모리 특성 (DOE로 전달)drivers/cxl/core/cdat.c
DSDT/SSDTCXL 장치 ACPI 이름 공간drivers/acpi/
# ACPI 테이블 덤프 (iasl 필요)
acpidump -b
iasl -d cedt.dat  # CEDT 테이블 역어셈블

# CEDT 내용 예시
# [000h] Signature       : "CEDT"
# [024h] Subtable Type   : 0001 (CFMWS)
# [030h] Window Size     : 0x0000000100000000 (4 GB)
# [038h] Base HPA        : 0x0000004080000000
# [040h] Interleave Ways : 1
# [044h] Interleave Targets : Host Bridge UID 0

# HMAT 정보 확인
cat /sys/firmware/acpi/tables/HMAT | xxd | head -40

# SRAT 정보에서 CXL NUMA 확인
dmesg | grep -i "SRAT\|proximity\|CXL"
# SRAT: Node 2 PXM 2 [mem 0x4080000000-0x40ffffffff]

# CDAT (장치에서 DOE로 읽은 데이터)
cat /sys/bus/cxl/devices/mem0/CDAT
# DSMAS: DPA Range 0x0-0x3FFFFFFF, Non-volatile=0
# DSLBIS: Read Latency=300ns, Write Latency=350ns
# DSLBIS: Read Bandwidth=50000 MB/s, Write Bandwidth=45000 MB/s
/* drivers/cxl/acpi.c — CEDT/CFMWS 파싱 */
static int cxl_parse_cfmws(union acpi_subtable_headers *header,
                            void *arg, const unsigned long end)
{
    struct acpi_cedt_cfmws *cfmws = (struct acpi_cedt_cfmws *)header;
    struct cxl_cfmws_context *ctx = arg;

    /* CFMWS에서 HPA 범위 추출 */
    resource_size_t base = cfmws->base_hpa;
    resource_size_t size = cfmws->window_size;
    int ways = CFMWS_INTERLEAVE_WAYS(cfmws->interleave_ways);
    int granularity = CFMWS_INTERLEAVE_GRANULARITY(cfmws->granularity);

    /* CXL 루트 디코더 생성 */
    return cxl_root_decoder_add(ctx->root_port, base, size, ways, granularity);
}

/* ACPI CEDT 서브테이블 유형 */
/* Type 0: CXL Host Bridge Structure (CHBS) */
/* Type 1: CXL Fixed Memory Window Structure (CFMWS) */
/* Type 2: CXL XOR Interleave Math Structure (CXIMS) */

CXL 스위칭과 메모리 풀링

CXL 2.0부터 도입된 CXL 스위치는 여러 호스트와 CXL 장치를 연결하는 패브릭을 형성합니다. CXL 3.0에서는 다중 레벨 스위칭과 패브릭 연결을 통해 데이터센터 규모의 메모리 풀링이 가능해집니다. MLD(Multi-Logical Device)를 통해 하나의 물리 장치를 여러 호스트가 논리적으로 분할하여 사용합니다.

CXL 3.0 스위칭과 메모리 풀링 토폴로지 Host 0 CPU + DDR Host 1 CPU + DDR Host 2 CPU + DDR Host 3 CPU + DDR CXL 3.0 패브릭 스위치 Fabric Manager (FM) / LD 할당 관리 SLD: Type3 (512 GB) Single Logical Device Host 0 전용 MLD: Type3 (2 TB) Multi-Logical Device LD0→Host1 / LD1→Host2 Shared: Type3 (1 TB) CXL 3.0 공유 메모리 Host 2,3 공유 (BI 필요) SLD: 단일 호스트 전용 / MLD: LD 단위 분할 할당 / Shared: Back-Invalidate로 다중 호스트 공유
Fabric Manager (FM): CXL 3.0 스위치의 관리 엔티티입니다. FM은 MLD의 LD(Logical Device) 할당, 대역폭 QoS, 장애 격리, 동적 재구성을 담당합니다. 리눅스에서 FM 인터페이스는 CXL.io 메일박스를 통해 구현되며, drivers/cxl/core/mbox.c에서 FM 관련 명령(Tunnel Management)을 처리합니다. 현재 리눅스 커널에서 FM 지원은 초기 단계입니다.
/* CXL 2.0 MLD (Multi-Logical Device) 구조 */

/* 하나의 물리 장치를 여러 LD로 분할 */
struct cxl_mld {
    int num_lds;                       /* 논리 장치 수 (최대 16) */
    struct cxl_mld_ld ld[16];          /* 각 LD 정보 */
};

struct cxl_mld_ld {
    int              ld_id;            /* LD 식별자 (0~15) */
    resource_size_t  capacity;        /* LD 용량 */
    int              host_id;         /* 할당된 호스트 ID */
    enum cxl_ld_state state;          /* Active/Standby */
};

/* CXL 3.0 Back-Invalidate 구조 */
/* 장치가 호스트 캐시를 무효화 요청 */
struct cxl_bi_message {
    u64  address;     /* 무효화 대상 HPA */
    u16  length;      /* 무효화 범위 (캐시 라인 수) */
    u8   bi_type;     /* BI-Snoop, BI-Invalidate */
    u8   bi_id;       /* 타겟 호스트 ID */
};

CXL 도입 체크리스트

CXL 메모리는 용량 확장에 강점이 있지만 지연 특성이 DRAM과 다릅니다. 운영에서는 "어떤 데이터를 CXL 티어로 내릴지" 정책 설계가 성능을 좌우합니다.

점검 항목확인 방법운영 기준
하드웨어 호환성CPU CXL 지원 여부 (SPR+)Intel Sapphire Rapids / AMD Genoa 이상
BIOS/펌웨어 설정CEDT/CFMWS 노출 확인CXL 메모리 윈도우 활성화
커널 버전uname -rLinux 6.2+ (리전), 6.6+ (PMU/포이즌)
커널 설정grep CXL /boot/config-*CONFIG_CXL_BUS/PCI/ACPI/MEM=y
토폴로지 인식cxl list -BEIMRu포트/리전/노드 매핑 일치
NUMA 통합numactl --hardwareCXL 노드가 정상 노출
HMAT/CDAT 특성/sys/devices/system/node/지연/대역폭 값 합리적
티어 정책memory_tier sysfsCXL이 적절한 tier에 배치
demotion 테스트메모리 압박 시 CXL 사용량hot set은 DRAM 유지
워크로드 적합성latency-sensitive vs capacity-bound용량 중심 데이터 우선 배치
보안cxl list -m mem0 -i필요시 패스프레이즈 설정
모니터링CXL PMU, perf, numastat대시보드 구성
# CXL/NUMA 점검 종합 스크립트
#!/bin/bash
echo "=== CXL 장치 ==="
cxl list -M 2>/dev/null || echo "CXL 장치 없음"

echo "=== NUMA 토폴로지 ==="
numactl --hardware

echo "=== 메모리 총량 ==="
cat /proc/meminfo | grep -E "MemTotal|MemAvailable"

echo "=== 메모리 티어 ==="
for tier in /sys/devices/virtual/memory_tiering/memory_tier*; do
    echo "$(basename $tier): nodes=$(cat $tier/nodelist 2>/dev/null)"
done

echo "=== CXL 커널 메시지 ==="
dmesg | grep -Ei "cxl|hmat|memory.tier" | tail -20

echo "=== HMAT 지연 정보 ==="
for node in /sys/devices/system/node/node*; do
    if [ -f "$node/access0/initiators/read_latency" ]; then
        echo "$(basename $node): read_latency=$(cat $node/access0/initiators/read_latency)ns"
    fi
done

echo "=== Demotion 설정 ==="
cat /sys/kernel/mm/numa/demotion_enabled 2>/dev/null
cat /proc/sys/kernel/numa_balancing

CXL 3.0/3.1 프로토콜 심화

CXL 3.0은 PCIe 6.0 물리 계층(64 GT/s PAM4) 위에서 동작하며, 256B FLIT(Flow Control Unit)을 기본 전송 단위로 사용합니다. CXL 1.x/2.0의 68B FLIT과 비교하여 프로토콜 효율이 크게 향상되었으며, FEC(Forward Error Correction) 기반 에러 보호, 멀티 프로토콜 슬롯 다중화, 그리고 Back-Invalidate(BI) 채널이 추가되었습니다. CXL 3.1은 TSP(TEE Security Protocol), 포트 기반 라우팅, 향상된 GFD(Global Fabric Attached Memory)를 도입합니다.

CXL 3.0 256B FLIT 내부 구조 상세 256바이트 FLIT (PCIe 6.0 64 GT/s PAM4) Header (2B) Protocol ID | Slot Map (어떤 슬롯에 어떤 프로토콜) Bit[7:6]: FLIT Type | Bit[5:0]: Slot 인코딩 Slot 0 (16B) CXL.io TLP / CXL.cache 또는 CXL.mem 메시지 Slot 1 (16B) CXL.cache D2H Req 또는 CXL.mem M2S Slot 2 (16B) CXL.cache H2D Snoop 또는 Back-Invalidate Slot 3 (16B) CXL.mem S2M DRS 또는 제어 메시지 Data Payload (186B) 최대 3개의 캐시 라인 데이터(64B x 3 = 192B)를 전송 가능 Cache Line 0 (64B) S2M DRS Data / H2D Data Cache Line 1 (64B) (선택적 두 번째 라인) Cache Line 2 (58B 부분) (선택적, 나머지 6B 패딩) CRC-32 (4B) 무결성 검증 FEC (48B) Reed-Solomon 순방향 에러 정정 CXL 3.0: 256B FLIT 효율 ~73% (186/256), PCIe 6.0 64 GT/s CXL 1.x/2.0: 68B FLIT 효율 ~50%, PCIe 5.0 32 GT/s

FLIT 모드와 프로토콜 다중화

CXL 3.0의 256B FLIT은 여러 프로토콜 메시지를 동시에 포함할 수 있어 링크 활용률이 크게 향상됩니다. 하나의 FLIT에 CXL.io TLP, CXL.cache 요청, CXL.mem 응답이 모두 담길 수 있습니다.

FLIT 유형슬롯 구성사용 사례데이터 효율
G0 (Generic 0)4개 16B 슬롯 + 186B 데이터일반 데이터 전송73%
G1 (Generic 1)3개 16B 슬롯 + 202B 데이터대용량 데이터 전송79%
G2 (Generic 2)2개 16B 슬롯 + 218B 데이터캐시 라인 전송 최적화85%
H (Header Only)4개 16B 슬롯만제어 메시지 전용제어 전용
BI (Back-Invalidate)BI 슬롯 포함호스트 캐시 무효화BI 전용
/* CXL 3.0 FLIT 슬롯 인코딩 */

/* 슬롯 유형 (4비트 인코딩) */
enum cxl_flit_slot_type {
    CXL_SLOT_NULL       = 0x0,   /* 빈 슬롯 */
    CXL_SLOT_IO_TLP     = 0x1,   /* CXL.io TLP */
    CXL_SLOT_CACHE_D2H  = 0x2,   /* CXL.cache D2H Req/Rsp */
    CXL_SLOT_CACHE_H2D  = 0x3,   /* CXL.cache H2D Snoop/Rsp */
    CXL_SLOT_MEM_M2S    = 0x4,   /* CXL.mem M2S Req/RwD */
    CXL_SLOT_MEM_S2M    = 0x5,   /* CXL.mem S2M NDR/DRS */
    CXL_SLOT_BI         = 0x6,   /* Back-Invalidate (CXL 3.0) */
};

/* 256B FLIT 헤더 구조 */
struct cxl_flit_header {
    u8 flit_type : 2;      /* G0/G1/G2/H */
    u8 slot0_type : 4;     /* 슬롯 0 프로토콜 유형 */
    u8 slot1_type : 4;     /* 슬롯 1 프로토콜 유형 */
    u8 slot2_type : 4;     /* 슬롯 2 프로토콜 유형 */
    u8 slot3_type : 2;     /* 슬롯 3 (축약 인코딩) */
} __packed;

/* CXL.cache 코히런시 상태 전이 (MESI 확장) */
/*
 * 장치가 RdOwn 요청 → 호스트는 GO-E(Exclusive) 또는 GO-M(Modified) 응답
 * 장치가 RdShared 요청 → 호스트는 GO-S(Shared) 응답 + 데이터
 * 호스트가 SnpInv → 장치는 캐시 라인 무효화 + WritePull(dirty 시)
 * 호스트가 SnpData → 장치는 캐시 라인 데이터 전달 + 상태 유지
 */

Back-Invalidate (BI) 채널 상세

CXL 3.0의 Back-Invalidate(BI)는 CXL 장치가 호스트 CPU의 캐시 라인을 직접 무효화할 수 있는 새로운 채널입니다. 이는 다중 호스트가 하나의 CXL 메모리를 공유할 때 캐시 일관성을 유지하는 데 핵심적입니다. BI 없이는 호스트 A가 캐시한 데이터를 호스트 B가 변경해도 A의 캐시가 갱신되지 않아 일관성 문제가 발생합니다.

BI 메시지방향동작사용 시나리오
BISnoopDevice → Host호스트 캐시 라인 상태 조회다른 호스트 캐시 확인
BIInvalidateDevice → Host호스트 캐시 라인 무효화공유 메모리 변경 전
BIRspHost → DeviceBI 요청에 대한 응답캐시 라인 상태/데이터 반환
BIConflictHost → Device동시 요청 충돌 보고데드락 방지
/* CXL 3.0 Back-Invalidate 처리 흐름 (개념) */

/*
 * 시나리오: Host A와 Host B가 동일한 CXL 메모리 주소 공유
 *
 * 1. Host A가 CXL 메모리 주소 X를 읽음 → CPU A 캐시에 상태 S(Shared)
 * 2. Host B가 동일 주소 X에 쓰기 요청
 * 3. CXL 장치가 Host A에 BIInvalidate(X) 전송
 * 4. Host A CPU가 캐시 라인 X를 무효화하고 BIRsp 반환
 * 5. CXL 장치가 Host B의 쓰기 완료 응답(S2M Cmp)
 * 6. Host A가 다시 X를 읽으면 CXL 장치에서 최신 데이터 로드
 */

/* BI 처리를 위한 커널 후보 인터페이스 (향후 구현 예정) */
struct cxl_bi_handler {
    int (*bi_invalidate)(struct cxl_port *port,
                          u64 hpa, u32 length);
    int (*bi_snoop)(struct cxl_port *port,
                     u64 hpa, u8 *state);
};

/* CXL 3.1 향상 사항 */
/*
 * - TSP (TEE Security Protocol): 장치별 메모리 영역 암호화/격리
 * - 포트 기반 라우팅: 스위치에서 LD ID가 아닌 포트 번호로 라우팅
 * - GFD (Global Fabric Attached Memory):
 *     여러 패브릭 스위치를 연결한 데이터센터 규모 메모리 풀
 * - 향상된 QoS: LD별 대역폭/지연 보장 (QoS Telemetry)
 * - CMA+ (향상된 메모리 인증): MAC 태그 크기 확장
 */
CXL 3.0 BI 커널 지원 현황: Linux 커널 6.10 기준으로 Back-Invalidate 채널의 하드웨어 인터페이스는 CXL 3.0 규격에 정의되어 있으나, 리눅스 커널에서 BI 처리를 위한 캐시 무효화 인프라는 아직 개발 중입니다. 현재는 단일 호스트 연결 환경에서만 완전한 지원이 이루어지며, 다중 호스트 공유 메모리 시나리오는 향후 커널 릴리스에서 점진적으로 지원될 예정입니다.

Type 1/2/3 디바이스 심화 비교

CXL 장치 유형은 사용하는 서브프로토콜과 하드웨어 구성에 따라 결정됩니다. 각 유형의 메모리 모델, 코히런시 엔진, 커널 드라이버 바인딩, 성능 특성이 다르며, 실제 하드웨어 제품도 각기 다른 형태(E3.S, EDSFF, CXL DIMM, AIC)로 출시됩니다.

CXL Type1/2/3 장치 내부 아키텍처 상세 Type 1 장치 (SmartNIC, FPGA 가속기) CXL.io (필수) CXL.cache (필수) CXL.mem (없음) DCOH 엔진 Device Coherency Engine 장치 캐시 (SRAM) 호스트 메모리 캐싱 자체 메모리 없음 커널: cxl_pci.ko HDM: 불필요 NUMA: 노드 생성 안함 예: Intel IPU E2000 Xilinx Alveo (CXL) Type 2 장치 (GPU, AI 가속기) CXL.io (필수) CXL.cache (필수) CXL.mem (필수) DCOH + HDM-DB Device-Bias / Host-Bias 전환 캐시 (SRAM) VRAM (HBM3) GPU 컴퓨팅 코어 (CU/SM) 커널: cxl_pci + HMM HDM: HDM-DB (Device-Bias) NUMA: 있음 (VRAM 노드) 예: AMD MI300A 향후 CXL GPU Type 3 장치 (메모리 확장기) CXL.io (필수) CXL.cache (없음) CXL.mem (필수) HDM-H (Host-Only) 호스트가 코히런시 완전 관리 DDR5 / LPDDR5 DRAM 대용량 (수백 GB ~ 수 TB) 메모리 컨트롤러 + ECC 커널: cxl_mem + DAX HDM: HDM-H (Host-Only) NUMA: 있음 (CPU-less) 예: Samsung CMM-D SK hynix CMS, Micron CZ120

HDM Bias 모드: Host-Bias vs Device-Bias

CXL Type2 장치는 HDM-DB(Device-Bias)를 지원하여, 메모리 접근 코히런시 관리 주체를 동적으로 전환할 수 있습니다. GPU 커널이 VRAM을 집중적으로 사용할 때는 Device-Bias(장치가 코히런시 관리), CPU가 결과를 읽을 때는 Host-Bias(호스트가 코히런시 관리)로 전환합니다.

특성HDM-H (Host-Only)HDM-DB Device-BiasHDM-DB Host-Bias
코히런시 관리 주체호스트 CPU장치 (DCOH)호스트 CPU
장치 유형Type 3Type 2Type 2
호스트 캐싱가능불가 (UC 매핑)가능
장치 캐싱불가가능 (WB)불가
전환 비용해당 없음Bias 전환 시 캐시 플러시 필요
최적 사용 시점항상GPU 연산 중CPU 결과 읽기
커널 인터페이스DAX / system-ramHMM ZONE_DEVICE + migrate_vma()
/* Type2 HDM-DB Bias 전환 (개념적 커널 인터페이스) */

/* HMM 기반 Type2 CXL 메모리 접근 */
#include <linux/hmm.h>

/* Device-Bias → Host-Bias 전환 */
static int cxl_type2_switch_to_host_bias(
    struct cxl_memdev *cxlmd,
    u64 dpa_start, u64 length)
{
    /* 1. 장치 캐시 플러시 (DCOH → 메모리) */
    cxl_flush_device_cache(cxlmd, dpa_start, length);

    /* 2. Bias 모드 레지스터 업데이트 */
    cxl_set_hdm_bias(cxlmd, dpa_start, length,
                     CXL_HDM_BIAS_HOST);

    /* 3. 호스트 TLB shootdown (UC → WB 전환) */
    flush_tlb_range(vma, addr, addr + length);

    return 0;
}

/* Type2 장치의 ZONE_DEVICE 페이지 등록 */
/* mm/hmm.c와 연동하여 CPU 페이지 테이블에 매핑 */
static int cxl_type2_register_memory(
    struct cxl_memdev *cxlmd)
{
    struct dev_pagemap pgmap = {
        .type  = MEMORY_DEVICE_COHERENT,  /* 코히런트 장치 메모리 */
        .range = {
            .start = cxlmd->dpa_base,
            .end   = cxlmd->dpa_base + cxlmd->size - 1,
        },
        .ops   = &cxl_type2_pgmap_ops,
    };
    return devm_memremap_pages(&cxlmd->dev, &pgmap);
}
Type 1/2/3 폼팩터: CXL 장치는 다양한 물리적 형태로 출시됩니다. E3.S(Enterprise SSD 폼팩터, 1U 서버 최적화), E3.L(대용량 확장 폼팩터), CXL DIMM(DDR5 DIMM 슬롯 호환, CXL 프로토콜 사용), AIC(Add-in Card, PCIe x16 슬롯), OCP NIC 3.0(SmartNIC 폼팩터)이 있습니다. E3.S가 Type3 메모리 확장기의 주류 폼팩터가 되고 있으며, Samsung CMM-D, SK hynix CMS, Micron CZ120 등이 E3.S로 출시되었습니다.

HDM 디코더 프로그래밍 상세

HDM 디코더는 CXL 토폴로지의 각 레벨에서 주소 라우팅을 담당합니다. 루트 디코더(CFMWS 기반), 스위치 디코더(다운스트림 포트 라우팅), 엔드포인트 디코더(HPA→DPA 최종 변환)의 3단계 디코더 체인을 거쳐 메모리 접근이 이루어집니다. 커널은 리전 생성 시 이 디코더 체인을 자동으로 프로그래밍합니다.

/* drivers/cxl/core/hdm.c — HDM 디코더 레지스터 프로그래밍 */

/* HDM 디코더 레지스터 오프셋 (CXL 3.0 Spec 8.2.5) */
#define CXL_HDM_DECODER_CTRL_OFFSET      0x00
#define CXL_HDM_DECODER_TARGET_LIST      0x04
#define CXL_HDM_DECODER_BASE_LOW         0x10
#define CXL_HDM_DECODER_BASE_HIGH        0x14
#define CXL_HDM_DECODER_SIZE_LOW         0x18
#define CXL_HDM_DECODER_SIZE_HIGH        0x1C
#define CXL_HDM_DECODER_CTRL             0x20
#define CXL_HDM_DECODER_SKIP_LOW         0x24
#define CXL_HDM_DECODER_SKIP_HIGH        0x28

/* 디코더 제어 레지스터 비트 필드 */
#define CXL_HDM_DECODER_CTRL_COMMIT      BIT(9)   /* 디코더 커밋 */
#define CXL_HDM_DECODER_CTRL_COMMITTED   BIT(10)  /* 커밋 완료 */
#define CXL_HDM_DECODER_CTRL_ERROR       BIT(11)  /* 커밋 에러 */
#define CXL_HDM_DECODER_CTRL_TYPE        BIT(12)  /* 0=RAM, 1=PMEM */
#define CXL_HDM_DECODER_CTRL_IW_MASK     GENMASK(7, 4)  /* 인터리브 ways */
#define CXL_HDM_DECODER_CTRL_IG_MASK     GENMASK(3, 0)  /* 인터리브 granularity */

/* 엔드포인트 디코더 프로그래밍 */
static int cxl_hdm_decode_commit(
    struct cxl_decoder *cxld,
    struct cxl_endpoint_decoder *cxled)
{
    void __iomem *hdm = cxld->hpa_range.start;
    u32 ctrl;

    /* 1. HPA 범위 레지스터 설정 */
    lo_hi_writeq(cxld->hpa_range.start,
                 hdm + CXL_HDM_DECODER_BASE_LOW);
    lo_hi_writeq(range_len(&cxld->hpa_range),
                 hdm + CXL_HDM_DECODER_SIZE_LOW);

    /* 2. 인터리브 설정 */
    ctrl = FIELD_PREP(CXL_HDM_DECODER_CTRL_IW_MASK,
                      ilog2(cxld->interleave_ways));
    ctrl |= FIELD_PREP(CXL_HDM_DECODER_CTRL_IG_MASK,
                       ilog2(cxld->interleave_granularity) - 8);

    /* 3. 디코더 커밋 (하드웨어 활성화) */
    ctrl |= CXL_HDM_DECODER_CTRL_COMMIT;
    writel(ctrl, hdm + CXL_HDM_DECODER_CTRL);

    /* 4. 커밋 완료 대기 */
    return cxl_hdm_wait_for_commit(hdm);
}

/* HPA → DPA 변환 로직 (인터리브 고려) */
/*
 * 2-way 인터리브, 256B granularity 예시:
 *
 * HPA: 0x0000_1000_0000_0100
 *      ├── 비트[8] = 1 → Target: mem1
 *      └── DPA = (HPA[63:9] << 8) | HPA[7:0]
 *            = 0x0000_0800_0000_0000 | 0x00
 *
 * 4-way 인터리브, 256B granularity 예시:
 * HPA: 0x0000_1000_0000_0300
 *      ├── 비트[9:8] = 0b11 → Target: mem3
 *      └── DPA = (HPA[63:10] << 8) | HPA[7:0]
 */
# HDM 디코더 상태 확인 (sysfs)
ls /sys/bus/cxl/devices/decoder*

# 루트 디코더 상세
cat /sys/bus/cxl/devices/decoder0.0/start          # HPA 시작 (16진수)
cat /sys/bus/cxl/devices/decoder0.0/size            # 디코더 범위 크기
cat /sys/bus/cxl/devices/decoder0.0/interleave_ways # 인터리브 ways
cat /sys/bus/cxl/devices/decoder0.0/interleave_granularity # 인터리브 단위
cat /sys/bus/cxl/devices/decoder0.0/mode            # ram 또는 pmem

# 엔드포인트 디코더 상세
cat /sys/bus/cxl/devices/endpoint2/decoder2.0/start
cat /sys/bus/cxl/devices/endpoint2/decoder2.0/dpa_resource_start # DPA 시작
cat /sys/bus/cxl/devices/endpoint2/decoder2.0/dpa_size           # DPA 크기

# 디코더 커밋 상태 확인
cat /sys/bus/cxl/devices/decoder0.0/committed  # 1이면 활성

# 인터리브 방식별 HPA 비트 매핑
# ways=1, gran=256: HPA → mem0 (고정)
# ways=2, gran=256: HPA[8] → mem0/mem1
# ways=4, gran=256: HPA[9:8] → mem0~mem3
# ways=8, gran=256: HPA[10:8] → mem0~mem7
# ways=2, gran=4K:  HPA[12] → mem0/mem1
# ways=4, gran=4K:  HPA[13:12] → mem0~mem3
XOR 인터리브 (CXL 3.0): CXL 3.0은 기존의 모듈로(modulo) 인터리브 외에 XOR 기반 인터리브를 도입했습니다. ACPI CEDT의 CXIMS(CXL XOR Interleave Math Structure) 서브테이블에서 XOR 비트 맵을 정의하며, 커널의 cxl_calc_interleave_pos()에서 이를 처리합니다. XOR 인터리브는 특정 접근 패턴에서 모듈로 방식보다 더 균등한 분산을 제공합니다.

커널 CXL 드라이버 내부 심화

CXL 커널 드라이버는 cxl_bus라는 전용 가상 버스를 기반으로 동작합니다. 모든 CXL 장치(포트, 디코더, memdev, 리전)는 이 버스에 등록되며, 장치-드라이버 매칭은 cxl_bus_type의 match 콜백을 통해 이루어집니다. 드라이버 간의 의존성은 모듈 레벨에서 관리되며, 프로브 순서가 중요합니다.

CXL 드라이버 프로브 순서와 장치 등록 흐름 Phase 1: cxl_acpi.ko 프로브 (ACPI CEDT 파싱) CEDT 서브테이블 순회 CHBS → cxl_root 등록 CFMWS → cxl_root_decoder 등록 Phase 2: cxl_pci.ko 프로브 (PCIe CXL 장치 바인딩) DVSEC 파싱 (Register Locator) 레지스터 매핑 (MMIO) cxl_memdev 등록 (memN) Phase 3: cxl_port.ko 프로브 (포트 토폴로지 구축) 루트 포트 열거 스위치 포트 발견 (있을 경우) 엔드포인트 포트 등록 Phase 4: cxl_mem.ko 프로브 (메모리 장치 활성화) Identify 명령 (용량 확인) CDAT 읽기 (DOE) HDM 디코더 프로그래밍 Phase 5: 리전 생성 + DAX 등록 + NUMA 노드 cxl create-region (사용자 명령) dax_cxl.ko → daxN.M 생성 system-ram → NUMA nodeN
/* drivers/cxl/cxl.h — CXL 버스 타입과 장치/드라이버 매칭 */

/* CXL 버스 장치 유형 */
enum cxl_devtype {
    CXL_DEVTYPE_ROOT,       /* ACPI CXL 루트 */
    CXL_DEVTYPE_PORT,       /* CXL 포트 (루트/스위치) */
    CXL_DEVTYPE_MEMDEV,     /* CXL 메모리 장치 */
    CXL_DEVTYPE_DECODER,    /* CXL 디코더 */
    CXL_DEVTYPE_REGION,     /* CXL 리전 */
    CXL_DEVTYPE_PMU,        /* CXL PMU */
};

/* CXL 포트 구조체 */
struct cxl_port {
    struct device        dev;
    struct device        *host_bridge; /* 상위 호스트 브리지 */
    struct device        *uport_dev;   /* 업스트림 포트 장치 */
    struct list_head     dports;       /* 다운스트림 포트 목록 */
    struct cxl_hdm       *hdm;         /* HDM 디코더 관리 */
    int                  id;
    int                  depth;        /* 토폴로지 깊이 */
    bool                 dead;         /* 제거 중 */
    int                  nr_dports;    /* 다운스트림 포트 수 */
    struct cxl_cdat      cdat;         /* CDAT 정보 */
};

/* CXL memdev 구조체 */
struct cxl_memdev {
    struct device        dev;
    struct cdev          cdev;         /* 문자 장치 (ioctl) */
    struct cxl_dev_state *cxlds;       /* 장치 상태 */
    struct cxl_port      *endpoint;    /* 연결된 엔드포인트 */
    int                  id;
    int                  num_ram;      /* RAM 디코더 수 */
    int                  num_pmem;     /* PMEM 디코더 수 */
};

/* CXL 버스 매칭 */
static int cxl_bus_match(struct device *dev,
                          struct device_driver *drv)
{
    /* device_type으로 매칭 */
    return dev->type == &((struct cxl_driver *)drv)->type;
}
드라이버 프로브 순서 문제: CXL 드라이버 프로브는 반드시 ACPI → PCI → Port → Mem 순서로 진행되어야 합니다. 순서가 어긋나면 포트 토폴로지가 불완전하여 리전 생성이 실패합니다. 커널은 lock_depdevice_lock()을 사용하여 프로브 순서를 보장하며, cxl_port_probe()는 상위 포트가 준비될 때까지 대기합니다. 문제 발생 시 cxl_core.dyndbg=+p 부팅 옵션으로 프로브 로그를 확인하세요.

리전 인터리브 구성 심화

CXL 리전의 인터리브 구성은 대역폭과 지연 특성에 큰 영향을 미칩니다. 인터리브 ways(병렬도)와 granularity(전환 단위)의 조합에 따라 워크로드에 최적화된 메모리 배치가 달라집니다.

CXL 2-way 인터리브 vs Non-Interleave 대역폭 비교 Non-Interleave (1-way) CPU 전체 트래픽 mem0 (256 GB) 최대 ~50 GB/s mem1 (256 GB) 별도 리전 필요 단일 리전 대역폭: ~50 GB/s 단순 구성, 장치별 독립 관리 장치 장애 시 일부만 영향 2-way Interleave CPU 분산 mem0 짝수 블록 mem1 홀수 블록 region0 (512 GB, 2-way interleave) 256B 단위로 교차 배치 리전 대역폭: ~100 GB/s 대역폭 2배, 순차 접근 최적 장치 장애 시 전체 리전 영향
인터리브 구성Granularity최적 워크로드대역폭장애 영향
1-way (non-interleave)해당 없음독립 메모리 풀~50 GB/s해당 장치만
2-way, 256B256B대역폭 집중 순차 접근~100 GB/s전체 리전
2-way, 4KB4KB페이지 단위 분산~100 GB/s전체 리전
4-way, 256B256B최대 대역폭 (HPC, AI)~200 GB/s전체 리전
8-way, 256B256B극한 대역폭 (벤치마크)~256 GB/s전체 리전
# 인터리브 리전 생성 실전 예제

# 사전 조건: CXL 장치 확인
cxl list -M
# [{"memdev":"mem0","ram_size":"256.00 GiB"},
#  {"memdev":"mem1","ram_size":"256.00 GiB"},
#  {"memdev":"mem2","ram_size":"256.00 GiB"},
#  {"memdev":"mem3","ram_size":"256.00 GiB"}]

# 루트 디코더 확인 (CFMWS 범위)
cxl list -D -d root
# [{"decoder":"decoder0.0","resource":"0x4080000000",
#   "size":"4.00 TiB","interleave_ways":4}]

# 4-way 인터리브 리전 생성 (1TB = 256GB x 4)
cxl create-region -m mem0 -m mem1 -m mem2 -m mem3 \
    -d decoder0.0 -w 4 -g 256 -s 1024G
# cxl region: created region0

# 리전 확인
cxl list -R
# [{"region":"region0","resource":"0x4080000000",
#   "size":"1.00 TiB","interleave_ways":4,
#   "interleave_granularity":256,
#   "state":"enabled"}]

# DAX 장치로 NUMA 노드에 추가
daxctl list
# [{"chardev":"dax0.0","size":"1.00 TiB","mode":"devdax"}]

daxctl reconfigure-device --mode=system-ram --no-online dax0.0
daxctl online-memory dax0.0 --no-movable  # movable 비활성 (안정성)

# NUMA 노드 확인 — 4-way 인터리브 1TB
numactl --hardware | grep "node 2"
# node 2 size: 1048576 MB

# 대역폭 벤치마크 (STREAM)
numactl --cpunodebind=0 --membind=2 ./stream_c.exe
# Copy:  95,234 MB/s (4-way 인터리브)
# Scale: 94,567 MB/s
# Add:   96,789 MB/s
# Triad: 95,123 MB/s
인터리브 granularity 선택 가이드: 256B: 캐시 라인 수준 분산으로 순차 접근 대역폭 극대화. STREAM, HPC, AI 학습 워크로드에 최적. 4KB: 페이지 단위 분산으로 랜덤 접근 시 특정 장치 핫스팟 방지. 데이터베이스, 키-밸류 스토어에 적합. 16KB: 대용량 I/O 패턴에 적합. 페이지 마이그레이션 오버헤드 감소. 일반적 추천은 256B이며, 워크로드 프로파일링 후 granularity를 조정하는 것이 좋습니다.

메모리 티어링 정책 심화

Linux 커널의 memory_tier 프레임워크는 각 NUMA 노드를 성능 특성에 따라 계층(tier)으로 분류하고, 페이지의 접근 빈도에 따라 자동으로 demotion(하강)과 promotion(승격)을 수행합니다. CXL 메모리를 효과적으로 활용하려면 이 정책을 워크로드에 맞게 튜닝해야 합니다.

/* mm/memory-tiers.c — 추상 거리(adistance) 기반 티어 배치 */

/* 추상 거리 상수 정의 */
#define MEMTIER_ADISTANCE_DRAM    512   /* 로컬 DRAM 기준 */
#define MEMTIER_ADISTANCE_PMEM    1024  /* PMEM 기준 */
#define MEMTIER_HOTPLUG_RANGE     128   /* 동일 티어 판정 범위 */

/* CXL 메모리의 adistance 계산 (drivers/cxl/core/cdat.c) */
static int cxl_cdat_calculate_adistance(
    struct cxl_port *port)
{
    struct access_coordinate coord;
    int adist;

    /* CDAT DSLBIS에서 지연/대역폭 읽기 */
    coord.read_latency  = port->cdat.read_latency;   /* 예: 300ns */
    coord.read_bandwidth = port->cdat.read_bandwidth; /* 예: 50000 MB/s */

    /* 추상 거리 계산: 지연 기반 (높을수록 느림) */
    /* DRAM(80ns) → 512, CXL(300ns) → ~700, PMEM(500ns) → 1024 */
    adist = MEMTIER_ADISTANCE_DRAM +
            (coord.read_latency - 80) * 512 / (500 - 80);

    return clamp(adist, 0, 4096);
}

/* 노드 demotion 경로 결정 */
/* mm/migrate.c — next_demotion_node() */
int next_demotion_node(int node)
{
    struct demotion_nodes *nd;
    int target;

    nd = rcu_dereference(node_demotion[node]);
    if (!nd)
        return NUMA_NO_NODE;

    /* 라운드 로빈으로 다음 demotion 대상 노드 선택 */
    target = nd->preferred[nd->nr++ % nd->nr_nodes];
    return target;
}

/* Promotion 경로: NUMA fault 핸들러 */
/* mm/memory.c — do_numa_page() */
static vm_fault_t do_numa_page(struct vm_fault *vmf)
{
    struct folio *folio = vm_normal_folio(vmf->vma, ...);
    int nid = folio_nid(folio);
    int target_nid;

    /* 페이지가 현재 노드보다 느린 티어에 있는지 확인 */
    if (node_is_toptier(nid))
        return 0;  /* 이미 최상위 tier */

    /* 접근 빈도 확인 후 promotion 결정 */
    target_nid = numa_migrate_prep(folio, vmf, ...);
    if (target_nid != NUMA_NO_NODE)
        migrate_misplaced_folio(folio, vmf->vma, target_nid);
    return 0;
}
# 메모리 티어링 정책 튜닝 가이드

# 1. NUMA 밸런싱 활성화 (promotion 전제 조건)
echo 1 > /proc/sys/kernel/numa_balancing
echo 2 > /proc/sys/kernel/numa_balancing  # 2=tiered (6.6+)

# 2. Demotion 활성화
echo true > /sys/kernel/mm/numa/demotion_enabled

# 3. Promotion 스캔 간격 조정 (기본 1000ms)
echo 500 > /proc/sys/kernel/numa_balancing_scan_delay_ms
# AI 워크로드: 500ms (빠른 promotion), DB: 2000ms (안정성)

# 4. Promotion 스캔 크기 (기본 256MB)
echo 512 > /proc/sys/kernel/numa_balancing_scan_size_mb
# 대용량 워크로드는 512MB로 증가

# 5. Promotion 임계값 조정
echo 16 > /proc/sys/kernel/numa_balancing_promote_rate_limit_MBps
# promotion 속도 제한 (과도한 마이그레이션 방지)

# 6. 워크로드별 정책 예시

# AI 추론 서버 (LLM): Hot KV 캐시 → DDR, 모델 가중치 → CXL
numactl --preferred=0 --membind=0,2 python llm_server.py
# 자동 tiering: 자주 접근하는 KV → DDR promotion
# 모델 가중치는 접근 빈도 낮아 CXL에 유지

# 데이터베이스 서버: 인덱스 → DDR, 데이터 → CXL
numactl --interleave=0,2 mysqld
# 인덱스 hot set 자동 promotion, cold data CXL demotion

# 7. 티어링 모니터링
# NUMA 페이지 이동 통계
cat /proc/vmstat | grep -E "pgpromote|pgdemote|numa"
# pgpromote_success    12345  ← CXL→DDR 성공한 페이지 수
# pgdemote_kswapd      67890  ← DDR→CXL demotion (kswapd)
# pgdemote_direct       1234  ← DDR→CXL demotion (직접)
# numa_hint_faults     98765  ← NUMA fault 발생 수

# perf로 promotion/demotion 이벤트 추적
perf stat -e migrate:mm_migrate_pages \
          -e sched:sched_numa_pair_template \
          -p $(pidof my_app) -- sleep 60
티어링 안티패턴 주의: 1) Promotion 폭풍: scan_delay가 너무 짧으면 CXL↔DDR 간 지속적인 페이지 이동(ping-pong)이 발생합니다. pgpromote_successpgdemote_kswapd가 동시에 높다면 이 현상입니다. 2) Demotion 지연: kswapd가 CXL 노드로 demote하기 전에 DDR에서 직접 회수(reclaim)하면 CXL 활용률이 낮아집니다. /proc/sys/vm/zone_reclaim_mode=0으로 설정하세요. 3) Movable 페이지 문제: DAX를 system-ram으로 변환할 때 --no-movable 옵션 없이 추가하면 CXL 메모리가 커널 내부 할당에 사용되어 핫 리무브가 불가능해집니다.

CXL Dynamic Capacity (동적 용량)

CXL 3.0에서 도입된 Dynamic Capacity(DC) 기능은 CXL 메모리 장치의 용량을 호스트에 동적으로 할당/해제할 수 있게 합니다. 클라우드 환경에서 VM이나 컨테이너에 필요한 만큼만 CXL 메모리를 할당하고, 반납된 메모리를 다른 호스트에 재할당하는 메모리 오버커밋이 가능해집니다. DC는 Fabric Manager(FM)가 메모리 블록의 할당/해제를 관리합니다.

CXL Dynamic Capacity: 메모리 동적 할당/해제 흐름 Fabric Manager (FM) DC 블록 할당/해제 오케스트레이터 Host A (VM 1, 2) DC Region: 256 GB 요청 Host B (VM 3) DC Region: 128 GB 요청 Add Capacity Add Capacity CXL Switch (MLD 연결) CXL Type3 DC Device (2 TB 총 용량) DC Region 0: Host A → 256 GB (블록 0~63) DC Region 1: Host B → 128 GB (블록 64~95) 미할당 풀: 1,640 GB (블록 96~511) FM이 DC 블록(4GB 단위) 할당/해제를 제어, 호스트는 이벤트로 용량 변경 인지
/* CXL Dynamic Capacity 메일박스 명령 */

/* DC 관련 메일박스 오프코드 */
#define CXL_MBOX_OP_GET_DC_CONFIG           0x4800  /* DC 설정 조회 */
#define CXL_MBOX_OP_GET_DC_EXTENT_LIST      0x4801  /* DC 범위 목록 */
#define CXL_MBOX_OP_ADD_DC_RESPONSE         0x4802  /* DC 추가 응답 */
#define CXL_MBOX_OP_RELEASE_DC              0x4803  /* DC 해제 */

/* DC 설정 조회 응답 구조체 */
struct cxl_mbox_dc_config {
    u8   num_regions;          /* DC 리전 수 (최대 8) */
    u8   regions_returned;
    u8   reserved[6];
    struct {
        __le64 base;           /* DPA 시작 */
        __le64 decode_len;     /* 최대 디코딩 크기 */
        __le64 region_len;     /* 리전 크기 */
        __le64 block_size;     /* DC 블록 크기 (예: 4 GB) */
        __le32 dsmad_handle;   /* DSMAS 핸들 */
        u8     flags;           /* DC 플래그 */
        u8     reserved[3];
    } region[];
} __packed;

/* DC 이벤트 레코드 (장치 → 호스트 알림) */
struct cxl_dc_event_record {
    u8     event_type;       /* Add/Release/Force Release */
    u8     flags;
    __le16 host_id;          /* 대상 호스트 */
    u8     region_index;     /* DC 리전 인덱스 */
    u8     reserved[3];
    __le64 dpa_start;        /* 추가/해제 DPA 시작 */
    __le64 length;           /* 추가/해제 크기 */
    u8     tag[16];          /* DC 범위 태그 */
} __packed;

/* DC 범위(extent) 추가 처리 (개념적 코드) */
static int cxl_dc_add_extent(
    struct cxl_memdev *cxlmd,
    u64 dpa_start, u64 length)
{
    /* 1. DAX 장치에 새 범위 추가 */
    dev_dax_grow(cxlmd->dax_dev, dpa_start, length);

    /* 2. system-ram 모드면 메모리 핫플러그 */
    if (cxlmd->mode == CXL_DAX_SYSTEM_RAM)
        add_memory(cxlmd->nid, dpa_start, length,
                   MHP_MERGE_RESOURCE);

    /* 3. Add 응답을 장치에 전송 */
    return cxl_dc_send_add_response(cxlmd, dpa_start, length);
}
# QEMU에서 Dynamic Capacity 테스트

# DC 지원 Type3 장치 생성 (QEMU)
-device cxl-type3,bus=root_port0,volatile-memdev=cxl-mem0,\
  lsa=cxl-lsa0,id=cxl-vmem0,num-dc-regions=2

# 게스트에서 DC 설정 확인
cxl list -m mem0 -vi | grep -i "dc\|dynamic"
# "dc_region_count":2
# "dc_region0":{"base":"0x0","max_size":"2.00 GiB","block_size":"256 MiB"}
# "dc_region1":{"base":"0x80000000","max_size":"2.00 GiB","block_size":"256 MiB"}

# DC 리전 생성 (커널 지원 진행 중)
# 향후: cxl create-region --type=dc -m mem0 -d decoder0.0
# DC 범위가 동적으로 추가/제거되며 NUMA 노드 메모리 크기 변동
Dynamic Capacity 커널 지원 현황: Linux 커널 6.10 기준으로 DC 메일박스 명령 인터페이스와 DC 이벤트 처리의 기본 프레임워크가 개발 중입니다(패치 시리즈 v8+). 완전한 DC 지원은 DAX 핫플러그 인프라와 연동이 필요하며, 향후 릴리스에서 cxl create-region --type=dc 형태로 지원될 예정입니다. QEMU 8.2+에서 num-dc-regions 옵션으로 DC 에뮬레이션 테스트가 가능합니다.

CXL RAS와 에러 처리

CXL은 PCIe AER(Advanced Error Reporting) 위에 CXL 고유의 RAS(Reliability, Availability, Serviceability) 메커니즘을 추가합니다. CXL 이벤트 로그, 바이러스(Viral) 전파 모드, Component Error Log(CEL), 그리고 GHES(Generic Hardware Error Source)를 통한 APEI 연동까지 다중 계층 에러 보고 체계를 갖추고 있습니다.

에러 유형보고 경로커널 처리심각도
Correctable ECCCXL Event Log + GHES카운터 증가, 로그 기록정보
Uncorrectable ECCMCE + CXL Poison + GHESmemory_failure(), 페이지 오프라인경고
포이즌 감지S2M DRS with Poison 비트trace_cxl_poison(), SIGBUS위험
PCIe AER (CXL)AER 인터럽트cxl_pci_aer_handler()심각
CXL Protocol ErrorCXL Event Logcxl_event_trace()심각
Viral 전파CXL Status Register장치 격리 + 리전 비활성화치명적
OvertempCXL Health Infothermal_zone 연동경고
Dirty ShutdownCXL Event LogPMEM 데이터 복구 필요경고
/* drivers/cxl/core/ras.c — CXL RAS 처리 */

/* CXL 이벤트 로그 유형 */
enum cxl_event_log_type {
    CXL_EVENT_TYPE_INFO        = 0x00,  /* 정보 이벤트 */
    CXL_EVENT_TYPE_WARN        = 0x01,  /* 경고 이벤트 */
    CXL_EVENT_TYPE_FAIL        = 0x02,  /* 실패 이벤트 */
    CXL_EVENT_TYPE_FATAL       = 0x03,  /* 치명적 이벤트 */
    CXL_EVENT_TYPE_DCD         = 0x04,  /* Dynamic Capacity 이벤트 */
};

/* CXL 이벤트 레코드 공통 헤더 */
struct cxl_event_record_hdr {
    uuid_t  id;                /* 이벤트 UUID */
    __le32  flags_length;       /* 플래그 + 길이 */
    __le16  handle;             /* 이벤트 핸들 */
    __le16  related_handle;     /* 관련 이벤트 */
    __le64  timestamp;          /* 발생 시각 */
    u8      maintenance_op;     /* 유지보수 권고 */
    u8      reserved[15];
} __packed;

/* CXL AER 에러 핸들러 */
static pci_ers_result_t cxl_error_detected(
    struct pci_dev *pdev,
    pci_channel_state_t state)
{
    struct cxl_dev_state *cxlds = pci_get_drvdata(pdev);

    /* Viral 상태 확인 */
    if (cxl_check_viral(cxlds)) {
        dev_err(&pdev->dev, "CXL Viral 상태 감지 - 장치 격리");
        cxl_disable_region(cxlds);
        return PCI_ERS_RESULT_DISCONNECT;
    }

    /* Uncorrectable Error 처리 */
    cxl_handle_ras_uc_error(cxlds);

    return PCI_ERS_RESULT_NEED_RESET;
}

/* Viral 전파 모드 */
/*
 * CXL Viral은 "독성 전파" 메커니즘입니다.
 * 한 장치에서 복구 불가능한 에러가 발생하면,
 * 해당 장치와 연결된 모든 포트/스위치에 Viral 상태가 전파됩니다.
 * 목적: 오염된 데이터가 다른 장치/호스트로 전파되는 것 방지
 * 결과: Viral 상태의 모든 장치/리전이 비활성화됨
 */
# CXL RAS 이벤트 모니터링

# CXL 이벤트 로그 조회
cxl list -m mem0 -e
# [{"event_log":"Information","nr_records":3},
#  {"event_log":"Warning","nr_records":0},
#  {"event_log":"Failure","nr_records":1}]

# 건강 상태 상세 조회
cxl list -m mem0 -H
# {"health":{
#   "maintenance_needed": false,
#   "performance_degraded": false,
#   "hw_replacement_needed": false,
#   "media_normal": true,
#   "life_used_percent": 5,
#   "temperature": 42,
#   "dirty_shutdowns": 0,
#   "volatile_err_cnt": 0,
#   "pmem_err_cnt": 0}}

# 커널 트레이스 이벤트 활성화
echo 1 > /sys/kernel/debug/tracing/events/cxl/enable
cat /sys/kernel/debug/tracing/trace_pipe
# cxl_general_media: memdev=mem0 dpa=0x1000 descriptor=UC ...
# cxl_aer_uncorrectable_error: memdev=mem0 status=0x... ...
# cxl_poison: memdev=mem0 host_addr=0x... source=Internal

# AER 에러 확인
lspci -vvv -d ::0502 | grep -A5 "AER"
# UESta: ... (Uncorrectable Error Status)
# CESta: ... (Correctable Error Status)

# rasdaemon으로 CXL 에러 수집
systemctl start rasdaemon
ras-mc-ctl --errors  # CXL 에러 포함 전체 RAS 이벤트
Viral 전파 비활성화 주의: 일부 BIOS에서 CXL Viral 전파를 비활성화하는 옵션이 있습니다. 프로덕션 환경에서는 반드시 활성화 상태를 유지하세요. Viral이 비활성이면 오염된 데이터가 DDR에 캐싱되어 시스템 전체의 데이터 무결성이 손상될 수 있습니다. dmesg | grep "CXL.*[Vv]iral"로 상태를 확인할 수 있습니다.

CXL 메모리 핫플러그

CXL Type3 메모리 장치는 시스템 동작 중 추가/제거가 가능한 핫플러그를 지원합니다. PCIe 핫플러그 인프라를 기반으로 CXL 장치를 발견하고, DAX 장치 등록, system-ram 전환, NUMA 노드 추가까지의 전체 과정이 런타임에 이루어집니다. 반대로 장치 제거 시에는 메모리 오프라인, DAX 해제, 리전 삭제가 순서대로 진행됩니다.

# CXL 메모리 핫플러그 (런타임 장치 추가)

# 1. 장치 삽입 후 자동 감지 확인
dmesg | tail -20
# cxl_pci 0000:81:00.0: CXL.mem: bound mem2
# cxl_port: port3 registered, depth 1

# 2. 새 장치 확인
cxl list -M
# [..., {"memdev":"mem2","ram_size":"512.00 GiB"}]

# 3. 리전 생성 및 DAX 등록
cxl create-region -m mem2 -d decoder0.0 -w 1 -g 256 -s 512G
daxctl reconfigure-device --mode=system-ram dax0.1

# 4. NUMA 노드 확인
numactl --hardware
# node 3 추가됨 (512 GB, CPU-less)

# CXL 메모리 핫리무브 (런타임 장치 제거)

# 1. 메모리 오프라인 (페이지 마이그레이션)
daxctl offline-memory dax0.1
# 모든 페이지가 다른 노드로 마이그레이션됨

# 2. DAX 장치 모드 복원
daxctl reconfigure-device --mode=devdax dax0.1

# 3. 리전 비활성화 및 삭제
cxl disable-region region1
cxl destroy-region region1

# 4. 장치 unbind (PCIe 핫플러그)
echo 1 > /sys/bus/cxl/devices/mem2/driver/unbind
# 또는 물리적으로 E3.S 장치 제거

# 핫리무브 실패 시 원인 확인
# "unmovable page" 에러 → 커널 내부 할당 페이지 (non-movable)
# 해결: daxctl online-memory --no-movable 대신
#       daxctl online-memory (movable zone 사용)

# 핫플러그 이벤트 모니터링
udevadm monitor --subsystem-match=cxl
# UDEV  [1234.5678] add      /devices/.../cxl/mem2
# UDEV  [1234.5678] remove   /devices/.../cxl/mem2
핫리무브와 Movable Zone: CXL 메모리를 핫리무브 가능하게 하려면 daxctl online-memory dax0.0(기본: movable zone)으로 온라인해야 합니다. --no-movable 옵션을 사용하면 커널 내부 할당이 CXL에 배치되어 핫리무브가 불가능해집니다. 단, movable zone은 커널 slab 할당에 사용되지 않으므로 일부 워크로드에서 성능 차이가 있을 수 있습니다. 프로덕션에서는 핫리무브 필요 여부에 따라 선택하세요.

QEMU CXL 에뮬레이션 심화

QEMU의 CXL 에뮬레이션은 스위치, 멀티 루트 포트, MLD(Multi-Logical Device), Dynamic Capacity, PMEM 모드 등 다양한 CXL 토폴로지를 구성할 수 있습니다. 실제 하드웨어 없이 커널 드라이버 개발, 리전 관리 스크립트, 티어링 정책을 테스트하는 데 필수적입니다.

# QEMU CXL 고급 토폴로지: 스위치 + MLD + DC

# === 토폴로지 구성 ===
# Host Bridge
#   ├── Root Port 0 ─── Type3 (mem0, 256MB RAM)
#   ├── Root Port 1 ─── Type3 (mem1, 256MB RAM + 256MB PMEM)
#   └── Root Port 2 ─── CXL Switch
#                          ├── DSP 0 ─── Type3 (mem2, 512MB RAM)
#                          └── DSP 1 ─── Type3 (mem3, 512MB RAM, DC)

qemu-system-x86_64 \
  -machine q35,cxl=on \
  -m 4G,maxmem=16G,slots=8 \
  -smp 8 \
  -cpu host -enable-kvm \
  \
  # 메모리 백엔드
  -object memory-backend-file,id=vmem0,share=on,mem-path=/tmp/cxl-vmem0,size=256M \
  -object memory-backend-file,id=vmem1,share=on,mem-path=/tmp/cxl-vmem1,size=256M \
  -object memory-backend-file,id=pmem1,share=on,mem-path=/tmp/cxl-pmem1,size=256M \
  -object memory-backend-file,id=vmem2,share=on,mem-path=/tmp/cxl-vmem2,size=512M \
  -object memory-backend-file,id=vmem3,share=on,mem-path=/tmp/cxl-vmem3,size=512M \
  -object memory-backend-file,id=lsa0,share=on,mem-path=/tmp/cxl-lsa0,size=256M \
  -object memory-backend-file,id=lsa1,share=on,mem-path=/tmp/cxl-lsa1,size=256M \
  -object memory-backend-file,id=lsa2,share=on,mem-path=/tmp/cxl-lsa2,size=256M \
  -object memory-backend-file,id=lsa3,share=on,mem-path=/tmp/cxl-lsa3,size=256M \
  \
  # CXL 호스트 브리지 + CFMWS
  -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \
  -cxl-fixed-memory-window targets.0=cxl.1,size=4G \
  \
  # 직접 연결: Root Port 0, 1
  -device cxl-rp,port=0,bus=cxl.1,id=rp0,chassis=0,slot=2 \
  -device cxl-rp,port=1,bus=cxl.1,id=rp1,chassis=0,slot=3 \
  # 스위치 연결: Root Port 2
  -device cxl-rp,port=2,bus=cxl.1,id=rp2,chassis=0,slot=4 \
  \
  # Type3 장치 (직접 연결)
  -device cxl-type3,bus=rp0,volatile-memdev=vmem0,\
    lsa=lsa0,id=cxl-t3-0 \
  -device cxl-type3,bus=rp1,volatile-memdev=vmem1,\
    persistent-memdev=pmem1,lsa=lsa1,id=cxl-t3-1 \
  \
  # CXL 스위치 (USP + 2 DSP)
  -device cxl-upstream,bus=rp2,id=cxl-us0 \
  -device cxl-downstream,port=0,bus=cxl-us0,id=cxl-ds0,chassis=0,slot=5 \
  -device cxl-downstream,port=1,bus=cxl-us0,id=cxl-ds1,chassis=0,slot=6 \
  \
  # Type3 장치 (스위치 하위)
  -device cxl-type3,bus=cxl-ds0,volatile-memdev=vmem2,\
    lsa=lsa2,id=cxl-t3-2 \
  -device cxl-type3,bus=cxl-ds1,volatile-memdev=vmem3,\
    lsa=lsa3,id=cxl-t3-3,num-dc-regions=2 \
  \
  # 부팅
  -kernel /boot/vmlinuz-6.8.0 \
  -initrd /boot/initrd.img-6.8.0 \
  -append "root=/dev/sda1 console=ttyS0 \
    cxl_acpi.dyndbg=+p cxl_core.dyndbg=+p \
    cxl_pci.dyndbg=+p cxl_port.dyndbg=+p \
    cxl_mem.dyndbg=+p" \
  -drive file=guest.qcow2,format=qcow2 \
  -nographic

# === 게스트 내부 검증 ===

# CXL 토폴로지 전체 확인
cxl list -BEIMPDRu
# bus0 → root0 → port1,2,3 → endpoint4,5,6,7 → mem0,1,2,3

# 스위치 토폴로지 확인
cxl list -S  # switch 목록
# [{"switch":"switch0","depth":1,"nr_dports":2}]

# 2-way 인터리브 리전 생성 (스위치 하위 장치)
cxl create-region -m mem2 -m mem3 -d decoder0.0 -w 2 -g 256 -s 1G

# DC 장치 확인
cxl list -m mem3 -vi | grep dc
# "dc_region_count":2
QEMU CXL 빌드: 최신 CXL 기능(DC, 스위치, MLD)을 테스트하려면 QEMU 8.2+ 버전이 필요합니다. 소스 빌드 시 --enable-cxl 옵션을 확인하세요. CXL 게스트 커널은 6.6+ (PMU/포이즌), 6.8+ (향상된 리전), 6.10+ (DC 초기)가 권장됩니다. 게스트 내 cxl-cli는 ndctl 패키지 v79+에서 제공됩니다.

성능 특성과 NUMA 노드 연동 심화

CXL 메모리의 실질적 성능은 PCIe 링크 세대, 레인 수, 인터리브 구성, NUMA 거리, 그리고 워크로드의 접근 패턴(순차/랜덤, 읽기/쓰기 비율)에 따라 크게 달라집니다. 실전에서는 HMAT/CDAT 기반 성능 특성을 정확히 파악하고, 워크로드를 프로파일링하여 CXL 메모리 배치 전략을 수립해야 합니다.

메모리 계층별 지연 vs 대역폭 특성 랜덤 읽기 지연 (ns, 로그 스케일) 순차 읽기 대역폭 (GB/s) 80 200 350 500 70000 us 14 50 100 200 256 DDR5 로컬 DDR5 원격 CXL 3.0 4-way CXL 1.1 단일 PMEM NVMe 메모리 계층 DDR5 로컬 (~80ns, ~200GB/s) CXL 3.0 4-way (~250ns, ~256GB/s) CXL 1.1 단일 (~350ns, ~50GB/s) PMEM/NVMe (400ns~70us)
# === CXL 성능 프로파일링 실전 가이드 ===

# 1. HMAT/CDAT 기반 성능 특성 확인
# 각 NUMA 노드의 접근 특성 (initiator별)
for node in /sys/devices/system/node/node*; do
    name=$(basename $node)
    if [ -d "$node/access0/initiators" ]; then
        rlat=$(cat $node/access0/initiators/read_latency 2>/dev/null)
        wlat=$(cat $node/access0/initiators/write_latency 2>/dev/null)
        rbw=$(cat $node/access0/initiators/read_bandwidth 2>/dev/null)
        wbw=$(cat $node/access0/initiators/write_bandwidth 2>/dev/null)
        echo "$name: R_lat=${rlat}ns W_lat=${wlat}ns R_BW=${rbw}MB/s W_BW=${wbw}MB/s"
    fi
done
# node0: R_lat=80ns W_lat=85ns R_BW=204800MB/s W_BW=204800MB/s  (DDR5)
# node1: R_lat=160ns W_lat=170ns R_BW=102400MB/s W_BW=102400MB/s (DDR5 원격)
# node2: R_lat=300ns W_lat=350ns R_BW=51200MB/s W_BW=46080MB/s   (CXL)

# 2. Intel MLC 상세 지연 측정
# 노드 간 유휴 지연 행렬
mlc --latency_matrix
#        0       1       2
# 0     78.3   158.2   298.5
# 1    158.2    78.3   298.5
# 2    298.5   298.5     -    (CXL는 CPU 없음)

# 부하 시 지연 곡선 (대역폭 증가에 따른 지연 변화)
mlc --loaded_latency -d0 -T -b100m
# Inject  Latency  Bandwidth
# 00000   298.5ns  52.3 GB/s   ← 유휴 상태
# 10000   305.2ns  51.8 GB/s
# 50000   342.1ns  48.2 GB/s
# 80000   425.3ns  35.6 GB/s   ← 대역폭 포화 시 지연 급증

# 3. STREAM 벤치마크 비교
# DDR5 노드
numactl --cpunodebind=0 --membind=0 ./stream_c.exe
# Copy:  198,456 MB/s

# CXL 노드 (동일 CPU)
numactl --cpunodebind=0 --membind=2 ./stream_c.exe
# Copy:  49,234 MB/s (DDR 대비 ~25%)

# DDR + CXL 인터리브
numactl --cpunodebind=0 --interleave=0,2 ./stream_c.exe
# Copy:  123,890 MB/s (DDR+CXL 합산 ~62%)

# 4. perf c2c로 캐시 라인 공유 분석
perf c2c record -a -- sleep 10
perf c2c report --sort=dso,iaddr
# CXL 메모리의 캐시 라인 false sharing 감지

# 5. bpftrace로 NUMA fault 추적
bpftrace -e '
tracepoint:migrate:mm_migrate_pages {
    printf("migrate: src_nid=%d dst_nid=%d nr=%d mode=%s\n",
           args->src_nid, args->dst_nid,
           args->nr_succeeded, args->mode == 0 ? "sync" : "async");
}'
# migrate: src_nid=2 dst_nid=0 nr=32 mode=async  ← CXL→DDR promotion
# migrate: src_nid=0 dst_nid=2 nr=128 mode=async ← DDR→CXL demotion
워크로드 유형CXL 배치 전략예상 성능 영향권장 인터리브
LLM 추론 (배치)KV 캐시 → CXL, 모델 → DDR처리량 유지, 배치 8x1-way (장치별 독립)
LLM 추론 (실시간)KV 캐시 → DDR, overflow → CXLP99 지연 2x 이내자동 tiering
벡터 DB (ANN)인덱스 전체 → CXL검색 지연 +200ns2-way, 256B
인메모리 DB인덱스 → DDR, 데이터 → CXLTPS -15~30%자동 tiering
HPC (MPI)통신 버퍼 → DDR, 계산 배열 → CXL대역폭 민감 구간 -40%4-way, 256B
로그/캐시 서버전체 → CXL (지연 무관)용량 10x, 비용 -60%1-way
Java/JVM 힙Old Gen → CXL, Young Gen → DDRGC 지연 +50%자동 tiering
CXL 메모리 비용 효율성: 2024년 기준 CXL DDR5 메모리 가격은 일반 DDR5의 약 40~60% 수준입니다. 512GB DDR5 서버 구성 비용의 상당 부분을 CXL로 대체하면 TCO(Total Cost of Ownership)를 크게 절감할 수 있습니다. 특히 AI 추론 서버에서 KV 캐시 용량을 8~16배 확장하면서 DDR 비용을 억제하는 전략이 주목받고 있습니다. Samsung CMM-D 256GB(E3.S) 기준 약 $300~400으로, 동일 용량 DDR5 RDIMM($600~800) 대비 50% 절약 가능합니다.

CXL 커널 테스트 프레임워크

리눅스 커널의 tools/testing/cxl/ 디렉토리에는 CXL 드라이버를 위한 모의(mock) 테스트 인프라가 제공됩니다. 실제 하드웨어 없이 커널 내부의 CXL 코드 경로를 단위 테스트할 수 있으며, QEMU보다 빠르고 가벼운 검증이 가능합니다.

# CXL 단위 테스트 빌드 및 실행

# 커널 설정에 테스트 옵션 활성화
# CONFIG_CXL_REGION_INVALIDATION_TEST=y
# CONFIG_TEST_CXL=m

# cxl_mock 모듈 로드
modprobe cxl_test
# cxl_test: 가상 CXL 토폴로지 생성
# cxl_test: mock host bridge 0 registered
# cxl_test: mock root port 0,1 registered
# cxl_test: mock memdev 0,1,2,3 registered

# ndctl/cxl 테스트 스위트 실행
cd tools/testing/cxl/
make cxl_test

# 또는 ndctl 프로젝트의 통합 테스트
git clone https://github.com/pmem/ndctl.git
cd ndctl
meson setup build
cd build
meson test -C . --suite cxl
# 테스트 항목:
#   cxl-topology.sh    — 토폴로지 열거 테스트
#   cxl-region.sh      — 리전 생성/삭제 테스트
#   cxl-labels.sh      — LSA 레이블 테스트
#   cxl-security.sh    — 보안 명령 테스트
#   cxl-events.sh      — 이벤트 로그 테스트
#   cxl-poison.sh      — 포이즌 관리 테스트

# 테스트 정리
modprobe -r cxl_test
/* tools/testing/cxl/test/cxl.c — CXL 모의 드라이버 */

/* 가상 CXL 토폴로지 정의 */
static struct cxl_mock_topology mock_topo = {
    .nr_host_bridges = 2,
    .host_bridge = {
        [0] = {
            .nr_root_ports = 2,
            .root_port = {
                [0] = { .nr_endpoints = 1 },  /* mem0 */
                [1] = { .nr_endpoints = 1 },  /* mem1 */
            },
        },
        [1] = {
            .nr_root_ports = 2,
            .root_port = {
                [0] = { .nr_endpoints = 1 },  /* mem2 */
                [1] = { .nr_endpoints = 1 },  /* mem3 */
            },
        },
    },
};

/* 가상 memdev 설정 */
static void mock_memdev_init(struct cxl_dev_state *cxlds,
                              int id)
{
    cxlds->serial = 0xDEADBEEF0000 + id;
    cxlds->total_bytes = SZ_256M;    /* 256MB 모의 메모리 */
    cxlds->volatile_only_bytes = SZ_256M;
    cxlds->persistent_only_bytes = 0;
    cxlds->lsa_size = SZ_64K;
}

/* 가상 메일박스 명령 처리 */
static int mock_mbox_send(struct cxl_mailbox *mbox,
                           struct cxl_mbox_cmd *cmd)
{
    switch (cmd->opcode) {
    case CXL_MBOX_OP_IDENTIFY:
        return mock_identify(mbox, cmd);
    case CXL_MBOX_OP_GET_HEALTH_INFO:
        return mock_health(mbox, cmd);
    case CXL_MBOX_OP_GET_POISON:
        return mock_get_poison(mbox, cmd);
    default:
        return -ENOTSUPP;
    }
}

CXL 디버깅과 트러블슈팅

CXL 시스템에서 발생하는 문제는 하드웨어(BIOS/펌웨어), 토폴로지(포트/디코더 구성), 드라이버(프로브 실패), 리전(생성/커밋 오류), 성능(예상보다 낮은 대역폭/높은 지연) 등 다양한 계층에서 발생합니다. 체계적인 디버깅 워크플로가 필수적입니다.

증상의심 계층진단 명령해결 방법
CXL 장치 미발견BIOS/펌웨어lspci -d ::0502BIOS에서 CXL 활성화, CEDT 확인
memdev 미등록cxl_pci 드라이버dmesg | grep cxl_pciDVSEC 파싱 에러 확인, 커널 버전 확인
포트 누락cxl_port 드라이버cxl list -P스위치 감지 여부, cxl_port.dyndbg
리전 생성 실패디코더/리전cxl list -DCFMWS 범위, 인터리브 설정 확인
DAX 전환 실패DAX/memhotplugdaxctl listCONFIG_DEV_DAX_KMEM 확인
NUMA 노드 미노출memory-tiersnumactl -Hdaxctl reconfigure 확인
성능 저하인터리브/PCIemlc --bandwidth_matrix인터리브 구성, PCIe 링크 속도
포이즌 에러메모리 미디어cxl list -m mem0 --poisonClear Poison, 미디어 스캔
Viral 상태RASdmesg | grep -i viral장치 리셋, 리전 재구성
# CXL 디버깅 종합 스크립트
#!/bin/bash
set -e

echo "=== 1. PCIe/CXL 장치 감지 ==="
lspci -d ::0502 2>/dev/null || echo "CXL Type3 장치 없음"
lspci -d ::0501 2>/dev/null || echo "CXL Type2 장치 없음"
lspci -d ::0500 2>/dev/null || echo "CXL Type1 장치 없음"

echo ""
echo "=== 2. CXL 커널 모듈 상태 ==="
lsmod | grep -E "^cxl" || echo "CXL 모듈 미로드"

echo ""
echo "=== 3. CXL 장치 목록 ==="
cxl list -BEIMPDRu 2>/dev/null || echo "cxl-cli 미설치 또는 장치 없음"

echo ""
echo "=== 4. CXL 커널 메시지 (최근 50줄) ==="
dmesg | grep -Ei "cxl|cedt|cfmws|hmat" | tail -50

echo ""
echo "=== 5. NUMA 토폴로지 ==="
numactl --hardware 2>/dev/null || echo "numactl 미설치"

echo ""
echo "=== 6. 메모리 티어 ==="
for tier in /sys/devices/virtual/memory_tiering/memory_tier*; do
    if [ -d "$tier" ]; then
        echo "$(basename $tier): $(cat $tier/nodelist 2>/dev/null)"
    fi
done

echo ""
echo "=== 7. DAX 장치 ==="
daxctl list 2>/dev/null || echo "daxctl 미설치"

echo ""
echo "=== 8. PCIe 링크 상태 ==="
for dev in $(lspci -d ::0502 -D 2>/dev/null | awk '{print $1}'); do
    echo "--- $dev ---"
    lspci -vvv -s $dev 2>/dev/null | grep -E "LnkSta:|LnkCap:|CXL"
done

echo ""
echo "=== 9. 에러 상태 ==="
for mem in /sys/bus/cxl/devices/mem*; do
    if [ -d "$mem" ]; then
        name=$(basename $mem)
        echo "--- $name ---"
        cat $mem/firmware_version 2>/dev/null
        cat $mem/serial 2>/dev/null
    fi
done

echo ""
echo "=== 10. demotion/promotion 통계 ==="
grep -E "pgpromote|pgdemote|numa_hint" /proc/vmstat 2>/dev/null
동적 디버그 활성화: CXL 드라이버 문제 진단 시 가장 유용한 도구는 동적 디버그입니다. 부팅 옵션 또는 런타임에 활성화할 수 있습니다. 부팅 옵션: cxl_acpi.dyndbg=+p cxl_core.dyndbg=+p cxl_pci.dyndbg=+p cxl_port.dyndbg=+p cxl_mem.dyndbg=+p 런타임: echo 'module cxl_core +p' > /sys/kernel/debug/dynamic_debug/control 또한 ftrace의 CXL 이벤트를 활용하면 포이즌, AER, 이벤트 로그를 실시간으로 추적할 수 있습니다. echo 1 > /sys/kernel/debug/tracing/events/cxl/enable
# CXL 관련 커널 설정 확인 스크립트
#!/bin/bash
CONFIG="/boot/config-$(uname -r)"

echo "=== CXL 커널 설정 확인 ==="
for opt in \
    CONFIG_CXL_BUS CONFIG_CXL_PCI CONFIG_CXL_ACPI \
    CONFIG_CXL_PMEM CONFIG_CXL_MEM CONFIG_CXL_PORT \
    CONFIG_CXL_REGION CONFIG_CXL_PMU \
    CONFIG_DEV_DAX CONFIG_DEV_DAX_CXL CONFIG_DEV_DAX_KMEM \
    CONFIG_NUMA_BALANCING CONFIG_MEMORY_HOTPLUG \
    CONFIG_MEMORY_TIER_DEFAULT_DRAM_PERF_VALUES; do

    val=$(grep "^${opt}=" $CONFIG 2>/dev/null || echo "not set")
    if echo "$val" | grep -q "=y\|=m"; then
        echo "[OK]  $opt = $(echo $val | cut -d= -f2)"
    else
        echo "[!!]  $opt : $val"
    fi
done
다음 학습 경로:
외부 참고 자료:
  • CXL 규격서: computeexpresslink.org (CXL 3.1 Specification)
  • 커널 문서: Documentation/driver-api/cxl/ (커널 소스 트리)
  • ndctl/cxl-cli: github.com/pmem/ndctl (cxl-cli 소스)
  • QEMU CXL: docs/system/devices/cxl.rst (QEMU 소스 트리)
  • 커널 CXL 테스트: tools/testing/cxl/ (cxl_mock 드라이버)