libnvdimm/dimm: Add a no-BLK quirk based on NVDIMM family
authorDan Williams <dan.j.williams@intel.com>
Sun, 3 Feb 2019 00:35:26 +0000 (16:35 -0800)
committerDan Williams <dan.j.williams@intel.com>
Sun, 3 Feb 2019 00:35:26 +0000 (16:35 -0800)
As Dexuan reports the NVDIMM_FAMILY_HYPERV platform is incompatible with
the existing Linux namespace implementation because it uses
NSLABEL_FLAG_LOCAL for x1-width PMEM interleave sets. Quirk it as an
platform / DIMM that does not provide BLK-aperture access. Allow the
libnvdimm core to assume no potential for aliasing. In case other
implementations make the same mistake, provide a "noblk" module
parameter to force-enable the quirk.

Link: https://lkml.kernel.org/r/PU1P153MB0169977604493B82B662A01CBF920@PU1P153MB0169.APCP153.PROD.OUTLOOK.COM
Reported-by: Dexuan Cui <decui@microsoft.com>
Tested-by: Dexuan Cui <decui@microsoft.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
drivers/acpi/nfit/core.c
drivers/nvdimm/dimm_devs.c
drivers/nvdimm/label.c
drivers/nvdimm/namespace_devs.c
drivers/nvdimm/region_devs.c
include/linux/libnvdimm.h

index 4a7e8b1fa43b9aebd3f84a0dd2bd547ceab4fc94..811c399a3a7653aed1672d976427aab4dd229653 100644 (file)
@@ -2016,6 +2016,10 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
                        cmd_mask |= nfit_mem->dsm_mask & NVDIMM_STANDARD_CMDMASK;
                }
 
+               /* Quirk to ignore LOCAL for labels on HYPERV DIMMs */
+               if (nfit_mem->family == NVDIMM_FAMILY_HYPERV)
+                       set_bit(NDD_NOBLK, &flags);
+
                if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags)) {
                        set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
                        set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
index 4890310df87440a3fcd459de19d563e026c0922d..553aa78abeeeb5f3c05dcd67d3cefc2fa784d473 100644 (file)
@@ -11,6 +11,7 @@
  * General Public License for more details.
  */
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/moduleparam.h>
 #include <linux/vmalloc.h>
 #include <linux/device.h>
 #include <linux/ndctl.h>
 
 static DEFINE_IDA(dimm_ida);
 
+static bool noblk;
+module_param(noblk, bool, 0444);
+MODULE_PARM_DESC(noblk, "force disable BLK / local alias support");
+
 /*
  * Retrieve bus and dimm handle and return if this bus supports
  * get_config_data commands
@@ -551,6 +556,8 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus,
 
        nvdimm->dimm_id = dimm_id;
        nvdimm->provider_data = provider_data;
+       if (noblk)
+               flags |= 1 << NDD_NOBLK;
        nvdimm->flags = flags;
        nvdimm->cmd_mask = cmd_mask;
        nvdimm->num_flush = num_flush;
index 6d6e9a12150b675a22e314c96db1a2379f41080f..f3d753d3169cb4487de3ed9007a53a02cf70a8f2 100644 (file)
@@ -392,6 +392,7 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
                return 0; /* no label, nothing to reserve */
 
        for_each_clear_bit_le(slot, free, nslot) {
+               struct nvdimm *nvdimm = to_nvdimm(ndd->dev);
                struct nd_namespace_label *nd_label;
                struct nd_region *nd_region = NULL;
                u8 label_uuid[NSLABEL_UUID_LEN];
@@ -406,6 +407,8 @@ int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd)
 
                memcpy(label_uuid, nd_label->uuid, NSLABEL_UUID_LEN);
                flags = __le32_to_cpu(nd_label->flags);
+               if (test_bit(NDD_NOBLK, &nvdimm->flags))
+                       flags &= ~NSLABEL_FLAG_LOCAL;
                nd_label_gen_id(&label_id, label_uuid, flags);
                res = nvdimm_allocate_dpa(ndd, &label_id,
                                __le64_to_cpu(nd_label->dpa),
index 4b077555ac70248292eba1f8a73b6a75fda3025d..3677b0c4a33d7e1c23a4a712026b9b93291d5fd9 100644 (file)
@@ -2492,6 +2492,12 @@ static int init_active_labels(struct nd_region *nd_region)
                        if (!label_ent)
                                break;
                        label = nd_label_active(ndd, j);
+                       if (test_bit(NDD_NOBLK, &nvdimm->flags)) {
+                               u32 flags = __le32_to_cpu(label->flags);
+
+                               flags &= ~NSLABEL_FLAG_LOCAL;
+                               label->flags = __cpu_to_le32(flags);
+                       }
                        label_ent->label = label;
 
                        mutex_lock(&nd_mapping->lock);
index e2818f94f2928ffdd3fd71797c49bfbf07f96ace..3b58baa44b5cf4b2622c0ab487a0eeebe08b6d89 100644 (file)
@@ -1003,6 +1003,13 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus,
 
                if (test_bit(NDD_UNARMED, &nvdimm->flags))
                        ro = 1;
+
+               if (test_bit(NDD_NOBLK, &nvdimm->flags)
+                               && dev_type == &nd_blk_device_type) {
+                       dev_err(&nvdimm_bus->dev, "%s: %s mapping%d is not BLK capable\n",
+                                       caller, dev_name(&nvdimm->dev), i);
+                       return NULL;
+               }
        }
 
        if (dev_type == &nd_blk_device_type) {
index 5440f11b0907d33474e4612fcc0dea5374935f15..7da406ae3a2b6c7a08130d3e0d19217d9dba660a 100644 (file)
@@ -42,6 +42,8 @@ enum {
        NDD_SECURITY_OVERWRITE = 3,
        /*  tracking whether or not there is a pending device reference */
        NDD_WORK_PENDING = 4,
+       /* ignore / filter NSLABEL_FLAG_LOCAL for this DIMM, i.e. no aliasing */
+       NDD_NOBLK = 5,
 
        /* need to set a limit somewhere, but yes, this is likely overkill */
        ND_IOCTL_MAX_BUFLEN = SZ_4M,