libnvdimm, nfit: move flush hint mapping to region-device driver-data
authorDan Williams <dan.j.williams@intel.com>
Wed, 8 Jun 2016 00:00:04 +0000 (17:00 -0700)
committerDan Williams <dan.j.williams@intel.com>
Mon, 11 Jul 2016 22:09:26 +0000 (15:09 -0700)
In preparation for triggering flushes of a DIMM's writes-posted-queue
(WPQ) via the pmem driver move mapping of flush hint addresses to the
region driver.  Since this uses devm_nvdimm_memremap() the flush
addresses will remain mapped while any region to which the dimm belongs
is active.

We need to communicate more information to the nvdimm core to facilitate
this mapping, namely each dimm object now carries an array of flush hint
address resources.

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
drivers/acpi/nfit.c
drivers/acpi/nfit.h
drivers/nvdimm/dimm_devs.c
drivers/nvdimm/nd-core.h
drivers/nvdimm/nd.h
drivers/nvdimm/region.c
drivers/nvdimm/region_devs.c
include/linux/libnvdimm.h

index b76c959815474ad655a0388ab666684140c5dfc9..6796f780870ae5113bbb84784a0c09e695e51a2b 100644 (file)
@@ -714,9 +714,24 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc,
                }
 
                list_for_each_entry(nfit_flush, &acpi_desc->flushes, list) {
+                       struct acpi_nfit_flush_address *flush;
+                       u16 i;
+
                        if (nfit_flush->flush->device_handle != device_handle)
                                continue;
                        nfit_mem->nfit_flush = nfit_flush;
+                       flush = nfit_flush->flush;
+                       nfit_mem->flush_wpq = devm_kzalloc(acpi_desc->dev,
+                                       flush->hint_count
+                                       * sizeof(struct resource), GFP_KERNEL);
+                       if (!nfit_mem->flush_wpq)
+                               return -ENOMEM;
+                       for (i = 0; i < flush->hint_count; i++) {
+                               struct resource *res = &nfit_mem->flush_wpq[i];
+
+                               res->start = flush->hint_address[i];
+                               res->end = res->start + 8 - 1;
+                       }
                        break;
                }
 
@@ -1171,6 +1186,7 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
        int dimm_count = 0;
 
        list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
+               struct acpi_nfit_flush_address *flush;
                unsigned long flags = 0, cmd_mask;
                struct nvdimm *nvdimm;
                u32 device_handle;
@@ -1204,9 +1220,12 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
                if (nfit_mem->family == NVDIMM_FAMILY_INTEL)
                        cmd_mask |= nfit_mem->dsm_mask;
 
+               flush = nfit_mem->nfit_flush ? nfit_mem->nfit_flush->flush
+                       : NULL;
                nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem,
                                acpi_nfit_dimm_attribute_groups,
-                               flags, cmd_mask);
+                               flags, cmd_mask, flush ? flush->hint_count : 0,
+                               nfit_mem->flush_wpq);
                if (!nvdimm)
                        return -ENOMEM;
 
index 52078475d9696c5d15989dac631e7e7004fde3fc..9282eb324dcc04d10e284b415d79a6ea504276f1 100644 (file)
@@ -127,6 +127,7 @@ struct nfit_mem {
        struct list_head list;
        struct acpi_device *adev;
        struct acpi_nfit_desc *acpi_desc;
+       struct resource *flush_wpq;
        unsigned long dsm_mask;
        int family;
 };
index bbde28d3dec5cd4bbbb283847d2f736bc81148c9..d9bba5edd8dcf0646cad13a0160534648c52ce8d 100644 (file)
@@ -346,7 +346,8 @@ EXPORT_SYMBOL_GPL(nvdimm_attribute_group);
 
 struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
                const struct attribute_group **groups, unsigned long flags,
-               unsigned long cmd_mask)
+               unsigned long cmd_mask, int num_flush,
+               struct resource *flush_wpq)
 {
        struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL);
        struct device *dev;
@@ -362,6 +363,8 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
        nvdimm->provider_data = provider_data;
        nvdimm->flags = flags;
        nvdimm->cmd_mask = cmd_mask;
+       nvdimm->num_flush = num_flush;
+       nvdimm->flush_wpq = flush_wpq;
        atomic_set(&nvdimm->busy, 0);
        dev = &nvdimm->dev;
        dev_set_name(dev, "nmem%d", nvdimm->id);
index 790b62cc81eddf8e1faa90003959b23bc326cfe7..6e961f7f43e7817b62935a358a8c5bfe04ab1f09 100644 (file)
@@ -41,7 +41,8 @@ struct nvdimm {
        unsigned long cmd_mask;
        struct device dev;
        atomic_t busy;
-       int id;
+       int id, num_flush;
+       struct resource *flush_wpq;
 };
 
 bool is_nvdimm(struct device *dev);
index 2819e886dfd2dcd48c813dcac2b1cde92e85cc5c..5912bd6b423459aa7f67964e91fdf09a5e525f69 100644 (file)
@@ -49,9 +49,10 @@ struct nvdimm_drvdata {
        struct kref kref;
 };
 
-struct nd_region_namespaces {
-       int count;
-       int active;
+struct nd_region_data {
+       int ns_count;
+       int ns_active;
+       void __iomem *flush_wpq[0][0];
 };
 
 static inline struct nd_namespace_index *to_namespace_index(
@@ -324,6 +325,7 @@ static inline void devm_nsio_disable(struct device *dev,
 }
 #endif
 int nd_blk_region_init(struct nd_region *nd_region);
+int nd_region_activate(struct nd_region *nd_region);
 void __nd_iostat_start(struct bio *bio, unsigned long *start);
 static inline bool nd_iostat_start(struct bio *bio, unsigned long *start)
 {
index 05a91235993969752a8e1edad056a036847923e5..333175dac8d5b4bf258ae84344ce76c4c192188e 100644 (file)
@@ -20,7 +20,7 @@ static int nd_region_probe(struct device *dev)
 {
        int err, rc;
        static unsigned long once;
-       struct nd_region_namespaces *num_ns;
+       struct nd_region_data *ndrd;
        struct nd_region *nd_region = to_nd_region(dev);
 
        if (nd_region->num_lanes > num_online_cpus()
@@ -33,21 +33,21 @@ static int nd_region_probe(struct device *dev)
                                nd_region->num_lanes);
        }
 
+       rc = nd_region_activate(nd_region);
+       if (rc)
+               return rc;
+
        rc = nd_blk_region_init(nd_region);
        if (rc)
                return rc;
 
        rc = nd_region_register_namespaces(nd_region, &err);
-       num_ns = devm_kzalloc(dev, sizeof(*num_ns), GFP_KERNEL);
-       if (!num_ns)
-               return -ENOMEM;
-
        if (rc < 0)
                return rc;
 
-       num_ns->active = rc;
-       num_ns->count = rc + err;
-       dev_set_drvdata(dev, num_ns);
+       ndrd = dev_get_drvdata(dev);
+       ndrd->ns_active = rc;
+       ndrd->ns_count = rc + err;
 
        if (rc && err && rc == err)
                return -ENODEV;
index 694b21024871abe6788eea46c5c0e9f95a4e2b14..67022f74febc9f49467a35f0a5026adcbcdc39f3 100644 (file)
 
 static DEFINE_IDA(region_ida);
 
+static int nvdimm_map_flush(struct device *dev, struct nvdimm *nvdimm, int dimm,
+               struct nd_region_data *ndrd)
+{
+       int i, j;
+
+       dev_dbg(dev, "%s: map %d flush address%s\n", nvdimm_name(nvdimm),
+                       nvdimm->num_flush, nvdimm->num_flush == 1 ? "" : "es");
+       for (i = 0; i < nvdimm->num_flush; i++) {
+               struct resource *res = &nvdimm->flush_wpq[i];
+               unsigned long pfn = PHYS_PFN(res->start);
+               void __iomem *flush_page;
+
+               /* check if flush hints share a page */
+               for (j = 0; j < i; j++) {
+                       struct resource *res_j = &nvdimm->flush_wpq[j];
+                       unsigned long pfn_j = PHYS_PFN(res_j->start);
+
+                       if (pfn == pfn_j)
+                               break;
+               }
+
+               if (j < i)
+                       flush_page = (void __iomem *) ((unsigned long)
+                                       ndrd->flush_wpq[dimm][j] & PAGE_MASK);
+               else
+                       flush_page = devm_nvdimm_ioremap(dev,
+                                       PHYS_PFN(pfn), PAGE_SIZE);
+               if (!flush_page)
+                       return -ENXIO;
+               ndrd->flush_wpq[dimm][i] = flush_page
+                       + (res->start & ~PAGE_MASK);
+       }
+
+       return 0;
+}
+
+int nd_region_activate(struct nd_region *nd_region)
+{
+       int i;
+       struct nd_region_data *ndrd;
+       struct device *dev = &nd_region->dev;
+       size_t flush_data_size = sizeof(void *);
+
+       nvdimm_bus_lock(&nd_region->dev);
+       for (i = 0; i < nd_region->ndr_mappings; i++) {
+               struct nd_mapping *nd_mapping = &nd_region->mapping[i];
+               struct nvdimm *nvdimm = nd_mapping->nvdimm;
+
+               /* at least one null hint slot per-dimm for the "no-hint" case */
+               flush_data_size += sizeof(void *);
+               if (!nvdimm->num_flush)
+                       continue;
+               flush_data_size += nvdimm->num_flush * sizeof(void *);
+       }
+       nvdimm_bus_unlock(&nd_region->dev);
+
+       ndrd = devm_kzalloc(dev, sizeof(*ndrd) + flush_data_size, GFP_KERNEL);
+       if (!ndrd)
+               return -ENOMEM;
+       dev_set_drvdata(dev, ndrd);
+
+       for (i = 0; i < nd_region->ndr_mappings; i++) {
+               struct nd_mapping *nd_mapping = &nd_region->mapping[i];
+               struct nvdimm *nvdimm = nd_mapping->nvdimm;
+               int rc = nvdimm_map_flush(&nd_region->dev, nvdimm, i, ndrd);
+
+               if (rc)
+                       return rc;
+       }
+
+       return 0;
+}
+
 static void nd_region_release(struct device *dev)
 {
        struct nd_region *nd_region = to_nd_region(dev);
@@ -242,12 +315,12 @@ static DEVICE_ATTR_RO(available_size);
 static ssize_t init_namespaces_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       struct nd_region_namespaces *num_ns = dev_get_drvdata(dev);
+       struct nd_region_data *ndrd = dev_get_drvdata(dev);
        ssize_t rc;
 
        nvdimm_bus_lock(dev);
-       if (num_ns)
-               rc = sprintf(buf, "%d/%d\n", num_ns->active, num_ns->count);
+       if (ndrd)
+               rc = sprintf(buf, "%d/%d\n", ndrd->ns_active, ndrd->ns_count);
        else
                rc = -ENXIO;
        nvdimm_bus_unlock(dev);
index 1050f9aa3a3ea7a409ef38a60c52859c26f2703f..815b9b430ead6e241fbe7b47f07ad46af20aec2c 100644 (file)
@@ -52,6 +52,7 @@ typedef int (*ndctl_fn)(struct nvdimm_bus_descriptor *nd_desc,
 
 struct nd_namespace_label;
 struct nvdimm_drvdata;
+
 struct nd_mapping {
        struct nvdimm *nvdimm;
        struct nd_namespace_label **labels;
@@ -142,7 +143,8 @@ unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm);
 void *nvdimm_provider_data(struct nvdimm *nvdimm);
 struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
                const struct attribute_group **groups, unsigned long flags,
-               unsigned long cmd_mask);
+               unsigned long cmd_mask, int num_flush,
+               struct resource *flush_wpq);
 const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd);
 const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd);
 u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd,
This page took 0.034014 seconds and 5 git commands to generate.