Skip to contentSkip to conversation

PM: hibernate: make compression threads configurable and support dynamic crc arrays

PATCH In review Xueqin Luo 2 participants 5 messages Oct 21, 2025 – Oct 30, 2025

Subsystems (3)
lore
Share & tools
lore

Xueqin Luo

author
#0 lore raw

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
author
#1 lore raw

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
index 0beff7eeaaba..f8c13f5672ec 100644
@@ -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
author
#2 lore raw

PATCH v5 2/3 PM: hibernate: make compression threads configurable

In reply to 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:

    Threads  Hibernate(s)  Resume(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 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
index db84a629f7b1..fb577fb2c893 100644
@@ -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
index f8c13f5672ec..aa11576e92a9 100644
@@ -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
author
#3 lore raw

PATCH v5 3/3 PM: hibernate: add sysfs interface for hibernate_compression_threads

In reply to Xueqin Luo

Add 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_threads

This 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
index 4d8e1ad020f0..d38da077905a 100644
@@ -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
index aa11576e92a9..d173e276b494 100644
@@ -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
#4 lore raw
In reply to Xueqin Luo
Quoted 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 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.

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 for hibernate_compression_threads

All applied as 6.19 material, thanks!