FDT, live node, 장치 등록의 경계
이 글의 두 대표 함수는 이미 만들어진 live device tree를 사용한다. flat FDT에서 device_node tree를 만드는 unflatten_device_tree()는 drivers/of/fdt.c의 앞 단계다. of_platform_populate()는 그 live tree를 순회하여 장치 생성 경로에 들어가고, of_find_node_by_path()는 경로 조회만 한다.
device_node treeunflatten_device_tree()가 생성. 아래 두 함수의 입력 기반of_platform_populate()→of_platform_bus_create()가 조건에 따라 생성·건너뜀of_find_node_by_path(path) → live node 참조 또는 NULL. compatible match를 수행하지 않음.고정 원문: fdt.c unflatten · platform.c populate · of.h path wrapper
소스 코드 위치
먼저 확인할 Linux v6.6 소스 파일
Linux v6.6: drivers/of/base.c
Linux v6.6: drivers/of/platform.c
Linux v6.6: drivers/of/fdt.c
Linux v6.6: include/linux/of.h
설명: 진입 함수가 있는 파일과 구조체 정의, 아키텍처별 연결 코드, 콜백 구현을 함께 확인한다. 여러 파일을 대조해야 호출 관계와 각 코드의 역할을 알 수 있다.
대표 코드
of_platform_populate
int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent)
{
struct device_node *child;
int rc = 0;
root = root ? of_node_get(root) : of_find_node_by_path("/");
if (!root)
return -EINVAL;
pr_debug("%s()\n", __func__);
pr_debug(" starting at: %pOF\n", root);
device_links_supplier_sync_state_pause();
for_each_child_of_node(root, child) {
rc = of_platform_bus_create(child, matches, lookup, parent, true);
if (rc) {
of_node_put(child);
break;
}
}
device_links_supplier_sync_state_resume();
of_node_set_flag(root, OF_POPULATED_BUS);
of_node_put(root);
return rc;
}
확인 사항: root 참조 획득 실패는 -EINVAL이다. 반복문에는 rc != 0이면 중단하는 분기가 있지만, 장치 생성 실패가 모두 음수 오류로 전달되지는 않는다. sync-state resume와 root의 OF_POPULATED_BUS 설정·of_node_put(root)은 이후에도 실행된다. 이 flag를 모든 child/probe 성공으로 읽지 않는다.
of_find_node_by_path
static inline struct device_node *of_find_node_by_path(const char *path)
{
return of_find_node_opts_by_path(path, NULL);
}
CONFIG_OF가 켜진 이 wrapper는 경로를 of_find_node_opts_by_path(path, NULL)에 전달한다. CONFIG_OF가 꺼진 별도 stub은 NULL을 반환한다. machine compatible 검사와는 관계가 없다. 성공 시 반환된 device_node 참조는 사용 후 of_node_put()으로 해제해야 한다.
두 대표 함수가 하는 일과 하지 않는 일
of_platform_populate()와 of_find_node_by_path()는 순차 호출 체인이 아니다. populate가 root 인자로 NULL을 받았을 때 경로 "/"를 조회할 수 있지만, 호출자가 다른 path를 찾는 wrapper는 독립적으로 쓸 수 있다.
- 1root 참조 확보전달된 root면
of_node_get(root), NULL이면of_find_node_by_path("/"). 둘 다 root가 없으면-EINVAL. - 2device-link sync pause → 자식 순회
for_each_child_of_node(root, child)에서of_platform_bus_create(..., true)호출. strict mode에서는 compatible이 없는 노드를 건너뛸 수 있다. - 3오류 시 break·child 참조 정리
for_each_child_of_node의 break에서of_node_put(child)을 명시적으로 호출한다. 이미 처리한 장치의 포괄적 rollback은 이 함수에 없다. - 4resume → root flag → root put → rc
OF_POPULATED_BUS는 이 위치에서rc가 0이 아니어도 설정된다. 모든 자식 생성·driver probe 성공 표시가 아니다.
of_find_node_by_path(path)는 of_find_node_opts_by_path(path, NULL)를 부른다. 성공 시 caller가 of_node_put().| 함수 | 입력 | 소스가 보장하는 처리 | 보장하지 않는 것 |
|---|---|---|---|
of_platform_populate | live root 또는 NULL, bus matches, auxdata, parent | root 참조를 확보·반납하고 child 생성 경로를 순회; rc 반환 (생성 실패가 0으로 처리될 수 있음) | FDT unflatten, 모든 노드의 장치화, 모든 driver probe 완료 |
of_find_node_by_path | 경로 문자열 | node 참조 또는 NULL; CONFIG_OF off stub은 NULL | compatible 검사, platform device 생성, driver binding |
device_node 참조와 장치 객체의 소유권
경로 조회가 돌려준 node 참조, populate 내부가 임시 확보한 root 참조, 자식 순회 중의 참조는 서로 다른 소유권이다.
of_node_get, 없으면 "/" 조회 → 내부에서 of_node_put(root). 호출자가 원래 보유한 참조는 별개.for_each_child_of_node의 break에서 of_node_put(child)을 명시적으로 호출한다. of_platform_bus_create의 반환에 따라 다음 자식 또는 break.of_find_node_by_path(path)를 직접 호출한 쪽이 성공한 node를 사용한 뒤 of_node_put().| 참조·객체 | 획득/생성 | 반납/수명 | 흔한 오해 |
|---|---|---|---|
| populate root ref | of_node_get 또는 root path lookup | 함수 말미 of_node_put(root) | 호출자가 넘긴 원래 참조까지 소비한다고 착각 |
| child iterator ref | for_each_child_of_node | break 시 child를 명시적으로 put | break 뒤 참조 누락/중복 put |
| 직접 path lookup ref | 성공한 of_find_node_by_path | 호출자 of_node_put | 함수 자체가 driver를 probe한다고 착각 |
| platform/AMBA device | of_platform_bus_create의 조건별 분기 | device model 수명·해제 경로 | node ref 하나의 put이 device를 제거한다고 착각 |
참조 계약: of.h node ref API · populate 반복
이 고정 버전의 of_platform_device_create_pdata()는 비활성/이미 populated 노드뿐 아니라 할당·등록 실패에도 NULL을 반환한다. 이를 호출한 of_platform_bus_create()의 !dev 분기는 0을 반환한다. 따라서 모든 장치 생성 실패가 음수 rc로 전파된다고 해석하면 안 된다. 반환 0과 OF_POPULATED_BUS만으로 장치 생성 완료를 판단하지 않고 실제 객체와 로그를 확인한다. 생성 helper · bus 생성의 NULL 처리
성공·건너뜀·오류를 구별하는 상태표
한 함수가 0을 반환해도 모든 DT node가 장치로 바뀌거나 probe가 끝난 것은 아니다. 먼저 live node 존재와 장치 등록 여부를 분리한다.
root == NULL → -EINVAL. child 순회·flag 설정 전 종료.rc != 0이면 반복 중단. pause는 resume되고 root flag가 설정된 뒤 root ref를 놓고 rc 반환.OF_POPULATED_BUS는 이 함수의 최종 성공 비트가 아니다. 코드상 오류 반환 전에 root에도 설정된다.
| 관찰 | 소스 단계 | 직접 확인할 것 | 단정할 수 없는 것 |
|---|---|---|---|
| live node | FDT unflatten 또는 동적 OF tree | /sys/firmware/devicetree/base의 해당 경로 | device 등록 |
| population 시도 | of_platform_populate → child helper | return code, skip 조건, root flag | 모든 child 성공 |
| device 객체 | platform/AMBA device 생성 분기 | device의 of_node 연결 | driver binding |
| driver 상태 | match·probe 또는 deferred probe | driver link·deferred 목록·관련 로그 | DT 기재만으로 probe 완료 |
두 API에서 지켜야 할 경계
of_platform_populate()가 flat blob을 unflatten하지 않는다. root가 없으면 -EINVAL이며, 노드가 있어도 skip 조건을 통과해야 device 생성에 이른다.
populate 내부 root 참조와 외부 path 조회 참조를 각각 한 번씩 반납한다. 이 버전의 child 반복에서는 break 시 child를 명시적으로 put.
장치 생성 실패가 0으로 처리되어도 앞서 만든 device를 여기서 일괄 되돌리지 않는다. root의 OF_POPULATED_BUS도 설정된다. 반환값과 실제 객체를 함께 검사한다.
compatible은 device/driver 매칭의 한 재료다. path lookup 성공이나 population 반환 0만으로 driver probe 성공을 주장하지 않는다.
근거: populate · path wrapper · path lookup 구현
읽기 전용으로 단계별 확인
아래 경로는 해당 기능이 켜진 보드에서만 나타난다. 실제 DT 내용과 Kconfig·driver 로그를 함께 기록해야 원인 단계가 구분된다. 전역 tracefs tracer/filter를 바꾸지 않는다.
| 가설 | 읽기 전용 관찰 | 해석 한계 |
|---|---|---|
| live node가 있나? | ls /sys/firmware/devicetree/base와 대상 경로 확인 | device 생성이나 probe를 보장하지 않음 |
| platform device가 생겼나? | ls /sys/bus/platform/devices, 대상 device의 of_node symlink | AMBA 등 별도 bus 경로도 가능 |
| driver가 붙었나? | 대상 device의 driver symlink와 관련 dmesg | 없는 경우 no match·probe 오류·defer를 분리해야 함 |
| defer 상태인가? | 존재할 때 cat /sys/kernel/debug/devices_deferred | debugfs/설정 조건부, 목록에 없다는 것만으로 probe 성공 아님 |
동적 추적이 필요하면 테스트 환경에서 사용 가능한 event/function을 먼저 확인하고 범위를 협의한다. 시스템 전체 current_tracer·set_ftrace_filter를 예제로 덮어쓰지 않는다.
보드에서 추가로 확인할 질문
- 사용한 FDT와 Kconfig에서 어떤 node가 live tree에 존재하며
status와compatible은 무엇인가? - 선택한 root 아래 각 child가 strict compatible·skip table·already-populated 조건 중 어디에서 걸리는가?
- root 부재의
-EINVAL과, 반환 0인데도 일부 장치가 없는 경우를 구분했는가? 생성 helper의 NULL 반환과 root flag를 각각 확인했는가? - 찾은 node의 참조는 어느 호출자가
of_node_put()하며, 같은 node에 대응하는 device와 driver link는 별도로 존재하는가? - device는 있는데 driver가 없다면 match 부재, probe 실패, deferred probe 중 어느 로그가 근거인가?