libnvdimm: fix potential deadlock while clearing errors
authorVishal Verma <vishal.l.verma@intel.com>
Thu, 31 Aug 2017 01:36:02 +0000 (19:36 -0600)
committerDan Williams <dan.j.williams@intel.com>
Thu, 31 Aug 2017 22:05:10 +0000 (15:05 -0700)
With the ACPI NFIT 'DSM' methods, acpi can be called from IO paths.
Specifically, the DSM to clear media errors is called during writes, so
that we can provide a writes-fix-errors model.

However it is easy to imagine a scenario like:
 -> write through the nvdimm driver
   -> acpi allocation
     -> writeback, causes more IO through the nvdimm driver
       -> deadlock

Fix this by using memalloc_noio_{save,restore}, which sets the GFP_NOIO
flag for the current scope when issuing commands/IOs that are expected
to clear errors.

Cc: <linux-acpi@vger.kernel.org>
Cc: <linux-nvdimm@lists.01.org>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Robert Moore <robert.moore@intel.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/nvdimm/bus.c

index 937fafa1886a8056f08ae809a129eb43f90daffd..a18c2914f4b6f3bea1583bcfcab8e1d6a5878b7a 100644 (file)
@@ -11,6 +11,7 @@
  * General Public License for more details.
  */
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/sched/mm.h>
 #include <linux/vmalloc.h>
 #include <linux/uaccess.h>
 #include <linux/module.h>
@@ -234,6 +235,7 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys,
        struct nd_cmd_clear_error clear_err;
        struct nd_cmd_ars_cap ars_cap;
        u32 clear_err_unit, mask;
+       unsigned int noio_flag;
        int cmd_rc, rc;
 
        if (!nvdimm_bus)
@@ -250,8 +252,10 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys,
        memset(&ars_cap, 0, sizeof(ars_cap));
        ars_cap.address = phys;
        ars_cap.length = len;
+       noio_flag = memalloc_noio_save();
        rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_CAP, &ars_cap,
                        sizeof(ars_cap), &cmd_rc);
+       memalloc_noio_restore(noio_flag);
        if (rc < 0)
                return rc;
        if (cmd_rc < 0)
@@ -266,8 +270,10 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys,
        memset(&clear_err, 0, sizeof(clear_err));
        clear_err.address = phys;
        clear_err.length = len;
+       noio_flag = memalloc_noio_save();
        rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CLEAR_ERROR, &clear_err,
                        sizeof(clear_err), &cmd_rc);
+       memalloc_noio_restore(noio_flag);
        if (rc < 0)
                return rc;
        if (cmd_rc < 0)