diff mbox series

PM: Batch hibernate and resume IO requests

Message ID cc09f36e506145399fe470c71ad34c7c@EX13D12UWA002.ant.amazon.com
State New
Headers show
Series PM: Batch hibernate and resume IO requests | expand

Commit Message

Chen, Xiaoyi Sept. 22, 2020, 4:19 p.m. UTC
Hibernate and resume process submits individual IO requests for each page
of the data. With this patch, we use blk_plug to improve the batching of
these requests.

Tested this patch with hibernate and resumes, and consistently observed the
merging of the IO requests. We see more than an order of magnitude
improvement in hibernate and resume speed.

One hibernate and resume cycle for 16GB used RAM out of 32GB takes around
21 minutes before the change, and 1 minutes after the change on systems
with limited storage IOPS.

Signed-off-by: Xiaoyi Chen <cxiaoyi@amazon.com>
Signed-off-by: Anchal Agarwal <anchalag@amazon.com>
---
 kernel/power/swap.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

--
2.23.3

Comments

Rafael J. Wysocki Sept. 25, 2020, 3:27 p.m. UTC | #1
On Tue, Sep 22, 2020 at 6:19 PM Chen, Xiaoyi <cxiaoyi@amazon.com> wrote:
>

>

> Hibernate and resume process submits individual IO requests for each page

> of the data. With this patch, we use blk_plug to improve the batching of

> these requests.

>

> Tested this patch with hibernate and resumes, and consistently observed the

> merging of the IO requests. We see more than an order of magnitude

> improvement in hibernate and resume speed.

>

> One hibernate and resume cycle for 16GB used RAM out of 32GB takes around

> 21 minutes before the change, and 1 minutes after the change on systems

> with limited storage IOPS.

>

> Signed-off-by: Xiaoyi Chen <cxiaoyi@amazon.com>

> Signed-off-by: Anchal Agarwal <anchalag@amazon.com>

> ---

>  kernel/power/swap.c | 15 +++++++++++++++

>  1 file changed, 15 insertions(+)

>

> diff --git a/kernel/power/swap.c b/kernel/power/swap.c

> index 01e2858b5fe3..961615365b5c 100644

> --- a/kernel/power/swap.c

> +++ b/kernel/power/swap.c

> @@ -226,6 +226,7 @@ struct hib_bio_batch {

>         atomic_t                count;

>         wait_queue_head_t       wait;

>         blk_status_t            error;

> +       struct blk_plug         plug;

>  };

>

>  static void hib_init_batch(struct hib_bio_batch *hb)

> @@ -233,6 +234,12 @@ static void hib_init_batch(struct hib_bio_batch *hb)

>         atomic_set(&hb->count, 0);

>         init_waitqueue_head(&hb->wait);

>         hb->error = BLK_STS_OK;

> +       blk_start_plug(&hb->plug);

> +}

> +

> +static void hib_finish_batch(struct hib_bio_batch *hb)

> +{

> +       blk_finish_plug(&hb->plug);

>  }

>

>  static void hib_end_io(struct bio *bio)

> @@ -294,6 +301,10 @@ static int hib_submit_io(int op, int op_flags, pgoff_t page_off, void *addr,

>

>  static blk_status_t hib_wait_io(struct hib_bio_batch *hb)

>  {

> +       /*

> +        * We are relying on the behavior of blk_plug that a thread with

> +        * a plug will flush the plug list before sleeping.

> +        */

>         wait_event(hb->wait, atomic_read(&hb->count) == 0);

>         return blk_status_to_errno(hb->error);

>  }

> @@ -561,6 +572,7 @@ static int save_image(struct swap_map_handle *handle,

>                 nr_pages++;

>         }

>         err2 = hib_wait_io(&hb);

> +       hib_finish_batch(&hb);

>         stop = ktime_get();

>         if (!ret)

>                 ret = err2;

> @@ -854,6 +866,7 @@ static int save_image_lzo(struct swap_map_handle *handle,

>                 pr_info("Image saving done\n");

>         swsusp_show_speed(start, stop, nr_to_write, "Wrote");

>  out_clean:

> +       hib_finish_batch(&hb);

>         if (crc) {

>                 if (crc->thr)

>                         kthread_stop(crc->thr);

> @@ -1084,6 +1097,7 @@ static int load_image(struct swap_map_handle *handle,

>                 nr_pages++;

>         }

>         err2 = hib_wait_io(&hb);

> +       hib_finish_batch(&hb);

>         stop = ktime_get();

>         if (!ret)

>                 ret = err2;

> @@ -1447,6 +1461,7 @@ static int load_image_lzo(struct swap_map_handle *handle,

>         }

>         swsusp_show_speed(start, stop, nr_to_read, "Read");

>  out_clean:

> +       hib_finish_batch(&hb);

>         for (i = 0; i < ring_size; i++)

>                 free_page((unsigned long)page[i]);

>         if (crc) {

> --


Applied as 5.10 material with some subject and changelog edits, but:
1. The patch is white-space-damaged and I needed to fix that up
manually which was not fun.
2. I dropped the second S-o-b, because it was not clear to me whether
a Co-developed-by tag was missing or Reviewed-by should have been used
instead.

Thanks!
Chen, Xiaoyi Sept. 25, 2020, 7:26 p.m. UTC | #2
On 9/25/20 4:27 PM, Rafael J. Wysocki wrote:
> 

> 

> On Tue, Sep 22, 2020 at 6:19 PM Chen, Xiaoyi <cxiaoyi@amazon.com> wrote:

>>

>>

>> Hibernate and resume process submits individual IO requests for each page

>> of the data. With this patch, we use blk_plug to improve the batching of

>> these requests.

>>

>> Tested this patch with hibernate and resumes, and consistently observed the

>> merging of the IO requests. We see more than an order of magnitude

>> improvement in hibernate and resume speed.

>>

>> One hibernate and resume cycle for 16GB used RAM out of 32GB takes around

>> 21 minutes before the change, and 1 minutes after the change on systems

>> with limited storage IOPS.

>>

>> Signed-off-by: Xiaoyi Chen <cxiaoyi@amazon.com>

>> Signed-off-by: Anchal Agarwal <anchalag@amazon.com>

>> ---

>>   kernel/power/swap.c | 15 +++++++++++++++

>>   1 file changed, 15 insertions(+)

>>

>> diff --git a/kernel/power/swap.c b/kernel/power/swap.c

>> index 01e2858b5fe3..961615365b5c 100644

>> --- a/kernel/power/swap.c

>> +++ b/kernel/power/swap.c

>> @@ -226,6 +226,7 @@ struct hib_bio_batch {

>>          atomic_t                count;

>>          wait_queue_head_t       wait;

>>          blk_status_t            error;

>> +       struct blk_plug         plug;

>>   };

>>

>>   static void hib_init_batch(struct hib_bio_batch *hb)

>> @@ -233,6 +234,12 @@ static void hib_init_batch(struct hib_bio_batch *hb)

>>          atomic_set(&hb->count, 0);

>>          init_waitqueue_head(&hb->wait);

>>          hb->error = BLK_STS_OK;

>> +       blk_start_plug(&hb->plug);

>> +}

>> +

>> +static void hib_finish_batch(struct hib_bio_batch *hb)

>> +{

>> +       blk_finish_plug(&hb->plug);

>>   }

>>

>>   static void hib_end_io(struct bio *bio)

>> @@ -294,6 +301,10 @@ static int hib_submit_io(int op, int op_flags, pgoff_t page_off, void *addr,

>>

>>   static blk_status_t hib_wait_io(struct hib_bio_batch *hb)

>>   {

>> +       /*

>> +        * We are relying on the behavior of blk_plug that a thread with

>> +        * a plug will flush the plug list before sleeping.

>> +        */

>>          wait_event(hb->wait, atomic_read(&hb->count) == 0);

>>          return blk_status_to_errno(hb->error);

>>   }

>> @@ -561,6 +572,7 @@ static int save_image(struct swap_map_handle *handle,

>>                  nr_pages++;

>>          }

>>          err2 = hib_wait_io(&hb);

>> +       hib_finish_batch(&hb);

>>          stop = ktime_get();

>>          if (!ret)

>>                  ret = err2;

>> @@ -854,6 +866,7 @@ static int save_image_lzo(struct swap_map_handle *handle,

>>                  pr_info("Image saving done\n");

>>          swsusp_show_speed(start, stop, nr_to_write, "Wrote");

>>   out_clean:

>> +       hib_finish_batch(&hb);

>>          if (crc) {

>>                  if (crc->thr)

>>                          kthread_stop(crc->thr);

>> @@ -1084,6 +1097,7 @@ static int load_image(struct swap_map_handle *handle,

>>                  nr_pages++;

>>          }

>>          err2 = hib_wait_io(&hb);

>> +       hib_finish_batch(&hb);

>>          stop = ktime_get();

>>          if (!ret)

>>                  ret = err2;

>> @@ -1447,6 +1461,7 @@ static int load_image_lzo(struct swap_map_handle *handle,

>>          }

>>          swsusp_show_speed(start, stop, nr_to_read, "Read");

>>   out_clean:

>> +       hib_finish_batch(&hb);

>>          for (i = 0; i < ring_size; i++)

>>                  free_page((unsigned long)page[i]);

>>          if (crc) {

>> --

> 

> Applied as 5.10 material with some subject and changelog edits, but:

> 1. The patch is white-space-damaged and I needed to fix that up

> manually which was not fun.

> 2. I dropped the second S-o-b, because it was not clear to me whether

> a Co-developed-by tag was missing or Reviewed-by should have been used

> instead.

> 

> Thanks!

> 


Thanks for the prompt review. Apologies for the white-space and tags
issues, will do better next time.

Would you mind adding following tags if not too late?
Signed-off-by: Anchal Agarwal <anchalag@amazon.com>

Co-Developed-by: Anchal Agarwal <anchalag@amazon.com>
Rafael J. Wysocki Sept. 28, 2020, 1:59 p.m. UTC | #3
On Fri, Sep 25, 2020 at 9:26 PM Chen, Xiaoyi <cxiaoyi@amazon.com> wrote:
>
> On 9/25/20 4:27 PM, Rafael J. Wysocki wrote:
> >
> >
> > On Tue, Sep 22, 2020 at 6:19 PM Chen, Xiaoyi <cxiaoyi@amazon.com> wrote:
> >>
> >>
> >> Hibernate and resume process submits individual IO requests for each page
> >> of the data. With this patch, we use blk_plug to improve the batching of
> >> these requests.
> >>
> >> Tested this patch with hibernate and resumes, and consistently observed the
> >> merging of the IO requests. We see more than an order of magnitude
> >> improvement in hibernate and resume speed.
> >>
> >> One hibernate and resume cycle for 16GB used RAM out of 32GB takes around
> >> 21 minutes before the change, and 1 minutes after the change on systems
> >> with limited storage IOPS.
> >>
> >> Signed-off-by: Xiaoyi Chen <cxiaoyi@amazon.com>
> >> Signed-off-by: Anchal Agarwal <anchalag@amazon.com>
> >> ---
> >>   kernel/power/swap.c | 15 +++++++++++++++
> >>   1 file changed, 15 insertions(+)
> >>
> >> diff --git a/kernel/power/swap.c b/kernel/power/swap.c
> >> index 01e2858b5fe3..961615365b5c 100644
> >> --- a/kernel/power/swap.c
> >> +++ b/kernel/power/swap.c
> >> @@ -226,6 +226,7 @@ struct hib_bio_batch {
> >>          atomic_t                count;
> >>          wait_queue_head_t       wait;
> >>          blk_status_t            error;
> >> +       struct blk_plug         plug;
> >>   };
> >>
> >>   static void hib_init_batch(struct hib_bio_batch *hb)
> >> @@ -233,6 +234,12 @@ static void hib_init_batch(struct hib_bio_batch *hb)
> >>          atomic_set(&hb->count, 0);
> >>          init_waitqueue_head(&hb->wait);
> >>          hb->error = BLK_STS_OK;
> >> +       blk_start_plug(&hb->plug);
> >> +}
> >> +
> >> +static void hib_finish_batch(struct hib_bio_batch *hb)
> >> +{
> >> +       blk_finish_plug(&hb->plug);
> >>   }
> >>
> >>   static void hib_end_io(struct bio *bio)
> >> @@ -294,6 +301,10 @@ static int hib_submit_io(int op, int op_flags, pgoff_t page_off, void *addr,
> >>
> >>   static blk_status_t hib_wait_io(struct hib_bio_batch *hb)
> >>   {
> >> +       /*
> >> +        * We are relying on the behavior of blk_plug that a thread with
> >> +        * a plug will flush the plug list before sleeping.
> >> +        */
> >>          wait_event(hb->wait, atomic_read(&hb->count) == 0);
> >>          return blk_status_to_errno(hb->error);
> >>   }
> >> @@ -561,6 +572,7 @@ static int save_image(struct swap_map_handle *handle,
> >>                  nr_pages++;
> >>          }
> >>          err2 = hib_wait_io(&hb);
> >> +       hib_finish_batch(&hb);
> >>          stop = ktime_get();
> >>          if (!ret)
> >>                  ret = err2;
> >> @@ -854,6 +866,7 @@ static int save_image_lzo(struct swap_map_handle *handle,
> >>                  pr_info("Image saving done\n");
> >>          swsusp_show_speed(start, stop, nr_to_write, "Wrote");
> >>   out_clean:
> >> +       hib_finish_batch(&hb);
> >>          if (crc) {
> >>                  if (crc->thr)
> >>                          kthread_stop(crc->thr);
> >> @@ -1084,6 +1097,7 @@ static int load_image(struct swap_map_handle *handle,
> >>                  nr_pages++;
> >>          }
> >>          err2 = hib_wait_io(&hb);
> >> +       hib_finish_batch(&hb);
> >>          stop = ktime_get();
> >>          if (!ret)
> >>                  ret = err2;
> >> @@ -1447,6 +1461,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
> >>          }
> >>          swsusp_show_speed(start, stop, nr_to_read, "Read");
> >>   out_clean:
> >> +       hib_finish_batch(&hb);
> >>          for (i = 0; i < ring_size; i++)
> >>                  free_page((unsigned long)page[i]);
> >>          if (crc) {
> >> --
> >
> > Applied as 5.10 material with some subject and changelog edits, but:
> > 1. The patch is white-space-damaged and I needed to fix that up
> > manually which was not fun.
> > 2. I dropped the second S-o-b, because it was not clear to me whether
> > a Co-developed-by tag was missing or Reviewed-by should have been used
> > instead.
> >
> > Thanks!
> >
>
> Thanks for the prompt review. Apologies for the white-space and tags
> issues, will do better next time.
>
> Would you mind adding following tags if not too late?
> Signed-off-by: Anchal Agarwal <anchalag@amazon.com>
> Co-Developed-by: Anchal Agarwal <anchalag@amazon.com>

Done, thanks!
diff mbox series

Patch

diff --git a/kernel/power/swap.c b/kernel/power/swap.c
index 01e2858b5fe3..961615365b5c 100644
--- a/kernel/power/swap.c
+++ b/kernel/power/swap.c
@@ -226,6 +226,7 @@  struct hib_bio_batch {
        atomic_t                count;
        wait_queue_head_t       wait;
        blk_status_t            error;
+       struct blk_plug         plug;
 };

 static void hib_init_batch(struct hib_bio_batch *hb)
@@ -233,6 +234,12 @@  static void hib_init_batch(struct hib_bio_batch *hb)
        atomic_set(&hb->count, 0);
        init_waitqueue_head(&hb->wait);
        hb->error = BLK_STS_OK;
+       blk_start_plug(&hb->plug);
+}
+
+static void hib_finish_batch(struct hib_bio_batch *hb)
+{
+       blk_finish_plug(&hb->plug);
 }

 static void hib_end_io(struct bio *bio)
@@ -294,6 +301,10 @@  static int hib_submit_io(int op, int op_flags, pgoff_t page_off, void *addr,

 static blk_status_t hib_wait_io(struct hib_bio_batch *hb)
 {
+       /*
+        * We are relying on the behavior of blk_plug that a thread with
+        * a plug will flush the plug list before sleeping.
+        */
        wait_event(hb->wait, atomic_read(&hb->count) == 0);
        return blk_status_to_errno(hb->error);
 }
@@ -561,6 +572,7 @@  static int save_image(struct swap_map_handle *handle,
                nr_pages++;
        }
        err2 = hib_wait_io(&hb);
+       hib_finish_batch(&hb);
        stop = ktime_get();
        if (!ret)
                ret = err2;
@@ -854,6 +866,7 @@  static int save_image_lzo(struct swap_map_handle *handle,
                pr_info("Image saving done\n");
        swsusp_show_speed(start, stop, nr_to_write, "Wrote");
 out_clean:
+       hib_finish_batch(&hb);
        if (crc) {
                if (crc->thr)
                        kthread_stop(crc->thr);
@@ -1084,6 +1097,7 @@  static int load_image(struct swap_map_handle *handle,
                nr_pages++;
        }
        err2 = hib_wait_io(&hb);
+       hib_finish_batch(&hb);
        stop = ktime_get();
        if (!ret)
                ret = err2;
@@ -1447,6 +1461,7 @@  static int load_image_lzo(struct swap_map_handle *handle,
        }
        swsusp_show_speed(start, stop, nr_to_read, "Read");
 out_clean:
+       hib_finish_batch(&hb);
        for (i = 0; i < ring_size; i++)
                free_page((unsigned long)page[i]);
        if (crc) {