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

fsnotify: optimize FS_MODIFY events with no ignored masks

fsnotify() treats FS_MODIFY events specially - it does not skip them
even if the FS_MODIFY event does not apear in the object's fsnotify
mask. This is because send_to_group() checks if FS_MODIFY needs to
clear ignored mask of marks.

The common case is that an object does not have any mark with ignored
mask and in particular, that it does not have a mark with ignored mask
and without the FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY flag.

Set FS_MODIFY in object's fsnotify mask during fsnotify_recalc_mask()
if object has a mark with an ignored mask and without the
FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY flag and remove the special
treatment of FS_MODIFY in fsnotify(), so that FS_MODIFY events could
be optimized in the common case.

Call fsnotify_recalc_mask() from fanotify after adding or removing an
ignored mask from a mark without FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY
or when adding the FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY flag to a mark
with ignored mask (the flag cannot be removed by fanotify uapi).

Performance results for doing 10000000 write(2)s to tmpfs:

vanilla patched
without notification mark 25.486+-1.054 24.965+-0.244
with notification mark 30.111+-0.139 26.891+-1.355

So we can see the overhead of notification subsystem has been
drastically reduced.

Link: https://lore.kernel.org/r/20220223151438.790268-3-amir73il@gmail.com
Suggested-by: Jan Kara <jack@suse.cz>
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
04e317ba 4f0b903d

+34 -10
+25 -7
fs/notify/fanotify/fanotify_user.c
··· 1081 1081 flags, umask); 1082 1082 } 1083 1083 1084 + static void fanotify_mark_add_ignored_mask(struct fsnotify_mark *fsn_mark, 1085 + __u32 mask, unsigned int flags, 1086 + __u32 *removed) 1087 + { 1088 + fsn_mark->ignored_mask |= mask; 1089 + 1090 + /* 1091 + * Setting FAN_MARK_IGNORED_SURV_MODIFY for the first time may lead to 1092 + * the removal of the FS_MODIFY bit in calculated mask if it was set 1093 + * because of an ignored mask that is now going to survive FS_MODIFY. 1094 + */ 1095 + if ((flags & FAN_MARK_IGNORED_SURV_MODIFY) && 1096 + !(fsn_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) { 1097 + fsn_mark->flags |= FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY; 1098 + if (!(fsn_mark->mask & FS_MODIFY)) 1099 + *removed = FS_MODIFY; 1100 + } 1101 + } 1102 + 1084 1103 static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, 1085 - __u32 mask, unsigned int flags) 1104 + __u32 mask, unsigned int flags, 1105 + __u32 *removed) 1086 1106 { 1087 1107 __u32 oldmask, newmask; 1088 1108 ··· 1111 1091 if (!(flags & FAN_MARK_IGNORED_MASK)) { 1112 1092 fsn_mark->mask |= mask; 1113 1093 } else { 1114 - fsn_mark->ignored_mask |= mask; 1115 - if (flags & FAN_MARK_IGNORED_SURV_MODIFY) 1116 - fsn_mark->flags |= FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY; 1094 + fanotify_mark_add_ignored_mask(fsn_mark, mask, flags, removed); 1117 1095 } 1118 1096 newmask = fsnotify_calc_mask(fsn_mark); 1119 1097 spin_unlock(&fsn_mark->lock); ··· 1174 1156 __kernel_fsid_t *fsid) 1175 1157 { 1176 1158 struct fsnotify_mark *fsn_mark; 1177 - __u32 added; 1159 + __u32 added, removed = 0; 1178 1160 int ret = 0; 1179 1161 1180 1162 mutex_lock(&group->mark_mutex); ··· 1197 1179 goto out; 1198 1180 } 1199 1181 1200 - added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); 1201 - if (added & ~fsnotify_conn_mask(fsn_mark->connector)) 1182 + added = fanotify_mark_add_to_mask(fsn_mark, mask, flags, &removed); 1183 + if (removed || (added & ~fsnotify_conn_mask(fsn_mark->connector))) 1202 1184 fsnotify_recalc_mask(fsn_mark->connector); 1203 1185 1204 1186 out:
+5 -3
fs/notify/fsnotify.c
··· 531 531 532 532 533 533 /* 534 - * if this is a modify event we may need to clear the ignored masks 535 - * otherwise return if none of the marks care about this type of event. 534 + * If this is a modify event we may need to clear some ignored masks. 535 + * In that case, the object with ignored masks will have the FS_MODIFY 536 + * event in its mask. 537 + * Otherwise, return if none of the marks care about this type of event. 536 538 */ 537 539 test_mask = (mask & ALL_FSNOTIFY_EVENTS); 538 - if (!(mask & FS_MODIFY) && !(test_mask & marks_mask)) 540 + if (!(test_mask & marks_mask)) 539 541 return 0; 540 542 541 543 iter_info.srcu_idx = srcu_read_lock(&fsnotify_mark_srcu);
+4
include/linux/fsnotify_backend.h
··· 609 609 if (!mark->ignored_mask) 610 610 return mask; 611 611 612 + /* Interest in FS_MODIFY may be needed for clearing ignored mask */ 613 + if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) 614 + mask |= FS_MODIFY; 615 + 612 616 /* 613 617 * If mark is interested in ignoring events on children, the object must 614 618 * show interest in those events for fsnotify_parent() to notice it.