이 코드는 어떤 문제를 푸나요?
파티션 표는 디스크의 어떤 범위를 독립된 구간으로 다룰지 기록한 메타데이터입니다. 표를 읽었다고 각 파티션 장치가 저절로 생기지는 않습니다. blk_add_partitions는 읽기 결과를 검사한 뒤 파티션을 등록하며, 장치 용량 때문에 다시 읽어야 할 때와 실제 오류도 구분합니다.
읽을 범위: v6.6 · block/partitions/core.c · blk_add_partitions 585–647행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.
먼저 알아둘 개념
gendisk와 파티션
gendisk는 디스크를 표현하고 파티션은 그 안의 구간을 나타냅니다. 파티션의 시작 섹터와 길이는 파일시스템의 파일 경로와 다른 층의 정보입니다.
오류 포인터
커널은 포인터 반환형에 ERR_PTR로 오류 번호를 담기도 합니다. IS_ERR로 검사하기 전에는 일반 구조체처럼 역참조해서는 안 됩니다.
EOD와 재시도
EOD는 현재 알려진 장치 끝입니다. 원래 용량을 다시 활성화할 수 있는 경우 -EAGAIN으로 더 큰 용량에서의 재시도를 요청합니다.
처음 읽을 때
파티션 표를 읽는 단계와 읽은 결과로 파티션 장치를 만드는 단계를 나누십시오. 예시로 시작 섹터 2048을 가정할 수 있지만, 이 코드가 모든 파티션을 그 위치에 만드는 것은 아닙니다.
더 깊이 살펴볼 때
NULL, 오류 포인터, 유효한 parsed_partitions의 세 반환 형태를 구분하고 각 경로의 해제 책임을 확인하십시오. 이미 상태 객체를 얻었다면 실패와 성공이 out_free_state에서 합쳐집니다.
그림으로 보는 변화

1. 표를 읽습니다
장치가 파티션 검색 대상이면 check_partition으로 표를 해석합니다.
화살표는 저장장치 메타데이터를 메모리 상태 객체로 읽는 방향입니다.
2. 검색 결과를 판단합니다
표 없음, 읽기 오류, 장치 끝 초과, 지원하지 않는 zoned 구성을 구분합니다.
되돌아가는 화살표는 용량을 확인한 후의 재시도 요청입니다.
3. 파티션을 등록합니다
유효한 항목을 순서대로 등록한 뒤 임시 해석 결과를 해제합니다.
화살표는 해석된 항목에서 커널 파티션 객체로의 등록입니다.
blk_add_partitions를 한 줄씩 읽기
줄 번호는 v6.6 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.
static int blk_add_partitions(struct gendisk *disk)
{
struct parsed_partitions *state;
int ret = -EAGAIN, p;
if (disk->flags & GENHD_FL_NO_PART)
return 0;
if (test_bit(GD_SUPPRESS_PART_SCAN, &disk->state))
return 0;
state = check_partition(disk);
if (!state)
return 0;
if (IS_ERR(state)) {
/*
* I/O error reading the partition table. If we tried to read
* beyond EOD, retry after unlocking the native capacity.
*/
if (PTR_ERR(state) == -ENOSPC) {
printk(KERN_WARNING "%s: partition table beyond EOD, ",
disk->disk_name);
if (disk_unlock_native_capacity(disk))
return -EAGAIN;
}
return -EIO;
}
/*
* Partitions are not supported on host managed zoned block devices.
*/
if (disk->queue->limits.zoned == BLK_ZONED_HM) {
pr_warn("%s: ignoring partition table on host managed zoned block device\n",
disk->disk_name);
ret = 0;
goto out_free_state;
}
/*
* If we read beyond EOD, try unlocking native capacity even if the
* partition table was successfully read as we could be missing some
* partitions.
*/
if (state->access_beyond_eod) {
printk(KERN_WARNING
"%s: partition table partially beyond EOD, ",
disk->disk_name);
if (disk_unlock_native_capacity(disk))
goto out_free_state;
}
/* tell userspace that the media / partition table may have changed */
kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE);
for (p = 1; p < state->limit; p++)
if (!blk_add_partition(disk, state, p))
goto out_free_state;
ret = 0;
out_free_state:
free_partitions(state);
return ret;
}static int blk_add_partitions(struct gendisk *disk)파티션을 검색하고 등록할 디스크를 받으며, 결과를 정수 상태 코드로 반환합니다.
struct parsed_partitions *state;파티션 표를 해석한 임시 상태 객체를 가리킵니다.
int ret = -EAGAIN, p;초기 결과를 재시도 오류로 설정합니다. p는 등록할 파티션 번호입니다.
if (disk->flags & GENHD_FL_NO_PART)디스크에 파티션을 사용하지 않는 GENHD_FL_NO_PART 플래그가 있는지 확인합니다.
return 0;파티션을 사용하지 않는 디스크이므로 표 검색 없이 정상 반환합니다.
if (test_bit(GD_SUPPRESS_PART_SCAN, &disk->state))현재 디스크 상태에 파티션 검색을 억제하는 GD_SUPPRESS_PART_SCAN 비트가 설정됐는지 확인합니다.
return 0;검색을 억제한 상태이면 파티션을 추가하지 않고 정상 반환합니다.
state = check_partition(disk);지원하는 파티션 형식의 해석기로 디스크의 표를 읽습니다.
if (!state)해석할 파티션 정보가 없다는 NULL 결과인지 검사합니다.
return 0;표가 없다는 사실 자체를 I/O 오류로 만들지 않습니다.
if (IS_ERR(state)) {포인터처럼 보이는 반환값이 사실 오류 번호인지 확인합니다.
if (PTR_ERR(state) == -ENOSPC) {읽으려던 표가 알려진 장치 용량을 넘어갔다는 -ENOSPC인지 구분합니다.
printk(KERN_WARNING "%s: partition table beyond EOD, ",장치 끝을 넘는 파티션 표가 발견됐다는 경고 출력을 시작합니다.
disk->disk_name);경고에 실제 디스크 이름을 넣습니다.
if (disk_unlock_native_capacity(disk))숨겨진 원래 장치 용량을 활성화할 수 있었는지 확인합니다.
return -EAGAIN;용량 조건이 바뀌었으므로 호출자에게 다시 검색하도록 -EAGAIN을 반환합니다.
return -EIO;이 경로에서 해결할 수 없는 파티션 표 읽기 실패는 -EIO로 알립니다.
if (disk->queue->limits.zoned == BLK_ZONED_HM) {큐의 zoned 종류가 host-managed인지 확인합니다. v6.6의 이 분기는 모든 zoned 종류를 무조건 배제하는 조건이 아닙니다.
pr_warn("%s: ignoring partition table on host managed zoned block device\n",이 장치의 파티션 표를 무시한다는 경고를 출력합니다.
disk->disk_name);경고 메시지의 대상 장치 이름을 전달합니다.
ret = 0;표를 무시하는 정책으로 처리했으므로 정상 결과를 선택합니다.
goto out_free_state;이미 할당한 파티션 해석 상태를 해제하는 위치로 갑니다.
if (state->access_beyond_eod) {표 해석이 끝났어도 장치 끝을 넘은 접근이 있었는지 확인합니다.
printk(KERN_WARNING부분적으로 장치 끝을 넘는 경우의 경고를 시작합니다.
"%s: partition table partially beyond EOD, ",파티션 표 일부가 현재 용량 밖이었다는 문구입니다.
disk->disk_name);어떤 디스크에서 발생했는지 이름을 출력합니다.
if (disk_unlock_native_capacity(disk))이 경우에도 원래 용량을 다시 사용할 수 있는지 시도합니다.
goto out_free_state;용량을 활성화했으면 초기값 -EAGAIN을 유지하며 임시 상태를 해제합니다.
kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE);매체나 파티션 표가 바뀌었을 수 있음을 사용자 공간에 KOBJ_CHANGE 이벤트로 알립니다.
for (p = 1; p < state->limit; p++)전체 디스크를 나타내는 0번을 제외하고 해석 가능한 파티션 항목을 순회합니다.
if (!blk_add_partition(disk, state, p))해당 파티션의 범위를 확인하고 등록을 시도합니다. 하위 함수가 원래 용량을 활성화해 재검색이 필요해지면 거짓을 반환합니다.
goto out_free_state;용량을 다시 확인해야 하므로 초기 결과 -EAGAIN을 유지한 채 임시 상태를 해제합니다.
ret = 0;모든 항목 처리를 마쳐 성공값 0을 설정합니다.
out_free_state:유효한 해석 상태를 얻은 경로들이 모이는 정리 위치입니다.
free_partitions(state);임시 파티션 해석 결과를 해제합니다. 이미 등록한 장치 객체와는 구분됩니다.
return ret;성공 또는 재시도 상태를 호출자에게 전달합니다.
함께 생각해 볼 질문
표가 없으면 반드시 오류인가요?
이 함수는 해석 결과가 NULL이면 0을 반환합니다. 파티션 없는 장치를 사용할 수도 있기 때문입니다.
파티션 번호가 1부터인 이유는 무엇인가요?
이 등록 순회는 개별 파티션을 대상으로 하며 전체 디스크를 나타내는 part0과 구분합니다.
왜 EAGAIN과 EIO를 구분하나요?
용량을 다시 확인하면 성공할 수 있는 상황과 읽기 실패로 끝낼 상황을 호출자에게 다르게 알려야 합니다.
출처와 읽은 범위
Linux stable v6.6 · block/partitions/core.c
해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고
