XFS 파일시스템 심화

대용량·고병렬 환경에 최적화된 XFS를 대상으로 Allocation Group 분할이 락 경합을 줄이는 방식, B+tree 기반 free space/inode 인덱싱, extent 지연 할당과 writeback 경로, 로그(WAL) 재생 및 복구 절차, reflink/COW 동작, quota/프로젝트 ID 운용, xfs_db·xfs_repair·xfs_scrub 활용법까지 성능과 안정성 관점에서 상세히 설명합니다.

전제 조건: VFSBlock I/O 서브시스템 문서를 먼저 읽으세요. 디스크 기반 파일시스템은 저널링·할당기·복구 정책 차이가 핵심이므로, I/O 경로와 on-disk 구조를 함께 봐야 합니다.
일상 비유: 이 주제는 창고 배치와 재고 장부 운영과 비슷합니다. 공간 배치 규칙과 기록 정책이 달라지면 성능·복구·무결성 특성이 크게 달라집니다.

핵심 요약

  • 계층 이해 — VFS, 캐시, 하위 FS 경계를 구분합니다.
  • 메타데이터 우선 — inode/dentry 일관성을 먼저 확인합니다.
  • 저장 정책 — 저널링/압축/할당 정책 차이를 비교합니다.
  • 일관성 모델 — 로컬/원격/합성 FS의 반영 시점을 구분합니다.
  • 복구 관점 — 장애 시 재구성 경로를 함께 점검합니다.

단계별 이해

  1. 경계 계층 파악
    요청이 VFS에서 어디로 내려가는지 확인합니다.
  2. 메타/데이터 분리
    어느 경로에서 무엇이 갱신되는지 나눠 봅니다.
  3. 동기화/플러시 확인
    쓰기 반영 시점과 순서를 검증합니다.
  4. 복구 시나리오 점검
    비정상 종료 후 일관성 회복을 확인합니다.
관련 표준: XFS는 POSIX.1-2017 호환 파일시스템으로, SGI IRIX에서 개발되어 Linux에 이식되었습니다. RHEL 7 이후 기본 파일시스템으로 채택되어 엔터프라이즈 Linux 환경에서 핵심적인 역할을 합니다. 종합 목록은 참고자료 — 표준 & 규격 섹션을 참고하세요.

개요 & 역사

XFS는 1993년 SGI(Silicon Graphics)가 IRIX 운영체제를 위해 개발한 64비트 고성능 저널링 파일시스템입니다. 2001년 Linux 커널 2.4에 이식되었으며, 2014년 RHEL 7에서 기본 파일시스템으로 채택된 이후 엔터프라이즈 Linux의 사실상 표준이 되었습니다.

XFS 역사

연도이벤트
1993SGI IRIX 5.3에서 XFS 최초 공개
2001Linux 커널 2.4에 이식 (SGI 오픈소스 기여)
2006Delayed Allocation 지원 추가
2012v5 on-disk format: CRC32C 셀프체크 메타데이터
2014RHEL 7 기본 파일시스템 채택
2016Reflink / COW 지원 (커널 4.9)
2019Online repair 프레임워크 개발 시작
2023Online fsck (xfs_scrub) 안정화, Large extent counters

XFS 주요 스펙

항목
최대 볼륨 크기8 EiB (263 바이트)
최대 파일 크기8 EiB
최대 파일 수264 (동적 inode 할당)
파일명 길이255 바이트
블록 크기512B / 1K / 2K / 4K (기본 4K, 최대 64K)
타임스탬프 범위1901 ~ 2486 (나노초 정밀도)
저널 방식Metadata-only Write-Ahead Logging (WAL)
On-disk formatv5 (CRC32C self-describing metadata, 기본)

ext4 / Btrfs / XFS 비교

특성ext4XFSBtrfs
최대 볼륨1 EiB8 EiB16 EiB
최대 파일16 TiB8 EiB16 EiB
온라인 축소지원미지원지원
온라인 확장지원지원지원
COW / 스냅샷미지원Reflink (4.9+)기본 COW
저널링JBD2 (데이터+메타)WAL (메타 전용)COW (암묵적)
inode 할당고정 (mkfs 시 결정)동적동적
병렬 I/OBlock Group 단위AG 기반 고도 병렬Chunk 기반
대형 파일 성능우수최우수우수
RHEL 기본 FSRHEL 6RHEL 7+미채택

아키텍처 & 디스크 레이아웃

XFS의 핵심 설계 철학은 Allocation Group(AG) 기반 병렬 처리입니다. 전체 파일시스템을 독립적인 AG로 분할하여 각 AG가 자체 공간 관리 구조를 가지므로, 멀티코어 환경에서 메타데이터 경합 없이 병렬 할당이 가능합니다.

XFS Allocation Group 레이아웃 XFS 파일시스템 (전체 볼륨) AG 0 | AG 1 | AG 2 | ... | AG N-1 Allocation Group 내부 구조 (AG 0) Superblock xfs_sb AGF Free Space B+tree AGI Inode B+tree AGFL Free List 데이터 / 메타데이터 블록 Inode Chunks, Extents, B+tree Nodes AGF 내부: 이중 B+tree Free Space 관리 BNO B+tree (블록 번호 순) CNT B+tree (크기 순) BNO: 인접 할당 최적화 | CNT: 최적 크기 Extent 탐색 각 AG는 독립적 잠금 → 멀티코어 병렬 할당 가능

xfs_sb 주요 필드

/* fs/xfs/libxfs/xfs_format.h */
typedef struct xfs_sb {
    __uint32_t  sb_magicnum;     /* 0x58465342 ('XFSB') */
    __uint32_t  sb_blocksize;    /* 파일시스템 블록 크기 (바이트) */
    xfs_rfsblock_t sb_dblocks;  /* 데이터 영역 총 블록 수 */
    xfs_rfsblock_t sb_rblocks;  /* 리얼타임 영역 블록 수 */
    xfs_rtblock_t  sb_rextents; /* 리얼타임 extent 수 */
    uuid_t      sb_uuid;         /* 파일시스템 UUID */
    xfs_fsblock_t sb_logstart;  /* 내부 로그 시작 블록 (0=외부) */
    xfs_ino_t   sb_rootino;      /* 루트 디렉토리 inode 번호 */
    xfs_agblock_t sb_agblocks;  /* AG당 블록 수 */
    xfs_agnumber_t sb_agcount; /* AG 개수 */
    __uint32_t  sb_sectsize;     /* 디스크 섹터 크기 */
    __uint16_t  sb_inodesize;    /* inode 크기 (기본 512) */
    __uint16_t  sb_inopblock;    /* 블록당 inode 수 */
    __uint32_t  sb_versionnum;   /* 기능 비트 마스크 */
    __uint32_t  sb_features2;    /* 확장 기능 플래그 */
    __uint32_t  sb_features_compat;   /* v5: 호환 기능 */
    __uint32_t  sb_features_incompat; /* v5: 비호환 기능 (reflink 등) */
    __uint32_t  sb_crc;          /* v5: CRC32C 체크섬 */
    ...
} xfs_sb_t;

Allocation Groups 상세

XFS 볼륨은 여러 개의 Allocation Group(AG)으로 등분됩니다. 각 AG는 자체 수퍼블록 복사본, 프리 스페이스 B+tree, inode 관리 구조를 가집니다. 이 설계 덕분에 여러 스레드가 서로 다른 AG에서 동시에 할당 작업을 수행할 수 있습니다.

AGF (AG Free Space)

AGF 헤더는 각 AG의 프리 블록 관리를 담당합니다. 두 개의 B+tree를 유지합니다:

B+tree용도
BNO tree시작 블록 번호특정 위치 근처에서 할당 (공간적 인접성)
CNT treeextent 크기요청 크기에 가장 적합한 free extent 탐색
/* fs/xfs/libxfs/xfs_format.h */
typedef struct xfs_agf {
    __be32   agf_magicnum;      /* 'XAGF' */
    __be32   agf_versionnum;
    __be32   agf_seqno;         /* AG 번호 */
    __be32   agf_length;        /* AG 블록 수 */
    __be32   agf_roots[2];      /* BNO, CNT B+tree 루트 */
    __be32   agf_levels[2];     /* BNO, CNT B+tree 높이 */
    __be32   agf_flfirst;       /* AGFL 첫 활성 항목 */
    __be32   agf_fllast;        /* AGFL 마지막 활성 항목 */
    __be32   agf_flcount;       /* AGFL 활성 항목 수 */
    __be32   agf_freeblks;      /* AG 내 총 free 블록 */
    __be32   agf_longest;       /* 가장 긴 free extent 크기 */
    __be32   agf_rmap_root;     /* v5: reverse mapping B+tree 루트 */
    __be32   agf_refcount_root; /* v5: refcount B+tree 루트 */
    ...
} xfs_agf_t;

AGI (AG Inode Management)

AGI는 AG 내 inode 할당을 관리합니다. Inode B+tree로 사용 중인 inode chunk를 추적하고, Free Inode B+tree로 여유 inode가 있는 chunk를 빠르게 찾습니다.

/* fs/xfs/libxfs/xfs_format.h */
typedef struct xfs_agi {
    __be32   agi_magicnum;     /* 'XAGI' */
    __be32   agi_versionnum;
    __be32   agi_seqno;        /* AG 번호 */
    __be32   agi_length;       /* AG 블록 수 */
    __be32   agi_count;        /* AG 내 할당된 inode 수 */
    __be32   agi_root;         /* Inode B+tree 루트 */
    __be32   agi_level;        /* Inode B+tree 높이 */
    __be32   agi_freecount;    /* AG 내 free inode 수 */
    __be32   agi_newino;       /* 가장 최근 할당 inode chunk */
    __be32   agi_free_root;    /* Free Inode B+tree 루트 */
    __be32   agi_free_level;   /* Free Inode B+tree 높이 */
    ...
} xfs_agi_t;

AGFL (AG Free List)

AGFL은 B+tree 분할/병합 시 필요한 메타데이터 블록을 예약하는 소규모 풀입니다. B+tree 조작 중에 추가 블록이 필요하면 AGFL에서 꺼내 쓰고, B+tree가 축소되면 반납합니다. 이는 B+tree 수정과 공간 할당 사이의 순환 의존성을 해결합니다.

병렬 I/O 스케일링

설계 원리: ext4의 Block Group은 잠금 경합이 발생할 수 있지만, XFS AG는 각각 독립적인 B+tree와 잠금을 가집니다. 따라서 N개 코어가 서로 다른 AG에서 동시에 파일을 생성/확장할 수 있어, 대규모 멀티스레드 I/O 워크로드에서 선형적 성능 확장이 가능합니다.
/* AG별 독립 잠금 — fs/xfs/xfs_mount.h */
typedef struct xfs_perag {
    struct xfs_mount   *pag_mount;
    xfs_agnumber_t      pag_agno;      /* AG 번호 */
    atomic_t            pag_ref;       /* 참조 카운트 */
    struct rw_semaphore pag_ici_lock;  /* inode cache lock */
    struct xfs_buf     *pag_agf_bp;   /* AGF 버퍼 */
    struct xfs_buf     *pag_agi_bp;   /* AGI 버퍼 */
    ...
} xfs_perag_t;

B+tree 구조

XFS는 모든 메타데이터를 B+tree로 관리합니다. 이는 O(log n) 탐색 성능과 대규모 데이터셋에서의 일관된 성능을 보장합니다.

XFS B+tree 종류

B+tree위치용도
BNO B+treeAGF시작 블록 번호Free space (위치별 검색)
CNT B+treeAGFextent 크기Free space (크기별 검색)
Inode B+treeAGIinode 번호Inode chunk 추적
Free Inode B+treeAGIinode 번호Free inode chunk 추적
Reverse Map B+treeAGF (v5)물리 블록역방향 매핑 (online repair)
Refcount B+treeAGF (v5)시작 블록Reflink 참조 카운트
Extent B+treeInode data fork파일 오프셋파일 extent 매핑
Directory B+treeInode data fork해시값대형 디렉토리 엔트리
Attr B+treeInode attr fork이름 해시확장 속성

Short-format vs Long-format

XFS B+tree는 두 가지 포인터 형식을 사용합니다:

형식범위사용처
Short-formatAG 내부 (AG 상대 블록 번호)AGF/AGI의 free space, inode, rmap, refcount B+tree
Long-format전체 파일시스템 (절대 블록 번호)Inode data fork의 extent B+tree, 디렉토리/속성 B+tree
/* B+tree 커서 — fs/xfs/libxfs/xfs_btree.h */
struct xfs_btree_cur {
    struct xfs_mount   *bc_mp;        /* 파일시스템 마운트 */
    const struct xfs_btree_ops *bc_ops;  /* B+tree 연산 함수 */
    uint                bc_btnum;     /* B+tree 종류 식별자 */
    int                 bc_nlevels;   /* 트리 높이 */
    union {
        struct {
            struct xfs_buf  *agbp; /* AG 헤더 버퍼 (short) */
            xfs_agnumber_t  agno; /* AG 번호 */
        } s;
        struct {
            struct xfs_inode *ip; /* inode (long) */
            int             whichfork;
        } l;
    } bc_ino;
    struct xfs_btree_level bc_levels[]; /* 레벨별 상태 */
};

Extent 관리

XFS는 파일 데이터를 extent 단위로 관리합니다. 각 extent는 연속된 파일시스템 블록의 범위를 나타내며, 128비트(16바이트) packed 레코드로 저장됩니다.

Extent 레코드 형식

/* 128비트 Extent 레코드 구조:
 *  비트 [0:8]    — extent flag (1비트) + 논리 오프셋 상위 (8비트)
 *  비트 [9:62]   — 논리 오프셋(54비트): 파일 내 시작 블록
 *  비트 [63:115] — 물리 블록 번호(52비트): AG번호 + AG 내 블록
 *  비트 [116:127]— extent 길이(21비트): 최대 2M 블록
 */
typedef struct xfs_bmbt_rec {
    __be64   l0;   /* flag(1) + offset(54) + startblock 상위(9) */
    __be64   l1;   /* startblock 하위(43) + blockcount(21) */
} xfs_bmbt_rec_t;

/* 언패킹 후 논리적 표현 */
typedef struct xfs_bmbt_irec {
    xfs_fileoff_t   br_startoff;    /* 파일 내 논리 오프셋 */
    xfs_fsblock_t   br_startblock;  /* 물리 블록 번호 */
    xfs_filblks_t   br_blockcount;  /* 블록 수 */
    xfs_exntst_t    br_state;       /* written / unwritten */
} xfs_bmbt_irec_t;

Delayed Allocation (delalloc)

XFS의 Delayed Allocation은 write() 시점에서 실제 블록 할당을 지연시키고, writeback 시점에 한꺼번에 할당합니다. 이를 통해:

/* delalloc extent는 br_startblock에 특수 값을 사용 */
#define DELAYSTARTBLOCK  ((xfs_fsblock_t)-1LL)
#define HOLESTARTBLOCK   ((xfs_fsblock_t)-2LL)

/* delalloc 예약: 실제 블록 없이 카운터만 증가 */
int xfs_bmapi_reserve_delalloc(
    struct xfs_inode    *ip,
    int                 whichfork,
    struct xfs_bmbt_irec *got,
    struct xfs_bmbt_irec *prev,
    xfs_filblks_t       len,
    int                 eof);

Preallocation & Extent Size Hints

fallocate() 또는 xfs_io -c 'extsize'를 통해 extent 할당 크기를 제어할 수 있습니다:

/* Extent size hint 설정 — 데이터베이스 워크로드 최적화 */
$ xfs_io -c 'extsize 16m' /data/tablespace
/* 16MB 단위로 extent 할당하여 단편화 방지 */

/* fallocate로 사전 할당 */
$ fallocate -l 10G /data/bigfile
/* unwritten extent로 10GB 연속 공간 확보 */

Unwritten Extents

Unwritten extent는 물리 블록이 할당되었지만 아직 데이터가 쓰여지지 않은 상태입니다. fallocate()로 사전 할당하면 이 상태가 됩니다. 읽기 시 0을 반환하고, 실제 쓰기 시 written 상태로 전환됩니다. 이를 통해 보안(이전 데이터 노출 방지)과 성능(연속 할당)을 모두 달성합니다.

Inode 구조

XFS inode는 고정 크기(기본 512바이트)로, 동적으로 할당됩니다. 64개의 inode가 하나의 inode chunk를 구성하며, 필요에 따라 AG 내에서 새 chunk를 할당합니다.

Inode 포맷

버전크기특징
v1256B초기 형식, 32비트 프로젝트 ID
v2256B나노초 타임스탬프, 64비트 프로젝트 ID
v3512B (기본)CRC32C 체크섬, change count, 생성 시간
/* fs/xfs/libxfs/xfs_format.h */
typedef struct xfs_dinode {
    __be16   di_magic;         /* 0x494e ('IN') */
    __be16   di_mode;          /* 파일 유형 + 퍼미션 */
    __u8     di_version;       /* inode 버전 (1/2/3) */
    __u8     di_format;        /* data fork 형식 */
    __be32   di_uid;           /* 소유자 UID */
    __be32   di_gid;           /* 소유자 GID */
    __be32   di_nlink;         /* 하드링크 수 */
    __be64   di_size;          /* 파일 크기 (바이트) */
    __be64   di_nblocks;       /* 할당된 블록 수 */
    __be32   di_extsize;       /* extent size hint */
    __be32   di_nextents;      /* data fork extent 수 */
    __be16   di_anextents;     /* attr fork extent 수 */
    __u8     di_forkoff;       /* attr fork 시작 오프셋 (8바이트 단위) */
    __s8     di_aformat;       /* attr fork 형식 */
    /* v3 추가 필드 */
    __be32   di_crc;           /* CRC32C */
    __be64   di_changecount;   /* inode 변경 횟수 */
    __be64   di_flags2;        /* 확장 플래그 (reflink 등) */
    ...
} xfs_dinode_t;

Data Fork 형식 전환

파일의 extent 수에 따라 data fork 저장 형식이 자동 전환됩니다:

형식di_format 값조건설명
Local (Inline)XFS_DINODE_FMT_LOCAL데이터가 inode 내 수용 가능심볼릭 링크, 소형 디렉토리
Extents ListXFS_DINODE_FMT_EXTENTSextent 수가 fork 공간 내extent 레코드를 inode에 직접 저장
B+treeXFS_DINODE_FMT_BTREEextent 수가 fork 초과B+tree 루트만 inode에, 나머지는 외부 블록

Attr Fork

inode 내부는 Data ForkAttr Fork로 분할됩니다. di_forkoff가 경계를 정의하며, 확장 속성(xattr)은 Attr Fork에 저장됩니다. Data Fork와 동일한 Local → Extents → B+tree 전환 메커니즘을 사용합니다.

디렉토리 구조

XFS 디렉토리는 엔트리 수에 따라 5단계로 구조가 확장됩니다:

형식조건구조
Shortforminode 내 수용 가능이름/inode 쌍을 inode data fork에 인라인 저장
Block1 블록에 수용단일 디렉토리 데이터 블록 (해시 정렬)
Leaf다수 데이터 블록데이터 블록 + 별도 리프 블록 (해시→데이터 매핑)
Node리프가 1블록 초과데이터 + 리프 + 내부 노드 블록 (B+tree)
B+treeextent 수 초과inode의 extent list가 B+tree로 전환

디렉토리 해시

XFS는 xfs_da_hashname()으로 파일명의 해시값을 계산합니다. 이 해시는 디렉토리 내 엔트리 검색을 O(log n)으로 만들어 대규모 디렉토리에서도 빠른 lookup을 보장합니다.

/* fs/xfs/libxfs/xfs_da_btree.h */
xfs_dahash_t xfs_da_hashname(
    const __uint8_t *name,
    int              namelen);

/* Leaf 엔트리: 해시값 → 데이터 블록 오프셋 */
typedef struct xfs_dir2_leaf_entry {
    __be32   hashval;    /* 이름 해시 */
    __be32   address;    /* 데이터 블록 내 오프셋 */
} xfs_dir2_leaf_entry_t;

저널링 (Log)

XFS는 Write-Ahead Logging(WAL) 기반의 메타데이터 전용 저널링을 사용합니다. 모든 메타데이터 변경은 로그에 먼저 기록되고, 이후 실제 위치에 반영(checkpoint)됩니다.

Log 구조

XFS Log 구조 순환 로그 버퍼 (Circular Log) Log Record 0 | Log Record 1 | ... | Log Record N | (wrap around) Log Record 내부 Log Record Header Log Item (inode) Log Item (buf/EFI) Log Item (dquot) Commit Record (LSN) LSN(Log Sequence Number)으로 로그 진행 상태 추적

AIL (Active Item List)

AIL은 디스크에 반영되지 않은 로그 항목을 LSN 순서로 추적하는 자료구조입니다. Checkpoint 쓰레드가 AIL의 가장 오래된 항목부터 디스크에 반영하고, 해당 로그 공간을 재사용합니다.

/* fs/xfs/xfs_trans_ail.c — AIL 핵심 동작 */

/* 트랜잭션 커밋 시 로그 아이템을 AIL에 삽입 */
void xfs_trans_ail_insert(
    struct xfs_ail      *ailp,
    struct xfs_log_item *lip,
    xfs_lsn_t           lsn);

/* Checkpoint: AIL tail부터 디스크 반영 */
void xfs_ail_push_all(
    struct xfs_ail  *ailp);

/* 반영 완료 후 AIL에서 제거 → 로그 공간 해제 */
void xfs_trans_ail_delete(
    struct xfs_log_item *lip,
    int                 shutdown_type);

Intent Logging (EFI/EFD, RUI/RUD)

XFS는 Intent Logging으로 복잡한 다단계 연산의 원자성을 보장합니다. Intent 로그 아이템(예: EFI)이 먼저 기록되고, 완료 시 Done 아이템(예: EFD)이 기록됩니다. 복구 시 Done이 없는 Intent를 재실행합니다.

IntentDone용도
EFI (Extent Free Intent)EFDextent 해제 (truncate, rm)
RUI (Rmap Update Intent)RUDreverse mapping 업데이트
CUI (Refcount Update Intent)CUDrefcount 업데이트 (reflink)
BUI (Bmap Update Intent)BUDextent 매핑 업데이트

외부 로그 디바이스

XFS는 저널을 별도 디바이스에 배치할 수 있어, 데이터 I/O와 저널 I/O를 물리적으로 분리하여 성능을 향상시킬 수 있습니다:

# 외부 로그 디바이스를 사용하여 XFS 생성
$ mkfs.xfs -l logdev=/dev/sdb1,size=512m /dev/sda1

# 마운트 시 외부 로그 지정
$ mount -o logdev=/dev/sdb1 /dev/sda1 /mnt/data

고급 기능

Reflink은 두 파일이 동일한 물리 extent를 공유하고, 어느 한쪽이 수정되면 COW(Copy-on-Write)로 분기하는 기능입니다. cp --reflink은 메타데이터만 복사하므로 즉각 완료됩니다.

# instant copy — 실제 데이터 복사 없음
$ cp --reflink=always source.img dest.img

# Reflink 활성화 확인 (v5 포맷 기본 활성)
$ xfs_info /mnt/data | grep reflink
reflink=1
Refcount B+tree: AGF 내의 Refcount B+tree가 각 물리 extent의 참조 수를 추적합니다. 참조 수가 0이 되면 해당 extent가 해제되고, 참조 수가 2 이상이면 COW 시 새 블록을 할당합니다.
/* fs/xfs/xfs_reflink.c — COW fork 처리 */
int xfs_reflink_allocate_cow(
    struct xfs_inode    *ip,
    struct xfs_bmbt_irec *imap,
    bool                *shared,
    uint                *lockmode,
    bool                convert_now);

/* COW extent writeback 완료 후 원본 매핑 교체 */
int xfs_reflink_end_cow(
    struct xfs_inode    *ip,
    xfs_off_t           offset,
    xfs_off_t           count);

Online 확장 (xfs_growfs)

XFS는 마운트 상태에서 파일시스템을 확장할 수 있습니다. 새 AG를 추가하는 방식이므로 기존 데이터 재배치가 불필요합니다:

# LV 확장 후 XFS 온라인 확장
$ lvextend -L +100G /dev/vg0/data
$ xfs_growfs /mnt/data

# 특정 크기로 확장 (블록 단위)
$ xfs_growfs -D 524288000 /mnt/data
축소 불가: XFS는 온라인/오프라인 축소를 모두 지원하지 않습니다. 이는 AG 기반 설계의 한계로, 축소가 필요하면 백업 후 더 작은 볼륨에 재생성해야 합니다.

Project Quotas (디렉토리 기반)

XFS의 Project Quota는 디렉토리 트리 단위로 공간 제한을 적용합니다. uid/gid 기반 quota와 달리, 특정 디렉토리 계층에 용량 한도를 설정할 수 있어 컨테이너나 프로젝트별 공간 관리에 유용합니다:

# Project Quota 설정
$ echo "42:/data/project_a" >> /etc/projects
$ echo "project_a:42" >> /etc/projid

# 마운트 옵션에 pquota 추가
$ mount -o pquota /dev/sda1 /data

# 프로젝트 디렉토리 초기화 및 제한 설정
$ xfs_quota -x -c 'project -s project_a' /data
$ xfs_quota -x -c 'limit -p bhard=100g project_a' /data

Real-time Subvolume

XFS는 선택적으로 별도의 real-time subvolume을 구성할 수 있습니다. 이 영역은 extent 단위 할당으로 일반 AG 메커니즘을 우회하여, 예측 가능한 I/O 지연이 필요한 워크로드(멀티미디어, 실시간 데이터 수집)에 적합합니다:

# real-time subvolume 포함 mkfs
$ mkfs.xfs -r rtdev=/dev/sdb1,extsize=1m /dev/sda1

# 마운트 시 rtdev 지정
$ mount -o rtdev=/dev/sdb1 /dev/sda1 /mnt/rtdata

DAX (Direct Access)

Persistent Memory(pmem) 디바이스에서 XFS를 DAX 모드로 사용하면, 페이지 캐시를 우회하여 CPU가 메모리 매핑을 통해 직접 스토리지에 접근합니다:

# DAX 모드로 마운트 (전체 FS)
$ mount -o dax=always /dev/pmem0 /mnt/pmem

# 파일별 DAX 속성 설정 (커널 5.8+)
$ xfs_io -c 'chattr +x' /mnt/pmem/datafile

Atomic Writes (커널 6.13+)

커널 6.13에서 XFS에 atomic writes 지원이 추가되었습니다. 데이터베이스 등의 애플리케이션이 저널링 없이도 블록 단위의 원자적 쓰기를 보장받을 수 있습니다. RWF_ATOMIC 플래그를 사용한 Direct I/O 쓰기는 전체 기록되거나 전혀 기록되지 않음을 보장합니다.

/* atomic write — 블록 단위 all-or-nothing 보장 */
/* statx로 지원 여부 확인 */
struct statx stx;
statx(AT_FDCWD, path, 0, STATX_WRITE_ATOMIC, &stx);
/* stx.stx_atomic_write_unit_min/max 확인 */

/* pwritev2()에 RWF_ATOMIC 플래그 사용 */
pwritev2(fd, iov, iovcnt, offset, RWF_ATOMIC);
RT reflink & reverse-mapping (v6.14): 커널 6.14에서 XFS real-time 서브볼륨에 reflink 및 reverse-mapping B+tree 지원이 추가되었습니다. 이를 통해 RT 디바이스에서도 COW 스냅샷과 빠른 extent 역추적이 가능해졌습니다.
대형 atomic writes (v6.16): 커널 6.16에서 XFS의 atomic write 크기 제한이 확대되어, 단일 extent 범위를 넘어서는 대형 atomic write가 가능해졌습니다. 이는 데이터베이스의 대형 페이지(16KB~64KB) 원자적 기록 요구사항을 충족합니다.

CRC32C Self-describing Metadata (v5 포맷)

v5 on-disk 포맷(Linux 3.7+, 기본 활성)은 모든 메타데이터 블록에 다음을 추가합니다:

성능 튜닝

마운트 옵션

옵션기본값설명
logbufs=N8인메모리 로그 버퍼 수 (2-8). 높을수록 쓰기 버스트 흡수
logbsize=N32K/256K로그 버퍼 크기. 대형 트랜잭션 워크로드에서 증가
allocsize=N64K스트리밍 쓰기 시 사전 할당 단위 (최대 1G)
inode64v5 기본모든 AG에서 inode 할당 (32비트 앱 호환 주의)
largeiooffstat()의 st_blksize를 stripe width로 설정
nobarrierbarrier=1쓰기 배리어 비활성 (배터리 캐시 RAID만)
discardoffSSD TRIM 자동 발행
lazytimeoff타임스탬프 업데이트 지연

핵심 sysctl

# XFS 관련 sysctl 파라미터
fs.xfs.xfssyncd_centisecs = 3000   # 동기화 데몬 주기 (30초)
fs.xfs.filestream_centisecs = 3000 # filestream 할당 AG 유지 시간
fs.xfs.speculative_prealloc_lifetime = 300 # delalloc 사전 할당 유지 (초)
fs.xfs.error_level = 3             # 오류 보고 수준 (0-5)

xfs_fsr (단편화 해소)

# 전체 파일시스템 단편화 해소
$ xfs_fsr /mnt/data

# 특정 파일만 defrag
$ xfs_fsr /mnt/data/largefile.dat

# 단편화 상태 확인
$ xfs_db -r -c 'frag -f' /dev/sda1

I/O 패턴별 최적화

워크로드권장 설정
대용량 순차 쓰기 (미디어, 백업)allocsize=1g, logbsize=256k, extent size hint 설정
소형 랜덤 I/O (데이터베이스)inode64, noatime, logbufs=8, AG 수 조정
메타데이터 집중 (메일 서버)외부 로그 디바이스, logbsize=256k, lazytime
가상화 이미지 (VM)reflink=1, extent size hint, allocsize 증가
NVMe/SSDdiscard 또는 fstrim cron, inode64
mkfs 시 최적화: RAID 환경에서는 mkfs.xfs -d su=256k,sw=4로 stripe unit/width를 디스크 배열에 맞추면 데이터 정렬이 최적화됩니다. AG 크기도 -d agcount=N으로 CPU 코어 수에 맞출 수 있습니다.

관리 도구 (xfsprogs)

주요 도구 요약

도구용도
mkfs.xfsXFS 파일시스템 생성
xfs_info마운트된 FS의 지오메트리 정보 출력
xfs_adminUUID, 레이블 변경, lazy-count 전환
xfs_repair오프라인 파일시스템 복구
xfs_db디버그 모드 — 메타데이터 직접 검사/수정
xfs_logprint저널 로그 내용 덤프
xfs_metadump메타데이터 이미지 추출 (버그 리포트용)
xfs_growfs온라인 파일시스템 확장
xfs_fsr파일 단편화 해소 (defrag)
xfs_quota사용자/그룹/프로젝트 quota 관리
xfs_freeze / xfs_thawI/O 일시 중지/재개 (스냅샷용)
xfs_scrub온라인 메타데이터 검증 (v5 포맷)
xfs_io파일 I/O 디버깅 (extent 정보, fallocate, fiemap)

mkfs.xfs 주요 옵션

# 기본 생성 (v5 포맷, reflink 활성, CRC 활성)
$ mkfs.xfs /dev/sda1

# RAID 최적화 (stripe unit 256K, 4 data disks)
$ mkfs.xfs -d su=256k,sw=4 -l su=256k /dev/md0

# 외부 로그, 큰 로그 크기
$ mkfs.xfs -l logdev=/dev/sdb1,size=1g /dev/sda1

# inode 크기 변경, AG 수 지정
$ mkfs.xfs -i size=1024 -d agcount=32 /dev/sda1

# 블록 크기 변경 (1K, 2K, 4K 중 선택)
$ mkfs.xfs -b size=4096 /dev/sda1

디버깅 & 트러블슈팅

xfs_db 실전 활용

# 수퍼블록 검사
$ xfs_db -r /dev/sda1
xfs_db> sb 0
xfs_db> print
magicnum = 0x58465342
blocksize = 4096
dblocks = 524288000
agcount = 4
agblocks = 131072000
...

# 특정 inode 검사
xfs_db> inode 131
xfs_db> print
core.magic = 0x494e
core.mode = 0100644
core.version = 3
core.format = 2 (extents)
core.size = 1048576
...

# AG free space 통계
xfs_db> agf 0
xfs_db> print freeblks longest
freeblks = 95000000
longest = 32000000

# 단편화 통계
xfs_db> frag -f

xfs_repair 복구 절차

주의: xfs_repair는 반드시 언마운트 상태에서 실행해야 합니다. 마운트된 상태에서 실행하면 데이터 손상이 발생할 수 있습니다.
# 1단계: 드라이런 (변경 없이 검사만)
$ xfs_repair -n /dev/sda1

# 2단계: 실제 복구
$ umount /mnt/data
$ xfs_repair /dev/sda1

# 더티 로그가 있을 경우: 먼저 로그 클리어 후 복구
$ xfs_repair -L /dev/sda1
# -L은 로그를 제로화하므로 최근 트랜잭션 손실 가능

# 외부 로그 디바이스 사용 시
$ xfs_repair -l /dev/sdb1 /dev/sda1

커널 로그 메시지 해석

메시지 패턴의미대응
XFS: Corruption detected메타데이터 CRC 불일치xfs_repair 실행
XFS: Log force timed out로그 I/O 응답 없음스토리지 상태 점검
XFS: xfs_do_force_shutdown치명적 오류로 FS 중단dmesg 확인 후 xfs_repair
XFS: Filesystem has duplicate UUIDUUID 충돌 (클론 볼륨)xfs_admin -U generate
XFS: possible memory allocation deadlock메모리 압력 하 할당 실패메모리 부족 원인 해결

Tracepoints (xfs:*)

# 사용 가능한 XFS tracepoints 목록
$ perf list 'xfs:*' 2>&1 | head -20
xfs:xfs_alloc_exact_done
xfs:xfs_alloc_near_first
xfs:xfs_buf_read
xfs:xfs_ilock
xfs:xfs_iomap_found
xfs:xfs_reflink_bounce_dio_write
...

# extent 할당 추적
$ perf record -e 'xfs:xfs_alloc_*' -a -- sleep 10
$ perf script

# trace-cmd로 delalloc 모니터링
$ trace-cmd record -e 'xfs:xfs_iomap*' -e 'xfs:xfs_alloc*'
$ trace-cmd report

# bpftrace로 실시간 분석
$ bpftrace -e 'tracepoint:xfs:xfs_file_buffered_write { printf("%s %d bytes\n", comm, args->count); }'
실전 디버깅 팁: xfs_io -c 'fiemap -v' file로 파일의 extent 매핑을 확인하고, xfs_io -c 'stat' file로 inode 세부 정보를 조회할 수 있습니다. 성능 문제 진단 시 xfs_io -c 'freesp -s' mountpoint로 free space 단편화를 확인하세요.

AG(Allocation Group) 아키텍처 심화

XFS의 Allocation Group은 단순한 파티셔닝이 아니라 완전히 독립적인 파일시스템 단위입니다. 각 AG는 자체 수퍼블록 복사본, 프리 스페이스 인덱스, inode 할당기, 역방향 매핑 트리를 보유하며, 고유한 잠금 세트로 보호됩니다. 이 설계는 NUMA 아키텍처에서 메모리 지역성과 결합하여 대규모 병렬 I/O 성능을 극대화합니다.

AG 헤더 구조와 병렬 할당 흐름 AG 0 Superblock AGF AGI AGFL Free Space AGF 관리 B+tree BNO tree | CNT tree RMAP tree | Refcount tree AGI 관리 B+tree Inode B+tree Free Inode B+tree pag_ici_lock | AGF lock | AGI lock (독립 잠금 세트) 데이터/메타데이터 블록 영역 AG 1 Superblock AGF AGI AGFL Free Space AGF 관리 B+tree BNO tree | CNT tree RMAP tree | Refcount tree AGI 관리 B+tree Inode B+tree Free Inode B+tree pag_ici_lock | AGF lock | AGI lock (독립 잠금 세트) 데이터/메타데이터 블록 영역 병렬 할당 흐름 Thread A (CPU 0) xfs_alloc_ag_vextent() Thread B (CPU 3) AG 0 선택 AG 1 선택 AG 0 AGF lock 획득 경합 없음 AG 1 AGF lock 획득 경합 없음 BNO/CNT tree 탐색→할당 BNO/CNT tree 탐색→할당 서로 다른 AG에 접근 → 잠금 경합 제로 → 선형 스케일링 mkfs.xfs -d agcount=N 으로 코어 수에 맞춰 AG 수 조정 가능

AG 헤더 섹터 배치

각 AG의 처음 4개 섹터는 고정된 헤더 구조체를 담습니다. 이 배치는 모든 AG에서 동일하며, AG 0의 수퍼블록이 마스터 역할을 합니다:

섹터 오프셋구조체매직 넘버역할
0xfs_sbXFSB수퍼블록 (AG 0 = 마스터, 나머지 = 복사본)
1xfs_agfXAGF프리 스페이스 관리 (BNO/CNT/RMAP/Refcount B+tree 루트)
2xfs_agiXAGIInode 할당 관리 (Inode/Free Inode B+tree 루트)
3xfs_agflXAFLB+tree 메타데이터 블록 예약 풀 (순환 배열)

AG 선택 전략

XFS는 새 파일이나 inode를 할당할 때 라운드 로빈파일 스트림 두 가지 AG 선택 전략을 사용합니다:

/* AG 선택 정책 — fs/xfs/xfs_ialloc.c */

/* 기본: 라운드 로빈 — 부모 디렉토리의 AG부터 시작하여 순환 탐색 */
xfs_agnumber_t xfs_ialloc_ag_select(
    struct xfs_trans    *tp,
    xfs_ino_t           parent,
    umode_t             mode);

/* 파일 스트림: 관련 파일을 동일 AG에 배치하여 인접성 극대화 */
/* mount -o filestreams 로 활성화 */
int xfs_filestream_new_ag(
    struct xfs_bmalloca *ap,
    xfs_agnumber_t     *agp);

/* filestream AG 점유 타이머 — 기본 30초 유지 */
/* fs.xfs.filestream_centisecs = 3000 */
AG 수 결정 기준: mkfs.xfs는 기본적으로 볼륨 크기에 따라 AG 수를 자동 결정합니다. 일반적으로 AG 크기는 최소 16MB에서 최대 1TB 범위이며, CPU 코어 수보다 AG 수가 많아야 병렬 할당의 이점을 최대한 활용할 수 있습니다. -d agcount=64와 같이 명시적으로 지정할 수도 있습니다.

AG 잠금 순서 규약

데드락을 방지하기 위해 XFS는 엄격한 잠금 순서를 정의합니다. AG 내에서는 항상 AGF → AGI → AGFL 순서로 잠금을 획득해야 하며, 여러 AG에 동시 접근할 때는 낮은 AG 번호부터 잠금합니다:

/* 잠금 순서 규약 — fs/xfs/xfs_ag.h */
/*
 * AG 잠금 순서 (데드락 방지):
 *   1. AGF (free space 잠금)
 *   2. AGI (inode 잠금)
 *   3. AGFL (free list 잠금)
 *
 * 다중 AG 접근 시: agno 오름차순으로 잠금
 *   AG 0 → AG 1 → AG 2 (역순 금지)
 */

/* AGF 잠금 획득 */
int xfs_alloc_read_agf(
    struct xfs_perag   *pag,
    struct xfs_trans   *tp,
    int                 flags,
    struct xfs_buf    **agfbpp);

/* AGI 잠금 획득 — AGF 이후에만 호출 */
int xfs_ialloc_read_agi(
    struct xfs_perag   *pag,
    struct xfs_trans   *tp,
    int                 flags,
    struct xfs_buf    **agibpp);

B+tree 구조 심화

XFS의 모든 메타데이터 인덱싱은 일관된 generic B+tree 프레임워크로 구현됩니다. 이 프레임워크는 공통 삽입/삭제/탐색 로직을 제공하고, 각 B+tree 유형별 차이점은 xfs_btree_ops 콜백으로 추상화합니다. 이 섹션에서는 온디스크 노드 구조, 탐색 알고리즘, 분할/병합 메커니즘을 상세히 살펴봅니다.

XFS B+tree 내부 구조 (BNO tree 예시) Level 2 Level 1 Level 0 (Leaf) Root Node (xfs_btree_block) keys: [100, 500] | ptrs: [blk3, blk7, blk12] Internal Node (blk3) keys: [10, 50] | ptrs: [blk20, blk21, blk22] Internal Node (blk7) keys: [100, 300] | ptrs: [blk23, blk24, blk25] Internal Node (blk12) keys: [500, 800] | ptrs: [blk26, blk27, blk28] Leaf (blk20) [10,5] [20,3] [30,8] [45,2] Leaf (blk21) [50,12] [70,4] [85,6] [95,1] Leaf (blk23) [100,20] [150,8] [200,15] [280,3] Leaf (blk26) [500,32] [600,7] [700,11] [780,4] Leaf (blk27) [800,50] [900,6] [950,14] Leaf 레코드: [시작블록, 길이] — Free Extent 표현 Leaf 간 형제 포인터(sibling) → 순차 스캔 지원 v5 Self-Describing Metadata (모든 B+tree 블록에 적용) CRC32C | UUID | Owner (AG번호) | Block Number | LSN

B+tree 블록 헤더

모든 B+tree 블록은 공통 헤더를 가집니다. v5 포맷에서는 자기 검증 메타데이터가 추가됩니다:

/* fs/xfs/libxfs/xfs_btree.h — B+tree 블록 헤더 */
struct xfs_btree_block {
    __be32   bb_magic;          /* B+tree 유형별 매직 넘버 */
    __be16   bb_level;          /* 트리 레벨 (0=리프) */
    __be16   bb_numrecs;        /* 이 블록의 레코드/키 수 */
    union {
        struct {  /* short-format (AG 내부) */
            __be32 bb_leftsib;  /* 왼쪽 형제 AG 블록 */
            __be32 bb_rightsib; /* 오른쪽 형제 AG 블록 */
            /* v5 추가 */
            __be64 bb_blkno;   /* 자기 참조 블록 번호 */
            __be64 bb_lsn;     /* 마지막 수정 LSN */
            uuid_t bb_uuid;    /* FS UUID */
            __be32 bb_crc;     /* CRC32C */
            __be32 bb_owner;   /* AG 번호 */
        } s;
        struct {  /* long-format (전체 FS) */
            __be64 bb_leftsib;
            __be64 bb_rightsib;
            __be64 bb_blkno;
            __be64 bb_lsn;
            uuid_t bb_uuid;
            __be32 bb_crc;
            __be64 bb_owner;   /* inode 번호 */
        } l;
    } bb_u;
};

xfs_btree_ops 콜백 프레임워크

각 B+tree 유형은 xfs_btree_ops 구조체로 유형별 동작을 정의합니다. 이 추상화 덕분에 삽입, 삭제, 탐색 등의 공통 알고리즘을 한 번만 구현하면 됩니다:

/* fs/xfs/libxfs/xfs_btree.h — B+tree 연산 콜백 */
struct xfs_btree_ops {
    /* 키 비교 */
    int  (*key_diff)(struct xfs_btree_cur *cur,
                     const union xfs_btree_key *key);

    /* 레코드 읽기/쓰기 */
    int  (*get_rec)(struct xfs_btree_cur *cur,
                    union xfs_btree_rec **recp);

    /* 블록 할당/해제 (AGFL과 상호작용) */
    int  (*alloc_block)(struct xfs_btree_cur *cur,
                       const union xfs_btree_ptr *start,
                       union xfs_btree_ptr *new,
                       int *stat);
    int  (*free_block)(struct xfs_btree_cur *cur,
                      struct xfs_buf *bp);

    /* 키 업데이트 (내부 노드) */
    void (*init_key_from_rec)(union xfs_btree_key *key,
                             const union xfs_btree_rec *rec);
};

B+tree 분할/병합

B+tree 노드가 가득 차면 분할(split)이 발생합니다. 이때 AGFL에서 새 블록을 가져와 사용합니다. 반대로 노드의 점유율이 낮아지면 병합(merge)이 발생하고 빈 블록은 AGFL에 반납됩니다:

/* B+tree 분할 — fs/xfs/libxfs/xfs_btree.c */
int xfs_btree_split(
    struct xfs_btree_cur  *cur,
    int                   level,    /* 분할할 레벨 */
    union xfs_btree_ptr  *ptrp,    /* 새 블록 포인터 반환 */
    union xfs_btree_key  *key,     /* 분할 키 반환 */
    struct xfs_btree_cur **curp,   /* 새 커서 반환 */
    int                  *stat);

/* 핵심 흐름:
 *  1. bc_ops->alloc_block()으로 AGFL에서 새 블록 확보
 *  2. 기존 블록의 상위 절반을 새 블록으로 이동
 *  3. 형제 포인터(sibling) 업데이트
 *  4. 상위 노드에 새 키/포인터 삽입 (재귀적 분할 가능)
 */

지연 할당(Delayed Allocation) 심화

XFS의 지연 할당은 write() 시스템 콜 시점에서 디스크 블록을 즉시 할당하지 않고, 페이지 캐시에 데이터를 축적한 뒤 writeback 시점에 한꺼번에 최적의 연속 extent를 할당하는 전략입니다. 이 메커니즘은 XFS가 대용량 순차 쓰기에서 탁월한 성능을 내는 핵심 기술입니다.

지연 할당 흐름: write() → writeback → 디스크 기록 Phase 1: write() 시점 xfs_file_buffered_write() xfs_bmapi_reserve_delalloc() DELAYSTARTBLOCK 마킹 (블록 미할당) Phase 2: 페이지 캐시 축적 Dirty Pages 축적 delalloc 예약 카운터만 증가 실제 I/O 없음 → 오버헤드 최소 Phase 3: Writeback 할당 xfs_map_blocks() xfs_bmapi_convert_delalloc() 최종 크기 알고 연속 extent 할당 즉시 할당 vs 지연 할당 비교 즉시 할당 (ext3 방식) 4K 4K 4K 4K 4K 매 write()마다 블록 할당 → 5회 B+tree 수정 불연속 할당 가능 → 단편화 발생 각 블록이 분산 배치될 위험 지연 할당 (XFS 방식) 20K 연속 Extent (단일 할당) writeback 시 1회 B+tree 수정 → 오버헤드 최소 연속 할당 보장 → 순차 읽기 성능 최적 임시 파일은 블록 할당 없이 삭제 가능 Delalloc 예약 카운터 메커니즘 m_fdblocks (free data blocks) 전체 가용 블록 카운터 m_resblks (reserved blocks) 시스템 예약 블록 delalloc reserved 지연 할당 예약분 delalloc 예약 시 m_fdblocks 차감 → ENOSPC 사전 방지 실제 할당 시 예약분 해제 + 실제 할당분 차감 (이중 계산 방지)

ENOSPC 방지와 Speculative Preallocation

지연 할당의 핵심 난제는 ENOSPC(디스크 부족) 상황 처리입니다. write() 시점에 블록을 할당하지 않으므로, writeback 시점에 공간이 부족할 수 있습니다. XFS는 이를 예약 카운터로 해결합니다:

/* Speculative preallocation — fs/xfs/xfs_iomap.c */

/* write 시 실제 크기보다 넉넉하게 예약 (EOF 너머 추가 공간) */
xfs_extlen_t xfs_iomap_prealloc_size(
    struct xfs_inode    *ip,
    int                 whichfork,
    loff_t              offset,
    loff_t              count,
    struct xfs_bmbt_irec *prev);

/* speculative prealloc 크기 결정:
 *  - 파일 크기의 2배 (최대 64MB까지 지수 성장)
 *  - extent size hint가 설정되면 해당 값 사용
 *  - 여유 공간 부족 시 축소 할당
 */

/* 유휴 파일의 speculative prealloc 해제 */
/* fs.xfs.speculative_prealloc_lifetime = 300 (초) */
void xfs_inode_free_eofblocks(
    struct xfs_inode    *ip);
ENOSPC 복구 절차: writeback 시 공간 부족이 감지되면, XFS는 먼저 다른 파일의 speculative preallocation을 회수(xfs_blockgc_scan())하고, 그래도 부족하면 현재 쓰기를 가능한 범위까지만 할당합니다. 최악의 경우 쓰기가 부분적으로 실패할 수 있지만, 메타데이터 일관성은 항상 보장됩니다.

Delalloc 변환 경로

/* Writeback 시 delalloc → 실제 블록 변환 경로 */

/* 1. writeback 데몬이 dirty page flush 시작 */
xfs_vm_writepages()
  → iomap_writepages()                /* iomap 프레임워크 */xfs_map_blocks()               /* XFS iomap 콜백 */xfs_bmapi_convert_delalloc()  /* 핵심: delalloc→실제 할당 */

/* 2. xfs_bmapi_convert_delalloc() 내부 */
int xfs_bmapi_convert_delalloc(
    struct xfs_inode    *ip,
    int                 whichfork,
    loff_t              offset,
    struct iomap        *iomap,
    unsigned int        *seq);
/*  a. DELAYSTARTBLOCK extent를 찾음
 *  b. AG를 선택하고 BNO/CNT tree에서 최적 free extent 탐색
 *  c. 실제 블록 번호로 extent 레코드 업데이트
 *  d. 예약 카운터 정산 (예약 해제 + 실제 할당 반영)
 *  e. 트랜잭션 로그에 변경 기록
 */

XFS 저널링 심화

XFS의 저널(로그)은 단순한 순환 버퍼가 아니라, CIL(Committed Item List)AIL(Active Item List)이라는 두 단계 파이프라인을 통해 높은 동시성과 빠른 커밋 지연시간을 동시에 달성하는 정교한 구조입니다. 이 섹션에서는 트랜잭션 생명주기, CIL 집계, AIL 체크포인팅, 복구 절차를 상세히 분석합니다.

XFS 저널링 파이프라인: 트랜잭션 → CIL → 로그 → AIL → 디스크 트랜잭션 생성 xfs_trans_alloc() 메타데이터 수정 + 로깅 CIL 집계 xfs_trans_commit() CIL 버퍼에 아이템 축적 로그 기록 xlog_cil_push_work() CIL→로그 디스크 기록 AIL 체크포인트 xfsaild 데몬 실제 위치에 메타데이터 반영 CIL (Committed Item List) 상세 CIL 컨텍스트 (현재 시퀀스) txn A: inode 수정 txn B: extent 할당 txn C: dir 엔트리 txn D: AGF 업데이트 txn E: quota 수정 여러 트랜잭션을 하나의 CIL 컨텍스트에 집계 → 로그 I/O 배치 최적화 순환 로그 디스크 구조 Checkpoint 1 LSN 0x1000 Checkpoint 2 LSN 0x2000 Checkpoint 3 LSN 0x3000 현재 기록 중 Log Head (빈 공간) AIL tail 재사용 대기 크래시 복구 흐름 크래시 발생 로그 스캔 트랜잭션 재생 Intent 재실행 마운트 완료 로그 tail → head 순서대로 스캔, committed 트랜잭션만 재생, 미완료 Intent 재실행 복구 시간: 로그 크기에 비례 (데이터 양과 무관) — 수 초 내 완료

CIL(Committed Item List) 동작

CIL은 XFS 로그 성능의 핵심입니다. 여러 트랜잭션의 로그 아이템을 메모리에 집계(aggregate)하여 디스크 로그에 일괄 기록합니다. 이를 통해 개별 트랜잭션마다 디스크 I/O를 발생시키지 않고 배치 효과를 극대화합니다:

/* CIL 구조 — fs/xfs/xfs_log_priv.h */
struct xlog_cil {
    struct xlog         *xc_log;
    struct list_head    xc_cil;        /* 현재 CIL 아이템 리스트 */
    struct xlog_cil_ctx *xc_ctx;       /* 현재 CIL 컨텍스트 */
    xfs_csn_t           xc_current_sequence; /* CIL 시퀀스 번호 */
    wait_queue_head_t   xc_push_wait; /* push 완료 대기 큐 */
    atomic_t            xc_space_used; /* 현재 CIL 사용 공간 */
};

/* 트랜잭션 커밋 → CIL에 아이템 삽입 */
void xlog_cil_insert_items(
    struct xlog          *log,
    struct xfs_trans     *tp);

/* CIL push: 집계된 아이템을 로그 디스크에 기록 */
void xlog_cil_push_work(
    struct work_struct   *work);

LSN(Log Sequence Number) 체계

LSN은 64비트 값으로, 상위 32비트는 사이클 번호(순환 로그의 랩 횟수), 하위 32비트는 블록 오프셋(로그 내 위치)입니다. 모든 메타데이터 블록에 LSN이 기록되어, 해당 블록이 마지막으로 수정된 로그 시점을 정확히 추적합니다:

/* LSN 구조 — fs/xfs/xfs_log_format.h */
typedef __be64 xfs_lsn_t;

/* LSN = (cycle << 32) | block_offset */
#define CYCLE_LSN(lsn)  ((uint)((lsn) >> 32))
#define BLOCK_LSN(lsn)  ((uint)(lsn))

/* LSN 비교: a > b 인지 확인 (사이클 랩어라운드 고려) */
static inline bool xlog_lsn_is_later(
    xfs_lsn_t  a,
    xfs_lsn_t  b)
{
    return CYCLE_LSN(a) > CYCLE_LSN(b) ||
           (CYCLE_LSN(a) == CYCLE_LSN(b) &&
            BLOCK_LSN(a) > BLOCK_LSN(b));
}

로그 복구 절차 상세

비정상 종료 후 마운트 시, XFS는 다음 단계를 거쳐 메타데이터를 복구합니다:

단계함수동작
1. 로그 스캔xlog_find_head()로그 head/tail 위치 파악 (사이클 넘버 비교)
2. 레코드 수집xlog_do_recovery_pass()tail→head 순서로 커밋된 트랜잭션 수집
3. 메타데이터 재생xlog_recover_items_pass2()수집된 아이템을 실제 디스크 위치에 기록
4. Intent 재실행xlog_recover_finish()미완료 EFI/RUI/CUI/BUI의 작업 재실행
5. 정리xfs_log_mount_finish()복구 완료, 정상 운영 전환
로그 크기와 복구 시간: XFS 복구 시간은 파일시스템 크기가 아니라 로그 크기에 비례합니다. 기본 로그 크기는 볼륨의 약 0.1%이며, 최대 2GB까지 설정 가능합니다. 대부분의 경우 복구는 수 초 이내에 완료됩니다.

XFS의 reflink는 물리 extent를 여러 파일이 공유하면서 COW 의미론을 통해 독립적 수정을 보장하는 기능입니다. 가상 머신 이미지의 빠른 복제, 파일 레벨 스냅샷, 데이터 중복 제거(dedup) 등에 활용됩니다. 커널 4.9에서 도입되어 v5 포맷에서 기본 활성화됩니다.

Reflink/COW 동작 흐름 Step 1: cp --reflink=always src dst File A (원본) extent 0 extent 1 extent 2 File B (reflink 복사본) extent 0 extent 1 extent 2 물리 Extent (공유) — Refcount = 2 메타데이터만 복사 (Refcount B+tree 업데이트) → 즉각 완료, 디스크 공간 추가 사용 없음 Step 2: File B의 extent 1에 쓰기 → COW 분기 File A (변경 없음) extent 0 extent 1 extent 2 File B (extent 1 수정됨) extent 0 COW extent extent 2 원본 extent 1 (refcount=1) 새 extent (COW 결과) Refcount B+tree: extent별 참조 수 추적

COW Fork 메커니즘

XFS는 COW를 위해 inode에 COW fork라는 세 번째 fork를 도입했습니다. 공유 extent에 쓰기가 발생하면, 새 블록을 COW fork에 먼저 할당하고 데이터를 복사한 뒤, writeback 완료 시 data fork의 매핑을 교체합니다:

/* COW fork 처리 — fs/xfs/xfs_reflink.c */

/* 1. 공유 여부 확인 → COW fork에 extent 예약 */
int xfs_reflink_allocate_cow(
    struct xfs_inode    *ip,
    struct xfs_bmbt_irec *imap,     /* 대상 extent 정보 */
    bool                *shared,   /* 공유 여부 반환 */
    uint                *lockmode,
    bool                convert_now);
/*  - Refcount B+tree 조회로 공유 여부 판별
 *  - 공유 시 COW fork에 새 extent 할당 (unwritten 상태)
 */

/* 2. writeback 완료 → data fork 매핑 교체 */
int xfs_reflink_end_cow(
    struct xfs_inode    *ip,
    xfs_off_t           offset,
    xfs_off_t           count);
/*  - COW fork의 extent를 data fork로 이동
 *  - 원본 extent의 refcount 감소 (CUI intent 로그)
 *  - refcount 0이면 원본 extent 해제 (EFI intent 로그)
 */

/* 3. Refcount B+tree 업데이트 */
int xfs_refcount_adjust(
    struct xfs_btree_cur *cur,
    xfs_agblock_t       agbno,
    xfs_extlen_t        aglen,
    int                 adj);     /* +1 (공유) 또는 -1 (해제) */

Deduplication (중복 제거)

XFS의 reflink 인프라를 활용한 데이터 중복 제거가 가능합니다. xfs_io -c 'dedupe' 또는 FIDEDUPERANGE ioctl로 동일한 내용의 블록을 공유 extent로 병합합니다:

# 두 파일의 동일 범위를 deduplicate
$ xfs_io -c 'dedupe /path/to/src 0 0 4m' /path/to/dst

# duperemove: 파일시스템 전체 중복 블록 탐색 및 제거
$ duperemove -dhr /data/
# -d: dedupe 실행, -h: 해시 기반 비교, -r: 재귀

# 공유 extent 확인
$ xfs_io -c 'fiemap -v' /path/to/file
# flags 필드에 'shared' 표시 확인
COW 성능 고려사항: Reflink된 파일에 대한 무작위 소규모 쓰기는 COW 오버헤드(새 블록 할당 + 데이터 복사 + refcount 업데이트)를 유발합니다. 대용량 순차 쓰기나 전체 파일 재작성에서는 COW 오버헤드가 무시할 만하지만, 데이터베이스와 같은 랜덤 I/O 워크로드에서는 reflink 사용을 신중히 검토해야 합니다.

xfs_scrub 온라인 검증

xfs_scrub은 마운트된 상태에서 XFS 메타데이터를 검증하고 경미한 손상을 자동 복구하는 도구입니다. 오프라인 xfs_repair와 달리 서비스 중단 없이 운영할 수 있어 엔터프라이즈 환경에서 특히 유용합니다. v5 포맷의 self-describing metadata(CRC, UUID, owner)를 활용하여 교차 검증을 수행합니다.

xfs_scrub 아키텍처: 온라인 메타데이터 검증 사용자 공간 (xfs_scrub 데몬) phase1: 수퍼블록 검증 phase2: AG 메타데이터 phase3: inode 스캔 phase4: 디렉토리 phase5~7: 복구 XFS_IOC_SCRUB_METADATA ioctl 커널 공간 (xfs_scrub 커널 모듈) AG 검사기 AGF/AGI/AGFL 검증 BNO/CNT/RMAP tree Inode 검사기 di_core 필드 검증 fork 구조 일관성 B+tree 검사기 키 순서 검증 교차 참조 확인 Quota 검사기 dquot 일관성 Refcount 검사기 공유 extent 정합 교차 참조 엔진 RMAP ↔ BNO ↔ Refcount 대조 손상 감지 corrupt / preen / warn 온라인 복구 (repair) B+tree 재구축 / 메타 수정 RMAP B+tree가 핵심: 물리 블록 → 소유자 역추적으로 교차 검증 가능 검증 결과 상태 CLEAN: 이상 없음 PREEN: 자동 수정 가능 CORRUPT: 수동 복구 필요 XCORRUPT: 교차 참조 불일치

xfs_scrub 실전 활용

# 전체 파일시스템 온라인 검증 (읽기 전용)
$ xfs_scrub /mnt/data

# 백그라운드 주기적 검증 (systemd 타이머 권장)
$ xfs_scrub -b /mnt/data

# 수정 가능한 문제 자동 복구 (-n: 드라이런)
$ xfs_scrub -n /mnt/data    # 먼저 확인
$ xfs_scrub -y /mnt/data    # 자동 복구 승인

# 특정 AG만 검증
$ xfs_io -c 'scrub agf 3' /mnt/data    # AG 3의 AGF 검증
$ xfs_io -c 'scrub bnobt 0' /mnt/data  # AG 0의 BNO B+tree 검증

# systemd 서비스로 주기적 실행
$ systemctl enable --now xfs_scrub_all.timer
# /etc/systemd/system/xfs_scrub_all.timer로 주기 조정

xfs_scrub vs xfs_repair 비교

특성xfs_scrubxfs_repair
실행 조건마운트 상태 (온라인)언마운트 상태 (오프라인)
포맷 요구v5 포맷 필수 (CRC/RMAP)모든 포맷
검증 방식교차 참조 (RMAP 기반)전체 재스캔
복구 범위경미한 메타데이터 손상심각한 손상 포함 전체
성능 영향백그라운드, I/O 우선도 낮춤서비스 중단 필요
사용 시나리오예방적 정기 검증크래시 후 복구, 심각한 손상
RMAP과 교차 검증: xfs_scrub의 핵심은 Reverse Mapping B+tree(RMAP)입니다. RMAP은 물리 블록에서 소유자(inode, AG 구조체 등)를 역추적할 수 있으므로, BNO tree의 free 블록이 실제로 어떤 inode에도 할당되지 않았는지, refcount tree의 값이 실제 참조 수와 일치하는지를 교차 검증할 수 있습니다.

프로젝트 쿼타 심화

XFS는 사용자(uid), 그룹(gid), 프로젝트(projid) 세 가지 차원의 쿼타를 지원합니다. 특히 프로젝트 쿼타는 디렉토리 트리 단위로 공간 제한을 적용할 수 있어, 컨테이너, 가상 호스팅, 부서별 공간 관리에 매우 유용합니다.

XFS 프로젝트 쿼타: 디렉토리 트리 기반 공간 제한 XFS 파일시스템 (/data) — mount -o pquota 프로젝트 A (projid=42) bhard=100G, ihard=1M /data/project_a/ src/ (30G) data/ (45G) logs/ (10G) tmp/ (5G) 합계: 90G / 100G (90% 사용) 90% 프로젝트 B (projid=43) bhard=200G, ihard=2M /data/project_b/ app/ (50G) db/ (80G) cache/ (20G) 합계: 150G / 200G (75% 사용) 75% 비프로젝트 영역 (projid=0) 제한 없음 (기본) /data/shared/, /data/misc/ XFS Disk Quota (dquot) — 온디스크 쿼타 레코드 d_blk_hardlimit (블록 하드) d_blk_softlimit (블록 소프트) d_bcount (현재 사용) d_ino_hardlimit d_icount 각 프로젝트 ID별 dquot 레코드가 inode의 특수 quota 파일에 저장됨 소프트 제한 초과 → 유예 기간 내 정리 필요, 하드 제한 → 즉시 EDQUOT 반환

프로젝트 쿼타 설정 절차

# 1. /etc/projects에 프로젝트 ID ↔ 경로 매핑
$ echo "42:/data/project_a" >> /etc/projects
$ echo "43:/data/project_b" >> /etc/projects

# 2. /etc/projid에 프로젝트 이름 ↔ ID 매핑
$ echo "project_a:42" >> /etc/projid
$ echo "project_b:43" >> /etc/projid

# 3. pquota 옵션으로 마운트
$ mount -o pquota /dev/sda1 /data

# 4. 프로젝트 디렉토리 초기화 (재귀적으로 projid 설정)
$ xfs_quota -x -c 'project -s project_a' /data
$ xfs_quota -x -c 'project -s project_b' /data

# 5. 제한 설정
$ xfs_quota -x -c 'limit -p bhard=100g bsoft=90g ihard=1000000 project_a' /data
$ xfs_quota -x -c 'limit -p bhard=200g bsoft=180g ihard=2000000 project_b' /data

# 6. 사용량 확인
$ xfs_quota -x -c 'report -p -h' /data
Project quota on /data (/dev/sda1)
                        Blocks              Inodes
Project ID    Used   Soft   Hard    Used   Soft   Hard
---------- ------ ------ ------  ------ ------ ------
project_a    90G    90G   100G   450000      0  1000000
project_b   150G   180G   200G   800000      0  2000000

쿼타 커널 내부 구조

/* 쿼타 레코드 — fs/xfs/libxfs/xfs_format.h */
typedef struct xfs_disk_dquot {
    __be16   d_magic;          /* 0x4451 ('DQ') */
    __u8     d_type;           /* USER/GROUP/PROJ */
    __be32   d_id;             /* uid/gid/projid */
    __be64   d_blk_hardlimit;  /* 블록 하드 제한 */
    __be64   d_blk_softlimit;  /* 블록 소프트 제한 */
    __be64   d_bcount;         /* 현재 사용 블록 */
    __be64   d_ino_hardlimit;  /* inode 하드 제한 */
    __be64   d_ino_softlimit;  /* inode 소프트 제한 */
    __be64   d_icount;         /* 현재 사용 inode */
    __be32   d_btimer;         /* 블록 유예 만료 시각 */
    __be32   d_itimer;         /* inode 유예 만료 시각 */
    ...
} xfs_disk_dquot_t;

/* 쿼타 검사: 블록 할당 시 호출 */
int xfs_trans_dqresv(
    struct xfs_trans   *tp,
    struct xfs_mount   *mp,
    struct xfs_dquot   *dqp,
    long                nblks,    /* 할당 요청 블록 수 */
    long                ninos,    /* 할당 요청 inode 수 */
    uint                flags);
/* 제한 초과 시 -EDQUOT 반환 */

커널 내부 핵심 구조체

XFS 커널 드라이버의 핵심은 xfs_mount(파일시스템 전역 상태), xfs_inode(인메모리 inode 표현), xfs_buf(메타데이터 캐시 버퍼), xfs_trans(트랜잭션) 네 가지 구조체입니다. 이들의 상호작용을 이해하면 XFS의 내부 동작을 완전히 파악할 수 있습니다.

XFS 커널 내부 핵심 구조체 관계 xfs_mount 파일시스템 전역 상태 m_sb (수퍼블록) m_perag[] (AG별 상태) m_log (로그) m_fdblocks m_quotainfo xfs_perag AG별 인메모리 상태 pag_agno, pag_ref pag_ici_lock, pag_agf_bp xfs_inode 인메모리 inode 표현 i_ino (inode 번호) i_mount (*xfs_mount) i_df (data fork) i_af (attr fork) xfs_buf 메타데이터 캐시 버퍼 b_bn (블록 번호) b_length (크기) b_addr (데이터 포인터) b_ops (검증 콜백) xfs_trans 트랜잭션 (원자적 메타데이터 변경 단위) t_mountp (*xfs_mount) t_items (로그 아이템 리스트) t_blk_res (블록 예약) t_log_res (로그 예약) t_ticket (*xlog_ticket) t_flags m_perag[] inode cache buf cache 트랜잭션에 잠금 buf 로그 아이템 AG 잠금 CIL → 로그 디스크 트랜잭션 커밋 시 삽입 AIL → 실제 위치 반영 xfsaild 데몬이 처리 xfs_trans → commit → CIL 집계 → 로그 기록 → AIL 체크포인트 → 실제 디스크 반영

xfs_mount 상세

/* fs/xfs/xfs_mount.h — 파일시스템 마운트 구조체 */
typedef struct xfs_mount {
    struct super_block  *m_super;       /* VFS 수퍼블록 */
    struct xfs_sb        m_sb;          /* 인메모리 수퍼블록 복사 */
    struct xlog         *m_log;         /* 로그 구조체 */
    struct xfs_ail       m_ail;         /* AIL */
    struct xfs_perag   **m_perag;       /* AG별 상태 배열 */
    struct xfs_quotainfo *m_quotainfo;  /* 쿼타 정보 */

    percpu_counter_t    m_fdblocks;    /* free 데이터 블록 (per-CPU) */
    percpu_counter_t    m_ifree;       /* free inode (per-CPU) */
    percpu_counter_t    m_icount;      /* 총 inode (per-CPU) */

    uint64_t            m_resblks;     /* 시스템 예약 블록 */
    uint                m_alloc_mxr[2]; /* BNO/CNT max recs/block */
    uint                m_bmap_dmxr[2]; /* BMAP max recs/block */
    ...
} xfs_mount_t;

/* per-CPU 카운터: 멀티코어 환경에서 락 없이 블록 카운팅
 *  - 각 CPU가 로컬 카운터를 유지
 *  - 정확한 값이 필요할 때만 모든 CPU 합산
 *  - statvfs() 등에서 빠른 응답 가능
 */

xfs_inode 상세

/* fs/xfs/xfs_inode.h — 인메모리 inode */
typedef struct xfs_inode {
    struct inode        vfs_inode;     /* VFS inode (내장) */
    struct xfs_mount   *i_mount;      /* 마운트 포인터 */
    xfs_ino_t           i_ino;        /* inode 번호 */

    /* Data fork / Attr fork */
    struct xfs_ifork    i_df;         /* 데이터 fork */
    struct xfs_ifork   *i_af;         /* 속성 fork (없으면 NULL) */
    struct xfs_ifork   *i_cowfp;      /* COW fork (reflink 용) */

    /* 잠금 */
    struct rw_semaphore i_lock;       /* inode 잠금 */
    mrlock_t            i_mmaplock;   /* mmap 잠금 */

    /* 쿼타 */
    struct xfs_dquot   *i_udquot;     /* 사용자 쿼타 */
    struct xfs_dquot   *i_gdquot;     /* 그룹 쿼타 */
    struct xfs_dquot   *i_pdquot;     /* 프로젝트 쿼타 */

    uint64_t            i_diflags2;   /* 확장 플래그 */
    ...
} xfs_inode_t;

xfs_buf 버퍼 캐시

xfs_buf는 XFS의 독자적인 메타데이터 버퍼 캐시입니다. Linux의 기본 buffer_head/bio와 별도로, XFS는 자체 캐시를 유지하여 B+tree 노드, AGF/AGI 헤더 등의 메타데이터를 효율적으로 관리합니다:

/* fs/xfs/xfs_buf.h — 메타데이터 버퍼 */
struct xfs_buf {
    struct list_head     b_lru;       /* LRU 리스트 */
    xfs_daddr_t          b_bn;       /* 디스크 블록 번호 */
    int                  b_length;   /* 버퍼 크기 (섹터 단위) */
    void                *b_addr;     /* 데이터 포인터 */
    struct xfs_mount    *b_mount;    /* 마운트 포인터 */
    atomic_t             b_hold;     /* 참조 카운트 */
    xfs_buf_flags_t      b_flags;    /* 상태 플래그 */
    const struct xfs_buf_ops *b_ops; /* 검증 콜백 */
    struct completion    b_iowait;   /* I/O 완료 대기 */
    struct xfs_log_item *b_log_item; /* 로그 아이템 */
};

/* b_ops: 읽기 시 CRC/UUID/magic 검증, 쓰기 시 CRC 재계산 */
struct xfs_buf_ops {
    const char  *name;
    void  (*verify_read)(struct xfs_buf *bp);
    void  (*verify_write)(struct xfs_buf *bp);
};

ftrace/bpftrace XFS 성능 분석

XFS는 커널에 400개 이상의 tracepoint를 제공하여, 할당, 잠금, 로그, I/O 경로를 상세히 추적할 수 있습니다. ftrace, perf, bpftrace를 사용하여 실시간 성능 분석과 병목 진단이 가능합니다.

XFS 성능 분석: Tracepoint 기반 진단 체계 분석 도구 perf trace-cmd bpftrace ftrace BCC (bcc-tools) SystemTap XFS Tracepoints (400+ 종류) 할당 추적 xfs:xfs_alloc_* exact_done, near_first I/O 추적 xfs:xfs_file_* buffered_write, read 버퍼 추적 xfs:xfs_buf_* read, write, iowait 잠금 추적 xfs:xfs_ilock* ilock, iunlock 로그 추적 xfs:xfs_log_* force, grant iomap 추적 xfs:xfs_iomap_* reflink 추적 xfs:xfs_reflink_* AG 추적 xfs:xfs_perag_* 주요 진단 시나리오 할당 지연 AG 잠금 경합 분석 로그 I/O 병목 log force/grant 추적 delalloc 분석 iomap/writeback 추적 COW 오버헤드 reflink bounce 추적 tracepoint → perf/bpftrace → 히스토그램/지연시간 분포 → 병목 식별 → 튜닝

ftrace/perf 실전 레시피

# === 할당 경로 추적 ===

# AG별 할당 요청 분포 확인
$ perf record -e 'xfs:xfs_alloc_exact_done' \
              -e 'xfs:xfs_alloc_near_first' \
              -e 'xfs:xfs_alloc_near_greater' -a -- sleep 30
$ perf script | awk '/agno=/{print $0}' | sort | uniq -c

# === 로그 I/O 병목 진단 ===

# log force 빈도 — 높으면 로그 크기 부족 또는 sync 과다
$ trace-cmd record -e 'xfs:xfs_log_force' -e 'xfs:xfs_log_grant_sleep' -- sleep 60
$ trace-cmd report | grep 'log_grant_sleep'
# log_grant_sleep 빈번 → logbsize/logbufs 증가 검토

# === 버퍼 I/O 대기 시간 ===

# xfs_buf read/write 지연시간 측정
$ trace-cmd record -e 'xfs:xfs_buf_read' \
                   -e 'xfs:xfs_buf_iowait_done' -- sleep 30
$ trace-cmd report

# === inode 잠금 경합 ===

# 어떤 파일에서 ilock 경합이 발생하는지 확인
$ perf record -e 'xfs:xfs_ilock' -e 'xfs:xfs_ilock_demote' \
              -e 'xfs:xfs_iunlock' -a -- sleep 30
$ perf script | grep 'ilock' | awk '{print $NF}' | sort | uniq -c | sort -rn

bpftrace 실전 레시피

# === 파일별 쓰기 크기 히스토그램 ===
$ bpftrace -e '
tracepoint:xfs:xfs_file_buffered_write {
    @write_size = hist(args->count);
    @by_comm[comm] = sum(args->count);
}
END { print(@write_size); print(@by_comm); }
'

# === delalloc 변환 지연시간 측정 ===
$ bpftrace -e '
kprobe:xfs_bmapi_convert_delalloc {
    @start[tid] = nsecs;
}
kretprobe:xfs_bmapi_convert_delalloc /@start[tid]/ {
    @convert_us = hist((nsecs - @start[tid]) / 1000);
    delete(@start[tid]);
}
'

# === AG별 할당 횟수 실시간 모니터링 ===
$ bpftrace -e '
tracepoint:xfs:xfs_alloc_exact_done,
tracepoint:xfs:xfs_alloc_near_first {
    @ag_allocs[args->agno] = count();
}
interval:s:5 { print(@ag_allocs); clear(@ag_allocs); }
'

# === COW extent 할당 추적 ===
$ bpftrace -e '
tracepoint:xfs:xfs_reflink_allocate_cow {
    printf("%s pid=%d ino=%lu off=%lu len=%lu\n",
           comm, pid, args->ino, args->offset, args->len);
}
'

# === xfs_buf I/O 지연시간 분포 ===
$ bpftrace -e '
kprobe:xfs_buf_read_map { @start[arg0] = nsecs; }
kretprobe:xfs_buf_read_map /@start[arg0]/ {
    @buf_read_us = hist((nsecs - @start[arg0]) / 1000);
    delete(@start[arg0]);
}
'
운영 환경 주의사항: tracepoint 추적은 일정한 오버헤드를 유발합니다. 프로덕션 환경에서는 필요한 tracepoint만 선택적으로 활성화하고, 추적 시간을 제한(예: sleep 30)하세요. bpftrace는 커널 5.0 이상에서 가장 안정적으로 동작합니다.

성능 튜닝 심화

XFS 성능 최적화는 mkfs 시점(볼륨 생성), 마운트 시점(런타임 옵션), sysctl(커널 파라미터), 워크로드 프로파일(응용별 튜닝)의 네 단계로 접근합니다. 각 단계에서 잘못된 설정은 성능을 오히려 저하시킬 수 있으므로, 워크로드 특성을 먼저 파악하는 것이 중요합니다.

XFS 성능 튜닝 의사결정 트리 워크로드 특성 식별 대용량 순차 I/O 미디어, 백업, 로그 수집 allocsize=1g extsize hint, logbsize=256k 소형 랜덤 I/O 데이터베이스, 캐시, KV 스토어 inode64, noatime, logbufs=8 AG 수 ≥ CPU 수, 외부 로그 메타데이터 집중 메일, 소규모 다량 파일 외부 로그 SSD, lazytime logbsize=256k, inode64 mkfs.xfs 최적화 매트릭스 -d su=256k,sw=N (RAID) -d agcount=N (AG 수) -l size=Nm (로그 크기) -i size=1024 (inode) -l logdev=DEV (외부) -b size=4096 (블록) -n size=4096 (dir 블록) -r rtdev=DEV (RT) 런타임 최적화 매트릭스 마운트 옵션 allocsize=64K~1G (순차 쓰기 사전 할당) logbufs=2~8 (로그 버퍼 수) logbsize=32K~256K (로그 버퍼 크기) noatime/lazytime (타임스탬프 최적화) discard/nodiscard (SSD TRIM 제어) inode64 (전체 AG inode 할당) sysctl / I/O 스케줄러 fs.xfs.xfssyncd_centisecs (동기화 주기) fs.xfs.speculative_prealloc_lifetime fs.xfs.filestream_centisecs vm.dirty_ratio / vm.dirty_background_ratio I/O 스케줄러: mq-deadline (HDD), none (NVMe) nr_requests, read_ahead_kb 조정

mkfs.xfs 워크로드별 최적 설정

# === 대용량 순차 쓰기 (미디어/백업 서버) ===
$ mkfs.xfs \
    -d agcount=32 \                 # 병렬 할당
    -l size=512m,logdev=/dev/sdb1 \ # 외부 로그 + 큰 로그
    -b size=4096 \
    /dev/sda1

$ mount -o allocsize=1g,logbsize=256k,logbufs=8,noatime \
    -o logdev=/dev/sdb1 /dev/sda1 /data

# extent size hint 설정 (디렉토리 단위)
$ xfs_io -c 'extsize 16m' /data/media/

# === 데이터베이스 (PostgreSQL/MySQL) ===
$ mkfs.xfs \
    -d agcount=64,su=256k,sw=4 \   # RAID 최적화 + 많은 AG
    -l size=1g,logdev=/dev/nvme1n1p1 \ # NVMe 외부 로그
    -i size=512 \                   # 작은 inode (DB는 소수 대형 파일)
    /dev/md0

$ mount -o inode64,noatime,logbufs=8,logbsize=256k,allocsize=64k \
    -o logdev=/dev/nvme1n1p1 /dev/md0 /data/db

# === 컨테이너/가상화 (VM 이미지) ===
$ mkfs.xfs \
    -d agcount=16 \
    -m reflink=1 \                  # reflink 활성 (기본값이지만 명시)
    /dev/sda1

$ mount -o inode64,lazytime /dev/sda1 /var/lib/containers

sysctl 튜닝 가이드

파라미터기본값대용량 순차DB 랜덤 I/O설명
fs.xfs.xfssyncd_centisecs300060003000동기화 데몬 주기 (센티초). 높이면 배치 효과 증가
fs.xfs.speculative_prealloc_lifetime30060060Speculative prealloc 유지 시간 (초)
fs.xfs.filestream_centisecs300060001500filestream AG 점유 유지 시간
vm.dirty_ratio204010쓰기 캐시 최대 비율 (%)
vm.dirty_background_ratio10205백그라운드 flush 시작 비율
vm.dirty_expire_centisecs300060001500dirty 페이지 만료 시간

튜닝 효과 측정

# XFS 통계 확인 (누적/리셋 가능)
$ cat /sys/fs/xfs/sda1/stats/stats
extent_alloc 1523456 2345678 ...
abt 234567 345678 ...
blk_map 4567890 5678901 ...
log 678901 789012 ...

# 실시간 통계 변화 모니터링 (1초 간격)
$ watch -n 1 'cat /sys/fs/xfs/sda1/stats/stats'

# 튜닝 전후 비교: fio 벤치마크
$ fio --name=seq-write --rw=write --bs=1m --size=10g \
      --directory=/data --numjobs=4 --ioengine=libaio --direct=1

$ fio --name=rand-rw --rw=randrw --bs=4k --size=1g \
      --directory=/data --numjobs=16 --ioengine=libaio --direct=1

# extent 단편화 비율 확인 (낮을수록 좋음)
$ xfs_db -r -c 'frag -f' /dev/sda1
actual 1523456, ideal 234567, fragmentation factor 84.60%

# AG별 free space 분포 — 불균형 시 rebalance 필요
$ xfs_info /data
$ for ag in $(seq 0 31); do
    xfs_db -r -c "agf $ag" -c 'print freeblks longest' /dev/sda1
  done
튜닝 원칙: (1) 반드시 측정 먼저, 튜닝 후의 순서를 따르세요. 기본값은 대부분의 워크로드에서 합리적입니다. (2) 한 번에 하나의 파라미터만 변경하고 효과를 측정하세요. (3) nobarrier는 배터리 백업 RAID 컨트롤러에서만 사용하고, 그 외 환경에서는 데이터 손실 위험이 있습니다. (4) mkfs 옵션은 재생성 전에는 변경할 수 없으므로 초기 설계가 중요합니다.

XFS와 관련된 다른 주제를 더 깊이 이해하고 싶다면 다음 문서를 참고하세요.