Skip to content

Commit 497b0c5

Browse files
amir73iljankara
authored andcommitted
fsnotify: send event to parent and child with single callback
Instead of calling fsnotify() twice, once with parent inode and once with child inode, if event should be sent to parent inode, send it with both parent and child inodes marks in object type iterator and call the backend handle_event() callback only once. The parent inode is assigned to the standard "inode" iterator type and the child inode is assigned to the special "child" iterator type. In that case, the bit FS_EVENT_ON_CHILD will be set in the event mask, the dir argument to handle_event will be the parent inode, the file_name argument to handle_event is non NULL and refers to the name of the child and the child inode can be accessed with fsnotify_data_inode(). This will allow fanotify to make decisions based on child or parent's ignored mask. For example, when a parent is interested in a specific event on its children, but a specific child wishes to ignore this event, the event will not be reported. This is not what happens with current code, but according to man page, it is the expected behavior. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Amir Goldstein <[email protected]> Signed-off-by: Jan Kara <[email protected]>
1 parent c8f3446 commit 497b0c5

File tree

3 files changed

+45
-33
lines changed

3 files changed

+45
-33
lines changed

fs/kernfs/file.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,7 @@ static void kernfs_notify_workfn(struct work_struct *work)
883883

884884
list_for_each_entry(info, &kernfs_root(kn)->supers, node) {
885885
struct kernfs_node *parent;
886+
struct inode *p_inode = NULL;
886887
struct inode *inode;
887888
struct qstr name;
888889

@@ -899,8 +900,6 @@ static void kernfs_notify_workfn(struct work_struct *work)
899900
name = (struct qstr)QSTR_INIT(kn->name, strlen(kn->name));
900901
parent = kernfs_get_parent(kn);
901902
if (parent) {
902-
struct inode *p_inode;
903-
904903
p_inode = ilookup(info->sb, kernfs_ino(parent));
905904
if (p_inode) {
906905
fsnotify(p_inode, FS_MODIFY | FS_EVENT_ON_CHILD,
@@ -911,8 +910,11 @@ static void kernfs_notify_workfn(struct work_struct *work)
911910
kernfs_put(parent);
912911
}
913912

914-
fsnotify(inode, FS_MODIFY, inode, FSNOTIFY_EVENT_INODE,
915-
NULL, 0);
913+
if (!p_inode) {
914+
fsnotify(inode, FS_MODIFY, inode, FSNOTIFY_EVENT_INODE,
915+
NULL, 0);
916+
}
917+
916918
iput(inode);
917919
}
918920

fs/notify/fanotify/fanotify.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,12 +261,12 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
261261
continue;
262262

263263
/*
264-
* If the event is for a child and this mark doesn't care about
265-
* events on a child, don't send it!
264+
* If the event is for a child and this mark is on a parent not
265+
* watching children, don't send it!
266266
*/
267267
if (event_mask & FS_EVENT_ON_CHILD &&
268-
(type != FSNOTIFY_OBJ_TYPE_INODE ||
269-
!(mark->mask & FS_EVENT_ON_CHILD)))
268+
type == FSNOTIFY_OBJ_TYPE_INODE &&
269+
!(mark->mask & FS_EVENT_ON_CHILD))
270270
continue;
271271

272272
marks_mask |= mark->mask;

fs/notify/fsnotify.c

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -145,43 +145,46 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode)
145145
/*
146146
* Notify this dentry's parent about a child's events with child name info
147147
* if parent is watching.
148-
* Notify also the child without name info if child inode is watching.
148+
* Notify only the child without name info if parent is not watching.
149149
*/
150150
int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data,
151151
int data_type)
152152
{
153+
struct inode *inode = d_inode(dentry);
153154
struct dentry *parent;
154155
struct inode *p_inode;
156+
struct name_snapshot name;
157+
struct qstr *file_name = NULL;
155158
int ret = 0;
156159

160+
parent = NULL;
157161
if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED))
158-
goto notify_child;
162+
goto notify;
159163

160164
parent = dget_parent(dentry);
161165
p_inode = parent->d_inode;
162166

163167
if (unlikely(!fsnotify_inode_watches_children(p_inode))) {
164168
__fsnotify_update_child_dentry_flags(p_inode);
165169
} else if (p_inode->i_fsnotify_mask & mask & ALL_FSNOTIFY_EVENTS) {
166-
struct name_snapshot name;
170+
/* When notifying parent, child should be passed as data */
171+
WARN_ON_ONCE(inode != fsnotify_data_inode(data, data_type));
167172

168-
/*
169-
* We are notifying a parent, so set a flag in mask to inform
170-
* backend that event has information about a child entry.
171-
*/
173+
/* Notify both parent and child with child name info */
174+
inode = p_inode;
172175
take_dentry_name_snapshot(&name, dentry);
173-
ret = fsnotify(p_inode, mask | FS_EVENT_ON_CHILD, data,
174-
data_type, &name.name, 0);
175-
release_dentry_name_snapshot(&name);
176+
file_name = &name.name;
177+
mask |= FS_EVENT_ON_CHILD;
176178
}
177179

178-
dput(parent);
180+
notify:
181+
ret = fsnotify(inode, mask, data, data_type, file_name, 0);
179182

180-
if (ret)
181-
return ret;
183+
if (file_name)
184+
release_dentry_name_snapshot(&name);
185+
dput(parent);
182186

183-
notify_child:
184-
return fsnotify(d_inode(dentry), mask, data, data_type, NULL, 0);
187+
return ret;
185188
}
186189
EXPORT_SYMBOL_GPL(__fsnotify_parent);
187190

@@ -322,12 +325,16 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_type,
322325
struct super_block *sb = to_tell->i_sb;
323326
struct inode *dir = file_name ? to_tell : NULL;
324327
struct mount *mnt = NULL;
328+
struct inode *child = NULL;
325329
int ret = 0;
326330
__u32 test_mask, marks_mask;
327331

328332
if (path)
329333
mnt = real_mount(path->mnt);
330334

335+
if (mask & FS_EVENT_ON_CHILD)
336+
child = fsnotify_data_inode(data, data_type);
337+
331338
/*
332339
* Optimization: srcu_read_lock() has a memory barrier which can
333340
* be expensive. It protects walking the *_fsnotify_marks lists.
@@ -336,21 +343,20 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_type,
336343
* need SRCU to keep them "alive".
337344
*/
338345
if (!to_tell->i_fsnotify_marks && !sb->s_fsnotify_marks &&
339-
(!mnt || !mnt->mnt_fsnotify_marks))
346+
(!mnt || !mnt->mnt_fsnotify_marks) &&
347+
(!child || !child->i_fsnotify_marks))
340348
return 0;
341349

342-
/* An event "on child" is not intended for a mount/sb mark */
343-
marks_mask = to_tell->i_fsnotify_mask;
344-
if (!(mask & FS_EVENT_ON_CHILD)) {
345-
marks_mask |= sb->s_fsnotify_mask;
346-
if (mnt)
347-
marks_mask |= mnt->mnt_fsnotify_mask;
348-
}
350+
marks_mask = to_tell->i_fsnotify_mask | sb->s_fsnotify_mask;
351+
if (mnt)
352+
marks_mask |= mnt->mnt_fsnotify_mask;
353+
if (child)
354+
marks_mask |= child->i_fsnotify_mask;
355+
349356

350357
/*
351358
* if this is a modify event we may need to clear the ignored masks
352-
* otherwise return if neither the inode nor the vfsmount/sb care about
353-
* this type of event.
359+
* otherwise return if none of the marks care about this type of event.
354360
*/
355361
test_mask = (mask & ALL_FSNOTIFY_EVENTS);
356362
if (!(mask & FS_MODIFY) && !(test_mask & marks_mask))
@@ -366,6 +372,10 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_type,
366372
iter_info.marks[FSNOTIFY_OBJ_TYPE_VFSMOUNT] =
367373
fsnotify_first_mark(&mnt->mnt_fsnotify_marks);
368374
}
375+
if (child) {
376+
iter_info.marks[FSNOTIFY_OBJ_TYPE_CHILD] =
377+
fsnotify_first_mark(&child->i_fsnotify_marks);
378+
}
369379

370380
/*
371381
* We need to merge inode/vfsmount/sb mark lists so that e.g. inode mark

0 commit comments

Comments
 (0)