先了解一下vfs
vfs是文件系统与kernel之间的桥梁,是一种机制:用来提供一个统一组织和管理各个文件系统,这样各种文件系统类型之间即使有差异也被vfs屏蔽。
vfs中有两个很重要的数据结构:dentry和inode
-
dentry 就是“目录项”保存着诸如文件名、路径等信息;
-
inode 是索引节点,保存具体文件的数据,比如权限、修改日期、设备号(如果是设备文件的话)等等
文件系统中的所有的文件(目录也是一种特殊的文件)都必有一个 inode 与之对应,而每个 inode 也至少有一个 dentry 与之对应(也有可能有多个,比如硬链接)。结合下图我们可以更清晰的理解这个架构:
[图片来源:dentry和inode的关系_inode dentry-CSDN博客]
一个Oracle acfs故障的理解
先说说为什么要了解这两个结构,偶然间有同事发来一个处理Oracle ACFS的报告,里面涉及到升级Linux内核后,ACFS不识别的情况导致生产事故。
具体的分析过程就不展开了,因为这个触发Oracle一个bug,这里只对其CAUSE做个扩展。
这里说的d_splice_alias
,我通过网上找的一些材料来梳理一下关系。
d_splice_alias
是在vfs dcache函数中调用(通过EXPORT_SYMBOL导出了一系列函数,供内核、文件系统程序使用)。
|
|
这个函数一般在lookup中调用,
将一个inode与一个负状态的dentry绑定,在这个函数内部,首先要判断inode是否为目录,如果是目录的话,调用__d_find_alias函数搜索一个别名dentry(注意,目录是没有硬链接的),如果找到(说明这个inode的dentry曾经被使用过,而后来dentry变为负状态,但里面的很多内容还可以利用),调用d_rehash更新hash值, 并调用d_move将这个别名dentry的内容移动需要的dentry上。如果__d_find_alias没有找到结果,说明这个inode没有对应的别名dentry,需要调用__d_instantiate、d_rehash将inode绑定到dentry上。如果inode是一个文件的话,直接使用d_add将inode与dentry绑定。引用原文链接
几个名词
- disconnected dentry
dentry 一般是通过文件名一级一级查找创建的,子 dentry 的 parent 成员被设置为父 dentry。无论经过多少次访问,生成的 dentry 都会连接到 root dentry 树上。也就是说,正常文件系统访问文件不会生成游离于 root 之外的 dentry;但 get_dentry 生成的 dentry 不是通过文件路径访问被创建的,无法附加到 root 树上。这些 dentry 被称为 disconnected dentry。
disconnected dentry 会在 refcount 清零时立即回收,其 inode 也可能随之回收。所以对于 decode_fh 来说,如果想要在 decode 后仍然使用 inode 或者 dentry,就需要把 dentry 连接到 parent 上,再对 parent 逐次向上连接,直到 root。
为统一接口, dcache 提供了两个 helper,d_obtain_alias 和 d_splice_alias。他们现在已经成为所有 lookup 的最终调用函数。
- negative dentry
指文件树lookup过程中失败的项目在memory中的记录。其表现为与目录相关联的索引节点不存在,相应的磁盘索引节点已经被删除(相应的dentry暂时没有从dentry hash table中删除),方便non-exists文件的快速查找。negative dentry可能有助于那些经常敲错命令的场景,可以快速反馈不存在的文件/目录。
d_splice_alias(struct inode *inode, struct dentry *dentry)
我们在linux的内核解释中可以掌握:
NAME
d_splice_alias - splice a disconnected dentry into the tree if one exists
SYNOPSIS
struct dentry * d_splice_alias(struct inode * inode**, struct dentry *** dentry**);**
ARGUMENTS
inode
the inode which may have a disconnected dentry
dentry
a negative dentry which we want to point to the inode.
DESCRIPTION
If inode is a directory and has an IS_ROOT alias, then d_move that in place of the given dentry and return it, else simply d_add the inode to the dentry and return NULL.
If a non-IS_ROOT directory is found, the filesystem is corrupt, and we should error out: directories can't have multiple aliases.
This is needed in the lookup routine of any filesystem that is exportable (via knfsd) so that we can build dcache paths to directories effectively.
If a dentry was found and moved, then it is returned. Otherwise NULL is returned. This matches the expected return value of ->lookup.
Cluster filesystems may call this function with a negative, hashed dentry. In that case, we know that the inode will be a regular file, and also this will only occur during atomic_open. So we need to check for the dentry being already hashed only in the final case.
从d_splice_alias
dentry,它的行为将是让negative dentry指向inode,而这个3.10的内核行为是对ACFS的dentry返回的是EIO错误,而非去指向inode(即Oracle MOS上所说的将这个dentry加入到inode),这应该是新linux内核对ACFS的不兼容,本身redhat是没什么问题的,这属于期望结果。所以Oracle的做法是弄个补丁,或者降到老版本的linux内核。