]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/commitdiff
Merge branch 'for-4.10/libnvdimm' into libnvdimm-for-next
authorDan Williams <dan.j.williams@intel.com>
Sat, 17 Dec 2016 23:08:10 +0000 (15:08 -0800)
committerDan Williams <dan.j.williams@intel.com>
Sat, 17 Dec 2016 23:08:10 +0000 (15:08 -0800)
1  2 
drivers/dax/dax.c
drivers/dax/pmem.c
drivers/nvdimm/namespace_devs.c
drivers/nvdimm/pmem.c
tools/testing/nvdimm/test/nfit.c

diff --combined drivers/dax/dax.c
index 286447a83dab809e0522e461c7e6ca416e997462,369fccda3b1b04c33ac603a218aa4b30dc518962..9352cbc1f9537d1e7ecc2eeb5c2da2983159cf70
@@@ -75,6 -75,73 +75,73 @@@ struct dax_dev 
        struct resource res[0];
  };
  
+ static ssize_t id_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+ {
+       struct dax_region *dax_region;
+       ssize_t rc = -ENXIO;
+       device_lock(dev);
+       dax_region = dev_get_drvdata(dev);
+       if (dax_region)
+               rc = sprintf(buf, "%d\n", dax_region->id);
+       device_unlock(dev);
+       return rc;
+ }
+ static DEVICE_ATTR_RO(id);
+ static ssize_t region_size_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+ {
+       struct dax_region *dax_region;
+       ssize_t rc = -ENXIO;
+       device_lock(dev);
+       dax_region = dev_get_drvdata(dev);
+       if (dax_region)
+               rc = sprintf(buf, "%llu\n", (unsigned long long)
+                               resource_size(&dax_region->res));
+       device_unlock(dev);
+       return rc;
+ }
+ static struct device_attribute dev_attr_region_size = __ATTR(size, 0444,
+               region_size_show, NULL);
+ static ssize_t align_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+ {
+       struct dax_region *dax_region;
+       ssize_t rc = -ENXIO;
+       device_lock(dev);
+       dax_region = dev_get_drvdata(dev);
+       if (dax_region)
+               rc = sprintf(buf, "%u\n", dax_region->align);
+       device_unlock(dev);
+       return rc;
+ }
+ static DEVICE_ATTR_RO(align);
+ static struct attribute *dax_region_attributes[] = {
+       &dev_attr_region_size.attr,
+       &dev_attr_align.attr,
+       &dev_attr_id.attr,
+       NULL,
+ };
+ static const struct attribute_group dax_region_attribute_group = {
+       .name = "dax_region",
+       .attrs = dax_region_attributes,
+ };
+ static const struct attribute_group *dax_region_attribute_groups[] = {
+       &dax_region_attribute_group,
+       NULL,
+ };
  static struct inode *dax_alloc_inode(struct super_block *sb)
  {
        return kmem_cache_alloc(dax_cache, GFP_KERNEL);
@@@ -200,12 -267,31 +267,31 @@@ void dax_region_put(struct dax_region *
  }
  EXPORT_SYMBOL_GPL(dax_region_put);
  
+ static void dax_region_unregister(void *region)
+ {
+       struct dax_region *dax_region = region;
+       sysfs_remove_groups(&dax_region->dev->kobj,
+                       dax_region_attribute_groups);
+       dax_region_put(dax_region);
+ }
  struct dax_region *alloc_dax_region(struct device *parent, int region_id,
                struct resource *res, unsigned int align, void *addr,
                unsigned long pfn_flags)
  {
        struct dax_region *dax_region;
  
+       /*
+        * The DAX core assumes that it can store its private data in
+        * parent->driver_data. This WARN is a reminder / safeguard for
+        * developers of device-dax drivers.
+        */
+       if (dev_get_drvdata(parent)) {
+               dev_WARN(parent, "dax core failed to setup private data\n");
+               return NULL;
+       }
        if (!IS_ALIGNED(res->start, align)
                        || !IS_ALIGNED(resource_size(res), align))
                return NULL;
        if (!dax_region)
                return NULL;
  
+       dev_set_drvdata(parent, dax_region);
        memcpy(&dax_region->res, res, sizeof(*res));
        dax_region->pfn_flags = pfn_flags;
        kref_init(&dax_region->kref);
        dax_region->align = align;
        dax_region->dev = parent;
        dax_region->base = addr;
+       if (sysfs_create_groups(&parent->kobj, dax_region_attribute_groups)) {
+               kfree(dax_region);
+               return NULL;;
+       }
  
+       kref_get(&dax_region->kref);
+       if (devm_add_action_or_reset(parent, dax_region_unregister, dax_region))
+               return NULL;
        return dax_region;
  }
  EXPORT_SYMBOL_GPL(alloc_dax_region);
@@@ -270,8 -364,8 +364,8 @@@ static int check_vma(struct dax_dev *da
        if (!dax_dev->alive)
                return -ENXIO;
  
 -      /* prevent private / writable mappings from being established */
 -      if ((vma->vm_flags & (VM_NORESERVE|VM_SHARED|VM_WRITE)) == VM_WRITE) {
 +      /* prevent private mappings from being established */
 +      if ((vma->vm_flags & VM_MAYSHARE) != VM_MAYSHARE) {
                dev_info(dev, "%s: %s: fail, attempted private mapping\n",
                                current->comm, func);
                return -EINVAL;
diff --combined drivers/dax/pmem.c
index 73c6ce93a0d9204227818465a707db9f7d5806fb,3ff84784249a1502f3cfa95353319b5e218057c8..033f49b31fdcfeeedede6a6349bbdab5115c82f6
@@@ -44,6 -44,7 +44,6 @@@ static void dax_pmem_percpu_exit(void *
  
        dev_dbg(dax_pmem->dev, "%s\n", __func__);
        percpu_ref_exit(ref);
 -      wait_for_completion(&dax_pmem->cmp);
  }
  
  static void dax_pmem_percpu_kill(void *data)
@@@ -53,7 -54,6 +53,7 @@@
  
        dev_dbg(dax_pmem->dev, "%s\n", __func__);
        percpu_ref_kill(ref);
 +      wait_for_completion(&dax_pmem->cmp);
  }
  
  static int dax_pmem_probe(struct device *dev)
@@@ -78,9 -78,7 +78,9 @@@
        nsio = to_nd_namespace_io(&ndns->dev);
  
        /* parse the 'pfn' info block via ->rw_bytes */
 -      devm_nsio_enable(dev, nsio);
 +      rc = devm_nsio_enable(dev, nsio);
 +      if (rc)
 +              return rc;
        altmap = nvdimm_setup_pfn(nd_pfn, &res, &__altmap);
        if (IS_ERR(altmap))
                return PTR_ERR(altmap);
@@@ -89,7 -87,8 +89,8 @@@
        pfn_sb = nd_pfn->pfn_sb;
  
        if (!devm_request_mem_region(dev, nsio->res.start,
-                               resource_size(&nsio->res), dev_name(dev))) {
+                               resource_size(&nsio->res),
+                               dev_name(&ndns->dev))) {
                dev_warn(dev, "could not reserve region %pR\n", &nsio->res);
                return -EBUSY;
        }
index abe5c6bc756c255193d803039973971368b9471d,de5809a88512bf124315598e4a0c1dfabb872f99..6307088b375f2d899002f9fc9fae16c387357598
@@@ -1132,7 -1132,7 +1132,7 @@@ static ssize_t size_show(struct device 
        return sprintf(buf, "%llu\n", (unsigned long long)
                        nvdimm_namespace_capacity(to_ndns(dev)));
  }
- static DEVICE_ATTR(size, S_IRUGO, size_show, size_store);
+ static DEVICE_ATTR(size, 0444, size_show, size_store);
  
  static u8 *namespace_to_uuid(struct device *dev)
  {
@@@ -1456,7 -1456,7 +1456,7 @@@ static umode_t namespace_visible(struc
  
        if (is_namespace_pmem(dev) || is_namespace_blk(dev)) {
                if (a == &dev_attr_size.attr)
-                       return S_IWUSR | S_IRUGO;
+                       return 0644;
  
                if (is_namespace_pmem(dev) && a == &dev_attr_sector_size.attr)
                        return 0;
@@@ -1653,7 -1653,7 +1653,7 @@@ static int select_pmem_id(struct nd_reg
                u64 hw_start, hw_end, pmem_start, pmem_end;
                struct nd_label_ent *label_ent;
  
-               WARN_ON(!mutex_is_locked(&nd_mapping->lock));
+               lockdep_assert_held(&nd_mapping->lock);
                list_for_each_entry(label_ent, &nd_mapping->labels, list) {
                        nd_label = label_ent->label;
                        if (!nd_label)
@@@ -1997,7 -1997,7 +1997,7 @@@ struct device *create_namespace_blk(str
        struct nd_mapping *nd_mapping = &nd_region->mapping[0];
        struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
        struct nd_namespace_blk *nsblk;
-       char *name[NSLABEL_NAME_LEN];
+       char name[NSLABEL_NAME_LEN];
        struct device *dev = NULL;
        struct resource *res;
  
@@@ -2176,14 -2176,12 +2176,14 @@@ static struct device **scan_labels(stru
        return devs;
  
   err:
 -      for (i = 0; devs[i]; i++)
 -              if (is_nd_blk(&nd_region->dev))
 -                      namespace_blk_release(devs[i]);
 -              else
 -                      namespace_pmem_release(devs[i]);
 -      kfree(devs);
 +      if (devs) {
 +              for (i = 0; devs[i]; i++)
 +                      if (is_nd_blk(&nd_region->dev))
 +                              namespace_blk_release(devs[i]);
 +                      else
 +                              namespace_pmem_release(devs[i]);
 +              kfree(devs);
 +      }
        return NULL;
  }
  
diff --combined drivers/nvdimm/pmem.c
index 24618431a14bae7e891438b3601d017d1d34db4d,ecf79fd64517236049dbf6c6cdb2612d927aa23c..7282d7495bf1f0a1bf6685012dafb1d9cc60bfa4
@@@ -53,21 -53,23 +53,24 @@@ static int pmem_clear_poison(struct pme
        struct device *dev = to_dev(pmem);
        sector_t sector;
        long cleared;
+       int rc = 0;
  
        sector = (offset - pmem->data_offset) / 512;
-       cleared = nvdimm_clear_poison(dev, pmem->phys_addr + offset, len);
  
+       cleared = nvdimm_clear_poison(dev, pmem->phys_addr + offset, len);
+       if (cleared < len)
+               rc = -EIO;
        if (cleared > 0 && cleared / 512) {
-               dev_dbg(dev, "%s: %#llx clear %ld sector%s\n",
-                               __func__, (unsigned long long) sector,
-                               cleared / 512, cleared / 512 > 1 ? "s" : "");
-               badblocks_clear(&pmem->bb, sector, cleared / 512);
-       } else {
-               return -EIO;
+               cleared /= 512;
+               dev_dbg(dev, "%s: %#llx clear %ld sector%s\n", __func__,
+                               (unsigned long long) sector, cleared,
+                               cleared > 1 ? "s" : "");
+               badblocks_clear(&pmem->bb, sector, cleared);
        }
 +
        invalidate_pmem(pmem->virt_addr + offset, len);
-       return 0;
+       return rc;
  }
  
  static void write_pmem(void *pmem_addr, struct page *page,
@@@ -127,7 -129,7 +130,7 @@@ static int pmem_do_bvec(struct pmem_dev
                flush_dcache_page(page);
                write_pmem(pmem_addr, page, off, len);
                if (unlikely(bad_pmem)) {
 -                      pmem_clear_poison(pmem, pmem_off, len);
 +                      rc = pmem_clear_poison(pmem, pmem_off, len);
                        write_pmem(pmem_addr, page, off, len);
                }
        }
@@@ -270,7 -272,7 +273,7 @@@ static int pmem_attach_disk(struct devi
                dev_warn(dev, "unable to guarantee persistence of writes\n");
  
        if (!devm_request_mem_region(dev, res->start, resource_size(res),
-                               dev_name(dev))) {
+                               dev_name(&ndns->dev))) {
                dev_warn(dev, "could not reserve region %pR\n", res);
                return -EBUSY;
        }
index 71620fa959530211c6c73c25ab71aaaba7cfa50a,c02f9a8bd38200c046fa45c1111472c7fe43c017..45be8b55a663453748a8c3156b865610fcfc37a8
@@@ -23,7 -23,6 +23,7 @@@
  #include <linux/sizes.h>
  #include <linux/list.h>
  #include <linux/slab.h>
 +#include <nd-core.h>
  #include <nfit.h>
  #include <nd.h>
  #include "nfit_test.h"
@@@ -125,12 -124,13 +125,13 @@@ struct nfit_test_dcr 
        (((node & 0xfff) << 16) | ((socket & 0xf) << 12) \
         | ((imc & 0xf) << 8) | ((chan & 0xf) << 4) | (dimm & 0xf))
  
- static u32 handle[NUM_DCR] = {
+ static u32 handle[] = {
        [0] = NFIT_DIMM_HANDLE(0, 0, 0, 0, 0),
        [1] = NFIT_DIMM_HANDLE(0, 0, 0, 0, 1),
        [2] = NFIT_DIMM_HANDLE(0, 0, 1, 0, 0),
        [3] = NFIT_DIMM_HANDLE(0, 0, 1, 0, 1),
        [4] = NFIT_DIMM_HANDLE(0, 1, 0, 0, 0),
+       [5] = NFIT_DIMM_HANDLE(1, 0, 0, 0, 0),
  };
  
  static unsigned long dimm_fail_cmd_flags[NUM_DCR];
@@@ -142,6 -142,7 +143,7 @@@ struct nfit_test 
        void *nfit_buf;
        dma_addr_t nfit_dma;
        size_t nfit_size;
+       int dcr_idx;
        int num_dcr;
        int num_pm;
        void **dimm;
@@@ -426,11 -427,11 +428,11 @@@ static int nfit_test_ctl(struct nvdimm_
                        break;
                case ND_CMD_GET_CONFIG_DATA:
                        rc = nfit_test_cmd_get_config_data(buf, buf_len,
-                               t->label[i]);
+                               t->label[i - t->dcr_idx]);
                        break;
                case ND_CMD_SET_CONFIG_DATA:
                        rc = nfit_test_cmd_set_config_data(buf, buf_len,
-                               t->label[i]);
+                               t->label[i - t->dcr_idx]);
                        break;
                case ND_CMD_SMART:
                        rc = nfit_test_cmd_smart(buf, buf_len);
@@@ -682,7 -683,7 +684,7 @@@ static int nfit_test0_alloc(struct nfit
        if (!t->spa_set[2])
                return -ENOMEM;
  
-       for (i = 0; i < NUM_DCR; i++) {
+       for (i = 0; i < t->num_dcr; i++) {
                t->dimm[i] = test_alloc(t, DIMM_SIZE, &t->dimm_dma[i]);
                if (!t->dimm[i])
                        return -ENOMEM;
                        return -ENOMEM;
        }
  
-       for (i = 0; i < NUM_DCR; i++) {
+       for (i = 0; i < t->num_dcr; i++) {
                t->dcr[i] = test_alloc(t, LABEL_SIZE, &t->dcr_dma[i]);
                if (!t->dcr[i])
                        return -ENOMEM;
@@@ -728,6 -729,7 +730,7 @@@ static int nfit_test1_alloc(struct nfit
        size_t nfit_size = sizeof(struct acpi_nfit_system_address) * 2
                + sizeof(struct acpi_nfit_memory_map)
                + offsetof(struct acpi_nfit_control_region, window_size);
+       int i;
  
        t->nfit_buf = test_alloc(t, nfit_size, &t->nfit_dma);
        if (!t->nfit_buf)
        if (!t->spa_set[0])
                return -ENOMEM;
  
+       for (i = 0; i < t->num_dcr; i++) {
+               t->label[i] = test_alloc(t, LABEL_SIZE, &t->label_dma[i]);
+               if (!t->label[i])
+                       return -ENOMEM;
+               sprintf(t->label[i], "label%d", i);
+       }
        t->spa_set[1] = test_alloc(t, SPA_VCD_SIZE, &t->spa_set_dma[1]);
        if (!t->spa_set[1])
                return -ENOMEM;
@@@ -1450,7 -1459,7 +1460,7 @@@ static void nfit_test1_setup(struct nfi
        memdev = nfit_buf + offset;
        memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
        memdev->header.length = sizeof(*memdev);
-       memdev->device_handle = 0;
+       memdev->device_handle = handle[5];
        memdev->physical_id = 0;
        memdev->region_id = 0;
        memdev->range_index = 0+1;
                        window_size);
        dcr->region_index = 0+1;
        dcr_common_init(dcr);
-       dcr->serial_number = ~0;
+       dcr->serial_number = ~handle[5];
        dcr->code = NFIT_FIC_BYTE;
        dcr->windows = 0;
  
        set_bit(ND_CMD_ARS_START, &acpi_desc->bus_cmd_force_en);
        set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_force_en);
        set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_cmd_force_en);
+       set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_cmd_force_en);
+       set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en);
+       set_bit(ND_CMD_SET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en);
  }
  
  static int nfit_test_blk_do_io(struct nd_blk_region *ndbr, resource_size_t dpa,
        return 0;
  }
  
 +static unsigned long nfit_ctl_handle;
 +
 +union acpi_object *result;
 +
 +static union acpi_object *nfit_test_evaluate_dsm(acpi_handle handle,
 +              const u8 *uuid, u64 rev, u64 func, union acpi_object *argv4)
 +{
 +      if (handle != &nfit_ctl_handle)
 +              return ERR_PTR(-ENXIO);
 +
 +      return result;
 +}
 +
 +static int setup_result(void *buf, size_t size)
 +{
 +      result = kmalloc(sizeof(union acpi_object) + size, GFP_KERNEL);
 +      if (!result)
 +              return -ENOMEM;
 +      result->package.type = ACPI_TYPE_BUFFER,
 +      result->buffer.pointer = (void *) (result + 1);
 +      result->buffer.length = size;
 +      memcpy(result->buffer.pointer, buf, size);
 +      memset(buf, 0, size);
 +      return 0;
 +}
 +
 +static int nfit_ctl_test(struct device *dev)
 +{
 +      int rc, cmd_rc;
 +      struct nvdimm *nvdimm;
 +      struct acpi_device *adev;
 +      struct nfit_mem *nfit_mem;
 +      struct nd_ars_record *record;
 +      struct acpi_nfit_desc *acpi_desc;
 +      const u64 test_val = 0x0123456789abcdefULL;
 +      unsigned long mask, cmd_size, offset;
 +      union {
 +              struct nd_cmd_get_config_size cfg_size;
 +              struct nd_cmd_ars_status ars_stat;
 +              struct nd_cmd_ars_cap ars_cap;
 +              char buf[sizeof(struct nd_cmd_ars_status)
 +                      + sizeof(struct nd_ars_record)];
 +      } cmds;
 +
 +      adev = devm_kzalloc(dev, sizeof(*adev), GFP_KERNEL);
 +      if (!adev)
 +              return -ENOMEM;
 +      *adev = (struct acpi_device) {
 +              .handle = &nfit_ctl_handle,
 +              .dev = {
 +                      .init_name = "test-adev",
 +              },
 +      };
 +
 +      acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL);
 +      if (!acpi_desc)
 +              return -ENOMEM;
 +      *acpi_desc = (struct acpi_nfit_desc) {
 +              .nd_desc = {
 +                      .cmd_mask = 1UL << ND_CMD_ARS_CAP
 +                              | 1UL << ND_CMD_ARS_START
 +                              | 1UL << ND_CMD_ARS_STATUS
 +                              | 1UL << ND_CMD_CLEAR_ERROR,
 +                      .module = THIS_MODULE,
 +                      .provider_name = "ACPI.NFIT",
 +                      .ndctl = acpi_nfit_ctl,
 +              },
 +              .dev = &adev->dev,
 +      };
 +
 +      nfit_mem = devm_kzalloc(dev, sizeof(*nfit_mem), GFP_KERNEL);
 +      if (!nfit_mem)
 +              return -ENOMEM;
 +
 +      mask = 1UL << ND_CMD_SMART | 1UL << ND_CMD_SMART_THRESHOLD
 +              | 1UL << ND_CMD_DIMM_FLAGS | 1UL << ND_CMD_GET_CONFIG_SIZE
 +              | 1UL << ND_CMD_GET_CONFIG_DATA | 1UL << ND_CMD_SET_CONFIG_DATA
 +              | 1UL << ND_CMD_VENDOR;
 +      *nfit_mem = (struct nfit_mem) {
 +              .adev = adev,
 +              .family = NVDIMM_FAMILY_INTEL,
 +              .dsm_mask = mask,
 +      };
 +
 +      nvdimm = devm_kzalloc(dev, sizeof(*nvdimm), GFP_KERNEL);
 +      if (!nvdimm)
 +              return -ENOMEM;
 +      *nvdimm = (struct nvdimm) {
 +              .provider_data = nfit_mem,
 +              .cmd_mask = mask,
 +              .dev = {
 +                      .init_name = "test-dimm",
 +              },
 +      };
 +
 +
 +      /* basic checkout of a typical 'get config size' command */
 +      cmd_size = sizeof(cmds.cfg_size);
 +      cmds.cfg_size = (struct nd_cmd_get_config_size) {
 +              .status = 0,
 +              .config_size = SZ_128K,
 +              .max_xfer = SZ_4K,
 +      };
 +      rc = setup_result(cmds.buf, cmd_size);
 +      if (rc)
 +              return rc;
 +      rc = acpi_nfit_ctl(&acpi_desc->nd_desc, nvdimm, ND_CMD_GET_CONFIG_SIZE,
 +                      cmds.buf, cmd_size, &cmd_rc);
 +
 +      if (rc < 0 || cmd_rc || cmds.cfg_size.status != 0
 +                      || cmds.cfg_size.config_size != SZ_128K
 +                      || cmds.cfg_size.max_xfer != SZ_4K) {
 +              dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n",
 +                              __func__, __LINE__, rc, cmd_rc);
 +              return -EIO;
 +      }
 +
 +
 +      /* test ars_status with zero output */
 +      cmd_size = offsetof(struct nd_cmd_ars_status, address);
 +      cmds.ars_stat = (struct nd_cmd_ars_status) {
 +              .out_length = 0,
 +      };
 +      rc = setup_result(cmds.buf, cmd_size);
 +      if (rc)
 +              return rc;
 +      rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_ARS_STATUS,
 +                      cmds.buf, cmd_size, &cmd_rc);
 +
 +      if (rc < 0 || cmd_rc) {
 +              dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n",
 +                              __func__, __LINE__, rc, cmd_rc);
 +              return -EIO;
 +      }
 +
 +
 +      /* test ars_cap with benign extended status */
 +      cmd_size = sizeof(cmds.ars_cap);
 +      cmds.ars_cap = (struct nd_cmd_ars_cap) {
 +              .status = ND_ARS_PERSISTENT << 16,
 +      };
 +      offset = offsetof(struct nd_cmd_ars_cap, status);
 +      rc = setup_result(cmds.buf + offset, cmd_size - offset);
 +      if (rc)
 +              return rc;
 +      rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_ARS_CAP,
 +                      cmds.buf, cmd_size, &cmd_rc);
 +
 +      if (rc < 0 || cmd_rc) {
 +              dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n",
 +                              __func__, __LINE__, rc, cmd_rc);
 +              return -EIO;
 +      }
 +
 +
 +      /* test ars_status with 'status' trimmed from 'out_length' */
 +      cmd_size = sizeof(cmds.ars_stat) + sizeof(struct nd_ars_record);
 +      cmds.ars_stat = (struct nd_cmd_ars_status) {
 +              .out_length = cmd_size - 4,
 +      };
 +      record = &cmds.ars_stat.records[0];
 +      *record = (struct nd_ars_record) {
 +              .length = test_val,
 +      };
 +      rc = setup_result(cmds.buf, cmd_size);
 +      if (rc)
 +              return rc;
 +      rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_ARS_STATUS,
 +                      cmds.buf, cmd_size, &cmd_rc);
 +
 +      if (rc < 0 || cmd_rc || record->length != test_val) {
 +              dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n",
 +                              __func__, __LINE__, rc, cmd_rc);
 +              return -EIO;
 +      }
 +
 +
 +      /* test ars_status with 'Output (Size)' including 'status' */
 +      cmd_size = sizeof(cmds.ars_stat) + sizeof(struct nd_ars_record);
 +      cmds.ars_stat = (struct nd_cmd_ars_status) {
 +              .out_length = cmd_size,
 +      };
 +      record = &cmds.ars_stat.records[0];
 +      *record = (struct nd_ars_record) {
 +              .length = test_val,
 +      };
 +      rc = setup_result(cmds.buf, cmd_size);
 +      if (rc)
 +              return rc;
 +      rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_ARS_STATUS,
 +                      cmds.buf, cmd_size, &cmd_rc);
 +
 +      if (rc < 0 || cmd_rc || record->length != test_val) {
 +              dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n",
 +                              __func__, __LINE__, rc, cmd_rc);
 +              return -EIO;
 +      }
 +
 +
 +      /* test extended status for get_config_size results in failure */
 +      cmd_size = sizeof(cmds.cfg_size);
 +      cmds.cfg_size = (struct nd_cmd_get_config_size) {
 +              .status = 1 << 16,
 +      };
 +      rc = setup_result(cmds.buf, cmd_size);
 +      if (rc)
 +              return rc;
 +      rc = acpi_nfit_ctl(&acpi_desc->nd_desc, nvdimm, ND_CMD_GET_CONFIG_SIZE,
 +                      cmds.buf, cmd_size, &cmd_rc);
 +
 +      if (rc < 0 || cmd_rc >= 0) {
 +              dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n",
 +                              __func__, __LINE__, rc, cmd_rc);
 +              return -EIO;
 +      }
 +
 +      return 0;
 +}
 +
  static int nfit_test_probe(struct platform_device *pdev)
  {
        struct nvdimm_bus_descriptor *nd_desc;
        union acpi_object *obj;
        int rc;
  
 +      if (strcmp(dev_name(&pdev->dev), "nfit_test.0") == 0) {
 +              rc = nfit_ctl_test(&pdev->dev);
 +              if (rc)
 +                      return rc;
 +      }
 +
        nfit_test = to_nfit_test(&pdev->dev);
  
        /* common alloc */
@@@ -1865,13 -1652,11 +1878,13 @@@ static __init int nfit_test_init(void
  {
        int rc, i;
  
 -      nfit_test_dimm = class_create(THIS_MODULE, "nfit_test_dimm");
 -      if (IS_ERR(nfit_test_dimm))
 -              return PTR_ERR(nfit_test_dimm);
 +      nfit_test_setup(nfit_test_lookup, nfit_test_evaluate_dsm);
  
 -      nfit_test_setup(nfit_test_lookup);
 +      nfit_test_dimm = class_create(THIS_MODULE, "nfit_test_dimm");
 +      if (IS_ERR(nfit_test_dimm)) {
 +              rc = PTR_ERR(nfit_test_dimm);
 +              goto err_register;
 +      }
  
        for (i = 0; i < NUM_NFITS; i++) {
                struct nfit_test *nfit_test;
                switch (i) {
                case 0:
                        nfit_test->num_pm = NUM_PM;
+                       nfit_test->dcr_idx = 0;
                        nfit_test->num_dcr = NUM_DCR;
                        nfit_test->alloc = nfit_test0_alloc;
                        nfit_test->setup = nfit_test0_setup;
                        break;
                case 1:
                        nfit_test->num_pm = 1;
+                       nfit_test->dcr_idx = NUM_DCR;
+                       nfit_test->num_dcr = 1;
                        nfit_test->alloc = nfit_test1_alloc;
                        nfit_test->setup = nfit_test1_setup;
                        break;