On Fri, Oct 11, 2019 at 04:14:02PM -0500, Eric Sandeen wrote: > > > On 10/11/19 1:45 PM, Eric Sandeen wrote: > > On 10/11/19 1:32 PM, Matthew Wilcox wrote: > >> On Fri, Oct 11, 2019 at 11:49:38AM -0500, Eric Sandeen wrote: > >>> @@ -698,6 +699,13 @@ int invalidate_inodes(struct super_block *sb, bool kill_dirty) > >>> inode_lru_list_del(inode); > >>> spin_unlock(&inode->i_lock); > >>> list_add(&inode->i_lru, &dispose); > >>> + > >>> + if (need_resched()) { > >>> + spin_unlock(&sb->s_inode_list_lock); > >>> + cond_resched(); > >>> + dispose_list(&dispose); > >>> + goto again; > >>> + } > >>> } > >>> spin_unlock(&sb->s_inode_list_lock); > >>> > >> > >> Is this equivalent to: > >> > >> + cond_resched_lock(&sb->s_inode_list_lock)); > >> > >> or is disposing of the list a crucial part here? > > > > I think we need to dispose, or we'll start with the entire ~unmodified list again after the goto: > > Oh, if you meant in lieu of the goto, we can't drop that lock and > expect to pick up our traversal where we left off, can we? No, we can't. Unless you're doing the iget/iput game (which we can't here!) the moment the s_inode_list_lock is dropped we cannot rely on the inode or it's next pointer to still be valid. Hence we have to restart the traversal. And we dispose of the list before restarting because there's nothing to gain by waiting until we've done the entire sb inode list walk (could be hundreds of millions of inodes) before we start actually freeing them.... Cheers, Dave. -- Dave Chinner david@xxxxxxxxxxxxx