Skip to content

Commit

Permalink
fsnotify: optimize the case of no parent watcher
Browse files Browse the repository at this point in the history
If parent inode is not watching, check for the event in masks of
sb/mount/inode masks early to optimize out most of the code in
__fsnotify_parent() and avoid calling fsnotify().

Jens has reported that this optimization improves BW and IOPS in an
io_uring benchmark by more than 10% and reduces perf reported CPU usage.

before:

+    4.51%  io_uring  [kernel.vmlinux]  [k] fsnotify
+    3.67%  io_uring  [kernel.vmlinux]  [k] __fsnotify_parent

after:

+    2.37%  io_uring  [kernel.vmlinux]  [k] __fsnotify_parent

Reported-and-tested-by: Jens Axboe <axboe@kernel.dk>
Link: https://lore.kernel.org/linux-fsdevel/b45bd8ff-5654-4e67-90a6-aad5e6759e0b@kernel.dk/
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Message-Id: <20240116113247.758848-1-amir73il@gmail.com>
  • Loading branch information
Amir Goldstein authored and Jan Kara committed Jan 24, 2024
1 parent 615d300 commit 082fd1e
Showing 1 changed file with 17 additions and 11 deletions.
28 changes: 17 additions & 11 deletions fs/notify/fsnotify.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode)
}

/* Are inode/sb/mount interested in parent and name info with this event? */
static bool fsnotify_event_needs_parent(struct inode *inode, struct mount *mnt,
static bool fsnotify_event_needs_parent(struct inode *inode, __u32 mnt_mask,
__u32 mask)
{
__u32 marks_mask = 0;
Expand All @@ -160,13 +160,22 @@ static bool fsnotify_event_needs_parent(struct inode *inode, struct mount *mnt,
/* Did either inode/sb/mount subscribe for events with parent/name? */
marks_mask |= fsnotify_parent_needed_mask(inode->i_fsnotify_mask);
marks_mask |= fsnotify_parent_needed_mask(inode->i_sb->s_fsnotify_mask);
if (mnt)
marks_mask |= fsnotify_parent_needed_mask(mnt->mnt_fsnotify_mask);
marks_mask |= fsnotify_parent_needed_mask(mnt_mask);

/* Did they subscribe for this event with parent/name info? */
return mask & marks_mask;
}

/* Are there any inode/mount/sb objects that are interested in this event? */
static inline bool fsnotify_object_watched(struct inode *inode, __u32 mnt_mask,
__u32 mask)
{
__u32 marks_mask = inode->i_fsnotify_mask | mnt_mask |
inode->i_sb->s_fsnotify_mask;

return mask & marks_mask & ALL_FSNOTIFY_EVENTS;
}

/*
* Notify this dentry's parent about a child's events with child name info
* if parent is watching or if inode/sb/mount are interested in events with
Expand All @@ -179,7 +188,7 @@ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data,
int data_type)
{
const struct path *path = fsnotify_data_path(data, data_type);
struct mount *mnt = path ? real_mount(path->mnt) : NULL;
__u32 mnt_mask = path ? real_mount(path->mnt)->mnt_fsnotify_mask : 0;
struct inode *inode = d_inode(dentry);
struct dentry *parent;
bool parent_watched = dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED;
Expand All @@ -190,16 +199,13 @@ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data,
struct qstr *file_name = NULL;
int ret = 0;

/*
* Do inode/sb/mount care about parent and name info on non-dir?
* Do they care about any event at all?
*/
if (!inode->i_fsnotify_marks && !inode->i_sb->s_fsnotify_marks &&
(!mnt || !mnt->mnt_fsnotify_marks) && !parent_watched)
/* Optimize the likely case of nobody watching this path */
if (likely(!parent_watched &&
!fsnotify_object_watched(inode, mnt_mask, mask)))
return 0;

parent = NULL;
parent_needed = fsnotify_event_needs_parent(inode, mnt, mask);
parent_needed = fsnotify_event_needs_parent(inode, mnt_mask, mask);
if (!parent_watched && !parent_needed)
goto notify;

Expand Down

0 comments on commit 082fd1e

Please sign in to comment.