shmem: fix race between shmem_unuse and umount From: Konstantin Khlebnikov Function shmem_unuse could race with generic_shutdown_super. Inode reference is not enough for preventing umount and freeing superblock. Signed-off-by: Konstantin Khlebnikov --- mm/shmem.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/mm/shmem.c b/mm/shmem.c index b3db3779a30a..2018a9a96bb7 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1218,6 +1218,10 @@ static int shmem_unuse_inode(struct inode *inode, unsigned int type, return ret; } +static void shmem_synchronize_umount(struct super_block *sb, void *arg) +{ +} + /* * Read all the shared memory data that resides in the swap * device 'type' back into memory, so the swap device can be @@ -1229,6 +1233,7 @@ int shmem_unuse(unsigned int type, bool frontswap, struct shmem_inode_info *info, *next; struct inode *inode; struct inode *prev_inode = NULL; + struct super_block *sb; int error = 0; if (list_empty(&shmem_swaplist)) @@ -1247,9 +1252,22 @@ int shmem_unuse(unsigned int type, bool frontswap, continue; } + /* + * Lock superblock to prevent umount and freeing it under us. + * If umount in progress it will free swap enties. + * + * Must be done before grabbing inode reference, otherwise + * generic_shutdown_super() will complain about busy inodes. + */ + sb = info->vfs_inode.i_sb; + if (!trylock_super(sb)) + continue; + inode = igrab(&info->vfs_inode); - if (!inode) + if (!inode) { + up_read(&sb->s_umount); continue; + } mutex_unlock(&shmem_swaplist_mutex); if (prev_inode) @@ -1258,6 +1276,7 @@ int shmem_unuse(unsigned int type, bool frontswap, error = shmem_unuse_inode(inode, type, frontswap, fs_pages_to_unuse); + up_read(&sb->s_umount); cond_resched(); mutex_lock(&shmem_swaplist_mutex); @@ -1272,6 +1291,9 @@ int shmem_unuse(unsigned int type, bool frontswap, if (prev_inode) iput(prev_inode); + /* Wait for umounts, this grabs s_umount for each superblock. */ + iterate_supers_type(&shmem_fs_type, shmem_synchronize_umount, NULL); + return error; }