Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fsnotify: create helpers for group mark_mutex lock

Create helpers to take and release the group mark_mutex lock.

Define a flag FSNOTIFY_GROUP_NOFS in fsnotify_group that determines
if the mark_mutex lock is fs reclaim safe or not. If not safe, the
lock helpers take the lock and disable direct fs reclaim.

In that case we annotate the mutex with a different lockdep class to
express to lockdep that an allocation of mark of an fs reclaim safe group
may take the group lock of another "NOFS" group to evict inodes.

For now, converted only the callers in common code and no backend
defines the NOFS flag. It is intended to be set by fanotify for
evictable marks support.

Link: https://lore.kernel.org/r/20220422120327.3459282-7-amir73il@gmail.com
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>
Signed-off-by: Jan Kara <jack@suse.cz>

authored by

Amir Goldstein and committed by
Jan Kara
43b245a7 f3010343

+52 -15
+2 -2
fs/notify/fdinfo.c
··· 28 28 struct fsnotify_group *group = f->private_data; 29 29 struct fsnotify_mark *mark; 30 30 31 - mutex_lock(&group->mark_mutex); 31 + fsnotify_group_lock(group); 32 32 list_for_each_entry(mark, &group->marks_list, g_list) { 33 33 show(m, mark); 34 34 if (seq_has_overflowed(m)) 35 35 break; 36 36 } 37 - mutex_unlock(&group->mark_mutex); 37 + fsnotify_group_unlock(group); 38 38 } 39 39 40 40 #if defined(CONFIG_EXPORTFS)
+11
fs/notify/group.c
··· 115 115 const struct fsnotify_ops *ops, 116 116 int flags, gfp_t gfp) 117 117 { 118 + static struct lock_class_key nofs_marks_lock; 118 119 struct fsnotify_group *group; 119 120 120 121 group = kzalloc(sizeof(struct fsnotify_group), gfp); ··· 136 135 137 136 group->ops = ops; 138 137 group->flags = flags; 138 + /* 139 + * For most backends, eviction of inode with a mark is not expected, 140 + * because marks hold a refcount on the inode against eviction. 141 + * 142 + * Use a different lockdep class for groups that support evictable 143 + * inode marks, because with evictable marks, mark_mutex is NOT 144 + * fs-reclaim safe - the mutex is taken when evicting inodes. 145 + */ 146 + if (flags & FSNOTIFY_GROUP_NOFS) 147 + lockdep_set_class(&group->mark_mutex, &nofs_marks_lock); 139 148 140 149 return group; 141 150 }
+11 -13
fs/notify/mark.c
··· 398 398 */ 399 399 void fsnotify_detach_mark(struct fsnotify_mark *mark) 400 400 { 401 - struct fsnotify_group *group = mark->group; 402 - 403 - WARN_ON_ONCE(!mutex_is_locked(&group->mark_mutex)); 401 + fsnotify_group_assert_locked(mark->group); 404 402 WARN_ON_ONCE(!srcu_read_lock_held(&fsnotify_mark_srcu) && 405 403 refcount_read(&mark->refcnt) < 1 + 406 404 !!(mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED)); ··· 450 452 void fsnotify_destroy_mark(struct fsnotify_mark *mark, 451 453 struct fsnotify_group *group) 452 454 { 453 - mutex_lock(&group->mark_mutex); 455 + fsnotify_group_lock(group); 454 456 fsnotify_detach_mark(mark); 455 - mutex_unlock(&group->mark_mutex); 457 + fsnotify_group_unlock(group); 456 458 fsnotify_free_mark(mark); 457 459 } 458 460 EXPORT_SYMBOL_GPL(fsnotify_destroy_mark); ··· 671 673 struct fsnotify_group *group = mark->group; 672 674 int ret = 0; 673 675 674 - BUG_ON(!mutex_is_locked(&group->mark_mutex)); 676 + fsnotify_group_assert_locked(group); 675 677 676 678 /* 677 679 * LOCKING ORDER!!!! ··· 712 714 int ret; 713 715 struct fsnotify_group *group = mark->group; 714 716 715 - mutex_lock(&group->mark_mutex); 717 + fsnotify_group_lock(group); 716 718 ret = fsnotify_add_mark_locked(mark, connp, obj_type, add_flags, fsid); 717 - mutex_unlock(&group->mark_mutex); 719 + fsnotify_group_unlock(group); 718 720 return ret; 719 721 } 720 722 EXPORT_SYMBOL_GPL(fsnotify_add_mark); ··· 768 770 * move marks to free to to_free list in one go and then free marks in 769 771 * to_free list one by one. 770 772 */ 771 - mutex_lock(&group->mark_mutex); 773 + fsnotify_group_lock(group); 772 774 list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) { 773 775 if (mark->connector->type == obj_type) 774 776 list_move(&mark->g_list, &to_free); 775 777 } 776 - mutex_unlock(&group->mark_mutex); 778 + fsnotify_group_unlock(group); 777 779 778 780 clear: 779 781 while (1) { 780 - mutex_lock(&group->mark_mutex); 782 + fsnotify_group_lock(group); 781 783 if (list_empty(head)) { 782 - mutex_unlock(&group->mark_mutex); 784 + fsnotify_group_unlock(group); 783 785 break; 784 786 } 785 787 mark = list_first_entry(head, struct fsnotify_mark, g_list); 786 788 fsnotify_get_mark(mark); 787 789 fsnotify_detach_mark(mark); 788 - mutex_unlock(&group->mark_mutex); 790 + fsnotify_group_unlock(group); 789 791 fsnotify_free_mark(mark); 790 792 fsnotify_put_mark(mark); 791 793 }
+28
include/linux/fsnotify_backend.h
··· 20 20 #include <linux/user_namespace.h> 21 21 #include <linux/refcount.h> 22 22 #include <linux/mempool.h> 23 + #include <linux/sched/mm.h> 23 24 24 25 /* 25 26 * IN_* from inotfy.h lines up EXACTLY with FS_*, this is so we can easily ··· 213 212 214 213 #define FSNOTIFY_GROUP_USER 0x01 /* user allocated group */ 215 214 #define FSNOTIFY_GROUP_DUPS 0x02 /* allow multiple marks per object */ 215 + #define FSNOTIFY_GROUP_NOFS 0x04 /* group lock is not direct reclaim safe */ 216 216 int flags; 217 + unsigned int owner_flags; /* stored flags of mark_mutex owner */ 217 218 218 219 /* stores all fastpath marks assoc with this group so they can be cleaned on unregister */ 219 220 struct mutex mark_mutex; /* protect marks_list */ ··· 256 253 #endif /* CONFIG_FANOTIFY */ 257 254 }; 258 255 }; 256 + 257 + /* 258 + * These helpers are used to prevent deadlock when reclaiming inodes with 259 + * evictable marks of the same group that is allocating a new mark. 260 + */ 261 + static inline void fsnotify_group_lock(struct fsnotify_group *group) 262 + { 263 + mutex_lock(&group->mark_mutex); 264 + if (group->flags & FSNOTIFY_GROUP_NOFS) 265 + group->owner_flags = memalloc_nofs_save(); 266 + } 267 + 268 + static inline void fsnotify_group_unlock(struct fsnotify_group *group) 269 + { 270 + if (group->flags & FSNOTIFY_GROUP_NOFS) 271 + memalloc_nofs_restore(group->owner_flags); 272 + mutex_unlock(&group->mark_mutex); 273 + } 274 + 275 + static inline void fsnotify_group_assert_locked(struct fsnotify_group *group) 276 + { 277 + WARN_ON_ONCE(!mutex_is_locked(&group->mark_mutex)); 278 + if (group->flags & FSNOTIFY_GROUP_NOFS) 279 + WARN_ON_ONCE(!(current->flags & PF_MEMALLOC_NOFS)); 280 + } 259 281 260 282 /* When calling fsnotify tell it if the data is a path or inode */ 261 283 enum fsnotify_data_type {