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. Signed-off-by: Jan Kara --- fs/notify/fsnotify.c | 74 +++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 49 deletions(-) diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 706484fb3bf3..16a4a537d8c3 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -34,62 +34,38 @@ void __fsnotify_mntns_delete(struct mnt_namespace *mntns) } /** - * fsnotify_unmount_inodes - an sb is unmounting. handle any watched inodes. - * @sb: superblock being unmounted. + * fsnotify_unmount_inodes - an sb is unmounting. Handle any watched inodes. + * @sbinfo: fsnotify info for superblock being unmounted. * - * Called during unmount with no locks held, so needs to be safe against - * concurrent modifiers. We temporarily drop sb->s_inode_list_lock and CAN block. + * Walk all inode connectors for the superblock and free all associated marks. */ -static void fsnotify_unmount_inodes(struct super_block *sb) +static void fsnotify_unmount_inodes(struct fsnotify_sb_info *sbinfo) { - struct inode *inode, *iput_inode = NULL; - - spin_lock(&sb->s_inode_list_lock); - list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { - /* - * We cannot __iget() an inode in state I_FREEING, - * I_WILL_FREE, or I_NEW which is fine because by that point - * the inode cannot have any associated watches. - */ - spin_lock(&inode->i_lock); - if (inode_state_read(inode) & (I_FREEING | I_WILL_FREE | I_NEW)) { - spin_unlock(&inode->i_lock); - continue; - } - - /* - * If i_count is zero, the inode cannot have any watches and - * doing an __iget/iput with SB_ACTIVE clear would actually - * evict all inodes with zero i_count from icache which is - * unnecessarily violent and may in fact be illegal to do. - * However, we should have been called /after/ evict_inodes - * removed all zero refcount inodes, in any case. Test to - * be sure. - */ - if (!icount_read(inode)) { - spin_unlock(&inode->i_lock); - continue; - } + int idx; + struct fsnotify_mark_connector *conn; + struct inode *inode; + /* + * We hold srcu over the iteration so that returned connectors stay + * allocated until we can grab them in fsnotify_destroy_conn_marks() + */ + idx = srcu_read_lock(&fsnotify_mark_srcu); + spin_lock(&sbinfo->list_lock); + while (!list_empty(&sbinfo->inode_conn_list)) { + conn = fsnotify_inode_connector_from_list( + sbinfo->inode_conn_list.next); + /* All connectors on the list are still attached to an inode */ + inode = conn->obj; __iget(inode); - spin_unlock(&inode->i_lock); - spin_unlock(&sb->s_inode_list_lock); - - iput(iput_inode); - - /* for each watch, send FS_UNMOUNT and then remove it */ + spin_unlock(&sbinfo->list_lock); fsnotify_inode(inode, FS_UNMOUNT); - - fsnotify_inode_delete(inode); - - iput_inode = inode; - + fsnotify_destroy_marks(&inode->i_fsnotify_marks); + iput(inode); cond_resched(); - spin_lock(&sb->s_inode_list_lock); + spin_lock(&sbinfo->list_lock); } - spin_unlock(&sb->s_inode_list_lock); - - iput(iput_inode); + spin_unlock(&sbinfo->list_lock); + srcu_read_unlock(&fsnotify_mark_srcu, idx); } void fsnotify_sb_delete(struct super_block *sb) @@ -100,7 +76,7 @@ void fsnotify_sb_delete(struct super_block *sb) if (!sbinfo) return; - fsnotify_unmount_inodes(sb); + fsnotify_unmount_inodes(sbinfo); fsnotify_clear_marks_by_sb(sb); /* Wait for outstanding object references from connectors */ wait_var_event(fsnotify_sb_watched_objects(sb), -- 2.51.0