이 코드는 어떤 문제를 푸나요?
경로 문자열을 inode로 연결하려면 각 디렉터리에서 다음 이름을 찾아야 합니다. 자주 찾는 이름은 dentry 캐시에 있지만, 다른 태스크가 같은 순간 이름을 바꾸거나 항목을 무효화할 수 있습니다. lookup_fast는 캐시에서 빠르게 찾은 결과를 그대로 믿지 않고 일관성을 확인하며, 필요하면 더 안전한 탐색 방식으로 바꿉니다.
읽을 범위: v6.6 · fs/namei.c · lookup_fast 1618–1664행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.
먼저 알아둘 개념
dentry와 inode
dentry는 부모 디렉터리 아래의 이름과 파일 객체 사이 관계를 나타냅니다. inode는 파일 자체의 상태이며, 여러 이름이 같은 inode를 가리킬 수 있습니다.
RCU 탐색
조회 때마다 객체 참조와 강한 잠금을 잡는 비용을 줄이려고 RCU 보호와 변경 검사를 함께 사용하는 방식입니다. 데이터가 절대로 바뀌지 않는다는 뜻은 아닙니다.
sequence count
읽기 전후의 순번을 비교해 읽는 동안 변경이 있었는지 판단합니다. 값이 바뀌었다면 빠르게 읽은 결과를 다시 검증하거나 탐색해야 합니다.
재검증
캐시 항목이 있어도 파일시스템이 현재 유효하다고 인정하는지 확인하는 절차입니다. 특히 외부에서 상태가 바뀔 수 있는 파일시스템에서는 캐시 존재만으로 충분하지 않습니다.
처음 읽을 때
설명용 경로 /a/b에서 부모 a 아래의 b를 찾는 장면을 생각하십시오. 이 함수는 경로 전체를 한 번에 해석하지 않고 현재 구성 요소의 캐시 조회를 담당합니다.
더 깊이 살펴볼 때
NULL과 ERR_PTR(-ECHILD), 유효한 dentry의 차이를 추적하십시오. -ECHILD는 여기서 자식 프로세스 문제를 뜻하지 않고 RCU 탐색을 계속할 수 없으므로 다른 방식으로 다시 처리하라는 내부 신호로 쓰입니다.
그림으로 보는 변화

1. 이름 캐시를 조회합니다
현재 부모 dentry와 마지막 경로 구성 요소로 캐시를 찾습니다.
화살표는 부모와 이름에서 dentry를 찾는 관계입니다.
2. 변경 여부를 확인합니다
부모의 순번과 파일시스템 재검증 결과로 캐시 결과를 믿어도 되는지 판단합니다.
갈라지는 화살표는 유효한 결과와 재탐색이 필요한 결과를 구분합니다.
3. 필요하면 탐색 방식을 바꿉니다
RCU 방식으로 확정하기 어렵다면 참조를 확보하는 탐색으로 전환하거나 호출자에게 재시도를 알립니다.
되돌아가는 화살표는 경로 재탐색이며 파일 데이터를 읽는 방향이 아닙니다.
lookup_fast를 한 줄씩 읽기
줄 번호는 v6.6 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.
static struct dentry *lookup_fast(struct nameidata *nd)
{
struct dentry *dentry, *parent = nd->path.dentry;
int status = 1;
/*
* Rename seqlock is not required here because in the off chance
* of a false negative due to a concurrent rename, the caller is
* going to fall back to non-racy lookup.
*/
if (nd->flags & LOOKUP_RCU) {
dentry = __d_lookup_rcu(parent, &nd->last, &nd->next_seq);
if (unlikely(!dentry)) {
if (!try_to_unlazy(nd))
return ERR_PTR(-ECHILD);
return NULL;
}
/*
* This sequence count validates that the parent had no
* changes while we did the lookup of the dentry above.
*/
if (read_seqcount_retry(&parent->d_seq, nd->seq))
return ERR_PTR(-ECHILD);
status = d_revalidate(dentry, nd->flags);
if (likely(status > 0))
return dentry;
if (!try_to_unlazy_next(nd, dentry))
return ERR_PTR(-ECHILD);
if (status == -ECHILD)
/* we'd been told to redo it in non-rcu mode */
status = d_revalidate(dentry, nd->flags);
} else {
dentry = __d_lookup(parent, &nd->last);
if (unlikely(!dentry))
return NULL;
status = d_revalidate(dentry, nd->flags);
}
if (unlikely(status <= 0)) {
if (!status)
d_invalidate(dentry);
dput(dentry);
return ERR_PTR(status);
}
return dentry;
}static struct dentry *lookup_fast(struct nameidata *nd)진행 중인 경로 탐색 상태 nd를 받아 현재 이름의 dentry를 캐시에서 찾습니다.
struct dentry *dentry, *parent = nd->path.dentry;결과 포인터와 현재 부모 dentry를 준비합니다. 부모는 nd의 현재 경로에서 얻습니다.
int status = 1;기본 재검증 상태를 유효하다는 양수로 준비합니다.
if (nd->flags & LOOKUP_RCU) {현재 탐색이 참조 획득 비용을 줄이는 RCU 모드인지 확인합니다.
dentry = __d_lookup_rcu(parent, &nd->last, &nd->next_seq);부모와 마지막 이름으로 캐시를 조회하고 찾은 자식의 변경 순번도 보관합니다.
if (unlikely(!dentry)) {캐시에서 찾지 못한 드문 경우를 처리합니다. unlikely는 확률 힌트입니다.
if (!try_to_unlazy(nd))RCU 방식에서 참조를 확보하는 방식으로 안전하게 전환할 수 있는지 시도합니다.
return ERR_PTR(-ECHILD);전환 중 상태를 확정할 수 없으면 -ECHILD 오류 포인터로 상위의 재탐색을 요청합니다.
return NULL;전환에 성공했지만 캐시 항목이 없으므로 NULL을 돌려 느린 조회가 이어지게 합니다.
if (read_seqcount_retry(&parent->d_seq, nd->seq))캐시 조회 동안 부모 dentry가 변경됐는지 순번으로 검사합니다.
return ERR_PTR(-ECHILD);부모가 바뀌었다면 결과를 확정하지 않고 재탐색 신호를 반환합니다.
status = d_revalidate(dentry, nd->flags);찾은 dentry와 현재 탐색 플래그로 파일시스템 재검증을 요청합니다. 이 버전의 d_revalidate에는 부모 inode나 이름 인자를 별도로 넘기지 않습니다.
if (likely(status > 0))유효하다는 양수 결과가 가장 흔한 경로입니다.
return dentry;RCU 탐색 상태를 유지한 채 찾은 dentry를 반환합니다.
if (!try_to_unlazy_next(nd, dentry))재검증이 끝나지 않은 경우 다음 dentry까지 포함해 참조 기반 탐색으로 전환합니다.
return ERR_PTR(-ECHILD);필요한 참조를 안전하게 확보하지 못하면 상위에서 다시 탐색하도록 알립니다.
if (status == -ECHILD)파일시스템이 RCU가 아닌 방식의 재검증을 요구했는지 검사합니다.
status = d_revalidate(dentry, nd->flags);RCU 모드를 벗어나 다시 검증해야 한다는 요청에 따라 같은 dentry를 변경된 탐색 플래그로 재검증합니다.
} else {처음부터 RCU 모드가 아니었던 탐색 경로입니다.
dentry = __d_lookup(parent, &nd->last);일반 참조 기반 dentry 조회를 수행합니다.
if (unlikely(!dentry))이 방식에서도 캐시 항목을 찾지 못했는지 확인합니다.
return NULL;캐시 미스로 NULL을 반환하여 후속 탐색으로 넘깁니다.
status = d_revalidate(dentry, nd->flags);일반 참조 기반 조회에서 얻은 dentry가 유효한지 현재 탐색 플래그와 함께 확인합니다.
if (unlikely(status <= 0)) {재검증이 무효 0 또는 음수 오류를 반환한 경우를 처리합니다.
if (!status)음수 오류가 아니라 캐시 항목 자체가 무효라는 0인지 구분합니다.
d_invalidate(dentry);해당 dentry를 무효화하여 낡은 결과가 다시 사용되지 않게 합니다.
dput(dentry);이 경로가 확보한 dentry 참조를 내려놓습니다.
return ERR_PTR(status);음수면 오류 포인터를, 0이면 NULL을 반환합니다. ERR_PTR(0)은 NULL이라는 점이 중요합니다.
return dentry;유효성 검사를 통과한 dentry를 반환합니다.
함께 생각해 볼 질문
캐시에 이름이 있으면 바로 성공인가요?
아닙니다. 부모가 바뀌지 않았는지와 파일시스템 재검증 결과를 함께 확인합니다.
여기서 -ECHILD는 자식 프로세스가 없다는 뜻인가요?
이 탐색 경로에서는 RCU 방식으로 계속할 수 없다는 재처리 신호로 쓰입니다.
dput은 파일의 데이터를 삭제하나요?
아닙니다. 이 경로에서 확보한 dentry 참조를 내려놓는 일입니다.
출처와 읽은 범위
Linux stable v6.6 · fs/namei.c
해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고
