요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
==============
Data Integrity
==============
1. Introduction
===============
Modern filesystems feature checksumming of data and metadata to
protect against data corruption. However, the detection of the
corruption is done at read time which could potentially be months
after the data was written. At that point the original data that the
application tried to write is most likely lost.
The solution is to ensure that the disk is actually storing what the
application meant it to. Recent additions to both the SCSI family
protocols (SBC Data Integrity Field, SCC protection proposal) as well
as SATA/T13 (External Path Protection) try to remedy this by adding
support for appending integrity metadata to an I/O. The integrity
metadata (or protection information in SCSI terminology) includes a
checksum for each sector as well as an incrementing counter that
ensures the individual sectors are written in the right order. And
for some protection schemes also that the I/O is written to the right
place on disk.
Current storage controllers and devices implement various protective
measures, for instance checksumming and scrubbing. But these
technologies are working in their own isolated domains or at best
between adjacent nodes in the I/O path. The interesting thing about
DIF and the other integrity extensions is that the protection format
is well defined and every node in the I/O path can verify the
integrity of the I/O and reject it if corruption is detected. This
allows not only corruption prevention but also isolation of the point
of failure.
2. The Data Integrity Extensions
================================
As written, the protocol extensions only protect the path between
controller and storage device. However, many controllers actually
allow the operating system to interact with the integrity metadata
(IMD). We have been working with several FC/SAS HBA vendors to enable
the protection information to be transferred to and from their
controllers.
The SCSI Data Integrity Field works by appending 8 bytes of protection
information to each sector. The data + integrity metadata is stored
in 520 byte sectors on disk. Data + IMD are interleaved when
transferred between the controller and target. The T13 proposal is
similar.
Because it is highly inconvenient for operating systems to deal with
520 (and 4104) byte sectors, we approached several HBA vendors and
encouraged them to allow separation of the data and integrity metadata
scatter-gather lists.
The controller will interleave the buffers on write and split them on
read. This means that Linux can DMA the data buffers to and from
host memory without changes to the page cache.
Also, the 16-bit CRC checksum mandated by both the SCSI and SATA specs
is somewhat heavy to compute in software. Benchmarks found that
calculating this checksum had a significant impact on system
performance for a number of workloads. Some controllers allow a
lighter-weight checksum to be used when interfacing with the operating
system. Emulex, for instance, supports the TCP/IP checksum instead.
The IP checksum received from the OS is converted to the 16-bit CRC
when writing and vice versa. This allows the integrity metadata to be
generated by Linux or the application at very low cost (comparable to
software RAID5).
The IP checksum is weaker than the CRC in terms of detecting bit
errors. However, the strength is really in the separation of the data
buffers and the integrity metadata. These two distinct buffers must
match up for an I/O to complete.
The separation of the data and integrity metadata buffers as well as
the choice in checksums is referred to as the Data Integrity
Extensions. As these extensions are outside the scope of the protocol
bodies (T10, T13), Oracle and its partners are trying to standardize
them within the Storage Networking Industry Association.
3. Kernel Changes
=================
The data integrity framework in Linux enables protection information
to be pinned to I/Os and sent to/received from controllers that
support it.
The advantage to the integrity extensions in SCSI and SATA is that
they enable us to protect the entire path from application to storage
device. However, at the same time this is also the biggest
disadvantage. It means that the protection information must be in a
format that can be understood by the disk.
Generally Linux/POSIX applications are agnostic to the intricacies of
the storage devices they are accessing. The virtual filesystem switch
and the block layer make things like hardware sector size and
transport protocols completely transparent to the application.
However, this level of detail is required when preparing the
protection information to send to a disk. Consequently, the very
concept of an end-to-end protection scheme is a layering violation.
It is completely unreasonable for an application to be aware whether
it is accessing a SCSI or SATA disk.
The data integrity support implemented in Linux attempts to hide this
from the application. As far as the application (and to some extent
the kernel) is concerned, the integrity metadata is opaque information
that's attached to the I/O.
The current implementation allows the block layer to automatically
generate the protection information for any I/O. Eventually the
intent is to move the integrity metadata calculation to userspace for
user data. Metadata and other I/O that originates within the kernel
will still use the automatic generation interface.
Some storage devices allow each hardware sector to be tagged with a
16-bit value. The owner of this tag space is the owner of the block
device. I.e. the filesystem in most cases. The filesystem can use
this extra space to tag sectors as they see fit. Because the tag
space is limited, the block interface allows tagging bigger chunks by
way of interleaving. This way, 8*16 bits of information can be
attached to a typical 4KB filesystem block.
This also means that applications such as fsck and mkfs will need
access to manipulate the tags from user space. A passthrough
interface for this is being worked on.
4. Block Layer Implementation Details
=====================================
4.1 Bio
-------
The data integrity patches add a new field to struct bio when
CONFIG_BLK_DEV_INTEGRITY is enabled. bio_integrity(bio) returns a
pointer to a struct bip which contains the bio integrity payload.
Essentially a bip is a trimmed down struct bio which holds a bio_vec
containing the integrity metadata and the required housekeeping
information (bvec pool, vector count, etc.)
A kernel subsystem can enable data integrity protection on a bio by
calling bio_integrity_alloc(bio). This will allocate and attach the
bip to the bio.
Individual pages containing integrity metadata can subsequently be
attached using bio_integrity_add_page().
bio_free() will automatically free the bip.
4.2 Block Device
----------------
Block devices can set up the integrity information in the integrity
sub-struture of the queue_limits structure.
Layered block devices will need to pick a profile that's appropriate
for all subdevices. queue_limits_stack_integrity() can help with that. DM
and MD linear, RAID0 and RAID1 are currently supported. RAID4/5/6
will require extra work due to the application tag.
5.0 Block Layer Integrity API
=============================
5.1 Normal Filesystem
---------------------
The normal filesystem is unaware that the underlying block device
is capable of sending/receiving integrity metadata. The IMD will
be automatically generated by the block layer at submit_bio() time
in case of a WRITE. A READ request will cause the I/O integrity
to be verified upon completion.
IMD generation and verification can be toggled using the::
/sys/block/<bdev>/integrity/write_generate
and::
/sys/block/<bdev>/integrity/read_verify
flags.
5.2 Integrity-Aware Filesystem
------------------------------
A filesystem that is integrity-aware can prepare I/Os with IMD
attached. It can also use the application tag space if this is
supported by the block device.
`bool bio_integrity_prep(bio);`
To generate IMD for WRITE and to set up buffers for READ, the
filesystem must call bio_integrity_prep(bio).
Prior to calling this function, the bio data direction and start
sector must be set, and the bio should have all data pages
added. It is up to the caller to ensure that the bio does not
change while I/O is in progress.
Complete bio with error if prepare failed for some reason.
5.3 Passing Existing Integrity Metadata
---------------------------------------
Filesystems that either generate their own integrity metadata or
are capable of transferring IMD from user space can use the
following calls:
`struct bip * bio_integrity_alloc(bio, gfp_mask, nr_pages);`
Allocates the bio integrity payload and hangs it off of the bio.
nr_pages indicate how many pages of protection data need to be
stored in the integrity bio_vec list (similar to bio_alloc()).
The integrity payload will be freed at bio_free() time.
`int bio_integrity_add_page(bio, page, len, offset);`
Attaches a page containing integrity metadata to an existing
bio. The bio must have an existing bip,
i.e. bio_integrity_alloc() must have been called. For a WRITE,
the integrity metadata in the pages must be in a format
understood by the target device with the notable exception that
the sector numbers will be remapped as the request traverses the
I/O stack. This implies that the pages added using this call
will be modified during I/O! The first reference tag in the
integrity metadata must have a value of bip->bip_sector.
Pages can be added using bio_integrity_add_page() as long as
there is room in the bip bio_vec array (nr_pages).
Upon completion of a READ operation, the attached pages will
contain the integrity metadata received from the storage device.
It is up to the receiver to process them and verify data
integrity upon completion.
----------------------------------------------------------------------
2007-12-24 Martin K. Petersen <[email protected]>
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
data integrity가 필요한 이유
1-34modern filesystem은 data와 metadata에 checksum을 적용해 data corruption을 막습니다. 그러나 corruption은 read 시점에 검출되며, data를 쓴 뒤 수개월이 지난 시점일 수도 있습니다. 그때는 application이 원래 쓰려던 data가 이미 사라졌을 가능성이 큽니다.
해법은 disk가 application의 의도와 같은 내용을 실제로 저장하는지 보장하는 것입니다. SCSI 계열 protocol의 `SBC Data Integrity Field`, `SCC protection proposal`과 SATA/T13의 `External Path Protection`은 I/O에 integrity metadata를 덧붙이는 방식으로 이를 해결합니다.
SCSI 용어로 protection information이라고도 하는 integrity metadata에는 sector마다 checksum과 증가하는 counter가 들어 있습니다. counter는 각 sector가 올바른 순서로 기록되었음을 보장하며, 일부 protection scheme에서는 I/O가 disk의 올바른 위치에 기록되었는지도 확인합니다.
현재 storage controller와 device도 checksumming, scrubbing 같은 여러 보호 수단을 구현합니다. 그러나 이 기술은 각자의 격리된 domain에서만, 잘해도 I/O path의 인접 node 사이에서만 동작합니다.
`DIF`와 다른 integrity extension의 중요한 점은 protection format이 명확히 정의되어 I/O path의 모든 node가 I/O integrity를 검증하고 corruption을 발견하면 거부할 수 있다는 것입니다. 따라서 corruption을 예방할 뿐 아니라 failure가 발생한 지점을 격리할 수 있습니다.
Data Integrity Extensions
35-81protocol extension 자체는 controller와 storage device 사이의 path만 보호합니다. 하지만 많은 controller가 operating system이 integrity metadata, 즉 `IMD`와 상호 작용하도록 지원합니다. 여러 FC/SAS HBA vendor와 협력해 protection information을 controller로 주고받을 수 있게 했습니다.
`SCSI Data Integrity Field`는 sector마다 protection information 8 byte를 덧붙입니다. data와 integrity metadata는 disk의 520 byte sector에 저장되고, controller와 target 사이를 전송할 때 data와 IMD를 interleave합니다. T13 proposal도 비슷합니다.
operating system에서 520 byte, 그리고 4104 byte sector를 다루기는 매우 불편합니다. 이에 여러 HBA vendor가 data scatter-gather list와 integrity metadata scatter-gather list를 분리하도록 권장했습니다.
controller는 write할 때 두 buffer를 interleave하고 read할 때 분리합니다. 덕분에 Linux는 page cache를 바꾸지 않고 data buffer를 host memory와 주고받도록 DMA할 수 있습니다.
SCSI와 SATA specification이 요구하는 16-bit CRC checksum은 software로 계산하기에 다소 무겁습니다. benchmark에서는 여러 workload에서 이 checksum 계산이 system performance에 상당한 영향을 미쳤습니다.
일부 controller는 operating system과 통신할 때 더 가벼운 checksum을 허용합니다. 예를 들어 Emulex는 대신 TCP/IP checksum을 지원합니다. OS에서 받은 IP checksum을 write할 때 16-bit CRC로 변환하고 read할 때는 반대로 변환합니다. 따라서 Linux나 application이 software RAID5와 비슷한 매우 낮은 비용으로 integrity metadata를 생성할 수 있습니다.
bit error 검출 능력은 IP checksum이 CRC보다 약합니다. 그러나 실제 강점은 data buffer와 integrity metadata를 분리하는 데 있습니다. I/O가 완료되려면 서로 분리된 두 buffer가 반드시 서로 일치해야 합니다.
data buffer와 integrity metadata buffer의 분리, 그리고 checksum 선택 기능을 합쳐 `Data Integrity Extensions`라고 합니다. 이 extension은 protocol 단체인 T10과 T13의 범위 밖에 있으므로 Oracle과 partner들은 Storage Networking Industry Association에서 표준화를 추진하고 있습니다.
Linux kernel의 추상화
82-129Linux data integrity framework는 protection information을 I/O에 고정해 이를 지원하는 controller로 보내거나 controller에서 받게 합니다.
SCSI와 SATA integrity extension은 application부터 storage device까지 전체 path를 보호한다는 장점이 있습니다. 동시에 이것이 가장 큰 단점이기도 합니다. protection information이 disk가 이해할 수 있는 format이어야 하기 때문입니다.
일반적으로 Linux/POSIX application은 접근하는 storage device의 세부 사항을 알지 못합니다. virtual filesystem switch와 block layer는 hardware sector size와 transport protocol 같은 요소를 application에 완전히 투명하게 만듭니다.
그러나 disk에 보낼 protection information을 준비할 때는 이런 세부 정보가 필요합니다. 따라서 end-to-end protection scheme이라는 개념 자체가 layering violation입니다. application이 SCSI disk에 접근하는지 SATA disk에 접근하는지 알아야 한다는 것은 합리적이지 않습니다.
Linux의 data integrity 지원은 이런 내용을 application에서 숨깁니다. application과 어느 정도 kernel의 관점에서 integrity metadata는 I/O에 붙은 opaque information입니다.
현재 구현에서는 block layer가 모든 I/O의 protection information을 자동으로 생성할 수 있습니다. 최종 목표는 user data의 integrity metadata 계산을 userspace로 옮기는 것입니다. metadata와 kernel 내부에서 시작된 다른 I/O는 계속 automatic generation interface를 사용합니다.
일부 storage device는 각 hardware sector에 16-bit value tag를 붙일 수 있습니다. 이 tag space의 소유자는 block device 소유자이며 대부분 filesystem입니다. filesystem은 이 추가 공간을 원하는 방식으로 sector tagging에 사용할 수 있습니다.
tag space가 제한되어 있으므로 block interface는 interleaving으로 더 큰 chunk를 tagging하게 합니다. 이 방식이면 일반적인 4KB filesystem block에 `8*16 bits`의 정보를 붙일 수 있습니다.
이는 `fsck`, `mkfs` 같은 application도 userspace에서 tag를 조작할 수 있어야 한다는 뜻입니다. 이를 위한 passthrough interface를 개발 중입니다.
Bio integrity payload
130-152`CONFIG_BLK_DEV_INTEGRITY`가 enabled이면 data integrity patch가 `struct bio`에 새 field를 추가합니다. `bio_integrity(bio)`는 bio integrity payload를 포함하는 `struct bip` pointer를 반환합니다.
`bip`는 간소화된 `struct bio`와 본질적으로 같습니다. integrity metadata를 담은 `bio_vec`와 bvec pool, vector count 같은 필수 관리 정보를 보관합니다.
kernel subsystem은 `bio_integrity_alloc(bio)`를 호출해 `bio`의 data integrity protection을 활성화할 수 있습니다. 이 함수는 `bip`를 할당해 `bio`에 연결합니다.
그 뒤 `bio_integrity_add_page()`로 integrity metadata가 든 개별 page를 연결할 수 있습니다. `bio_free()`는 `bip`를 자동으로 free합니다.
Block device profile
153-164block device는 `queue_limits` 구조체의 integrity sub-structure에 integrity information을 설정할 수 있습니다.
layered block device는 모든 subdevice에 적합한 profile을 선택해야 하며 `queue_limits_stack_integrity()`가 이를 도울 수 있습니다. 현재 DM과 MD linear, RAID0, RAID1을 지원합니다. RAID4/5/6은 application tag 때문에 추가 작업이 필요합니다.
일반 filesystem API
165-187일반 filesystem은 underlying block device가 integrity metadata를 송수신할 수 있다는 사실을 알지 못합니다. WRITE라면 `submit_bio()` 시점에 block layer가 IMD를 자동 생성하고, READ request라면 완료 시 I/O integrity를 검증합니다.
IMD 생성을 다음 flag로 켜거나 끌 수 있습니다.
/sys/block/<bdev>/integrity/write_generate
IMD 검증은 다음 flag로 켜거나 끌 수 있습니다.
/sys/block/<bdev>/integrity/read_verify
integrity-aware filesystem API
188-207integrity-aware filesystem은 IMD가 연결된 I/O를 준비할 수 있으며 block device가 지원하면 application tag space도 사용할 수 있습니다.
`bool bio_integrity_prep(bio);`
WRITE용 IMD를 생성하고 READ용 buffer를 설정하려면 filesystem이 `bio_integrity_prep(bio)`를 호출해야 합니다. 호출하기 전에 bio data direction과 start sector를 설정하고 모든 data page를 `bio`에 추가해야 합니다.
I/O가 진행되는 동안 `bio`가 변하지 않도록 보장하는 책임은 caller에게 있습니다. 어떤 이유로 prepare가 실패하면 해당 `bio`를 error로 완료합니다.
기존 IMD 전달: payload 할당
208-224자체 integrity metadata를 생성하거나 userspace에서 IMD를 전달할 수 있는 filesystem은 다음 호출을 사용할 수 있습니다.
`struct bip * bio_integrity_alloc(bio, gfp_mask, nr_pages);`
`bio_integrity_alloc(bio, gfp_mask, nr_pages)`는 bio integrity payload를 할당해 `bio`에 연결합니다. `nr_pages`는 protection data를 integrity `bio_vec` list에 저장하는 데 필요한 page 수이며 `bio_alloc()`의 동작과 비슷합니다.
integrity payload는 `bio_free()` 시점에 free됩니다.
기존 IMD 전달: page 연결
225-248`int bio_integrity_add_page(bio, page, len, offset);`
`bio_integrity_add_page(bio, page, len, offset)`는 integrity metadata가 든 page를 기존 `bio`에 연결합니다. `bio`에는 기존 `bip`가 있어야 하므로 먼저 `bio_integrity_alloc()`을 호출해야 합니다.
WRITE의 경우 page 안 integrity metadata는 target device가 이해하는 format이어야 합니다. 단, request가 I/O stack을 통과하는 동안 sector number는 remap됩니다. 따라서 이 호출로 추가한 page는 I/O 중에 수정됩니다. integrity metadata의 첫 reference tag 값은 반드시 `bip->bip_sector`여야 합니다.
`bip`의 `bio_vec` array에 `nr_pages` 범위의 공간이 남아 있는 동안 `bio_integrity_add_page()`로 page를 추가할 수 있습니다.
READ operation이 완료되면 연결된 page에는 storage device에서 받은 integrity metadata가 들어 있습니다. 완료 시 이를 처리하고 data integrity를 검증하는 책임은 receiver에게 있습니다.
원문 기록: `2007-12-24 Martin K. Petersen <[email protected]>`.
요약과 해설
data-integrity.rst:1-248Linux data integrity framework는 sector checksum·순서·위치 정보를 I/O와 함께 전달해 application에서 storage device까지 corruption을 검출하고 failure 지점을 격리합니다.
block layer는 일반 filesystem을 위해 IMD를 자동 생성·검증할 수 있고, integrity-aware filesystem에는 `bio_integrity_prep()`, `bio_integrity_alloc()`, `bio_integrity_add_page()` API를 제공합니다.