Driver / Device · Linux v6.6 · source + diagram note

driver_init

Linux v6.6 driver_init()의 core 등록 순서와 devices_init()의 sysfs 기반 생성·실패 정리를 읽습니다. 개별 driver probe는 뒤의 단계입니다.

전체 흐름

driver_init()은 init/main.c의 do_basic_setup()에서 L1326에 직접 호출되고 일반 do_initcalls()보다 앞선다. drivers/base/init.c에 적힌 순서로 device model의 기반을 만든다. 특정 driver 등록·match·probe는 이후 단계다.

이 함수의 책임

devtmpfs, device kobject, bus, class, firmware/hypervisor 기반과 후속 platform 등의 core 진입점을 순서대로 부른다.

범위의 끝

driver_init()은 void다. devices_init()의 int 반환값은 이 호출자에게서 사용되지 않는다.

그림 1. core 기반 초기화와 후속 driver 등록의 경계
init/main.cdo_basic_setupdriver_init() 호출일반 do_initcalls 이전drivers/base/init.cdriver_init()bdi · devtmpfs · devices · buses · classesfirmware · hypervisorOF → platform → auxiliary후속 등록driver / devicebus/driver 등록 뒤match · probe 가능driver_init()의 호출 목록은 core 기반을 만드는 순서다. 하드웨어 probe 완료 경로가 아니다.

실선은 고정 init.c의 호출 순서와 이후 단계의 경계를 뜻한다. probe 완료 표시가 아니다.

소스 코드 위치

먼저 확인할 Linux v6.6 소스 파일

원본 코드:
Linux v6.6: drivers/base/init.c
Linux v6.6: drivers/base/core.c
Linux v6.6: drivers/base/bus.c
Linux v6.6: drivers/base/dd.c

설명: 이 글의 대표 함수는 init.c의 driver_init()과 core.c의 devices_init()이다. bus 등록과 driver matching/probe는 별도 파일·후속 단계로 읽는다.

대표 코드

void __init driver_init

원본 코드: drivers/base/init.c:21-42 · v6.6 · 로컬 원문
void __init driver_init(void)
{
	/* These are the core pieces */
	bdi_init(&noop_backing_dev_info);
	devtmpfs_init();
	devices_init();
	buses_init();
	classes_init();
	firmware_init();
	hypervisor_init();

	/* These are also core pieces, but must come after the
	 * core core pieces.
	 */
	of_core_init();
	platform_bus_init();
	auxiliary_bus_init();
	cpu_dev_init();
	memory_dev_init();
	node_dev_init();
	container_dev_init();
}

확인 사항: driver_init()은 void이고 devices_init()의 반환값을 사용하지 않는다. 호출 순서는 core 기반의 순서이며 probe 완료가 아니다.

devices_init

원본 코드: drivers/base/core.c:4072-4096 · v6.6 · 로컬 원문
int __init devices_init(void)
{
	devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
	if (!devices_kset)
		return -ENOMEM;
	dev_kobj = kobject_create_and_add("dev", NULL);
	if (!dev_kobj)
		goto dev_kobj_err;
	sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
	if (!sysfs_dev_block_kobj)
		goto block_kobj_err;
	sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
	if (!sysfs_dev_char_kobj)
		goto char_kobj_err;

	return 0;

 char_kobj_err:
	kobject_put(sysfs_dev_block_kobj);
 block_kobj_err:
	kobject_put(dev_kobj);
 dev_kobj_err:
	kset_unregister(devices_kset);
	return -ENOMEM;
}

devices_init()은 driver_init()의 직접 호출이다. devices_kset과 dev/block/char sysfs 기반을 만들고, 실패 시 이미 만든 것만 역순 정리해 -ENOMEM을 돌려주지만 상위 호출은 그 값을 읽지 않는다.

함수별 분석

devices_init()은 driver_init()이 직접 호출한다. 내부에서 devices_kset, dev_kobj, block/char kobject를 순서대로 만들며, workqueue 할당 단계 없이 끝난다. 이 함수는 bus/class 객체나 개별 driver의 private data를 만들지 않는다.

그림 2. devices_init의 생성 순서와 실패 정리
1devices_kset/sys/devices2dev_kobj/sys/dev3block/sys/dev/block4char/sys/dev/charv6.6return 0workqueue 없음실패하면 생성된 객체만 반대 순서로 put/unregister한다. v6.6에는 device_link_wq와 wq_err가 없다.

각 단계가 실패하면 그때까지 만든 대상만 반대 순서로 해제한다. 함수 내부의 오류 처리와 driver_init()의 반환값 무시를 구분한다.

실제 함수변경하는 상태별도로 남는 질문
driver_init()하위 core init 함수를 순서대로 호출각 void/int 호출의 반환 정책은 서로 다름
devices_init()kset/kobject 기반성공 0 / 실패 -ENOMEM; 호출자는 반환값을 읽지 않음
이후 driver 등록bus에 driver를 추가하고 matching 진입 가능이 코드 조각만으로 특정 probe의 실행·완료를 단정할 수 없음

객체와 sysfs 경로

devices_kset은 /sys/devices의 기반이다. dev_kobj는 별도 /sys/dev root이고 block/char kobject는 그 자식이다. device_link_wq와 wq_err는 이 버전에 없다.

그림 3. devices_init이 만드는 두 root와 자식
별도 rootdevices_kset/sys/devices별도 rootdev_kobj/sys/devchildblock kobject/sys/dev/blockchildchar kobject/sys/dev/chardevices_kset과 dev_kobj는 서로 다른 top-level 객체다. 여기에는 bus_type이나 특정 device의 probe가 없다.

bus_type과 class는 각각 뒤의 buses_init(), classes_init()에서 다룬다.

객체생성 코드관찰 가능한 위치
devices_ksetkset_create_and_add("devices", …)/sys/devices
dev_kobjkobject_create_and_add("dev", NULL)/sys/dev
block / char kobjectparent = dev_kobj/sys/dev/block, /sys/dev/char

실행 단계와 상태 변화

devices_init()의 성공·실패는 함수 내부에서는 구분된다. 하지만 driver_init()은 반환값을 검사하지 않고 다음 buses_init()으로 진행한다. 그러므로 뒤 함수가 호출되었다는 사실을 앞의 모든 객체 생성 성공으로 읽지 않는다.

그림 4. 내부 실패 정리와 상위 호출 계속 진행
driver_init: voiddevices_init() 호출int 반환값을 사용하지 않음devices_init 성공return 0생성한 기반 유지devices_init 실패-ENOMEM함수 내부 unwind상위 호출다음 init 호출buses_init → classes_initprobe 완료와 별개성공·실패 어느 쪽도 driver_init()에 오류 값으로 전달되지 않는다. 이 코드만으로 모든 기반의 성공을 보증할 수 없다.

초기화 실패가 무조건 특정 probe의 실패나 자동 복구로 이어진다고 단정하지 않는다.

지점성공실패다음 단계
devices_ksetdevices root 생성즉시 -ENOMEMdev_kobj
dev_kobjdev root 생성devices_kset unregisterblock
block kobjectblock child 생성dev put → kset unregisterchar
char kobjectchar child 생성block → dev → kset 정리return 0

불변 조건과 실패 사례

생성 순서

char는 block과 dev root가 만들어진 뒤 생성한다. v6.6에는 faux_bus_init()가 없고 CPU→memory→node 순서다.

국소 unwind

devices_init()의 label은 현재까지 만든 객체만 정리한다. device_link_wq와 wq_err는 이 버전에 없다.

상위 호출

driver_init()은 devices_init()의 -ENOMEM을 전파하지 않는다. 뒤의 기반이 정상이라는 결론을 여기서 내릴 수 없다.

probe 경계

sysfs 기반의 존재는 특정 device에 driver가 붙었거나 하드웨어 초기화가 끝났다는 증거가 아니다.

계측과 검증

이 경로에는 전용 driver_init tracepoint가 없다. initcall_debug도 do_initcalls()의 개별 initcall에 대한 것이므로, 그 앞의 직접 호출인 driver_init() 자체가 완료됐다는 기록으로 읽지 않는다. 다음은 공유 tracefs 설정을 바꾸지 않는 관찰이다.

읽기 전용 관찰알 수 있는 것한계
ls -ld /sys/devices /sys/dev /sys/dev/block /sys/dev/char현재 sysfs root와 자식 존재부팅 당시 어느 내부 호출이 성공했는지 단독 증명 불가
ls /sys/bus이후 등록된 bus 목록특정 device probe 완료와 다름
dmesg | grep -i "probe\|device"장치별 후속 메시지로그 설정과 driver 구현에 좌우
cat /sys/kernel/debug/devices_deferred (파일이 있을 때)현재 deferred probe 대상driver_init() 완료 기록이 아님
읽기 전용 예:
ls -ld /sys/devices /sys/dev /sys/dev/block /sys/dev/char
ls /sys/bus
if [ -r /sys/kernel/debug/devices_deferred ]; then cat /sys/kernel/debug/devices_deferred; fi

추가 확인 사항

  • devices_init()가 -ENOMEM을 반환한 뒤 driver_init()은 왜 그 값을 전파하지 않는가?
  • v6.6의 마지막 char kobject 실패는 어떤 순서로 되돌리는가?
  • /sys/devices가 보이는 것과 특정 driver의 probe 완료를 어떤 로그·소스로 구분할 수 있는가?
  • 두 버전의 뒤쪽 core 호출 순서 차이가 특정 platform 초기화 의존성에 영향을 주는가?