]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - mm/page_io.c
mm/page_io.c: fix oops during block io poll in swapin path
[karo-tx-linux.git] / mm / page_io.c
index 2da71e627812ea5cee576be0bca85cce32974708..5f61b54ee1f38e0bb7ae7bee3efe3257f8967a44 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/frontswap.h>
 #include <linux/blkdev.h>
 #include <linux/uio.h>
+#include <linux/sched/task.h>
 #include <asm/pgtable.h>
 
 static struct bio *get_swap_bio(gfp_t gfp_flags,
@@ -117,6 +118,7 @@ static void swap_slot_free_notify(struct page *page)
 static void end_swap_bio_read(struct bio *bio)
 {
        struct page *page = bio->bi_io_vec[0].bv_page;
+       struct task_struct *waiter = bio->bi_private;
 
        if (bio->bi_status) {
                SetPageError(page);
@@ -132,7 +134,10 @@ static void end_swap_bio_read(struct bio *bio)
        swap_slot_free_notify(page);
 out:
        unlock_page(page);
+       WRITE_ONCE(bio->bi_private, NULL);
        bio_put(bio);
+       wake_up_process(waiter);
+       put_task_struct(waiter);
 }
 
 int generic_swapfile_activate(struct swap_info_struct *sis,
@@ -329,11 +334,13 @@ out:
        return ret;
 }
 
-int swap_readpage(struct page *page)
+int swap_readpage(struct page *page, bool do_poll)
 {
        struct bio *bio;
        int ret = 0;
        struct swap_info_struct *sis = page_swap_info(page);
+       blk_qc_t qc;
+       struct block_device *bdev;
 
        VM_BUG_ON_PAGE(!PageSwapCache(page), page);
        VM_BUG_ON_PAGE(!PageLocked(page), page);
@@ -372,9 +379,28 @@ int swap_readpage(struct page *page)
                ret = -ENOMEM;
                goto out;
        }
+       bdev = bio->bi_bdev;
+       /*
+        * Keep this task valid during swap readpage because the oom killer may
+        * attempt to access it in the page fault retry time check.
+        */
+       get_task_struct(current);
+       bio->bi_private = current;
        bio_set_op_attrs(bio, REQ_OP_READ, 0);
        count_vm_event(PSWPIN);
-       submit_bio(bio);
+       bio_get(bio);
+       qc = submit_bio(bio);
+       while (do_poll) {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               if (!READ_ONCE(bio->bi_private))
+                       break;
+
+               if (!blk_mq_poll(bdev_get_queue(bdev), qc))
+                       break;
+       }
+       __set_current_state(TASK_RUNNING);
+       bio_put(bio);
+
 out:
        return ret;
 }