ACPI, APEI, EINJ, Fix resource conflict on some machine
authorHuang Ying <ying.huang@intel.com>
Thu, 8 Dec 2011 03:25:47 +0000 (11:25 +0800)
committerLen Brown <len.brown@intel.com>
Tue, 17 Jan 2012 08:54:38 +0000 (03:54 -0500)
Some APEI firmware implementation will access injected address
specified in param1 to trigger the error when injecting memory error.
This will cause resource conflict with RAM.

On one of our testing machine, if injecting at memory address
0x10000000, the following error will be reported in dmesg:

  APEI: Can not request iomem region <0000000010000000-0000000010000008> for GARs.

This patch removes the injecting memory address range from trigger
table resources to avoid conflict.

Signed-off-by: Huang Ying <ying.huang@intel.com>
Tested-by: Tony Luck <tony.luck@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
drivers/acpi/apei/apei-base.c
drivers/acpi/apei/apei-internal.h
drivers/acpi/apei/einj.c

index 3492896b96f23363c4c51000d1e3fbeae68ee2f9..f2c5062e2b3214393f67e0e6c0afc94b9f9d935d 100644 (file)
@@ -421,6 +421,17 @@ static int apei_resources_merge(struct apei_resources *resources1,
        return 0;
 }
 
+int apei_resources_add(struct apei_resources *resources,
+                      unsigned long start, unsigned long size,
+                      bool iomem)
+{
+       if (iomem)
+               return apei_res_add(&resources->iomem, start, size);
+       else
+               return apei_res_add(&resources->ioport, start, size);
+}
+EXPORT_SYMBOL_GPL(apei_resources_add);
+
 /*
  * EINJ has two groups of GARs (EINJ table entry and trigger table
  * entry), so common resources are subtracted from the trigger table
index f57050e7a5e756ceb1f8885ce01030d0ac593ea4..d778edd34fbaec85ba0e24f0710eefaf44c543c2 100644 (file)
@@ -95,6 +95,9 @@ static inline void apei_resources_init(struct apei_resources *resources)
 }
 
 void apei_resources_fini(struct apei_resources *resources);
+int apei_resources_add(struct apei_resources *resources,
+                      unsigned long start, unsigned long size,
+                      bool iomem);
 int apei_resources_sub(struct apei_resources *resources1,
                       struct apei_resources *resources2);
 int apei_resources_request(struct apei_resources *resources,
index 43eeb2e6e635996faef0d9afa0a2d653320d78c6..4fdc8a3b4f6c7df1ebc5a5e4929a295f2cab49b5 100644 (file)
@@ -195,7 +195,8 @@ static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab)
 }
 
 /* Execute instructions in trigger error action table */
-static int __einj_error_trigger(u64 trigger_paddr)
+static int __einj_error_trigger(u64 trigger_paddr, u32 type,
+                               u64 param1, u64 param2)
 {
        struct acpi_einj_trigger *trigger_tab = NULL;
        struct apei_exec_context trigger_ctx;
@@ -256,6 +257,25 @@ static int __einj_error_trigger(u64 trigger_paddr)
        rc = apei_resources_sub(&trigger_resources, &einj_resources);
        if (rc)
                goto out_fini;
+       /*
+        * Some firmware will access target address specified in
+        * param1 to trigger the error when injecting memory error.
+        * This will cause resource conflict with regular memory.  So
+        * remove it from trigger table resources.
+        */
+       if (param_extension && (type & 0x0038) && param2) {
+               struct apei_resources addr_resources;
+               apei_resources_init(&addr_resources);
+               rc = apei_resources_add(&addr_resources,
+                                       param1 & param2,
+                                       ~param2 + 1, true);
+               if (rc)
+                       goto out_fini;
+               rc = apei_resources_sub(&trigger_resources, &addr_resources);
+               apei_resources_fini(&addr_resources);
+               if (rc)
+                       goto out_fini;
+       }
        rc = apei_resources_request(&trigger_resources, "APEI EINJ Trigger");
        if (rc)
                goto out_fini;
@@ -325,7 +345,7 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2)
        if (rc)
                return rc;
        trigger_paddr = apei_exec_ctx_get_output(&ctx);
-       rc = __einj_error_trigger(trigger_paddr);
+       rc = __einj_error_trigger(trigger_paddr, type, param1, param2);
        if (rc)
                return rc;
        rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION);