XFS 파일시스템 심화
XFS Allocation Group 병렬 설계, B+tree 메타데이터, Extent 관리, 저널링(WAL), Reflink/COW, 성능 튜닝, xfsprogs 도구 종합 가이드.
개요 & 역사
XFS는 1993년 SGI(Silicon Graphics)가 IRIX 운영체제를 위해 개발한 64비트 고성능 저널링 파일시스템입니다. 2001년 Linux 커널 2.4에 이식되었으며, 2014년 RHEL 7에서 기본 파일시스템으로 채택된 이후 엔터프라이즈 Linux의 사실상 표준이 되었습니다.
XFS 역사
| 연도 | 이벤트 |
|---|---|
| 1993 | SGI IRIX 5.3에서 XFS 최초 공개 |
| 2001 | Linux 커널 2.4에 이식 (SGI 오픈소스 기여) |
| 2006 | Delayed Allocation 지원 추가 |
| 2012 | v5 on-disk format: CRC32C 셀프체크 메타데이터 |
| 2014 | RHEL 7 기본 파일시스템 채택 |
| 2016 | Reflink / COW 지원 (커널 4.9) |
| 2019 | Online repair 프레임워크 개발 시작 |
| 2023 | Online 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 format | v5 (CRC32C self-describing metadata, 기본) |
ext4 / Btrfs / XFS 비교
| 특성 | ext4 | XFS | Btrfs |
|---|---|---|---|
| 최대 볼륨 | 1 EiB | 8 EiB | 16 EiB |
| 최대 파일 | 16 TiB | 8 EiB | 16 EiB |
| 온라인 축소 | 지원 | 미지원 | 지원 |
| 온라인 확장 | 지원 | 지원 | 지원 |
| COW / 스냅샷 | 미지원 | Reflink (4.9+) | 기본 COW |
| 저널링 | JBD2 (데이터+메타) | WAL (메타 전용) | COW (암묵적) |
| inode 할당 | 고정 (mkfs 시 결정) | 동적 | 동적 |
| 병렬 I/O | Block Group 단위 | AG 기반 고도 병렬 | Chunk 기반 |
| 대형 파일 성능 | 우수 | 최우수 | 우수 |
| RHEL 기본 FS | RHEL 6 | RHEL 7+ | 미채택 |
아키텍처 & 디스크 레이아웃
XFS의 핵심 설계 철학은 Allocation Group(AG) 기반 병렬 처리입니다. 전체 파일시스템을 독립적인 AG로 분할하여 각 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 tree | extent 크기 | 요청 크기에 가장 적합한 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 스케일링
/* 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+tree | AGF | 시작 블록 번호 | Free space (위치별 검색) |
| CNT B+tree | AGF | extent 크기 | Free space (크기별 검색) |
| Inode B+tree | AGI | inode 번호 | Inode chunk 추적 |
| Free Inode B+tree | AGI | inode 번호 | Free inode chunk 추적 |
| Reverse Map B+tree | AGF (v5) | 물리 블록 | 역방향 매핑 (online repair) |
| Refcount B+tree | AGF (v5) | 시작 블록 | Reflink 참조 카운트 |
| Extent B+tree | Inode data fork | 파일 오프셋 | 파일 extent 매핑 |
| Directory B+tree | Inode data fork | 해시값 | 대형 디렉토리 엔트리 |
| Attr B+tree | Inode attr fork | 이름 해시 | 확장 속성 |
Short-format vs Long-format
XFS B+tree는 두 가지 포인터 형식을 사용합니다:
| 형식 | 범위 | 사용처 |
|---|---|---|
| Short-format | AG 내부 (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 시점에 한꺼번에 할당합니다. 이를 통해:
- 단편화 최소화: 최종 파일 크기를 알고 할당하므로 연속 extent 확보 가능
- 메타데이터 오버헤드 감소: 임시 파일은 블록 할당 없이 삭제 가능
- 대역폭 최적화: 인접 블록을 한 번에 할당하여 디스크 시크 최소화
/* 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 포맷
| 버전 | 크기 | 특징 |
|---|---|---|
| v1 | 256B | 초기 형식, 32비트 프로젝트 ID |
| v2 | 256B | 나노초 타임스탬프, 64비트 프로젝트 ID |
| v3 | 512B (기본) | 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 List | XFS_DINODE_FMT_EXTENTS | extent 수가 fork 공간 내 | extent 레코드를 inode에 직접 저장 |
| B+tree | XFS_DINODE_FMT_BTREE | extent 수가 fork 초과 | B+tree 루트만 inode에, 나머지는 외부 블록 |
Attr Fork
inode 내부는 Data Fork와 Attr Fork로 분할됩니다. di_forkoff가 경계를 정의하며, 확장 속성(xattr)은 Attr Fork에 저장됩니다. Data Fork와 동일한 Local → Extents → B+tree 전환 메커니즘을 사용합니다.
디렉토리 구조
XFS 디렉토리는 엔트리 수에 따라 5단계로 구조가 확장됩니다:
| 형식 | 조건 | 구조 |
|---|---|---|
| Shortform | inode 내 수용 가능 | 이름/inode 쌍을 inode data fork에 인라인 저장 |
| Block | 1 블록에 수용 | 단일 디렉토리 데이터 블록 (해시 정렬) |
| Leaf | 다수 데이터 블록 | 데이터 블록 + 별도 리프 블록 (해시→데이터 매핑) |
| Node | 리프가 1블록 초과 | 데이터 + 리프 + 내부 노드 블록 (B+tree) |
| B+tree | extent 수 초과 | 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 구조
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를 재실행합니다.
| Intent | Done | 용도 |
|---|---|---|
| EFI (Extent Free Intent) | EFD | extent 해제 (truncate, rm) |
| RUI (Rmap Update Intent) | RUD | reverse mapping 업데이트 |
| CUI (Refcount Update Intent) | CUD | refcount 업데이트 (reflink) |
| BUI (Bmap Update Intent) | BUD | extent 매핑 업데이트 |
외부 로그 디바이스
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 & COW (커널 4.9+)
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
/* 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
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
CRC32C Self-describing Metadata (v5 포맷)
v5 on-disk 포맷(Linux 3.7+, 기본 활성)은 모든 메타데이터 블록에 다음을 추가합니다:
- CRC32C 체크섬: 무결성 검증 (silent corruption 감지)
- UUID: 파일시스템 식별 (잘못된 블록 참조 방지)
- Block number: 자기 참조 블록 번호 (misplaced write 감지)
- Log Sequence Number: 최종 수정 시점 추적
성능 튜닝
마운트 옵션
| 옵션 | 기본값 | 설명 |
|---|---|---|
logbufs=N | 8 | 인메모리 로그 버퍼 수 (2-8). 높을수록 쓰기 버스트 흡수 |
logbsize=N | 32K/256K | 로그 버퍼 크기. 대형 트랜잭션 워크로드에서 증가 |
allocsize=N | 64K | 스트리밍 쓰기 시 사전 할당 단위 (최대 1G) |
inode64 | v5 기본 | 모든 AG에서 inode 할당 (32비트 앱 호환 주의) |
largeio | off | stat()의 st_blksize를 stripe width로 설정 |
nobarrier | barrier=1 | 쓰기 배리어 비활성 (배터리 캐시 RAID만) |
discard | off | SSD TRIM 자동 발행 |
lazytime | off | 타임스탬프 업데이트 지연 |
핵심 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/SSD | discard 또는 fstrim cron, inode64 |
mkfs.xfs -d su=256k,sw=4로 stripe unit/width를 디스크 배열에 맞추면 데이터 정렬이 최적화됩니다. AG 크기도 -d agcount=N으로 CPU 코어 수에 맞출 수 있습니다.
관리 도구 (xfsprogs)
주요 도구 요약
| 도구 | 용도 |
|---|---|
mkfs.xfs | XFS 파일시스템 생성 |
xfs_info | 마운트된 FS의 지오메트리 정보 출력 |
xfs_admin | UUID, 레이블 변경, lazy-count 전환 |
xfs_repair | 오프라인 파일시스템 복구 |
xfs_db | 디버그 모드 — 메타데이터 직접 검사/수정 |
xfs_logprint | 저널 로그 내용 덤프 |
xfs_metadump | 메타데이터 이미지 추출 (버그 리포트용) |
xfs_growfs | 온라인 파일시스템 확장 |
xfs_fsr | 파일 단편화 해소 (defrag) |
xfs_quota | 사용자/그룹/프로젝트 quota 관리 |
xfs_freeze / xfs_thaw | I/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 UUID | UUID 충돌 (클론 볼륨) | 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 단편화를 확인하세요.