libnvdimm: Add DSM support for Address Range Scrub commands
authorVishal Verma <vishal.l.verma@intel.com>
Thu, 9 Jul 2015 19:25:36 +0000 (13:25 -0600)
committerDan Williams <dan.j.williams@intel.com>
Tue, 28 Jul 2015 02:53:19 +0000 (22:53 -0400)
Add support for the three ARS DSM commands:
- Query ARS Capabilities - Queries the firmware to check if a given
  range supports scrub, and if so, which type (persistent vs. volatile)
- Start ARS - Starts a scrub for a given range/type
- Query ARS Status - Checks status of a previously started scrub, and
  provides the error logs if any.

  The commands are described by the example DSM spec at:
  http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf

Also add these commands to the nfit_test test framework, and return
canned data.

Signed-off-by: Vishal Verma <vishal.l.verma@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
drivers/acpi/nfit.c
drivers/acpi/nfit.h
include/uapi/linux/ndctl.h
tools/testing/nvdimm/test/nfit.c

index 628a42c..ef8a664 100644 (file)
@@ -868,6 +868,7 @@ static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc)
        struct acpi_device *adev;
        int i;
 
+       nd_desc->dsm_mask = acpi_desc->bus_dsm_force_en;
        adev = to_acpi_dev(acpi_desc);
        if (!adev)
                return;
index 79b6d83..f2c2bb7 100644 (file)
@@ -107,6 +107,7 @@ struct acpi_nfit_desc {
        struct nvdimm_bus *nvdimm_bus;
        struct device *dev;
        unsigned long dimm_dsm_force_en;
+       unsigned long bus_dsm_force_en;
        int (*blk_do_io)(struct nd_blk_region *ndbr, resource_size_t dpa,
                        void *iobuf, u64 len, int rw);
 };
index e94bc20..5b4a4be 100644 (file)
@@ -111,6 +111,11 @@ enum {
        ND_CMD_VENDOR = 9,
 };
 
+enum {
+       ND_ARS_VOLATILE = 1,
+       ND_ARS_PERSISTENT = 2,
+};
+
 static inline const char *nvdimm_bus_cmd_name(unsigned cmd)
 {
        static const char * const names[] = {
@@ -194,4 +199,9 @@ enum nd_driver_flags {
 enum {
        ND_MIN_NAMESPACE_SIZE = 0x00400000,
 };
+
+enum ars_masks {
+       ARS_STATUS_MASK = 0x0000FFFF,
+       ARS_EXT_STATUS_SHIFT = 16,
+};
 #endif /* __NDCTL_H__ */
index d0bdae4..28dba91 100644 (file)
@@ -147,75 +147,153 @@ static struct nfit_test *to_nfit_test(struct device *dev)
        return container_of(pdev, struct nfit_test, pdev);
 }
 
+static int nfit_test_cmd_get_config_size(struct nd_cmd_get_config_size *nd_cmd,
+               unsigned int buf_len)
+{
+       if (buf_len < sizeof(*nd_cmd))
+               return -EINVAL;
+
+       nd_cmd->status = 0;
+       nd_cmd->config_size = LABEL_SIZE;
+       nd_cmd->max_xfer = SZ_4K;
+
+       return 0;
+}
+
+static int nfit_test_cmd_get_config_data(struct nd_cmd_get_config_data_hdr
+               *nd_cmd, unsigned int buf_len, void *label)
+{
+       unsigned int len, offset = nd_cmd->in_offset;
+       int rc;
+
+       if (buf_len < sizeof(*nd_cmd))
+               return -EINVAL;
+       if (offset >= LABEL_SIZE)
+               return -EINVAL;
+       if (nd_cmd->in_length + sizeof(*nd_cmd) > buf_len)
+               return -EINVAL;
+
+       nd_cmd->status = 0;
+       len = min(nd_cmd->in_length, LABEL_SIZE - offset);
+       memcpy(nd_cmd->out_buf, label + offset, len);
+       rc = buf_len - sizeof(*nd_cmd) - len;
+
+       return rc;
+}
+
+static int nfit_test_cmd_set_config_data(struct nd_cmd_set_config_hdr *nd_cmd,
+               unsigned int buf_len, void *label)
+{
+       unsigned int len, offset = nd_cmd->in_offset;
+       u32 *status;
+       int rc;
+
+       if (buf_len < sizeof(*nd_cmd))
+               return -EINVAL;
+       if (offset >= LABEL_SIZE)
+               return -EINVAL;
+       if (nd_cmd->in_length + sizeof(*nd_cmd) + 4 > buf_len)
+               return -EINVAL;
+
+       status = (void *)nd_cmd + nd_cmd->in_length + sizeof(*nd_cmd);
+       *status = 0;
+       len = min(nd_cmd->in_length, LABEL_SIZE - offset);
+       memcpy(label + offset, nd_cmd->in_buf, len);
+       rc = buf_len - sizeof(*nd_cmd) - (len + 4);
+
+       return rc;
+}
+
+static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd,
+               unsigned int buf_len)
+{
+       if (buf_len < sizeof(*nd_cmd))
+               return -EINVAL;
+
+       nd_cmd->max_ars_out = 256;
+       nd_cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16;
+
+       return 0;
+}
+
+static int nfit_test_cmd_ars_start(struct nd_cmd_ars_start *nd_cmd,
+               unsigned int buf_len)
+{
+       if (buf_len < sizeof(*nd_cmd))
+               return -EINVAL;
+
+       nd_cmd->status = 0;
+
+       return 0;
+}
+
+static int nfit_test_cmd_ars_status(struct nd_cmd_ars_status *nd_cmd,
+               unsigned int buf_len)
+{
+       if (buf_len < sizeof(*nd_cmd))
+               return -EINVAL;
+
+       nd_cmd->out_length = 256;
+       nd_cmd->num_records = 0;
+       nd_cmd->status = 0;
+
+       return 0;
+}
+
 static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
                struct nvdimm *nvdimm, unsigned int cmd, void *buf,
                unsigned int buf_len)
 {
        struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
        struct nfit_test *t = container_of(acpi_desc, typeof(*t), acpi_desc);
-       struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
-       int i, rc;
+       int i, rc = 0;
+
+       if (nvdimm) {
+               struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
 
-       if (!nfit_mem || !test_bit(cmd, &nfit_mem->dsm_mask))
-               return -ENOTTY;
+               if (!nfit_mem || !test_bit(cmd, &nfit_mem->dsm_mask))
+                       return -ENOTTY;
 
-       /* lookup label space for the given dimm */
-       for (i = 0; i < ARRAY_SIZE(handle); i++)
-               if (__to_nfit_memdev(nfit_mem)->device_handle == handle[i])
+               /* lookup label space for the given dimm */
+               for (i = 0; i < ARRAY_SIZE(handle); i++)
+                       if (__to_nfit_memdev(nfit_mem)->device_handle ==
+                                       handle[i])
+                               break;
+               if (i >= ARRAY_SIZE(handle))
+                       return -ENXIO;
+
+               switch (cmd) {
+               case ND_CMD_GET_CONFIG_SIZE:
+                       rc = nfit_test_cmd_get_config_size(buf, buf_len);
                        break;
-       if (i >= ARRAY_SIZE(handle))
-               return -ENXIO;
+               case ND_CMD_GET_CONFIG_DATA:
+                       rc = nfit_test_cmd_get_config_data(buf, buf_len,
+                               t->label[i]);
+                       break;
+               case ND_CMD_SET_CONFIG_DATA:
+                       rc = nfit_test_cmd_set_config_data(buf, buf_len,
+                               t->label[i]);
+                       break;
+               default:
+                       return -ENOTTY;
+               }
+       } else {
+               if (!nd_desc || !test_bit(cmd, &nd_desc->dsm_mask))
+                       return -ENOTTY;
 
-       switch (cmd) {
-       case ND_CMD_GET_CONFIG_SIZE: {
-               struct nd_cmd_get_config_size *nd_cmd = buf;
-
-               if (buf_len < sizeof(*nd_cmd))
-                       return -EINVAL;
-               nd_cmd->status = 0;
-               nd_cmd->config_size = LABEL_SIZE;
-               nd_cmd->max_xfer = SZ_4K;
-               rc = 0;
-               break;
-       }
-       case ND_CMD_GET_CONFIG_DATA: {
-               struct nd_cmd_get_config_data_hdr *nd_cmd = buf;
-               unsigned int len, offset = nd_cmd->in_offset;
-
-               if (buf_len < sizeof(*nd_cmd))
-                       return -EINVAL;
-               if (offset >= LABEL_SIZE)
-                       return -EINVAL;
-               if (nd_cmd->in_length + sizeof(*nd_cmd) > buf_len)
-                       return -EINVAL;
-
-               nd_cmd->status = 0;
-               len = min(nd_cmd->in_length, LABEL_SIZE - offset);
-               memcpy(nd_cmd->out_buf, t->label[i] + offset, len);
-               rc = buf_len - sizeof(*nd_cmd) - len;
-               break;
-       }
-       case ND_CMD_SET_CONFIG_DATA: {
-               struct nd_cmd_set_config_hdr *nd_cmd = buf;
-               unsigned int len, offset = nd_cmd->in_offset;
-               u32 *status;
-
-               if (buf_len < sizeof(*nd_cmd))
-                       return -EINVAL;
-               if (offset >= LABEL_SIZE)
-                       return -EINVAL;
-               if (nd_cmd->in_length + sizeof(*nd_cmd) + 4 > buf_len)
-                       return -EINVAL;
-
-               status = buf + nd_cmd->in_length + sizeof(*nd_cmd);
-               *status = 0;
-               len = min(nd_cmd->in_length, LABEL_SIZE - offset);
-               memcpy(t->label[i] + offset, nd_cmd->in_buf, len);
-               rc = buf_len - sizeof(*nd_cmd) - (len + 4);
-               break;
-       }
-       default:
-               return -ENOTTY;
+               switch (cmd) {
+               case ND_CMD_ARS_CAP:
+                       rc = nfit_test_cmd_ars_cap(buf, buf_len);
+                       break;
+               case ND_CMD_ARS_START:
+                       rc = nfit_test_cmd_ars_start(buf, buf_len);
+                       break;
+               case ND_CMD_ARS_STATUS:
+                       rc = nfit_test_cmd_ars_status(buf, buf_len);
+                       break;
+               default:
+                       return -ENOTTY;
+               }
        }
 
        return rc;
@@ -876,6 +954,9 @@ static void nfit_test0_setup(struct nfit_test *t)
        set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_dsm_force_en);
        set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_dsm_force_en);
        set_bit(ND_CMD_SET_CONFIG_DATA, &acpi_desc->dimm_dsm_force_en);
+       set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en);
+       set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en);
+       set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en);
        nd_desc = &acpi_desc->nd_desc;
        nd_desc->ndctl = nfit_test_ctl;
 }