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/` 樣式連結使用 `nd_jump_link`。

---

強制

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`。對於以前傳遞 `mnt_root>` 對(即給定掛載點的根)的呼叫者,現在提供了一個新的輔助函式 - `file_open_root_mnt()`。核心中的使用者已進行調整。

---

強制

`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` 作為持有者。然而,這方面的文件嚴重不足

  1. 任何設法獲取現有超級塊活動引用的併發掛載者都被要求等待,直到超級塊準備就緒,或者直到超級塊從檔案系統型別的超級塊列表中移除。如果超級塊已準備就緒,呼叫者將簡單地重用它。

  2. 如果掛載者在 deactivate_locked_super() 之後但在超級塊從檔案系統型別的超級塊列表中移除之前到達,掛載者將等待直到超級塊關閉,然後重用塊裝置並分配一個新的超級塊。

  3. 如果掛載者在 deactivate_locked_super() 之後且超級塊從檔案系統型別的超級塊列表中移除之後到達,掛載者將重用塊裝置並分配一個新的超級塊(`bd_holder` 指標可能仍設定為檔案系統型別)。

因為塊裝置的持有者是 `file_system_type`,任何併發掛載者都可以開啟相同 `file_system_type` 的任何超級塊的塊裝置,而不會冒著看到 `EBUSY` 的風險,因為該塊裝置仍被另一個超級塊使用。

將超級塊設為塊裝置的擁有者改變了這一點,因為現在持有者是唯一的超級塊,因此與它關聯的塊裝置不能被併發掛載者重用。因此,(2) 中的併發掛載者在嘗試開啟一個其持有者是不同超級塊的塊裝置時,可能會突然看到 `EBUSY`。

因此,新邏輯會等待,直到超級塊和裝置在 `->kill_sb()` 中關閉。現在,從檔案系統型別的超級塊列表中移除超級塊的操作已移至裝置關閉後的較晚時間。

  1. 任何設法獲取現有超級塊活動引用的併發掛載者都被要求等待,直到超級塊準備就緒,或者直到超級塊和所有裝置都在 `->kill_sb()` 中關閉。如果超級塊已準備就緒,呼叫者將簡單地重用它。

  2. 如果掛載者在 deactivate_locked_super() 之後但在超級塊從檔案系統型別的超級塊列表中移除之前到達,則掛載者將被要求等待,直到超級塊和裝置在 `->kill_sb()` 中關閉,並且超級塊從檔案系統型別的超級塊列表中移除。掛載者將分配一個新的超級塊並獲取塊裝置的擁有權(塊裝置的 `bd_holder` 指標將被設定為新分配的超級塊)。

  3. 這種情況現在已歸入 (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` 指向呼叫者名稱空間中的位置,這些位置將是克隆掛載點的根。