File System · Linux v6.6 · source + diagram note

vfs_caches_init

early hash와 정식 VFS 기반 초기화: dentry·inode·file·mount·device의 서로 다른 책임.

전체 흐름

vfs_caches_init_early()와 vfs_caches_init()는 start_kernel(): early 호출과 start_kernel(): 정식 호출에서 분리되어 호출된다. 후자는 dentry·inode뿐 아니라 file, mount, block/character-device 기반까지 준비한다. pagecache_init(): 뒤따름은 별도 후속 호출이므로 여기서 page cache 전체가 준비된다고 읽지 않는다.

그림 1. start_kernel의 두 VFS 초기화 시점
초기 할당 단계
vfs_caches_init_early() in_lookup_hashtable head · dentry/inode hash의 early 분기
mm_core_init() 등 allocator·부팅 하위 체계 준비가 계속됨
정식 초기화 단계
vfs_caches_init() names, dentry, inode, file, mount, bdev, chrdev
pagecache_init() 별도 후속 호출

호출 순서는 start_kernel() 기준이다. 특정 파일 객체의 생성·공개 순서를 뜻하지 않는다.

호출 지점 고정 원문 범위
early VFS start_kernel(): early 호출 → vfs_caches_init_early lookup-head와 hashdist 조건부 hash 준비
정식 VFS start_kernel(): 정식 호출 → vfs_caches_init names slab 후 하위 초기화 함수를 차례로 호출
별도 page cache pagecache_init(): 뒤따름 VFS cache 함수 본문 바깥

소스 코드 위치

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

원본 코드:
Linux v6.6: fs/dcache.c
Linux v6.6: fs/inode.c
Linux v6.6: fs/file_table.c
Linux v6.6: fs/namespace.c

설명: fs/dcache.c의 두 진입 함수와 init/main.c 호출 지점, 각 하위 함수 구현을 대조해 초기화 책임을 구분한다.

대표 코드

void __init vfs_caches_init

원본 코드: fs/dcache.c, 3326–3353행
/* SLAB cache for __getname() consumers */
struct kmem_cache *names_cachep __read_mostly;
EXPORT_SYMBOL(names_cachep);

void __init vfs_caches_init_early(void)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(in_lookup_hashtable); i++)
		INIT_HLIST_BL_HEAD(&in_lookup_hashtable[i]);

	dcache_init_early();
	inode_init_early();
}

void __init vfs_caches_init(void)
{
	names_cachep = kmem_cache_create_usercopy("names_cache", PATH_MAX, 0,
			SLAB_HWCACHE_ALIGN|SLAB_PANIC, 0, PATH_MAX, NULL);

	dcache_init();
	inode_init();
	files_init();
	files_maxfiles_init();
	mnt_init();
	bdev_cache_init();
	chrdev_init();
}

확인 사항: in-lookup head, early 호출과 정식 호출 순서를 함께 보인다. names cache는 SLAB_PANIC이다.

dcache_init

원본 코드: fs/dcache.c, 3272–3324행
	dhash_entries = simple_strtoul(str, &str, 0);
	return 1;
}
__setup("dhash_entries=", set_dhash_entries);

static void __init dcache_init_early(void)
{
	/* If hashes are distributed across NUMA nodes, defer
	 * hash allocation until vmalloc space is available.
	 */
	if (hashdist)
		return;

	dentry_hashtable =
		alloc_large_system_hash("Dentry cache",
					sizeof(struct hlist_bl_head),
					dhash_entries,
					13,
					HASH_EARLY | HASH_ZERO,
					&d_hash_shift,
					NULL,
					0,
					0);
	d_hash_shift = 32 - d_hash_shift;
}

static void __init dcache_init(void)
{
	/*
	 * A constructor could be added for stable state like the lists,
	 * but it is probably not worth it because of the cache nature
	 * of the dcache.
	 */
	dentry_cache = KMEM_CACHE_USERCOPY(dentry,
		SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD|SLAB_ACCOUNT,
		d_iname);

	/* Hash may have been set up in dcache_init_early */
	if (!hashdist)
		return;

	dentry_hashtable =
		alloc_large_system_hash("Dentry cache",
					sizeof(struct hlist_bl_head),
					dhash_entries,
					13,
					HASH_ZERO,
					&d_hash_shift,
					NULL,
					0,
					0);
	d_hash_shift = 32 - d_hash_shift;
}

확인 사항: early/정식 dentry hash의 hashdist 분기를 끝까지 포함한다. runtime_const_init() 호출 없음

함수별 분석

early 함수는 in-lookup hash head를 초기화한 뒤 dentry·inode early 함수를 부른다. dcache_init_early/inode_init_early는 hashdist가 참이면 hash 할당을 미룬다. 정식 함수의 dcache_init/inode_init는 slab을 만들고 미뤄진 경우에만 hash를 할당한다.

그림 2. hashdist가 바꾸는 hash 할당 위치
vfs_caches_init_early() in_lookup_hashtable head → dcache_init_early → inode_init_early
hashdist = 0 early: dentry·inode hash를 HASH_EARLY | HASH_ZERO로 할당 정식: slab 생성 뒤 hash 재할당 없이 반환
hashdist ≠ 0 early: 두 hash 할당을 미룸 정식: slab 생성 뒤 HASH_ZERO로 hash 할당
vfs_caches_init() names slab → dcache_init · inode_init → file/mount/device

runtime_const_init() 호출 없음

그림 3. 정식 초기화의 실제 호출 순서
vfs_caches_init() 본문 순서
names_cachep PATH_MAX name buffer용 usercopy slab
dcache_init() dentry slab · 조건부 hash · d_iname usercopy field
inode_init() inode slab · 조건부 hash
files_init() filp slab + nr_files per-CPU counter
이어지는 호출
files_maxfiles_init() files_stat.max_files 계산
mnt_init() mount hash · kernfs/sysfs · rootfs/tree
bdev_cache_init() bdev pseudo-fs 등록·mount
chrdev_init() character-device map

fs/dcache.c의 호출열이다. 세부 준비·실패 처리는 하위 함수가 담당한다.

함수 실제 준비·변경 고정 원문
dcache / inode dentry slab의 usercopy 필드는 d_iname; inode slab; hashdist에 따라 early 또는 정식 시점에 hash 할당 dcache_init · inode_init
file filp_cachep 한 종류와 nr_files counter; 파일 수 상한도 별도 계산 files_init · files_maxfiles_init
mount mount/mountpoint hash, kernfs/sysfs, rootfs와 initial mount tree mnt_init
device bdev pseudo-fs 등록·mount; chrdev kobj_map bdev_cache_init · chrdev_init

구조체와 필드

여기서 만드는 것은 특정 dentry·inode·file 객체가 아니라 그것을 위한 slab, hash, 계수와 namespace 기반이다. vfs_caches_init()의 직접 설정과 하위 함수가 준비하는 기반을 구분한다.

그림 4. VFS 초기화가 준비하는 자료구조 관계
vfs_caches_init()
호출 관계의 중심
names_cachep __read_mostly · __getname() consumers
dentry_cache + hash usercopy field: d_iname
inode_cachep + hash inode slab / inode_hashtable
filp cache filp slab + nr_files per-CPU counter
mnt cache + hash rootfs / initial mount tree
bdev + cdev map pseudo-fs / chrdev lookup

허브에서 뻗는 초기화 책임을 나타낸다. dentry → inode → file 객체를 여기서 차례로 만든다는 뜻이 아니다.

자료구조 설정 함수 해석
in_lookup_hashtable vfs_caches_init_early head 초기화; 일반 dentry hash와 구별
dentry_hashtable dcache_init_early / dcache_init hashdist에 따라 early 또는 정식
names_cachep vfs_caches_init __read_mostly; name buffer slab
filp_cachep files_init filp slab + nr_files per-CPU counter
mount_hashtable / mountpoint_hashtable mnt_init rootfs/tree 구성도 같은 함수 안

실행 단계와 상태 변화

초기화 함수는 void이며 여기서 통합 오류 반환값이나 공통 unwind label을 제공하지 않는다. 실패 처리는 하위 함수마다 다르다. 준비 완료는 일반적인 모든 filesystem이 mount됐다는 뜻이 아니다.

경계 정상 완료 시 실패·주의
early hash in-lookup head; hashdist=0이면 dentry/inode hash hashdist≠0이면 아직 hash를 만들지 않은 것이 정상
정식 slab/hash names, dentry, inode, file slab·hash·파일 수 상한 SLAB_PANIC slab 실패는 정상 반환·unwind로 이어지지 않음
mount mount hash, kernfs/sysfs, rootfs/tree mount hash 실패는 panic; sysfs_init·fs_kobj 오류는 경고 후 진행
bdev/chrdev bdev pseudo-fs mount 뒤 cdev lookup map bdev 등록·kern_mount 오류는 panic
후속 start_kernel의 pagecache_init() VFS 초기화 본문 밖
hashdist는 hash 생성 시점을 옮긴다. mount tree는 mnt_init() 내부에서 만들어지고 pagecache_init()은 그 뒤에 온다.

불변 조건과 실패 사례

early와 정식 분리

hashdist가 참이면 early hash 함수는 의도적으로 반환한다. 정식 함수에서 slab을 만든 뒤 hash를 할당한다.

객체와 기반 구분

slab·hash 준비를 특정 dentry·inode·file 객체 생성이나 pathname lookup 완료로 해석하지 않는다.

mount의 실제 범위

mnt_init()은 mount hash뿐 아니라 kernfs, sysfs, rootfs와 initial mount tree까지 진행한다.

실패 의미

공통 goto 복구가 없다. SLAB_PANIC, mount hash panic, sysfs 경고, bdev panic을 각각 읽는다.

계측과 검증

부팅 초기화의 호출 순서는 고정 소스에서 확인하고, 부팅한 시스템에서는 읽기 전용 출력으로 결과 기반을 관찰한다. 사후 slab 수치만으로 어느 init 함수가 언제 실행됐는지는 단정할 수 없다.

읽기 전용 관찰 확인 가능한 내용 한계
cat /proc/slabinfo dentry, inode_cache, filp, mnt_cache, bdev_cache 등 이름 사용·할당 수는 부팅 이후 변함; 호출 순서 증거 아님
cat /proc/sys/fs/file-max files_stat.max_files 관련 상한 런타임에 바뀔 수 있어 초기값과 다를 수 있음
cat /proc/self/mountinfo 현재 namespace의 mount 결과 초기 mount tree 생성 시각을 보여주지 않음
고정 원문 대조 fs/dcache.c · start_kernel(): 정식 호출 hashdist·panic·warning은 함수별 분기 확인
읽기 전용 예시:
cat /proc/slabinfo
cat /proc/sys/fs/file-max
cat /proc/self/mountinfo

추가 확인 사항

  • 대상 빌드의 hashdist 값에 따라 dentry·inode hash는 어느 함수에서 할당되는가?
  • 진단 대상이 slab, hash, mount tree, bdev pseudo-fs 중 어느 단계까지 도달했는가?
  • sysfs 경고와 bdev mount panic을 구분했는가?
  • page cache 문제는 pagecache_init() 이후의 경로인가?