aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2015-06-23 20:08:34 -0400
committerDan Williams <dan.j.williams@intel.com>2015-06-26 11:23:38 -0400
commit581388209405902b56d055f644b4dd124a206112 (patch)
tree2160b6616cf072396067ca654cd5231e139fc304
parent0f51c4fa7f60838a87cd45e8ba144dddcd4c066c (diff)
libnvdimm, nfit: handle unarmed dimms, mark namespaces read-only
Upon detection of an unarmed dimm in a region, arrange for descendant BTT, PMEM, or BLK instances to be read-only. A dimm is primarily marked "unarmed" via flags passed by platform firmware (NFIT). The flags in the NFIT memory device sub-structure indicate the state of the data on the nvdimm relative to its energy source or last "flush to persistence". For the most part there is nothing the driver can do but advertise the state of these flags in sysfs and emit a message if firmware indicates that the contents of the device may be corrupted. However, for the case of ACPI_NFIT_MEM_ARMED, the driver can arrange for the block devices incorporating that nvdimm to be marked read-only. This is a safe default as the data is still available and new writes are held off until the administrator either forces read-write mode, or the energy source becomes armed. A 'read_only' attribute is added to REGION devices to allow for overriding the default read-only policy of all descendant block devices. Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--drivers/acpi/nfit.c31
-rw-r--r--drivers/acpi/nfit.h3
-rw-r--r--drivers/nvdimm/blk.c2
-rw-r--r--drivers/nvdimm/btt.c10
-rw-r--r--drivers/nvdimm/bus.c18
-rw-r--r--drivers/nvdimm/nd.h3
-rw-r--r--drivers/nvdimm/pmem.c2
-rw-r--r--drivers/nvdimm/region_devs.c29
-rw-r--r--include/linux/libnvdimm.h2
-rw-r--r--tools/testing/nvdimm/test/nfit.c3
10 files changed, 100 insertions, 3 deletions
diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c
index 07d630e9f4ae..1f6f1b1a54f4 100644
--- a/drivers/acpi/nfit.c
+++ b/drivers/acpi/nfit.c
@@ -668,6 +668,20 @@ static ssize_t serial_show(struct device *dev,
668} 668}
669static DEVICE_ATTR_RO(serial); 669static DEVICE_ATTR_RO(serial);
670 670
671static ssize_t flags_show(struct device *dev,
672 struct device_attribute *attr, char *buf)
673{
674 u16 flags = to_nfit_memdev(dev)->flags;
675
676 return sprintf(buf, "%s%s%s%s%s\n",
677 flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save " : "",
678 flags & ACPI_NFIT_MEM_RESTORE_FAILED ? "restore " : "",
679 flags & ACPI_NFIT_MEM_FLUSH_FAILED ? "flush " : "",
680 flags & ACPI_NFIT_MEM_ARMED ? "arm " : "",
681 flags & ACPI_NFIT_MEM_HEALTH_OBSERVED ? "smart " : "");
682}
683static DEVICE_ATTR_RO(flags);
684
671static struct attribute *acpi_nfit_dimm_attributes[] = { 685static struct attribute *acpi_nfit_dimm_attributes[] = {
672 &dev_attr_handle.attr, 686 &dev_attr_handle.attr,
673 &dev_attr_phys_id.attr, 687 &dev_attr_phys_id.attr,
@@ -676,6 +690,7 @@ static struct attribute *acpi_nfit_dimm_attributes[] = {
676 &dev_attr_format.attr, 690 &dev_attr_format.attr,
677 &dev_attr_serial.attr, 691 &dev_attr_serial.attr,
678 &dev_attr_rev_id.attr, 692 &dev_attr_rev_id.attr,
693 &dev_attr_flags.attr,
679 NULL, 694 NULL,
680}; 695};
681 696
@@ -768,6 +783,7 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
768 struct nvdimm *nvdimm; 783 struct nvdimm *nvdimm;
769 unsigned long flags = 0; 784 unsigned long flags = 0;
770 u32 device_handle; 785 u32 device_handle;
786 u16 mem_flags;
771 int rc; 787 int rc;
772 788
773 device_handle = __to_nfit_memdev(nfit_mem)->device_handle; 789 device_handle = __to_nfit_memdev(nfit_mem)->device_handle;
@@ -785,6 +801,10 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
785 if (nfit_mem->bdw && nfit_mem->memdev_pmem) 801 if (nfit_mem->bdw && nfit_mem->memdev_pmem)
786 flags |= NDD_ALIASING; 802 flags |= NDD_ALIASING;
787 803
804 mem_flags = __to_nfit_memdev(nfit_mem)->flags;
805 if (mem_flags & ACPI_NFIT_MEM_ARMED)
806 flags |= NDD_UNARMED;
807
788 rc = acpi_nfit_add_dimm(acpi_desc, nfit_mem, device_handle); 808 rc = acpi_nfit_add_dimm(acpi_desc, nfit_mem, device_handle);
789 if (rc) 809 if (rc)
790 continue; 810 continue;
@@ -797,6 +817,17 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
797 817
798 nfit_mem->nvdimm = nvdimm; 818 nfit_mem->nvdimm = nvdimm;
799 dimm_count++; 819 dimm_count++;
820
821 if ((mem_flags & ACPI_NFIT_MEM_FAILED_MASK) == 0)
822 continue;
823
824 dev_info(acpi_desc->dev, "%s: failed: %s%s%s%s\n",
825 nvdimm_name(nvdimm),
826 mem_flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save " : "",
827 mem_flags & ACPI_NFIT_MEM_RESTORE_FAILED ? "restore " : "",
828 mem_flags & ACPI_NFIT_MEM_FLUSH_FAILED ? "flush " : "",
829 mem_flags & ACPI_NFIT_MEM_ARMED ? "arm " : "");
830
800 } 831 }
801 832
802 return nvdimm_bus_check_dimm_count(acpi_desc->nvdimm_bus, dimm_count); 833 return nvdimm_bus_check_dimm_count(acpi_desc->nvdimm_bus, dimm_count);
diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h
index c62fffea8423..81f2e8c5a79c 100644
--- a/drivers/acpi/nfit.h
+++ b/drivers/acpi/nfit.h
@@ -22,6 +22,9 @@
22 22
23#define UUID_NFIT_BUS "2f10e7a4-9e91-11e4-89d3-123b93f75cba" 23#define UUID_NFIT_BUS "2f10e7a4-9e91-11e4-89d3-123b93f75cba"
24#define UUID_NFIT_DIMM "4309ac30-0d11-11e4-9191-0800200c9a66" 24#define UUID_NFIT_DIMM "4309ac30-0d11-11e4-9191-0800200c9a66"
25#define ACPI_NFIT_MEM_FAILED_MASK (ACPI_NFIT_MEM_SAVE_FAILED \
26 | ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \
27 | ACPI_NFIT_MEM_ARMED)
25 28
26enum nfit_uuids { 29enum nfit_uuids {
27 NFIT_SPA_VOLATILE, 30 NFIT_SPA_VOLATILE,
diff --git a/drivers/nvdimm/blk.c b/drivers/nvdimm/blk.c
index 96ef38ceeceb..4f97b248c236 100644
--- a/drivers/nvdimm/blk.c
+++ b/drivers/nvdimm/blk.c
@@ -232,6 +232,7 @@ static int nd_blk_rw_bytes(struct nd_namespace_common *ndns,
232 232
233static const struct block_device_operations nd_blk_fops = { 233static const struct block_device_operations nd_blk_fops = {
234 .owner = THIS_MODULE, 234 .owner = THIS_MODULE,
235 .revalidate_disk = nvdimm_revalidate_disk,
235}; 236};
236 237
237static int nd_blk_attach_disk(struct nd_namespace_common *ndns, 238static int nd_blk_attach_disk(struct nd_namespace_common *ndns,
@@ -283,6 +284,7 @@ static int nd_blk_attach_disk(struct nd_namespace_common *ndns,
283 } 284 }
284 285
285 set_capacity(disk, available_disk_size >> SECTOR_SHIFT); 286 set_capacity(disk, available_disk_size >> SECTOR_SHIFT);
287 revalidate_disk(disk);
286 return 0; 288 return 0;
287} 289}
288 290
diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c
index c02065aed03d..411c7b2bb37a 100644
--- a/drivers/nvdimm/btt.c
+++ b/drivers/nvdimm/btt.c
@@ -1245,6 +1245,7 @@ static const struct block_device_operations btt_fops = {
1245 .owner = THIS_MODULE, 1245 .owner = THIS_MODULE,
1246 .rw_page = btt_rw_page, 1246 .rw_page = btt_rw_page,
1247 .getgeo = btt_getgeo, 1247 .getgeo = btt_getgeo,
1248 .revalidate_disk = nvdimm_revalidate_disk,
1248}; 1249};
1249 1250
1250static int btt_blk_init(struct btt *btt) 1251static int btt_blk_init(struct btt *btt)
@@ -1292,6 +1293,7 @@ static int btt_blk_init(struct btt *btt)
1292 } 1293 }
1293 } 1294 }
1294 set_capacity(btt->btt_disk, btt->nlba * btt->sector_size >> 9); 1295 set_capacity(btt->btt_disk, btt->nlba * btt->sector_size >> 9);
1296 revalidate_disk(btt->btt_disk);
1295 1297
1296 return 0; 1298 return 0;
1297} 1299}
@@ -1346,7 +1348,11 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize,
1346 goto out_free; 1348 goto out_free;
1347 } 1349 }
1348 1350
1349 if (btt->init_state != INIT_READY) { 1351 if (btt->init_state != INIT_READY && nd_region->ro) {
1352 dev_info(dev, "%s is read-only, unable to init btt metadata\n",
1353 dev_name(&nd_region->dev));
1354 goto out_free;
1355 } else if (btt->init_state != INIT_READY) {
1350 btt->num_arenas = (rawsize / ARENA_MAX_SIZE) + 1356 btt->num_arenas = (rawsize / ARENA_MAX_SIZE) +
1351 ((rawsize % ARENA_MAX_SIZE) ? 1 : 0); 1357 ((rawsize % ARENA_MAX_SIZE) ? 1 : 0);
1352 dev_dbg(dev, "init: %d arenas for %llu rawsize\n", 1358 dev_dbg(dev, "init: %d arenas for %llu rawsize\n",
@@ -1361,7 +1367,7 @@ static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize,
1361 ret = btt_meta_init(btt); 1367 ret = btt_meta_init(btt);
1362 if (ret) { 1368 if (ret) {
1363 dev_err(dev, "init: error in meta_init: %d\n", ret); 1369 dev_err(dev, "init: error in meta_init: %d\n", ret);
1364 return NULL; 1370 goto out_free;
1365 } 1371 }
1366 } 1372 }
1367 1373
diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c
index dd12f38397db..ec59f1f26d95 100644
--- a/drivers/nvdimm/bus.c
+++ b/drivers/nvdimm/bus.c
@@ -227,6 +227,24 @@ int __nd_driver_register(struct nd_device_driver *nd_drv, struct module *owner,
227} 227}
228EXPORT_SYMBOL(__nd_driver_register); 228EXPORT_SYMBOL(__nd_driver_register);
229 229
230int nvdimm_revalidate_disk(struct gendisk *disk)
231{
232 struct device *dev = disk->driverfs_dev;
233 struct nd_region *nd_region = to_nd_region(dev->parent);
234 const char *pol = nd_region->ro ? "only" : "write";
235
236 if (nd_region->ro == get_disk_ro(disk))
237 return 0;
238
239 dev_info(dev, "%s read-%s, marking %s read-%s\n",
240 dev_name(&nd_region->dev), pol, disk->disk_name, pol);
241 set_disk_ro(disk, nd_region->ro);
242
243 return 0;
244
245}
246EXPORT_SYMBOL(nvdimm_revalidate_disk);
247
230static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 248static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
231 char *buf) 249 char *buf)
232{ 250{
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index 4614b00542d1..48b09a210689 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -97,7 +97,7 @@ struct nd_region {
97 u16 ndr_mappings; 97 u16 ndr_mappings;
98 u64 ndr_size; 98 u64 ndr_size;
99 u64 ndr_start; 99 u64 ndr_start;
100 int id, num_lanes; 100 int id, num_lanes, ro;
101 void *provider_data; 101 void *provider_data;
102 struct nd_interleave_set *nd_set; 102 struct nd_interleave_set *nd_set;
103 struct nd_percpu_lane __percpu *lane; 103 struct nd_percpu_lane __percpu *lane;
@@ -189,6 +189,7 @@ u64 nd_region_interleave_set_cookie(struct nd_region *nd_region);
189void nvdimm_bus_lock(struct device *dev); 189void nvdimm_bus_lock(struct device *dev);
190void nvdimm_bus_unlock(struct device *dev); 190void nvdimm_bus_unlock(struct device *dev);
191bool is_nvdimm_bus_locked(struct device *dev); 191bool is_nvdimm_bus_locked(struct device *dev);
192int nvdimm_revalidate_disk(struct gendisk *disk);
192void nvdimm_drvdata_release(struct kref *kref); 193void nvdimm_drvdata_release(struct kref *kref);
193void put_ndd(struct nvdimm_drvdata *ndd); 194void put_ndd(struct nvdimm_drvdata *ndd);
194int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd); 195int nd_label_reserve_dpa(struct nvdimm_drvdata *ndd);
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index a9709db0704c..42b766f33e59 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -104,6 +104,7 @@ static const struct block_device_operations pmem_fops = {
104 .owner = THIS_MODULE, 104 .owner = THIS_MODULE,
105 .rw_page = pmem_rw_page, 105 .rw_page = pmem_rw_page,
106 .direct_access = pmem_direct_access, 106 .direct_access = pmem_direct_access,
107 .revalidate_disk = nvdimm_revalidate_disk,
107}; 108};
108 109
109static struct pmem_device *pmem_alloc(struct device *dev, 110static struct pmem_device *pmem_alloc(struct device *dev,
@@ -178,6 +179,7 @@ static int pmem_attach_disk(struct nd_namespace_common *ndns,
178 pmem->pmem_disk = disk; 179 pmem->pmem_disk = disk;
179 180
180 add_disk(disk); 181 add_disk(disk);
182 revalidate_disk(disk);
181 183
182 return 0; 184 return 0;
183} 185}
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index 2cfb3f74bcbf..482ee3e4e04a 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -345,11 +345,35 @@ static ssize_t btt_seed_show(struct device *dev,
345} 345}
346static DEVICE_ATTR_RO(btt_seed); 346static DEVICE_ATTR_RO(btt_seed);
347 347
348static ssize_t read_only_show(struct device *dev,
349 struct device_attribute *attr, char *buf)
350{
351 struct nd_region *nd_region = to_nd_region(dev);
352
353 return sprintf(buf, "%d\n", nd_region->ro);
354}
355
356static ssize_t read_only_store(struct device *dev,
357 struct device_attribute *attr, const char *buf, size_t len)
358{
359 bool ro;
360 int rc = strtobool(buf, &ro);
361 struct nd_region *nd_region = to_nd_region(dev);
362
363 if (rc)
364 return rc;
365
366 nd_region->ro = ro;
367 return len;
368}
369static DEVICE_ATTR_RW(read_only);
370
348static struct attribute *nd_region_attributes[] = { 371static struct attribute *nd_region_attributes[] = {
349 &dev_attr_size.attr, 372 &dev_attr_size.attr,
350 &dev_attr_nstype.attr, 373 &dev_attr_nstype.attr,
351 &dev_attr_mappings.attr, 374 &dev_attr_mappings.attr,
352 &dev_attr_btt_seed.attr, 375 &dev_attr_btt_seed.attr,
376 &dev_attr_read_only.attr,
353 &dev_attr_set_cookie.attr, 377 &dev_attr_set_cookie.attr,
354 &dev_attr_available_size.attr, 378 &dev_attr_available_size.attr,
355 &dev_attr_namespace_seed.attr, 379 &dev_attr_namespace_seed.attr,
@@ -641,6 +665,7 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus,
641 struct device *dev; 665 struct device *dev;
642 void *region_buf; 666 void *region_buf;
643 unsigned int i; 667 unsigned int i;
668 int ro = 0;
644 669
645 for (i = 0; i < ndr_desc->num_mappings; i++) { 670 for (i = 0; i < ndr_desc->num_mappings; i++) {
646 struct nd_mapping *nd_mapping = &ndr_desc->nd_mapping[i]; 671 struct nd_mapping *nd_mapping = &ndr_desc->nd_mapping[i];
@@ -652,6 +677,9 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus,
652 677
653 return NULL; 678 return NULL;
654 } 679 }
680
681 if (nvdimm->flags & NDD_UNARMED)
682 ro = 1;
655 } 683 }
656 684
657 if (dev_type == &nd_blk_device_type) { 685 if (dev_type == &nd_blk_device_type) {
@@ -707,6 +735,7 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus,
707 nd_region->provider_data = ndr_desc->provider_data; 735 nd_region->provider_data = ndr_desc->provider_data;
708 nd_region->nd_set = ndr_desc->nd_set; 736 nd_region->nd_set = ndr_desc->nd_set;
709 nd_region->num_lanes = ndr_desc->num_lanes; 737 nd_region->num_lanes = ndr_desc->num_lanes;
738 nd_region->ro = ro;
710 ida_init(&nd_region->ns_ida); 739 ida_init(&nd_region->ns_ida);
711 ida_init(&nd_region->btt_ida); 740 ida_init(&nd_region->btt_ida);
712 dev = &nd_region->dev; 741 dev = &nd_region->dev;
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index 7fc1b25bdb5d..dc799a29ed1a 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -21,6 +21,8 @@
21enum { 21enum {
22 /* when a dimm supports both PMEM and BLK access a label is required */ 22 /* when a dimm supports both PMEM and BLK access a label is required */
23 NDD_ALIASING = 1 << 0, 23 NDD_ALIASING = 1 << 0,
24 /* unarmed memory devices may not persist writes */
25 NDD_UNARMED = 1 << 1,
24 26
25 /* need to set a limit somewhere, but yes, this is likely overkill */ 27 /* need to set a limit somewhere, but yes, this is likely overkill */
26 ND_IOCTL_MAX_BUFLEN = SZ_4M, 28 ND_IOCTL_MAX_BUFLEN = SZ_4M,
diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c
index 7a4a5a5edbe4..4b69b8368de0 100644
--- a/tools/testing/nvdimm/test/nfit.c
+++ b/tools/testing/nvdimm/test/nfit.c
@@ -874,6 +874,9 @@ static void nfit_test1_setup(struct nfit_test *t)
874 memdev->address = 0; 874 memdev->address = 0;
875 memdev->interleave_index = 0; 875 memdev->interleave_index = 0;
876 memdev->interleave_ways = 1; 876 memdev->interleave_ways = 1;
877 memdev->flags = ACPI_NFIT_MEM_SAVE_FAILED | ACPI_NFIT_MEM_RESTORE_FAILED
878 | ACPI_NFIT_MEM_FLUSH_FAILED | ACPI_NFIT_MEM_HEALTH_OBSERVED
879 | ACPI_NFIT_MEM_ARMED;
877 880
878 offset += sizeof(*memdev); 881 offset += sizeof(*memdev);
879 /* dcr-descriptor0 */ 882 /* dcr-descriptor0 */