dentry有两个哈希表,一个是主哈希表,一个是in lookup哈希表。
分配一个alloc,接口有:
struct dentry *d_alloc(struct dentry * parent, const struct qstr *name) struct dentry *d_alloc_name(struct dentry *parent, const char *name)
这两个接口一个传参是qstr,qstr要求计算过hash,一个是纯字符串,
这两个接口只是单纯分配dentry,不去查找hash表,也不加入哈希表。因此这两个接口不用任何锁。
1 分配一个dentry,并且初始化,
d_lockref.count初始化为1
d_flags和d_inode初始化为NULL。
d_parent初始化为parent
d_sb初始化为parent->d_sb。
2 挂在parent下面
hlist_add_head(&dentry->d_sib, &parent->d_children);
这里要上parent的d_lock。
第二个alloc接口是非常重要的,namei里面的lookup等业务是调这个接口
struct dentry *d_alloc_parallel(struct dentry *parent, const struct qstr *name, wait_queue_head_t *wq)
这个接口支持并行操作,也就是说只需要上parent的inode的共享锁即可。
inode_lock_shared(parent->d_inode);
当然上写锁也是可以的。
1 它首先调用d_alloc()分配一个dentry。
这个dentry会做备用,后面如果找到了,会把这个dentry使用dput(dentry)释放掉。
2 然后使用RCU方式去主hash表查找,如果找到了,说明一个并行的操作已经完成了,并且进了主hash表。
所以释放分配的dentry,直接返回找到的dentry。
3 如果没找到,就去in lookup哈希表查找,如果找到,
则表示有一个并行的操作正在进行(可能正在执行文件系统的lookup(),还没有完成)
此时调用,d_wait_lookup()等待wait queue,完成。
完成后,释放分配的dentry,返回找到的这个dentry。
4 最后都没找到,那么就返回分配的这个dentry。
a. 设置d_flags包含DCACHE_PAR_LOOKUP
b. 保存wq到d_wait
c. 添加到in lookup 哈希表。
d_in_lookup()
可以判断一个dentry,是不是在in lookup哈希表中,其判断DCACHE_PAR_LOOKUP标志。
将一个dentry添加到哈希表
void d_add(struct dentry *entry, struct inode *inode)
inode可以为NULL,这样会产生一个negative dentry
1 如果dentry,在in lookup哈希表,则将其移除,移除前调用start_dir_add()给父目录的inode上i_dir_seq锁。
start_dir_add()
先禁止抢占,然后循环判断i_dir_seq是否为偶数,奇数表示正在修改,奇数继续循环,期间调用cpu_relax,
如果是偶数,则加+1,调用cmpxchg实现原子操作。
调用__d_lookup_unhash(),将其从in lookup哈希表中移除。
2 关联dentry和inode
设置dentry->inode,以及flags。
3 加入主hash表
__d_rehash(dentry);
4 如果之前在in lookup哈希表,做结束处理:
end_dir_add(dir, n, d_wait);
增加i_dir_seq,唤醒等待队列。
关联一个dentry和inode,和Add的不同,就是不处理哈希表,原来不再hash表中,则不会添加。
void d_instantiate(struct dentry *entry, struct inode * inode)
和add类似,不过对于目录,它有特殊的处理。
struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
1 找到inode是否有之前的dentry关联。
struct dentry *new = __d_find_any_alias(inode);
2 如果有,那么调用:
__d_unalias(dentry, new)
进行移动操作
fs的lookup()接口,最终要调用此函数,来关联dentry和inode。
移动dentry到另外一个dentry
void d_move(struct dentry *dentry, struct dentry *target)
整个操作在rename_lock全局锁中保护
dentry和target都从hash表中移除。
然后dentry的parent等于target的parent,名字也拷贝过来,
然后重新哈希dentry。
target如果在in lookup哈希表中,走
start_dir_add()/__d_lookup_unhash()/end_dir_add()流程。
target从哈希表中摘除了,其它字段没变。
通常由调用者释放target及其inode。
将dentry从哈希表中移除,其它啥都不变。
void __d_drop(struct dentry *dentry); void d_drop(struct dentry *dentry);
将dentry重新添加到hash队列
void d_rehash(struct dentry *);
从哈希表移除,或者将其变成negative。
void d_delete(struct dentry * dentry)
如果有多个引用计数,则将其从哈希表中拿掉,但是inode/parent这些字段不动。
如果只有最后一个引用计数,则
dentry->d_flags &= ~DCACHE_CANT_MOUNT;
设置为不可挂载,同时调用
dentry_unlink_inode(dentry);
释放一个引用计数
void dput(struct dentry *dentry)