mm: call swap_slot_free_notify() with page lock held
authorMinchan Kim <minchan@kernel.org>
Thu, 28 Apr 2016 23:18:41 +0000 (16:18 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 29 Apr 2016 02:34:04 +0000 (19:34 -0700)
Kyeongdon reported below error which is BUG_ON(!PageSwapCache(page)) in
page_swap_info.  The reason is that page_endio in rw_page unlocks the
page if read I/O is completed so we need to hold a PG_lock again to
check PageSwapCache.  Otherwise, the page can be removed from swapcache.

  Kernel BUG at c00f9040 [verbose debug info unavailable]
  Internal error: Oops - BUG: 0 [#1] PREEMPT SMP ARM
  Modules linked in:
  CPU: 4 PID: 13446 Comm: RenderThread Tainted: G        W 3.10.84-g9f14aec-dirty #73
  task: c3b73200 ti: dd192000 task.ti: dd192000
  PC is at page_swap_info+0x10/0x2c
  LR is at swap_slot_free_notify+0x18/0x6c
  pc : [<c00f9040>]    lr : [<c00f5560>]    psr: 400f0113
  sp : dd193d78  ip : c2deb1e4  fp : da015180
  r10: 00000000  r9 : 000200da  r8 : c120fe08
  r7 : 00000000  r6 : 00000000  r5 : c249a6c0  r4 : = c249a6c0
  r3 : 00000000  r2 : 40080009  r1 : 200f0113  r0 : = c249a6c0
  ..<snip> ..
  Call Trace:
    page_swap_info+0x10/0x2c
    swap_slot_free_notify+0x18/0x6c
    swap_readpage+0x90/0x11c
    read_swap_cache_async+0x134/0x1ac
    swapin_readahead+0x70/0xb0
    handle_pte_fault+0x320/0x6fc
    handle_mm_fault+0xc0/0xf0
    do_page_fault+0x11c/0x36c
    do_DataAbort+0x34/0x118

Fixes: 3f2b1a04f44933f2 ("zram: revive swap_slot_free_notify")
Signed-off-by: Minchan Kim <minchan@kernel.org>
Tested-by: Kyeongdon Kim <kyeongdon.kim@lge.com>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
mm/page_io.c

index cd92e3d67a32201fac72196a050fb6e47017d33d..985f23cfa79b75d3b38fad9bfa5cdf4385f7c8e4 100644 (file)
@@ -353,7 +353,11 @@ int swap_readpage(struct page *page)
 
        ret = bdev_read_page(sis->bdev, swap_page_sector(page), page);
        if (!ret) {
-               swap_slot_free_notify(page);
+               if (trylock_page(page)) {
+                       swap_slot_free_notify(page);
+                       unlock_page(page);
+               }
+
                count_vm_event(PSWPIN);
                return 0;
        }