Skip to contentSkip to conversation

Hibernate: Handle ACPI NVS memory as required by the spec

PATCH In review Rafael J. Wysocki 4 participants 46 messages Oct 22, 2008 – Nov 18, 2008

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

cover Hibernate: Handle ACPI NVS memory as required by the spec

Hi,

I have recently tried to rebase the Zhang Rui's patches making the Linux hibernation code handle the ACPI NVS memory as required by the spec, but I found that they didn't work on x86_64 boxes where the NVS region was located behind the last usable PFN. Also, I decided to put the memory management part into the hibernation core rather than into drivers/acpi/sleep/main.c .

As a result, I have obtained the following patch series. Please review and if it looks good, please queue in the 'suspend' branch as .29 material.

Thanks,
Rafael

#1 lore raw

PATCH 1/4 Hibernate: Call platform_begin before swsusp_shrink_memory

From: Zhang Rui <[email protected]>

Hibernate: Call platform_begin before swsusp_shrink_memory

Call platform_begin() before swsusp_shrink_memory() so that we can always allocate enough memory to save the ACPI NVS region from platform_begin().

---
 kernel/power/disk.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
kernel/power/disk.c +3 -3
Index: linux-2.6/kernel/power/disk.c
===================================================================
@@ -267,12 +267,12 @@ int hibernation_snapshot(int platform_mo
 {
 	int error, ftrace_save;
 
-	/* Free memory before shutting down devices. */
-	error = swsusp_shrink_memory();
+	error = platform_begin(platform_mode);
 	if (error)
 		return error;
 
-	error = platform_begin(platform_mode);
+	/* Free memory before shutting down devices. */
+	error = swsusp_shrink_memory();
 	if (error)
 		goto Close;
 
#2 lore raw

PATCH 1/4 Hibernate: Call platform_begin before swsusp_shrink_memory

In reply to Rafael J. Wysocki

From: Zhang Rui <[email protected]>

Hibernate: Call platform_begin before swsusp_shrink_memory

Call platform_begin() before swsusp_shrink_memory() so that we can always allocate enough memory to save the ACPI NVS region from platform_begin().

---
 kernel/power/disk.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
kernel/power/disk.c +3 -3
Index: linux-2.6/kernel/power/disk.c
===================================================================
@@ -267,12 +267,12 @@ int hibernation_snapshot(int platform_mo
 {
 	int error, ftrace_save;
 
-	/* Free memory before shutting down devices. */
-	error = swsusp_shrink_memory();
+	error = platform_begin(platform_mode);
 	if (error)
 		return error;
 
-	error = platform_begin(platform_mode);
+	/* Free memory before shutting down devices. */
+	error = swsusp_shrink_memory();
 	if (error)
 		goto Close;
 
Acked-by on v1 1/4
#3 lore raw

Re: [PATCH 1/4] Hibernate: Call platform_begin before swsusp_shrink_memory

Hi.

Quoted 24 lines · Rafael J. Wysocki

From: Zhang Rui <[email protected]>

Hibernate: Call platform_begin before swsusp_shrink_memory

Call platform_begin() before swsusp_shrink_memory() so that we can always allocate enough memory to save the ACPI NVS region from platform_begin().

---
 kernel/power/disk.c |    6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

Index: linux-2.6/kernel/power/disk.c
===================================================================

kernel/power/disk.c +0 -2
@@ -267,12 +267,12 @@ int hibernation_snapshot(int platform_mo
 {
 	int error, ftrace_save;
 
-	/* Free memory before shutting down devices. */
-	error = swsusp_shrink_memory();

kernel/power/disk.c int hibernation_snapshot(int platform_mo · line 278

Quoted 10 lines
kernel/power/disk.c +3 -1
+	error = platform_begin(platform_mode);
 	if (error)
 		return error;
 
-	error = platform_begin(platform_mode);
+	/* Free memory before shutting down devices. */
+	error = swsusp_shrink_memory();
 	if (error)
 		goto Close;
 
#4 lore raw

Re: [PATCH 1/4] Hibernate: Call platform_begin before swsusp_shrink_memory

In reply to Rafael J. Wysocki
Quoted 10 lines · Rafael J. Wysocki

From: Zhang Rui <[email protected]>

Hibernate: Call platform_begin before swsusp_shrink_memory

Call platform_begin() before swsusp_shrink_memory() so that we can always allocate enough memory to save the ACPI NVS region from platform_begin().

ACK.

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#5 lore raw

Re: [linux-pm] [PATCH 1/4] Hibernate: Call platform_begin before swsusp_shrink_memory

In reply to Rafael J. Wysocki
Quoted 10 lines · Rafael J. Wysocki

From: Zhang Rui <[email protected]>

Hibernate: Call platform_begin before swsusp_shrink_memory

Call platform_begin() before swsusp_shrink_memory() so that we can always allocate enough memory to save the ACPI NVS region from platform_begin().

ACK.

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#6 lore raw

PATCH 2/4 ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

According to the ACPI Specification 3.0b, Section 15.3.2, "OSPM will call the _PTS control method some time before entering a sleeping state, to allow the platform’s AML code to update this memory image before entering the sleeping state. After the system awakes from an S4 state, OSPM will restore this memory area and call the _WAK control method to enable the BIOS to reclaim its memory image." For this reason, implement a mechanism allowing us to save the NVS memory during hibernation and to restore it during the subsequent resume.

Based on a patch by Zhang Rui.

---
 drivers/acpi/sleep/main.c |   50 ++++++++++++++++---
 include/linux/suspend.h   |   13 ++++
 kernel/power/swsusp.c     |  121 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 177 insertions(+), 7 deletions(-)
drivers/acpi/sleep/main.c +43 -7
Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================
@@ -321,8 +321,23 @@ void __init acpi_no_s4_hw_signature(void
 
 static int acpi_hibernation_begin(void)
 {
-	acpi_target_sleep_state = ACPI_STATE_S4;
-	return 0;
+	int error;
+
+	error = hibernate_nvs_alloc();
+	if (!error)
+		acpi_target_sleep_state = ACPI_STATE_S4;
+
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot(void)
+{
+	int error = acpi_pm_prepare();
+
+	if (!error)
+		hibernate_nvs_save();
+
+	return error;
 }
 
 static int acpi_hibernation_enter(void)
@@ -343,6 +358,12 @@ static int acpi_hibernation_enter(void)
 	return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 }
 
+static void acpi_hibernation_finish(void)
+{
+	hibernate_nvs_free();
+	acpi_pm_finish();
+}
+
 static void acpi_hibernation_leave(void)
 {
 	/*
@@ -358,6 +379,8 @@ static void acpi_hibernation_leave(void)
 			"cannot resume!\n");
 		panic("ACPI S4 hardware signature mismatch");
 	}
+	/* Restore the NVS memory area */
+	hibernate_nvs_restore();
 }
 
 static void acpi_pm_enable_gpes(void)
@@ -368,8 +391,8 @@ static void acpi_pm_enable_gpes(void)
 static struct platform_hibernation_ops acpi_hibernation_ops = {
 	.begin = acpi_hibernation_begin,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_prepare,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_prepare,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
@@ -387,8 +410,21 @@ static int acpi_hibernation_begin_old(vo
 {
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
+	if (!error) {
+		error = hibernate_nvs_alloc();
+		if (!error)
+			acpi_target_sleep_state = ACPI_STATE_S4;
+	}
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot_old(void)
+{
+	int error = acpi_pm_disable_gpes();
+
 	if (!error)
-		acpi_target_sleep_state = ACPI_STATE_S4;
+		hibernate_nvs_save();
+
 	return error;
 }
 
@@ -399,8 +435,8 @@ static int acpi_hibernation_begin_old(vo
 static struct platform_hibernation_ops acpi_hibernation_ops_old = {
 	.begin = acpi_hibernation_begin_old,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_disable_gpes,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot_old,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_disable_gpes,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
include/linux/suspend.h +13 -0
Index: linux-2.6/include/linux/suspend.h
===================================================================
@@ -233,6 +233,11 @@ extern unsigned long get_safe_page(gfp_t
 extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
 extern int hibernate(void);
 extern bool system_entering_hibernation(void);
+extern int hibernate_nvs_register(unsigned long start, unsigned long size);
+extern int hibernate_nvs_alloc(void);
+extern void hibernate_nvs_free(void);
+extern void hibernate_nvs_save(void);
+extern void hibernate_nvs_restore(void);
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
 static inline void swsusp_set_page_free(struct page *p) {}
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */
 
 #ifdef CONFIG_PM_SLEEP
kernel/power/swsusp.c +121 -0

124 lines not on this page · raw message

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#7 lore raw

Re: [PATCH 2/4] ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

Hi.

Quoted 210 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

According to the ACPI Specification 3.0b, Section 15.3.2, "OSPM will call the _PTS control method some time before entering a sleeping state, to allow the platform’s AML code to update this memory image before entering the sleeping state. After the system awakes from an S4 state, OSPM will restore this memory area and call the _WAK control method to enable the BIOS to reclaim its memory image." For this reason, implement a mechanism allowing us to save the NVS memory during hibernation and to restore it during the subsequent resume.

Based on a patch by Zhang Rui.

---
 drivers/acpi/sleep/main.c |   50 ++++++++++++++++---
 include/linux/suspend.h   |   13 ++++
 kernel/power/swsusp.c     |  121 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 177 insertions(+), 7 deletions(-)

Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================

drivers/acpi/sleep/main.c +43 -7
@@ -321,8 +321,23 @@ void __init acpi_no_s4_hw_signature(void
 
 static int acpi_hibernation_begin(void)
 {
-	acpi_target_sleep_state = ACPI_STATE_S4;
-	return 0;
+	int error;
+
+	error = hibernate_nvs_alloc();
+	if (!error)
+		acpi_target_sleep_state = ACPI_STATE_S4;
+
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot(void)
+{
+	int error = acpi_pm_prepare();
+
+	if (!error)
+		hibernate_nvs_save();
+
+	return error;
 }
 
 static int acpi_hibernation_enter(void)
@@ -343,6 +358,12 @@ static int acpi_hibernation_enter(void)
 	return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 }
 
+static void acpi_hibernation_finish(void)
+{
+	hibernate_nvs_free();
+	acpi_pm_finish();
+}
+
 static void acpi_hibernation_leave(void)
 {
 	/*
@@ -358,6 +379,8 @@ static void acpi_hibernation_leave(void)
 			"cannot resume!\n");
 		panic("ACPI S4 hardware signature mismatch");
 	}
+	/* Restore the NVS memory area */
+	hibernate_nvs_restore();
 }
 
 static void acpi_pm_enable_gpes(void)
@@ -368,8 +391,8 @@ static void acpi_pm_enable_gpes(void)
 static struct platform_hibernation_ops acpi_hibernation_ops = {
 	.begin = acpi_hibernation_begin,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_prepare,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_prepare,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
@@ -387,8 +410,21 @@ static int acpi_hibernation_begin_old(vo
 {
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
+	if (!error) {
+		error = hibernate_nvs_alloc();
+		if (!error)
+			acpi_target_sleep_state = ACPI_STATE_S4;
+	}
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot_old(void)
+{
+	int error = acpi_pm_disable_gpes();
+
 	if (!error)
-		acpi_target_sleep_state = ACPI_STATE_S4;
+		hibernate_nvs_save();
+
 	return error;
 }
 
@@ -399,8 +435,8 @@ static int acpi_hibernation_begin_old(vo
 static struct platform_hibernation_ops acpi_hibernation_ops_old = {
 	.begin = acpi_hibernation_begin_old,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_disable_gpes,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot_old,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_disable_gpes,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,

Index: linux-2.6/include/linux/suspend.h
===================================================================

include/linux/suspend.h +13 -0
@@ -233,6 +233,11 @@ extern unsigned long get_safe_page(gfp_t
 extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
 extern int hibernate(void);
 extern bool system_entering_hibernation(void);
+extern int hibernate_nvs_register(unsigned long start, unsigned long size);
+extern int hibernate_nvs_alloc(void);
+extern void hibernate_nvs_free(void);
+extern void hibernate_nvs_save(void);
+extern void hibernate_nvs_restore(void);
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
 static inline void swsusp_set_page_free(struct page *p) {}
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */
 
 #ifdef CONFIG_PM_SLEEP

Index: linux-2.6/kernel/power/swsusp.c
===================================================================

kernel/power/swsusp.c +50 -0
@@ -262,3 +262,124 @@ int swsusp_shrink_memory(void)
 
 	return 0;
 }
+
+/*
+ * Platforms, like ACPI, may want us to save some memory used by them during
+ * hibernation and to restore the contents of this memory during the subsequent
+ * resume.  The code below implements a mechanism allowing us to do that.
+ */
+
+struct nvs_page {
+	unsigned long phys_start;
+	unsigned int size;
+	void *kaddr;
+	void *data;
+	struct list_head node;
+};
+
+static LIST_HEAD(nvs_list);
+
+/**
+ *	hibernate_nvs_register - register platform NVS memory region to save
+ *	@start - physical address of the region
+ *	@size - size of the region
+ *
+ *	The NVS region need not be page-aligned (both ends) and we arrange
+ *	things so that the data from page-aligned addresses in this region will
+ *	be copied into separate RAM pages.
+ */
+int hibernate_nvs_register(unsigned long start, unsigned long size)
+{
+	struct nvs_page *entry, *next;
+
+	while (size > 0) {
+		unsigned int nr_bytes;
+
+		entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
+		if (!entry)
+			goto Error;
+
+		list_add_tail(&entry->node, &nvs_list);
+		entry->phys_start = start;
+		nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
+		entry->size = (size < nr_bytes) ? size : nr_bytes;
+
+		start += entry->size;
+		size -= entry->size;
+	}
+	return 0;
+
+ Error:
+	list_for_each_entry_safe(entry, next, &nvs_list, node) {
+		list_del(&entry->node);

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 324

Quoted 10 lines
kernel/power/swsusp.c +10 -0
+		kfree(entry);
+	}
+	return -ENOMEM;
+}
+
+/**
+ *	hibernate_nvs_free - free data pages allocated for saving NVS regions
+ *
+ *	It is allowed to call this function many times in a row.
+ */

If this one is safe for multiple calls, should nvs_alloc be safe for multiple calls too? Or, conversely, is something funny going on such that hibernate_nvs_free needs to be safe for multiple calls?

Quoted 35 lines
kernel/power/swsusp.c +35 -0
+void hibernate_nvs_free(void)
+{
+	struct nvs_page *entry;
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			free_page((unsigned long)entry->data);
+			entry->data = NULL;
+			if (entry->kaddr) {
+				iounmap(entry->kaddr);
+				entry->kaddr = NULL;
+			}
+		}
+}
+
+/**
+ *	hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
+ */
+int hibernate_nvs_alloc(void)
+{
+	struct nvs_page *entry;
+
+	list_for_each_entry(entry, &nvs_list, node) {
+		entry->data = (void *)__get_free_page(GFP_KERNEL);
+		if (!entry->data) {
+			hibernate_nvs_free();
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+/**
+ *	hibernate_nvs_save - save NVS memory regions
+ */

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 369

Quoted 10 lines
kernel/power/swsusp.c +10 -0
+void hibernate_nvs_save(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Saving platform NVS memory\n");
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			entry->kaddr = ioremap(entry->phys_start, entry->size);
+			memcpy(entry->data, entry->kaddr, entry->size);

Why not unmap here? (I know you're delaying the unmap until the free, but it's probably clearer to map and unmap each time the memory is accessed (ie in restore too).

Quoted 6 lines
kernel/power/swsusp.c +6 -0
+		}
+}
+
+/**
+ *	hibernate_nvs_restore - restore NVS memory regions
+ */

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 385

Quoted 10 lines
kernel/power/swsusp.c +10 -0
+void hibernate_nvs_restore(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Restoring platform NVS memory\n");
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data)
+			memcpy(entry->kaddr, entry->data, entry->size);
+}

Regards,

Nigel

_______________________________________________
linux-pm mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

#8 lore raw

Re: [linux-pm] [PATCH 2/4] ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

In reply to Rafael J. Wysocki

Hi.

Quoted 210 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

According to the ACPI Specification 3.0b, Section 15.3.2, "OSPM will call the _PTS control method some time before entering a sleeping state, to allow the platform’s AML code to update this memory image before entering the sleeping state. After the system awakes from an S4 state, OSPM will restore this memory area and call the _WAK control method to enable the BIOS to reclaim its memory image." For this reason, implement a mechanism allowing us to save the NVS memory during hibernation and to restore it during the subsequent resume.

Based on a patch by Zhang Rui.

---
 drivers/acpi/sleep/main.c |   50 ++++++++++++++++---
 include/linux/suspend.h   |   13 ++++
 kernel/power/swsusp.c     |  121 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 177 insertions(+), 7 deletions(-)

Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================

drivers/acpi/sleep/main.c +43 -7
@@ -321,8 +321,23 @@ void __init acpi_no_s4_hw_signature(void
 
 static int acpi_hibernation_begin(void)
 {
-	acpi_target_sleep_state = ACPI_STATE_S4;
-	return 0;
+	int error;
+
+	error = hibernate_nvs_alloc();
+	if (!error)
+		acpi_target_sleep_state = ACPI_STATE_S4;
+
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot(void)
+{
+	int error = acpi_pm_prepare();
+
+	if (!error)
+		hibernate_nvs_save();
+
+	return error;
 }
 
 static int acpi_hibernation_enter(void)
@@ -343,6 +358,12 @@ static int acpi_hibernation_enter(void)
 	return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 }
 
+static void acpi_hibernation_finish(void)
+{
+	hibernate_nvs_free();
+	acpi_pm_finish();
+}
+
 static void acpi_hibernation_leave(void)
 {
 	/*
@@ -358,6 +379,8 @@ static void acpi_hibernation_leave(void)
 			"cannot resume!\n");
 		panic("ACPI S4 hardware signature mismatch");
 	}
+	/* Restore the NVS memory area */
+	hibernate_nvs_restore();
 }
 
 static void acpi_pm_enable_gpes(void)
@@ -368,8 +391,8 @@ static void acpi_pm_enable_gpes(void)
 static struct platform_hibernation_ops acpi_hibernation_ops = {
 	.begin = acpi_hibernation_begin,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_prepare,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_prepare,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
@@ -387,8 +410,21 @@ static int acpi_hibernation_begin_old(vo
 {
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
+	if (!error) {
+		error = hibernate_nvs_alloc();
+		if (!error)
+			acpi_target_sleep_state = ACPI_STATE_S4;
+	}
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot_old(void)
+{
+	int error = acpi_pm_disable_gpes();
+
 	if (!error)
-		acpi_target_sleep_state = ACPI_STATE_S4;
+		hibernate_nvs_save();
+
 	return error;
 }
 
@@ -399,8 +435,8 @@ static int acpi_hibernation_begin_old(vo
 static struct platform_hibernation_ops acpi_hibernation_ops_old = {
 	.begin = acpi_hibernation_begin_old,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_disable_gpes,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot_old,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_disable_gpes,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,

Index: linux-2.6/include/linux/suspend.h
===================================================================

include/linux/suspend.h +13 -0
@@ -233,6 +233,11 @@ extern unsigned long get_safe_page(gfp_t
 extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
 extern int hibernate(void);
 extern bool system_entering_hibernation(void);
+extern int hibernate_nvs_register(unsigned long start, unsigned long size);
+extern int hibernate_nvs_alloc(void);
+extern void hibernate_nvs_free(void);
+extern void hibernate_nvs_save(void);
+extern void hibernate_nvs_restore(void);
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
 static inline void swsusp_set_page_free(struct page *p) {}
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */
 
 #ifdef CONFIG_PM_SLEEP

Index: linux-2.6/kernel/power/swsusp.c
===================================================================

kernel/power/swsusp.c +50 -0
@@ -262,3 +262,124 @@ int swsusp_shrink_memory(void)
 
 	return 0;
 }
+
+/*
+ * Platforms, like ACPI, may want us to save some memory used by them during
+ * hibernation and to restore the contents of this memory during the subsequent
+ * resume.  The code below implements a mechanism allowing us to do that.
+ */
+
+struct nvs_page {
+	unsigned long phys_start;
+	unsigned int size;
+	void *kaddr;
+	void *data;
+	struct list_head node;
+};
+
+static LIST_HEAD(nvs_list);
+
+/**
+ *	hibernate_nvs_register - register platform NVS memory region to save
+ *	@start - physical address of the region
+ *	@size - size of the region
+ *
+ *	The NVS region need not be page-aligned (both ends) and we arrange
+ *	things so that the data from page-aligned addresses in this region will
+ *	be copied into separate RAM pages.
+ */
+int hibernate_nvs_register(unsigned long start, unsigned long size)
+{
+	struct nvs_page *entry, *next;
+
+	while (size > 0) {
+		unsigned int nr_bytes;
+
+		entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
+		if (!entry)
+			goto Error;
+
+		list_add_tail(&entry->node, &nvs_list);
+		entry->phys_start = start;
+		nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
+		entry->size = (size < nr_bytes) ? size : nr_bytes;
+
+		start += entry->size;
+		size -= entry->size;
+	}
+	return 0;
+
+ Error:
+	list_for_each_entry_safe(entry, next, &nvs_list, node) {
+		list_del(&entry->node);

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 324

Quoted 10 lines
kernel/power/swsusp.c +10 -0
+		kfree(entry);
+	}
+	return -ENOMEM;
+}
+
+/**
+ *	hibernate_nvs_free - free data pages allocated for saving NVS regions
+ *
+ *	It is allowed to call this function many times in a row.
+ */

If this one is safe for multiple calls, should nvs_alloc be safe for multiple calls too? Or, conversely, is something funny going on such that hibernate_nvs_free needs to be safe for multiple calls?

Quoted 35 lines
kernel/power/swsusp.c +35 -0
+void hibernate_nvs_free(void)
+{
+	struct nvs_page *entry;
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			free_page((unsigned long)entry->data);
+			entry->data = NULL;
+			if (entry->kaddr) {
+				iounmap(entry->kaddr);
+				entry->kaddr = NULL;
+			}
+		}
+}
+
+/**
+ *	hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
+ */
+int hibernate_nvs_alloc(void)
+{
+	struct nvs_page *entry;
+
+	list_for_each_entry(entry, &nvs_list, node) {
+		entry->data = (void *)__get_free_page(GFP_KERNEL);
+		if (!entry->data) {
+			hibernate_nvs_free();
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+/**
+ *	hibernate_nvs_save - save NVS memory regions
+ */

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 369

Quoted 10 lines
kernel/power/swsusp.c +10 -0
+void hibernate_nvs_save(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Saving platform NVS memory\n");
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			entry->kaddr = ioremap(entry->phys_start, entry->size);
+			memcpy(entry->data, entry->kaddr, entry->size);

Why not unmap here? (I know you're delaying the unmap until the free, but it's probably clearer to map and unmap each time the memory is accessed (ie in restore too).

Quoted 6 lines
kernel/power/swsusp.c +6 -0
+		}
+}
+
+/**
+ *	hibernate_nvs_restore - restore NVS memory regions
+ */

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 385

Quoted 10 lines
kernel/power/swsusp.c +10 -0
+void hibernate_nvs_restore(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Restoring platform NVS memory\n");
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data)
+			memcpy(entry->kaddr, entry->data, entry->size);
+}

Regards,

Nigel

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#9 lore raw

Re: [linux-pm] [PATCH 2/4] ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

Quoted 1 line · Nigel Cunningham

Hi.

Hi,

Quoted 217 lines · Rafael J. Wysocki
Quoted 216 lines

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

According to the ACPI Specification 3.0b, Section 15.3.2, "OSPM will call the _PTS control method some time before entering a sleeping state, to allow the platform’s AML code to update this memory image before entering the sleeping state. After the system awakes from an S4 state, OSPM will restore this memory area and call the _WAK control method to enable the BIOS to reclaim its memory image." For this reason, implement a mechanism allowing us to save the NVS memory during hibernation and to restore it during the subsequent resume.

Based on a patch by Zhang Rui.

---
 drivers/acpi/sleep/main.c |   50 ++++++++++++++++---
 include/linux/suspend.h   |   13 ++++
 kernel/power/swsusp.c     |  121 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 177 insertions(+), 7 deletions(-)

Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================

drivers/acpi/sleep/main.c +43 -7
@@ -321,8 +321,23 @@ void __init acpi_no_s4_hw_signature(void
 
 static int acpi_hibernation_begin(void)
 {
-	acpi_target_sleep_state = ACPI_STATE_S4;
-	return 0;
+	int error;
+
+	error = hibernate_nvs_alloc();
+	if (!error)
+		acpi_target_sleep_state = ACPI_STATE_S4;
+
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot(void)
+{
+	int error = acpi_pm_prepare();
+
+	if (!error)
+		hibernate_nvs_save();
+
+	return error;
 }
 
 static int acpi_hibernation_enter(void)
@@ -343,6 +358,12 @@ static int acpi_hibernation_enter(void)
 	return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 }
 
+static void acpi_hibernation_finish(void)
+{
+	hibernate_nvs_free();
+	acpi_pm_finish();
+}
+
 static void acpi_hibernation_leave(void)
 {
 	/*
@@ -358,6 +379,8 @@ static void acpi_hibernation_leave(void)
 			"cannot resume!\n");
 		panic("ACPI S4 hardware signature mismatch");
 	}
+	/* Restore the NVS memory area */
+	hibernate_nvs_restore();
 }
 
 static void acpi_pm_enable_gpes(void)
@@ -368,8 +391,8 @@ static void acpi_pm_enable_gpes(void)
 static struct platform_hibernation_ops acpi_hibernation_ops = {
 	.begin = acpi_hibernation_begin,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_prepare,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_prepare,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
@@ -387,8 +410,21 @@ static int acpi_hibernation_begin_old(vo
 {
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
+	if (!error) {
+		error = hibernate_nvs_alloc();
+		if (!error)
+			acpi_target_sleep_state = ACPI_STATE_S4;
+	}
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot_old(void)
+{
+	int error = acpi_pm_disable_gpes();
+
 	if (!error)
-		acpi_target_sleep_state = ACPI_STATE_S4;
+		hibernate_nvs_save();
+
 	return error;
 }
 
@@ -399,8 +435,8 @@ static int acpi_hibernation_begin_old(vo
 static struct platform_hibernation_ops acpi_hibernation_ops_old = {
 	.begin = acpi_hibernation_begin_old,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_disable_gpes,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot_old,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_disable_gpes,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,

Index: linux-2.6/include/linux/suspend.h
===================================================================

include/linux/suspend.h +13 -0
@@ -233,6 +233,11 @@ extern unsigned long get_safe_page(gfp_t
 extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
 extern int hibernate(void);
 extern bool system_entering_hibernation(void);
+extern int hibernate_nvs_register(unsigned long start, unsigned long size);
+extern int hibernate_nvs_alloc(void);
+extern void hibernate_nvs_free(void);
+extern void hibernate_nvs_save(void);
+extern void hibernate_nvs_restore(void);
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
 static inline void swsusp_set_page_free(struct page *p) {}
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */
 
 #ifdef CONFIG_PM_SLEEP

Index: linux-2.6/kernel/power/swsusp.c
===================================================================

kernel/power/swsusp.c +56 -0
@@ -262,3 +262,124 @@ int swsusp_shrink_memory(void)
 
 	return 0;
 }
+
+/*
+ * Platforms, like ACPI, may want us to save some memory used by them during
+ * hibernation and to restore the contents of this memory during the subsequent
+ * resume.  The code below implements a mechanism allowing us to do that.
+ */
+
+struct nvs_page {
+	unsigned long phys_start;
+	unsigned int size;
+	void *kaddr;
+	void *data;
+	struct list_head node;
+};
+
+static LIST_HEAD(nvs_list);
+
+/**
+ *	hibernate_nvs_register - register platform NVS memory region to save
+ *	@start - physical address of the region
+ *	@size - size of the region
+ *
+ *	The NVS region need not be page-aligned (both ends) and we arrange
+ *	things so that the data from page-aligned addresses in this region will
+ *	be copied into separate RAM pages.
+ */
+int hibernate_nvs_register(unsigned long start, unsigned long size)
+{
+	struct nvs_page *entry, *next;
+
+	while (size > 0) {
+		unsigned int nr_bytes;
+
+		entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
+		if (!entry)
+			goto Error;
+
+		list_add_tail(&entry->node, &nvs_list);
+		entry->phys_start = start;
+		nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
+		entry->size = (size < nr_bytes) ? size : nr_bytes;
+
+		start += entry->size;
+		size -= entry->size;
+	}
+	return 0;
+
+ Error:
+	list_for_each_entry_safe(entry, next, &nvs_list, node) {
+		list_del(&entry->node);
+		kfree(entry);
+	}
+	return -ENOMEM;
+}
+
+/**
Quoted 8 lines

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 324

Quoted 4 lines
kernel/power/swsusp.c +4 -0
+ *	hibernate_nvs_free - free data pages allocated for saving NVS regions
+ *
+ *	It is allowed to call this function many times in a row.
+ */

If this one is safe for multiple calls, should nvs_alloc be safe for multiple calls too? Or, conversely, is something funny going on such that hibernate_nvs_free needs to be safe for multiple calls?

I thought it would be necessary to call that additionally from platform_end() for the error path in which platform_pre_snapshot() fails, but that doesn't seem to be the case. It doesn't hurt to keep it in this form, though, IMO.

Quoted 41 lines
Quoted 41 lines
kernel/power/swsusp.c +41 -0
+void hibernate_nvs_free(void)
+{
+	struct nvs_page *entry;
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			free_page((unsigned long)entry->data);
+			entry->data = NULL;
+			if (entry->kaddr) {
+				iounmap(entry->kaddr);
+				entry->kaddr = NULL;
+			}
+		}
+}
+
+/**
+ *	hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
+ */
+int hibernate_nvs_alloc(void)
+{
+	struct nvs_page *entry;
+
+	list_for_each_entry(entry, &nvs_list, node) {
+		entry->data = (void *)__get_free_page(GFP_KERNEL);
+		if (!entry->data) {
+			hibernate_nvs_free();
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+/**
+ *	hibernate_nvs_save - save NVS memory regions
+ */
+void hibernate_nvs_save(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Saving platform NVS memory\n");
+
Quoted 8 lines

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 369

Quoted 4 lines
kernel/power/swsusp.c +4 -0
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			entry->kaddr = ioremap(entry->phys_start, entry->size);
+			memcpy(entry->data, entry->kaddr, entry->size);

Why not unmap here? (I know you're delaying the unmap until the free, but it's probably clearer to map and unmap each time the memory is accessed (ie in restore too).

Ah, this one is tricky. We cannot unmap from atomic context. :-)

Quoted 8 lines
Quoted 8 lines
kernel/power/swsusp.c +8 -0
+		}
+}
+
+/**
+ *	hibernate_nvs_restore - restore NVS memory regions
+ */
+void hibernate_nvs_restore(void)
+{
Quoted 8 lines

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 385

Quoted 8 lines
kernel/power/swsusp.c +8 -0
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Restoring platform NVS memory\n");
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data)
+			memcpy(entry->kaddr, entry->data, entry->size);
+}

Thanks,
Rafael
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#10 lore raw

Re: [PATCH 2/4] ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

Hi.

Quoted 231 lines · Rafael J. Wysocki
Quoted 1 line · Nigel Cunningham

Hi.

Hi,

Quoted 217 lines · Rafael J. Wysocki
Quoted 216 lines

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

According to the ACPI Specification 3.0b, Section 15.3.2, "OSPM will call the _PTS control method some time before entering a sleeping state, to allow the platform’s AML code to update this memory image before entering the sleeping state. After the system awakes from an S4 state, OSPM will restore this memory area and call the _WAK control method to enable the BIOS to reclaim its memory image." For this reason, implement a mechanism allowing us to save the NVS memory during hibernation and to restore it during the subsequent resume.

Based on a patch by Zhang Rui.

---
 drivers/acpi/sleep/main.c |   50 ++++++++++++++++---
 include/linux/suspend.h   |   13 ++++
 kernel/power/swsusp.c     |  121 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 177 insertions(+), 7 deletions(-)

Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================

drivers/acpi/sleep/main.c +43 -7
@@ -321,8 +321,23 @@ void __init acpi_no_s4_hw_signature(void
 
 static int acpi_hibernation_begin(void)
 {
-	acpi_target_sleep_state = ACPI_STATE_S4;
-	return 0;
+	int error;
+
+	error = hibernate_nvs_alloc();
+	if (!error)
+		acpi_target_sleep_state = ACPI_STATE_S4;
+
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot(void)
+{
+	int error = acpi_pm_prepare();
+
+	if (!error)
+		hibernate_nvs_save();
+
+	return error;
 }
 
 static int acpi_hibernation_enter(void)
@@ -343,6 +358,12 @@ static int acpi_hibernation_enter(void)
 	return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 }
 
+static void acpi_hibernation_finish(void)
+{
+	hibernate_nvs_free();
+	acpi_pm_finish();
+}
+
 static void acpi_hibernation_leave(void)
 {
 	/*
@@ -358,6 +379,8 @@ static void acpi_hibernation_leave(void)
 			"cannot resume!\n");
 		panic("ACPI S4 hardware signature mismatch");
 	}
+	/* Restore the NVS memory area */
+	hibernate_nvs_restore();
 }
 
 static void acpi_pm_enable_gpes(void)
@@ -368,8 +391,8 @@ static void acpi_pm_enable_gpes(void)
 static struct platform_hibernation_ops acpi_hibernation_ops = {
 	.begin = acpi_hibernation_begin,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_prepare,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_prepare,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
@@ -387,8 +410,21 @@ static int acpi_hibernation_begin_old(vo
 {
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
+	if (!error) {
+		error = hibernate_nvs_alloc();
+		if (!error)
+			acpi_target_sleep_state = ACPI_STATE_S4;
+	}
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot_old(void)
+{
+	int error = acpi_pm_disable_gpes();
+
 	if (!error)
-		acpi_target_sleep_state = ACPI_STATE_S4;
+		hibernate_nvs_save();
+
 	return error;
 }
 
@@ -399,8 +435,8 @@ static int acpi_hibernation_begin_old(vo
 static struct platform_hibernation_ops acpi_hibernation_ops_old = {
 	.begin = acpi_hibernation_begin_old,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_disable_gpes,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot_old,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_disable_gpes,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,

Index: linux-2.6/include/linux/suspend.h
===================================================================

include/linux/suspend.h +13 -0
@@ -233,6 +233,11 @@ extern unsigned long get_safe_page(gfp_t
 extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
 extern int hibernate(void);
 extern bool system_entering_hibernation(void);
+extern int hibernate_nvs_register(unsigned long start, unsigned long size);
+extern int hibernate_nvs_alloc(void);
+extern void hibernate_nvs_free(void);
+extern void hibernate_nvs_save(void);
+extern void hibernate_nvs_restore(void);
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
 static inline void swsusp_set_page_free(struct page *p) {}
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */
 
 #ifdef CONFIG_PM_SLEEP

Index: linux-2.6/kernel/power/swsusp.c
===================================================================

kernel/power/swsusp.c +56 -0
@@ -262,3 +262,124 @@ int swsusp_shrink_memory(void)
 
 	return 0;
 }
+
+/*
+ * Platforms, like ACPI, may want us to save some memory used by them during
+ * hibernation and to restore the contents of this memory during the subsequent
+ * resume.  The code below implements a mechanism allowing us to do that.
+ */
+
+struct nvs_page {
+	unsigned long phys_start;
+	unsigned int size;
+	void *kaddr;
+	void *data;
+	struct list_head node;
+};
+
+static LIST_HEAD(nvs_list);
+
+/**
+ *	hibernate_nvs_register - register platform NVS memory region to save
+ *	@start - physical address of the region
+ *	@size - size of the region
+ *
+ *	The NVS region need not be page-aligned (both ends) and we arrange
+ *	things so that the data from page-aligned addresses in this region will
+ *	be copied into separate RAM pages.
+ */
+int hibernate_nvs_register(unsigned long start, unsigned long size)
+{
+	struct nvs_page *entry, *next;
+
+	while (size > 0) {
+		unsigned int nr_bytes;
+
+		entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
+		if (!entry)
+			goto Error;
+
+		list_add_tail(&entry->node, &nvs_list);
+		entry->phys_start = start;
+		nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
+		entry->size = (size < nr_bytes) ? size : nr_bytes;
+
+		start += entry->size;
+		size -= entry->size;
+	}
+	return 0;
+
+ Error:
+	list_for_each_entry_safe(entry, next, &nvs_list, node) {
+		list_del(&entry->node);
+		kfree(entry);
+	}
+	return -ENOMEM;
+}
+
+/**
Quoted 8 lines

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 324

Quoted 4 lines
kernel/power/swsusp.c +4 -0
+ *	hibernate_nvs_free - free data pages allocated for saving NVS regions
+ *
+ *	It is allowed to call this function many times in a row.
+ */

If this one is safe for multiple calls, should nvs_alloc be safe for multiple calls too? Or, conversely, is something funny going on such that hibernate_nvs_free needs to be safe for multiple calls?

Quoted 3 lines

I thought it would be necessary to call that additionally from platform_end() for the error path in which platform_pre_snapshot() fails, but that doesn't seem to be the case. It doesn't hurt to keep it in this form, though, IMO.

Okee doke.

Quoted 50 lines
Quoted 41 lines
Quoted 41 lines
kernel/power/swsusp.c +41 -0
+void hibernate_nvs_free(void)
+{
+	struct nvs_page *entry;
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			free_page((unsigned long)entry->data);
+			entry->data = NULL;
+			if (entry->kaddr) {
+				iounmap(entry->kaddr);
+				entry->kaddr = NULL;
+			}
+		}
+}
+
+/**
+ *	hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
+ */
+int hibernate_nvs_alloc(void)
+{
+	struct nvs_page *entry;
+
+	list_for_each_entry(entry, &nvs_list, node) {
+		entry->data = (void *)__get_free_page(GFP_KERNEL);
+		if (!entry->data) {
+			hibernate_nvs_free();
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+/**
+ *	hibernate_nvs_save - save NVS memory regions
+ */
+void hibernate_nvs_save(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Saving platform NVS memory\n");
+
Quoted 8 lines

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 369

Quoted 4 lines
kernel/power/swsusp.c +4 -0
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			entry->kaddr = ioremap(entry->phys_start, entry->size);
+			memcpy(entry->data, entry->kaddr, entry->size);

Why not unmap here? (I know you're delaying the unmap until the free, but it's probably clearer to map and unmap each time the memory is accessed (ie in restore too).

Quoted 1 line

Ah, this one is tricky. We cannot unmap from atomic context. :-)

Maybe a comment then, lest someone get the same idea in future and be as ignorant as me? :)

Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
kernel/power/swsusp.c +11 -0
+		}
+}
+
+/**
+ *	hibernate_nvs_restore - restore NVS memory regions
+ */
+void hibernate_nvs_restore(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Restoring platform NVS memory\n");
Quoted 8 lines
Quoted 5 lines

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 385

Quoted 5 lines
kernel/power/swsusp.c +5 -0
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data)
+			memcpy(entry->kaddr, entry->data, entry->size);
+}

Thanks,
Rafael

Regards,

Nigel

_______________________________________________
linux-pm mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

#11 lore raw

Re: [linux-pm] [PATCH 2/4] ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

In reply to Rafael J. Wysocki

Hi.

Quoted 231 lines · Rafael J. Wysocki
Quoted 1 line · Nigel Cunningham

Hi.

Hi,

Quoted 217 lines · Rafael J. Wysocki
Quoted 216 lines

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

According to the ACPI Specification 3.0b, Section 15.3.2, "OSPM will call the _PTS control method some time before entering a sleeping state, to allow the platform’s AML code to update this memory image before entering the sleeping state. After the system awakes from an S4 state, OSPM will restore this memory area and call the _WAK control method to enable the BIOS to reclaim its memory image." For this reason, implement a mechanism allowing us to save the NVS memory during hibernation and to restore it during the subsequent resume.

Based on a patch by Zhang Rui.

---
 drivers/acpi/sleep/main.c |   50 ++++++++++++++++---
 include/linux/suspend.h   |   13 ++++
 kernel/power/swsusp.c     |  121 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 177 insertions(+), 7 deletions(-)

Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================

drivers/acpi/sleep/main.c +43 -7
@@ -321,8 +321,23 @@ void __init acpi_no_s4_hw_signature(void
 
 static int acpi_hibernation_begin(void)
 {
-	acpi_target_sleep_state = ACPI_STATE_S4;
-	return 0;
+	int error;
+
+	error = hibernate_nvs_alloc();
+	if (!error)
+		acpi_target_sleep_state = ACPI_STATE_S4;
+
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot(void)
+{
+	int error = acpi_pm_prepare();
+
+	if (!error)
+		hibernate_nvs_save();
+
+	return error;
 }
 
 static int acpi_hibernation_enter(void)
@@ -343,6 +358,12 @@ static int acpi_hibernation_enter(void)
 	return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 }
 
+static void acpi_hibernation_finish(void)
+{
+	hibernate_nvs_free();
+	acpi_pm_finish();
+}
+
 static void acpi_hibernation_leave(void)
 {
 	/*
@@ -358,6 +379,8 @@ static void acpi_hibernation_leave(void)
 			"cannot resume!\n");
 		panic("ACPI S4 hardware signature mismatch");
 	}
+	/* Restore the NVS memory area */
+	hibernate_nvs_restore();
 }
 
 static void acpi_pm_enable_gpes(void)
@@ -368,8 +391,8 @@ static void acpi_pm_enable_gpes(void)
 static struct platform_hibernation_ops acpi_hibernation_ops = {
 	.begin = acpi_hibernation_begin,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_prepare,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_prepare,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
@@ -387,8 +410,21 @@ static int acpi_hibernation_begin_old(vo
 {
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
+	if (!error) {
+		error = hibernate_nvs_alloc();
+		if (!error)
+			acpi_target_sleep_state = ACPI_STATE_S4;
+	}
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot_old(void)
+{
+	int error = acpi_pm_disable_gpes();
+
 	if (!error)
-		acpi_target_sleep_state = ACPI_STATE_S4;
+		hibernate_nvs_save();
+
 	return error;
 }
 
@@ -399,8 +435,8 @@ static int acpi_hibernation_begin_old(vo
 static struct platform_hibernation_ops acpi_hibernation_ops_old = {
 	.begin = acpi_hibernation_begin_old,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_disable_gpes,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot_old,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_disable_gpes,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,

Index: linux-2.6/include/linux/suspend.h
===================================================================

include/linux/suspend.h +13 -0
@@ -233,6 +233,11 @@ extern unsigned long get_safe_page(gfp_t
 extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
 extern int hibernate(void);
 extern bool system_entering_hibernation(void);
+extern int hibernate_nvs_register(unsigned long start, unsigned long size);
+extern int hibernate_nvs_alloc(void);
+extern void hibernate_nvs_free(void);
+extern void hibernate_nvs_save(void);
+extern void hibernate_nvs_restore(void);
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
 static inline void swsusp_set_page_free(struct page *p) {}
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */
 
 #ifdef CONFIG_PM_SLEEP

Index: linux-2.6/kernel/power/swsusp.c
===================================================================

kernel/power/swsusp.c +56 -0
@@ -262,3 +262,124 @@ int swsusp_shrink_memory(void)
 
 	return 0;
 }
+
+/*
+ * Platforms, like ACPI, may want us to save some memory used by them during
+ * hibernation and to restore the contents of this memory during the subsequent
+ * resume.  The code below implements a mechanism allowing us to do that.
+ */
+
+struct nvs_page {
+	unsigned long phys_start;
+	unsigned int size;
+	void *kaddr;
+	void *data;
+	struct list_head node;
+};
+
+static LIST_HEAD(nvs_list);
+
+/**
+ *	hibernate_nvs_register - register platform NVS memory region to save
+ *	@start - physical address of the region
+ *	@size - size of the region
+ *
+ *	The NVS region need not be page-aligned (both ends) and we arrange
+ *	things so that the data from page-aligned addresses in this region will
+ *	be copied into separate RAM pages.
+ */
+int hibernate_nvs_register(unsigned long start, unsigned long size)
+{
+	struct nvs_page *entry, *next;
+
+	while (size > 0) {
+		unsigned int nr_bytes;
+
+		entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
+		if (!entry)
+			goto Error;
+
+		list_add_tail(&entry->node, &nvs_list);
+		entry->phys_start = start;
+		nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
+		entry->size = (size < nr_bytes) ? size : nr_bytes;
+
+		start += entry->size;
+		size -= entry->size;
+	}
+	return 0;
+
+ Error:
+	list_for_each_entry_safe(entry, next, &nvs_list, node) {
+		list_del(&entry->node);
+		kfree(entry);
+	}
+	return -ENOMEM;
+}
+
+/**
Quoted 8 lines

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 324

Quoted 4 lines
kernel/power/swsusp.c +4 -0
+ *	hibernate_nvs_free - free data pages allocated for saving NVS regions
+ *
+ *	It is allowed to call this function many times in a row.
+ */

If this one is safe for multiple calls, should nvs_alloc be safe for multiple calls too? Or, conversely, is something funny going on such that hibernate_nvs_free needs to be safe for multiple calls?

Quoted 3 lines

I thought it would be necessary to call that additionally from platform_end() for the error path in which platform_pre_snapshot() fails, but that doesn't seem to be the case. It doesn't hurt to keep it in this form, though, IMO.

Okee doke.

Quoted 50 lines
Quoted 41 lines
Quoted 41 lines
kernel/power/swsusp.c +41 -0
+void hibernate_nvs_free(void)
+{
+	struct nvs_page *entry;
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			free_page((unsigned long)entry->data);
+			entry->data = NULL;
+			if (entry->kaddr) {
+				iounmap(entry->kaddr);
+				entry->kaddr = NULL;
+			}
+		}
+}
+
+/**
+ *	hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
+ */
+int hibernate_nvs_alloc(void)
+{
+	struct nvs_page *entry;
+
+	list_for_each_entry(entry, &nvs_list, node) {
+		entry->data = (void *)__get_free_page(GFP_KERNEL);
+		if (!entry->data) {
+			hibernate_nvs_free();
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+/**
+ *	hibernate_nvs_save - save NVS memory regions
+ */
+void hibernate_nvs_save(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Saving platform NVS memory\n");
+
Quoted 8 lines

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 369

Quoted 4 lines
kernel/power/swsusp.c +4 -0
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			entry->kaddr = ioremap(entry->phys_start, entry->size);
+			memcpy(entry->data, entry->kaddr, entry->size);

Why not unmap here? (I know you're delaying the unmap until the free, but it's probably clearer to map and unmap each time the memory is accessed (ie in restore too).

Quoted 1 line

Ah, this one is tricky. We cannot unmap from atomic context. :-)

Maybe a comment then, lest someone get the same idea in future and be as ignorant as me? :)

Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
kernel/power/swsusp.c +11 -0
+		}
+}
+
+/**
+ *	hibernate_nvs_restore - restore NVS memory regions
+ */
+void hibernate_nvs_restore(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Restoring platform NVS memory\n");
Quoted 8 lines
Quoted 5 lines

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 385

Quoted 5 lines
kernel/power/swsusp.c +5 -0
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data)
+			memcpy(entry->kaddr, entry->data, entry->size);
+}

Thanks,
Rafael

Regards,

Nigel

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#12 lore raw

PATCH 2/4 ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory (rev. 2)

Quoted 1 line · Nigel Cunningham

Hi.

Hi,

Quoted 4 lines · Rafael J. Wysocki
Quoted 3 lines · Nigel Cunningham
Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line

From: Rafael J. Wysocki <[email protected]>

[--snip--]

Quoted 18 lines
Quoted 18 lines
Quoted 17 lines

Review

Quoted 13 lines
(quoted diff) +13 -0
+/**
+ *	hibernate_nvs_save - save NVS memory regions
+ */
+void hibernate_nvs_save(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Saving platform NVS memory\n");
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			entry->kaddr = ioremap(entry->phys_start, entry->size);
+			memcpy(entry->data, entry->kaddr, entry->size);

Why not unmap here? (I know you're delaying the unmap until the free, but it's probably clearer to map and unmap each time the memory is accessed (ie in restore too).

Quoted 4 lines
Quoted 1 line

Ah, this one is tricky. We cannot unmap from atomic context. :-)

Maybe a comment then, lest someone get the same idea in future and be as ignorant as me? :)

Well, I was considering to add one, now changed the kerneldoc comment to mention that.

Please find updated patch below.

---
From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory (rev. 2)

According to the ACPI Specification 3.0b, Section 15.3.2, "OSPM will call the _PTS control method some time before entering a sleeping state, to allow the platform’s AML code to update this memory image before entering the sleeping state. After the system awakes from an S4 state, OSPM will restore this memory area and call the _WAK control method to enable the BIOS to reclaim its memory image." For this reason, implement a mechanism allowing us to save the NVS memory during hibernation and to restore it during the subsequent resume.

Based on a patch by Zhang Rui.

---
 drivers/acpi/sleep/main.c |   50 +++++++++++++++---
 include/linux/suspend.h   |   13 ++++
 kernel/power/swsusp.c     |  124 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 180 insertions(+), 7 deletions(-)
drivers/acpi/sleep/main.c +43 -7
Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================
@@ -321,8 +321,23 @@ void __init acpi_no_s4_hw_signature(void
 
 static int acpi_hibernation_begin(void)
 {
-	acpi_target_sleep_state = ACPI_STATE_S4;
-	return 0;
+	int error;
+
+	error = hibernate_nvs_alloc();
+	if (!error)
+		acpi_target_sleep_state = ACPI_STATE_S4;
+
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot(void)
+{
+	int error = acpi_pm_prepare();
+
+	if (!error)
+		hibernate_nvs_save();
+
+	return error;
 }
 
 static int acpi_hibernation_enter(void)
@@ -343,6 +358,12 @@ static int acpi_hibernation_enter(void)
 	return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 }
 
+static void acpi_hibernation_finish(void)
+{
+	hibernate_nvs_free();
+	acpi_pm_finish();
+}
+
 static void acpi_hibernation_leave(void)
 {
 	/*
@@ -358,6 +379,8 @@ static void acpi_hibernation_leave(void)
 			"cannot resume!\n");
 		panic("ACPI S4 hardware signature mismatch");
 	}
+	/* Restore the NVS memory area */
+	hibernate_nvs_restore();
 }
 
 static void acpi_pm_enable_gpes(void)
@@ -368,8 +391,8 @@ static void acpi_pm_enable_gpes(void)
 static struct platform_hibernation_ops acpi_hibernation_ops = {
 	.begin = acpi_hibernation_begin,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_prepare,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_prepare,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
@@ -387,8 +410,21 @@ static int acpi_hibernation_begin_old(vo
 {
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
+	if (!error) {
+		error = hibernate_nvs_alloc();
+		if (!error)
+			acpi_target_sleep_state = ACPI_STATE_S4;
+	}
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot_old(void)
+{
+	int error = acpi_pm_disable_gpes();
+
 	if (!error)
-		acpi_target_sleep_state = ACPI_STATE_S4;
+		hibernate_nvs_save();
+
 	return error;
 }
 
@@ -399,8 +435,8 @@ static int acpi_hibernation_begin_old(vo
 static struct platform_hibernation_ops acpi_hibernation_ops_old = {
 	.begin = acpi_hibernation_begin_old,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_disable_gpes,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot_old,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_disable_gpes,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
include/linux/suspend.h +13 -0
Index: linux-2.6/include/linux/suspend.h
===================================================================
@@ -233,6 +233,11 @@ extern unsigned long get_safe_page(gfp_t
 extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
 extern int hibernate(void);
 extern bool system_entering_hibernation(void);
+extern int hibernate_nvs_register(unsigned long start, unsigned long size);
+extern int hibernate_nvs_alloc(void);
+extern void hibernate_nvs_free(void);
+extern void hibernate_nvs_save(void);
+extern void hibernate_nvs_restore(void);
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
 static inline void swsusp_set_page_free(struct page *p) {}
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */
 
 #ifdef CONFIG_PM_SLEEP
kernel/power/swsusp.c +124 -0

127 lines not on this page · raw message

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#13 lore raw

Re: [PATCH 2/4] ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory (rev. 2)

Hi.

Quoted 24 lines · Rafael J. Wysocki
Quoted 4 lines · Nigel Cunningham
Quoted 3 lines · Rafael J. Wysocki
Quoted 2 lines · Nigel Cunningham
Quoted 1 line · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

[--snip--]

Quoted 18 lines
Quoted 18 lines
Quoted 17 lines

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 369

Quoted 13 lines
kernel/power/swsusp.c +13 -0
+/**
+ *	hibernate_nvs_save - save NVS memory regions
+ */
+void hibernate_nvs_save(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Saving platform NVS memory\n");
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			entry->kaddr = ioremap(entry->phys_start, entry->size);
+			memcpy(entry->data, entry->kaddr, entry->size);

Why not unmap here? (I know you're delaying the unmap until the free, but it's probably clearer to map and unmap each time the memory is accessed (ie in restore too).

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

Ah, this one is tricky. We cannot unmap from atomic context. :-)

Maybe a comment then, lest someone get the same idea in future and be as ignorant as me? :)

Well, I was considering to add one, now changed the kerneldoc comment to mention that.

Terrific. Thanks.

Quoted 161 lines

Please find updated patch below.

---
From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory (rev. 2)

According to the ACPI Specification 3.0b, Section 15.3.2, "OSPM will call the _PTS control method some time before entering a sleeping state, to allow the platform’s AML code to update this memory image before entering the sleeping state. After the system awakes from an S4 state, OSPM will restore this memory area and call the _WAK control method to enable the BIOS to reclaim its memory image." For this reason, implement a mechanism allowing us to save the NVS memory during hibernation and to restore it during the subsequent resume.

Based on a patch by Zhang Rui.

---
 drivers/acpi/sleep/main.c |   50 +++++++++++++++---
 include/linux/suspend.h   |   13 ++++
 kernel/power/swsusp.c     |  124 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 180 insertions(+), 7 deletions(-)

Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================

drivers/acpi/sleep/main.c +43 -7
@@ -321,8 +321,23 @@ void __init acpi_no_s4_hw_signature(void
 
 static int acpi_hibernation_begin(void)
 {
-	acpi_target_sleep_state = ACPI_STATE_S4;
-	return 0;
+	int error;
+
+	error = hibernate_nvs_alloc();
+	if (!error)
+		acpi_target_sleep_state = ACPI_STATE_S4;
+
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot(void)
+{
+	int error = acpi_pm_prepare();
+
+	if (!error)
+		hibernate_nvs_save();
+
+	return error;
 }
 
 static int acpi_hibernation_enter(void)
@@ -343,6 +358,12 @@ static int acpi_hibernation_enter(void)
 	return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 }
 
+static void acpi_hibernation_finish(void)
+{
+	hibernate_nvs_free();
+	acpi_pm_finish();
+}
+
 static void acpi_hibernation_leave(void)
 {
 	/*
@@ -358,6 +379,8 @@ static void acpi_hibernation_leave(void)
 			"cannot resume!\n");
 		panic("ACPI S4 hardware signature mismatch");
 	}
+	/* Restore the NVS memory area */
+	hibernate_nvs_restore();
 }
 
 static void acpi_pm_enable_gpes(void)
@@ -368,8 +391,8 @@ static void acpi_pm_enable_gpes(void)
 static struct platform_hibernation_ops acpi_hibernation_ops = {
 	.begin = acpi_hibernation_begin,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_prepare,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_prepare,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
@@ -387,8 +410,21 @@ static int acpi_hibernation_begin_old(vo
 {
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
+	if (!error) {
+		error = hibernate_nvs_alloc();
+		if (!error)
+			acpi_target_sleep_state = ACPI_STATE_S4;
+	}
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot_old(void)
+{
+	int error = acpi_pm_disable_gpes();
+
 	if (!error)
-		acpi_target_sleep_state = ACPI_STATE_S4;
+		hibernate_nvs_save();
+
 	return error;
 }
 
@@ -399,8 +435,8 @@ static int acpi_hibernation_begin_old(vo
 static struct platform_hibernation_ops acpi_hibernation_ops_old = {
 	.begin = acpi_hibernation_begin_old,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_disable_gpes,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot_old,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_disable_gpes,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,

Index: linux-2.6/include/linux/suspend.h
===================================================================

include/linux/suspend.h +13 -0
@@ -233,6 +233,11 @@ extern unsigned long get_safe_page(gfp_t
 extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
 extern int hibernate(void);
 extern bool system_entering_hibernation(void);
+extern int hibernate_nvs_register(unsigned long start, unsigned long size);
+extern int hibernate_nvs_alloc(void);
+extern void hibernate_nvs_free(void);
+extern void hibernate_nvs_save(void);
+extern void hibernate_nvs_restore(void);
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
 static inline void swsusp_set_page_free(struct page *p) {}
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */
 
 #ifdef CONFIG_PM_SLEEP

Index: linux-2.6/kernel/power/swsusp.c
===================================================================

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 267

Quoted 6 lines
kernel/power/swsusp.c +3 -0
@@ -262,3 +262,127 @@ int swsusp_shrink_memory(void)
 
 	return 0;
 }
+
+/*
+ * Platforms, like ACPI, may want us to save some memory used by them during

Both commas should be removed from the line above.

Quoted 46 lines
kernel/power/swsusp.c +46 -0
+ * hibernation and to restore the contents of this memory during the subsequent
+ * resume.  The code below implements a mechanism allowing us to do that.
+ */
+
+struct nvs_page {
+	unsigned long phys_start;
+	unsigned int size;
+	void *kaddr;
+	void *data;
+	struct list_head node;
+};
+
+static LIST_HEAD(nvs_list);
+
+/**
+ *	hibernate_nvs_register - register platform NVS memory region to save
+ *	@start - physical address of the region
+ *	@size - size of the region
+ *
+ *	The NVS region need not be page-aligned (both ends) and we arrange
+ *	things so that the data from page-aligned addresses in this region will
+ *	be copied into separate RAM pages.
+ */
+int hibernate_nvs_register(unsigned long start, unsigned long size)
+{
+	struct nvs_page *entry, *next;
+
+	while (size > 0) {
+		unsigned int nr_bytes;
+
+		entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
+		if (!entry)
+			goto Error;
+
+		list_add_tail(&entry->node, &nvs_list);
+		entry->phys_start = start;
+		nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
+		entry->size = (size < nr_bytes) ? size : nr_bytes;
+
+		start += entry->size;
+		size -= entry->size;
+	}
+	return 0;
+
+ Error:
+	list_for_each_entry_safe(entry, next, &nvs_list, node) {

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 323

Quoted 10 lines
kernel/power/swsusp.c +10 -0
+		list_del(&entry->node);
+		kfree(entry);
+	}
+	return -ENOMEM;
+}
+
+/**
+ *	hibernate_nvs_free - free data pages allocated for saving NVS regions
+ *
+ *	It is allowed to call this function many times in a row.

"This function may safely be called multiple times." .... maybe just do without the comment?

Quoted 55 lines
kernel/power/swsusp.c +55 -0
+ */
+void hibernate_nvs_free(void)
+{
+	struct nvs_page *entry;
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			free_page((unsigned long)entry->data);
+			entry->data = NULL;
+			if (entry->kaddr) {
+				iounmap(entry->kaddr);
+				entry->kaddr = NULL;
+			}
+		}
+}
+
+/**
+ *	hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
+ */
+int hibernate_nvs_alloc(void)
+{
+	struct nvs_page *entry;
+
+	list_for_each_entry(entry, &nvs_list, node) {
+		entry->data = (void *)__get_free_page(GFP_KERNEL);
+		if (!entry->data) {
+			hibernate_nvs_free();
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+/**
+ *	hibernate_nvs_save - save NVS memory regions
+ */
+void hibernate_nvs_save(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Saving platform NVS memory\n");
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			entry->kaddr = ioremap(entry->phys_start, entry->size);
+			memcpy(entry->data, entry->kaddr, entry->size);
+		}
+}
+
+/**
+ *	hibernate_nvs_restore - restore NVS memory regions
+ *
+ *	This function is going to be called with interrupts disabled, so it
+ *	cannot iounmap the virtual addresses used to access the NVS region.
+ */

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 388

Quoted 10 lines
kernel/power/swsusp.c +10 -0
+void hibernate_nvs_restore(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Restoring platform NVS memory\n");
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data)
+			memcpy(entry->kaddr, entry->data, entry->size);
+}

Regards,

Nigel

_______________________________________________
linux-pm mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

#14 lore raw

Re: [PATCH 2/4] ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory (rev. 2)

In reply to Rafael J. Wysocki

Hi.

Quoted 24 lines · Rafael J. Wysocki
Quoted 4 lines · Nigel Cunningham
Quoted 3 lines · Rafael J. Wysocki
Quoted 2 lines · Nigel Cunningham
Quoted 1 line · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

[--snip--]

Quoted 18 lines
Quoted 18 lines
Quoted 17 lines

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 369

Quoted 13 lines
kernel/power/swsusp.c +13 -0
+/**
+ *	hibernate_nvs_save - save NVS memory regions
+ */
+void hibernate_nvs_save(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Saving platform NVS memory\n");
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			entry->kaddr = ioremap(entry->phys_start, entry->size);
+			memcpy(entry->data, entry->kaddr, entry->size);

Why not unmap here? (I know you're delaying the unmap until the free, but it's probably clearer to map and unmap each time the memory is accessed (ie in restore too).

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

Ah, this one is tricky. We cannot unmap from atomic context. :-)

Maybe a comment then, lest someone get the same idea in future and be as ignorant as me? :)

Well, I was considering to add one, now changed the kerneldoc comment to mention that.

Terrific. Thanks.

Quoted 161 lines

Please find updated patch below.

---
From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory (rev. 2)

According to the ACPI Specification 3.0b, Section 15.3.2, "OSPM will call the _PTS control method some time before entering a sleeping state, to allow the platform’s AML code to update this memory image before entering the sleeping state. After the system awakes from an S4 state, OSPM will restore this memory area and call the _WAK control method to enable the BIOS to reclaim its memory image." For this reason, implement a mechanism allowing us to save the NVS memory during hibernation and to restore it during the subsequent resume.

Based on a patch by Zhang Rui.

---
 drivers/acpi/sleep/main.c |   50 +++++++++++++++---
 include/linux/suspend.h   |   13 ++++
 kernel/power/swsusp.c     |  124 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 180 insertions(+), 7 deletions(-)

Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================

drivers/acpi/sleep/main.c +43 -7
@@ -321,8 +321,23 @@ void __init acpi_no_s4_hw_signature(void
 
 static int acpi_hibernation_begin(void)
 {
-	acpi_target_sleep_state = ACPI_STATE_S4;
-	return 0;
+	int error;
+
+	error = hibernate_nvs_alloc();
+	if (!error)
+		acpi_target_sleep_state = ACPI_STATE_S4;
+
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot(void)
+{
+	int error = acpi_pm_prepare();
+
+	if (!error)
+		hibernate_nvs_save();
+
+	return error;
 }
 
 static int acpi_hibernation_enter(void)
@@ -343,6 +358,12 @@ static int acpi_hibernation_enter(void)
 	return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 }
 
+static void acpi_hibernation_finish(void)
+{
+	hibernate_nvs_free();
+	acpi_pm_finish();
+}
+
 static void acpi_hibernation_leave(void)
 {
 	/*
@@ -358,6 +379,8 @@ static void acpi_hibernation_leave(void)
 			"cannot resume!\n");
 		panic("ACPI S4 hardware signature mismatch");
 	}
+	/* Restore the NVS memory area */
+	hibernate_nvs_restore();
 }
 
 static void acpi_pm_enable_gpes(void)
@@ -368,8 +391,8 @@ static void acpi_pm_enable_gpes(void)
 static struct platform_hibernation_ops acpi_hibernation_ops = {
 	.begin = acpi_hibernation_begin,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_prepare,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_prepare,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
@@ -387,8 +410,21 @@ static int acpi_hibernation_begin_old(vo
 {
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
+	if (!error) {
+		error = hibernate_nvs_alloc();
+		if (!error)
+			acpi_target_sleep_state = ACPI_STATE_S4;
+	}
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot_old(void)
+{
+	int error = acpi_pm_disable_gpes();
+
 	if (!error)
-		acpi_target_sleep_state = ACPI_STATE_S4;
+		hibernate_nvs_save();
+
 	return error;
 }
 
@@ -399,8 +435,8 @@ static int acpi_hibernation_begin_old(vo
 static struct platform_hibernation_ops acpi_hibernation_ops_old = {
 	.begin = acpi_hibernation_begin_old,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_disable_gpes,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot_old,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_disable_gpes,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,

Index: linux-2.6/include/linux/suspend.h
===================================================================

include/linux/suspend.h +13 -0
@@ -233,6 +233,11 @@ extern unsigned long get_safe_page(gfp_t
 extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
 extern int hibernate(void);
 extern bool system_entering_hibernation(void);
+extern int hibernate_nvs_register(unsigned long start, unsigned long size);
+extern int hibernate_nvs_alloc(void);
+extern void hibernate_nvs_free(void);
+extern void hibernate_nvs_save(void);
+extern void hibernate_nvs_restore(void);
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
 static inline void swsusp_set_page_free(struct page *p) {}
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */
 
 #ifdef CONFIG_PM_SLEEP

Index: linux-2.6/kernel/power/swsusp.c
===================================================================

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 267

Quoted 6 lines
kernel/power/swsusp.c +3 -0
@@ -262,3 +262,127 @@ int swsusp_shrink_memory(void)
 
 	return 0;
 }
+
+/*
+ * Platforms, like ACPI, may want us to save some memory used by them during

Both commas should be removed from the line above.

Quoted 46 lines
kernel/power/swsusp.c +46 -0
+ * hibernation and to restore the contents of this memory during the subsequent
+ * resume.  The code below implements a mechanism allowing us to do that.
+ */
+
+struct nvs_page {
+	unsigned long phys_start;
+	unsigned int size;
+	void *kaddr;
+	void *data;
+	struct list_head node;
+};
+
+static LIST_HEAD(nvs_list);
+
+/**
+ *	hibernate_nvs_register - register platform NVS memory region to save
+ *	@start - physical address of the region
+ *	@size - size of the region
+ *
+ *	The NVS region need not be page-aligned (both ends) and we arrange
+ *	things so that the data from page-aligned addresses in this region will
+ *	be copied into separate RAM pages.
+ */
+int hibernate_nvs_register(unsigned long start, unsigned long size)
+{
+	struct nvs_page *entry, *next;
+
+	while (size > 0) {
+		unsigned int nr_bytes;
+
+		entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
+		if (!entry)
+			goto Error;
+
+		list_add_tail(&entry->node, &nvs_list);
+		entry->phys_start = start;
+		nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
+		entry->size = (size < nr_bytes) ? size : nr_bytes;
+
+		start += entry->size;
+		size -= entry->size;
+	}
+	return 0;
+
+ Error:
+	list_for_each_entry_safe(entry, next, &nvs_list, node) {

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 323

Quoted 10 lines
kernel/power/swsusp.c +10 -0
+		list_del(&entry->node);
+		kfree(entry);
+	}
+	return -ENOMEM;
+}
+
+/**
+ *	hibernate_nvs_free - free data pages allocated for saving NVS regions
+ *
+ *	It is allowed to call this function many times in a row.

"This function may safely be called multiple times." .... maybe just do without the comment?

Quoted 55 lines
kernel/power/swsusp.c +55 -0
+ */
+void hibernate_nvs_free(void)
+{
+	struct nvs_page *entry;
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			free_page((unsigned long)entry->data);
+			entry->data = NULL;
+			if (entry->kaddr) {
+				iounmap(entry->kaddr);
+				entry->kaddr = NULL;
+			}
+		}
+}
+
+/**
+ *	hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
+ */
+int hibernate_nvs_alloc(void)
+{
+	struct nvs_page *entry;
+
+	list_for_each_entry(entry, &nvs_list, node) {
+		entry->data = (void *)__get_free_page(GFP_KERNEL);
+		if (!entry->data) {
+			hibernate_nvs_free();
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+/**
+ *	hibernate_nvs_save - save NVS memory regions
+ */
+void hibernate_nvs_save(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Saving platform NVS memory\n");
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			entry->kaddr = ioremap(entry->phys_start, entry->size);
+			memcpy(entry->data, entry->kaddr, entry->size);
+		}
+}
+
+/**
+ *	hibernate_nvs_restore - restore NVS memory regions
+ *
+ *	This function is going to be called with interrupts disabled, so it
+ *	cannot iounmap the virtual addresses used to access the NVS region.
+ */

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 388

Quoted 10 lines
kernel/power/swsusp.c +10 -0
+void hibernate_nvs_restore(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Restoring platform NVS memory\n");
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data)
+			memcpy(entry->kaddr, entry->data, entry->size);
+}

Regards,

Nigel

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#15 lore raw

PATCH 2/4 ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory (rev. 2)

In reply to Nigel Cunningham
Quoted 1 line · Nigel Cunningham

Hi.

Hi,

Quoted 4 lines · Rafael J. Wysocki
Quoted 3 lines · Nigel Cunningham
Quoted 2 lines · Rafael J. Wysocki
Quoted 1 line

From: Rafael J. Wysocki <[email protected]>

[--snip--]

Quoted 18 lines
Quoted 18 lines
Quoted 17 lines

Review

Quoted 13 lines
(quoted diff) +13 -0
+/**
+ *	hibernate_nvs_save - save NVS memory regions
+ */
+void hibernate_nvs_save(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Saving platform NVS memory\n");
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			entry->kaddr = ioremap(entry->phys_start, entry->size);
+			memcpy(entry->data, entry->kaddr, entry->size);

Why not unmap here? (I know you're delaying the unmap until the free, but it's probably clearer to map and unmap each time the memory is accessed (ie in restore too).

Quoted 4 lines
Quoted 1 line

Ah, this one is tricky. We cannot unmap from atomic context. :-)

Maybe a comment then, lest someone get the same idea in future and be as ignorant as me? :)

Well, I was considering to add one, now changed the kerneldoc comment to mention that.

Please find updated patch below.

---
From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory (rev. 2)

According to the ACPI Specification 3.0b, Section 15.3.2, "OSPM will call the _PTS control method some time before entering a sleeping state, to allow the platform’s AML code to update this memory image before entering the sleeping state. After the system awakes from an S4 state, OSPM will restore this memory area and call the _WAK control method to enable the BIOS to reclaim its memory image." For this reason, implement a mechanism allowing us to save the NVS memory during hibernation and to restore it during the subsequent resume.

Based on a patch by Zhang Rui.

---
 drivers/acpi/sleep/main.c |   50 +++++++++++++++---
 include/linux/suspend.h   |   13 ++++
 kernel/power/swsusp.c     |  124 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 180 insertions(+), 7 deletions(-)
drivers/acpi/sleep/main.c +43 -7
Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================
@@ -321,8 +321,23 @@ void __init acpi_no_s4_hw_signature(void
 
 static int acpi_hibernation_begin(void)
 {
-	acpi_target_sleep_state = ACPI_STATE_S4;
-	return 0;
+	int error;
+
+	error = hibernate_nvs_alloc();
+	if (!error)
+		acpi_target_sleep_state = ACPI_STATE_S4;
+
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot(void)
+{
+	int error = acpi_pm_prepare();
+
+	if (!error)
+		hibernate_nvs_save();
+
+	return error;
 }
 
 static int acpi_hibernation_enter(void)
@@ -343,6 +358,12 @@ static int acpi_hibernation_enter(void)
 	return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 }
 
+static void acpi_hibernation_finish(void)
+{
+	hibernate_nvs_free();
+	acpi_pm_finish();
+}
+
 static void acpi_hibernation_leave(void)
 {
 	/*
@@ -358,6 +379,8 @@ static void acpi_hibernation_leave(void)
 			"cannot resume!\n");
 		panic("ACPI S4 hardware signature mismatch");
 	}
+	/* Restore the NVS memory area */
+	hibernate_nvs_restore();
 }
 
 static void acpi_pm_enable_gpes(void)
@@ -368,8 +391,8 @@ static void acpi_pm_enable_gpes(void)
 static struct platform_hibernation_ops acpi_hibernation_ops = {
 	.begin = acpi_hibernation_begin,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_prepare,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_prepare,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
@@ -387,8 +410,21 @@ static int acpi_hibernation_begin_old(vo
 {
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
+	if (!error) {
+		error = hibernate_nvs_alloc();
+		if (!error)
+			acpi_target_sleep_state = ACPI_STATE_S4;
+	}
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot_old(void)
+{
+	int error = acpi_pm_disable_gpes();
+
 	if (!error)
-		acpi_target_sleep_state = ACPI_STATE_S4;
+		hibernate_nvs_save();
+
 	return error;
 }
 
@@ -399,8 +435,8 @@ static int acpi_hibernation_begin_old(vo
 static struct platform_hibernation_ops acpi_hibernation_ops_old = {
 	.begin = acpi_hibernation_begin_old,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_disable_gpes,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot_old,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_disable_gpes,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
include/linux/suspend.h +13 -0
Index: linux-2.6/include/linux/suspend.h
===================================================================
@@ -233,6 +233,11 @@ extern unsigned long get_safe_page(gfp_t
 extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
 extern int hibernate(void);
 extern bool system_entering_hibernation(void);
+extern int hibernate_nvs_register(unsigned long start, unsigned long size);
+extern int hibernate_nvs_alloc(void);
+extern void hibernate_nvs_free(void);
+extern void hibernate_nvs_save(void);
+extern void hibernate_nvs_restore(void);
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
 static inline void swsusp_set_page_free(struct page *p) {}
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */
 
 #ifdef CONFIG_PM_SLEEP
kernel/power/swsusp.c +124 -0

127 lines not on this page · raw message

_______________________________________________
linux-pm mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

#16 lore raw

Re: [PATCH 2/4] ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

In reply to Nigel Cunningham
Quoted 1 line · Nigel Cunningham

Hi.

Hi,

Quoted 217 lines · Rafael J. Wysocki
Quoted 216 lines

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

According to the ACPI Specification 3.0b, Section 15.3.2, "OSPM will call the _PTS control method some time before entering a sleeping state, to allow the platform’s AML code to update this memory image before entering the sleeping state. After the system awakes from an S4 state, OSPM will restore this memory area and call the _WAK control method to enable the BIOS to reclaim its memory image." For this reason, implement a mechanism allowing us to save the NVS memory during hibernation and to restore it during the subsequent resume.

Based on a patch by Zhang Rui.

---
 drivers/acpi/sleep/main.c |   50 ++++++++++++++++---
 include/linux/suspend.h   |   13 ++++
 kernel/power/swsusp.c     |  121 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 177 insertions(+), 7 deletions(-)

Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================

drivers/acpi/sleep/main.c +43 -7
@@ -321,8 +321,23 @@ void __init acpi_no_s4_hw_signature(void
 
 static int acpi_hibernation_begin(void)
 {
-	acpi_target_sleep_state = ACPI_STATE_S4;
-	return 0;
+	int error;
+
+	error = hibernate_nvs_alloc();
+	if (!error)
+		acpi_target_sleep_state = ACPI_STATE_S4;
+
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot(void)
+{
+	int error = acpi_pm_prepare();
+
+	if (!error)
+		hibernate_nvs_save();
+
+	return error;
 }
 
 static int acpi_hibernation_enter(void)
@@ -343,6 +358,12 @@ static int acpi_hibernation_enter(void)
 	return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 }
 
+static void acpi_hibernation_finish(void)
+{
+	hibernate_nvs_free();
+	acpi_pm_finish();
+}
+
 static void acpi_hibernation_leave(void)
 {
 	/*
@@ -358,6 +379,8 @@ static void acpi_hibernation_leave(void)
 			"cannot resume!\n");
 		panic("ACPI S4 hardware signature mismatch");
 	}
+	/* Restore the NVS memory area */
+	hibernate_nvs_restore();
 }
 
 static void acpi_pm_enable_gpes(void)
@@ -368,8 +391,8 @@ static void acpi_pm_enable_gpes(void)
 static struct platform_hibernation_ops acpi_hibernation_ops = {
 	.begin = acpi_hibernation_begin,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_prepare,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_prepare,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
@@ -387,8 +410,21 @@ static int acpi_hibernation_begin_old(vo
 {
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
+	if (!error) {
+		error = hibernate_nvs_alloc();
+		if (!error)
+			acpi_target_sleep_state = ACPI_STATE_S4;
+	}
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot_old(void)
+{
+	int error = acpi_pm_disable_gpes();
+
 	if (!error)
-		acpi_target_sleep_state = ACPI_STATE_S4;
+		hibernate_nvs_save();
+
 	return error;
 }
 
@@ -399,8 +435,8 @@ static int acpi_hibernation_begin_old(vo
 static struct platform_hibernation_ops acpi_hibernation_ops_old = {
 	.begin = acpi_hibernation_begin_old,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_disable_gpes,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot_old,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_disable_gpes,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,

Index: linux-2.6/include/linux/suspend.h
===================================================================

include/linux/suspend.h +13 -0
@@ -233,6 +233,11 @@ extern unsigned long get_safe_page(gfp_t
 extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
 extern int hibernate(void);
 extern bool system_entering_hibernation(void);
+extern int hibernate_nvs_register(unsigned long start, unsigned long size);
+extern int hibernate_nvs_alloc(void);
+extern void hibernate_nvs_free(void);
+extern void hibernate_nvs_save(void);
+extern void hibernate_nvs_restore(void);
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
 static inline void swsusp_set_page_free(struct page *p) {}
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */
 
 #ifdef CONFIG_PM_SLEEP

Index: linux-2.6/kernel/power/swsusp.c
===================================================================

kernel/power/swsusp.c +56 -0
@@ -262,3 +262,124 @@ int swsusp_shrink_memory(void)
 
 	return 0;
 }
+
+/*
+ * Platforms, like ACPI, may want us to save some memory used by them during
+ * hibernation and to restore the contents of this memory during the subsequent
+ * resume.  The code below implements a mechanism allowing us to do that.
+ */
+
+struct nvs_page {
+	unsigned long phys_start;
+	unsigned int size;
+	void *kaddr;
+	void *data;
+	struct list_head node;
+};
+
+static LIST_HEAD(nvs_list);
+
+/**
+ *	hibernate_nvs_register - register platform NVS memory region to save
+ *	@start - physical address of the region
+ *	@size - size of the region
+ *
+ *	The NVS region need not be page-aligned (both ends) and we arrange
+ *	things so that the data from page-aligned addresses in this region will
+ *	be copied into separate RAM pages.
+ */
+int hibernate_nvs_register(unsigned long start, unsigned long size)
+{
+	struct nvs_page *entry, *next;
+
+	while (size > 0) {
+		unsigned int nr_bytes;
+
+		entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
+		if (!entry)
+			goto Error;
+
+		list_add_tail(&entry->node, &nvs_list);
+		entry->phys_start = start;
+		nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
+		entry->size = (size < nr_bytes) ? size : nr_bytes;
+
+		start += entry->size;
+		size -= entry->size;
+	}
+	return 0;
+
+ Error:
+	list_for_each_entry_safe(entry, next, &nvs_list, node) {
+		list_del(&entry->node);
+		kfree(entry);
+	}
+	return -ENOMEM;
+}
+
+/**
Quoted 8 lines

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 324

Quoted 4 lines
kernel/power/swsusp.c +4 -0
+ *	hibernate_nvs_free - free data pages allocated for saving NVS regions
+ *
+ *	It is allowed to call this function many times in a row.
+ */

If this one is safe for multiple calls, should nvs_alloc be safe for multiple calls too? Or, conversely, is something funny going on such that hibernate_nvs_free needs to be safe for multiple calls?

I thought it would be necessary to call that additionally from platform_end() for the error path in which platform_pre_snapshot() fails, but that doesn't seem to be the case. It doesn't hurt to keep it in this form, though, IMO.

Quoted 41 lines
Quoted 41 lines
kernel/power/swsusp.c +41 -0
+void hibernate_nvs_free(void)
+{
+	struct nvs_page *entry;
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			free_page((unsigned long)entry->data);
+			entry->data = NULL;
+			if (entry->kaddr) {
+				iounmap(entry->kaddr);
+				entry->kaddr = NULL;
+			}
+		}
+}
+
+/**
+ *	hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
+ */
+int hibernate_nvs_alloc(void)
+{
+	struct nvs_page *entry;
+
+	list_for_each_entry(entry, &nvs_list, node) {
+		entry->data = (void *)__get_free_page(GFP_KERNEL);
+		if (!entry->data) {
+			hibernate_nvs_free();
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+/**
+ *	hibernate_nvs_save - save NVS memory regions
+ */
+void hibernate_nvs_save(void)
+{
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Saving platform NVS memory\n");
+
Quoted 8 lines

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 369

Quoted 4 lines
kernel/power/swsusp.c +4 -0
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data) {
+			entry->kaddr = ioremap(entry->phys_start, entry->size);
+			memcpy(entry->data, entry->kaddr, entry->size);

Why not unmap here? (I know you're delaying the unmap until the free, but it's probably clearer to map and unmap each time the memory is accessed (ie in restore too).

Ah, this one is tricky. We cannot unmap from atomic context. :-)

Quoted 8 lines
Quoted 8 lines
kernel/power/swsusp.c +8 -0
+		}
+}
+
+/**
+ *	hibernate_nvs_restore - restore NVS memory regions
+ */
+void hibernate_nvs_restore(void)
+{
Quoted 8 lines

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 385

Quoted 8 lines
kernel/power/swsusp.c +8 -0
+	struct nvs_page *entry;
+
+	printk(KERN_INFO "PM: Restoring platform NVS memory\n");
+
+	list_for_each_entry(entry, &nvs_list, node)
+		if (entry->data)
+			memcpy(entry->kaddr, entry->data, entry->size);
+}

Thanks,
Rafael
_______________________________________________
linux-pm mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

#17 lore raw

Re: [PATCH 2/4] ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

In reply to Rafael J. Wysocki

Hi!

Quoted 27 lines

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

According to the ACPI Specification 3.0b, Section 15.3.2, "OSPM will call the _PTS control method some time before entering a sleeping state, to allow the platform???s AML code to update this memory image before entering the sleeping state. After the system awakes from an S4 state, OSPM will restore this memory area and call the _WAK control method to enable the BIOS to reclaim its memory image." For this reason, implement a mechanism allowing us to save the NVS memory during hibernation and to restore it during the subsequent resume.

Based on a patch by Zhang Rui.

---
 drivers/acpi/sleep/main.c |   50 ++++++++++++++++---
 include/linux/suspend.h   |   13 ++++
 kernel/power/swsusp.c     |  121 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 177 insertions(+), 7 deletions(-)

drivers/acpi/sleep/main.c static int acpi_hibernation_begin_old(vo · line 419

Quoted 10 lines
drivers/acpi/sleep/main.c +7 -0
@@ -387,8 +410,21 @@ static int acpi_hibernation_begin_old(vo
 {
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
+	if (!error) {
+		error = hibernate_nvs_alloc();
+		if (!error)
+			acpi_target_sleep_state = ACPI_STATE_S4;
+	}
+	return error;
+}

You can chain this without nesting...

If sleep_prepare() succeeds but nvs_alloc() fails, you should undo sleep_prepare here, right?

Quoted 6 lines
include/linux/suspend.h +0 -0
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
 static inline void swsusp_set_page_free(struct page *p) {}
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }

include/linux/suspend.h static inline void swsusp_unset_page_fre · line 257

Quoted 10 lines
include/linux/suspend.h +8 -0
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */

Can someone call these for !hibernation case? Should they fake success if they do call them?

Quoted 12 lines

Index: linux-2.6/kernel/power/swsusp.c
===================================================================

kernel/power/swsusp.c +4 -0
@@ -262,3 +262,124 @@ int swsusp_shrink_memory(void)
 
 	return 0;
 }
+
+/*
+ * Platforms, like ACPI, may want us to save some memory used by them during
+ * hibernation and to restore the contents of this memory during the subsequent

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 278

Quoted 10 lines
kernel/power/swsusp.c +10 -0
+ * resume.  The code below implements a mechanism allowing us to do that.
+ */
+
+struct nvs_page {
+	unsigned long phys_start;
+	unsigned int size;
+	void *kaddr;
+	void *data;
+	struct list_head node;
+};

Maybe separate nvs.c file would be good? I believe this _is_ acpi specific, altrough in theory it may not be.

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#18 lore raw

Re: [linux-pm] [PATCH 2/4] ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

In reply to Rafael J. Wysocki

Hi!

Quoted 27 lines

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

According to the ACPI Specification 3.0b, Section 15.3.2, "OSPM will call the _PTS control method some time before entering a sleeping state, to allow the platform???s AML code to update this memory image before entering the sleeping state. After the system awakes from an S4 state, OSPM will restore this memory area and call the _WAK control method to enable the BIOS to reclaim its memory image." For this reason, implement a mechanism allowing us to save the NVS memory during hibernation and to restore it during the subsequent resume.

Based on a patch by Zhang Rui.

---
 drivers/acpi/sleep/main.c |   50 ++++++++++++++++---
 include/linux/suspend.h   |   13 ++++
 kernel/power/swsusp.c     |  121 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 177 insertions(+), 7 deletions(-)

drivers/acpi/sleep/main.c static int acpi_hibernation_begin_old(vo · line 419

Quoted 10 lines
drivers/acpi/sleep/main.c +7 -0
@@ -387,8 +410,21 @@ static int acpi_hibernation_begin_old(vo
 {
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
+	if (!error) {
+		error = hibernate_nvs_alloc();
+		if (!error)
+			acpi_target_sleep_state = ACPI_STATE_S4;
+	}
+	return error;
+}

You can chain this without nesting...

If sleep_prepare() succeeds but nvs_alloc() fails, you should undo sleep_prepare here, right?

Quoted 6 lines
include/linux/suspend.h +0 -0
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
 static inline void swsusp_set_page_free(struct page *p) {}
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }

include/linux/suspend.h static inline void swsusp_unset_page_fre · line 257

Quoted 10 lines
include/linux/suspend.h +8 -0
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */

Can someone call these for !hibernation case? Should they fake success if they do call them?

Quoted 12 lines

Index: linux-2.6/kernel/power/swsusp.c
===================================================================

kernel/power/swsusp.c +4 -0
@@ -262,3 +262,124 @@ int swsusp_shrink_memory(void)
 
 	return 0;
 }
+
+/*
+ * Platforms, like ACPI, may want us to save some memory used by them during
+ * hibernation and to restore the contents of this memory during the subsequent

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 278

Quoted 10 lines
kernel/power/swsusp.c +10 -0
+ * resume.  The code below implements a mechanism allowing us to do that.
+ */
+
+struct nvs_page {
+	unsigned long phys_start;
+	unsigned int size;
+	void *kaddr;
+	void *data;
+	struct list_head node;
+};

Maybe separate nvs.c file would be good? I believe this _is_ acpi specific, altrough in theory it may not be.

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#19 lore raw

Re: [linux-pm] [PATCH 2/4] ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

Quoted 42 lines · Pavel Machek

Hi!

Quoted 27 lines

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

According to the ACPI Specification 3.0b, Section 15.3.2, "OSPM will call the _PTS control method some time before entering a sleeping state, to allow the platform???s AML code to update this memory image before entering the sleeping state. After the system awakes from an S4 state, OSPM will restore this memory area and call the _WAK control method to enable the BIOS to reclaim its memory image." For this reason, implement a mechanism allowing us to save the NVS memory during hibernation and to restore it during the subsequent resume.

Based on a patch by Zhang Rui.

---
 drivers/acpi/sleep/main.c |   50 ++++++++++++++++---
 include/linux/suspend.h   |   13 ++++
 kernel/power/swsusp.c     |  121 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 177 insertions(+), 7 deletions(-)

drivers/acpi/sleep/main.c static int acpi_hibernation_begin_old(vo · line 419

Quoted 10 lines
drivers/acpi/sleep/main.c +7 -0
@@ -387,8 +410,21 @@ static int acpi_hibernation_begin_old(vo
 {
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
+	if (!error) {
+		error = hibernate_nvs_alloc();
+		if (!error)
+			acpi_target_sleep_state = ACPI_STATE_S4;
+	}
+	return error;
+}

You can chain this without nesting...

Quoted 2 lines

If sleep_prepare() succeeds but nvs_alloc() fails, you should undo sleep_prepare here, right?

No. acpi_hibernation_finish() will undo it anyway.

Quoted 10 lines
Quoted 10 lines
include/linux/suspend.h +3 -0
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
 static inline void swsusp_set_page_free(struct page *p) {}
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
Quoted 8 lines

include/linux/suspend.h static inline void swsusp_unset_page_fre · line 257

Quoted 6 lines
include/linux/suspend.h +5 -0
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */

Can someone call these for !hibernation case?

Not at the moment, but why not to add them?

Quoted 20 lines

Should they fake success if they do call them?

Quoted 17 lines

Index: linux-2.6/kernel/power/swsusp.c
===================================================================

kernel/power/swsusp.c +9 -0
@@ -262,3 +262,124 @@ int swsusp_shrink_memory(void)
 
 	return 0;
 }
+
+/*
+ * Platforms, like ACPI, may want us to save some memory used by them during
+ * hibernation and to restore the contents of this memory during the subsequent
+ * resume.  The code below implements a mechanism allowing us to do that.
+ */
+
+struct nvs_page {
+	unsigned long phys_start;
Quoted 8 lines

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 278

Quoted 5 lines
kernel/power/swsusp.c +5 -0
+	unsigned int size;
+	void *kaddr;
+	void *data;
+	struct list_head node;
+};

Maybe separate nvs.c file would be good? I believe this _is_ acpi specific, altrough in theory it may not be.

No, I don't think so. The patch is correct IMO.

Thanks,
Rafael

#20 lore raw

Re: [PATCH 2/4] ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

Hi!

Quoted 19 lines
Quoted 10 lines
Quoted 10 lines

#else /* CONFIG_HIBERNATION */
static inline int swsusp_page_is_forbidden(struct page *p) { return 0; } static inline void swsusp_set_page_free(struct page *p) {}

include/linux/suspend.h +3 -0
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
Quoted 8 lines

include/linux/suspend.h static inline void swsusp_unset_page_fre · line 257

Quoted 6 lines
include/linux/suspend.h +5 -0
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */

Can someone call these for !hibernation case?

Quoted 1 line

Not at the moment, but why not to add them?

Well, someone may call them, expecting them to save / restore nvs, except that they will not be there. I'd prefer inlines not to exist (so mistake is caught at compile time) or them to contain BUG(), so that mistake is obvious.
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#21 lore raw

Re: [linux-pm] [PATCH 2/4] ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

In reply to Rafael J. Wysocki

Hi!

Quoted 19 lines
Quoted 10 lines
Quoted 10 lines

#else /* CONFIG_HIBERNATION */
static inline int swsusp_page_is_forbidden(struct page *p) { return 0; } static inline void swsusp_set_page_free(struct page *p) {}

include/linux/suspend.h +3 -0
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
Quoted 8 lines

include/linux/suspend.h static inline void swsusp_unset_page_fre · line 257

Quoted 6 lines
include/linux/suspend.h +5 -0
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */

Can someone call these for !hibernation case?

Quoted 1 line

Not at the moment, but why not to add them?

Well, someone may call them, expecting them to save / restore nvs, except that they will not be there. I'd prefer inlines not to exist (so mistake is caught at compile time) or them to contain BUG(), so that mistake is obvious.
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#22 lore raw

Re: [PATCH 2/4] ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

In reply to Pavel Machek
Quoted 42 lines · Pavel Machek

Hi!

Quoted 27 lines

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

According to the ACPI Specification 3.0b, Section 15.3.2, "OSPM will call the _PTS control method some time before entering a sleeping state, to allow the platform???s AML code to update this memory image before entering the sleeping state. After the system awakes from an S4 state, OSPM will restore this memory area and call the _WAK control method to enable the BIOS to reclaim its memory image." For this reason, implement a mechanism allowing us to save the NVS memory during hibernation and to restore it during the subsequent resume.

Based on a patch by Zhang Rui.

---
 drivers/acpi/sleep/main.c |   50 ++++++++++++++++---
 include/linux/suspend.h   |   13 ++++
 kernel/power/swsusp.c     |  121 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 177 insertions(+), 7 deletions(-)

drivers/acpi/sleep/main.c static int acpi_hibernation_begin_old(vo · line 419

Quoted 10 lines
drivers/acpi/sleep/main.c +7 -0
@@ -387,8 +410,21 @@ static int acpi_hibernation_begin_old(vo
 {
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
+	if (!error) {
+		error = hibernate_nvs_alloc();
+		if (!error)
+			acpi_target_sleep_state = ACPI_STATE_S4;
+	}
+	return error;
+}

You can chain this without nesting...

Quoted 2 lines

If sleep_prepare() succeeds but nvs_alloc() fails, you should undo sleep_prepare here, right?

No. acpi_hibernation_finish() will undo it anyway.

Quoted 10 lines
Quoted 10 lines
include/linux/suspend.h +3 -0
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
 static inline void swsusp_set_page_free(struct page *p) {}
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
Quoted 8 lines

include/linux/suspend.h static inline void swsusp_unset_page_fre · line 257

Quoted 6 lines
include/linux/suspend.h +5 -0
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */

Can someone call these for !hibernation case?

Not at the moment, but why not to add them?

Quoted 20 lines

Should they fake success if they do call them?

Quoted 17 lines

Index: linux-2.6/kernel/power/swsusp.c
===================================================================

kernel/power/swsusp.c +9 -0
@@ -262,3 +262,124 @@ int swsusp_shrink_memory(void)
 
 	return 0;
 }
+
+/*
+ * Platforms, like ACPI, may want us to save some memory used by them during
+ * hibernation and to restore the contents of this memory during the subsequent
+ * resume.  The code below implements a mechanism allowing us to do that.
+ */
+
+struct nvs_page {
+	unsigned long phys_start;
Quoted 8 lines

kernel/power/swsusp.c int swsusp_shrink_memory(void) · line 278

Quoted 5 lines
kernel/power/swsusp.c +5 -0
+	unsigned int size;
+	void *kaddr;
+	void *data;
+	struct list_head node;
+};

Maybe separate nvs.c file would be good? I believe this _is_ acpi specific, altrough in theory it may not be.

No, I don't think so. The patch is correct IMO.

Thanks,
Rafael

#23 lore raw

PATCH 2/4 ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Add a mechanism to save/restore ACPI NVS memory

According to the ACPI Specification 3.0b, Section 15.3.2, "OSPM will call the _PTS control method some time before entering a sleeping state, to allow the platform’s AML code to update this memory image before entering the sleeping state. After the system awakes from an S4 state, OSPM will restore this memory area and call the _WAK control method to enable the BIOS to reclaim its memory image." For this reason, implement a mechanism allowing us to save the NVS memory during hibernation and to restore it during the subsequent resume.

Based on a patch by Zhang Rui.

---
 drivers/acpi/sleep/main.c |   50 ++++++++++++++++---
 include/linux/suspend.h   |   13 ++++
 kernel/power/swsusp.c     |  121 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 177 insertions(+), 7 deletions(-)
drivers/acpi/sleep/main.c +43 -7
Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================
@@ -321,8 +321,23 @@ void __init acpi_no_s4_hw_signature(void
 
 static int acpi_hibernation_begin(void)
 {
-	acpi_target_sleep_state = ACPI_STATE_S4;
-	return 0;
+	int error;
+
+	error = hibernate_nvs_alloc();
+	if (!error)
+		acpi_target_sleep_state = ACPI_STATE_S4;
+
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot(void)
+{
+	int error = acpi_pm_prepare();
+
+	if (!error)
+		hibernate_nvs_save();
+
+	return error;
 }
 
 static int acpi_hibernation_enter(void)
@@ -343,6 +358,12 @@ static int acpi_hibernation_enter(void)
 	return ACPI_SUCCESS(status) ? 0 : -EFAULT;
 }
 
+static void acpi_hibernation_finish(void)
+{
+	hibernate_nvs_free();
+	acpi_pm_finish();
+}
+
 static void acpi_hibernation_leave(void)
 {
 	/*
@@ -358,6 +379,8 @@ static void acpi_hibernation_leave(void)
 			"cannot resume!\n");
 		panic("ACPI S4 hardware signature mismatch");
 	}
+	/* Restore the NVS memory area */
+	hibernate_nvs_restore();
 }
 
 static void acpi_pm_enable_gpes(void)
@@ -368,8 +391,8 @@ static void acpi_pm_enable_gpes(void)
 static struct platform_hibernation_ops acpi_hibernation_ops = {
 	.begin = acpi_hibernation_begin,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_prepare,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_prepare,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
@@ -387,8 +410,21 @@ static int acpi_hibernation_begin_old(vo
 {
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
+	if (!error) {
+		error = hibernate_nvs_alloc();
+		if (!error)
+			acpi_target_sleep_state = ACPI_STATE_S4;
+	}
+	return error;
+}
+
+static int acpi_hibernation_pre_snapshot_old(void)
+{
+	int error = acpi_pm_disable_gpes();
+
 	if (!error)
-		acpi_target_sleep_state = ACPI_STATE_S4;
+		hibernate_nvs_save();
+
 	return error;
 }
 
@@ -399,8 +435,8 @@ static int acpi_hibernation_begin_old(vo
 static struct platform_hibernation_ops acpi_hibernation_ops_old = {
 	.begin = acpi_hibernation_begin_old,
 	.end = acpi_pm_end,
-	.pre_snapshot = acpi_pm_disable_gpes,
-	.finish = acpi_pm_finish,
+	.pre_snapshot = acpi_hibernation_pre_snapshot_old,
+	.finish = acpi_hibernation_finish,
 	.prepare = acpi_pm_disable_gpes,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
include/linux/suspend.h +13 -0
Index: linux-2.6/include/linux/suspend.h
===================================================================
@@ -233,6 +233,11 @@ extern unsigned long get_safe_page(gfp_t
 extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
 extern int hibernate(void);
 extern bool system_entering_hibernation(void);
+extern int hibernate_nvs_register(unsigned long start, unsigned long size);
+extern int hibernate_nvs_alloc(void);
+extern void hibernate_nvs_free(void);
+extern void hibernate_nvs_save(void);
+extern void hibernate_nvs_restore(void);
 #else /* CONFIG_HIBERNATION */
 static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
 static inline void swsusp_set_page_free(struct page *p) {}
@@ -241,6 +246,14 @@ static inline void swsusp_unset_page_fre
 static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
 static inline int hibernate(void) { return -ENOSYS; }
 static inline bool system_entering_hibernation(void) { return false; }
+static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
+{
+	return 0;
+}
+static inline int hibernate_nvs_alloc(void) { return 0; }
+static inline void hibernate_nvs_free(void) {}
+static inline void hibernate_nvs_save(void) {}
+static inline void hibernate_nvs_restore(void) {}
 #endif /* CONFIG_HIBERNATION */
 
 #ifdef CONFIG_PM_SLEEP
kernel/power/swsusp.c +121 -0

124 lines not on this page · raw message

_______________________________________________
linux-pm mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

#24 lore raw

PATCH 3/4 x86 hibernate: Mark ACPI NVS memory region at startup

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

x86 hibernate: Mark ACPI NVS memory region at startup

Introduce new initcall for marking the ACPI NVS memory at startup, so that it can be saved/restore during hibernation/resume.

Based on a patch by Zhang Rui.

---
 arch/x86/kernel/e820.c |   25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)
arch/x86/kernel/e820.c +25 -0
Index: linux-2.6/arch/x86/kernel/e820.c
===================================================================
@@ -20,6 +20,7 @@
 #include <linux/pfn.h>
 #include <linux/suspend.h>
 #include <linux/firmware-map.h>
+#include <linux/efi.h>
 
 #include <asm/pgtable.h>
 #include <asm/page.h>
@@ -665,6 +666,30 @@ void __init e820_mark_nosave_regions(uns
 }
 #endif
 
+#ifdef CONFIG_HIBERNATION
+/**
+ * Mark ACPI NVS memory region, so that we can save/restore it during
+ * hibernation and the subsequent resume.
+ */
+static int __init e820_mark_nvs_memory(void)
+{
+	int i;
+
+	if (efi_enabled)
+		return 0;
+
+	for (i = 0; i < e820.nr_map; i++) {
+		struct e820entry *ei = &e820.map[i];
+
+		if (ei->type == E820_NVS)
+			hibernate_nvs_register(ei->addr, ei->size);
+	}
+
+	return 0;
+}
+core_initcall(e820_mark_nvs_memory);
+#endif
+
 /*
  * Early reserved memory areas.
  */
#25 lore raw

Re: [PATCH 3/4] x86 hibernate: Mark ACPI NVS memory region at startup

Quoted 20 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

x86 hibernate: Mark ACPI NVS memory region at startup

Introduce new initcall for marking the ACPI NVS memory at startup, so that it can be saved/restore during hibernation/resume.

Based on a patch by Zhang Rui.

---
 arch/x86/kernel/e820.c |   25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

Index: linux-2.6/arch/x86/kernel/e820.c
===================================================================

arch/x86/kernel/e820.c line 26

Quoted 7 lines
arch/x86/kernel/e820.c +1 -0
@@ -20,6 +20,7 @@
 #include <linux/pfn.h>
 #include <linux/suspend.h>
 #include <linux/firmware-map.h>
+#include <linux/efi.h>
 
 #include <asm/pgtable.h>
 #include <asm/page.h>

Unrelated chunk?

Quoted 5 lines
arch/x86/kernel/e820.c +1 -0
@@ -665,6 +666,30 @@ void __init e820_mark_nosave_regions(uns
 }
 #endif
 
+#ifdef CONFIG_HIBERNATION

arch/x86/kernel/e820.c void __init e820_mark_nosave_regions(uns · line 679

Quoted 10 lines
arch/x86/kernel/e820.c +10 -0
+/**
+ * Mark ACPI NVS memory region, so that we can save/restore it during
+ * hibernation and the subsequent resume.
+ */
+static int __init e820_mark_nvs_memory(void)
+{
+	int i;
+
+	if (efi_enabled)
+		return 0;

Aha, not unrelated... why is that? EFI does not use acpi?

arch/x86/kernel/e820.c void __init e820_mark_nosave_regions(uns · line 685

Quoted 5 lines
arch/x86/kernel/e820.c +5 -0
+	for (i = 0; i < e820.nr_map; i++) {
+		struct e820entry *ei = &e820.map[i];
+
+		if (ei->type == E820_NVS)
+			hibernate_nvs_register(ei->addr, ei->size);

can nvs_register fail? (OOM?)

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#26 lore raw

Re: [linux-pm] [PATCH 3/4] x86 hibernate: Mark ACPI NVS memory region at startup

In reply to Rafael J. Wysocki
Quoted 20 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

x86 hibernate: Mark ACPI NVS memory region at startup

Introduce new initcall for marking the ACPI NVS memory at startup, so that it can be saved/restore during hibernation/resume.

Based on a patch by Zhang Rui.

---
 arch/x86/kernel/e820.c |   25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

Index: linux-2.6/arch/x86/kernel/e820.c
===================================================================

arch/x86/kernel/e820.c line 26

Quoted 7 lines
arch/x86/kernel/e820.c +1 -0
@@ -20,6 +20,7 @@
 #include <linux/pfn.h>
 #include <linux/suspend.h>
 #include <linux/firmware-map.h>
+#include <linux/efi.h>
 
 #include <asm/pgtable.h>
 #include <asm/page.h>

Unrelated chunk?

Quoted 5 lines
arch/x86/kernel/e820.c +1 -0
@@ -665,6 +666,30 @@ void __init e820_mark_nosave_regions(uns
 }
 #endif
 
+#ifdef CONFIG_HIBERNATION

arch/x86/kernel/e820.c void __init e820_mark_nosave_regions(uns · line 679

Quoted 10 lines
arch/x86/kernel/e820.c +10 -0
+/**
+ * Mark ACPI NVS memory region, so that we can save/restore it during
+ * hibernation and the subsequent resume.
+ */
+static int __init e820_mark_nvs_memory(void)
+{
+	int i;
+
+	if (efi_enabled)
+		return 0;

Aha, not unrelated... why is that? EFI does not use acpi?

arch/x86/kernel/e820.c void __init e820_mark_nosave_regions(uns · line 685

Quoted 5 lines
arch/x86/kernel/e820.c +5 -0
+	for (i = 0; i < e820.nr_map; i++) {
+		struct e820entry *ei = &e820.map[i];
+
+		if (ei->type == E820_NVS)
+			hibernate_nvs_register(ei->addr, ei->size);

can nvs_register fail? (OOM?)

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#27 lore raw

Re: [linux-pm] [PATCH 3/4] x86 hibernate: Mark ACPI NVS memory region at startup

Quoted 35 lines · Pavel Machek
Quoted 20 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

x86 hibernate: Mark ACPI NVS memory region at startup

Introduce new initcall for marking the ACPI NVS memory at startup, so that it can be saved/restore during hibernation/resume.

Based on a patch by Zhang Rui.

---
 arch/x86/kernel/e820.c |   25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

Index: linux-2.6/arch/x86/kernel/e820.c
===================================================================

arch/x86/kernel/e820.c line 26

Quoted 7 lines
arch/x86/kernel/e820.c +1 -0
@@ -20,6 +20,7 @@
 #include <linux/pfn.h>
 #include <linux/suspend.h>
 #include <linux/firmware-map.h>
+#include <linux/efi.h>
 
 #include <asm/pgtable.h>
 #include <asm/page.h>

Unrelated chunk?

Quoted 4 lines
arch/x86/kernel/e820.c +0 -0
@@ -665,6 +666,30 @@ void __init e820_mark_nosave_regions(uns
 }
 #endif
 
Quoted 13 lines

arch/x86/kernel/e820.c void __init e820_mark_nosave_regions(uns · line 679

Quoted 11 lines
arch/x86/kernel/e820.c +11 -0
+#ifdef CONFIG_HIBERNATION
+/**
+ * Mark ACPI NVS memory region, so that we can save/restore it during
+ * hibernation and the subsequent resume.
+ */
+static int __init e820_mark_nvs_memory(void)
+{
+	int i;
+
+	if (efi_enabled)
+		return 0;

Aha, not unrelated... why is that? EFI does not use acpi?

With EFI we are not supposed to do that. Rui knows the details. Rui?

Quoted 7 lines

arch/x86/kernel/e820.c void __init e820_mark_nosave_regions(uns · line 685

Quoted 5 lines
arch/x86/kernel/e820.c +5 -0
+	for (i = 0; i < e820.nr_map; i++) {
+		struct e820entry *ei = &e820.map[i];
+
+		if (ei->type == E820_NVS)
+			hibernate_nvs_register(ei->addr, ei->size);

can nvs_register fail? (OOM?)

It can, in which case it will do the cleanup.

Thanks,
Rafael

#28 lore raw

Re: [PATCH 3/4] x86 hibernate: Mark ACPI NVS memory region at startup

Hi, sorry for the delay,

Quoted 50 lines · Rafael J. Wysocki
Quoted 35 lines · Pavel Machek
Quoted 20 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

x86 hibernate: Mark ACPI NVS memory region at startup

Introduce new initcall for marking the ACPI NVS memory at startup, so that it can be saved/restore during hibernation/resume.

Based on a patch by Zhang Rui.

---
 arch/x86/kernel/e820.c |   25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

Index: linux-2.6/arch/x86/kernel/e820.c
===================================================================

arch/x86/kernel/e820.c line 26

Quoted 7 lines
arch/x86/kernel/e820.c +1 -0
@@ -20,6 +20,7 @@
 #include <linux/pfn.h>
 #include <linux/suspend.h>
 #include <linux/firmware-map.h>
+#include <linux/efi.h>
 
 #include <asm/pgtable.h>
 #include <asm/page.h>

Unrelated chunk?

Quoted 4 lines
arch/x86/kernel/e820.c +0 -0
@@ -665,6 +666,30 @@ void __init e820_mark_nosave_regions(uns
 }
 #endif
 
Quoted 13 lines

arch/x86/kernel/e820.c void __init e820_mark_nosave_regions(uns · line 679

Quoted 11 lines
arch/x86/kernel/e820.c +11 -0
+#ifdef CONFIG_HIBERNATION
+/**
+ * Mark ACPI NVS memory region, so that we can save/restore it during
+ * hibernation and the subsequent resume.
+ */
+static int __init e820_mark_nvs_memory(void)
+{
+	int i;
+
+	if (efi_enabled)
+		return 0;

Aha, not unrelated... why is that? EFI does not use acpi?

Quoted 2 lines

With EFI we are not supposed to do that. Rui knows the details. Rui?

well,
about EFI nvs memory, I only get
"EfiACPIMemoryNVS: The OS and loader must preserve this memory range in the working and ACPI S1–S3 states." in the ACPI spec 3.0b. whether we should save/restore this piece of memory is not clear. I'd prefer not to touch it currently.

thanks,
rui

Quoted 8 lines
Quoted 7 lines

arch/x86/kernel/e820.c void __init e820_mark_nosave_regions(uns · line 685

Quoted 5 lines
arch/x86/kernel/e820.c +5 -0
+	for (i = 0; i < e820.nr_map; i++) {
+		struct e820entry *ei = &e820.map[i];
+
+		if (ei->type == E820_NVS)
+			hibernate_nvs_register(ei->addr, ei->size);

can nvs_register fail? (OOM?)

Quoted 4 lines

It can, in which case it will do the cleanup.

Thanks,
Rafael

_______________________________________________
linux-pm mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/linux-pm

#29 lore raw

Re: [linux-pm] [PATCH 3/4] x86 hibernate: Mark ACPI NVS memory region at startup

In reply to Rafael J. Wysocki

Hi, sorry for the delay,

Quoted 50 lines · Rafael J. Wysocki
Quoted 35 lines · Pavel Machek
Quoted 20 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

x86 hibernate: Mark ACPI NVS memory region at startup

Introduce new initcall for marking the ACPI NVS memory at startup, so that it can be saved/restore during hibernation/resume.

Based on a patch by Zhang Rui.

---
 arch/x86/kernel/e820.c |   25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

Index: linux-2.6/arch/x86/kernel/e820.c
===================================================================

arch/x86/kernel/e820.c line 26

Quoted 7 lines
arch/x86/kernel/e820.c +1 -0
@@ -20,6 +20,7 @@
 #include <linux/pfn.h>
 #include <linux/suspend.h>
 #include <linux/firmware-map.h>
+#include <linux/efi.h>
 
 #include <asm/pgtable.h>
 #include <asm/page.h>

Unrelated chunk?

Quoted 4 lines
arch/x86/kernel/e820.c +0 -0
@@ -665,6 +666,30 @@ void __init e820_mark_nosave_regions(uns
 }
 #endif
 
Quoted 13 lines

arch/x86/kernel/e820.c void __init e820_mark_nosave_regions(uns · line 679

Quoted 11 lines
arch/x86/kernel/e820.c +11 -0
+#ifdef CONFIG_HIBERNATION
+/**
+ * Mark ACPI NVS memory region, so that we can save/restore it during
+ * hibernation and the subsequent resume.
+ */
+static int __init e820_mark_nvs_memory(void)
+{
+	int i;
+
+	if (efi_enabled)
+		return 0;

Aha, not unrelated... why is that? EFI does not use acpi?

Quoted 2 lines

With EFI we are not supposed to do that. Rui knows the details. Rui?

well,
about EFI nvs memory, I only get
"EfiACPIMemoryNVS: The OS and loader must preserve this memory range in the working and ACPI S1–S3 states." in the ACPI spec 3.0b. whether we should save/restore this piece of memory is not clear. I'd prefer not to touch it currently.

thanks,
rui

Quoted 8 lines
Quoted 7 lines

arch/x86/kernel/e820.c void __init e820_mark_nosave_regions(uns · line 685

Quoted 5 lines
arch/x86/kernel/e820.c +5 -0
+	for (i = 0; i < e820.nr_map; i++) {
+		struct e820entry *ei = &e820.map[i];
+
+		if (ei->type == E820_NVS)
+			hibernate_nvs_register(ei->addr, ei->size);

can nvs_register fail? (OOM?)

Quoted 4 lines

It can, in which case it will do the cleanup.

Thanks,
Rafael

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#30 lore raw

Re: [PATCH 3/4] x86 hibernate: Mark ACPI NVS memory region at startup

Hi!

Quoted 9 lines
Quoted 9 lines
Quoted 8 lines

arch/x86/kernel/e820.c void __init e820_mark_nosave_regions(uns · line 679

Quoted 6 lines
arch/x86/kernel/e820.c +6 -0
+static int __init e820_mark_nvs_memory(void)
+{
+	int i;
+
+	if (efi_enabled)
+		return 0;

Aha, not unrelated... why is that? EFI does not use acpi?

Quoted 8 lines
Quoted 2 lines

With EFI we are not supposed to do that. Rui knows the details. Rui?

well,
about EFI nvs memory, I only get
"EfiACPIMemoryNVS: The OS and loader must preserve this memory range in the working and ACPI S1???S3 states." in the ACPI spec 3.0b. whether we should save/restore this piece of memory is not clear. I'd prefer not to touch it currently.

EFI is a bootloader. Why should we change our runtime behaviour depending on bootloader?

EFI should be still compatible with normal ACPI, right? ...like you should be able to boot the same kernel with the same ACPI BIOS using EFI or EFI w/ legacy emulation.

So special-casing it here does not seem right.

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#31 lore raw

Re: [linux-pm] [PATCH 3/4] x86 hibernate: Mark ACPI NVS memory region at startup

In reply to Zhang Rui

Hi!

Quoted 9 lines
Quoted 9 lines
Quoted 8 lines

arch/x86/kernel/e820.c void __init e820_mark_nosave_regions(uns · line 679

Quoted 6 lines
arch/x86/kernel/e820.c +6 -0
+static int __init e820_mark_nvs_memory(void)
+{
+	int i;
+
+	if (efi_enabled)
+		return 0;

Aha, not unrelated... why is that? EFI does not use acpi?

Quoted 8 lines
Quoted 2 lines

With EFI we are not supposed to do that. Rui knows the details. Rui?

well,
about EFI nvs memory, I only get
"EfiACPIMemoryNVS: The OS and loader must preserve this memory range in the working and ACPI S1???S3 states." in the ACPI spec 3.0b. whether we should save/restore this piece of memory is not clear. I'd prefer not to touch it currently.

EFI is a bootloader. Why should we change our runtime behaviour depending on bootloader?

EFI should be still compatible with normal ACPI, right? ...like you should be able to boot the same kernel with the same ACPI BIOS using EFI or EFI w/ legacy emulation.

So special-casing it here does not seem right.

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#32 lore raw

Re: [linux-pm] [PATCH 3/4] x86 hibernate: Mark ACPI NVS memory region at startup

Quoted 18 lines · Pavel Machek

Hi!

Quoted 16 lines
Quoted 11 lines
Quoted 8 lines

arch/x86/kernel/e820.c void __init e820_mark_nosave_regions(uns · line 679

Quoted 6 lines
arch/x86/kernel/e820.c +6 -0
+static int __init e820_mark_nvs_memory(void)
+{
+	int i;
+
+	if (efi_enabled)
+		return 0;

Aha, not unrelated... why is that? EFI does not use acpi?

With EFI we are not supposed to do that. Rui knows the details. Rui?

well,
about EFI nvs memory, I only get
"EfiACPIMemoryNVS: The OS and loader must preserve this memory range in the working and ACPI S1???S3 states." in the ACPI spec 3.0b. whether we should save/restore this piece of memory is not clear.

Well. it's clear enough. Section 15.3.2 evidently refers to E820 and the EFI's GetMemoryMap() on equal footing.

Quoted 10 lines
Quoted 1 line

I'd prefer not to touch it currently.

EFI is a bootloader. Why should we change our runtime behaviour depending on bootloader?

EFI should be still compatible with normal ACPI, right? ...like you should be able to boot the same kernel with the same ACPI BIOS using EFI or EFI w/ legacy emulation.

So special-casing it here does not seem right.

I agree.

I'm going to send an updated patch to Len.

Thanks,
Rafael

#33 lore raw

Re: [PATCH 3/4] x86 hibernate: Mark ACPI NVS memory region at startup

In reply to Pavel Machek
Quoted 18 lines · Pavel Machek

Hi!

Quoted 16 lines
Quoted 11 lines
Quoted 8 lines

arch/x86/kernel/e820.c void __init e820_mark_nosave_regions(uns · line 679

Quoted 6 lines
arch/x86/kernel/e820.c +6 -0
+static int __init e820_mark_nvs_memory(void)
+{
+	int i;
+
+	if (efi_enabled)
+		return 0;

Aha, not unrelated... why is that? EFI does not use acpi?

With EFI we are not supposed to do that. Rui knows the details. Rui?

well,
about EFI nvs memory, I only get
"EfiACPIMemoryNVS: The OS and loader must preserve this memory range in the working and ACPI S1???S3 states." in the ACPI spec 3.0b. whether we should save/restore this piece of memory is not clear.

Well. it's clear enough. Section 15.3.2 evidently refers to E820 and the EFI's GetMemoryMap() on equal footing.

Quoted 10 lines
Quoted 1 line

I'd prefer not to touch it currently.

EFI is a bootloader. Why should we change our runtime behaviour depending on bootloader?

EFI should be still compatible with normal ACPI, right? ...like you should be able to boot the same kernel with the same ACPI BIOS using EFI or EFI w/ legacy emulation.

So special-casing it here does not seem right.

I agree.

I'm going to send an updated patch to Len.

Thanks,
Rafael

#34 lore raw

Re: [PATCH 3/4] x86 hibernate: Mark ACPI NVS memory region at startup

In reply to Pavel Machek
Quoted 35 lines · Pavel Machek
Quoted 20 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

x86 hibernate: Mark ACPI NVS memory region at startup

Introduce new initcall for marking the ACPI NVS memory at startup, so that it can be saved/restore during hibernation/resume.

Based on a patch by Zhang Rui.

---
 arch/x86/kernel/e820.c |   25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

Index: linux-2.6/arch/x86/kernel/e820.c
===================================================================

arch/x86/kernel/e820.c line 26

Quoted 7 lines
arch/x86/kernel/e820.c +1 -0
@@ -20,6 +20,7 @@
 #include <linux/pfn.h>
 #include <linux/suspend.h>
 #include <linux/firmware-map.h>
+#include <linux/efi.h>
 
 #include <asm/pgtable.h>
 #include <asm/page.h>

Unrelated chunk?

Quoted 4 lines
arch/x86/kernel/e820.c +0 -0
@@ -665,6 +666,30 @@ void __init e820_mark_nosave_regions(uns
 }
 #endif
 
Quoted 13 lines

arch/x86/kernel/e820.c void __init e820_mark_nosave_regions(uns · line 679

Quoted 11 lines
arch/x86/kernel/e820.c +11 -0
+#ifdef CONFIG_HIBERNATION
+/**
+ * Mark ACPI NVS memory region, so that we can save/restore it during
+ * hibernation and the subsequent resume.
+ */
+static int __init e820_mark_nvs_memory(void)
+{
+	int i;
+
+	if (efi_enabled)
+		return 0;

Aha, not unrelated... why is that? EFI does not use acpi?

With EFI we are not supposed to do that. Rui knows the details. Rui?

Quoted 7 lines

arch/x86/kernel/e820.c void __init e820_mark_nosave_regions(uns · line 685

Quoted 5 lines
arch/x86/kernel/e820.c +5 -0
+	for (i = 0; i < e820.nr_map; i++) {
+		struct e820entry *ei = &e820.map[i];
+
+		if (ei->type == E820_NVS)
+			hibernate_nvs_register(ei->addr, ei->size);

can nvs_register fail? (OOM?)

It can, in which case it will do the cleanup.

Thanks,
Rafael

#35 lore raw

PATCH 3/4 x86 hibernate: Mark ACPI NVS memory region at startup

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

x86 hibernate: Mark ACPI NVS memory region at startup

Introduce new initcall for marking the ACPI NVS memory at startup, so that it can be saved/restore during hibernation/resume.

Based on a patch by Zhang Rui.

---
 arch/x86/kernel/e820.c |   25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)
arch/x86/kernel/e820.c +25 -0
Index: linux-2.6/arch/x86/kernel/e820.c
===================================================================
@@ -20,6 +20,7 @@
 #include <linux/pfn.h>
 #include <linux/suspend.h>
 #include <linux/firmware-map.h>
+#include <linux/efi.h>
 
 #include <asm/pgtable.h>
 #include <asm/page.h>
@@ -665,6 +666,30 @@ void __init e820_mark_nosave_regions(uns
 }
 #endif
 
+#ifdef CONFIG_HIBERNATION
+/**
+ * Mark ACPI NVS memory region, so that we can save/restore it during
+ * hibernation and the subsequent resume.
+ */
+static int __init e820_mark_nvs_memory(void)
+{
+	int i;
+
+	if (efi_enabled)
+		return 0;
+
+	for (i = 0; i < e820.nr_map; i++) {
+		struct e820entry *ei = &e820.map[i];
+
+		if (ei->type == E820_NVS)
+			hibernate_nvs_register(ei->addr, ei->size);
+	}
+
+	return 0;
+}
+core_initcall(e820_mark_nvs_memory);
+#endif
+
 /*
  * Early reserved memory areas.
  */
#36 lore raw

PATCH 4/4 ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs

On some machines it may be necessary to disable the saving/restoring of the ACPI NVS memory region during hibernation/resume. For this purpose, introduce new ACPI kernel command line option acpi_sleep=s4_nonvs.

Based on a patch by Zhang Rui.

---
 Documentation/kernel-parameters.txt |    5 ++++-
 arch/x86/kernel/acpi/sleep.c        |    2 ++
 drivers/acpi/sleep/main.c           |   19 +++++++++++++++++--
 include/linux/acpi.h                |    1 +
 4 files changed, 24 insertions(+), 3 deletions(-)
Documentation/kernel-parameters.txt +4 -1
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
@@ -149,7 +149,8 @@ and is between 256 and 4096 characters. 
 			default: 0
 
 	acpi_sleep=	[HW,ACPI] Sleep options
-			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, old_ordering }
+			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig,
+				  old_ordering, s4_nonvs }
 			See Documentation/power/video.txt for s3_bios and s3_mode.
 			s3_beep is for debugging; it makes the PC's speaker beep
 			as soon as the kernel's real-mode entry point is called.
@@ -159,6 +160,8 @@ and is between 256 and 4096 characters. 
 			control method, wrt putting devices into low power
 			states, to be enforced (the ACPI 2.0 ordering of _PTS is
 			used by default).
+			s4_nonvs prevents the kernel from saving/restoring the
+			ACPI NVS memory during hibernation.
 
 	acpi_sci=	[HW,ACPI] ACPI System Control Interrupt trigger mode
 			Format: { level | edge | high | low }
arch/x86/kernel/acpi/sleep.c +2 -0
Index: linux-2.6/arch/x86/kernel/acpi/sleep.c
===================================================================
@@ -161,6 +161,8 @@ static int __init acpi_sleep_setup(char 
 #endif
 		if (strncmp(str, "old_ordering", 12) == 0)
 			acpi_old_suspend_ordering();
+		if (strncmp(str, "s4_nonvs", 13) == 0)
+			acpi_s4_no_nvs();
 		str = strchr(str, ',');
 		if (str != NULL)
 			str += strspn(str, ", \t");
drivers/acpi/sleep/main.c +17 -2
Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================
@@ -59,6 +59,20 @@ void __init acpi_old_suspend_ordering(vo
 	old_suspend_ordering = true;
 }
 
+/*
+ * The ACPI specification wants us to save NVS memory regions during hibernation
+ * and to restore them during the subsequent resume.  However, it is not certain
+ * if this mechanism is going to work on all machines, so we allow the user to
+ * disable this mechanism using the 'acpi_sleep=s4_nonvs' kernel command line
+ * option.
+ */
+static bool s4_no_nvs;
+
+void __init acpi_s4_no_nvs(void)
+{
+	s4_no_nvs = true;
+}
+
 /**
  *	acpi_pm_disable_gpes - Disable the GPEs.
  */
@@ -323,7 +337,7 @@ static int acpi_hibernation_begin(void)
 {
 	int error;
 
-	error = hibernate_nvs_alloc();
+	error = s4_no_nvs ? 0 : hibernate_nvs_alloc();
 	if (!error)
 		acpi_target_sleep_state = ACPI_STATE_S4;
 
@@ -411,7 +425,8 @@ static int acpi_hibernation_begin_old(vo
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
 	if (!error) {
-		error = hibernate_nvs_alloc();
+		if (!s4_no_nvs)
+			error = hibernate_nvs_alloc();
 		if (!error)
 			acpi_target_sleep_state = ACPI_STATE_S4;
 	}
include/linux/acpi.h +1 -0
Index: linux-2.6/include/linux/acpi.h
===================================================================
@@ -238,6 +238,7 @@ int acpi_check_mem_region(resource_size_
 #ifdef CONFIG_PM_SLEEP
 void __init acpi_no_s4_hw_signature(void);
 void __init acpi_old_suspend_ordering(void);
+void __init acpi_s4_no_nvs(void);
 #endif /* CONFIG_PM_SLEEP */
 #else	/* CONFIG_ACPI */
 
Acked-by on v1 4/4
#37 lore raw

Re: [PATCH 4/4] ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs

Hi.

Quoted 48 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs

On some machines it may be necessary to disable the saving/restoring of the ACPI NVS memory region during hibernation/resume. For this purpose, introduce new ACPI kernel command line option acpi_sleep=s4_nonvs.

Based on a patch by Zhang Rui.

---
 Documentation/kernel-parameters.txt |    5 ++++-
 arch/x86/kernel/acpi/sleep.c        |    2 ++
 drivers/acpi/sleep/main.c           |   19 +++++++++++++++++--
 include/linux/acpi.h                |    1 +
 4 files changed, 24 insertions(+), 3 deletions(-)

Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================

Documentation/kernel-parameters.txt +4 -1
@@ -149,7 +149,8 @@ and is between 256 and 4096 characters. 
 			default: 0
 
 	acpi_sleep=	[HW,ACPI] Sleep options
-			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, old_ordering }
+			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig,
+				  old_ordering, s4_nonvs }
 			See Documentation/power/video.txt for s3_bios and s3_mode.
 			s3_beep is for debugging; it makes the PC's speaker beep
 			as soon as the kernel's real-mode entry point is called.
@@ -159,6 +160,8 @@ and is between 256 and 4096 characters. 
 			control method, wrt putting devices into low power
 			states, to be enforced (the ACPI 2.0 ordering of _PTS is
 			used by default).
+			s4_nonvs prevents the kernel from saving/restoring the
+			ACPI NVS memory during hibernation.
 
 	acpi_sci=	[HW,ACPI] ACPI System Control Interrupt trigger mode
 			Format: { level | edge | high | low }

Index: linux-2.6/arch/x86/kernel/acpi/sleep.c
===================================================================

arch/x86/kernel/acpi/sleep.c static int __init acpi_sleep_setup(char · line 164

Quoted 4 lines
arch/x86/kernel/acpi/sleep.c +1 -0
@@ -161,6 +161,8 @@ static int __init acpi_sleep_setup(char 
 #endif
 		if (strncmp(str, "old_ordering", 12) == 0)
 			acpi_old_suspend_ordering();
+		if (strncmp(str, "s4_nonvs", 13) == 0)

s/13/8/

Quoted 53 lines
arch/x86/kernel/acpi/sleep.c +1 -0
+			acpi_s4_no_nvs();
 		str = strchr(str, ',');
 		if (str != NULL)
 			str += strspn(str, ", \t");

Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================

drivers/acpi/sleep/main.c +17 -2
@@ -59,6 +59,20 @@ void __init acpi_old_suspend_ordering(vo
 	old_suspend_ordering = true;
 }
 
+/*
+ * The ACPI specification wants us to save NVS memory regions during hibernation
+ * and to restore them during the subsequent resume.  However, it is not certain
+ * if this mechanism is going to work on all machines, so we allow the user to
+ * disable this mechanism using the 'acpi_sleep=s4_nonvs' kernel command line
+ * option.
+ */
+static bool s4_no_nvs;
+
+void __init acpi_s4_no_nvs(void)
+{
+	s4_no_nvs = true;
+}
+
 /**
  *	acpi_pm_disable_gpes - Disable the GPEs.
  */
@@ -323,7 +337,7 @@ static int acpi_hibernation_begin(void)
 {
 	int error;
 
-	error = hibernate_nvs_alloc();
+	error = s4_no_nvs ? 0 : hibernate_nvs_alloc();
 	if (!error)
 		acpi_target_sleep_state = ACPI_STATE_S4;
 
@@ -411,7 +425,8 @@ static int acpi_hibernation_begin_old(vo
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
 	if (!error) {
-		error = hibernate_nvs_alloc();
+		if (!s4_no_nvs)
+			error = hibernate_nvs_alloc();
 		if (!error)
 			acpi_target_sleep_state = ACPI_STATE_S4;
 	}

Index: linux-2.6/include/linux/acpi.h
===================================================================

include/linux/acpi.h int acpi_check_mem_region(resource_size_ · line 243

Quoted 6 lines
include/linux/acpi.h +1 -0
@@ -238,6 +238,7 @@ int acpi_check_mem_region(resource_size_
 #ifdef CONFIG_PM_SLEEP
 void __init acpi_no_s4_hw_signature(void);
 void __init acpi_old_suspend_ordering(void);
+void __init acpi_s4_no_nvs(void);
 #endif /* CONFIG_PM_SLEEP */
 #else	/* CONFIG_ACPI */

Apart from the above,

Acked-by on v1 4/4
#38 lore raw

Re: [linux-pm] [PATCH 4/4] ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs

In reply to Rafael J. Wysocki

Hi.

Quoted 48 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs

On some machines it may be necessary to disable the saving/restoring of the ACPI NVS memory region during hibernation/resume. For this purpose, introduce new ACPI kernel command line option acpi_sleep=s4_nonvs.

Based on a patch by Zhang Rui.

---
 Documentation/kernel-parameters.txt |    5 ++++-
 arch/x86/kernel/acpi/sleep.c        |    2 ++
 drivers/acpi/sleep/main.c           |   19 +++++++++++++++++--
 include/linux/acpi.h                |    1 +
 4 files changed, 24 insertions(+), 3 deletions(-)

Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================

Documentation/kernel-parameters.txt +4 -1
@@ -149,7 +149,8 @@ and is between 256 and 4096 characters. 
 			default: 0
 
 	acpi_sleep=	[HW,ACPI] Sleep options
-			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, old_ordering }
+			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig,
+				  old_ordering, s4_nonvs }
 			See Documentation/power/video.txt for s3_bios and s3_mode.
 			s3_beep is for debugging; it makes the PC's speaker beep
 			as soon as the kernel's real-mode entry point is called.
@@ -159,6 +160,8 @@ and is between 256 and 4096 characters. 
 			control method, wrt putting devices into low power
 			states, to be enforced (the ACPI 2.0 ordering of _PTS is
 			used by default).
+			s4_nonvs prevents the kernel from saving/restoring the
+			ACPI NVS memory during hibernation.
 
 	acpi_sci=	[HW,ACPI] ACPI System Control Interrupt trigger mode
 			Format: { level | edge | high | low }

Index: linux-2.6/arch/x86/kernel/acpi/sleep.c
===================================================================

arch/x86/kernel/acpi/sleep.c static int __init acpi_sleep_setup(char · line 164

Quoted 4 lines
arch/x86/kernel/acpi/sleep.c +1 -0
@@ -161,6 +161,8 @@ static int __init acpi_sleep_setup(char 
 #endif
 		if (strncmp(str, "old_ordering", 12) == 0)
 			acpi_old_suspend_ordering();
+		if (strncmp(str, "s4_nonvs", 13) == 0)

s/13/8/

Quoted 53 lines
arch/x86/kernel/acpi/sleep.c +1 -0
+			acpi_s4_no_nvs();
 		str = strchr(str, ',');
 		if (str != NULL)
 			str += strspn(str, ", \t");

Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================

drivers/acpi/sleep/main.c +17 -2
@@ -59,6 +59,20 @@ void __init acpi_old_suspend_ordering(vo
 	old_suspend_ordering = true;
 }
 
+/*
+ * The ACPI specification wants us to save NVS memory regions during hibernation
+ * and to restore them during the subsequent resume.  However, it is not certain
+ * if this mechanism is going to work on all machines, so we allow the user to
+ * disable this mechanism using the 'acpi_sleep=s4_nonvs' kernel command line
+ * option.
+ */
+static bool s4_no_nvs;
+
+void __init acpi_s4_no_nvs(void)
+{
+	s4_no_nvs = true;
+}
+
 /**
  *	acpi_pm_disable_gpes - Disable the GPEs.
  */
@@ -323,7 +337,7 @@ static int acpi_hibernation_begin(void)
 {
 	int error;
 
-	error = hibernate_nvs_alloc();
+	error = s4_no_nvs ? 0 : hibernate_nvs_alloc();
 	if (!error)
 		acpi_target_sleep_state = ACPI_STATE_S4;
 
@@ -411,7 +425,8 @@ static int acpi_hibernation_begin_old(vo
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
 	if (!error) {
-		error = hibernate_nvs_alloc();
+		if (!s4_no_nvs)
+			error = hibernate_nvs_alloc();
 		if (!error)
 			acpi_target_sleep_state = ACPI_STATE_S4;
 	}

Index: linux-2.6/include/linux/acpi.h
===================================================================

include/linux/acpi.h int acpi_check_mem_region(resource_size_ · line 243

Quoted 6 lines
include/linux/acpi.h +1 -0
@@ -238,6 +238,7 @@ int acpi_check_mem_region(resource_size_
 #ifdef CONFIG_PM_SLEEP
 void __init acpi_no_s4_hw_signature(void);
 void __init acpi_old_suspend_ordering(void);
+void __init acpi_s4_no_nvs(void);
 #endif /* CONFIG_PM_SLEEP */
 #else	/* CONFIG_ACPI */

Apart from the above,

#39 lore raw

Re: [linux-pm] [PATCH 4/4] ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs

Quoted 44 lines · Nigel Cunningham

Hi.

Quoted 41 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs

On some machines it may be necessary to disable the saving/restoring of the ACPI NVS memory region during hibernation/resume. For this purpose, introduce new ACPI kernel command line option acpi_sleep=s4_nonvs.

Based on a patch by Zhang Rui.

---
 Documentation/kernel-parameters.txt |    5 ++++-
 arch/x86/kernel/acpi/sleep.c        |    2 ++
 drivers/acpi/sleep/main.c           |   19 +++++++++++++++++--
 include/linux/acpi.h                |    1 +
 4 files changed, 24 insertions(+), 3 deletions(-)

Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================

Documentation/kernel-parameters.txt +4 -1
@@ -149,7 +149,8 @@ and is between 256 and 4096 characters. 
 			default: 0
 
 	acpi_sleep=	[HW,ACPI] Sleep options
-			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, old_ordering }
+			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig,
+				  old_ordering, s4_nonvs }
 			See Documentation/power/video.txt for s3_bios and s3_mode.
 			s3_beep is for debugging; it makes the PC's speaker beep
 			as soon as the kernel's real-mode entry point is called.
@@ -159,6 +160,8 @@ and is between 256 and 4096 characters. 
 			control method, wrt putting devices into low power
 			states, to be enforced (the ACPI 2.0 ordering of _PTS is
 			used by default).
+			s4_nonvs prevents the kernel from saving/restoring the
+			ACPI NVS memory during hibernation.
 
Quoted 13 lines

arch/x86/kernel/acpi/sleep.c static int __init acpi_sleep_setup(char · line 164

Quoted 11 lines
Documentation/kernel-parameters.txt +0 -0
 	acpi_sci=	[HW,ACPI] ACPI System Control Interrupt trigger mode
 			Format: { level | edge | high | low }

Index: linux-2.6/arch/x86/kernel/acpi/sleep.c
===================================================================

arch/x86/kernel/acpi/sleep.c +1 -0
@@ -161,6 +161,8 @@ static int __init acpi_sleep_setup(char 
 #endif
 		if (strncmp(str, "old_ordering", 12) == 0)
 			acpi_old_suspend_ordering();
+		if (strncmp(str, "s4_nonvs", 13) == 0)

s/13/8/

Sure, thanks for catching that (it was a different string in the original patch).

Quoted 62 lines
Quoted 53 lines
arch/x86/kernel/acpi/sleep.c +1 -0
+			acpi_s4_no_nvs();
 		str = strchr(str, ',');
 		if (str != NULL)
 			str += strspn(str, ", \t");

Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================

drivers/acpi/sleep/main.c +17 -2
@@ -59,6 +59,20 @@ void __init acpi_old_suspend_ordering(vo
 	old_suspend_ordering = true;
 }
 
+/*
+ * The ACPI specification wants us to save NVS memory regions during hibernation
+ * and to restore them during the subsequent resume.  However, it is not certain
+ * if this mechanism is going to work on all machines, so we allow the user to
+ * disable this mechanism using the 'acpi_sleep=s4_nonvs' kernel command line
+ * option.
+ */
+static bool s4_no_nvs;
+
+void __init acpi_s4_no_nvs(void)
+{
+	s4_no_nvs = true;
+}
+
 /**
  *	acpi_pm_disable_gpes - Disable the GPEs.
  */
@@ -323,7 +337,7 @@ static int acpi_hibernation_begin(void)
 {
 	int error;
 
-	error = hibernate_nvs_alloc();
+	error = s4_no_nvs ? 0 : hibernate_nvs_alloc();
 	if (!error)
 		acpi_target_sleep_state = ACPI_STATE_S4;
 
@@ -411,7 +425,8 @@ static int acpi_hibernation_begin_old(vo
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
 	if (!error) {
-		error = hibernate_nvs_alloc();
+		if (!s4_no_nvs)
+			error = hibernate_nvs_alloc();
 		if (!error)
 			acpi_target_sleep_state = ACPI_STATE_S4;
 	}

Index: linux-2.6/include/linux/acpi.h
===================================================================

include/linux/acpi.h int acpi_check_mem_region(resource_size_ · line 243

Quoted 6 lines
include/linux/acpi.h +1 -0
@@ -238,6 +238,7 @@ int acpi_check_mem_region(resource_size_
 #ifdef CONFIG_PM_SLEEP
 void __init acpi_no_s4_hw_signature(void);
 void __init acpi_old_suspend_ordering(void);
+void __init acpi_s4_no_nvs(void);
 #endif /* CONFIG_PM_SLEEP */
 #else	/* CONFIG_ACPI */

Apart from the above,

Quoted 1 line

Thanks,
Rafael

#40 lore raw

PATCH 4/4 ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs (rev. 2)

Quoted 59 lines · Rafael J. Wysocki
Quoted 44 lines · Nigel Cunningham

Hi.

Quoted 41 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs

On some machines it may be necessary to disable the saving/restoring of the ACPI NVS memory region during hibernation/resume. For this purpose, introduce new ACPI kernel command line option acpi_sleep=s4_nonvs.

Based on a patch by Zhang Rui.

---
 Documentation/kernel-parameters.txt |    5 ++++-
 arch/x86/kernel/acpi/sleep.c        |    2 ++
 drivers/acpi/sleep/main.c           |   19 +++++++++++++++++--
 include/linux/acpi.h                |    1 +
 4 files changed, 24 insertions(+), 3 deletions(-)

Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================

Documentation/kernel-parameters.txt +4 -1
@@ -149,7 +149,8 @@ and is between 256 and 4096 characters. 
 			default: 0
 
 	acpi_sleep=	[HW,ACPI] Sleep options
-			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, old_ordering }
+			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig,
+				  old_ordering, s4_nonvs }
 			See Documentation/power/video.txt for s3_bios and s3_mode.
 			s3_beep is for debugging; it makes the PC's speaker beep
 			as soon as the kernel's real-mode entry point is called.
@@ -159,6 +160,8 @@ and is between 256 and 4096 characters. 
 			control method, wrt putting devices into low power
 			states, to be enforced (the ACPI 2.0 ordering of _PTS is
 			used by default).
+			s4_nonvs prevents the kernel from saving/restoring the
+			ACPI NVS memory during hibernation.
 
Quoted 13 lines

arch/x86/kernel/acpi/sleep.c static int __init acpi_sleep_setup(char · line 164

Quoted 11 lines
Documentation/kernel-parameters.txt +0 -0
 	acpi_sci=	[HW,ACPI] ACPI System Control Interrupt trigger mode
 			Format: { level | edge | high | low }

Index: linux-2.6/arch/x86/kernel/acpi/sleep.c
===================================================================

arch/x86/kernel/acpi/sleep.c +1 -0
@@ -161,6 +161,8 @@ static int __init acpi_sleep_setup(char 
 #endif
 		if (strncmp(str, "old_ordering", 12) == 0)
 			acpi_old_suspend_ordering();
+		if (strncmp(str, "s4_nonvs", 13) == 0)

s/13/8/

Quoted 2 lines

Sure, thanks for catching that (it was a different string in the original patch).

Fixed version below, already with your ACK. ;-)

---
From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs (rev. 2)

On some machines it may be necessary to disable the saving/restoring of the ACPI NVS memory region during hibernation/resume. For this purpose, introduce new ACPI kernel command line option acpi_sleep=s4_nonvs.

Based on a patch by Zhang Rui.

---
 Documentation/kernel-parameters.txt |    5 ++++-
 arch/x86/kernel/acpi/sleep.c        |    2 ++
 drivers/acpi/sleep/main.c           |   19 +++++++++++++++++--
 include/linux/acpi.h                |    1 +
 4 files changed, 24 insertions(+), 3 deletions(-)
Documentation/kernel-parameters.txt +4 -1
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
@@ -149,7 +149,8 @@ and is between 256 and 4096 characters. 
 			default: 0
 
 	acpi_sleep=	[HW,ACPI] Sleep options
-			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, old_ordering }
+			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig,
+				  old_ordering, s4_nonvs }
 			See Documentation/power/video.txt for s3_bios and s3_mode.
 			s3_beep is for debugging; it makes the PC's speaker beep
 			as soon as the kernel's real-mode entry point is called.
@@ -159,6 +160,8 @@ and is between 256 and 4096 characters. 
 			control method, wrt putting devices into low power
 			states, to be enforced (the ACPI 2.0 ordering of _PTS is
 			used by default).
+			s4_nonvs prevents the kernel from saving/restoring the
+			ACPI NVS memory during hibernation.
 
 	acpi_sci=	[HW,ACPI] ACPI System Control Interrupt trigger mode
 			Format: { level | edge | high | low }
arch/x86/kernel/acpi/sleep.c +2 -0
Index: linux-2.6/arch/x86/kernel/acpi/sleep.c
===================================================================
@@ -161,6 +161,8 @@ static int __init acpi_sleep_setup(char 
 #endif
 		if (strncmp(str, "old_ordering", 12) == 0)
 			acpi_old_suspend_ordering();
+		if (strncmp(str, "s4_nonvs", 8) == 0)
+			acpi_s4_no_nvs();
 		str = strchr(str, ',');
 		if (str != NULL)
 			str += strspn(str, ", \t");
drivers/acpi/sleep/main.c +17 -2
Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================
@@ -59,6 +59,20 @@ void __init acpi_old_suspend_ordering(vo
 	old_suspend_ordering = true;
 }
 
+/*
+ * The ACPI specification wants us to save NVS memory regions during hibernation
+ * and to restore them during the subsequent resume.  However, it is not certain
+ * if this mechanism is going to work on all machines, so we allow the user to
+ * disable this mechanism using the 'acpi_sleep=s4_nonvs' kernel command line
+ * option.
+ */
+static bool s4_no_nvs;
+
+void __init acpi_s4_no_nvs(void)
+{
+	s4_no_nvs = true;
+}
+
 /**
  *	acpi_pm_disable_gpes - Disable the GPEs.
  */
@@ -323,7 +337,7 @@ static int acpi_hibernation_begin(void)
 {
 	int error;
 
-	error = hibernate_nvs_alloc();
+	error = s4_no_nvs ? 0 : hibernate_nvs_alloc();
 	if (!error)
 		acpi_target_sleep_state = ACPI_STATE_S4;
 
@@ -411,7 +425,8 @@ static int acpi_hibernation_begin_old(vo
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
 	if (!error) {
-		error = hibernate_nvs_alloc();
+		if (!s4_no_nvs)
+			error = hibernate_nvs_alloc();
 		if (!error)
 			acpi_target_sleep_state = ACPI_STATE_S4;
 	}
include/linux/acpi.h +1 -0
Index: linux-2.6/include/linux/acpi.h
===================================================================
@@ -238,6 +238,7 @@ int acpi_check_mem_region(resource_size_
 #ifdef CONFIG_PM_SLEEP
 void __init acpi_no_s4_hw_signature(void);
 void __init acpi_old_suspend_ordering(void);
+void __init acpi_s4_no_nvs(void);
 #endif /* CONFIG_PM_SLEEP */
 #else	/* CONFIG_ACPI */
 
#41 lore raw

Re: [PATCH 4/4] ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs (rev. 2)

Quoted 75 lines · Rafael J. Wysocki
Quoted 59 lines · Rafael J. Wysocki
Quoted 44 lines · Nigel Cunningham

Hi.

Quoted 41 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs

On some machines it may be necessary to disable the saving/restoring of the ACPI NVS memory region during hibernation/resume. For this purpose, introduce new ACPI kernel command line option acpi_sleep=s4_nonvs.

Based on a patch by Zhang Rui.

---
 Documentation/kernel-parameters.txt |    5 ++++-
 arch/x86/kernel/acpi/sleep.c        |    2 ++
 drivers/acpi/sleep/main.c           |   19 +++++++++++++++++--
 include/linux/acpi.h                |    1 +
 4 files changed, 24 insertions(+), 3 deletions(-)

Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================

Documentation/kernel-parameters.txt +4 -1
@@ -149,7 +149,8 @@ and is between 256 and 4096 characters. 
 			default: 0
 
 	acpi_sleep=	[HW,ACPI] Sleep options
-			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, old_ordering }
+			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig,
+				  old_ordering, s4_nonvs }
 			See Documentation/power/video.txt for s3_bios and s3_mode.
 			s3_beep is for debugging; it makes the PC's speaker beep
 			as soon as the kernel's real-mode entry point is called.
@@ -159,6 +160,8 @@ and is between 256 and 4096 characters. 
 			control method, wrt putting devices into low power
 			states, to be enforced (the ACPI 2.0 ordering of _PTS is
 			used by default).
+			s4_nonvs prevents the kernel from saving/restoring the
+			ACPI NVS memory during hibernation.
 
Quoted 13 lines

arch/x86/kernel/acpi/sleep.c static int __init acpi_sleep_setup(char · line 164

Quoted 11 lines
Documentation/kernel-parameters.txt +0 -0
 	acpi_sci=	[HW,ACPI] ACPI System Control Interrupt trigger mode
 			Format: { level | edge | high | low }

Index: linux-2.6/arch/x86/kernel/acpi/sleep.c
===================================================================

arch/x86/kernel/acpi/sleep.c +1 -0
@@ -161,6 +161,8 @@ static int __init acpi_sleep_setup(char 
 #endif
 		if (strncmp(str, "old_ordering", 12) == 0)
 			acpi_old_suspend_ordering();
+		if (strncmp(str, "s4_nonvs", 13) == 0)

s/13/8/

Quoted 2 lines

Sure, thanks for catching that (it was a different string in the original patch).

Fixed version below, already with your ACK. ;-)

---
From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs (rev. 2)

On some machines it may be necessary to disable the saving/restoring of the ACPI NVS memory region during hibernation/resume. For this purpose, introduce new ACPI kernel command line option acpi_sleep=s4_nonvs.

Quoted 5 lines

Based on a patch by Zhang Rui.

ACK.
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#42 lore raw

Re: [linux-pm] [PATCH 4/4] ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs (rev. 2)

In reply to Rafael J. Wysocki
Quoted 75 lines · Rafael J. Wysocki
Quoted 59 lines · Rafael J. Wysocki
Quoted 44 lines · Nigel Cunningham

Hi.

Quoted 41 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs

On some machines it may be necessary to disable the saving/restoring of the ACPI NVS memory region during hibernation/resume. For this purpose, introduce new ACPI kernel command line option acpi_sleep=s4_nonvs.

Based on a patch by Zhang Rui.

---
 Documentation/kernel-parameters.txt |    5 ++++-
 arch/x86/kernel/acpi/sleep.c        |    2 ++
 drivers/acpi/sleep/main.c           |   19 +++++++++++++++++--
 include/linux/acpi.h                |    1 +
 4 files changed, 24 insertions(+), 3 deletions(-)

Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================

Documentation/kernel-parameters.txt +4 -1
@@ -149,7 +149,8 @@ and is between 256 and 4096 characters. 
 			default: 0
 
 	acpi_sleep=	[HW,ACPI] Sleep options
-			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, old_ordering }
+			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig,
+				  old_ordering, s4_nonvs }
 			See Documentation/power/video.txt for s3_bios and s3_mode.
 			s3_beep is for debugging; it makes the PC's speaker beep
 			as soon as the kernel's real-mode entry point is called.
@@ -159,6 +160,8 @@ and is between 256 and 4096 characters. 
 			control method, wrt putting devices into low power
 			states, to be enforced (the ACPI 2.0 ordering of _PTS is
 			used by default).
+			s4_nonvs prevents the kernel from saving/restoring the
+			ACPI NVS memory during hibernation.
 
Quoted 13 lines

arch/x86/kernel/acpi/sleep.c static int __init acpi_sleep_setup(char · line 164

Quoted 11 lines
Documentation/kernel-parameters.txt +0 -0
 	acpi_sci=	[HW,ACPI] ACPI System Control Interrupt trigger mode
 			Format: { level | edge | high | low }

Index: linux-2.6/arch/x86/kernel/acpi/sleep.c
===================================================================

arch/x86/kernel/acpi/sleep.c +1 -0
@@ -161,6 +161,8 @@ static int __init acpi_sleep_setup(char 
 #endif
 		if (strncmp(str, "old_ordering", 12) == 0)
 			acpi_old_suspend_ordering();
+		if (strncmp(str, "s4_nonvs", 13) == 0)

s/13/8/

Quoted 2 lines

Sure, thanks for catching that (it was a different string in the original patch).

Fixed version below, already with your ACK. ;-)

---
From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs (rev. 2)

On some machines it may be necessary to disable the saving/restoring of the ACPI NVS memory region during hibernation/resume. For this purpose, introduce new ACPI kernel command line option acpi_sleep=s4_nonvs.

Quoted 5 lines

Based on a patch by Zhang Rui.

ACK.
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#43 lore raw

PATCH 4/4 ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs (rev. 2)

In reply to Rafael J. Wysocki
Quoted 59 lines · Rafael J. Wysocki
Quoted 44 lines · Nigel Cunningham

Hi.

Quoted 41 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs

On some machines it may be necessary to disable the saving/restoring of the ACPI NVS memory region during hibernation/resume. For this purpose, introduce new ACPI kernel command line option acpi_sleep=s4_nonvs.

Based on a patch by Zhang Rui.

---
 Documentation/kernel-parameters.txt |    5 ++++-
 arch/x86/kernel/acpi/sleep.c        |    2 ++
 drivers/acpi/sleep/main.c           |   19 +++++++++++++++++--
 include/linux/acpi.h                |    1 +
 4 files changed, 24 insertions(+), 3 deletions(-)

Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================

Documentation/kernel-parameters.txt +4 -1
@@ -149,7 +149,8 @@ and is between 256 and 4096 characters. 
 			default: 0
 
 	acpi_sleep=	[HW,ACPI] Sleep options
-			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, old_ordering }
+			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig,
+				  old_ordering, s4_nonvs }
 			See Documentation/power/video.txt for s3_bios and s3_mode.
 			s3_beep is for debugging; it makes the PC's speaker beep
 			as soon as the kernel's real-mode entry point is called.
@@ -159,6 +160,8 @@ and is between 256 and 4096 characters. 
 			control method, wrt putting devices into low power
 			states, to be enforced (the ACPI 2.0 ordering of _PTS is
 			used by default).
+			s4_nonvs prevents the kernel from saving/restoring the
+			ACPI NVS memory during hibernation.
 
Quoted 13 lines

arch/x86/kernel/acpi/sleep.c static int __init acpi_sleep_setup(char · line 164

Quoted 11 lines
Documentation/kernel-parameters.txt +0 -0
 	acpi_sci=	[HW,ACPI] ACPI System Control Interrupt trigger mode
 			Format: { level | edge | high | low }

Index: linux-2.6/arch/x86/kernel/acpi/sleep.c
===================================================================

arch/x86/kernel/acpi/sleep.c +1 -0
@@ -161,6 +161,8 @@ static int __init acpi_sleep_setup(char 
 #endif
 		if (strncmp(str, "old_ordering", 12) == 0)
 			acpi_old_suspend_ordering();
+		if (strncmp(str, "s4_nonvs", 13) == 0)

s/13/8/

Quoted 2 lines

Sure, thanks for catching that (it was a different string in the original patch).

Fixed version below, already with your ACK. ;-)

---
From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs (rev. 2)

On some machines it may be necessary to disable the saving/restoring of the ACPI NVS memory region during hibernation/resume. For this purpose, introduce new ACPI kernel command line option acpi_sleep=s4_nonvs.

Based on a patch by Zhang Rui.

---
 Documentation/kernel-parameters.txt |    5 ++++-
 arch/x86/kernel/acpi/sleep.c        |    2 ++
 drivers/acpi/sleep/main.c           |   19 +++++++++++++++++--
 include/linux/acpi.h                |    1 +
 4 files changed, 24 insertions(+), 3 deletions(-)
Documentation/kernel-parameters.txt +4 -1
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
@@ -149,7 +149,8 @@ and is between 256 and 4096 characters. 
 			default: 0
 
 	acpi_sleep=	[HW,ACPI] Sleep options
-			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, old_ordering }
+			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig,
+				  old_ordering, s4_nonvs }
 			See Documentation/power/video.txt for s3_bios and s3_mode.
 			s3_beep is for debugging; it makes the PC's speaker beep
 			as soon as the kernel's real-mode entry point is called.
@@ -159,6 +160,8 @@ and is between 256 and 4096 characters. 
 			control method, wrt putting devices into low power
 			states, to be enforced (the ACPI 2.0 ordering of _PTS is
 			used by default).
+			s4_nonvs prevents the kernel from saving/restoring the
+			ACPI NVS memory during hibernation.
 
 	acpi_sci=	[HW,ACPI] ACPI System Control Interrupt trigger mode
 			Format: { level | edge | high | low }
arch/x86/kernel/acpi/sleep.c +2 -0
Index: linux-2.6/arch/x86/kernel/acpi/sleep.c
===================================================================
@@ -161,6 +161,8 @@ static int __init acpi_sleep_setup(char 
 #endif
 		if (strncmp(str, "old_ordering", 12) == 0)
 			acpi_old_suspend_ordering();
+		if (strncmp(str, "s4_nonvs", 8) == 0)
+			acpi_s4_no_nvs();
 		str = strchr(str, ',');
 		if (str != NULL)
 			str += strspn(str, ", \t");
drivers/acpi/sleep/main.c +17 -2
Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================
@@ -59,6 +59,20 @@ void __init acpi_old_suspend_ordering(vo
 	old_suspend_ordering = true;
 }
 
+/*
+ * The ACPI specification wants us to save NVS memory regions during hibernation
+ * and to restore them during the subsequent resume.  However, it is not certain
+ * if this mechanism is going to work on all machines, so we allow the user to
+ * disable this mechanism using the 'acpi_sleep=s4_nonvs' kernel command line
+ * option.
+ */
+static bool s4_no_nvs;
+
+void __init acpi_s4_no_nvs(void)
+{
+	s4_no_nvs = true;
+}
+
 /**
  *	acpi_pm_disable_gpes - Disable the GPEs.
  */
@@ -323,7 +337,7 @@ static int acpi_hibernation_begin(void)
 {
 	int error;
 
-	error = hibernate_nvs_alloc();
+	error = s4_no_nvs ? 0 : hibernate_nvs_alloc();
 	if (!error)
 		acpi_target_sleep_state = ACPI_STATE_S4;
 
@@ -411,7 +425,8 @@ static int acpi_hibernation_begin_old(vo
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
 	if (!error) {
-		error = hibernate_nvs_alloc();
+		if (!s4_no_nvs)
+			error = hibernate_nvs_alloc();
 		if (!error)
 			acpi_target_sleep_state = ACPI_STATE_S4;
 	}
include/linux/acpi.h +1 -0
Index: linux-2.6/include/linux/acpi.h
===================================================================
@@ -238,6 +238,7 @@ int acpi_check_mem_region(resource_size_
 #ifdef CONFIG_PM_SLEEP
 void __init acpi_no_s4_hw_signature(void);
 void __init acpi_old_suspend_ordering(void);
+void __init acpi_s4_no_nvs(void);
 #endif /* CONFIG_PM_SLEEP */
 #else	/* CONFIG_ACPI */
 
#44 lore raw

Re: [PATCH 4/4] ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs

In reply to Nigel Cunningham
Quoted 44 lines · Nigel Cunningham

Hi.

Quoted 41 lines · Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs

On some machines it may be necessary to disable the saving/restoring of the ACPI NVS memory region during hibernation/resume. For this purpose, introduce new ACPI kernel command line option acpi_sleep=s4_nonvs.

Based on a patch by Zhang Rui.

---
 Documentation/kernel-parameters.txt |    5 ++++-
 arch/x86/kernel/acpi/sleep.c        |    2 ++
 drivers/acpi/sleep/main.c           |   19 +++++++++++++++++--
 include/linux/acpi.h                |    1 +
 4 files changed, 24 insertions(+), 3 deletions(-)

Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================

Documentation/kernel-parameters.txt +4 -1
@@ -149,7 +149,8 @@ and is between 256 and 4096 characters. 
 			default: 0
 
 	acpi_sleep=	[HW,ACPI] Sleep options
-			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, old_ordering }
+			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig,
+				  old_ordering, s4_nonvs }
 			See Documentation/power/video.txt for s3_bios and s3_mode.
 			s3_beep is for debugging; it makes the PC's speaker beep
 			as soon as the kernel's real-mode entry point is called.
@@ -159,6 +160,8 @@ and is between 256 and 4096 characters. 
 			control method, wrt putting devices into low power
 			states, to be enforced (the ACPI 2.0 ordering of _PTS is
 			used by default).
+			s4_nonvs prevents the kernel from saving/restoring the
+			ACPI NVS memory during hibernation.
 
Quoted 13 lines

arch/x86/kernel/acpi/sleep.c static int __init acpi_sleep_setup(char · line 164

Quoted 11 lines
Documentation/kernel-parameters.txt +0 -0
 	acpi_sci=	[HW,ACPI] ACPI System Control Interrupt trigger mode
 			Format: { level | edge | high | low }

Index: linux-2.6/arch/x86/kernel/acpi/sleep.c
===================================================================

arch/x86/kernel/acpi/sleep.c +1 -0
@@ -161,6 +161,8 @@ static int __init acpi_sleep_setup(char 
 #endif
 		if (strncmp(str, "old_ordering", 12) == 0)
 			acpi_old_suspend_ordering();
+		if (strncmp(str, "s4_nonvs", 13) == 0)

s/13/8/

Sure, thanks for catching that (it was a different string in the original patch).

Quoted 62 lines
Quoted 53 lines
arch/x86/kernel/acpi/sleep.c +1 -0
+			acpi_s4_no_nvs();
 		str = strchr(str, ',');
 		if (str != NULL)
 			str += strspn(str, ", \t");

Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================

drivers/acpi/sleep/main.c +17 -2
@@ -59,6 +59,20 @@ void __init acpi_old_suspend_ordering(vo
 	old_suspend_ordering = true;
 }
 
+/*
+ * The ACPI specification wants us to save NVS memory regions during hibernation
+ * and to restore them during the subsequent resume.  However, it is not certain
+ * if this mechanism is going to work on all machines, so we allow the user to
+ * disable this mechanism using the 'acpi_sleep=s4_nonvs' kernel command line
+ * option.
+ */
+static bool s4_no_nvs;
+
+void __init acpi_s4_no_nvs(void)
+{
+	s4_no_nvs = true;
+}
+
 /**
  *	acpi_pm_disable_gpes - Disable the GPEs.
  */
@@ -323,7 +337,7 @@ static int acpi_hibernation_begin(void)
 {
 	int error;
 
-	error = hibernate_nvs_alloc();
+	error = s4_no_nvs ? 0 : hibernate_nvs_alloc();
 	if (!error)
 		acpi_target_sleep_state = ACPI_STATE_S4;
 
@@ -411,7 +425,8 @@ static int acpi_hibernation_begin_old(vo
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
 	if (!error) {
-		error = hibernate_nvs_alloc();
+		if (!s4_no_nvs)
+			error = hibernate_nvs_alloc();
 		if (!error)
 			acpi_target_sleep_state = ACPI_STATE_S4;
 	}

Index: linux-2.6/include/linux/acpi.h
===================================================================

include/linux/acpi.h int acpi_check_mem_region(resource_size_ · line 243

Quoted 6 lines
include/linux/acpi.h +1 -0
@@ -238,6 +238,7 @@ int acpi_check_mem_region(resource_size_
 #ifdef CONFIG_PM_SLEEP
 void __init acpi_no_s4_hw_signature(void);
 void __init acpi_old_suspend_ordering(void);
+void __init acpi_s4_no_nvs(void);
 #endif /* CONFIG_PM_SLEEP */
 #else	/* CONFIG_ACPI */

Apart from the above,

Quoted 1 line

Thanks,
Rafael

#45 lore raw

PATCH 4/4 ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs

In reply to Rafael J. Wysocki

From: Rafael J. Wysocki <[email protected]>

ACPI hibernate: Introduce new kernel parameter acpi_sleep=s4_nonvs

On some machines it may be necessary to disable the saving/restoring of the ACPI NVS memory region during hibernation/resume. For this purpose, introduce new ACPI kernel command line option acpi_sleep=s4_nonvs.

Based on a patch by Zhang Rui.

---
 Documentation/kernel-parameters.txt |    5 ++++-
 arch/x86/kernel/acpi/sleep.c        |    2 ++
 drivers/acpi/sleep/main.c           |   19 +++++++++++++++++--
 include/linux/acpi.h                |    1 +
 4 files changed, 24 insertions(+), 3 deletions(-)
Documentation/kernel-parameters.txt +4 -1
Index: linux-2.6/Documentation/kernel-parameters.txt
===================================================================
@@ -149,7 +149,8 @@ and is between 256 and 4096 characters. 
 			default: 0
 
 	acpi_sleep=	[HW,ACPI] Sleep options
-			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, old_ordering }
+			Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig,
+				  old_ordering, s4_nonvs }
 			See Documentation/power/video.txt for s3_bios and s3_mode.
 			s3_beep is for debugging; it makes the PC's speaker beep
 			as soon as the kernel's real-mode entry point is called.
@@ -159,6 +160,8 @@ and is between 256 and 4096 characters. 
 			control method, wrt putting devices into low power
 			states, to be enforced (the ACPI 2.0 ordering of _PTS is
 			used by default).
+			s4_nonvs prevents the kernel from saving/restoring the
+			ACPI NVS memory during hibernation.
 
 	acpi_sci=	[HW,ACPI] ACPI System Control Interrupt trigger mode
 			Format: { level | edge | high | low }
arch/x86/kernel/acpi/sleep.c +2 -0
Index: linux-2.6/arch/x86/kernel/acpi/sleep.c
===================================================================
@@ -161,6 +161,8 @@ static int __init acpi_sleep_setup(char 
 #endif
 		if (strncmp(str, "old_ordering", 12) == 0)
 			acpi_old_suspend_ordering();
+		if (strncmp(str, "s4_nonvs", 13) == 0)
+			acpi_s4_no_nvs();
 		str = strchr(str, ',');
 		if (str != NULL)
 			str += strspn(str, ", \t");
drivers/acpi/sleep/main.c +17 -2
Index: linux-2.6/drivers/acpi/sleep/main.c
===================================================================
@@ -59,6 +59,20 @@ void __init acpi_old_suspend_ordering(vo
 	old_suspend_ordering = true;
 }
 
+/*
+ * The ACPI specification wants us to save NVS memory regions during hibernation
+ * and to restore them during the subsequent resume.  However, it is not certain
+ * if this mechanism is going to work on all machines, so we allow the user to
+ * disable this mechanism using the 'acpi_sleep=s4_nonvs' kernel command line
+ * option.
+ */
+static bool s4_no_nvs;
+
+void __init acpi_s4_no_nvs(void)
+{
+	s4_no_nvs = true;
+}
+
 /**
  *	acpi_pm_disable_gpes - Disable the GPEs.
  */
@@ -323,7 +337,7 @@ static int acpi_hibernation_begin(void)
 {
 	int error;
 
-	error = hibernate_nvs_alloc();
+	error = s4_no_nvs ? 0 : hibernate_nvs_alloc();
 	if (!error)
 		acpi_target_sleep_state = ACPI_STATE_S4;
 
@@ -411,7 +425,8 @@ static int acpi_hibernation_begin_old(vo
 	int error = acpi_sleep_prepare(ACPI_STATE_S4);
 
 	if (!error) {
-		error = hibernate_nvs_alloc();
+		if (!s4_no_nvs)
+			error = hibernate_nvs_alloc();
 		if (!error)
 			acpi_target_sleep_state = ACPI_STATE_S4;
 	}
include/linux/acpi.h +1 -0
Index: linux-2.6/include/linux/acpi.h
===================================================================
@@ -238,6 +238,7 @@ int acpi_check_mem_region(resource_size_
 #ifdef CONFIG_PM_SLEEP
 void __init acpi_no_s4_hw_signature(void);
 void __init acpi_old_suspend_ordering(void);
+void __init acpi_s4_no_nvs(void);
 #endif /* CONFIG_PM_SLEEP */
 #else	/* CONFIG_ACPI */