aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2016-06-07 20:00:04 -0400
committerDan Williams <dan.j.williams@intel.com>2016-07-11 18:09:26 -0400
commite5ae3b252c6732f838f5695170bbf2ea9fb5b9ff (patch)
tree33db4ea36acf0b1fe329e592b4cac6c266dfa7f5
parenta8a6d2e04c4ffda055db70814c50bd106e44730f (diff)
libnvdimm, nfit: move flush hint mapping to region-device driver-data
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>
-rw-r--r--drivers/acpi/nfit.c21
-rw-r--r--drivers/acpi/nfit.h1
-rw-r--r--drivers/nvdimm/dimm_devs.c5
-rw-r--r--drivers/nvdimm/nd-core.h3
-rw-r--r--drivers/nvdimm/nd.h8
-rw-r--r--drivers/nvdimm/region.c16
-rw-r--r--drivers/nvdimm/region_devs.c79
-rw-r--r--include/linux/libnvdimm.h4
8 files changed, 119 insertions, 18 deletions
diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c
index b76c95981547..6796f780870a 100644
--- a/drivers/acpi/nfit.c
+++ b/drivers/acpi/nfit.c
@@ -714,9 +714,24 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc,
714 } 714 }
715 715
716 list_for_each_entry(nfit_flush, &acpi_desc->flushes, list) { 716 list_for_each_entry(nfit_flush, &acpi_desc->flushes, list) {
717 struct acpi_nfit_flush_address *flush;
718 u16 i;
719
717 if (nfit_flush->flush->device_handle != device_handle) 720 if (nfit_flush->flush->device_handle != device_handle)
718 continue; 721 continue;
719 nfit_mem->nfit_flush = nfit_flush; 722 nfit_mem->nfit_flush = nfit_flush;
723 flush = nfit_flush->flush;
724 nfit_mem->flush_wpq = devm_kzalloc(acpi_desc->dev,
725 flush->hint_count
726 * sizeof(struct resource), GFP_KERNEL);
727 if (!nfit_mem->flush_wpq)
728 return -ENOMEM;
729 for (i = 0; i < flush->hint_count; i++) {
730 struct resource *res = &nfit_mem->flush_wpq[i];
731
732 res->start = flush->hint_address[i];
733 res->end = res->start + 8 - 1;
734 }
720 break; 735 break;
721 } 736 }
722 737
@@ -1171,6 +1186,7 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
1171 int dimm_count = 0; 1186 int dimm_count = 0;
1172 1187
1173 list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) { 1188 list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
1189 struct acpi_nfit_flush_address *flush;
1174 unsigned long flags = 0, cmd_mask; 1190 unsigned long flags = 0, cmd_mask;
1175 struct nvdimm *nvdimm; 1191 struct nvdimm *nvdimm;
1176 u32 device_handle; 1192 u32 device_handle;
@@ -1204,9 +1220,12 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
1204 if (nfit_mem->family == NVDIMM_FAMILY_INTEL) 1220 if (nfit_mem->family == NVDIMM_FAMILY_INTEL)
1205 cmd_mask |= nfit_mem->dsm_mask; 1221 cmd_mask |= nfit_mem->dsm_mask;
1206 1222
1223 flush = nfit_mem->nfit_flush ? nfit_mem->nfit_flush->flush
1224 : NULL;
1207 nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem, 1225 nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem,
1208 acpi_nfit_dimm_attribute_groups, 1226 acpi_nfit_dimm_attribute_groups,
1209 flags, cmd_mask); 1227 flags, cmd_mask, flush ? flush->hint_count : 0,
1228 nfit_mem->flush_wpq);
1210 if (!nvdimm) 1229 if (!nvdimm)
1211 return -ENOMEM; 1230 return -ENOMEM;
1212 1231
diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h
index 52078475d969..9282eb324dcc 100644
--- a/drivers/acpi/nfit.h
+++ b/drivers/acpi/nfit.h
@@ -127,6 +127,7 @@ struct nfit_mem {
127 struct list_head list; 127 struct list_head list;
128 struct acpi_device *adev; 128 struct acpi_device *adev;
129 struct acpi_nfit_desc *acpi_desc; 129 struct acpi_nfit_desc *acpi_desc;
130 struct resource *flush_wpq;
130 unsigned long dsm_mask; 131 unsigned long dsm_mask;
131 int family; 132 int family;
132}; 133};
diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c
index bbde28d3dec5..d9bba5edd8dc 100644
--- a/drivers/nvdimm/dimm_devs.c
+++ b/drivers/nvdimm/dimm_devs.c
@@ -346,7 +346,8 @@ EXPORT_SYMBOL_GPL(nvdimm_attribute_group);
346 346
347struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, 347struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
348 const struct attribute_group **groups, unsigned long flags, 348 const struct attribute_group **groups, unsigned long flags,
349 unsigned long cmd_mask) 349 unsigned long cmd_mask, int num_flush,
350 struct resource *flush_wpq)
350{ 351{
351 struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL); 352 struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL);
352 struct device *dev; 353 struct device *dev;
@@ -362,6 +363,8 @@ struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
362 nvdimm->provider_data = provider_data; 363 nvdimm->provider_data = provider_data;
363 nvdimm->flags = flags; 364 nvdimm->flags = flags;
364 nvdimm->cmd_mask = cmd_mask; 365 nvdimm->cmd_mask = cmd_mask;
366 nvdimm->num_flush = num_flush;
367 nvdimm->flush_wpq = flush_wpq;
365 atomic_set(&nvdimm->busy, 0); 368 atomic_set(&nvdimm->busy, 0);
366 dev = &nvdimm->dev; 369 dev = &nvdimm->dev;
367 dev_set_name(dev, "nmem%d", nvdimm->id); 370 dev_set_name(dev, "nmem%d", nvdimm->id);
diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h
index 790b62cc81ed..6e961f7f43e7 100644
--- a/drivers/nvdimm/nd-core.h
+++ b/drivers/nvdimm/nd-core.h
@@ -41,7 +41,8 @@ struct nvdimm {
41 unsigned long cmd_mask; 41 unsigned long cmd_mask;
42 struct device dev; 42 struct device dev;
43 atomic_t busy; 43 atomic_t busy;
44 int id; 44 int id, num_flush;
45 struct resource *flush_wpq;
45}; 46};
46 47
47bool is_nvdimm(struct device *dev); 48bool is_nvdimm(struct device *dev);
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index 2819e886dfd2..5912bd6b4234 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -49,9 +49,10 @@ struct nvdimm_drvdata {
49 struct kref kref; 49 struct kref kref;
50}; 50};
51 51
52struct nd_region_namespaces { 52struct nd_region_data {
53 int count; 53 int ns_count;
54 int active; 54 int ns_active;
55 void __iomem *flush_wpq[0][0];
55}; 56};
56 57
57static inline struct nd_namespace_index *to_namespace_index( 58static inline struct nd_namespace_index *to_namespace_index(
@@ -324,6 +325,7 @@ static inline void devm_nsio_disable(struct device *dev,
324} 325}
325#endif 326#endif
326int nd_blk_region_init(struct nd_region *nd_region); 327int nd_blk_region_init(struct nd_region *nd_region);
328int nd_region_activate(struct nd_region *nd_region);
327void __nd_iostat_start(struct bio *bio, unsigned long *start); 329void __nd_iostat_start(struct bio *bio, unsigned long *start);
328static inline bool nd_iostat_start(struct bio *bio, unsigned long *start) 330static inline bool nd_iostat_start(struct bio *bio, unsigned long *start)
329{ 331{
diff --git a/drivers/nvdimm/region.c b/drivers/nvdimm/region.c
index 05a912359939..333175dac8d5 100644
--- a/drivers/nvdimm/region.c
+++ b/drivers/nvdimm/region.c
@@ -20,7 +20,7 @@ static int nd_region_probe(struct device *dev)
20{ 20{
21 int err, rc; 21 int err, rc;
22 static unsigned long once; 22 static unsigned long once;
23 struct nd_region_namespaces *num_ns; 23 struct nd_region_data *ndrd;
24 struct nd_region *nd_region = to_nd_region(dev); 24 struct nd_region *nd_region = to_nd_region(dev);
25 25
26 if (nd_region->num_lanes > num_online_cpus() 26 if (nd_region->num_lanes > num_online_cpus()
@@ -33,21 +33,21 @@ static int nd_region_probe(struct device *dev)
33 nd_region->num_lanes); 33 nd_region->num_lanes);
34 } 34 }
35 35
36 rc = nd_region_activate(nd_region);
37 if (rc)
38 return rc;
39
36 rc = nd_blk_region_init(nd_region); 40 rc = nd_blk_region_init(nd_region);
37 if (rc) 41 if (rc)
38 return rc; 42 return rc;
39 43
40 rc = nd_region_register_namespaces(nd_region, &err); 44 rc = nd_region_register_namespaces(nd_region, &err);
41 num_ns = devm_kzalloc(dev, sizeof(*num_ns), GFP_KERNEL);
42 if (!num_ns)
43 return -ENOMEM;
44
45 if (rc < 0) 45 if (rc < 0)
46 return rc; 46 return rc;
47 47
48 num_ns->active = rc; 48 ndrd = dev_get_drvdata(dev);
49 num_ns->count = rc + err; 49 ndrd->ns_active = rc;
50 dev_set_drvdata(dev, num_ns); 50 ndrd->ns_count = rc + err;
51 51
52 if (rc && err && rc == err) 52 if (rc && err && rc == err)
53 return -ENODEV; 53 return -ENODEV;
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index 694b21024871..67022f74febc 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -22,6 +22,79 @@
22 22
23static DEFINE_IDA(region_ida); 23static DEFINE_IDA(region_ida);
24 24
25static int nvdimm_map_flush(struct device *dev, struct nvdimm *nvdimm, int dimm,
26 struct nd_region_data *ndrd)
27{
28 int i, j;
29
30 dev_dbg(dev, "%s: map %d flush address%s\n", nvdimm_name(nvdimm),
31 nvdimm->num_flush, nvdimm->num_flush == 1 ? "" : "es");
32 for (i = 0; i < nvdimm->num_flush; i++) {
33 struct resource *res = &nvdimm->flush_wpq[i];
34 unsigned long pfn = PHYS_PFN(res->start);
35 void __iomem *flush_page;
36
37 /* check if flush hints share a page */
38 for (j = 0; j < i; j++) {
39 struct resource *res_j = &nvdimm->flush_wpq[j];
40 unsigned long pfn_j = PHYS_PFN(res_j->start);
41
42 if (pfn == pfn_j)
43 break;
44 }
45
46 if (j < i)
47 flush_page = (void __iomem *) ((unsigned long)
48 ndrd->flush_wpq[dimm][j] & PAGE_MASK);
49 else
50 flush_page = devm_nvdimm_ioremap(dev,
51 PHYS_PFN(pfn), PAGE_SIZE);
52 if (!flush_page)
53 return -ENXIO;
54 ndrd->flush_wpq[dimm][i] = flush_page
55 + (res->start & ~PAGE_MASK);
56 }
57
58 return 0;
59}
60
61int nd_region_activate(struct nd_region *nd_region)
62{
63 int i;
64 struct nd_region_data *ndrd;
65 struct device *dev = &nd_region->dev;
66 size_t flush_data_size = sizeof(void *);
67
68 nvdimm_bus_lock(&nd_region->dev);
69 for (i = 0; i < nd_region->ndr_mappings; i++) {
70 struct nd_mapping *nd_mapping = &nd_region->mapping[i];
71 struct nvdimm *nvdimm = nd_mapping->nvdimm;
72
73 /* at least one null hint slot per-dimm for the "no-hint" case */
74 flush_data_size += sizeof(void *);
75 if (!nvdimm->num_flush)
76 continue;
77 flush_data_size += nvdimm->num_flush * sizeof(void *);
78 }
79 nvdimm_bus_unlock(&nd_region->dev);
80
81 ndrd = devm_kzalloc(dev, sizeof(*ndrd) + flush_data_size, GFP_KERNEL);
82 if (!ndrd)
83 return -ENOMEM;
84 dev_set_drvdata(dev, ndrd);
85
86 for (i = 0; i < nd_region->ndr_mappings; i++) {
87 struct nd_mapping *nd_mapping = &nd_region->mapping[i];
88 struct nvdimm *nvdimm = nd_mapping->nvdimm;
89 int rc = nvdimm_map_flush(&nd_region->dev, nvdimm, i, ndrd);
90
91 if (rc)
92 return rc;
93 }
94
95 return 0;
96}
97
25static void nd_region_release(struct device *dev) 98static void nd_region_release(struct device *dev)
26{ 99{
27 struct nd_region *nd_region = to_nd_region(dev); 100 struct nd_region *nd_region = to_nd_region(dev);
@@ -242,12 +315,12 @@ static DEVICE_ATTR_RO(available_size);
242static ssize_t init_namespaces_show(struct device *dev, 315static ssize_t init_namespaces_show(struct device *dev,
243 struct device_attribute *attr, char *buf) 316 struct device_attribute *attr, char *buf)
244{ 317{
245 struct nd_region_namespaces *num_ns = dev_get_drvdata(dev); 318 struct nd_region_data *ndrd = dev_get_drvdata(dev);
246 ssize_t rc; 319 ssize_t rc;
247 320
248 nvdimm_bus_lock(dev); 321 nvdimm_bus_lock(dev);
249 if (num_ns) 322 if (ndrd)
250 rc = sprintf(buf, "%d/%d\n", num_ns->active, num_ns->count); 323 rc = sprintf(buf, "%d/%d\n", ndrd->ns_active, ndrd->ns_count);
251 else 324 else
252 rc = -ENXIO; 325 rc = -ENXIO;
253 nvdimm_bus_unlock(dev); 326 nvdimm_bus_unlock(dev);
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index 1050f9aa3a3e..815b9b430ead 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -52,6 +52,7 @@ typedef int (*ndctl_fn)(struct nvdimm_bus_descriptor *nd_desc,
52 52
53struct nd_namespace_label; 53struct nd_namespace_label;
54struct nvdimm_drvdata; 54struct nvdimm_drvdata;
55
55struct nd_mapping { 56struct nd_mapping {
56 struct nvdimm *nvdimm; 57 struct nvdimm *nvdimm;
57 struct nd_namespace_label **labels; 58 struct nd_namespace_label **labels;
@@ -142,7 +143,8 @@ unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm);
142void *nvdimm_provider_data(struct nvdimm *nvdimm); 143void *nvdimm_provider_data(struct nvdimm *nvdimm);
143struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, 144struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
144 const struct attribute_group **groups, unsigned long flags, 145 const struct attribute_group **groups, unsigned long flags,
145 unsigned long cmd_mask); 146 unsigned long cmd_mask, int num_flush,
147 struct resource *flush_wpq);
146const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd); 148const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd);
147const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd); 149const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd);
148u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd, 150u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd,