一次XFS死鎖問題分析
kernel version: 5.1.0
現(xiàn)象
XFS 文件系統(tǒng)出現(xiàn)掛起(hung),業(yè)務(wù)進程大量處于 D 狀態(tài);多處堆棧顯示卡在 xfs_iget 與 xfs_fs_destroy_inode 路徑上。
診斷過程
關(guān)鍵堆棧1(最早D住的進程,釋放inode卡住)
- 進程:
postgres - PID:
202276 - 癥狀:在
xfs_fs_destroy_inode路徑上等待,向下追溯可見正在嘗試讀取/獲取 AGF 緩沖(xfs buf),而該鎖被其他事務(wù)持有。AGF保存了空閑塊的信息。
#0 __schedule
#1 schedule_timeout
#2 down (信號量/互斥等待)
#3 down
#4 xfs_buf_lock [xfs]
#5 xfs_buf_find [xfs]
#6 xfs_buf_get_map [xfs]
#7 xfs_trans_read_buf_map [xfs]
#8 xfs_trans_read_buf_map [xfs] (封裝層)
#9 xfs_read_agf / xfs_alloc_read_agf [xfs] ← 正在嘗試拿 AGF buf
#10 xfs_agf_* / xfs_read_agf_* [xfs]
#11 xfs_trans_read_buf [xfs]
#12 xfs_alloc_read_agf [xfs]
#13 xfs_btree_update / xfs_btree_del* [xfs]
#14 xfs_inodegc_* / xfs_ifree_cluster [xfs]
#15 xfs_inactive_ifree [xfs]
#16 xfs_destroy_inode [xfs]
#17 xfs_fs_destroy_inode [xfs] ← 釋放 inode 主路徑
#18 destroy_inode (VFS)
#19 evict (VFS)
#20 dentry_kill (VFS)
#21 dput (VFS)
#22 renameat2 / unlinkat (syscall)
#23 __x64_sys_* (syscall)
#24 do_syscall_64
#25 entry_SYSCALL_64_after_hwframe
解析xfs_buf地址,順著 xfs_buf → xfs_trans → xlog_ticket → task_struct.pid 反查,鎖持有者落到下一條堆棧(關(guān)鍵堆棧2)。
關(guān)鍵堆棧2(鎖的持有者,創(chuàng)建/iget 路徑卡住)
- 進程:
postgres - PID:
1894063 - 癥狀:在
xfs_create / xfs_iget路徑;該事務(wù)已持有 AGF/AGI 相關(guān)日志項(從日志 item 鏈可見),同時在iget上等待 inode 資源,構(gòu)成與 #1 的 ABBA 互等。
#0 __schedule
#1 schedule_timeout
#2 xfs_iget [xfs] ← iget 等待(可能循環(huán))
#3 xfs_ilock [xfs]
#4 xfs_iunlock [xfs]
#5 xfs_dir_ialloc [xfs]
#6 xfs_ialloc [xfs] (第1次/第2次分配)
#7 xfs_create [xfs]
#8 xfs_generic_create [xfs]
#9 path_openat / do_open (VFS)
#10 do_filp_open (VFS)
#11 do_sys_openat2
#12 do_sys_open
#13 __x64_sys_openat
#14 do_syscall_64
#15 entry_SYSCALL_64_after_hwframe
關(guān)聯(lián)關(guān)系:堆棧2 持有 AGF → 堆棧1 需要 AGF;堆棧1 持有 inode/inode-bp → 堆棧2 需要 inode-bp;互相等待形成系統(tǒng)級掛起(xfs hung + iget 死循環(huán))。
根因
兩個進程:
- 進程 1(銷毀文件):正在刪除文件、回收 inode;
- 進程 2(創(chuàng)建文件):正在分配新的 inode。
兩個進程都要去改 XFS 的元數(shù)據(jù)結(jié)構(gòu),尤其是: AGF(空閑塊信息)AGI(inode 信息)inode cluster buffer(一組 inode 的緩存塊)
這幾個結(jié)構(gòu)之間是要上鎖的。 如果 A 拿著 inode 的鎖再去要 AGF 的鎖, 而 B 拿著 AGF 的鎖再去要 inode 的鎖, 就會變成 “你等我,我等你” —— 這就是典型的死鎖(deadlock)。
于是,整個 XFS 文件系統(tǒng)“掛死”(hung),看到的就是:- postgres 進程全在 D 狀態(tài);
xfs_iget死循環(huán);
修復(fù)
補丁:xfs: use deferred frees for btree block freeing(commit b742d7b4f0e03...)
核心思想:把“立刻釋放”改為“延遲釋放”。
技術(shù)上怎么實現(xiàn)的:
- 把原來直接調(diào)用的
xfs_free_extent()改成了xfs_free_extent_later()。
意思是: “我先把要釋放的塊記錄到一個待辦列表(deferred list)里, 等當(dāng)前事務(wù)快提交時再一起處理。” - 提交階段,這些“待釋放塊”會被系統(tǒng)安全地處理:
- 如果空間緊張,可以分多次提交;
- 不會在持有其他鎖時再去改 AGF;
- 因此避免了死鎖的條件。
- 同時引入了一個
xefi_agresv參數(shù),
確保延遲釋放的塊仍然走正確的空閑空間管理邏輯(防止把 AG 專用塊搞亂)。

浙公網(wǎng)安備 33010602011771號