Linux v6.18.37 · 개념과 코드 읽기

파티션 검색: 디스크 구간을 장치로 등록하기까지

이 코드는 어떤 문제를 푸나요?

파티션 표는 디스크의 어떤 범위를 독립된 구간으로 다룰지 기록한 메타데이터입니다. 표를 읽었다고 각 파티션 장치가 저절로 생기지는 않습니다. blk_add_partitions는 읽기 결과를 검사한 뒤 파티션을 등록하며, 장치 용량 때문에 다시 읽어야 할 때와 실제 오류도 구분합니다.

읽을 범위: v6.18.37 · block/partitions/core.c · blk_add_partitions 581–640행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.

먼저 알아둘 개념

gendisk와 파티션

gendisk는 디스크를 표현하고 파티션은 그 안의 구간을 나타냅니다. 파티션의 시작 섹터와 길이는 파일시스템의 파일 경로와 다른 층의 정보입니다.

오류 포인터

커널은 포인터 반환형에 ERR_PTR로 오류 번호를 담기도 합니다. IS_ERR로 검사하기 전에는 일반 구조체처럼 역참조해서는 안 됩니다.

EOD와 재시도

EOD는 현재 알려진 장치 끝입니다. 원래 용량을 다시 활성화할 수 있는 경우 -EAGAIN으로 더 큰 용량에서의 재시도를 요청합니다.

처음 읽을 때

파티션 표를 읽는 단계와 읽은 결과로 파티션 장치를 만드는 단계를 나누십시오. 예시로 시작 섹터 2048을 가정할 수 있지만, 이 코드가 모든 파티션을 그 위치에 만드는 것은 아닙니다.

더 깊이 살펴볼 때

NULL, 오류 포인터, 유효한 parsed_partitions의 세 반환 형태를 구분하고 각 경로의 해제 책임을 확인하십시오. 이미 상태 객체를 얻었다면 실패와 성공이 out_free_state에서 합쳐집니다.

그림으로 보는 변화

파티션 검색: 디스크 구간을 장치로 등록하기까지의 단계별 개념 그림
각 단계에 화살표 의미와 생략 범위를 표시했습니다. 주소·숫자 예제는 실제 장치 값을 뜻하지 않습니다.
1단계 설명

1단계 고정

GIF 원본 열기

1. 표를 읽습니다

장치가 파티션 검색 대상이면 check_partition으로 표를 해석합니다.

화살표는 저장장치 메타데이터를 메모리 상태 객체로 읽는 방향입니다.

2. 검색 결과를 판단합니다

표 없음, 읽기 오류, 장치 끝 초과, 지원하지 않는 zoned 구성을 구분합니다.

되돌아가는 화살표는 용량을 확인한 후의 재시도 요청입니다.

3. 파티션을 등록합니다

유효한 항목을 순서대로 등록한 뒤 임시 해석 결과를 해제합니다.

화살표는 해석된 항목에서 커널 파티션 객체로의 등록입니다.

blk_add_partitions를 한 줄씩 읽기

줄 번호는 v6.18.37 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.

static int blk_add_partitions(struct gendisk *disk)
{
	struct parsed_partitions *state;
	int ret = -EAGAIN, p;

	if (!disk_has_partscan(disk))
		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 (bdev_is_zoned(disk->part0)) {
		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_has_partscan(disk))

이 디스크가 파티션 검색을 지원하고 필요로 하는지 확인합니다.

		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 (bdev_is_zoned(disk->part0)) {

파티션을 지원하지 않는 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.18.37 · block/partitions/core.c

해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고

맨 위로 ↑