@@ -1082,6 +1082,7 @@ static void fanotify_free_mark(struct fsnotify_mark *fsn_mark)
}
const struct fsnotify_ops fanotify_fsnotify_ops = {
+ .group_flags = FSNOTIFY_GROUP_NOFS,
.handle_event = fanotify_handle_event,
.free_group_priv = fanotify_free_group_priv,
.free_event = fanotify_free_event,
@@ -931,12 +931,12 @@ static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long ar
case FAN_IOC_SET_MARK_PAGE_ORDER:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- mutex_lock(&group->mark_mutex);
+ fsnotify_group_lock(group);
group->fanotify_data.mark_page_order = (unsigned int)arg;
pr_info("fanotify: set mark size page order to %u",
group->fanotify_data.mark_page_order);
ret = 0;
- mutex_unlock(&group->mark_mutex);
+ fsnotify_group_unlock(group);
break;
}
@@ -1044,11 +1044,12 @@ static int fanotify_remove_mark(struct fsnotify_group *group,
struct fsnotify_mark *fsn_mark = NULL;
__u32 removed;
int destroy_mark;
+ unsigned int nofs;
- mutex_lock(&group->mark_mutex);
+ nofs = fsnotify_group_nofs_lock(group);
fsn_mark = fsnotify_find_mark(connp, group);
if (!fsn_mark) {
- mutex_unlock(&group->mark_mutex);
+ fsnotify_group_nofs_unlock(group, nofs);
return -ENOENT;
}
@@ -1058,7 +1059,7 @@ static int fanotify_remove_mark(struct fsnotify_group *group,
fsnotify_recalc_mask(fsn_mark->connector);
if (destroy_mark)
fsnotify_detach_mark(fsn_mark);
- mutex_unlock(&group->mark_mutex);
+ fsnotify_group_nofs_unlock(group, nofs);
if (destroy_mark)
fsnotify_free_mark(fsn_mark);
@@ -1236,9 +1237,10 @@ static int fanotify_add_mark(struct fsnotify_group *group,
__kernel_fsid_t *fsid)
{
struct fsnotify_mark *fsn_mark = NULL;
+ unsigned int nofs;
int ret = 0;
- mutex_lock(&group->mark_mutex);
+ nofs = fsnotify_group_nofs_lock(group);
/* Allow adding multiple large marks per object for testing */
if (!group->fanotify_data.mark_page_order)
fsn_mark = fsnotify_find_mark(connp, group);
@@ -1246,7 +1248,7 @@ static int fanotify_add_mark(struct fsnotify_group *group,
fsn_mark = fanotify_add_new_mark(group, connp, obj_type, flags,
fsid);
if (IS_ERR(fsn_mark)) {
- mutex_unlock(&group->mark_mutex);
+ fsnotify_group_nofs_unlock(group, nofs);
return PTR_ERR(fsn_mark);
}
}
@@ -1264,7 +1266,7 @@ static int fanotify_add_mark(struct fsnotify_group *group,
ret = fanotify_mark_add_to_mask(fsn_mark, mask, flags);
out:
- mutex_unlock(&group->mark_mutex);
+ fsnotify_group_nofs_unlock(group, nofs);
fsnotify_put_mark(fsn_mark);
return ret;
Direct reclaim from fanotify mark allocation context may try to evict inodes with evictable marks of the same group and hit this deadlock: [<0>] fsnotify_destroy_mark+0x1f/0x3a [<0>] fsnotify_destroy_marks+0x71/0xd9 [<0>] __destroy_inode+0x24/0x7e [<0>] destroy_inode+0x2c/0x67 [<0>] dispose_list+0x49/0x68 [<0>] prune_icache_sb+0x5b/0x79 [<0>] super_cache_scan+0x11c/0x16f [<0>] shrink_slab.constprop.0+0x23e/0x40f [<0>] shrink_node+0x218/0x3e7 [<0>] do_try_to_free_pages+0x12a/0x2d2 [<0>] try_to_free_pages+0x166/0x242 [<0>] __alloc_pages_slowpath.constprop.0+0x30c/0x903 [<0>] __alloc_pages+0xeb/0x1c7 [<0>] cache_grow_begin+0x6f/0x31e [<0>] fallback_alloc+0xe0/0x12d [<0>] ____cache_alloc_node+0x15a/0x17e [<0>] kmem_cache_alloc_trace+0xa1/0x143 [<0>] fanotify_add_mark+0xd5/0x2b2 [<0>] do_fanotify_mark+0x566/0x5eb [<0>] __x64_sys_fanotify_mark+0x21/0x24 [<0>] do_syscall_64+0x6d/0x80 [<0>] entry_SYSCALL_64_after_hwframe+0x44/0xae Set the FSNOTIFY_GROUP_NOFS flag to prevent going into direct reclaim from allocations under fanotify group lock and use the nofs group lock helpers. Suggested-by: Jan Kara <jack@suse.cz> Link: https://lore.kernel.org/r/20220321112310.vpr7oxro2xkz5llh@quack3.lan/ Signed-off-by: Amir Goldstein <amir73il@gmail.com> --- fs/notify/fanotify/fanotify.c | 1 + fs/notify/fanotify/fanotify_user.c | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-)