Skip to content

Commit a05fc7e

Browse files
committed
fsnotify: Use connector list for destroying inode marks
Instead of iterating all inodes belonging to a superblock to find inode marks and remove them on umount, iterate all inode connectors for the superblock. This may be substantially faster since there are generally much less inodes with fsnotify marks than all inodes. It also removes one use of sb->s_inodes list which we strive to ultimately remove. Reviewed-by: Amir Goldstein <amir73il@gmail.com> Reviewed-by: Christian Brauner <brauner@kernel.org> Signed-off-by: Jan Kara <jack@suse.cz>
1 parent 94bd012 commit a05fc7e

File tree

3 files changed

+54
-60
lines changed

3 files changed

+54
-60
lines changed

fs/notify/fsnotify.c

Lines changed: 1 addition & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -33,65 +33,6 @@ void __fsnotify_mntns_delete(struct mnt_namespace *mntns)
3333
fsnotify_clear_marks_by_mntns(mntns);
3434
}
3535

36-
/**
37-
* fsnotify_unmount_inodes - an sb is unmounting. handle any watched inodes.
38-
* @sb: superblock being unmounted.
39-
*
40-
* Called during unmount with no locks held, so needs to be safe against
41-
* concurrent modifiers. We temporarily drop sb->s_inode_list_lock and CAN block.
42-
*/
43-
static void fsnotify_unmount_inodes(struct super_block *sb)
44-
{
45-
struct inode *inode, *iput_inode = NULL;
46-
47-
spin_lock(&sb->s_inode_list_lock);
48-
list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
49-
/*
50-
* We cannot __iget() an inode in state I_FREEING,
51-
* I_WILL_FREE, or I_NEW which is fine because by that point
52-
* the inode cannot have any associated watches.
53-
*/
54-
spin_lock(&inode->i_lock);
55-
if (inode_state_read(inode) & (I_FREEING | I_WILL_FREE | I_NEW)) {
56-
spin_unlock(&inode->i_lock);
57-
continue;
58-
}
59-
60-
/*
61-
* If i_count is zero, the inode cannot have any watches and
62-
* doing an __iget/iput with SB_ACTIVE clear would actually
63-
* evict all inodes with zero i_count from icache which is
64-
* unnecessarily violent and may in fact be illegal to do.
65-
* However, we should have been called /after/ evict_inodes
66-
* removed all zero refcount inodes, in any case. Test to
67-
* be sure.
68-
*/
69-
if (!icount_read(inode)) {
70-
spin_unlock(&inode->i_lock);
71-
continue;
72-
}
73-
74-
__iget(inode);
75-
spin_unlock(&inode->i_lock);
76-
spin_unlock(&sb->s_inode_list_lock);
77-
78-
iput(iput_inode);
79-
80-
/* for each watch, send FS_UNMOUNT and then remove it */
81-
fsnotify_inode(inode, FS_UNMOUNT);
82-
83-
fsnotify_inode_delete(inode);
84-
85-
iput_inode = inode;
86-
87-
cond_resched();
88-
spin_lock(&sb->s_inode_list_lock);
89-
}
90-
spin_unlock(&sb->s_inode_list_lock);
91-
92-
iput(iput_inode);
93-
}
94-
9536
void fsnotify_sb_delete(struct super_block *sb)
9637
{
9738
struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(sb);
@@ -100,7 +41,7 @@ void fsnotify_sb_delete(struct super_block *sb)
10041
if (!sbinfo)
10142
return;
10243

103-
fsnotify_unmount_inodes(sb);
44+
fsnotify_unmount_inodes(sbinfo);
10445
fsnotify_clear_marks_by_sb(sb);
10546
/* Wait for outstanding object references from connectors */
10647
wait_var_event(fsnotify_sb_watched_objects(sb),

fs/notify/fsnotify.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ extern struct srcu_struct fsnotify_mark_srcu;
7777
extern int fsnotify_compare_groups(struct fsnotify_group *a,
7878
struct fsnotify_group *b);
7979

80+
/* Destroy all inode marks for given superblock */
81+
void fsnotify_unmount_inodes(struct fsnotify_sb_info *sbinfo);
82+
8083
/* Destroy all marks attached to an object via connector */
8184
extern void fsnotify_destroy_marks(fsnotify_connp_t *connp);
8285
/* run the list of all marks associated with inode and destroy them */

fs/notify/mark.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,56 @@ struct fsnotify_inode_mark_connector {
666666
struct list_head conns_list;
667667
};
668668

669+
static struct inode *fsnotify_get_living_inode(struct fsnotify_sb_info *sbinfo)
670+
{
671+
struct fsnotify_inode_mark_connector *iconn;
672+
struct inode *inode;
673+
674+
spin_lock(&sbinfo->list_lock);
675+
/* Find the first non-evicting inode */
676+
list_for_each_entry(iconn, &sbinfo->inode_conn_list, conns_list) {
677+
/* All connectors on the list are still attached to an inode */
678+
inode = iconn->common.obj;
679+
/*
680+
* For connectors without FSNOTIFY_CONN_FLAG_HAS_IREF
681+
* (evictable marks) corresponding inode may well have 0
682+
* refcount and can be undergoing eviction. OTOH list_lock
683+
* protects us from the connector getting detached and inode
684+
* freed. So we can poke around the inode safely.
685+
*/
686+
spin_lock(&inode->i_lock);
687+
if (likely(
688+
!(inode_state_read(inode) & (I_FREEING | I_WILL_FREE)))) {
689+
__iget(inode);
690+
spin_unlock(&inode->i_lock);
691+
spin_unlock(&sbinfo->list_lock);
692+
return inode;
693+
}
694+
spin_unlock(&inode->i_lock);
695+
}
696+
spin_unlock(&sbinfo->list_lock);
697+
698+
return NULL;
699+
}
700+
701+
/**
702+
* fsnotify_unmount_inodes - an sb is unmounting. Handle any watched inodes.
703+
* @sbinfo: fsnotify info for superblock being unmounted.
704+
*
705+
* Walk all inode connectors for the superblock and free all associated marks.
706+
*/
707+
void fsnotify_unmount_inodes(struct fsnotify_sb_info *sbinfo)
708+
{
709+
struct inode *inode;
710+
711+
while ((inode = fsnotify_get_living_inode(sbinfo))) {
712+
fsnotify_inode(inode, FS_UNMOUNT);
713+
fsnotify_clear_marks_by_inode(inode);
714+
iput(inode);
715+
cond_resched();
716+
}
717+
}
718+
669719
static void fsnotify_init_connector(struct fsnotify_mark_connector *conn,
670720
void *obj, unsigned int obj_type)
671721
{

0 commit comments

Comments
 (0)