# bio: 저장장치 작업의 완료를 누가 받는가

v6.6 / block/bio.c

파일을 읽으려면 어느 장치의 어느 구간을 어느 메모리에 옮길지 정해야 합니다. bio는 이 정보를 묶어 아래 계층에 전달하는 객체입니다. 제출한 함수가 돌아왔다고 디스크 작업이 끝난 것은 아닙니다. 여기서는 bio_endio 전체를 따라가며 여러 조각의 완료가 합쳐지고 마지막에 원래 요청자에게 알려지는 과정을 설명합니다.

## bio_endio

```c

void bio_endio(struct bio *bio)
{
again:
	if (!bio_remaining_done(bio))
		return;
	if (!bio_integrity_endio(bio))
		return;

	rq_qos_done_bio(bio);

	if (bio->bi_bdev && bio_flagged(bio, BIO_TRACE_COMPLETION)) {
		trace_block_bio_complete(bdev_get_queue(bio->bi_bdev), bio);
		bio_clear_flag(bio, BIO_TRACE_COMPLETION);
	}

	/*
	 * Need to have a real endio function for chained bios, otherwise
	 * various corner cases will break (like stacking block devices that
	 * save/restore bi_end_io) - however, we want to avoid unbounded
	 * recursion and blowing the stack. Tail call optimization would
	 * handle this, but compiling with frame pointers also disables
	 * gcc's sibling call optimization.
	 */
	if (bio->bi_end_io == bio_chain_endio) {
		bio = __bio_chain_endio(bio);
		goto again;
	}

	blk_throtl_bio_endio(bio);
	/* release cgroup info */
	bio_uninit(bio);
	if (bio->bi_end_io)
		bio->bi_end_io(bio);
}

```

### 1571행

```c

void bio_endio(struct bio *bio)

```

완료할 bio 포인터를 받습니다. 반환값 없이 bio의 상태와 콜백을 통해 결과를 전달합니다.

### 1573행

```c

again:

```

연결된 부모 bio를 같은 함수 본문에서 다시 처리하기 위한 돌아올 위치입니다.

### 1574행

```c

	if (!bio_remaining_done(bio))

```

연결된 bio라면 남은 완료 계수를 하나 줄이고 0이 됐는지 검사합니다. 연결되지 않은 bio는 첫 완료에서 진행하며, 아직 조각이 남았다면 마지막 콜백을 미룹니다.

### 1575행

```c

		return;

```

기다릴 완료가 남았으므로 여기서 종료합니다.

### 1576행

```c

	if (!bio_integrity_endio(bio))

```

무결성 정보가 있는 bio의 완료 처리를 진행합니다. 이 함수가 아직 후속 처리가 필요하다고 알리면 아래 절차를 미룹니다.

### 1577행

```c

		return;

```

무결성 처리가 완료 통지를 이어 맡을 수 있으므로 여기서 돌아갑니다.

### 1579행

```c

	rq_qos_done_bio(bio);

```

이 bio를 추적한 I/O 서비스 품질 제어 계층에 완료를 알립니다.

### 1581행

```c

	if (bio->bi_bdev && bio_flagged(bio, BIO_TRACE_COMPLETION)) {

```

실제 대상 장치가 있고 완료 추적 플래그도 켜졌을 때만 추적 기록을 남깁니다.

### 1582행

```c

		trace_block_bio_complete(bdev_get_queue(bio->bi_bdev), bio);

```

대상 장치의 큐와 bio를 사용해 block_bio_complete 추적 이벤트를 기록합니다.

### 1583행

```c

		bio_clear_flag(bio, BIO_TRACE_COMPLETION);

```

동일 bio의 완료 추적을 반복하지 않도록 플래그를 지웁니다.

### 1594행

```c

	if (bio->bi_end_io == bio_chain_endio) {

```

콜백이 일반 요청자의 함수인지, 부모로 완료를 전달하는 연결용 함수인지 구분합니다.

### 1595행

```c

		bio = __bio_chain_endio(bio);

```

자식의 오류를 필요하면 부모에 전달하고 자식 bio의 참조를 내려놓습니다. 이어 처리할 부모 bio를 받아 현재 대상에 넣습니다.

### 1596행

```c

		goto again;

```

C 함수를 재귀 호출하지 않고 부모에 대해 남은 완료 검사부터 반복합니다.

### 1599행

```c

	blk_throtl_bio_endio(bio);

```

블록 I/O 속도 제한 계층에 bio 완료를 알립니다. 이 호출은 v6.6 완료 경로에 있으며 요청 데이터의 성공 여부를 새로 결정하는 것은 아닙니다.

### 1601행

```c

	bio_uninit(bio);

```

bio_uninit으로 cgroup 참조, 무결성 정보와 암호화 문맥 등 bio에 붙은 보조 자원을 정리합니다. bio 구조체 자체를 곧바로 해제하는 함수와 구분합니다.

### 1602행

```c

	if (bio->bi_end_io)

```

완료 함수를 지정한 bio인지 확인합니다.

### 1603행

```c

		bio->bi_end_io(bio);

```

원래 요청자에게 완료를 알립니다. 이 호출 안에서 bio가 해제될 수도 있습니다.

