Re: [BUG REPORT] potential deadlock in inode evicting under the inode lru traversing context on ext4 and ubifs

From: Zhihao Cheng
Date: Fri Jul 12 2024 - 22:30:24 EST




在 2024/7/12 22:37, Theodore Ts'o 写道:
Problem description
===================

The inode reclaiming process(See function prune_icache_sb) collects all
reclaimable inodes and mark them with I_FREEING flag at first, at that
time, other processes will be stuck if they try getting these inodes(See
function find_inode_fast), then the reclaiming process destroy the
inodes by function dispose_list().
Some filesystems(eg. ext4 with ea_inode feature, ubifs with xattr) may
do inode lookup in the inode evicting callback function, if the inode
lookup is operated under the inode lru traversing context, deadlock
problems may happen.

Case 1: In function ext4_evict_inode(), the ea inode lookup could happen
if ea_inode feature is enabled, the lookup process will be stuck under
the evicting context like this:

1. File A has inode i_reg and an ea inode i_ea
2. getfattr(A, xattr_buf) // i_ea is added into lru // lru->i_ea
3. Then, following three processes running like this:

PA PB
echo 2 > /proc/sys/vm/drop_caches
shrink_slab
prune_dcache_sb
// i_reg is added into lru, lru->i_ea->i_reg
prune_icache_sb
list_lru_walk_one
inode_lru_isolate
i_ea->i_state |= I_FREEING // set inode state
i_ea->i_state |= I_FREEING // set inode state
Um, I don't see how this can happen. If the ea_inode is in use,
i_count will be greater than zero, and hence the inode will never be
go down the rest of the path in inode_lru_inode():

The counter of ea_inode could become zero before the file inode, according to the following process:
path_getxattr
user_path_at(&path) // get file dentry and file inode
getxattr
ext4_xattr_get
ext4_xattr_ibody_get
ext4_xattr_inode_get
ext4_xattr_inode_iget(&ea_inode) // ea_inode->i_count = 1
iput(ea_inode) // ea_inode->i_count = 0, put it into lru
path_put(&path); // put file dentry and file inode


if (atomic_read(&inode->i_count) ||
...) {
list_lru_isolate(lru, &inode->i_lru);
spin_unlock(&inode->i_lock);
this_cpu_dec(nr_unused);
return LRU_REMOVED;
}

Do you have an actual reproduer which triggers this? Or would this
happen be any chance something that was dreamed up with DEPT?

The reproducer is in the second half of the ariticle, along with some of the solutions we tried.

Reproducer:
===========

https://bugzilla.kernel.org/show_bug.cgi?id=219022

About solutions
===============

[...]