libnvdimm, pfn: add 'align' attribute, default to HPAGE_SIZE
authorDan Williams <dan.j.williams@intel.com>
Thu, 10 Dec 2015 22:45:23 +0000 (14:45 -0800)
committerDan Williams <dan.j.williams@intel.com>
Sat, 12 Dec 2015 23:04:26 +0000 (15:04 -0800)
When setting aside capacity for struct page it must be aligned to the
largest mapping size that is to be made available via DAX.  Make the
alignment configurable to enable support for 1GiB page-size mappings.

The offset for PFN_MODE_RAM may now be larger than SZ_8K, so fixup the
offset check in nvdimm_namespace_attach_pfn().

Reported-by: Toshi Kani <toshi.kani@hpe.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
drivers/nvdimm/nd.h
drivers/nvdimm/pfn_devs.c
drivers/nvdimm/pmem.c

index 2ce428e9b5849f42cba0f44a64d75c01ef1c8637..e4e9f9ae0cc886e320528850c66674a6fc259ec3 100644 (file)
@@ -146,6 +146,7 @@ struct nd_pfn {
        int id;
        u8 *uuid;
        struct device dev;
+       unsigned long align;
        unsigned long npfns;
        enum nd_pfn_mode mode;
        struct nd_pfn_sb *pfn_sb;
index 613ffcca6ecbb9466fc31d2c3d83ac5288b1917f..95ecd7b0fab3a91145dd50c3d9d3fd128aa86ff0 100644 (file)
@@ -103,6 +103,52 @@ static ssize_t mode_store(struct device *dev,
 }
 static DEVICE_ATTR_RW(mode);
 
+static ssize_t align_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct nd_pfn *nd_pfn = to_nd_pfn(dev);
+
+       return sprintf(buf, "%lx\n", nd_pfn->align);
+}
+
+static ssize_t __align_store(struct nd_pfn *nd_pfn, const char *buf)
+{
+       unsigned long val;
+       int rc;
+
+       rc = kstrtoul(buf, 0, &val);
+       if (rc)
+               return rc;
+
+       if (!is_power_of_2(val) || val < PAGE_SIZE || val > SZ_1G)
+               return -EINVAL;
+
+       if (nd_pfn->dev.driver)
+               return -EBUSY;
+       else
+               nd_pfn->align = val;
+
+       return 0;
+}
+
+static ssize_t align_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       struct nd_pfn *nd_pfn = to_nd_pfn(dev);
+       ssize_t rc;
+
+       device_lock(dev);
+       nvdimm_bus_lock(dev);
+       rc = __align_store(nd_pfn, buf);
+       dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
+                       rc, buf, buf[len - 1] == '\n' ? "" : "\n");
+       nvdimm_bus_unlock(dev);
+       device_unlock(dev);
+
+       return rc ? rc : len;
+}
+static DEVICE_ATTR_RW(align);
+
 static ssize_t uuid_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
@@ -164,6 +210,7 @@ static struct attribute *nd_pfn_attributes[] = {
        &dev_attr_mode.attr,
        &dev_attr_namespace.attr,
        &dev_attr_uuid.attr,
+       &dev_attr_align.attr,
        NULL,
 };
 
@@ -199,6 +246,7 @@ static struct device *__nd_pfn_create(struct nd_region *nd_region,
        }
 
        nd_pfn->mode = PFN_MODE_NONE;
+       nd_pfn->align = HPAGE_SIZE;
        dev = &nd_pfn->dev;
        dev_set_name(dev, "pfn%d.%d", nd_region->id, nd_pfn->id);
        dev->parent = &nd_region->dev;
@@ -269,6 +317,12 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn)
                        return -EINVAL;
        }
 
+       if (nd_pfn->align > nvdimm_namespace_capacity(ndns)) {
+               dev_err(&nd_pfn->dev, "alignment: %lx exceeds capacity %llx\n",
+                               nd_pfn->align, nvdimm_namespace_capacity(ndns));
+               return -EINVAL;
+       }
+
        /*
         * These warnings are verbose because they can only trigger in
         * the case where the physical address alignment of the
@@ -283,6 +337,13 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn)
                return -EBUSY;
        }
 
+       nd_pfn->align = 1UL << ilog2(offset);
+       if (!is_power_of_2(offset) || offset < PAGE_SIZE) {
+               dev_err(&nd_pfn->dev, "bad offset: %#llx dax disabled\n",
+                               offset);
+               return -ENXIO;
+       }
+
        return 0;
 }
 EXPORT_SYMBOL(nd_pfn_validate);
index 520c00321dadd033686fac58d06e27008c9e7b96..5ba351e4f26a81c0aa6182791e3ba224c7baa8b0 100644 (file)
@@ -258,9 +258,9 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
         * ->direct_access() to those that are included in the memmap.
         */
        if (nd_pfn->mode == PFN_MODE_PMEM)
-               offset = ALIGN(SZ_8K + 64 * npfns, PMD_SIZE);
+               offset = ALIGN(SZ_8K + 64 * npfns, nd_pfn->align);
        else if (nd_pfn->mode == PFN_MODE_RAM)
-               offset = SZ_8K;
+               offset = ALIGN(SZ_8K, nd_pfn->align);
        else
                goto err;
 
@@ -325,7 +325,7 @@ static int nvdimm_namespace_attach_pfn(struct nd_namespace_common *ndns)
        offset = le64_to_cpu(pfn_sb->dataoff);
        nd_pfn->mode = le32_to_cpu(nd_pfn->pfn_sb->mode);
        if (nd_pfn->mode == PFN_MODE_RAM) {
-               if (offset != SZ_8K)
+               if (offset < SZ_8K)
                        return -EINVAL;
                nd_pfn->npfns = le64_to_cpu(pfn_sb->npfns);
                altmap = NULL;