Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

libnvdimm: Notify disk drivers to revalidate region read-only

Previous kernels allowed the BLKROSET to override the disk's read-only
status. With that situation fixed the pmem driver needs to rely on
notification events to reevaluate the disk read-only status after the
host region has been marked read-write.

Recall that when libnvdimm determines that the persistent memory has
lost persistence (for example lack of energy to flush from DRAM to FLASH
on an NVDIMM-N device) it marks the region read-only, but that state can
be overridden by the user via:

echo 0 > /sys/bus/nd/devices/regionX/read_only

...to date there is no notification that the region has restored
persistence, so the user override is the only recovery.

Fixes: 52f019d43c22 ("block: add a hard-readonly flag to struct gendisk")
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Vishal Verma <vishal.l.verma@intel.com>
Tested-by: Vishal Verma <vishal.l.verma@intel.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Ming Lei <ming.lei@redhat.com>
Cc: Martin K. Petersen <martin.petersen@oracle.com>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Jens Axboe <axboe@kernel.dk>
Link: https://lore.kernel.org/r/161534060720.528671.2341213328968989192.stgit@dwillia2-desk3.amr.corp.intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>

+47 -12
+6 -8
drivers/nvdimm/bus.c
··· 631 631 struct nd_region *nd_region = to_nd_region(dev->parent); 632 632 int disk_ro = get_disk_ro(disk); 633 633 634 - /* 635 - * Upgrade to read-only if the region is read-only preserve as 636 - * read-only if the disk is already read-only. 637 - */ 638 - if (disk_ro || nd_region->ro == disk_ro) 634 + /* catch the disk up with the region ro state */ 635 + if (disk_ro == nd_region->ro) 639 636 return; 640 637 641 - dev_info(dev, "%s read-only, marking %s read-only\n", 642 - dev_name(&nd_region->dev), disk->disk_name); 643 - set_disk_ro(disk, 1); 638 + dev_info(dev, "%s read-%s, marking %s read-%s\n", 639 + dev_name(&nd_region->dev), nd_region->ro ? "only" : "write", 640 + disk->disk_name, nd_region->ro ? "only" : "write"); 641 + set_disk_ro(disk, nd_region->ro); 644 642 } 645 643 EXPORT_SYMBOL(nvdimm_check_and_set_ro); 646 644
+33 -4
drivers/nvdimm/pmem.c
··· 26 26 #include <linux/mm.h> 27 27 #include <asm/cacheflush.h> 28 28 #include "pmem.h" 29 + #include "btt.h" 29 30 #include "pfn.h" 30 31 #include "nd.h" 31 32 ··· 586 585 nvdimm_flush(to_nd_region(dev->parent), NULL); 587 586 } 588 587 589 - static void nd_pmem_notify(struct device *dev, enum nvdimm_event event) 588 + static void pmem_revalidate_poison(struct device *dev) 590 589 { 591 590 struct nd_region *nd_region; 592 591 resource_size_t offset = 0, end_trunc = 0; ··· 595 594 struct badblocks *bb; 596 595 struct range range; 597 596 struct kernfs_node *bb_state; 598 - 599 - if (event != NVDIMM_REVALIDATE_POISON) 600 - return; 601 597 602 598 if (is_nd_btt(dev)) { 603 599 struct nd_btt *nd_btt = to_nd_btt(dev); ··· 631 633 nvdimm_badblocks_populate(nd_region, bb, &range); 632 634 if (bb_state) 633 635 sysfs_notify_dirent(bb_state); 636 + } 637 + 638 + static void pmem_revalidate_region(struct device *dev) 639 + { 640 + struct pmem_device *pmem; 641 + 642 + if (is_nd_btt(dev)) { 643 + struct nd_btt *nd_btt = to_nd_btt(dev); 644 + struct btt *btt = nd_btt->btt; 645 + 646 + nvdimm_check_and_set_ro(btt->btt_disk); 647 + return; 648 + } 649 + 650 + pmem = dev_get_drvdata(dev); 651 + nvdimm_check_and_set_ro(pmem->disk); 652 + } 653 + 654 + static void nd_pmem_notify(struct device *dev, enum nvdimm_event event) 655 + { 656 + switch (event) { 657 + case NVDIMM_REVALIDATE_POISON: 658 + pmem_revalidate_poison(dev); 659 + break; 660 + case NVDIMM_REVALIDATE_REGION: 661 + pmem_revalidate_region(dev); 662 + break; 663 + default: 664 + dev_WARN_ONCE(dev, 1, "notify: unknown event: %d\n", event); 665 + break; 666 + } 634 667 } 635 668 636 669 MODULE_ALIAS("pmem");
+7
drivers/nvdimm/region_devs.c
··· 518 518 return sprintf(buf, "%d\n", nd_region->ro); 519 519 } 520 520 521 + static int revalidate_read_only(struct device *dev, void *data) 522 + { 523 + nd_device_notify(dev, NVDIMM_REVALIDATE_REGION); 524 + return 0; 525 + } 526 + 521 527 static ssize_t read_only_store(struct device *dev, 522 528 struct device_attribute *attr, const char *buf, size_t len) 523 529 { ··· 535 529 return rc; 536 530 537 531 nd_region->ro = ro; 532 + device_for_each_child(dev, NULL, revalidate_read_only); 538 533 return len; 539 534 } 540 535 static DEVICE_ATTR_RW(read_only);
+1
include/linux/nd.h
··· 11 11 12 12 enum nvdimm_event { 13 13 NVDIMM_REVALIDATE_POISON, 14 + NVDIMM_REVALIDATE_REGION, 14 15 }; 15 16 16 17 enum nvdimm_claim_class {