2.5.0 以来的变化:¶
---
建议
新增辅助函数:`sb_bread()`、`sb_getblk()`、`sb_find_get_block()`、`set_bh()`、`sb_set_blocksize()` 和 `sb_min_blocksize()`。
使用它们。
(`sb_find_get_block()` 替换了 2.4 的 `get_hash_table()`)
---
建议
新增方法:`->alloc_inode()` 和 `->destroy_inode()`。
移除 `inode->u.foo_inode_i`
声明
struct foo_inode_info {
/* fs-private stuff */
struct inode vfs_inode;
};
static inline struct foo_inode_info *FOO_I(struct inode *inode)
{
return list_entry(inode, struct foo_inode_info, vfs_inode);
}
使用 `FOO_I(inode)` 代替 `&inode->u.foo_inode_i`;
添加 `foo_alloc_inode()` 和 `foo_destroy_inode()` - 前者应分配 `foo_inode_info` 并返回 `->vfs_inode` 的地址,后者应释放 `FOO_I(inode)`(请参阅内核文件系统中的示例)。
在你的 `super_operations` 中将它们设为 `->alloc_inode` 和 `->destroy_inode`。
请记住,现在你通常需要在调用 iget_locked()
和解锁 inode 之间显式初始化私有数据。
在某个时候,这将成为强制要求。
强制
`foo_inode_info` 应始终通过 `alloc_inode_sb()` 分配,而不是通过 kmem_cache_alloc()
或 kmalloc()
来正确设置 inode 回收上下文。
---
强制
`file_system_type` 方法的改变(`->read_super` 改为 `->get_sb`)
`->read_super()` 不再存在。`DECLARE_FSTYPE` 和 `DECLARE_FSTYPE_DEV` 也是如此。
将你的 `foo_read_super()` 转换为一个在成功时返回 0,在错误时返回负数(`-EINVAL`,除非你有更具信息量的错误值要报告)的函数。将其命名为 `foo_fill_super()`。现在声明
int foo_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
return get_sb_bdev(fs_type, flags, dev_name, data, foo_fill_super,
mnt);
}
(或根据文件系统类型,类似地使用 `s/bdev/nodev/` 或 `s/bdev/single/`)。
将 `DECLARE_FSTYPE...` 替换为显式初始化器,并将 `->get_sb` 设置为 `foo_get_sb`。
---
强制
锁的更改:`->s_vfs_rename_sem` 仅由跨目录重命名操作持有。很可能不需要更改任何内容,但如果你依赖重命名操作之间的全局排斥以实现某些内部目的,则需要更改你的内部锁。否则,排斥保证保持不变(即父目录和受害者被锁定等)。
---
信息
现在 `->lookup()` 与目录删除(通过 `->rmdir()` 和 `->rename()`)之间存在排斥。如果你以前需要这种排斥并通过内部锁定实现(大多数文件系统对此不关心),现在可以放宽锁定。
---
强制
现在 `->lookup()`、`->truncate()`、`->create()`、`->unlink()`、`->mknod()`、`->mkdir()`、`->rmdir()`、`->link()`、`->lseek()`、`->symlink()`、`->rename()` 和 `->readdir()` 在没有 BKL 的情况下被调用。在入口处获取它,在返回时释放它——这将保证你以前拥有的相同锁定。如果你的方法或其部分不需要 BKL——更好的是,现在你可以移动 `lock_kernel()` 和 `unlock_kernel()`,使它们精确保护需要保护的内容。
---
强制
BKL 也已从 `sb` 操作周围移开。BKL 应该已移入各个文件系统 `sb_op` 函数中。如果你不需要它,请将其移除。
---
信息
`->link()` 目标不是目录的检查由调用者完成。请随意删除它...
---
信息
`->link()` 调用者对我们链接到的对象持有 `->i_mutex`。你的一些问题可能已经解决了...
---
强制
新的 `file_system_type` 方法 - `kill_sb(superblock)`。如果你正在转换现有的文件系统,请根据 `->fs_flags` 设置它。
FS_REQUIRES_DEV - kill_block_super
FS_LITTER - kill_litter_super
neither - kill_anon_super
`FS_LITTER` 已移除 - 只需从 `fs_flags` 中移除即可。
---
强制
`FS_SINGLE` 已移除(实际上,这在 `->get_sb()` 加入时就已经发生了 - 但未被记录 ;-/)。只需从 `fs_flags` 中移除它(并查看 `->get_sb()` 条目以了解其他操作)。
---
强制
现在 `->setattr()` 在没有 BKL 的情况下被调用。调用者_总是_持有 `->i_mutex`,所以请注意 `->setattr()` 可能使用的获取 `->i_mutex` 的代码。notify_change()
的调用者现在需要 `->i_mutex`。
---
建议
新的 `super_block` 字段 struct export_operations *s_export_op
,用于显式支持导出,例如通过 NFS。该结构在其 `include/linux/fs.h` 中的声明以及《使文件系统可导出》文档中都有完整说明。
简而言之,它允许定义 `decode_fh` 和 `encode_fh` 操作来编码和解码文件句柄,并允许文件系统使用一个标准的辅助函数来处理 `decode_fh`,并为该辅助函数提供文件系统特定的支持,特别是 `get_parent`。
计划在代码稳定后,这将被要求用于导出。
强制
`s_export_op` 现在是导出文件系统所必需的。`isofs`、`ext2`、`ext3`、`fat` 可以作为非常不同文件系统的示例。
---
强制
`iget4()` 和 `read_inode2` 回调已被 iget5_locked()
取代,其原型如下
struct inode *iget5_locked(struct super_block *sb, unsigned long ino,
int (*test)(struct inode *, void *),
int (*set)(struct inode *, void *),
void *data);
‘test’ 是一个附加函数,当 inode 号不足以识别实际文件对象时可以使用。 “‘set’” 应该是一个非阻塞函数,用于初始化新创建 inode 的那些部分,以使测试函数能够成功。 “‘data’” 作为不透明值传递给 test 和 set 函数。
当 inode 由 iget5_locked()
创建时,它将返回时带有 `I_NEW` 标志集并仍处于锁定状态。文件系统需要完成初始化。一旦 inode 初始化完成,必须通过调用 unlock_new_inode()
来解锁它。
文件系统负责在适当的时候设置(并可能测试)`i_ino`。还有一个更简单的 `iget_locked` 函数,它只将超级块和 inode 号作为参数,并为你执行测试和设置。
例如:
inode = iget_locked(sb, ino);
if (inode->i_state & I_NEW) {
err = read_inode_from_disk(inode);
if (err < 0) {
iget_failed(inode);
return err;
}
unlock_new_inode(inode);
}
请注意,如果设置新 inode 的过程失败,则应在 inode 上调用 iget_failed()
以使其失效,并将适当的错误返回给调用者。
---
建议
`->getattr()` 最终被使用。请参阅 `nfs`、`minix` 等中的实例。
---
强制
`->revalidate()` 已移除。如果你的文件系统有它 - 提供 `->getattr()` 并让它调用你之前作为 `->revalidate()` 的内容 +(对于具有 `->revalidate()` 的符号链接)在 `->follow_link()`/`->readlink()` 中添加调用。
---
强制
`->d_parent` 的更改不再受 BKL 保护。如果以下至少一项为真,则读访问是安全的:
文件系统没有跨目录的 `rename()`
我们知道父目录已被锁定(例如,我们正在查看 `->lookup()` 参数的 `->d_parent`)。
我们是从 `->rename()` 调用。
子项的 `->d_lock` 被持有
审计你的代码并在需要时添加锁定。请注意,任何不受上述条件保护的地方即使在旧代码树中也存在风险 - 你一直依赖 BKL,这很容易出错。旧代码树中有不少此类漏洞 - 未受保护地访问 `->d_parent` 导致从 `oops` 到静默内存损坏的各种问题。
---
强制
`FS_NOMOUNT` 已移除。如果你使用它 - 只需在 `flags` 中设置 `SB_NOUSER`(参见 `rootfs` 了解一种解决方案,`bdev/socket/pipe` 了解另一种)。
---
建议
使用 `bdev_read_only(bdev)` 代替 `is_read_only(kdev)`。后者仍然存在,但只是因为 `drivers/s390/block/dasd.c` 中的混乱。一旦它被修复,`is_read_only()` 将被移除。
---
强制
现在 `->permission()` 在没有 BKL 的情况下被调用。在入口处获取它,在返回时释放它——这将保证你以前拥有的相同锁定。如果你的方法或其部分不需要 BKL——更好的是,现在你可以移动 `lock_kernel()` 和 `unlock_kernel()`,使它们精确保护需要保护的内容。
---
强制
现在 `->statfs()` 在不持有 BKL 的情况下被调用。BKL 应该已移入各个文件系统 `sb_op` 函数中,在那些不清楚是否可以安全移除它的地方。如果你不需要它,请将其移除。
---
强制
`is_read_only()` 已移除;请改用 `bdev_read_only()`。
---
强制
`destroy_buffers()` 已移除;请改用 `invalidate_bdev()`。
---
强制
`fsync_dev()` 已移除;请改用 `fsync_bdev()`。注意:lvm 破坏是故意的;一旦 `struct block_device *` 通过该代码以合理的方式传播,修复将变得微不足道;在此之前无能为力。
强制
`->write_begin` 和 `->direct_IO` 错误退出时的块截断已从通用方法(`block_write_begin`、`cont_write_begin`、`nobh_write_begin`、`blockdev_direct_IO*`)移至调用者。请查看 `ext2_write_failed` 及其调用者以获取示例。
强制
`->truncate` 已移除。整个截断序列需要在 `->setattr` 中实现,这对于实现磁盘上大小更改的文件系统现在是强制性的。从旧的 `inode_setattr` 和 `vmtruncate` 的副本开始,并重新排序 `vmtruncate + foofs_vmtruncate` 序列,以使用 `block_truncate_page` 或类似辅助函数进行块清零、大小更新以及最终不会失败的磁盘截断。`setattr_prepare`(以前是 `inode_change_ok`)现在包含 `ATTR_SIZE` 的大小检查,并且必须无条件地在 `->setattr` 的开头调用。
强制
`->clear_inode()` 和 `->delete_inode()` 已移除;应改用 `->evict_inode()`。无论 inode 是否有剩余链接,只要它被逐出,就会调用此函数。调用者_不_会逐出页缓存或与 inode 关联的元数据缓冲区;该方法必须使用 truncate_inode_pages_final()
来处理这些。调用者确保在调用 `->evict_inode()` 期间(或之后),inode 的异步回写不能正在运行。
`->drop_inode()` 现在返回 `int` 类型;它在最终的 iput()
调用中被调用,此时 `inode->i_lock` 被持有,如果文件系统希望丢弃 inode,它将返回 `true`。和以前一样,`generic_drop_inode()` 仍然是默认的,并且已相应更新。`generic_delete_inode()` 也仍然存在,它只是简单地返回 1。请注意,所有实际的逐出工作都在 `->drop_inode()` 返回后由调用者完成。
和以前一样,`clear_inode()` 必须在每次调用 `->evict_inode()` 时只被调用一次(就像以前每次调用 `->delete_inode()` 时一样)。与以前不同的是,如果你正在使用与 inode 相关的元数据缓冲区(即 `mark_buffer_dirty_inode()`),你需要在 `clear_inode()` 之前调用 `invalidate_inode_buffers()`。
注意:在 `->write_inode()` 开头检查 `i_nlink` 并如果它为零就退出,这在过去和现在都_不够_。最终的 `unlink()` 和 iput()
可能在 inode 正在执行 `->write_inode()` 期间发生;例如,如果你盲目地释放磁盘上的 inode,你可能会在 `->write_inode()` 正在写入它时执行此操作。
---
强制
.d_delete()
现在只建议 `dcache` 是否缓存未引用的 `dentry`,并且现在仅在 `dentry` 引用计数变为 0 时才调用。即使在引用计数从 0 转换时,它也必须能够容忍被调用 0、1 或更多次(例如,常量、幂等)。
---
强制
`.d_compare()` 的调用约定和锁定规则已显著改变。请阅读《Linux 虚拟文件系统概述》中的更新文档(并查看其他文件系统的示例)以获取指导。
---
强制
`.d_hash()` 的调用约定和锁定规则已显著改变。请阅读《Linux 虚拟文件系统概述》中的更新文档(并查看其他文件系统的示例)以获取指导。
---
强制
`dcache_lock` 已移除,取而代之的是细粒度锁。请参阅 `fs/dcache.c` 以了解用于替换 `dcache_lock` 以保护特定事物的锁的详细信息。大多数情况下,文件系统只需要 `->d_lock`,它保护给定 `dentry` 的_所有_ `dcache` 状态。
---
强制
如果文件系统可以通过 rcu-walk 路径遍历访问其 inode(基本上,如果文件在 `vfs` 命名空间中可以有路径名),则必须对它们的 inode 进行 RCU 释放。
尽管 `i_dentry` 和 `i_rcu` 在联合中共享存储,但我们将在 `inode_init_always()` 中初始化前者,因此在回调中不要动它。以前需要在那里清理它,但现在不需要了(从 3.2 版本开始)。
---
建议
`vfs` 现在尝试以“rcu-walk 模式”进行路径遍历,这避免了 `dentry` 和 `inode` 上的原子操作和可伸缩性隐患(参见《路径名查找》)。`d_hash` 和 `d_compare` 的更改(如上所述)是支持此功能所需更改的示例。对于更复杂的文件系统回调,`vfs` 在文件系统调用之前会退出 `rcu-walk` 模式,因此文件系统无需进行任何更改。但是,这代价高昂并失去了 `rcu-walk` 模式的优势。我们将开始添加支持 `rcu-walk` 的文件系统回调,如下所示。文件系统应尽可能利用这一点。
---
强制
`d_revalidate` 是一个在每个路径元素上执行的回调(如果文件系统提供),这需要退出 rcu-walk 模式。现在可以在 rcu-walk 模式下调用此函数(`nd->flags & LOOKUP_RCU`)。如果文件系统无法处理 rcu-walk,则应返回 `-ECHILD`。请参阅《Linux 虚拟文件系统概述》以获取更多详细信息。
`permission` 是一个 inode 权限检查,在路径遍历过程中会对许多或所有目录 inode 进行调用(以检查执行权限)。现在它必须支持 rcu-walk(`mask & MAY_NOT_BLOCK`)。请参阅《Linux 虚拟文件系统概述》以获取更多详细信息。
---
强制
在 `->fallocate()` 中,你必须检查传入的 `mode` 选项。如果你的文件系统不支持“打孔”(在文件中间释放空间),则如果 `mode` 中设置了 `FALLOC_FL_PUNCH_HOLE`,你必须返回 `-EOPNOTSUPP`。目前,你只能将 `FALLOC_FL_PUNCH_HOLE` 与 `FALLOC_FL_KEEP_SIZE` 一起设置,因此在“打孔”时,即使打掉文件末尾,`i_size` 也不应改变。
---
强制
`->get_sb()` 已移除。切换到使用 `->mount()`。通常,这只是将调用 get_sb_
... 更改为 mount_
... 并更改函数类型的问题。如果你是手动操作,只需将设置 `->mnt_root` 更改为返回该指针。在错误时返回 `ERR_PTR(...)`。
---
强制
`->permission()` 和 generic_permission()
已移除 `flags` 参数;现在我们不是传入 `IPERM_FLAG_RCU`,而是将 `MAY_NOT_BLOCK` 添加到 `mask` 中。
generic_permission()
也已移除 `check_acl` 参数;ACL 检查已移至 VFS,文件系统需要提供一个非 NULL 的 `->i_op->get_inode_acl` 来从磁盘读取 ACL。
---
强制
如果你实现自己的 `->llseek()`,则必须处理 `SEEK_HOLE` 和 `SEEK_DATA`。你可以通过返回 `-EINVAL` 来处理,但以某种方式支持它会更好。通用处理程序假定整个文件都是数据,并且文件末尾有一个虚拟空洞。因此,如果提供的偏移量小于 `i_size` 并且指定了 `SEEK_DATA`,则返回相同的偏移量。如果上述条件对偏移量成立并且你获得了 `SEEK_HOLE`,则返回文件末尾。如果偏移量大于或等于 `i_size`,则在这两种情况下都返回 `-ENXIO`。
强制
如果你有自己的 `->fsync()`,则必须确保调用 filemap_write_and_wait_range()
,以便所有脏页都能正确同步。你还必须记住 `->fsync()` 不再在持有 `i_mutex` 的情况下被调用,因此如果你需要 `i_mutex` 锁定,则必须确保自行获取和释放它。
---
强制
`d_alloc_root()` 已移除,同时解决了许多因代码误用它而导致的错误。替换:`d_make_root(inode)`。成功时,`d_make_root(inode)` 分配并返回一个使用传入的 inode 实例化的新 `dentry`。失败时返回 `NULL`,并且传入的 inode 被释放,因此在所有情况下对 inode 的引用都被消耗,并且错误处理无需对 inode 进行任何清理。如果 `d_make_root(inode)` 传入 `NULL` 的 inode,它将返回 `NULL` 并且也无需进一步的错误处理。典型用法是
inode = foofs_new_inode(....);
s->s_root = d_make_root(inode);
if (!s->s_root)
/* Nothing needed for the inode cleanup */
return -ENOMEM;
...
---
强制
巫婆已死!好吧,至少死了 2/3。`->d_revalidate()` 和 `->lookup()` 不再接受 `struct nameidata` 参数;只接受 `flags`。
---
强制
`->create()` 不再接受 struct nameidata *
;与前两个不同,它现在接受一个布尔参数,表示“它是否是 `O_EXCL` 或等效?”。请注意,本地文件系统可以忽略此参数 - 它们保证对象不存在。远程/分布式文件系统可能会关心...
---
强制
`FS_REVAL_DOT` 已移除;如果你以前有它,请改为在你的 `dentry` 操作中添加 `->d_weak_revalidate()`。
---
强制
`vfs_readdir()` 已移除;请改为使用 `iterate_dir()`。
---
强制
`->readdir()` 现已移除;请切换到 `->iterate_shared()`。
强制
`vfs_follow_link` 已移除。文件系统必须对普通符号链接使用 `->follow_link` 中的 `nd_set_link`,或对魔术 `/proc/
---
强制
iget5_locked()
/ilookup5()
/ilookup5_nowait()
的 `test()` 回调以前在持有 `->i_lock` 和 `inode_hash_lock` 的情况下调用;现在前者_不再_被持有,因此请验证你的回调不依赖于它(内核中的实例都没有)。当然,`inode_hash_lock` 仍然被持有,因此它们在从 inode 哈希中移除方面以及在 iget5_locked()
的 `set()` 回调方面仍然是序列化的。
---
强制
`d_materialise_unique()` 已移除;现在 d_splice_alias()
可以完成你所需的一切。请记住它们的参数顺序是相反的 ;-/
---
强制
`f_dentry` 已移除;请使用 `f_path.dentry`,或者更好的是,看看你是否可以完全避免它。
---
强制
永远不要直接调用 `->read()` 和 `->write()`;请使用 `__vfs_{read,write}` 或包装器;不要检查 `->write` 或 `->read` 是否为 `NULL`,而应在 `file->f_mode` 中查找 `FMODE_CAN_{WRITE,READ}`。
---
强制
`->read` / `->write` _不要_使用 `new_sync_{read,write}`;请将其设置为 `NULL`。
---
- 强制
`->aio_read` / `->aio_write` 已移除。请改用 `->read_iter` / `->write_iter`。
---
建议
对于嵌入式(“快速”)符号链接,只需将 `inode->i_link` 设置为符号链接主体所在的位置,并使用 `simple_follow_link()` 作为 `->follow_link()`。
---
强制
`->follow_link()` 的调用约定已更改。以前是返回 cookie 并使用 `nd_set_link()` 存储要遍历的主体,现在我们返回要遍历的主体,并使用显式的 `void **` 参数存储 cookie。`nameidata` 完全不传入 - `nd_jump_link()` 不需要它,并且 `nd_[gs]et_link()` 已移除。
---
强制
`->put_link()` 的调用约定已更改。它接收 `inode` 而不是 `dentry`,完全不接收 `nameidata`,并且仅在 `cookie` 非 NULL 时被调用。请注意,链接主体不再可用,因此如果你需要它,请将其存储为 `cookie`。
---
强制
任何可能使用 `page_follow_link_light`/page_put_link()
的符号链接,在任何可能开始操作其页缓存之前,都必须调用 `inode_nohighmem(inode)`。此类符号链接的页缓存中不应出现高端内存页。这包括在符号链接创建期间可能进行的任何预置。`page_symlink()` 将遵守映射 `gfp` 标志,因此一旦你执行了 `inode_nohighmem()`,就可以安全使用它,但如果你手动分配和插入页,请确保使用正确的 `gfp` 标志。
---
强制
`->follow_link()` 已被 `->get_link()` 替换;API 相同,除了:
`->get_link()` 将 `inode` 作为单独的参数
`->get_link()` 可以在 RCU 模式下调用 - 在这种情况下,传入 `NULL dentry`
---
强制
`->get_link()` 现在获得 `struct delayed_call *done
`,并且应该在以前设置 *cookie
的地方执行 `set_delayed_call()`。
`->put_link()` 已移除 - 只需在 `->get_link()` 中将析构函数传递给 `set_delayed_call()`。
---
强制
`->getxattr()` 和 `xattr_handler.get()` 分别接收 `dentry` 和 `inode`。`dentry` 可能尚未附加到 `inode`,因此在实例中_不要_使用其 `->d_inode`。理由是:在我们将 `dentry` 附加到 `inode` 之前,需要调用 security_d_instantiate()
。
---
强制
符号链接不再是唯一在 inode 逐出时 `i_bdev`/`i_cdev`/`i_pipe`/`i_link` 联合不归零的 inode。因此,你不能假设在 `->destroy_inode()` 中 `->i_nlink` 的非 NULL 值意味着它是符号链接。现在确实需要检查 `->i_mode`。在内核中,我们不得不修复 `shmem_destroy_callback()`,它曾经使用过这种捷径;请注意,该捷径不再有效。
---
强制
`->i_mutex` 现在被 `->i_rwsem` 替换。`inode_lock()` 等函数像以前一样工作 - 它们只是独占地获取它。但是,`->lookup()` 可以在父级共享锁定的情况下被调用。它的实例不能
分别使用 `d_instantiate()` 和
d_rehash()
- 请改用d_add()
或d_splice_alias()
。单独使用
d_rehash()
- 请改调用 `d_add(new_dentry, NULL)`。在不太可能的情况下,如果出于某种原因,对文件系统数据结构(只读)的访问需要排除,请自行安排。内核中的文件系统都没有这种需求。
依赖于 `->d_parent` 和 `->d_name` 在 `dentry` 被传递给
d_add()
或d_splice_alias()
后不改变。同样,内核中的实例都没有依赖这一点。
我们保证在同一目录下查找相同名称不会并行发生(“相同”指的是你的 `->d_compare()`)。现在,在同一目录下查找不同名称可以并行发生。
---
强制
`->iterate_shared()` 已添加。文件 (struct file
) 级别的排斥仍然提供(以及它与同一文件 (struct file
) 上的 `lseek` 之间的排斥),但如果你的目录已被多次打开,你可以并行调用它们。当然,该方法与所有目录修改方法之间的排斥仍然提供。
如果你有任何由 `->iterate_shared()` 修改的每 inode 或每 dentry 的内存中数据结构,你可能需要某种方式来序列化对它们的访问。如果你进行 `dcache` 预填充,你需要切换到 `d_alloc_parallel()`;请查找内核中的示例。
---
强制
`->atomic_open()` 不带 `O_CREAT` 的调用可以并行发生。
---
强制
`->setxattr()` 和 `xattr_handler.set()` 分别接收 `dentry` 和 `inode`。`xattr_handler.set()` 会传入 inode 所属挂载的用户命名空间,以便文件系统可以相应地映射 `i_uid` 和 `i_gid`。`dentry` 可能尚未附加到 `inode`,因此在实例中_不要_使用其 `->d_inode`。理由是:在我们将 `dentry` 附加到 `inode` 之前,需要调用 security_d_instantiate()
,而且 `smack ->`d_instantiate()
不仅使用 `->getxattr()`,还使用 `->setxattr()`。
---
强制
`->d_compare()` 不再单独接收 `parent` 参数。如果你以前用它来查找相关的 `struct super_block`,那么 `dentry->d_sb` 同样有效;如果更复杂,请使用 `dentry->d_parent`。请注意不要假设多次获取它会产生相同的值 - 在 RCU 模式下它可能会在你不知情的情况下改变。
---
强制
`->rename()` 增加了一个 `flags` 参数。文件系统未处理的任何标志都应导致返回 `EINVAL`。
---
建议
`->readlink` 对于符号链接是可选的。不要设置,除非文件系统需要为 `readlink(2)` 模拟一些东西。
---
强制
`->getattr()` 现在传递 `struct path` 而不是单独的 `vfsmount` 和 `dentry`,并且它现在有 `request_mask` 和 `query_flags` 参数来指定 `statx` 请求的字段和同步类型。不支持任何 `statx` 特定功能的文件系统可以忽略新参数。
---
强制
`->atomic_open()` 的调用约定已更改。`int *opened` 以及 `FILE_OPENED`/`FILE_CREATED` 已移除。取而代之的是 `FMODE_OPENED`/`FMODE_CREATED`,设置在 `file->f_mode` 中。此外,对于“已调用 `finish_no_open()`,请自行打开”的情况,返回值已变为 0,而不是 1。由于 `finish_no_open()` 本身现在返回 0,因此 `->atomic_open()` 实例的该部分不需要任何更改。
---
强制
`alloc_file()` 现在已成为静态;应改用两个包装器。`alloc_file_pseudo(inode, vfsmount, name, flags, ops)` 用于需要创建 `dentry` 的情况;这是旧 `alloc_file()` 用户的大多数。调用约定:成功时返回对新 struct file
的引用,并且调用者对 inode 的引用被其取代。失败时,返回 ERR_PTR()
,且不影响调用者的任何引用,因此调用者需要释放其持有的 inode 引用。alloc_file_clone(file, flags, ops)
不影响任何调用者的引用。成功时,你将获得一个新的 struct file
,它与原始文件共享挂载/`dentry`;失败时则返回 ERR_PTR()
。
---
强制
`->clone_file_range()` 和 `->dedupe_file_range` 已被 `->remap_file_range()` 替换。请参阅《Linux 虚拟文件系统概述》以获取更多信息。
---
建议
`->lookup()` 实例执行以下等效操作:
if (IS_ERR(inode))
return ERR_CAST(inode);
return d_splice_alias(inode, dentry);
无需费心检查 - d_splice_alias()
当接收 `ERR_PTR(...)` 作为 inode 时,将正确处理。此外,向 d_splice_alias()
传递 `NULL` inode 也会正确处理(相当于 `d_add(dentry, NULL); return NULL;`),因此这类特殊情况也不需要单独处理。
---
强烈建议
将 `->destroy_inode()` 的 RCU 延迟部分移到新方法 - `->free_inode()` 中。如果 `->destroy_inode()` 变为空 - 那就更好了,直接删除它。同步工作(例如,不能从 RCU 回调完成的工作,或者任何我们想要堆栈跟踪的 `WARN_ON()`)_可能_可以移动到 `->evict_inode()`;但是,这仅适用于不需要平衡 `->alloc_inode()` 所做的事情。换句话说,如果它是在清理内存中 inode 生命周期中可能积累的东西,`->evict_inode()` 可能是一个合适的选择。
inode 销毁规则
如果 `->destroy_inode()` 非 NULL,则会被调用
如果 `->free_inode()` 非 NULL,则由
call_rcu()
调度`NULL ->destroy_inode` 和 `NULL ->free_inode` 的组合被视为 `NULL/free_inode_nonrcu`,以保持兼容性。
请注意,回调(无论是通过 `->free_inode()` 还是 `->destroy_inode()` 中显式调用 call_rcu()
)与超级块销毁_不_按顺序执行;实际上,超级块和所有相关结构可能已经消失。文件系统驱动程序保证仍然存在,但仅此而已。在回调中释放内存是没问题的;做更多的事情是可能的,但需要非常小心,最好避免。
---
强制
`DCACHE_RCUACCESS` 已移除;dentry 释放时具有 RCU 延迟是默认行为。`DCACHE_NORCU` 选择退出,并且只有 `d_alloc_pseudo()` 才有理由这样做。
---
强制
`d_alloc_pseudo()` 仅供内部使用;在 `alloc_file_pseudo()` 之外使用它非常可疑(并且在模块中不起作用)。此类使用很可能是拼写错误的 `d_alloc_anon()`。
---
强制
[本应在 2016 年添加] 尽管 `finish_open()` 中存在陈旧注释,但 `->atomic_open()` 实例中的失败退出_不_应该 `fput()` 文件,无论如何。一切都由调用者处理。
---
强制
clone_private_mount()
现在返回一个长期挂载点,因此其结果的正确析构函数是 `kern_unmount()` 或 `kern_unmount_array()`。
---
强制
零长度的 `bvec` 段是不允许的,在传递给迭代器之前必须将其过滤掉。
---
强制
对于基于 bvec 的迭代器,bio_iov_iter_get_pages()
现在不复制 bvecs,而是使用提供的那个。任何发出 `kiocb-I/O` 的人应确保 bvec 和页引用在 I/O 完成之前保持有效,即直到 `->ki_complete()` 被调用或返回非 `-EIOCBQUEUED` 代码。
---
强制
mnt_want_write_file()
现在只能与 `mnt_drop_write_file()` 配对使用,而以前也可以与 mnt_drop_write()
配对使用。
---
强制
`iov_iter_copy_from_user_atomic()` 已移除;请改用 `copy_page_from_iter_atomic()`。区别在于 `copy_page_from_iter_atomic()` 会推进迭代器,之后你无需调用 `iov_iter_advance()`。但是,如果你决定只使用获得数据的一部分,则应执行 `iov_iter_revert()`。
---
强制
`file_open_root()` 的调用约定已更改;现在它接受 `struct path *` 而不是单独传递 `mount` 和 `dentry`。对于以前传递 `
---
强制
`no_llseek` 已移除;不要将 `.llseek` 设置为它 - 只需将其保留为 `NULL`。检查“该文件是否支持 `llseek(2)`,或者它是否应该因 `ESPIPE` 失败”应通过查看 `file->f_mode` 中的 `FMODE_LSEEK` 来完成。
---
强制
`filldir_t` (`readdir` 回调) 的调用约定已更改。现在它返回 `bool`,而不是返回 0 或 `-E...`。`false` 意味着“没有更多”(就像以前的 `-E...`),`true` 意味着“继续”(就像旧调用约定中的 0)。理由是:调用者无论如何都不会查看特定的 `-E...` 值。`->iterate_shared()` 实例完全不需要更改,所有内核中的 `filldir_t` 都已转换。
---
强制
`->tmpfile()` 的调用约定已更改。它现在接受一个 struct file
指针而不是 `struct dentry` 指针。`d_tmpfile()` 类似地进行了更改以简化调用者。传入的文件处于非打开状态,成功时必须在返回之前打开(例如,通过调用 `finish_open_simple()`)。
---
强制
`->huge_fault` 的调用约定已更改。它现在接受一个页面顺序而不是 `enum page_entry_size`,并且可以在不持有 `mmap_lock` 的情况下被调用。所有内核中的用户都已进行审计,并且似乎不依赖于持有 `mmap_lock`,但树外的用户应自行验证。如果他们确实需要,他们可以返回 `VM_FAULT_RETRY` 以在持有 `mmap_lock` 的情况下被调用。
---
强制
打开块设备以及匹配或创建超级块的顺序已更改。
旧逻辑首先打开块设备,然后尝试根据块设备指针查找可重用的合适超级块。
新逻辑首先尝试根据设备号查找合适的超级块,然后打开块设备。
由于锁排序要求,打开块设备不能在 `s_umount` 下进行,因此现在在打开块设备时释放 `s_umount`,并在调用 `fill_super()` 之前重新获取。
在旧逻辑中,并发挂载者会在文件系统类型的超级块列表中找到超级块。由于块设备的第一个打开者会持有 `s_umount`,他们会等待直到超级块诞生或因初始化失败而被丢弃。
由于新逻辑会释放 `s_umount`,并发挂载者可能会获取 `s_umount` 并导致自旋。现在改为使用显式的等待-唤醒机制使其等待,而无需持有 `s_umount`。
---
强制
块设备的持有者现在是超级块。
块设备的持有者以前是 `file_system_type`,这并不是特别有用。如果不匹配存储在超级块中的设备指针,就不可能从块设备找到拥有的超级块。这种机制仅适用于单个设备,因此块层无法找到任何附加设备的拥有超级块。
在旧机制中,为竞争性的 `mount(2)` 和 `umount(2)` 重用或创建超级块依赖于 `file_system_type` 作为持有者。然而,这方面的文档严重不足
任何设法获取现有超级块活动引用的并发挂载者都被要求等待,直到超级块准备就绪,或者直到超级块从文件系统类型的超级块列表中移除。如果超级块已准备就绪,调用者将简单地重用它。
如果挂载者在
deactivate_locked_super()
之后但在超级块从文件系统类型的超级块列表中移除之前到达,挂载者将等待直到超级块关闭,然后重用块设备并分配一个新的超级块。如果挂载者在
deactivate_locked_super()
之后且超级块从文件系统类型的超级块列表中移除之后到达,挂载者将重用块设备并分配一个新的超级块(`bd_holder` 指针可能仍设置为文件系统类型)。
因为块设备的持有者是 `file_system_type`,任何并发挂载者都可以打开相同 `file_system_type` 的任何超级块的块设备,而不会冒着看到 `EBUSY` 的风险,因为该块设备仍被另一个超级块使用。
将超级块设为块设备的拥有者改变了这一点,因为现在持有者是唯一的超级块,因此与它关联的块设备不能被并发挂载者重用。因此,(2) 中的并发挂载者在尝试打开一个其持有者是不同超级块的块设备时,可能会突然看到 `EBUSY`。
因此,新逻辑会等待,直到超级块和设备在 `->kill_sb()` 中关闭。现在,从文件系统类型的超级块列表中移除超级块的操作已移至设备关闭后的较晚时间。
任何设法获取现有超级块活动引用的并发挂载者都被要求等待,直到超级块准备就绪,或者直到超级块和所有设备都在 `->kill_sb()` 中关闭。如果超级块已准备就绪,调用者将简单地重用它。
如果挂载者在
deactivate_locked_super()
之后但在超级块从文件系统类型的超级块列表中移除之前到达,则挂载者将被要求等待,直到超级块和设备在 `->kill_sb()` 中关闭,并且超级块从文件系统类型的超级块列表中移除。挂载者将分配一个新的超级块并获取块设备的拥有权(块设备的 `bd_holder` 指针将被设置为新分配的超级块)。这种情况现在已归入 (2) 中,因为超级块会保留在文件系统类型的超级块列表中,直到所有设备在 `->kill_sb()` 中关闭。换句话说,如果超级块不再在文件系统类型的超级块列表中,那么它已经放弃了所有关联块设备的拥有权(`bd_holder` 指针为 `NULL`)。
由于这是一个 VFS 级别的更改,除了所有文件系统都必须使用提供的 `kill_litter_super()`、`kill_anon_super()` 或 `kill_block_super()` 辅助函数之一外,它对文件系统没有实际影响。
---
强制
锁排序已更改,使 `s_umount` 再次高于 `open_mutex`。所有在 `open_mutex` 下获取 `s_umount` 的地方都已修复。
---
强制
`export_operations ->encode_fh()` 不再具有编码 `FILEID_INO32_GEN*` 文件句柄的默认实现。使用默认实现的文件系统可以显式使用通用辅助函数 generic_encode_ino32_fh()
。
---
强制
如果 `->rename()` 在跨目录移动时更新 `..` 需要与目录修改互斥,则_不要_在你的 `->rename()` 中锁定相关子目录 - 现在由调用者完成 [该条目应已在 28eceeda130f "fs: Lock moved directories" 中添加]。
---
强制
在同目录 `->rename()` 中,`..` 的(同义反复的)更新不受任何锁的保护;如果旧父目录与新父目录相同,就不要这样做。我们真的不能在同目录重命名中锁定两个子目录 - 否则会导致死锁。
---
强制
`lock_rename()` 和 `lock_rename_child()` 在跨目录情况下可能会失败,如果它们的参数没有共同的祖先。在这种情况下,返回 `ERR_PTR(-EXDEV)`,不获取任何锁。内核内用户已更新;内核外用户需要这样做。
---
强制
锚定在父 dentry 中的子列表现在已变为 `hlist`。字段名称已更改(锚点/条目分别使用 `->d_children`/`->d_sib` 而不是 `->d_subdirs`/`->d_child`),因此任何受影响的地方将立即被编译器捕获。
---
强制
`->`d_delete()
实例现在在持有 `->d_lock` 且引用计数为 0 的 `dentry` 上被调用。它们不允许释放/重新获取 `->d_lock`。内核中的实例都没有进行此类操作。请确保你的实例也没有...
---
强制
`->d_prune()` 实例现在在父级不持有 `->d_lock` 的情况下被调用。`dentry` 本身的 `->d_lock` 仍然被持有;如果你需要每父级排除(内核中的实例都没有),请使用你自己的自旋锁。
`->d_iput()` 和 `->d_release()` 在受害者 dentry 仍在父级子项列表中时被调用。它仍然未被哈希,被标记为已终止等,只是尚未从父级的 `->d_children` 中移除。
任何遍历子列表的人都需要注意可能在那里看到的半终止 `dentry`;对它们获取 `->d_lock` 将使它们显示为负值、未哈希且引用计数为负,这意味着大多数内核内部用户无论如何都会在没有任何调整的情况下做正确的事情。
---
建议
块设备冻结和解冻已移至持有者操作。
在此更改之前,`get_active_super()` 只能找到主块设备的超级块,即存储在 `sb->s_bdev` 中的那个。现在,块设备冻结适用于给定超级块拥有的任何块设备,而不仅仅是主块设备。`get_active_super()` 辅助函数和 `bd_fsfreeze_sb` 指针已移除。
---
强制
`set_blocksize()` 现在接受已打开的 struct file
而不是 `struct block_device`,并且它_必须_以独占方式打开。
---
强制
`->d_revalidate()` 接收两个额外参数 - 父目录的 inode 以及我们的 `dentry` 预期具有的名称。两者都是稳定的(在非 RCU 情况下目录被固定并在调用期间保持不变,并且名称保证保持不变)。你的实例不必使用任何一个,但它通常有助于避免大量繁琐的样板代码。请注意,虽然 `name->name` 是稳定的且以 NULL 终止,但它可能(而且通常会)将 `name->name[name->len]` 等于 `'/'` 而不是 `'0'` - 在正常情况下它指向正在查找的路径名。注意:如果你需要诸如文件系统根目录的完整路径之类的内容,你仍然需要自行处理 - 这有助于处理简单情况,但它不是魔术。
---
建议
`kern_path_locked()` 和 `user_path_locked()` 不再返回负 `dentry`,因此无需检查。如果找不到名称,则返回 `ERR_PTR(-ENOENT)`。
---
建议
`lookup_one_qstr_excl()` 已更改为在更多情况下返回错误,因此这些条件不需要显式检查
如果未给定 `LOOKUP_CREATE`,则 `dentry` 不会为负,而是返回 `ERR_PTR(-ENOENT)`
如果给定 `LOOKUP_EXCL`,则 `dentry` 不会为正,而是返回 `ERR_PTR(-EEXIST)`
`LOOKUP_EXCL` 现在意味着“目标必须不存在”。它可以与 `LOOK_CREATE` 或 `LOOKUP_RENAME_TARGET` 结合使用。
---
强制 `invalidate_inodes()` 已移除,请改用 evict_inodes()
。
---
强制
`->mkdir()` 现在返回一个 `dentry`。如果创建的 inode 被发现在缓存中并且有一个 `dentry`(通常是 `IS_ROOT()`),它将需要被拼接(splice)到给定的名称中,取代给定的 `dentry`。现在需要返回该 `dentry`。如果使用原始 `dentry`,则应返回 `NULL`。任何错误都应通过 ERR_PTR()
返回。
通常,使用 `d_instantiate_new()` 安装新 inode 的文件系统可以安全地返回 `NULL`。可能没有 `I_NEW` inode 的文件系统应使用 d_drop()
;d_splice_alias()
并返回后者的结果。
如果出于某种原因无法返回正向 `dentry`,内核中的客户端(如 `cachefiles`、`nfsd`、`smb/server`)可能无法理想运行,但会以安全模式失败。
---
** 强制**
lookup_one()
、lookup_one_unlocked()
、lookup_one_positive_unlocked()
现在接受 `qstr` 而不是 `name` 和 `len`。每当通过挂载点从文件系统外部访问文件系统时(该挂载点将具有 `mnt_idmap`),应使用这些函数,而不是“`one_len`”版本。
---
** 强制**
函数 `try_lookup_one_len()`、`lookup_one_len()`、`lookup_one_len_unlocked()` 和 `lookup_positive_unlocked()` 已重命名为 try_lookup_noperm()
、lookup_noperm()
、lookup_noperm_unlocked()
、`lookup_noperm_positive_unlocked()`。它们现在接受 `qstr` 而不是单独的 `name` 和 `length`。当需要 strlen()
来获取长度时,可以使用 `QSTR()`。
对于 try_lookup_noperm()
,会传递 `qstr` 的引用,以防后续可能需要哈希值。
这些函数不再执行任何权限检查 - 它们以前检查调用者是否对父级具有“X”权限。它们只能由文件系统内部在它知道权限不相关或在已执行权限检查的上下文中(例如在 vfs_path_parent_lookup()
之后)使用。
---
** 强制**
`d_hash_and_lookup()` 不再导出或在 VFS 外部可用。请改用 try_lookup_noperm()
。这会添加名称验证并以相反的顺序接受参数,但其他方面是相同的。
使用 try_lookup_noperm()
将需要包含 `linux/namei.h`。
---
强制
`->d_automount()` 的调用约定已更改;我们_不_应该为新的挂载获取额外的引用 - 它应该以引用计数 1 返回。
---
`collect_mounts()`/`drop_collected_mounts()`/`iterate_mounts()` 现在已移除。替换为 `collect_paths()`/`drop_collected_path()`,无需特殊迭代器。新接口不再返回克隆的挂载树,而是返回一个 `struct path` 数组,每个 `collect_mounts()` 会创建的挂载点都有一个。这些 `struct path` 指向调用者命名空间中的位置,这些位置将是克隆挂载点的根。