PM: hibernate: make compression threads configurable and support dynamic crc arrays
Subsystems (3)
v5 cover PM: hibernate: make compression threads configurable and support dynamic crc arrays
Hi,
This is v5 of the series to make hibernate compression/decompression threads configurable and improve scalability.
Following feedback from previous versions:
- Patch 2 (PM: hibernate: make compression threads configurable) has been updated to rename the GRUB/kernel command line parameter from cmp_threads to hibernate_compression_threads, making its purpose clearer and more self-explanatory.
- Patch 3 (PM: hibernate: add sysfs interface for hibernate_compression_threads) has been added to provide a runtime interface under /sys/power/hibernate_compression_threads. This allows users and system integrators to tune the number of compression/decompression threads dynamically without rebooting the system.
- Corresponding documentation updates have been added:
- Documentation/admin-guide/kernel-parameters.txt now documents hibernate_compression_threads= GRUB parameter.
- Documentation/ABI/testing/sysfs-power describes the new sysfs interface.
This series ensures that both boot-time and runtime configuration of hibernate compression threads are supported, improving flexibility and scalability on multi-core systems.
Xueqin Luo (3):
PM: hibernate: dynamically allocate crc->unc_len/unc for configurable
threads
PM: hibernate: make compression threads configurable
PM: hibernate: add sysfs interface for hibernate_compression_threads
Documentation/ABI/testing/sysfs-power | 16 +++ .../admin-guide/kernel-parameters.txt | 10 ++ kernel/power/swap.c | 121 +++++++++++++++--- 3 files changed, 129 insertions(+), 18 deletions(-)
Signature · 1 line
2.43.0
PATCH v5 1/3 PM: hibernate: dynamically allocate crc->unc_len/unc for configurable threads
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.
--- kernel/power/swap.c | 58 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 14 deletions(-)
kernel/power/swap.c +44 -14
@@ -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)
Signature · 1 line
2.43.0
PATCH v5 2/3 PM: hibernate: make compression threads configurable
In reply to Xueqin LuoThe 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:
Threads Hibernate(s) Resume(s)
--------------------------------
3 12.14 18.86
4 12.28 17.48
5 11.09 16.77
6 11.08 16.44With 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 hibernate_compression_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.
--- .../admin-guide/kernel-parameters.txt | 10 ++++++++ kernel/power/swap.c | 25 ++++++++++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-)
Documentation/admin-guide/kernel-parameters.txt +10 -0
@@ -1889,6 +1889,16 @@ /sys/power/pm_test). Only available when CONFIG_PM_DEBUG is set. Default value is 5. + hibernate_compression_threads= + [HIBERNATION] + Set the number of threads used for compressing or decompressing + hibernation images. + + Format: <integer> + Default: 3 + Minimum: 1 + Example: hibernate_compression_threads=4 + highmem=nn[KMG] [KNL,BOOT,EARLY] forces the highmem zone to have an exact size of <nn>. This works even on boxes that have no highmem otherwise. This also works to reduce highmem
kernel/power/swap.c +21 -4
@@ -519,8 +519,9 @@ 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. */ +#define CMP_THREADS 3 +static unsigned int hibernate_compression_threads =CMP_THREADS ;/* Minimum/maximum number of pages for read buffering. */ #define CMP_MIN_RD_PAGES 1024
@@ -741,7 +742,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 , hibernate_compression_threads); page = (void *)__get_free_page (GFP_NOIO | __GFP_HIGH);if (!page) {
@@ -1257,7 +1258,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 , hibernate_compression_threads); page =vmalloc_array (CMP_MAX_RD_PAGES ,sizeof (*page));if (!page) {
@@ -1697,3 +1698,19 @@ static int __init swsusp_header_init(void) }core_initcall (swsusp_header_init); + +static int __init hibernate_compression_threads_setup (char *str) +{ +int rc =kstrtouint (str,0 , &hibernate_compression_threads); + +if (rc) +return rc; + +if (hibernate_compression_threads <1 ) + hibernate_compression_threads =CMP_THREADS ; + +return 1 ; + +} + +__setup ("hibernate_compression_threads=" , hibernate_compression_threads_setup);
Signature · 1 line
2.43.0
PATCH v5 3/3 PM: hibernate: add sysfs interface for hibernate_compression_threads
In reply to Xueqin LuoAdd a sysfs attribute /sys/power/hibernate_compression_threads to allow runtime configuration of the number of threads used for compressing and decompressing hibernation images.
The new sysfs interface enables dynamic adjustment at runtime:
# cat /sys/power/hibernate_compression_threads
3
# echo 4 > /sys/power/hibernate_compression_threadsThis change provides greater flexibility for debugging and performance tuning of hibernation without requiring a reboot.
--- Documentation/ABI/testing/sysfs-power | 16 +++++++++++ kernel/power/swap.c | 38 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+)
Documentation/ABI/testing/sysfs-power +16 -0
@@ -454,3 +454,19 @@ Description: disables it. Reads from the file return the current value. The default is "1" if the build-time "SUSPEND_SKIP_SYNC" config flag is unset, or "0" otherwise. + +What: /sys/power/hibernate_compression_threads +Date: October 2025 +Contact: <[email protected]> +Description: + Controls the number of threads used for compression + and decompression of hibernation images. + + The value can be adjusted at runtime to balance + performance and CPU utilization. + + The change takes effect on the next hibernation or + resume operation. + + Minimum value: 1 + Default value: 3
kernel/power/swap.c +38 -0
@@ -1689,8 +1689,46 @@ int swsusp_unmark(void) }#endif +static ssize_t hibernate_compression_threads_show (struct kobject *kobj, +struct kobj_attribute *attr,char *buf) +{ +return sysfs_emit (buf,"%d\n" , hibernate_compression_threads); +} + +static ssize_t hibernate_compression_threads_store (struct kobject *kobj, +struct kobj_attribute *attr, +const char *buf,size_t n) +{ +unsigned long val; + +if (kstrtoul (buf,0 , &val)) +return -EINVAL ; + +if (val <1 ) +return -EINVAL ; + + hibernate_compression_threads = val; +return n; +} +power_attr (hibernate_compression_threads); + +static struct attribute *g[] = { + &hibernate_compression_threads_attr.attr, +NULL , +}; + +static const struct attribute_group attr_group = { + .attrs = g, +}; +static int __init swsusp_header_init (void ) { +int error; + + error =sysfs_create_group (power_kobj, &attr_group); +if (error) +return -ENOMEM ; + swsusp_header = (struct swsusp_header *)__get_free_page (GFP_KERNEL );if (!swsusp_header)panic ("Could not allocate memory for swsusp_header\n" );
Signature · 1 line
2.43.0
Rafael J. Wysocki
maintainerQuoted 28 lines · Xueqin Luo
Hi,
This is v5 of the series to make hibernate compression/decompression threads configurable and improve scalability.
Following feedback from previous versions:
- Patch 2 (
PM: hibernate: make compression threads configurable) has been updated to rename the GRUB/kernel command line parameter fromcmp_threadstohibernate_compression_threads, making its purpose clearer and more self-explanatory.- Patch 3 (
PM: hibernate: add sysfs interface for hibernate_compression_threads) has been added to provide a runtime interface under/sys/power/hibernate_compression_threads. This allows users and system integrators to tune the number of compression/decompression threads dynamically without rebooting the system.- Corresponding documentation updates have been added:
-Documentation/admin-guide/kernel-parameters.txtnow documentshibernate_compression_threads=GRUB parameter.
-Documentation/ABI/testing/sysfs-powerdescribes the new sysfs interface.This series ensures that both boot-time and runtime configuration of hibernate compression threads are supported, improving flexibility and scalability on multi-core systems.
Quoted 5 lines
Xueqin Luo (3):
PM: hibernate: dynamically allocate crc->unc_len/unc for configurable
threads
PM: hibernate: make compression threads configurable
PM: hibernate: add sysfs interface forhibernate_compression_threads
All applied as 6.19 material, thanks!