mm/swapfile: fix possible data races of inuse_pages
authorMiaohe Lin <linmiaohe@huawei.com>
Wed, 8 Jun 2022 14:40:30 +0000 (22:40 +0800)
committerakpm <akpm@linux-foundation.org>
Mon, 4 Jul 2022 01:08:42 +0000 (18:08 -0700)
si->inuse_pages could still be accessed concurrently now.  The plain reads
outside si->lock critical section, i.e.  swap_show and si_swapinfo, which
results in data races.  READ_ONCE and WRITE_ONCE is used to fix such data
races.  Note these data races should be ok because they're just used for
showing swap info.

[linmiaohe@huawei.com: use WRITE_ONCE to pair with READ_ONCE]
Link: https://lkml.kernel.org/r/20220625093346.48894-2-linmiaohe@huawei.com
Link: https://lkml.kernel.org/r/20220608144031.829-3-linmiaohe@huawei.com
Signed-off-by: Miaohe Lin <linmiaohe@huawei.com>
Reviewed-by: David Hildenbrand <david@redhat.com>
Reviewed-by: "Huang, Ying" <ying.huang@intel.com>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
mm/swapfile.c

index a2e66d855b1996768ec7db943ad60aa9cb32712f..6a7579951fa455da13cade4aafafee01d5749583 100644 (file)
@@ -695,7 +695,7 @@ static void swap_range_alloc(struct swap_info_struct *si, unsigned long offset,
                si->lowest_bit += nr_entries;
        if (end == si->highest_bit)
                WRITE_ONCE(si->highest_bit, si->highest_bit - nr_entries);
-       si->inuse_pages += nr_entries;
+       WRITE_ONCE(si->inuse_pages, si->inuse_pages + nr_entries);
        if (si->inuse_pages == si->pages) {
                si->lowest_bit = si->max;
                si->highest_bit = 0;
@@ -732,7 +732,7 @@ static void swap_range_free(struct swap_info_struct *si, unsigned long offset,
                        add_to_avail_list(si);
        }
        atomic_long_add(nr_entries, &nr_swap_pages);
-       si->inuse_pages -= nr_entries;
+       WRITE_ONCE(si->inuse_pages, si->inuse_pages - nr_entries);
        if (si->flags & SWP_BLKDEV)
                swap_slot_free_notify =
                        si->bdev->bd_disk->fops->swap_slot_free_notify;
@@ -2640,7 +2640,7 @@ static int swap_show(struct seq_file *swap, void *v)
        }
 
        bytes = si->pages << (PAGE_SHIFT - 10);
-       inuse = si->inuse_pages << (PAGE_SHIFT - 10);
+       inuse = READ_ONCE(si->inuse_pages) << (PAGE_SHIFT - 10);
 
        file = si->swap_file;
        len = seq_file_path(swap, file, " \t\n\\");
@@ -3259,7 +3259,7 @@ void si_swapinfo(struct sysinfo *val)
                struct swap_info_struct *si = swap_info[type];
 
                if ((si->flags & SWP_USED) && !(si->flags & SWP_WRITEOK))
-                       nr_to_be_unused += si->inuse_pages;
+                       nr_to_be_unused += READ_ONCE(si->inuse_pages);
        }
        val->freeswap = atomic_long_read(&nr_swap_pages) + nr_to_be_unused;
        val->totalswap = total_swap_pages + nr_to_be_unused;