kernel code 분석중에, 특정 BIO를 클론하여 submit하는 역할을 하는 함수가 있었다. clone된 bio는 원래 bio의 일부만 처리할 수 있다. 여기서 왜 bio를 clone하고 분할 처리를 하는지에 대해 설명을 할 예정이다.
bio를 클론해서 submit하는 이유는 주로 asynchronous I/O 처리와 원본 bio의 상태 보존때문이다. 이 방법은 특히 kernel 내에서 복잡한 I/O 작업을 처리할 때 유용하다. 그 이유는:
bio를 클론하면, 클론된 bio는 원본과 독립적으로 비동기적으로 처리될 수 있다. 이는 원본 bio의 처리와는 별로도 클론된 bio가 I/O 작업을 수행할 수 있다는 의미이다. 이 방식은 I/O 작업을 병렬로 처리하거나, 특정 조건 하에서만 일부 작업을 따로 처리하고자 할 때 유용하다.
예를 들어, seq-zone에서는 data를 반드시 순차적으로 써야한다. 이때 특정 블록을 따로 클론해서 독립적으로 처리함으로써, 복잡한 sequential write 작업을 더 효율적으로 관리할 수 있다.
클론을 사용하면 원본 bio의 상태를 그대로 보존할 수 있다. bio는 kernel에서 block i/o 요청을 표현하는 구조체인데, error, scheduling, device driver, caching 등의 이유로 bio 구조체가 변경이 되면 원본 bio에 대한 정보가 손실될 수 있다. 특히, 복잡한 i/o 작업에서 여러 단계를 거치면서 bio가 여러 번 수정될 수 있기 때문에 원본 정보를 보존하는 것이 매우 중요하다.
다음은 이에 대한 예시 kernel 코드이다.
/*
* Issue a clone of a target BIO. The clone may only partially process the
* original target BIO.
*/
static int dmz_submit_bio(struct dmz_target * dmz, struct dm_zone * zone,
struct bio * bio, sector_t chunk_block,
unsigned int nr_blocks) {
struct dmz_bioctx * bioctx =
dm_per_bio_data(bio, sizeof(struct dmz_bioctx));
struct dmz_dev * dev = zone - > dev;
struct bio * clone;
if (dev - > flags & DMZ_BDEV_DYING) return -EIO;
clone = bio_clone_fast(bio, GFP_NOIO, & dmz - > bio_set);
if (!clone) return -ENOMEM;
bio_set_dev(clone, dev - > bdev);
bioctx - > dev = dev;
clone - > bi_iter.bi_sector = dmz_start_sect(dmz - > metadata, zone) + dmz_blk2sect(chunk_block);
clone - > bi_iter.bi_size = dmz_blk2sect(nr_blocks) << SECTOR_SHIFT;
clone - > bi_end_io = dmz_clone_endio;
clone - > bi_private = bioctx;
bio_advance(bio, clone - > bi_iter.bi_size);
refcount_inc( & bioctx - > ref);
submit_bio_noacct(clone);
if (bio_op(bio) == REQ_OP_WRITE && dmz_is_seq(zone)) zone - > wp_block += nr_blocks;
return 0;
}
위 함수는 zoned block device와 관련된 bio 작업에서 특정 bio를 클론해서 submit하는 역할을 한다. 주로, bio 작업을 수행할 때, 원본 bio를 복사하고, 복사된 bio를 장치에 제출하여 작업을 수행한다.
dmz_target
ZBD와 관련된 target 구조체이다. bio 작업을 수행하는데 필요한 다양한 metadata와 설정을 포함한다.
dm-zone
bio 작업이 수행될 특정 zone을 나타낸다.
struct bio *bio
이 함수가 처리할 원본 bio 요청이다. bio는 kernel에서 block device에 대한 i/o 요청을 나타내는 구조체
sector_t chunk_block
zone에서 bio 작업이 시작될 블록의 위치를 나타낸다.
nr_blocks
이 bio 요청에서 처리할 블록의 수
dmz_bioctx 초기화
bio 작업의 context를 나타내는 구조체로, dm_per_bio_data를 사용해서 bio에 할당된 메모리에서 이 context를 가져온다.
struct dmz_dev *dev = zone->dev 를 통해 해당 zone이 속한 device를 가져온다.
bio 클론 생성
bio_clone_fast를 사용해서 원본 bio clone을 생성한다. 이 함수는 원본 bio의 내용을 복사해서 새로운 bio 구조체를 생성한다.
bio clone 설정
bio_set_dev를 사용해서 clone된 bio가 작동할 block device를 설정
/*
* Zone descriptor.
*/
struct dm_zone {
/* For listing the zone depending on its state */
struct list_head link;
/* Device containing this zone */
struct dmz_dev * dev;
/* Zone type and state */
unsigned long flags;
/* Zone activation reference count */
atomic_t refcount;
/* Zone id */
unsigned int id;
/* Zone write pointer block (relative to the zone start block) */
unsigned int wp_block;
/* Zone weight (number of valid blocks in the zone) */
unsigned int weight;
/* The chunk that the zone maps */
unsigned int chunk;
/*
* For a sequential data zone, pointer to the random zone
* used as a buffer for processing unaligned writes.
* For a buffer zone, this points back to the data zone.
*/
struct dm_zone * bzone;
};
dm_zoned 구조체는 zoned block device에서 특정 zone에 대한 정보를 저장하고 관리하는데 사용되는 자료 구조이다. 이 구조체는 각 존에 대한 다양한 메타데이터와 상태 정보를 담고 있으며, zoned block device의 효율적인 I/O 작업 관리를 돕는다.
struct list_head
현재 zone의 상태에 따라 다른 zone들과 연결하기 위한 list node이다. kernel에서 사용하는 이중 연결 리스트의 구조로, 특정 상태에 있는 여러 zone들을 관리하는데 사용된다.
struct dmz_dev
이 zone이 속한 block device를 가리킨다. 이 zone이 어느 장치에 속해있는지, 어떤 장치와 관련된 작업인지 알 수 있게 한다.
unsigned long flags
zone의 타입과 상태를 나타내는 flag를 저장한다. 예를 들면, 이 flag를 통해 해당 zone이 seq-zone인지 conv-zone인지 등의 정보를 관리할 수 있다.
atomic_t refcount
zone의 활성화 참조 카운터이다. 특정 zone이 활성화된 상태에서 여러 작업에 의해 참조될 때, 이 카운터를 통해 참조 횟수를 추적하고, 마지막 참조가 해제되면 zone을 비활성화할 수 있다.
unsigned int id
zone의 고유 식별자( zone id)를 나타낸다. 각 zone을 고유하게 식별하기 위한 ID이다.
unsigned int wp_block
write pointer의 위치를 나타낸다. seq-zone의 경우, data는 반드시 sequential 하게 쓰여야 하므로, 현재까지 쓰여진 마지막 블록의 위치를 나타낸다. wp_block은 zone의 시작 블록을 기준으로 상대적인 위치를 가리킨다.
unsigned int weight
zone 내의 valid block의 수를 나타낸다. 즉, zone에서 실제로 데이터가 쓰여져서 valid 상태로 남아 있는 block의 개수를 의미한다.
unsigned int chunk
이 zone이 매핑되는 chunk를 나타낸다. chunk는 ZBD에서 zone을 더 작은 단위로 나누어 관리하는 개념으로, 특정 zone이 어느 청크에 속해 있는지를 나타낸다.
struct dm_zone
이 멤버는 seq-zone이 conv-zone을 buffer로 사용해서 정렬 되지 않은 write를 처리할 때 사용된다.
seq-zone의 경우, 정렬되지 않은 write 작업을 처리하기 위해 conv-zone을 임시 buffer로 사용할 수 있다. 이 경우 conv-zone을 가리킨다.
conv-zone이 buffer로 사용되는 경우, 이 멤버는 그 conv-zone이 buffering하는 원래의 seq-zone을 가리킨다.