diff --git a/block/bdev.c b/block/bdev.c index cd8323083740..ae472a062e51 100644 --- a/block/bdev.c +++ b/block/bdev.c @@ -1336,56 +1336,53 @@ void bdev_mark_dead(struct block_device *bdev, bool surprise) */ EXPORT_SYMBOL_GPL(bdev_mark_dead); -void sync_bdevs(bool wait) +static int sync_bdevs_inode_iter_cb(struct inode *inode, void *data) { - struct inode *inode, *old_inode = NULL; - - spin_lock(&blockdev_superblock->s_inode_list_lock); - list_for_each_entry(inode, &blockdev_superblock->s_inodes, i_sb_list) { - struct address_space *mapping = inode->i_mapping; - struct block_device *bdev; + bool wait = (bool)data; + struct block_device *bdev; + struct address_space *mapping = inode->i_mapping; - spin_lock(&inode->i_lock); - if (inode_state_read(inode) & (I_FREEING | I_WILL_FREE | I_NEW) || - mapping->nrpages == 0) { - spin_unlock(&inode->i_lock); - continue; - } - __iget(inode); + if (mapping->nrpages == 0) { spin_unlock(&inode->i_lock); - spin_unlock(&blockdev_superblock->s_inode_list_lock); + return 0; + } + + /* + * We hold a reference to 'inode' so it couldn't have been + * removed from s_inodes list while we dropped the + * s_inode_list_lock. + */ + __iget(inode); + spin_unlock(&inode->i_lock); + spin_unlock(&blockdev_superblock->s_inode_list_lock); + bdev = I_BDEV(inode); + + mutex_lock(&bdev->bd_disk->open_mutex); + if (!atomic_read(&bdev->bd_openers)) { + ; /* skip */ + } else if (wait) { /* - * We hold a reference to 'inode' so it couldn't have been - * removed from s_inodes list while we dropped the - * s_inode_list_lock We cannot iput the inode now as we can - * be holding the last reference and we cannot iput it under - * s_inode_list_lock. So we keep the reference and iput it - * later. + * We keep the error status of individual mapping so + * that applications can catch the writeback error using + * fsync(2). See filemap_fdatawait_keep_errors() for + * details. */ - iput(old_inode); - old_inode = inode; - bdev = I_BDEV(inode); - - mutex_lock(&bdev->bd_disk->open_mutex); - if (!atomic_read(&bdev->bd_openers)) { - ; /* skip */ - } else if (wait) { - /* - * We keep the error status of individual mapping so - * that applications can catch the writeback error using - * fsync(2). See filemap_fdatawait_keep_errors() for - * details. - */ - filemap_fdatawait_keep_errors(inode->i_mapping); - } else { - filemap_fdatawrite(inode->i_mapping); - } - mutex_unlock(&bdev->bd_disk->open_mutex); - - spin_lock(&blockdev_superblock->s_inode_list_lock); + filemap_fdatawait_keep_errors(inode->i_mapping); + } else { + filemap_fdatawrite(inode->i_mapping); } - spin_unlock(&blockdev_superblock->s_inode_list_lock); - iput(old_inode); + mutex_unlock(&bdev->bd_disk->open_mutex); + iput(inode); + + spin_lock(&blockdev_superblock->s_inode_list_lock); + + return 0; +} + +void sync_bdevs(bool wait) +{ + sb_for_each_inodes(blockdev_superblock, INODE_ITER_NORMAL, + sync_bdevs_inode_iter_cb, (void *)wait); } /* diff --git a/fs/drop_caches.c b/fs/drop_caches.c index 49f56a598ecb..0d475a5ff8cf 100644 --- a/fs/drop_caches.c +++ b/fs/drop_caches.c @@ -16,36 +16,30 @@ /* A global variable is a bit ugly, but it keeps the code simple */ static int sysctl_drop_caches; -static void drop_pagecache_sb(struct super_block *sb, void *unused) +static int drop_pagecache_inode_iter_cb(struct inode *inode, void *unused) { - struct inode *inode, *toput_inode = NULL; + struct super_block *sb = inode->i_sb; - spin_lock(&sb->s_inode_list_lock); - list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { - spin_lock(&inode->i_lock); - /* - * We must skip inodes in unusual state. We may also skip - * inodes without pages but we deliberately won't in case - * we need to reschedule to avoid softlockups. - */ - if ((inode_state_read(inode) & (I_FREEING | I_WILL_FREE | I_NEW)) || - (mapping_empty(inode->i_mapping) && !need_resched())) { - spin_unlock(&inode->i_lock); - continue; - } - __iget(inode); + if (mapping_empty(inode->i_mapping)) { spin_unlock(&inode->i_lock); - spin_unlock(&sb->s_inode_list_lock); - - invalidate_mapping_pages(inode->i_mapping, 0, -1); - iput(toput_inode); - toput_inode = inode; - - cond_resched(); - spin_lock(&sb->s_inode_list_lock); + return 0; } + + __iget(inode); + spin_unlock(&inode->i_lock); spin_unlock(&sb->s_inode_list_lock); - iput(toput_inode); + + invalidate_mapping_pages(inode->i_mapping, 0, -1); + iput(inode); + + spin_lock(&sb->s_inode_list_lock); + + return 0; +} + +static void drop_pagecache_sb(struct super_block *sb, void *unused) +{ + sb_for_each_inodes(sb, INODE_ITER_NORMAL, drop_pagecache_inode_iter_cb, NULL); } static int drop_caches_sysctl_handler(const struct ctl_table *table, int write, diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 718e0da7dfce..acdbf9ca44a3 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -996,7 +996,7 @@ static int gfs2_lm_mount(struct gfs2_sbd *sdp, int silent) switch (token) { case Opt_jid: ret = match_int(&tmp[0], &option); - if (ret || option < 0) + if (ret || option < 0) goto hostdata_error; if (test_and_clear_bit(SDF_NOJOURNALID, &sdp->sd_flags)) ls->ls_jid = option; @@ -1719,6 +1719,19 @@ static int gfs2_meta_init_fs_context(struct fs_context *fc) return 0; } +static int gfs2_evict_inode_iter_cb(struct inode *inode, void *unused) +{ + struct super_block *sb = inode->i_sb; + + __iget(inode); + spin_unlock(&inode->i_lock); + spin_unlock(&sb->s_inode_list_lock); + + iput(inode); + spin_lock(&sb->s_inode_list_lock); + return 0; +} + /** * gfs2_evict_inodes - evict inodes cooperatively * @sb: the superblock @@ -1741,31 +1754,10 @@ static int gfs2_meta_init_fs_context(struct fs_context *fc) */ static void gfs2_evict_inodes(struct super_block *sb) { - struct inode *inode, *toput_inode = NULL; struct gfs2_sbd *sdp = sb->s_fs_info; set_bit(SDF_EVICTING, &sdp->sd_flags); - - spin_lock(&sb->s_inode_list_lock); - list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { - spin_lock(&inode->i_lock); - if ((inode_state_read(inode) & (I_FREEING | I_WILL_FREE | I_NEW)) && - !need_resched()) { - spin_unlock(&inode->i_lock); - continue; - } - __iget(inode); - spin_unlock(&inode->i_lock); - spin_unlock(&sb->s_inode_list_lock); - - iput(toput_inode); - toput_inode = inode; - - cond_resched(); - spin_lock(&sb->s_inode_list_lock); - } - spin_unlock(&sb->s_inode_list_lock); - iput(toput_inode); + sb_for_each_inodes(sb, INODE_ITER_NORMAL, gfs2_evict_inode_iter_cb, NULL); } static void gfs2_kill_sb(struct super_block *sb) diff --git a/fs/inode.c b/fs/inode.c index ba7da39be4a3..07a5f48641af 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -69,6 +69,15 @@ const struct address_space_operations empty_aops = { }; EXPORT_SYMBOL(empty_aops); +struct inode_iter { + struct list_head iters_node; /* sb->s_inodes_iters */ + struct list_head *next; /* next node going to iterate */ + unsigned int flags; + inode_iter_cb func; + void *data; + int ret; +}; + static DEFINE_PER_CPU(unsigned long, nr_inodes); static DEFINE_PER_CPU(unsigned long, nr_unused); @@ -641,12 +650,96 @@ void inode_sb_list_add(struct inode *inode) } EXPORT_SYMBOL_GPL(inode_sb_list_add); +static void inode_sb_iter_start(struct super_block *sb, struct inode_iter *it, + unsigned int flags, inode_iter_cb fn, void *data) +{ + it->flags = flags; + it->func = fn; + it->data = data; + it->ret = 0; + spin_lock(&sb->s_inode_list_lock); + it->next = sb->s_inodes.next; + list_add(&it->iters_node, &sb->s_inodes_iters); +} + +static void inode_sb_iter_end(struct inode_iter *it, struct super_block *sb) +{ + list_del(&it->iters_node); + spin_unlock(&sb->s_inode_list_lock); +} + +static bool inode_sb_iter_next(struct inode_iter *it, struct super_block *sb) +{ + struct inode *inode = NULL; + int ret; + + while (!inode && it->next != &sb->s_inodes) { + inode = list_entry(it->next, struct inode, i_sb_list); + if (it->flags & INODE_ITER_UNUSED) { + if (icount_read_once(inode)) { + it->next = it->next->next; + continue; + } + + spin_lock(&inode->i_lock); + if (icount_read(inode)) { + spin_unlock(&inode->i_lock); + it->next = it->next->next; + continue; + } + } else { + spin_lock(&inode->i_lock); + } + + if ((it->flags & INODE_ITER_NORMAL) && + (inode_state_read(inode) & (I_NEW | I_FREEING | I_WILL_FREE))) { + spin_unlock(&inode->i_lock); + it->next = it->next->next; + continue; + } + + it->next = it->next->next; + ret = it->func(inode, it->data); + if (ret) { + it->ret = ret; + return false; + } + + if (need_resched()) { + spin_unlock(&sb->s_inode_list_lock); + cond_resched(); + spin_lock(&sb->s_inode_list_lock); + } + } + + return it->next == &sb->s_inodes ? false : true; +} + +int sb_for_each_inodes(struct super_block *sb, unsigned int flags, + inode_iter_cb fn, void *data) +{ + struct inode_iter it; + + inode_sb_iter_start(sb, &it, flags, fn, data); + while (inode_sb_iter_next(&it, sb)) + ; + inode_sb_iter_end(&it, sb); + + return it.ret; +} +EXPORT_SYMBOL(sb_for_each_inodes); + static inline void inode_sb_list_del(struct inode *inode) { struct super_block *sb = inode->i_sb; + struct inode_iter *it; if (!list_empty(&inode->i_sb_list)) { spin_lock(&sb->s_inode_list_lock); + list_for_each_entry(it, &sb->s_inodes_iters, iters_node) { + if (it->next == &inode->i_sb_list) + it->next = inode->i_sb_list.next; + } list_del_init(&inode->i_sb_list); spin_unlock(&sb->s_inode_list_lock); } @@ -866,6 +959,17 @@ static void dispose_list(struct list_head *head) } } +static int evict_inodes_inode_iter_cb(struct inode *inode, void *data) +{ + struct list_head *dispose = (struct list_head *)data; + + inode_state_set(inode, I_FREEING); + inode_lru_list_del(inode); + spin_unlock(&inode->i_lock); + list_add(&inode->i_lru, dispose); + return 0; +} + /** * evict_inodes - evict all evictable inodes for a superblock * @sb: superblock to operate on @@ -877,44 +981,10 @@ static void dispose_list(struct list_head *head) */ void evict_inodes(struct super_block *sb) { - struct inode *inode; LIST_HEAD(dispose); + unsigned int flags = INODE_ITER_NORMAL | INODE_ITER_UNUSED; -again: - spin_lock(&sb->s_inode_list_lock); - list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { - if (icount_read_once(inode)) - continue; - - spin_lock(&inode->i_lock); - if (icount_read(inode)) { - spin_unlock(&inode->i_lock); - continue; - } - if (inode_state_read(inode) & (I_NEW | I_FREEING | I_WILL_FREE)) { - spin_unlock(&inode->i_lock); - continue; - } - - inode_state_set(inode, I_FREEING); - inode_lru_list_del(inode); - spin_unlock(&inode->i_lock); - list_add(&inode->i_lru, &dispose); - - /* - * We can have a ton of inodes to evict at unmount time given - * enough memory, check to see if we need to go to sleep for a - * bit so we don't livelock. - */ - if (need_resched()) { - spin_unlock(&sb->s_inode_list_lock); - cond_resched(); - dispose_list(&dispose); - goto again; - } - } - spin_unlock(&sb->s_inode_list_lock); - + sb_for_each_inodes(sb, flags, evict_inodes_inode_iter_cb, &dispose); dispose_list(&dispose); } EXPORT_SYMBOL_GPL(evict_inodes); diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 1c78c695d0dd..aed94f4bc80d 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -1043,64 +1043,48 @@ static int dqinit_needed(struct inode *inode, int type) return 0; } -/* This routine is guarded by s_umount semaphore */ -static int add_dquot_ref(struct super_block *sb, int type) +static int add_dquot_ref_inode_iter_cb(struct inode *inode, void *data) { - struct inode *inode, *old_inode = NULL; + int type = *(int *)data; + int err = 0; + struct super_block *sb = inode->i_sb; #ifdef CONFIG_QUOTA_DEBUG int reserved = 0; #endif - int err = 0; - spin_lock(&sb->s_inode_list_lock); - list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { - spin_lock(&inode->i_lock); - if ((inode_state_read(inode) & (I_FREEING | I_WILL_FREE | I_NEW)) || - !atomic_read(&inode->i_writecount) || - !dqinit_needed(inode, type)) { - spin_unlock(&inode->i_lock); - continue; - } - __iget(inode); + if (!atomic_read(&inode->i_writecount) || + !dqinit_needed(inode, type)) { spin_unlock(&inode->i_lock); - spin_unlock(&sb->s_inode_list_lock); + return 0; + } + + __iget(inode); + spin_unlock(&inode->i_lock); + spin_unlock(&sb->s_inode_list_lock); #ifdef CONFIG_QUOTA_DEBUG - if (unlikely(inode_get_rsv_space(inode) > 0)) - reserved = 1; + if (unlikely(inode_get_rsv_space(inode) > 0)) + reserved = 1; #endif - iput(old_inode); - err = __dquot_initialize(inode, type); - if (err) { - iput(inode); - goto out; - } - - /* - * We hold a reference to 'inode' so it couldn't have been - * removed from s_inodes list while we dropped the - * s_inode_list_lock. We cannot iput the inode now as we can be - * holding the last reference and we cannot iput it under - * s_inode_list_lock. So we keep the reference and iput it - * later. - */ - old_inode = inode; - cond_resched(); - spin_lock(&sb->s_inode_list_lock); - } - spin_unlock(&sb->s_inode_list_lock); - iput(old_inode); -out: + err = __dquot_initialize(inode, type); #ifdef CONFIG_QUOTA_DEBUG - if (reserved) { - quota_error(sb, "Writes happened before quota was turned on " - "thus quota information is probably inconsistent. " - "Please run quotacheck(8)"); - } + if (reserved) + quota_error(sb, "Writes happened before quota was turned " + "on thus quota information is probably " + "inconsistent. Please run quotacheck(8)"); #endif + iput(inode); + spin_lock(&sb->s_inode_list_lock); return err; } +/* This routine is guarded by s_umount semaphore */ +static int add_dquot_ref(struct super_block *sb, int type) +{ + return sb_for_each_inodes(sb, INODE_ITER_NORMAL, + add_dquot_ref_inode_iter_cb, &type); +} + static void remove_dquot_ref(struct super_block *sb, int type) { struct inode *inode; diff --git a/fs/super.c b/fs/super.c index 9d4025213521..56091c5493e3 100644 --- a/fs/super.c +++ b/fs/super.c @@ -368,6 +368,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags, spin_lock_init(&s->s_roots_lock); mutex_init(&s->s_sync_lock); INIT_LIST_HEAD(&s->s_inodes); + INIT_LIST_HEAD(&s->s_inodes_iters); spin_lock_init(&s->s_inode_list_lock); INIT_LIST_HEAD(&s->s_inodes_wb); spin_lock_init(&s->s_inode_wblist_lock); diff --git a/include/linux/fs.h b/include/linux/fs.h index f9d1e05e8ae6..f3176ab10e65 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -296,7 +296,7 @@ struct iattr { */ #define FILESYSTEM_MAX_STACK_DEPTH 2 -/** +/** * enum positive_aop_returns - aop return codes with specific semantics * * @AOP_WRITEPAGE_ACTIVATE: Informs the caller that page writeback has @@ -306,7 +306,7 @@ struct iattr { * be a candidate for writeback again in the near * future. Other callers must be careful to unlock * the page if they get this return. Returned by - * writepage(); + * writepage(); * * @AOP_TRUNCATED_PAGE: The AOP method that was handed a locked page has * unlocked it and the page might have been truncated. @@ -870,6 +870,21 @@ struct inode { void *i_private; /* fs or device private pointer */ } __randomize_layout; +enum inode_iter_flags_enum { + INODE_ITER_NORMAL = (1U << 1), /* Exclude inodes with (I_NEW | I_FREEING | I_WILL_FREE). */ + INODE_ITER_UNUSED = (1U << 2), /* Only return inodes with (i_count == 0). */ +}; + +/* + * start end + * inode->i_lock locked unlocked + * sb->s_inode_list_lock locked locked + */ +typedef int (*inode_iter_cb) (struct inode *, void *); + +int sb_for_each_inodes(struct super_block *sb, unsigned int flags, + inode_iter_cb fn, void *data); + /* * i_state handling * @@ -1315,8 +1330,10 @@ struct file *get_file_active(struct file **f); #define MAX_NON_LFS ((1UL<<31) - 1) -/* Page cache limit. The filesystems should put that into their s_maxbytes - limits, otherwise bad things can happen in VM. */ +/* + * Page cache limit. The filesystems should put that into their s_maxbytes + * limits, otherwise bad things can happen in VM. + */ #if BITS_PER_LONG==32 #define MAX_LFS_FILESIZE ((loff_t)ULONG_MAX << PAGE_SHIFT) #elif BITS_PER_LONG==64 @@ -2285,7 +2302,7 @@ int sync_inode_metadata(struct inode *inode, int wait); struct file_system_type { const char *name; int fs_flags; -#define FS_REQUIRES_DEV 1 +#define FS_REQUIRES_DEV 1 #define FS_BINARY_MOUNTDATA 2 #define FS_HAS_SUBTYPE 4 #define FS_USERNS_MOUNT 8 /* Can be mounted by userns root */ @@ -2907,7 +2924,7 @@ ssize_t __kernel_read(struct file *file, void *buf, size_t count, loff_t *pos); extern ssize_t kernel_write(struct file *, const void *, size_t, loff_t *); extern ssize_t __kernel_write(struct file *, const void *, size_t, loff_t *); extern struct file * open_exec(const char *); - + /* fs/dcache.c -- generic fs support functions */ extern bool is_subdir(struct dentry *, struct dentry *); extern bool path_is_under(const struct path *, const struct path *); diff --git a/include/linux/fs/super_types.h b/include/linux/fs/super_types.h index ecd96aeb1cee..1f81cc219b8e 100644 --- a/include/linux/fs/super_types.h +++ b/include/linux/fs/super_types.h @@ -269,9 +269,10 @@ struct super_block { */ int s_stack_depth; - /* s_inode_list_lock protects s_inodes */ + /* s_inode_list_lock protects s_inodes and s_inodes_iters */ spinlock_t s_inode_list_lock ____cacheline_aligned_in_smp; struct list_head s_inodes; /* all inodes */ + struct list_head s_inodes_iters; /* all iterators */ spinlock_t s_inode_wblist_lock; struct list_head s_inodes_wb; /* writeback inodes */ diff --git a/security/landlock/fs.c b/security/landlock/fs.c index 330a1871bf94..9d8355257e86 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -1375,110 +1375,80 @@ static void hook_inode_free_security_rcu(void *inode_security) /* Super-block hooks */ -/* - * Release the inodes used in a security policy. - * - * Cf. fsnotify_unmount_inodes() and evict_inodes() - */ -static void hook_sb_delete(struct super_block *const sb) +static int hook_sb_delete_inode_iter_cb(struct inode *inode, void *data) { - struct inode *inode, *prev_inode = NULL; + struct landlock_object *object; + struct super_block *sb = inode->i_sb; - if (!landlock_initialized) - return; + if (!atomic_read(&inode->i_count)) { + spin_unlock(&inode->i_lock); + return 0; + } - spin_lock(&sb->s_inode_list_lock); - list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { - struct landlock_object *object; + rcu_read_lock(); + object = rcu_dereference(landlock_inode(inode)->object); + if (!object) { + rcu_read_unlock(); + spin_unlock(&inode->i_lock); + return 0; + } + /* Keeps a reference to this inode until the next loop walk. */ + __iget(inode); + spin_unlock(&inode->i_lock); - /* Only handles referenced inodes. */ - if (!icount_read_once(inode)) - continue; + /* + * If there is no concurrent release_inode() ongoing, then we + * are in charge of calling iput() on this inode, otherwise we + * will just wait for it to finish. + */ + spin_lock(&object->lock); + if (object->underobj == inode) { + object->underobj = NULL; + spin_unlock(&object->lock); + rcu_read_unlock(); /* - * Protects against concurrent modification of inode (e.g. - * from get_inode_object()). + * Because object->underobj was not NULL, + * release_inode() and get_inode_object() guarantee + * that it is safe to reset + * landlock_inode(inode)->object while it is not NULL. + * It is therefore not necessary to lock inode->i_lock. */ - spin_lock(&inode->i_lock); + rcu_assign_pointer(landlock_inode(inode)->object, NULL); /* - * Checks I_FREEING and I_WILL_FREE to protect against a race - * condition when release_inode() just called iput(), which - * could lead to a NULL dereference of inode->security or a - * second call to iput() for the same Landlock object. Also - * checks I_NEW because such inode cannot be tied to an object. + * At this point, we own the ihold() reference that was + * originally set up by get_inode_object() and the + * __iget() reference that we just set in this loop + * walk. Therefore there are at least two references + * on the inode. */ - if (inode_state_read(inode) & - (I_FREEING | I_WILL_FREE | I_NEW)) { - spin_unlock(&inode->i_lock); - continue; - } + iput_not_last(inode); + } else { + spin_unlock(&object->lock); + rcu_read_unlock(); + } - rcu_read_lock(); - object = rcu_dereference(landlock_inode(inode)->object); - if (!object) { - rcu_read_unlock(); - spin_unlock(&inode->i_lock); - continue; - } - /* Keeps a reference to this inode until the next loop walk. */ - __iget(inode); - spin_unlock(&inode->i_lock); + spin_unlock(&sb->s_inode_list_lock); + iput(inode); + spin_lock(&sb->s_inode_list_lock); - /* - * If there is no concurrent release_inode() ongoing, then we - * are in charge of calling iput() on this inode, otherwise we - * will just wait for it to finish. - */ - spin_lock(&object->lock); - if (object->underobj == inode) { - object->underobj = NULL; - spin_unlock(&object->lock); - rcu_read_unlock(); + return 0; +} - /* - * Because object->underobj was not NULL, - * release_inode() and get_inode_object() guarantee - * that it is safe to reset - * landlock_inode(inode)->object while it is not NULL. - * It is therefore not necessary to lock inode->i_lock. - */ - rcu_assign_pointer(landlock_inode(inode)->object, NULL); - /* - * At this point, we own the ihold() reference that was - * originally set up by get_inode_object() and the - * __iget() reference that we just set in this loop - * walk. Therefore there are at least two references - * on the inode. - */ - iput_not_last(inode); - } else { - spin_unlock(&object->lock); - rcu_read_unlock(); - } +/* + * Release the inodes used in a security policy. + * + * Cf. fsnotify_unmount_inodes() and evict_inodes() + */ +static void hook_sb_delete(struct super_block *const sb) +{ + unsigned int flags = INODE_ITER_NORMAL; - if (prev_inode) { - /* - * At this point, we still own the __iget() reference - * that we just set in this loop walk. Therefore we - * can drop the list lock and know that the inode won't - * disappear from under us until the next loop walk. - */ - spin_unlock(&sb->s_inode_list_lock); - /* - * We can now actually put the inode reference from the - * previous loop walk, which is not needed anymore. - */ - iput(prev_inode); - cond_resched(); - spin_lock(&sb->s_inode_list_lock); - } - prev_inode = inode; - } - spin_unlock(&sb->s_inode_list_lock); + if (!landlock_initialized) + return; + + sb_for_each_inodes(sb, flags, hook_sb_delete_inode_iter_cb, NULL); - /* Puts the inode reference from the last loop walk, if any. */ - if (prev_inode) - iput(prev_inode); /* Waits for pending iput() in release_inode(). */ wait_var_event(&landlock_superblock(sb)->inode_refs, !atomic_long_read(&landlock_superblock(sb)->inode_refs));