mm: fix nr_rotate_swap leak in swapon() error case
authorOmar Sandoval <osandov@fb.com>
Fri, 25 May 2018 21:47:17 +0000 (14:47 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 26 May 2018 01:12:10 +0000 (18:12 -0700)
If swapon() fails after incrementing nr_rotate_swap, we don't decrement
it and thus effectively leak it.  Make sure we decrement it if we
incremented it.

Link: http://lkml.kernel.org/r/b6fe6b879f17fa68eee6cbd876f459f6e5e33495.1526491581.git.osandov@fb.com
Fixes: 81a0298bdfab ("mm, swap: don't use VMA based swap readahead if HDD is used as swap")
Signed-off-by: Omar Sandoval <osandov@fb.com>
Reviewed-by: Rik van Riel <riel@surriel.com>
Reviewed-by: "Huang, Ying" <ying.huang@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
mm/swapfile.c

index cc2cf04d9018ad0c546648dfa6cb8167082bbc99..78a015fcec3b1efe29b56580eb3d4dc510243f6a 100644 (file)
@@ -3112,6 +3112,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
        unsigned long *frontswap_map = NULL;
        struct page *page = NULL;
        struct inode *inode = NULL;
+       bool inced_nr_rotate_swap = false;
 
        if (swap_flags & ~SWAP_FLAGS_VALID)
                return -EINVAL;
@@ -3215,8 +3216,10 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
                        cluster = per_cpu_ptr(p->percpu_cluster, cpu);
                        cluster_set_null(&cluster->index);
                }
-       } else
+       } else {
                atomic_inc(&nr_rotate_swap);
+               inced_nr_rotate_swap = true;
+       }
 
        error = swap_cgroup_swapon(p->type, maxpages);
        if (error)
@@ -3307,6 +3310,8 @@ bad_swap:
        vfree(swap_map);
        kvfree(cluster_info);
        kvfree(frontswap_map);
+       if (inced_nr_rotate_swap)
+               atomic_dec(&nr_rotate_swap);
        if (swap_file) {
                if (inode && S_ISREG(inode->i_mode)) {
                        inode_unlock(inode);