* [PATCH v3 0/2] PM: hibernate: make compression threads configurable and support dynamic crc arrays
@ 2025-09-15 2:21 Xueqin Luo
2025-09-15 2:21 ` [PATCH v3 1/2] PM: hibernate: dynamically allocate crc->unc_len/unc for configurable threads Xueqin Luo
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Xueqin Luo @ 2025-09-15 2:21 UTC (permalink / raw)
To: rafael, pavel, lenb, linux-pm, linux-kernel; +Cc: Xueqin Luo
Hi,
This is v2 of the series to make hibernate compression/decompression
threads configurable and improve scalability.
Changes since v2:
- Reworded commit message to avoid "this patch".
- Introduced helper functions alloc_crc_data() and free_crc_data() to
avoid code duplication for allocation/freeing in both save and load
paths(patch1).
Xueqin Luo (2):
PM: hibernate: dynamically allocate crc->unc_len/unc for configurable
threads
PM: hibernate: make compression threads configurable
kernel/power/swap.c | 82 +++++++++++++++++++++++++++++++++++----------
1 file changed, 64 insertions(+), 18 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v3 1/2] PM: hibernate: dynamically allocate crc->unc_len/unc for configurable threads 2025-09-15 2:21 [PATCH v3 0/2] PM: hibernate: make compression threads configurable and support dynamic crc arrays Xueqin Luo @ 2025-09-15 2:21 ` Xueqin Luo 2025-10-11 9:38 ` [PATCH RESEND " Xueqin Luo 2025-09-15 2:21 ` [PATCH v3 2/2] PM: hibernate: make compression threads configurable Xueqin Luo 2025-10-11 9:38 ` [PATCH RESEND v3 0/2] PM: hibernate: make compression threads configurable and support dynamic crc arrays Xueqin Luo 2 siblings, 1 reply; 7+ messages in thread From: Xueqin Luo @ 2025-09-15 2:21 UTC (permalink / raw) To: rafael, pavel, lenb, linux-pm, linux-kernel; +Cc: Xueqin Luo Convert crc->unc_len and crc->unc from fixed-size arrays to dynamically allocated arrays, sized according to the actual number of threads selected at runtime. This removes the fixed limit imposed by CMP_THREADS. Signed-off-by: Xueqin Luo <luoxueqin@kylinos.cn> --- kernel/power/swap.c | 58 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 0beff7eeaaba..f8c13f5672ec 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -585,10 +585,48 @@ struct crc_data { wait_queue_head_t go; /* start crc update */ wait_queue_head_t done; /* crc update done */ u32 *crc32; /* points to handle's crc32 */ - size_t *unc_len[CMP_THREADS]; /* uncompressed lengths */ - unsigned char *unc[CMP_THREADS]; /* uncompressed data */ + size_t **unc_len; /* uncompressed lengths */ + unsigned char **unc; /* uncompressed data */ }; +static struct crc_data *alloc_crc_data(int nr_threads) +{ + struct crc_data *crc; + + crc = kzalloc(sizeof(*crc), GFP_KERNEL); + if (!crc) + return NULL; + + crc->unc = kcalloc(nr_threads, sizeof(*crc->unc), GFP_KERNEL); + if (!crc->unc) + goto err_free_crc; + + crc->unc_len = kcalloc(nr_threads, sizeof(*crc->unc_len), GFP_KERNEL); + if (!crc->unc_len) + goto err_free_unc; + + return crc; + +err_free_unc: + kfree(crc->unc); +err_free_crc: + kfree(crc); + return NULL; +} + +static void free_crc_data(struct crc_data *crc) +{ + if (!crc) + return; + + if (crc->thr) + kthread_stop(crc->thr); + + kfree(crc->unc_len); + kfree(crc->unc); + kfree(crc); +} + /* * CRC32 update function that runs in its own thread. */ @@ -719,7 +757,7 @@ static int save_compressed_image(struct swap_map_handle *handle, goto out_clean; } - crc = kzalloc(sizeof(*crc), GFP_KERNEL); + crc = alloc_crc_data(nr_threads); if (!crc) { pr_err("Failed to allocate crc\n"); ret = -ENOMEM; @@ -885,11 +923,7 @@ static int save_compressed_image(struct swap_map_handle *handle, out_clean: hib_finish_batch(&hb); - if (crc) { - if (crc->thr) - kthread_stop(crc->thr); - kfree(crc); - } + free_crc_data(crc); if (data) { for (thr = 0; thr < nr_threads; thr++) { if (data[thr].thr) @@ -1239,7 +1273,7 @@ static int load_compressed_image(struct swap_map_handle *handle, goto out_clean; } - crc = kzalloc(sizeof(*crc), GFP_KERNEL); + crc = alloc_crc_data(nr_threads); if (!crc) { pr_err("Failed to allocate crc\n"); ret = -ENOMEM; @@ -1506,11 +1540,7 @@ static int load_compressed_image(struct swap_map_handle *handle, hib_finish_batch(&hb); for (i = 0; i < ring_size; i++) free_page((unsigned long)page[i]); - if (crc) { - if (crc->thr) - kthread_stop(crc->thr); - kfree(crc); - } + free_crc_data(crc); if (data) { for (thr = 0; thr < nr_threads; thr++) { if (data[thr].thr) -- 2.43.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH RESEND v3 1/2] PM: hibernate: dynamically allocate crc->unc_len/unc for configurable threads 2025-09-15 2:21 ` [PATCH v3 1/2] PM: hibernate: dynamically allocate crc->unc_len/unc for configurable threads Xueqin Luo @ 2025-10-11 9:38 ` Xueqin Luo 0 siblings, 0 replies; 7+ messages in thread From: Xueqin Luo @ 2025-10-11 9:38 UTC (permalink / raw) To: rafael, pavel, lenb, linux-pm, linux-kernel; +Cc: Xueqin Luo Convert crc->unc_len and crc->unc from fixed-size arrays to dynamically allocated arrays, sized according to the actual number of threads selected at runtime. This removes the fixed limit imposed by CMP_THREADS. Signed-off-by: Xueqin Luo <luoxueqin@kylinos.cn> --- kernel/power/swap.c | 58 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 0beff7eeaaba..f8c13f5672ec 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -585,10 +585,48 @@ struct crc_data { wait_queue_head_t go; /* start crc update */ wait_queue_head_t done; /* crc update done */ u32 *crc32; /* points to handle's crc32 */ - size_t *unc_len[CMP_THREADS]; /* uncompressed lengths */ - unsigned char *unc[CMP_THREADS]; /* uncompressed data */ + size_t **unc_len; /* uncompressed lengths */ + unsigned char **unc; /* uncompressed data */ }; +static struct crc_data *alloc_crc_data(int nr_threads) +{ + struct crc_data *crc; + + crc = kzalloc(sizeof(*crc), GFP_KERNEL); + if (!crc) + return NULL; + + crc->unc = kcalloc(nr_threads, sizeof(*crc->unc), GFP_KERNEL); + if (!crc->unc) + goto err_free_crc; + + crc->unc_len = kcalloc(nr_threads, sizeof(*crc->unc_len), GFP_KERNEL); + if (!crc->unc_len) + goto err_free_unc; + + return crc; + +err_free_unc: + kfree(crc->unc); +err_free_crc: + kfree(crc); + return NULL; +} + +static void free_crc_data(struct crc_data *crc) +{ + if (!crc) + return; + + if (crc->thr) + kthread_stop(crc->thr); + + kfree(crc->unc_len); + kfree(crc->unc); + kfree(crc); +} + /* * CRC32 update function that runs in its own thread. */ @@ -719,7 +757,7 @@ static int save_compressed_image(struct swap_map_handle *handle, goto out_clean; } - crc = kzalloc(sizeof(*crc), GFP_KERNEL); + crc = alloc_crc_data(nr_threads); if (!crc) { pr_err("Failed to allocate crc\n"); ret = -ENOMEM; @@ -885,11 +923,7 @@ static int save_compressed_image(struct swap_map_handle *handle, out_clean: hib_finish_batch(&hb); - if (crc) { - if (crc->thr) - kthread_stop(crc->thr); - kfree(crc); - } + free_crc_data(crc); if (data) { for (thr = 0; thr < nr_threads; thr++) { if (data[thr].thr) @@ -1239,7 +1273,7 @@ static int load_compressed_image(struct swap_map_handle *handle, goto out_clean; } - crc = kzalloc(sizeof(*crc), GFP_KERNEL); + crc = alloc_crc_data(nr_threads); if (!crc) { pr_err("Failed to allocate crc\n"); ret = -ENOMEM; @@ -1506,11 +1540,7 @@ static int load_compressed_image(struct swap_map_handle *handle, hib_finish_batch(&hb); for (i = 0; i < ring_size; i++) free_page((unsigned long)page[i]); - if (crc) { - if (crc->thr) - kthread_stop(crc->thr); - kfree(crc); - } + free_crc_data(crc); if (data) { for (thr = 0; thr < nr_threads; thr++) { if (data[thr].thr) -- 2.43.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v3 2/2] PM: hibernate: make compression threads configurable 2025-09-15 2:21 [PATCH v3 0/2] PM: hibernate: make compression threads configurable and support dynamic crc arrays Xueqin Luo 2025-09-15 2:21 ` [PATCH v3 1/2] PM: hibernate: dynamically allocate crc->unc_len/unc for configurable threads Xueqin Luo @ 2025-09-15 2:21 ` Xueqin Luo 2025-10-11 9:38 ` [PATCH RESEND " Xueqin Luo 2025-10-13 18:35 ` [PATCH " Rafael J. Wysocki 2025-10-11 9:38 ` [PATCH RESEND v3 0/2] PM: hibernate: make compression threads configurable and support dynamic crc arrays Xueqin Luo 2 siblings, 2 replies; 7+ messages in thread From: Xueqin Luo @ 2025-09-15 2:21 UTC (permalink / raw) To: rafael, pavel, lenb, linux-pm, linux-kernel; +Cc: Xueqin Luo The number of compression/decompression threads has a direct impact on hibernate image generation and resume latency. Using more threads can reduce overall resume time, but on systems with fewer CPU cores it may also introduce contention and reduce efficiency. Performance was evaluated on an 8-core ARM system, averaged over 10 runs: cmp_threads hibernate time (s) resume time (s) -------------------------------------------------- 3 12.14 18.86 4 12.28 17.48 5 11.09 16.77 6 11.08 16.44 With 5–6 threads, resume latency improves by approximately 12% compared to the default 3-thread configuration, with negligible impact on hibernate time. Introduce a new kernel parameter `cmp_threads=` that allows users and integrators to tune the number of compression/decompression threads at boot. This provides a way to balance performance and CPU utilization across a wide range of hardware without recompiling the kernel. Signed-off-by: Xueqin Luo <luoxueqin@kylinos.cn> --- kernel/power/swap.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/kernel/power/swap.c b/kernel/power/swap.c index f8c13f5672ec..dfa9b7c0f96c 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -519,8 +519,8 @@ static int swap_writer_finish(struct swap_map_handle *handle, CMP_HEADER, PAGE_SIZE) #define CMP_SIZE (CMP_PAGES * PAGE_SIZE) -/* Maximum number of threads for compression/decompression. */ -#define CMP_THREADS 3 +/* Default number of threads for compression/decompression. */ +static int cmp_threads = 3; /* Minimum/maximum number of pages for read buffering. */ #define CMP_MIN_RD_PAGES 1024 @@ -741,7 +741,7 @@ static int save_compressed_image(struct swap_map_handle *handle, * footprint. */ nr_threads = num_online_cpus() - 1; - nr_threads = clamp_val(nr_threads, 1, CMP_THREADS); + nr_threads = clamp_val(nr_threads, 1, cmp_threads); page = (void *)__get_free_page(GFP_NOIO | __GFP_HIGH); if (!page) { @@ -1257,7 +1257,7 @@ static int load_compressed_image(struct swap_map_handle *handle, * footprint. */ nr_threads = num_online_cpus() - 1; - nr_threads = clamp_val(nr_threads, 1, CMP_THREADS); + nr_threads = clamp_val(nr_threads, 1, cmp_threads); page = vmalloc_array(CMP_MAX_RD_PAGES, sizeof(*page)); if (!page) { @@ -1697,3 +1697,19 @@ static int __init swsusp_header_init(void) } core_initcall(swsusp_header_init); + +static int __init cmp_threads_setup(char *str) +{ + int rc = kstrtouint(str, 0, &cmp_threads); + + if (rc) + return rc; + + if (cmp_threads < 1) + cmp_threads = 1; + + return 1; + +} + +__setup("cmp_threads=", cmp_threads_setup); -- 2.43.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH RESEND v3 2/2] PM: hibernate: make compression threads configurable 2025-09-15 2:21 ` [PATCH v3 2/2] PM: hibernate: make compression threads configurable Xueqin Luo @ 2025-10-11 9:38 ` Xueqin Luo 2025-10-13 18:35 ` [PATCH " Rafael J. Wysocki 1 sibling, 0 replies; 7+ messages in thread From: Xueqin Luo @ 2025-10-11 9:38 UTC (permalink / raw) To: rafael, pavel, lenb, linux-pm, linux-kernel; +Cc: Xueqin Luo The number of compression/decompression threads has a direct impact on hibernate image generation and resume latency. Using more threads can reduce overall resume time, but on systems with fewer CPU cores it may also introduce contention and reduce efficiency. Performance was evaluated on an 8-core ARM system, averaged over 10 runs: cmp_threads hibernate time (s) resume time (s) -------------------------------------------------- 3 12.14 18.86 4 12.28 17.48 5 11.09 16.77 6 11.08 16.44 With 5–6 threads, resume latency improves by approximately 12% compared to the default 3-thread configuration, with negligible impact on hibernate time. Introduce a new kernel parameter `cmp_threads=` that allows users and integrators to tune the number of compression/decompression threads at boot. This provides a way to balance performance and CPU utilization across a wide range of hardware without recompiling the kernel. Signed-off-by: Xueqin Luo <luoxueqin@kylinos.cn> --- kernel/power/swap.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/kernel/power/swap.c b/kernel/power/swap.c index f8c13f5672ec..dfa9b7c0f96c 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -519,8 +519,8 @@ static int swap_writer_finish(struct swap_map_handle *handle, CMP_HEADER, PAGE_SIZE) #define CMP_SIZE (CMP_PAGES * PAGE_SIZE) -/* Maximum number of threads for compression/decompression. */ -#define CMP_THREADS 3 +/* Default number of threads for compression/decompression. */ +static int cmp_threads = 3; /* Minimum/maximum number of pages for read buffering. */ #define CMP_MIN_RD_PAGES 1024 @@ -741,7 +741,7 @@ static int save_compressed_image(struct swap_map_handle *handle, * footprint. */ nr_threads = num_online_cpus() - 1; - nr_threads = clamp_val(nr_threads, 1, CMP_THREADS); + nr_threads = clamp_val(nr_threads, 1, cmp_threads); page = (void *)__get_free_page(GFP_NOIO | __GFP_HIGH); if (!page) { @@ -1257,7 +1257,7 @@ static int load_compressed_image(struct swap_map_handle *handle, * footprint. */ nr_threads = num_online_cpus() - 1; - nr_threads = clamp_val(nr_threads, 1, CMP_THREADS); + nr_threads = clamp_val(nr_threads, 1, cmp_threads); page = vmalloc_array(CMP_MAX_RD_PAGES, sizeof(*page)); if (!page) { @@ -1697,3 +1697,19 @@ static int __init swsusp_header_init(void) } core_initcall(swsusp_header_init); + +static int __init cmp_threads_setup(char *str) +{ + int rc = kstrtouint(str, 0, &cmp_threads); + + if (rc) + return rc; + + if (cmp_threads < 1) + cmp_threads = 1; + + return 1; + +} + +__setup("cmp_threads=", cmp_threads_setup); -- 2.43.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v3 2/2] PM: hibernate: make compression threads configurable 2025-09-15 2:21 ` [PATCH v3 2/2] PM: hibernate: make compression threads configurable Xueqin Luo 2025-10-11 9:38 ` [PATCH RESEND " Xueqin Luo @ 2025-10-13 18:35 ` Rafael J. Wysocki 1 sibling, 0 replies; 7+ messages in thread From: Rafael J. Wysocki @ 2025-10-13 18:35 UTC (permalink / raw) To: Xueqin Luo; +Cc: rafael, pavel, lenb, linux-pm, linux-kernel On Mon, Sep 15, 2025 at 4:23 AM Xueqin Luo <luoxueqin@kylinos.cn> wrote: > > The number of compression/decompression threads has a direct impact on > hibernate image generation and resume latency. Using more threads can > reduce overall resume time, but on systems with fewer CPU cores it may > also introduce contention and reduce efficiency. > > Performance was evaluated on an 8-core ARM system, averaged over 10 runs: > > cmp_threads hibernate time (s) resume time (s) > -------------------------------------------------- > 3 12.14 18.86 > 4 12.28 17.48 > 5 11.09 16.77 > 6 11.08 16.44 > > With 5–6 threads, resume latency improves by approximately 12% compared > to the default 3-thread configuration, with negligible impact on > hibernate time. > > Introduce a new kernel parameter `cmp_threads=` that allows users and > integrators to tune the number of compression/decompression threads at > boot. This provides a way to balance performance and CPU utilization > across a wide range of hardware without recompiling the kernel. > > Signed-off-by: Xueqin Luo <luoxueqin@kylinos.cn> > --- > kernel/power/swap.c | 24 ++++++++++++++++++++---- > 1 file changed, 20 insertions(+), 4 deletions(-) > > diff --git a/kernel/power/swap.c b/kernel/power/swap.c > index f8c13f5672ec..dfa9b7c0f96c 100644 > --- a/kernel/power/swap.c > +++ b/kernel/power/swap.c > @@ -519,8 +519,8 @@ static int swap_writer_finish(struct swap_map_handle *handle, > CMP_HEADER, PAGE_SIZE) > #define CMP_SIZE (CMP_PAGES * PAGE_SIZE) > > -/* Maximum number of threads for compression/decompression. */ > -#define CMP_THREADS 3 > +/* Default number of threads for compression/decompression. */ > +static int cmp_threads = 3; > > /* Minimum/maximum number of pages for read buffering. */ > #define CMP_MIN_RD_PAGES 1024 > @@ -741,7 +741,7 @@ static int save_compressed_image(struct swap_map_handle *handle, > * footprint. > */ > nr_threads = num_online_cpus() - 1; > - nr_threads = clamp_val(nr_threads, 1, CMP_THREADS); > + nr_threads = clamp_val(nr_threads, 1, cmp_threads); > > page = (void *)__get_free_page(GFP_NOIO | __GFP_HIGH); > if (!page) { > @@ -1257,7 +1257,7 @@ static int load_compressed_image(struct swap_map_handle *handle, > * footprint. > */ > nr_threads = num_online_cpus() - 1; > - nr_threads = clamp_val(nr_threads, 1, CMP_THREADS); > + nr_threads = clamp_val(nr_threads, 1, cmp_threads); > > page = vmalloc_array(CMP_MAX_RD_PAGES, sizeof(*page)); > if (!page) { > @@ -1697,3 +1697,19 @@ static int __init swsusp_header_init(void) > } > > core_initcall(swsusp_header_init); > + > +static int __init cmp_threads_setup(char *str) > +{ > + int rc = kstrtouint(str, 0, &cmp_threads); > + > + if (rc) > + return rc; > + > + if (cmp_threads < 1) > + cmp_threads = 1; Why not use the default (3) here or return an error? > + > + return 1; > + > +} > + > +__setup("cmp_threads=", cmp_threads_setup); > -- ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH RESEND v3 0/2] PM: hibernate: make compression threads configurable and support dynamic crc arrays 2025-09-15 2:21 [PATCH v3 0/2] PM: hibernate: make compression threads configurable and support dynamic crc arrays Xueqin Luo 2025-09-15 2:21 ` [PATCH v3 1/2] PM: hibernate: dynamically allocate crc->unc_len/unc for configurable threads Xueqin Luo 2025-09-15 2:21 ` [PATCH v3 2/2] PM: hibernate: make compression threads configurable Xueqin Luo @ 2025-10-11 9:38 ` Xueqin Luo 2 siblings, 0 replies; 7+ messages in thread From: Xueqin Luo @ 2025-10-11 9:38 UTC (permalink / raw) To: rafael, pavel, lenb, linux-pm, linux-kernel; +Cc: Xueqin Luo Hi, This is v2 of the series to make hibernate compression/decompression threads configurable and improve scalability. Changes since v2: - Reworded commit message to avoid "this patch". - Introduced helper functions alloc_crc_data() and free_crc_data() to avoid code duplication for allocation/freeing in both save and load paths(patch1). Xueqin Luo (2): PM: hibernate: dynamically allocate crc->unc_len/unc for configurable threads PM: hibernate: make compression threads configurable kernel/power/swap.c | 82 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 18 deletions(-) -- 2.43.0 ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-10-13 18:35 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-09-15 2:21 [PATCH v3 0/2] PM: hibernate: make compression threads configurable and support dynamic crc arrays Xueqin Luo 2025-09-15 2:21 ` [PATCH v3 1/2] PM: hibernate: dynamically allocate crc->unc_len/unc for configurable threads Xueqin Luo 2025-10-11 9:38 ` [PATCH RESEND " Xueqin Luo 2025-09-15 2:21 ` [PATCH v3 2/2] PM: hibernate: make compression threads configurable Xueqin Luo 2025-10-11 9:38 ` [PATCH RESEND " Xueqin Luo 2025-10-13 18:35 ` [PATCH " Rafael J. Wysocki 2025-10-11 9:38 ` [PATCH RESEND v3 0/2] PM: hibernate: make compression threads configurable and support dynamic crc arrays Xueqin Luo
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).