dm-integrity — 블록 레벨 무결성 보호
dm-integrity는 Device Mapper 프레임워크 위에서 읽기/쓰기 모두 지원하는 블록 레벨 무결성 보호를 제공합니다.
저널링(Journaling) 기반 무결성 태그로 crash-safety를 보장하며,
Bitmap 모드로 대용량 디스크에서 성능을 최적화하고,
dm-crypt와 AEAD 결합으로 암호화+무결성을 단일 레이어에서 달성합니다.
커널 내부 구현(drivers/md/dm-integrity.c)을 심층 분석합니다.
- dm-verity — Merkle Tree 기반 읽기 전용 블록 검증
- dm-crypt — 블록 레벨 투명 암호화, LUKS, 성능 최적화
- Device Mapper / LVM — DM 프레임워크 기본
핵심 요약
- Device Mapper — 블록 디바이스 위에 가상 매핑(Mapping) 레이어를 제공하는 커널 프레임워크
- dm-integrity — 읽기/쓰기 모두 지원하는 무결성 태그 + 저널링
- Journal 모드 — 데이터+태그를 저널에 먼저 기록하여 crash-safety 완전 보장
- Bitmap 모드 — dirty 블록 추적으로 성능 향상, crash 후 부분 재검증
- AEAD 결합 — dm-crypt + dm-integrity를 결합한 인증 암호화 (AES-GCM)
단계별 이해
- Device Mapper 레이어 이해
커널의 블록 I/O 스택에서 DM은 bio를 가로채 target 드라이버에 전달합니다. - dm-integrity로 실시간 무결성
매 쓰기마다 무결성 태그를 계산하고 저널에 기록하여 crash-safety를 보장합니다. - 저널링과 crash-safety
저널은 순환 버퍼로 데이터+태그를 원자적으로 기록하며, 정전 후에도 일관성을 유지합니다. - AEAD 모드로 암호화 결합
dm-crypt + dm-integrity AEAD 모드로 암호화와 무결성을 동시에 달성합니다.
Device Mapper 개요
Device Mapper(DM)는 Linux 커널의 블록 레이어에서 가상 블록 디바이스를 생성하는 프레임워크입니다.
drivers/md/dm.c에 구현된 DM 코어는 struct mapped_device로 가상 디바이스를 표현하고,
struct dm_table로 매핑 테이블을 관리합니다. 각 target 드라이버(struct target_type)는
특정 기능(암호화, 검증, 스냅샷 등)을 구현하며, DM 코어가 bio를 적절한 target에 라우팅(Routing)합니다.
DM은 원래 Heinz Mauelshagen이 LVM(Logical Volume Manager)을 위해 개발했으며,
현재는 dm-crypt, dm-verity, dm-integrity, dm-thin, dm-cache, dm-snapshot 등
다양한 target을 통해 리눅스 스토리지 스택의 핵심 계층으로 자리잡았습니다.
사용자 공간(User Space)에서는 dmsetup, cryptsetup, veritysetup,
integritysetup 등의 도구로 DM 디바이스를 관리합니다.
DM 코어의 핵심 자료 구조를 살펴봅니다:
/* include/linux/device-mapper.h */
struct target_type {
uint64_t features;
const char *name;
struct module *module;
int (*ctr)(struct dm_target *ti, unsigned argc, char **argv);
void (*dtr)(struct dm_target *ti);
int (*map)(struct dm_target *ti, struct bio *bio);
int (*end_io)(struct dm_target *ti, struct bio *bio,
blk_status_t *error);
void (*status)(struct dm_target *ti, status_type_t type,
unsigned status_flags, char *result,
unsigned maxlen);
int (*iterate_devices)(struct dm_target *ti,
iterate_devices_callout_fn fn, void *data);
};
struct dm_target {
struct dm_table *table;
struct target_type *type;
sector_t begin; /* 매핑 시작 섹터 */
sector_t len; /* 매핑 길이 (섹터) */
void *private; /* target 별 사적 데이터 */
unsigned num_discard_bios;
unsigned num_secure_erase_bios;
unsigned num_write_zeroes_bios;
};
DM의 I/O 경로는 다음과 같이 진행됩니다: 사용자 공간에서 가상 디바이스(/dev/dm-N)에 I/O를 발행하면,
dm_submit_bio()가 호출됩니다. 이 함수는 dm_table에서 bio가 속하는 target을 찾아
해당 target의 map() 콜백(Callback)을 호출합니다. target은 bio를 변환(remap, split, encrypt 등)하여
하위 디바이스로 전달합니다. map() 콜백의 반환값에 따라 DM 코어가 bio의 후속 처리를 결정합니다:
DM_MAPIO_REMAPPED는 bio가 하위 디바이스로 redirect되었음을,
DM_MAPIO_SUBMITTED는 target이 bio를 완전히 처리했음을 의미합니다.
| DM 구성 요소 | 역할 | 소스 파일 |
|---|---|---|
mapped_device | 가상 블록 디바이스 표현 | drivers/md/dm.c |
dm_table | target 매핑 테이블 | drivers/md/dm-table.c |
dm_target | 개별 매핑 세그먼트 | drivers/md/dm.h |
dm_io | I/O 요청 추적 | drivers/md/dm.c |
dm_ioctl | 사용자 공간 인터페이스 | drivers/md/dm-ioctl.c |
dm_bufio | 해시/메타데이터 블록 캐시(Cache) | drivers/md/dm-bufio.c |
시작섹터 길이 target_name [target_args...] 형식입니다.
예: 0 2097152 integrity /dev/sdb1 0 32 J sha256
/* drivers/md/dm-ioctl.c - 주요 ioctl 명령 */
/* 사용자 공간 (dmsetup) <-> 커널 DM 인터페이스 */
/* DM_DEV_CREATE: 새 mapped_device 생성 */
/* DM_DEV_REMOVE: mapped_device 제거 */
/* DM_TABLE_LOAD: 매핑 테이블 로드 */
/* DM_DEV_SUSPEND: I/O 일시 중지 (테이블 교체 준비) */
/* DM_DEV_STATUS: 디바이스 상태 조회 */
/* DM_TABLE_STATUS: 테이블 상태 (dmsetup status) */
/* DM_TARGET_MSG: target에 메시지 전달 (dmsetup message) */
static int table_load(struct file *filp,
struct dm_ioctl *param,
size_t param_size)
{
struct dm_table *t;
struct mapped_device *md;
/* 1. 테이블 생성 */
r = dm_table_create(&t, get_mode(param), param->target_count, md);
/* 2. 각 target 파싱 및 추가 */
for (i = 0; i < param->target_count; i++) {
dm_table_add_target(t, type_name, start, len, params);
/* 내부에서 target_type->ctr() 호출 */
}
/* 3. 테이블 완료 (무결성 검증) */
r = dm_table_complete(t);
/* 4. 테이블을 mapped_device에 연결 */
dm_swap_table(md, t);
return 0;
}
/* bio 라우팅 - dm_submit_bio() 핵심 경로 */
static void dm_submit_bio(struct bio *bio)
{
struct mapped_device *md = bio->bi_bdev->bd_disk->private_data;
struct dm_table *map;
struct dm_target *ti;
map = dm_get_live_table(md, &srcu_idx);
/* bio가 속하는 target 찾기 */
ti = dm_table_find_target(map, bio->bi_iter.bi_sector);
/* target의 map() 콜백 호출 */
r = ti->type->map(ti, bio);
switch (r) {
case DM_MAPIO_SUBMITTED:
break; /* target이 처리 완료 */
case DM_MAPIO_REMAPPED:
submit_bio_noacct(bio); /* 하위 디바이스로 전달 */
break;
case DM_MAPIO_REQUEUE:
bio_io_error(bio); /* 재시도 */
break;
}
dm_put_live_table(md, srcu_idx);
}
물리 디바이스 -> dm-integrity -> dm-crypt -> ext4
이 경우 각 레이어의 map()이 순서대로 호출됩니다.
스택 깊이가 깊을수록 I/O 지연(Latency)이 증가하므로, AEAD 모드처럼
단일 레이어에서 암호화+무결성을 처리하는 것이 성능상 유리합니다.
dm-integrity 개요
dm-integrity는 읽기/쓰기 모두 지원하는 블록 레벨 무결성 보호를 제공합니다.
dm-verity와 달리 쓰기 시에도 무결성 태그를 자동으로 계산하여 저장하며,
저널링을 통해 crash-safety를 보장합니다. 커널 소스 drivers/md/dm-integrity.c에 구현되어 있으며,
Journal, Bitmap, Direct(no-journal) 세 가지 모드를 제공합니다.
/* drivers/md/dm-integrity.c */
struct dm_integrity_c {
struct dm_dev *dev;
struct dm_dev *meta_dev; /* 별도 메타 디바이스 (선택) */
unsigned int tag_size; /* 태그 크기 (바이트) */
unsigned int sectors_per_block; /* 블록당 섹터 수 */
__u64 provided_data_sectors; /* 사용 가능 데이터 섹터 */
unsigned int journal_entries;
unsigned int journal_sections;
struct journal_entry *journal;
struct crypto_shash *internal_hash;
struct crypto_shash *journal_mac;
enum integrity_mode mode; /* J, B, D (journal/bitmap/direct) */
bool recalculate;
};
| 모드 | 저널 | Crash Safety | 성능 | 주요 용도 |
|---|---|---|---|---|
| Journal (J) | 전체 저널 | 완전 보장 | 쓰기 2x 오버헤드 | 단독 사용 |
| Bitmap (B) | 비트맵만 | dirty 블록 재검증 | 저널보다 빠름 | 대용량 디스크 |
| Direct (D) | 없음 | 비보장 | 최소 오버헤드 | AEAD 모드 전용 |
저널링 메커니즘
dm-integrity의 저널은 데이터와 무결성 태그를 원자적(Atomic)으로 기록하여 crash-safety를 보장합니다. 저널은 순환 버퍼(circular buffer)로 구현되며, commit 단위로 데이터+태그 쌍을 기록합니다. Bitmap 모드에서는 저널 대신 비트맵으로 dirty 블록을 추적하며, crash 후 복구 시 dirty 비트가 설정된 블록만 재검증합니다.
/* drivers/md/dm-integrity.c - 저널 엔트리 구조 */
struct journal_entry {
union {
struct {
__le64 sector; /* 대상 섹터 번호 */
__le32 data_csum; /* 데이터 체크섬 */
__le32 tag_csum; /* 태그 체크섬 */
} s;
__le64 commit_id; /* 커밋 ID */
} u;
};
--journal-watermark 옵션으로 저널 flush 임계값을 설정할 수 있습니다.
저널이 가득 차면 쓰기가 차단되므로, 워크로드에 맞는 적절한 크기 설정이 중요합니다.
dm-integrity 설정
dm-integrity는 integritysetup 도구로 설정합니다.
cryptsetup 패키지에 포함되어 있으며, 포맷 후 활성화하는 2단계로 진행합니다.
# 1. dm-integrity 포맷 (SHA-256 해시, 저널 모드)
$ integritysetup format /dev/sdb1 \
--integrity sha256 \
--tag-size 32 \
--sector-size 4096 \
--journal-size 64M
# 2. dm-integrity 디바이스 활성화
$ integritysetup open /dev/sdb1 integrity_disk \
--integrity sha256
# 3. 파일시스템 생성 및 마운트
$ mkfs.ext4 /dev/mapper/integrity_disk
$ mount /dev/mapper/integrity_disk /mnt/secure
# 4. Bitmap 모드로 설정 (성능 우선)
$ integritysetup format /dev/sdb1 \
--integrity sha256 \
--integrity-bitmap-mode \
--bitmap-sectors-per-bit 65536
$ integritysetup open /dev/sdb1 integrity_bitmap \
--integrity sha256 \
--integrity-bitmap-mode
| 옵션 | 설명 | 기본값 |
|---|---|---|
--integrity | 해시 알고리즘 | sha256 |
--tag-size | 무결성 태그 크기 (바이트) | 알고리즘 출력 크기 |
--sector-size | 섹터 크기 | 512 |
--journal-size | 저널 크기 | 자동 계산 |
--journal-watermark | 저널 flush 임계값 (%) | 50% |
--journal-commit-time | 저널 commit 주기 (ms) | 10000 |
--integrity-bitmap-mode | 비트맵 모드 사용 | 비활성 |
dm-integrity 커널 내부 구현
dm-integrity의 커널 구현은 drivers/md/dm-integrity.c에 있으며,
약 4,500줄에 달하는 복잡한 코드입니다. 핵심 동작은 bio를 분할하여
데이터와 무결성 태그를 동시에 처리하는 것입니다.
/* drivers/md/dm-integrity.c - map 함수 */
static int integrity_map(struct dm_target *ti, struct bio *bio)
{
struct dm_integrity_c *ic = ti->private;
struct dm_integrity_io *dio;
dio = dm_per_bio_data(bio, ti->per_io_data_size);
dio->ic = ic;
dio->bi_sector = bio->bi_iter.bi_sector;
dio->bi_integrity = bio_integrity(bio);
if (bio_data_dir(bio) == WRITE) {
if (ic->mode == 'J') {
/* Journal 모드: 저널에 먼저 기록 */
integrity_journal_write(ic, dio);
} else if (ic->mode == 'B') {
/* Bitmap 모드: dirty 비트 설정 */
integrity_bitmap_write(ic, dio);
}
/* 무결성 태그 계산 및 저장 */
integrity_metadata_write(ic, dio);
} else {
/* READ: 데이터 읽기 후 태그 검증 */
integrity_metadata_read(ic, dio);
}
/* bio를 데이터 디바이스로 remap */
bio_set_dev(bio, ic->dev->bdev);
bio->bi_iter.bi_sector = integrity_data_sector(ic, dio->bi_sector);
return DM_MAPIO_REMAPPED;
}
/* 무결성 태그 검증 (읽기 시) */
static int integrity_check_tag(struct dm_integrity_c *ic,
u8 *data, u8 *stored_tag,
sector_t sector)
{
u8 computed_tag[MAX_TAG_SIZE];
int r;
/* 해시 계산 */
r = crypto_shash_digest(ic->internal_hash_desc,
data, ic->sectors_per_block << SECTOR_SHIFT,
computed_tag);
if (r)
return r;
/* 저장된 태그와 비교 */
if (memcmp(computed_tag, stored_tag, ic->tag_size)) {
DMERR("integrity checksum failed at sector %llu",
(unsigned long long)sector);
return -EILSEQ;
}
return 0;
}
dm-integrity의 I/O 경로에서 주의할 점은 메타데이터 I/O입니다.
데이터와 태그가 디스크 상에서 인터리빙되어 있기 때문에,
연속적인 데이터 I/O가 비연속적인 디스크 I/O로 변환될 수 있습니다.
이로 인해 HDD에서는 성능 저하가 심각할 수 있으며, SSD에서는 상대적으로 영향이 적습니다.
별도의 메타 디바이스(--data-device)를 사용하면 이 문제를 완화할 수 있습니다.
dm-integrity의 내부 동작을 더 깊이 살펴보면, 데이터 섹터와 태그 영역의 매핑이 핵심입니다.
논리 섹터 번호를 물리 섹터 번호로 변환할 때, 태그 영역의 크기를 고려하여 오프셋(Offset)을 계산합니다.
이 변환은 get_data_sector() 함수에서 수행됩니다.
/* drivers/md/dm-integrity.c - 섹터 변환 */
static sector_t get_data_sector(struct dm_integrity_c *ic,
area_t area, sector_t offset)
{
sector_t result;
/* 데이터 영역과 태그 영역이 인터리빙됨
* area = 논리 섹터 / interleave_sectors
* offset = 논리 섹터 % interleave_sectors */
result = area * (ic->interleave_sectors + ic->tag_area_sectors);
result += ic->initial_sectors; /* 슈퍼블록 + 저널 공간 */
result += offset;
return result;
}
/* 태그 읽기/쓰기 - 메타데이터 I/O */
static void rw_tag(struct dm_integrity_c *ic,
unsigned int tag_offset,
u8 *tag, unsigned int tag_size,
int op)
{
sector_t tag_sector;
unsigned int tag_sector_offset;
/* 태그의 디스크 위치 계산 */
tag_sector = tag_offset / (ic->tag_size * ic->journal_section_entries);
tag_sector_offset = tag_offset % (ic->tag_size * ic->journal_section_entries);
if (op == TAG_READ)
dm_bufio_read(ic->tag_bufio, tag_sector, ...);
else
dm_bufio_write_dirty(ic->tag_bufio, tag_sector, ...);
}
--data-device)를 사용하면
데이터와 태그를 물리적으로 분리하여 이 문제를 완화할 수 있습니다.
# 별도 메타 디바이스로 dm-integrity 설정 (성능 최적화)
$ integritysetup format --data-device /dev/nvme0n1p1 /dev/nvme1n1p1 --integrity sha256 --sector-size 4096
# 결과: 데이터는 nvme0n1p1, 태그는 nvme1n1p1에 저장
# 두 NVMe를 병렬로 사용하여 성능 극대화
| dm-integrity 커널 함수 | 역할 | 호출 시점 |
|---|---|---|
integrity_ctr() | target 생성, 슈퍼블록(Superblock) 로드 | 테이블 로드 |
integrity_dtr() | target 해제, 저널 flush | 디바이스 제거 |
integrity_map() | bio remap + 태그 처리 | 매 I/O |
integrity_end_io() | 읽기 태그 검증 | 읽기 완료 |
do_journal_write() | 저널 -> 실데이터 플러시(Flush) | 저널 watermark |
integrity_recalc() | 기존 데이터 태그 재계산 | recalculate 모드 |
integrity_bitmap_flush() | 비트맵 dirty 플러시 | bitmap 모드 |
dm-crypt + dm-integrity 결합
dm-crypt와 dm-integrity를 결합하면 인증 암호화(AEAD)를 구현할 수 있습니다. AES-GCM 또는 ChaCha20-Poly1305 같은 AEAD 알고리즘을 사용하여 암호화와 무결성 검증을 단일 연산으로 수행합니다. 이 결합 모드에서 dm-crypt는 암호화 담당, dm-integrity는 인증 태그 저장 담당입니다.
# AEAD 모드 LUKS2 볼륨 생성 (AES-GCM)
$ cryptsetup luksFormat --type luks2 \
--cipher aes-gcm-random \
--integrity aead \
--sector-size 4096 \
--key-size 256 \
/dev/sdb1
# HMAC 무결성 (비 AEAD, 별도 해시)
$ cryptsetup luksFormat --type luks2 \
--cipher aes-xts-plain64 \
--integrity hmac-sha256 \
--sector-size 4096 \
--key-size 512 \
/dev/sdb1
# dm-integrity 디바이스가 자동 생성됨
$ dmsetup ls
encrypted (254:1)
encrypted_dif (254:0) # dm-integrity 레이어
# 상태 확인
$ cryptsetup status encrypted
type: LUKS2
cipher: aes-gcm-random
keysize: 256 bits
integrity: aead
integrity keysize: 0 bits # AEAD는 별도 키 불필요
| 모드 | 암호화 | 무결성 | 태그 크기 | 성능 오버헤드 |
|---|---|---|---|---|
| AES-GCM (AEAD) | AES-GCM | GCM 내장 | 16B | ~15% |
| AES-XTS + HMAC | AES-XTS | HMAC-SHA256 | 32B | ~25% |
| ChaCha20-Poly1305 | ChaCha20 | Poly1305 | 16B | ~12% |
| AES-XTS 단독 | AES-XTS | 없음 | 0B | ~8% |
no-journal 모드로 동작합니다.
정전 시 데이터+태그 불일치로 해당 섹터가 읽기 불가능해질 수 있습니다.
UPS 또는 배터리 백업이 없는 환경에서는 저널 모드가 권장됩니다.
저널 Write-Ahead 로깅 상세
dm-integrity의 저널은 Write-Ahead Logging(WAL) 패턴을 따릅니다. 데이터와 무결성 태그를 실제 위치에 쓰기 전에 저널 영역에 먼저 기록함으로써 원자성(Atomicity)을 보장합니다. 정전 시 저널을 재생(replay)하여 일관성을 복구합니다.
/* drivers/md/dm-integrity.c - 저널 쓰기 핵심 경로 */
/* 저널 섹션 헤더 */
struct journal_section_header {
__le64 commit_id; /* 트랜잭션 식별자 */
__le64 commit_seq; /* 순서 번호 */
};
/* 저널 커밋 처리 */
static void integrity_commit(struct dm_integrity_c *ic)
{
struct journal_section_header *header;
unsigned int section;
section = ic->free_section;
header = journal_section_header(ic, section);
/* commit_id 기록 (홀수 = 커밋 완료 표시) */
header->commit_id = cpu_to_le64(ic->commit_id);
header->commit_seq = cpu_to_le64(ic->commit_seq);
/* 저널 MAC 계산 (선택적 보호) */
if (ic->journal_mac) {
crypto_shash_digest(ic->journal_mac_desc,
(u8 *)header,
ic->journal_section_size,
header->mac);
}
/* 저널 섹션을 디스크에 동기 쓰기 */
write_journal_section(ic, section);
flush_journal(ic);
ic->commit_seq++;
}
/* 저널 -> 실데이터 영역 플러시 */
static void do_journal_write(struct dm_integrity_c *ic)
{
unsigned int section, entry;
/* committed 섹션을 순서대로 처리 */
while (ic->flush_section != ic->free_section) {
section = ic->flush_section;
/* 섹션 내 각 엔트리 처리 */
for (entry = 0; entry < ic->journal_section_entries; entry++) {
struct journal_entry *je;
sector_t sector;
u8 *data, *tag;
je = journal_entry_get(ic, section, entry);
sector = le64_to_cpu(je->u.s.sector);
/* 데이터를 실제 위치에 쓰기 */
data = journal_data_get(ic, section, entry);
integrity_write_data(ic, sector, data);
/* 태그를 태그 영역에 쓰기 */
tag = journal_tag_get(ic, section, entry);
rw_tag(ic, sector, tag, ic->tag_size, TAG_WRITE);
}
/* 섹션 플러시 완료, flush_head 전진 */
ic->flush_section = (section + 1) % ic->journal_sections;
}
}
--journal-integrity 옵션으로 저널 자체의 무결성도 보호할 수 있습니다.
예: integritysetup format --journal-integrity hmac-sha256
이 옵션은 저널 섹션마다 HMAC을 추가하여 저널 변조를 탐지합니다.
--journal-crypt 옵션으로 저널을 암호화하여 기밀성도 제공 가능합니다.
Bitmap 모드 vs Journal 모드 비교
dm-integrity는 Journal 모드와 Bitmap 모드 두 가지 crash-safety 메커니즘을 제공합니다. Journal 모드는 완전한 crash-safety를 보장하지만 쓰기 증폭(Write Amplification)이 발생하며, Bitmap 모드는 dirty 블록만 추적하여 성능을 개선하지만 crash 후 재검증이 필요합니다.
/* drivers/md/dm-integrity.c - Bitmap 모드 핵심 */
/* 비트맵 dirty 비트 설정 */
static void integrity_bitmap_mark_dirty(struct dm_integrity_c *ic,
sector_t sector,
sector_t n_sectors)
{
unsigned long bit;
unsigned long end_bit;
/* 섹터를 비트맵 인덱스로 변환 */
bit = sector / ic->sectors_per_bitmap_bit;
end_bit = (sector + n_sectors - 1) / ic->sectors_per_bitmap_bit;
/* dirty 비트 설정 (원자적) */
while (bit <= end_bit) {
if (!test_and_set_bit(bit, ic->dirty_bitmap))
ic->dirty_bitmap_count++;
bit++;
}
}
/* Crash 후 비트맵 기반 재검증 */
static void integrity_bitmap_recalculate(struct dm_integrity_c *ic)
{
unsigned long bit;
/* dirty 비트가 설정된 블록만 재검증 */
for (bit = 0; bit < ic->bitmap_size; bit++) {
if (test_bit(bit, ic->dirty_bitmap)) {
sector_t start = bit * ic->sectors_per_bitmap_bit;
sector_t count = ic->sectors_per_bitmap_bit;
/* 데이터를 읽고 태그를 재계산 */
integrity_recalc_range(ic, start, count);
/* 재검증 완료 후 dirty 비트 해제 */
clear_bit(bit, ic->dirty_bitmap);
}
}
}
| 비교 항목 | Journal 모드 | Bitmap 모드 | No-journal (Direct) |
|---|---|---|---|
| 쓰기 증폭 | 2x (저널 + 실데이터) | 1x + 비트맵 I/O | 1x |
| Crash Safety | 완전 보장 | dirty 블록 재검증 | 비보장 |
| 복구 시간 | 수 초 (저널 replay) | 수 분~시간 | 없음 |
| 메모리 사용 | 저널 크기만큼 | 비트맵 크기 (작음) | 최소 |
| 순차 쓰기 처리량 | 기준 대비 ~50% | 기준 대비 ~80% | 기준 대비 ~90% |
| 랜덤 쓰기 IOPS | 기준 대비 ~40% | 기준 대비 ~70% | 기준 대비 ~85% |
| 주요 용도 | 단독 사용, 데이터 보호 | 대용량 디스크 | AEAD 결합 전용 |
--bitmap-sectors-per-bit 값을 크게 설정하면 비트맵 I/O가 줄지만
crash 후 재검증 범위가 넓어집니다. 기본값 65536은 각 비트가 약 32MB 범위를 커버합니다.
내부 자료 구조 상세
dm-integrity의 커널 구현은 여러 핵심 자료 구조에 의존합니다.
dm_integrity_c는 target 인스턴스의 전체 상태를 관리하며,
dm_integrity_io는 개별 I/O 요청의 컨텍스트(Context)를 추적합니다.
디스크 상의 슈퍼블록 구조와 저널 엔트리 포맷도 중요한 구성 요소입니다.
/* drivers/md/dm-integrity.c - 슈퍼블록 디스크 포맷 */
struct superblock {
__u8 magic[8]; /* "integrt\1" 매직 넘버 */
__u8 version; /* 포맷 버전 (현재 5) */
__s8 log2_interleave_sectors; /* 인터리빙 크기 (log2) */
__le16 integrity_tag_size; /* 태그 크기 (바이트) */
__le32 journal_sections; /* 저널 섹션 수 */
__le64 provided_data_sectors; /* 제공 가능 데이터 섹터 */
__le32 flags; /* SB_FLAG_* */
__u8 log2_sectors_per_block; /* 블록당 섹터 수 (log2) */
__le64 recalc_sector; /* 재계산 진행 위치 */
__u8 pad[4];
__u8 salt[SALT_SIZE]; /* HMAC salt */
};
#define SB_FLAG_HAVE_JOURNAL_MAC 0x1
#define SB_FLAG_RECALCULATING 0x2
#define SB_FLAG_DIRTY_BITMAP 0x4
#define SB_FLAG_FIXED_PADDING 0x8
#define SB_FLAG_FIXED_HMAC 0x10
/* dm_integrity_c 구조체 상세 필드 */
struct dm_integrity_c {
struct dm_dev *dev; /* 데이터 디바이스 */
struct dm_dev *meta_dev; /* 별도 메타 디바이스 (선택) */
struct dm_target *ti;
/* 해시/암호 */
struct crypto_shash *internal_hash; /* 태그 해시 알고리즘 */
struct crypto_shash *journal_mac; /* 저널 MAC 알고리즘 */
struct crypto_skcipher *journal_crypt; /* 저널 암호화 */
/* 디스크 레이아웃 */
unsigned int tag_size; /* 태그 크기 (바이트) */
unsigned int sectors_per_block; /* 블록당 섹터 수 */
__u64 provided_data_sectors; /* 사용 가능 데이터 섹터 */
sector_t initial_sectors; /* SB + 저널 + 패딩 */
sector_t metadata_run; /* 태그 영역 크기 */
__s8 log2_interleave_sectors;
/* 저널 관련 */
unsigned int journal_entries;
unsigned int journal_sections;
unsigned int journal_section_entries;
unsigned int journal_section_sectors;
unsigned int free_section; /* 현재 쓰기 섹션 */
unsigned int flush_section; /* 플러시 대기 섹션 */
__u64 commit_id; /* 현재 커밋 ID */
__u64 commit_seq; /* 커밋 시퀀스 번호 */
/* 비트맵 모드 */
unsigned long *dirty_bitmap; /* dirty 블록 비트맵 */
unsigned long bitmap_size; /* 비트맵 크기 (비트) */
unsigned int sectors_per_bitmap_bit;
/* dm-bufio 캐시 */
struct dm_bufio_client *bufio; /* 태그 블록 캐시 */
struct dm_bufio_client *journal_bufio;
/* 워크큐 */
struct workqueue_struct *commit_wq;
struct workqueue_struct *writer_wq;
struct workqueue_struct *recalc_wq;
/* 성능 카운터 */
enum integrity_mode mode; /* 'J', 'B', 'D' */
bool recalculate; /* 태그 재계산 진행 중 */
atomic64_t number_of_mismatches; /* 불일치 카운터 */
};
/* I/O별 컨텍스트 */
struct dm_integrity_io {
struct dm_integrity_c *ic;
sector_t bi_sector; /* 원래 섹터 번호 */
struct bio_integrity_payload *bi_integrity;
struct completion comp; /* I/O 완료 대기 */
int bi_status;
struct work_struct work; /* 워크큐 처리용 */
unsigned int range_start;
unsigned int range_end;
};
AEAD 통합 상세
AEAD(Authenticated Encryption with Associated Data) 결합 모드에서
dm-crypt는 인증 암호화를 수행하고, dm-integrity는 인증 태그의 저장/조회만 담당합니다.
이 구조에서 dm-integrity는 no-journal(Direct) 모드로 동작하며,
태그 계산을 dm-crypt의 AEAD 알고리즘에 위임합니다.
/* dm-crypt + dm-integrity AEAD 결합 내부 동작 */
/* dm-crypt가 AEAD 모드로 bio를 처리하는 경우:
* 1. dm-crypt가 bio를 가로채 AEAD 암호화 수행
* 2. 암호화 결과: ciphertext + authentication tag (16B for GCM)
* 3. ciphertext는 bio 데이터에, auth tag는 bio_integrity에 저장
* 4. dm-integrity가 bio_integrity에서 tag를 추출하여 디스크에 기록 */
/* drivers/md/dm-crypt.c - AEAD 처리 */
static int crypt_convert_block_aead(
struct crypt_config *cc,
struct convert_context *ctx,
struct aead_request *req,
unsigned int tag_offset)
{
struct bio_integrity_payload *bip = ctx->bio_out->bi_integrity;
u8 *tag;
/* bio_integrity에서 태그 저장 위치 획득 */
tag = bip->bip_buf + tag_offset;
/* AEAD 요청 구성: src=평문, dst=암호문, assocdata=IV+섹터 */
aead_request_set_crypt(req, &src, &dst,
cc->sector_size, /* 평문 길이 */
iv);
aead_request_set_ad(req, cc->integrity_iv_size);
if (bio_data_dir(ctx->bio_out) == WRITE) {
/* 암호화: 암호문 + 인증 태그 생성 */
r = crypto_aead_encrypt(req);
/* tag에 인증 태그가 저장됨 (GCM: 16바이트) */
} else {
/* 복호화: 인증 태그 검증 + 복호화 */
r = crypto_aead_decrypt(req);
/* 태그 불일치 시 -EBADMSG 반환 */
}
return r;
}
/* dm-integrity 측: AEAD 태그 수신/저장 */
/* integrity_map()에서 bio_integrity payload를 통해
* dm-crypt가 생성한 인증 태그를 디스크에 기록
* 읽기 시에는 디스크에서 태그를 읽어 bio_integrity에 채움 */
static void integrity_aead_tag_io(struct dm_integrity_c *ic,
struct dm_integrity_io *dio,
int rw)
{
struct bio_integrity_payload *bip = dio->bi_integrity;
u8 *tag_buf = bip ? bip->bip_buf : NULL;
if (rw == WRITE && tag_buf) {
/* dm-crypt가 생성한 인증 태그를 디스크에 기록 */
rw_tag(ic, dio->bi_sector, tag_buf,
ic->tag_size, TAG_WRITE);
} else if (rw == READ && tag_buf) {
/* 디스크에서 인증 태그를 읽어 bio_integrity에 전달 */
rw_tag(ic, dio->bi_sector, tag_buf,
ic->tag_size, TAG_READ);
}
}
| AEAD 알고리즘 | 키 크기 | 태그 크기 | 특징 |
|---|---|---|---|
| AES-GCM | 128/256비트 | 16바이트 | AES-NI 가속, 널리 지원, nonce 재사용 취약 |
| AES-CCM | 128/256비트 | 16바이트 | 소프트웨어 구현 용이, GCM보다 느림 |
| ChaCha20-Poly1305 | 256비트 | 16바이트 | AES-NI 없는 환경에서 빠름, ARM 최적화 |
| Aegis128 | 128비트 | 16바이트 | AES-NI 환경에서 GCM보다 빠름 (커널 6.0+) |
dm-integrity + LUKS2 연동
LUKS2(Linux Unified Key Setup version 2)는 dm-crypt와 dm-integrity를 통합 관리하는
상위 레벨 인터페이스를 제공합니다. cryptsetup을 사용하면 dm-integrity 레이어가
자동으로 생성되며, 키 관리와 무결성 설정을 단일 명령으로 처리할 수 있습니다.
# LUKS2 + dm-integrity 결합 설정 가이드
## 방법 1: AEAD 모드 (암호화 + 무결성 단일 연산)
$ cryptsetup luksFormat --type luks2 \
--cipher aes-gcm-random \
--integrity aead \
--sector-size 4096 \
--key-size 256 \
/dev/sdb1
## 방법 2: HMAC 모드 (별도 해시, 더 강력한 무결성)
$ cryptsetup luksFormat --type luks2 \
--cipher aes-xts-plain64 \
--integrity hmac-sha256 \
--sector-size 4096 \
--key-size 512 \
/dev/sdb1
# HMAC 모드에서는 키가 2개: 256-bit 암호화 + 256-bit HMAC
# --key-size 512 = AES-XTS(256) + HMAC-SHA256(256)
## 방법 3: Poly1305 (ARM/소프트웨어 환경 최적)
$ cryptsetup luksFormat --type luks2 \
--cipher chacha20,aead \
--integrity poly1305 \
--sector-size 4096 \
--key-size 256 \
/dev/sdb1
## 볼륨 열기 및 마운트
$ cryptsetup open /dev/sdb1 secure_vol
$ mkfs.ext4 /dev/mapper/secure_vol
$ mount /dev/mapper/secure_vol /mnt/secure
## LUKS2 헤더에서 integrity 정보 확인
$ cryptsetup luksDump /dev/sdb1
# ...
# Segments:
# 0: crypt
# cipher: aes-xts-plain64
# integrity: hmac(sha256)
# sector: 4096 [bytes]
# ...
# Integrity:
# type: LUKS2_INTEGRITY
# journal_size: 67108864
# interleave_sectors: 32768
## dm-integrity 하위 디바이스 상태 확인
$ dmsetup ls
secure_vol (254:1)
secure_vol_dif (254:0) # dm-integrity 레이어
$ dmsetup status secure_vol_dif
# 0 4194304 integrity 0 0
## LUKS2 + integrity 볼륨의 크기 오버헤드 확인
$ integritysetup dump /dev/sdb1
# Info for integrity device /dev/sdb1
# superblock_version: 5
# log2_interleave_sectors: 15
# integrity_tag_size: 32
# journal_sections: 88
# provided_data_sectors: 4063232
# sector_size: 4096
## 성능 비교: 무결성 없음 vs AEAD vs HMAC
# AES-XTS만 (무결성 없음)
$ cryptsetup luksFormat --type luks2 --cipher aes-xts-plain64 \
--key-size 512 /dev/sdb1
# fio 순차 쓰기: ~1.8 GB/s
# AES-GCM AEAD
# fio 순차 쓰기: ~1.5 GB/s (약 17% 오버헤드)
# AES-XTS + HMAC-SHA256
# fio 순차 쓰기: ~1.2 GB/s (약 33% 오버헤드)
성능 최적화
dm-integrity의 성능은 저널 설정, 섹터 크기, 인터리빙 파라미터에 크게 좌우됩니다. 워크로드 특성에 맞는 적절한 튜닝이 필수적입니다.
# dm-integrity 성능 튜닝 가이드
## 1. journal_watermark 최적화
# 기본값 50%: 저널이 50% 차면 플러시 시작
# 높은 값: 배치 플러시로 처리량 증가, 지연시간 증가
# 낮은 값: 즉시 플러시, 지연시간 감소, 처리량 감소
$ integritysetup format /dev/sdb1 \
--integrity sha256 \
--journal-watermark 75 \
--journal-commit-time 5000
# 75%에서 플러시, 또는 5초마다 강제 커밋
## 2. journal_commit_time 조정
# 기본값 10000ms (10초)
# 순차 쓰기 워크로드: 30000ms (30초) - 배치 효과 극대화
# OLTP 워크로드: 1000ms (1초) - 지연시간 최소화
$ integritysetup format /dev/sdb1 \
--integrity sha256 \
--journal-commit-time 1000
## 3. 저널 크기 최적화
# 큰 저널: 더 많은 쓰기를 배치 처리, 메모리 사용 증가
# 작은 저널: 빈번한 플러시, 쓰기 차단 가능
$ integritysetup format /dev/sdb1 \
--integrity sha256 \
--journal-size 256M
# 고부하 쓰기 워크로드에서 256MB 저널 권장
## 4. 섹터 크기 조정
# 4096B 섹터: 태그 오버헤드 비율 감소 (32B/4096B = 0.78%)
# 512B 섹터: 태그 오버헤드 비율 증가 (32B/512B = 6.25%)
$ integritysetup format /dev/sdb1 \
--integrity sha256 \
--sector-size 4096
## 5. 별도 메타 디바이스 사용 (최고 성능)
# 데이터와 태그를 물리적으로 분리하여 병렬 I/O
$ integritysetup format \
--data-device /dev/nvme0n1p1 \
/dev/nvme1n1p1 \
--integrity sha256 \
--sector-size 4096
# 데이터: NVMe 0, 태그: NVMe 1 (병렬 접근)
## 6. Bitmap 모드로 쓰기 성능 개선
$ integritysetup format /dev/sdb1 \
--integrity sha256 \
--integrity-bitmap-mode \
--bitmap-sectors-per-bit 65536 \
--bitmap-flush-time 5000
## 7. 벤치마크 실행
# 순차 쓰기
$ fio --name=seq_write --filename=/dev/mapper/integrity_disk \
--rw=write --bs=128k --numjobs=4 --iodepth=16 \
--size=4G --direct=1 --group_reporting
# 랜덤 쓰기 (저널 모드 병목 테스트)
$ fio --name=rand_write --filename=/dev/mapper/integrity_disk \
--rw=randwrite --bs=4k --numjobs=4 --iodepth=32 \
--size=1G --direct=1 --group_reporting
# 혼합 읽기/쓰기
$ fio --name=mixed --filename=/dev/mapper/integrity_disk \
--rw=randrw --rwmixread=70 --bs=4k --numjobs=4 \
--iodepth=32 --size=1G --direct=1 --group_reporting
| 튜닝 파라미터 | 기본값 | 순차 쓰기 최적 | 랜덤 쓰기 최적 | 영향 |
|---|---|---|---|---|
journal-watermark | 50% | 80% | 30% | 플러시 빈도 조절 |
journal-commit-time | 10000ms | 30000ms | 1000ms | 커밋 주기 |
journal-size | 자동 | 256MB | 64MB | 배치 크기 |
sector-size | 512B | 4096B | 4096B | 태그 오버헤드 비율 |
bitmap-sectors-per-bit | 65536 | 131072 | 32768 | 재검증 범위 |
iostat -x 1로 디바이스 사용률을 확인하고,
%util이 100%에 근접하면 저널 크기 확대 또는 Bitmap 모드 전환을 고려하세요.
HDD에서는 인터리빙으로 인한 seek 오버헤드가 지배적이므로
--data-device 옵션으로 메타 디바이스를 분리하는 것이 가장 효과적입니다.
디버깅 및 모니터링
dm-integrity 문제 진단에는 dmsetup, integritysetup,
커널 로그, 그리고 성능 모니터링 도구를 활용합니다.
# 1. dm-integrity 상태 확인
$ dmsetup status integrity_disk
# 0 4194304 integrity 0 0
# 형식: start_sector num_sectors integrity mismatch_count provision_mode
# mismatch_count가 0이 아니면 무결성 오류 발생
# 2. integritysetup dump - 디스크 레이아웃 상세
$ integritysetup dump /dev/sdb1
# Info for integrity device /dev/sdb1
# superblock_version 5
# log2_interleave_sectors 15
# integrity_tag_size 32
# journal_sections 88
# provided_data_sectors 4063232
# sector_size 4096
# flags FIXED_HMAC
# 3. 커널 로그 확인
$ dmesg | grep -i integrity
# device-mapper: integrity: dm-0: started in journal mode
# device-mapper: integrity: dm-0: initialized with tag size 32
# device-mapper: integrity: dm-0: checksum failed at sector 12345
# 4. 무결성 오류 카운터 실시간 모니터링
$ watch -n 1 'dmsetup status integrity_disk | awk "{print \"mismatches:\", \$5}"'
# 5. I/O 성능 모니터링
# 실제 디바이스와 DM 디바이스의 I/O 비교
$ iostat -x 1 /dev/sdb /dev/dm-0
# sdb (물리)와 dm-0 (dm-integrity)의 처리량 차이 = 오버헤드
# 6. dmsetup table로 현재 설정 확인
$ dmsetup table integrity_disk
# 0 4063232 integrity /dev/sdb1 0 32 J 6 journal_sectors:180224 ...
# 7. 저널 상태 확인
# 저널 사용률 추정 (쓰기 지연과 상관)
$ dmsetup message integrity_disk 0 stats
# (커널 버전에 따라 지원 여부 다름)
# 8. ftrace로 dm-integrity I/O 경로 추적
$ echo function_graph | sudo tee /sys/kernel/tracing/current_tracer
$ echo integrity_map | sudo tee /sys/kernel/tracing/set_graph_function
$ echo 1 | sudo tee /sys/kernel/tracing/tracing_on
# ... I/O 발생 ...
$ cat /sys/kernel/tracing/trace
# integrity_map -> integrity_journal_write -> do_journal_write
# 9. 강제 무결성 검증 (전체 디스크)
# 모든 블록을 읽어 태그 검증
$ dd if=/dev/mapper/integrity_disk of=/dev/null bs=1M status=progress
# -EIO 발생 시 해당 블록에 무결성 오류 존재
# 10. 재계산 모드 활성화 (기존 데이터에 태그 추가)
$ integritysetup open /dev/sdb1 integrity_disk \
--integrity sha256 --integrity-recalculate
# 백그라운드에서 모든 블록의 태그를 재계산
# 진행 상황: dmesg에서 확인
| 증상 | 원인 | 해결 방법 |
|---|---|---|
| 읽기 시 -EIO (EILSEQ) | 데이터-태그 불일치 | 해당 블록 재기록, 백업에서 복구 |
| 쓰기 속도 급감 | 저널 full, 플러시 차단 | journal-size 증가, watermark 조정 |
| 부팅 시 오래 걸림 | bitmap 모드 재검증 | sectors-per-bit 증가, 또는 journal 모드 |
| recalculate 완료 안됨 | 대용량 디스크 재계산 | 정상 동작, 백그라운드 진행 대기 |
| mismatch_count 증가 | 하드웨어 결함 또는 AEAD 미커밋 | SMART 확인, UPS 사용, 백업 |
| 메타 디바이스 오류 | 태그 디바이스 장애 | 메타 디바이스 교체, 재포맷 |
에러 복구 메커니즘
dm-integrity의 에러 복구는 모드에 따라 다르게 동작합니다. Journal 모드는 crash-safety가 보장되므로 저널 replay로 복구되지만, Bitmap 모드와 No-journal 모드에서는 데이터 손실이 발생할 수 있습니다.
/* drivers/md/dm-integrity.c - 저널 replay 구현 */
static void replay_journal(struct dm_integrity_c *ic)
{
unsigned int section;
__u64 last_commit_id = 0;
/* 모든 저널 섹션을 스캔 */
for (section = 0; section < ic->journal_sections; section++) {
struct journal_section_header *header;
header = journal_section_header(ic, section);
/* commit_id 유효성 검증 */
if (le64_to_cpu(header->commit_id) != ic->commit_id)
continue; /* 미완료 트랜잭션: 건너뜀 */
/* journal MAC 검증 (설정된 경우) */
if (ic->journal_mac) {
r = verify_journal_mac(ic, section);
if (r) {
DMWARN("journal MAC mismatch in section %u",
section);
continue; /* 변조된 저널 엔트리 */
}
}
/* 유효한 엔트리를 실제 위치에 replay */
for (entry = 0; entry < ic->journal_section_entries; entry++) {
struct journal_entry *je;
je = journal_entry_get(ic, section, entry);
if (journal_entry_is_unused(je))
break;
/* 데이터를 실제 위치에 쓰기 */
write_journal_entry_to_disk(ic, je, section, entry);
}
}
/* commit_id 증가 (다음 사용 준비) */
ic->commit_id++;
write_superblock(ic);
DMINFO("journal replay completed, %u sections replayed",
replayed_count);
}
/* recalculate 모드 - 백그라운드 태그 재계산 */
static void integrity_recalc(struct work_struct *work)
{
struct dm_integrity_c *ic =
container_of(work, struct dm_integrity_c, recalc_work);
sector_t sector = ic->sb->recalc_sector;
while (sector < ic->provided_data_sectors) {
u8 data_buf[PAGE_SIZE];
u8 tag[MAX_TAG_SIZE];
/* 데이터 블록 읽기 */
integrity_read_data(ic, sector, data_buf);
/* 해시 태그 계산 */
crypto_shash_digest(ic->internal_hash_desc,
data_buf, ic->sectors_per_block << SECTOR_SHIFT,
tag);
/* 태그 저장 */
rw_tag(ic, sector, tag, ic->tag_size, TAG_WRITE);
/* 진행 상황 슈퍼블록에 기록 (체크포인트) */
if (!(sector % RECALC_CHECKPOINT_INTERVAL)) {
ic->sb->recalc_sector = cpu_to_le64(sector);
write_superblock(ic);
}
sector += ic->sectors_per_block;
cond_resched(); /* 다른 작업에 양보 */
}
/* 재계산 완료 */
ic->sb->flags &= ~cpu_to_le32(SB_FLAG_RECALCULATING);
write_superblock(ic);
DMINFO("integrity recalculation completed");
}
integrity-recalculate 옵션으로 전체 디스크의 태그를 재계산할 수도 있지만,
이는 기존 태그를 모두 덮어쓰므로 변조 탐지 기능이 일시적으로 무효화됩니다.
dm-flakey / dm-dust
dm-flakey와 dm-dust는 테스트 및 결함 주입(fault injection)용 Device Mapper target입니다. 파일시스템이나 스토리지 스택의 에러 처리 경로를 테스트하는 데 사용됩니다.
/* dm-flakey: 주기적으로 I/O 에러를 발생시킴 */
# 10초 정상 동작 후 5초간 모든 I/O에 에러 반환
$ echo "0 $(blockdev --getsz /dev/sdb1) flakey /dev/sdb1 0 10 5" | \
dmsetup create flakey_disk
# 옵션: 쓰기만 실패, 읽기는 정상
$ echo "0 $(blockdev --getsz /dev/sdb1) flakey /dev/sdb1 0 10 5 \
1 drop_writes" | dmsetup create flakey_write
# dm-flakey 커널 구현 (간략화)
static int flakey_map(struct dm_target *ti, struct bio *bio)
{
struct flakey_c *fc = ti->private;
unsigned elapsed = jiffies - fc->start_time;
if (elapsed % (fc->up_interval + fc->down_interval)
>= fc->up_interval) {
/* down 구간: 에러 반환 또는 데이터 변조 */
if (fc->drop_writes && bio_data_dir(bio) == WRITE) {
bio_endio(bio); /* 쓰기 무시 */
return DM_MAPIO_SUBMITTED;
}
bio->bi_status = BLK_STS_IOERR;
bio_endio(bio);
return DM_MAPIO_SUBMITTED;
}
/* up 구간: 정상 통과 */
flakey_map_bio(ti, bio);
return DM_MAPIO_REMAPPED;
}
/* dm-dust: 특정 블록에 읽기 에러를 주입 */
# dm-dust 디바이스 생성
$ echo "0 $(blockdev --getsz /dev/sdb1) dust /dev/sdb1 0 4096" | \
dmsetup create dusty_disk
# 특정 블록에 bad block 마킹
$ dmsetup message dusty_disk 0 addbadblock 100
$ dmsetup message dusty_disk 0 addbadblock 200
# bad block 활성화
$ dmsetup message dusty_disk 0 enable
# 블록 100, 200 읽기 시 -EIO 반환
# 파일시스템의 에러 핸들링 로직 테스트에 활용
# bad block 제거
$ dmsetup message dusty_disk 0 removebadblock 100
관련 문서
- dm-verity — Merkle Tree 기반 읽기 전용 블록 검증, Android Verified Boot
- dm-crypt — 블록 레벨 투명 암호화, LUKS, AES-XTS, 성능 최적화
- Device Mapper / LVM — DM 프레임워크 기본, dm-linear, dm-thin, LVM2
- 블록 I/O — 블록 레이어 아키텍처, bio 구조, I/O 경로
- 파일시스템 개요 — VFS, 마운트(Mount), superblock
- I/O 스케줄러 — mq-deadline, BFQ, kyber
- NVMe — NVMe 프로토콜, SQ/CQ, 멀티큐
- Linux Crypto Framework — 커널 암호 API, skcipher, AEAD, ahash
- 커널 문서:
Documentation/admin-guide/device-mapper/(integrity) - cryptsetup/LUKS2: gitlab.com/cryptsetup
- dm-integrity 커널 소스:
drivers/md/dm-integrity.c - 커널 문서 — dm-integrity: kernel.org dm-integrity 문서
- dm-integrity 커널 소스 (Bootlin): dm-integrity.c
- LWN.net — dm-integrity 소개: dm-integrity: a new device-mapper target
- cryptsetup 매뉴얼 페이지: man cryptsetup(8)