nfit: don't start a full scrub by default for an MCE
authorVishal Verma <vishal.l.verma@intel.com>
Fri, 30 Sep 2016 23:19:29 +0000 (17:19 -0600)
committerDan Williams <dan.j.williams@intel.com>
Sat, 1 Oct 2016 00:00:10 +0000 (17:00 -0700)
Starting a full Address Range Scrub (ARS) on hitting a memory error
machine check exception may not always be desirable. Provide a way
through sysfs to toggle the behavior between just adding the address
(cache line) where the MCE happened to the poison list and doing a full
scrub. The former (selective insertion of the address) is done
unconditionally.

Cc: linux-acpi@vger.kernel.org
Cc: Linda Knippers <linda.knippers@hpe.com>
Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
drivers/acpi/nfit/core.c
drivers/acpi/nfit/mce.c
drivers/acpi/nfit/nfit.h

index ceb6671ab35536862135c93ba049e60d65562d9f..02838f928d7e5dbb03ca717a06a1599c58bb54c1 100644 (file)
@@ -878,6 +878,58 @@ static ssize_t revision_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(revision);
 
+static ssize_t hw_error_scrub_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
+       struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
+       struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
+
+       return sprintf(buf, "%d\n", acpi_desc->scrub_mode);
+}
+
+/*
+ * The 'hw_error_scrub' attribute can have the following values written to it:
+ * '0': Switch to the default mode where an exception will only insert
+ *      the address of the memory error into the poison and badblocks lists.
+ * '1': Enable a full scrub to happen if an exception for a memory error is
+ *      received.
+ */
+static ssize_t hw_error_scrub_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct nvdimm_bus_descriptor *nd_desc;
+       ssize_t rc;
+       long val;
+
+       rc = kstrtol(buf, 0, &val);
+       if (rc)
+               return rc;
+
+       device_lock(dev);
+       nd_desc = dev_get_drvdata(dev);
+       if (nd_desc) {
+               struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
+
+               switch (val) {
+               case HW_ERROR_SCRUB_ON:
+                       acpi_desc->scrub_mode = HW_ERROR_SCRUB_ON;
+                       break;
+               case HW_ERROR_SCRUB_OFF:
+                       acpi_desc->scrub_mode = HW_ERROR_SCRUB_OFF;
+                       break;
+               default:
+                       rc = -EINVAL;
+                       break;
+               }
+       }
+       device_unlock(dev);
+       if (rc)
+               return rc;
+       return size;
+}
+static DEVICE_ATTR_RW(hw_error_scrub);
+
 /*
  * This shows the number of full Address Range Scrubs that have been
  * completed since driver load time. Userspace can wait on this using
@@ -950,6 +1002,7 @@ static umode_t nfit_visible(struct kobject *kobj, struct attribute *a, int n)
 static struct attribute *acpi_nfit_attributes[] = {
        &dev_attr_revision.attr,
        &dev_attr_scrub.attr,
+       &dev_attr_hw_error_scrub.attr,
        NULL,
 };
 
index 4c745bf389fea19b0cd049533183087be8873a5d..2e25e252945f0ac6f57015f644d5348a8087c872 100644 (file)
@@ -14,6 +14,7 @@
  */
 #include <linux/notifier.h>
 #include <linux/acpi.h>
+#include <linux/nd.h>
 #include <asm/mce.h>
 #include "nfit.h"
 
@@ -62,12 +63,25 @@ static int nfit_handle_mce(struct notifier_block *nb, unsigned long val,
                }
                mutex_unlock(&acpi_desc->init_mutex);
 
-               /*
-                * We can ignore an -EBUSY here because if an ARS is already
-                * in progress, just let that be the last authoritative one
-                */
-               if (found_match)
+               if (!found_match)
+                       continue;
+
+               /* If this fails due to an -ENOMEM, there is little we can do */
+               nvdimm_bus_add_poison(acpi_desc->nvdimm_bus,
+                               ALIGN(mce->addr, L1_CACHE_BYTES),
+                               L1_CACHE_BYTES);
+               nvdimm_region_notify(nfit_spa->nd_region,
+                               NVDIMM_REVALIDATE_POISON);
+
+               if (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON) {
+                       /*
+                        * We can ignore an -EBUSY here because if an ARS is
+                        * already in progress, just let that be the last
+                        * authoritative one
+                        */
                        acpi_nfit_ars_rescan(acpi_desc);
+               }
+               break;
        }
 
        mutex_unlock(&acpi_desc_lock);
index bb101170cd0b383d5878965f1fe7b144b87d4151..14296f5267c8c45a2826b9b82a40dbe8aedf1918 100644 (file)
@@ -161,6 +161,7 @@ struct acpi_nfit_desc {
        struct list_head list;
        struct kernfs_node *scrub_count_state;
        unsigned int scrub_count;
+       unsigned int scrub_mode;
        unsigned int cancel:1;
        unsigned long dimm_cmd_force_en;
        unsigned long bus_cmd_force_en;
@@ -168,6 +169,11 @@ struct acpi_nfit_desc {
                        void *iobuf, u64 len, int rw);
 };
 
+enum scrub_mode {
+       HW_ERROR_SCRUB_OFF,
+       HW_ERROR_SCRUB_ON,
+};
+
 enum nd_blk_mmio_selector {
        BDW,
        DCR,