diff --git a/mm/shmem.c b/mm/shmem.c index 6df0237832c0..c44d2dd71c89 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1616,6 +1616,17 @@ static int shmem_setattr(const struct mnt_idmap *idmap, return error; } +static void shmem_disallow_eviction(struct shmem_inode_info *info) +{ + atomic_inc(&info->stop_eviction); +} + +static void shmem_allow_eviction(struct shmem_inode_info *info) +{ + if (atomic_dec_and_test(&info->stop_eviction)) + wake_up_var(&info->stop_eviction); +} + static void shmem_evict_inode(struct inode *inode) { struct shmem_inode_info *info = SHMEM_I(inode); @@ -1628,16 +1639,21 @@ static void shmem_evict_inode(struct inode *inode) mapping_set_exiting(inode->i_mapping); shmem_truncate_range(inode, 0, (loff_t)-1); shmem_unused_huge_del(inode); - while (!list_empty(&info->swaplist)) { - /* Wait while shmem_unuse() is scanning this inode... */ + /* + * Writeout may still use the inode after swapoff has removed it + * from the swaplist, so always wait at least once. + */ + do { wait_var_event(&info->stop_eviction, !atomic_read(&info->stop_eviction)); + if (list_empty(&info->swaplist)) + break; spin_lock(&shmem_swaplist_lock); /* ...but beware of the race if we peeked too early */ if (!atomic_read(&info->stop_eviction)) list_del_init(&info->swaplist); spin_unlock(&shmem_swaplist_lock); - } + } while (!list_empty(&info->swaplist)); } simple_xattrs_free(&sbinfo->xa_cache, &info->xattrs, sbinfo->max_inodes ? &freed : NULL); @@ -1776,15 +1792,14 @@ int shmem_unuse(unsigned int type) * remove placeholder inode from swaplist, nor let it be freed * (igrab() would protect from unlink, but not from unmount). */ - atomic_inc(&info->stop_eviction); + shmem_disallow_eviction(info); spin_unlock(&shmem_swaplist_lock); error = shmem_unuse_inode(&info->vfs_inode, type); cond_resched(); spin_lock(&shmem_swaplist_lock); - if (atomic_dec_and_test(&info->stop_eviction)) - wake_up_var(&info->stop_eviction); + shmem_allow_eviction(info); if (error) break; if (list_empty(&info->swaplist)) @@ -1915,6 +1930,13 @@ int shmem_writeout(struct swap_io_ctx *ctx, struct folio *folio, spin_unlock(&shmem_swaplist_lock); } + /* + * Once the folio has left the page cache, its lock no longer + * keeps the inode from being evicted and freed: hold eviction + * off, in case the folio has to be put back below. + */ + shmem_disallow_eviction(info); + folio_dup_swap(folio, NULL); shmem_delete_from_page_cache(folio, swp_to_radix_entry(folio->swap)); @@ -1922,6 +1944,7 @@ int shmem_writeout(struct swap_io_ctx *ctx, struct folio *folio, error = swap_writeout(ctx, folio); if (error != AOP_WRITEPAGE_ACTIVATE) { /* folio has been unlocked */ + shmem_allow_eviction(info); return error; } @@ -1938,6 +1961,7 @@ int shmem_writeout(struct swap_io_ctx *ctx, struct folio *folio, shmem_recalc_inode(inode, 0, -nr_pages); folio_put_swap(folio, NULL); } + shmem_allow_eviction(info); /* * The swap_cache_del_folio() below could be left for