lkml.org 
[lkml]   [2008]   [Jul]   [3]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    SubjectRe: [RFC PATCH 2/4] ACPI: introduce the mechanism to save/restore ACPI NVS memory
    From
    Date
    Oops, send to the right linux-kernel mail list, :)

    thanks,
    rui

    On Thu, 2008-07-03 at 10:24 +0800, Zhang Rui wrote:
    > According to the ACPI spec, ACPI NVS memory region is required to be
    > saved/restored by OS during hibernation.
    >
    > Section 15.3.2 ACPI Spec 3.0b,
    > "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."
    >
    > Add the mechanism to save/restore ACPI NVS memory during hibernation.
    >
    > Note: now Linux save ACPI NVS memory in acpi_hibernation_pre_snapshot, and restore it in
    > acpi_hibernation_leave. Both of these functions will be invoked only once during
    > the hibernate and resume.
    > Note: in Section 14.3 ACPI spec 3.0b, I only get
    > "EfiACPIMemoryNVS: The OS and loader must preserve this memory range in
    > the working and ACPI S1–S3 states."
    > whether we should save/restore this piece of memory is not cleared.
    >
    > Signed-off-by: Zhang Rui <rui.zhang@intel.com>
    > ---
    > drivers/acpi/sleep/main.c | 103 +++++++++++++++++++++++++++++++++++++++++++++-
    > include/linux/acpi.h | 3 +
    > 2 files changed, 104 insertions(+), 2 deletions(-)
    >
    > Index: linux-2.6/drivers/acpi/sleep/main.c
    > ===================================================================
    > --- linux-2.6.orig/drivers/acpi/sleep/main.c 2008-06-30 16:28:56.000000000 +0800
    > +++ linux-2.6/drivers/acpi/sleep/main.c 2008-07-01 09:30:36.000000000 +0800
    > @@ -15,6 +15,7 @@
    > #include <linux/dmi.h>
    > #include <linux/device.h>
    > #include <linux/suspend.h>
    > +#include <linux/highmem.h>
    >
    > #include <asm/io.h>
    >
    > @@ -255,11 +256,91 @@
    > #endif /* CONFIG_SUSPEND */
    >
    > #ifdef CONFIG_HIBERNATION
    > +
    > +/* ACPI NVS memory need to be saved/stored during hibernation */
    > +struct nvs_page {
    > + unsigned long pfn;
    > + void *data;
    > + struct list_head node;
    > +};
    > +static LIST_HEAD(nvs_pages_list);
    > +
    > +int acpi_mark_nvs_region(unsigned long start, unsigned long end)
    > +{
    > + struct nvs_page *pos;
    > +
    > + while (start <= end) {
    > + pos = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
    > + if (!pos)
    > + return -ENOMEM;
    > + pos->pfn = start;
    > + start++;
    > + list_add_tail(&pos->node, &nvs_pages_list);
    > + }
    > + return 0;
    > +}
    > +
    > +static int acpi_free_nvs_pages(void)
    > +{
    > + struct nvs_page *pos;
    > +
    > + list_for_each_entry(pos, &nvs_pages_list, node) {
    > + if (!pos->data)
    > + break;
    > + free_page((long)pos->data);
    > + pos->data = NULL;
    > + }
    > + return 0;
    > +}
    > +
    > +static int acpi_allocate_nvs_pages(void)
    > +{
    > + struct nvs_page *pos;
    > +
    > + list_for_each_entry(pos, &nvs_pages_list, node) {
    > + pos->data = (void *)__get_free_page(GFP_KERNEL);
    > + if (!pos->data) {
    > + acpi_free_nvs_pages();
    > + return -ENOMEM;
    > + }
    > + }
    > + return 0;
    > +}
    > +
    > +static void acpi_save_nvs_memory(void)
    > +{
    > + void *kaddr;
    > + struct nvs_page *pos;
    > +
    > + pr_debug("ACPI: Saving ACPI NVS memory\n");
    > + list_for_each_entry(pos, &nvs_pages_list, node) {
    > + /* nvs page might not have a 'struct page' */
    > + kaddr = kmap_atomic_pfn(pos->pfn, KM_USER0);
    > + copy_page(pos->data, kaddr);
    > + kunmap_atomic(kaddr, KM_USER0);
    > + }
    > + return;
    > +}
    > +
    > +static void acpi_restore_nvs_memory(void)
    > +{
    > + void *kaddr;
    > + struct nvs_page *pos;
    > +
    > + pr_debug("ACPI: Restoring ACPI NVS memory\n");
    > + list_for_each_entry(pos, &nvs_pages_list, node) {
    > + kaddr = kmap_atomic_pfn(pos->pfn, KM_USER0);
    > + copy_page(kaddr, pos->data);
    > + kunmap_atomic(kaddr, KM_USER0);
    > + }
    > +}
    > +
    > static int acpi_hibernation_begin(void)
    > {
    > acpi_target_sleep_state = ACPI_STATE_S4;
    >
    > - return 0;
    > + /* allocate pages for ACPI NVS memory before swsusp_shrink_memory */
    > + return acpi_allocate_nvs_pages();
    > }
    >
    > static int acpi_hibernation_prepare(void)
    > @@ -274,6 +355,20 @@
    > return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT;
    > }
    >
    > +static int acpi_hibernation_pre_snapshot(void)
    > +{
    > + int error = acpi_sleep_prepare(ACPI_STATE_S4);
    > +
    > + if (error) {
    > + acpi_target_sleep_state = ACPI_STATE_S0;
    > + return error;
    > + }
    > +
    > + acpi_save_nvs_memory();
    > +
    > + return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT;
    > +}
    > +
    > static int acpi_hibernation_enter(void)
    > {
    > acpi_status status = AE_OK;
    > @@ -301,6 +396,7 @@
    > acpi_enable();
    > /* Reprogram control registers and execute _BFS */
    > acpi_leave_sleep_state_prep(ACPI_STATE_S4);
    > + acpi_restore_nvs_memory();
    > }
    >
    > static void acpi_hibernation_finish(void)
    > @@ -321,6 +417,9 @@
    > * during a failing transition to the sleep state.
    > */
    > acpi_target_sleep_state = ACPI_STATE_S0;
    > +
    > + /* free pages allocated for ACPI NVS memory */
    > + acpi_free_nvs_pages();
    > }
    >
    > static int acpi_hibernation_pre_restore(void)
    > @@ -340,7 +439,7 @@
    > static struct platform_hibernation_ops acpi_hibernation_ops = {
    > .begin = acpi_hibernation_begin,
    > .end = acpi_hibernation_end,
    > - .pre_snapshot = acpi_hibernation_prepare,
    > + .pre_snapshot = acpi_hibernation_pre_snapshot,
    > .finish = acpi_hibernation_finish,
    > .prepare = acpi_hibernation_prepare,
    > .enter = acpi_hibernation_enter,
    > Index: linux-2.6/include/linux/acpi.h
    > ===================================================================
    > --- linux-2.6.orig/include/linux/acpi.h 2008-06-30 16:28:56.000000000 +0800
    > +++ linux-2.6/include/linux/acpi.h 2008-06-30 16:29:26.000000000 +0800
    > @@ -106,6 +106,9 @@
    > void acpi_numa_arch_fixup(void);
    > #endif
    >
    > +#ifdef CONFIG_HIBERNATION
    > +int acpi_mark_nvs_region(unsigned long, unsigned long);
    > +#endif
    > #ifdef CONFIG_ACPI_HOTPLUG_CPU
    > /* Arch dependent functions for cpu hotplug support */
    > int acpi_map_lsapic(acpi_handle handle, int *pcpu);
    >

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2008-07-03 10:55    [W:0.041 / U:31.428 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site