diff options
author | Dan Williams <dan.j.williams@intel.com> | 2015-05-30 12:36:02 -0400 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2015-06-24 21:24:10 -0400 |
commit | f524bf271a5cf12a44253194abcf8b6688ff5b9d (patch) | |
tree | e477ae652183e73a6a3c6fc592046e255d2bfd15 /drivers/nvdimm/namespace_devs.c | |
parent | 1b40e09a1232de537b193fa1b6b3ef16d3a1e397 (diff) |
libnvdimm: write pmem label set
After 'uuid', 'size', and optionally 'alt_name' have been set to valid
values the labels on the dimms can be updated.
Write procedure is:
1/ Allocate and write new labels in the "next" index
2/ Free the old labels in the working copy
3/ Write the bitmap and the label space on the dimm
4/ Write the index to make the update valid
Label ranges directly mirror the dpa resource values for the given
label_id of the namespace.
Cc: Greg KH <gregkh@linuxfoundation.org>
Cc: Neil Brown <neilb@suse.de>
Acked-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/nvdimm/namespace_devs.c')
-rw-r--r-- | drivers/nvdimm/namespace_devs.c | 83 |
1 files changed, 72 insertions, 11 deletions
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index ad0ec09ca40f..546e77e122fd 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c | |||
@@ -149,20 +149,53 @@ static resource_size_t nd_namespace_blk_size(struct nd_namespace_blk *nsblk) | |||
149 | return size; | 149 | return size; |
150 | } | 150 | } |
151 | 151 | ||
152 | static int nd_namespace_label_update(struct nd_region *nd_region, | ||
153 | struct device *dev) | ||
154 | { | ||
155 | dev_WARN_ONCE(dev, dev->driver, | ||
156 | "namespace must be idle during label update\n"); | ||
157 | if (dev->driver) | ||
158 | return 0; | ||
159 | |||
160 | /* | ||
161 | * Only allow label writes that will result in a valid namespace | ||
162 | * or deletion of an existing namespace. | ||
163 | */ | ||
164 | if (is_namespace_pmem(dev)) { | ||
165 | struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); | ||
166 | struct resource *res = &nspm->nsio.res; | ||
167 | resource_size_t size = resource_size(res); | ||
168 | |||
169 | if (size == 0 && nspm->uuid) | ||
170 | /* delete allocation */; | ||
171 | else if (!nspm->uuid) | ||
172 | return 0; | ||
173 | |||
174 | return nd_pmem_namespace_label_update(nd_region, nspm, size); | ||
175 | } else if (is_namespace_blk(dev)) { | ||
176 | /* TODO: implement blk labels */ | ||
177 | return 0; | ||
178 | } else | ||
179 | return -ENXIO; | ||
180 | } | ||
181 | |||
152 | static ssize_t alt_name_store(struct device *dev, | 182 | static ssize_t alt_name_store(struct device *dev, |
153 | struct device_attribute *attr, const char *buf, size_t len) | 183 | struct device_attribute *attr, const char *buf, size_t len) |
154 | { | 184 | { |
185 | struct nd_region *nd_region = to_nd_region(dev->parent); | ||
155 | ssize_t rc; | 186 | ssize_t rc; |
156 | 187 | ||
157 | device_lock(dev); | 188 | device_lock(dev); |
158 | nvdimm_bus_lock(dev); | 189 | nvdimm_bus_lock(dev); |
159 | wait_nvdimm_bus_probe_idle(dev); | 190 | wait_nvdimm_bus_probe_idle(dev); |
160 | rc = __alt_name_store(dev, buf, len); | 191 | rc = __alt_name_store(dev, buf, len); |
192 | if (rc >= 0) | ||
193 | rc = nd_namespace_label_update(nd_region, dev); | ||
161 | dev_dbg(dev, "%s: %s(%zd)\n", __func__, rc < 0 ? "fail " : "", rc); | 194 | dev_dbg(dev, "%s: %s(%zd)\n", __func__, rc < 0 ? "fail " : "", rc); |
162 | nvdimm_bus_unlock(dev); | 195 | nvdimm_bus_unlock(dev); |
163 | device_unlock(dev); | 196 | device_unlock(dev); |
164 | 197 | ||
165 | return rc; | 198 | return rc < 0 ? rc : len; |
166 | } | 199 | } |
167 | 200 | ||
168 | static ssize_t alt_name_show(struct device *dev, | 201 | static ssize_t alt_name_show(struct device *dev, |
@@ -709,6 +742,7 @@ static ssize_t __size_store(struct device *dev, unsigned long long val) | |||
709 | static ssize_t size_store(struct device *dev, | 742 | static ssize_t size_store(struct device *dev, |
710 | struct device_attribute *attr, const char *buf, size_t len) | 743 | struct device_attribute *attr, const char *buf, size_t len) |
711 | { | 744 | { |
745 | struct nd_region *nd_region = to_nd_region(dev->parent); | ||
712 | unsigned long long val; | 746 | unsigned long long val; |
713 | u8 **uuid = NULL; | 747 | u8 **uuid = NULL; |
714 | int rc; | 748 | int rc; |
@@ -721,6 +755,8 @@ static ssize_t size_store(struct device *dev, | |||
721 | nvdimm_bus_lock(dev); | 755 | nvdimm_bus_lock(dev); |
722 | wait_nvdimm_bus_probe_idle(dev); | 756 | wait_nvdimm_bus_probe_idle(dev); |
723 | rc = __size_store(dev, val); | 757 | rc = __size_store(dev, val); |
758 | if (rc >= 0) | ||
759 | rc = nd_namespace_label_update(nd_region, dev); | ||
724 | 760 | ||
725 | if (is_namespace_pmem(dev)) { | 761 | if (is_namespace_pmem(dev)) { |
726 | struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); | 762 | struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); |
@@ -744,7 +780,7 @@ static ssize_t size_store(struct device *dev, | |||
744 | nvdimm_bus_unlock(dev); | 780 | nvdimm_bus_unlock(dev); |
745 | device_unlock(dev); | 781 | device_unlock(dev); |
746 | 782 | ||
747 | return rc ? rc : len; | 783 | return rc < 0 ? rc : len; |
748 | } | 784 | } |
749 | 785 | ||
750 | static ssize_t size_show(struct device *dev, | 786 | static ssize_t size_show(struct device *dev, |
@@ -804,17 +840,34 @@ static int namespace_update_uuid(struct nd_region *nd_region, | |||
804 | u32 flags = is_namespace_blk(dev) ? NSLABEL_FLAG_LOCAL : 0; | 840 | u32 flags = is_namespace_blk(dev) ? NSLABEL_FLAG_LOCAL : 0; |
805 | struct nd_label_id old_label_id; | 841 | struct nd_label_id old_label_id; |
806 | struct nd_label_id new_label_id; | 842 | struct nd_label_id new_label_id; |
807 | int i, rc; | 843 | int i; |
808 | 844 | ||
809 | rc = nd_is_uuid_unique(dev, new_uuid) ? 0 : -EINVAL; | 845 | if (!nd_is_uuid_unique(dev, new_uuid)) |
810 | if (rc) { | 846 | return -EINVAL; |
811 | kfree(new_uuid); | ||
812 | return rc; | ||
813 | } | ||
814 | 847 | ||
815 | if (*old_uuid == NULL) | 848 | if (*old_uuid == NULL) |
816 | goto out; | 849 | goto out; |
817 | 850 | ||
851 | /* | ||
852 | * If we've already written a label with this uuid, then it's | ||
853 | * too late to rename because we can't reliably update the uuid | ||
854 | * without losing the old namespace. Userspace must delete this | ||
855 | * namespace to abandon the old uuid. | ||
856 | */ | ||
857 | for (i = 0; i < nd_region->ndr_mappings; i++) { | ||
858 | struct nd_mapping *nd_mapping = &nd_region->mapping[i]; | ||
859 | |||
860 | /* | ||
861 | * This check by itself is sufficient because old_uuid | ||
862 | * would be NULL above if this uuid did not exist in the | ||
863 | * currently written set. | ||
864 | * | ||
865 | * FIXME: can we delete uuid with zero dpa allocated? | ||
866 | */ | ||
867 | if (nd_mapping->labels) | ||
868 | return -EBUSY; | ||
869 | } | ||
870 | |||
818 | nd_label_gen_id(&old_label_id, *old_uuid, flags); | 871 | nd_label_gen_id(&old_label_id, *old_uuid, flags); |
819 | nd_label_gen_id(&new_label_id, new_uuid, flags); | 872 | nd_label_gen_id(&new_label_id, new_uuid, flags); |
820 | for (i = 0; i < nd_region->ndr_mappings; i++) { | 873 | for (i = 0; i < nd_region->ndr_mappings; i++) { |
@@ -858,12 +911,16 @@ static ssize_t uuid_store(struct device *dev, | |||
858 | rc = nd_uuid_store(dev, &uuid, buf, len); | 911 | rc = nd_uuid_store(dev, &uuid, buf, len); |
859 | if (rc >= 0) | 912 | if (rc >= 0) |
860 | rc = namespace_update_uuid(nd_region, dev, uuid, ns_uuid); | 913 | rc = namespace_update_uuid(nd_region, dev, uuid, ns_uuid); |
914 | if (rc >= 0) | ||
915 | rc = nd_namespace_label_update(nd_region, dev); | ||
916 | else | ||
917 | kfree(uuid); | ||
861 | dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__, | 918 | dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__, |
862 | rc, buf, buf[len - 1] == '\n' ? "" : "\n"); | 919 | rc, buf, buf[len - 1] == '\n' ? "" : "\n"); |
863 | nvdimm_bus_unlock(dev); | 920 | nvdimm_bus_unlock(dev); |
864 | device_unlock(dev); | 921 | device_unlock(dev); |
865 | 922 | ||
866 | return rc ? rc : len; | 923 | return rc < 0 ? rc : len; |
867 | } | 924 | } |
868 | static DEVICE_ATTR_RW(uuid); | 925 | static DEVICE_ATTR_RW(uuid); |
869 | 926 | ||
@@ -907,6 +964,7 @@ static ssize_t sector_size_store(struct device *dev, | |||
907 | struct device_attribute *attr, const char *buf, size_t len) | 964 | struct device_attribute *attr, const char *buf, size_t len) |
908 | { | 965 | { |
909 | struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev); | 966 | struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev); |
967 | struct nd_region *nd_region = to_nd_region(dev->parent); | ||
910 | ssize_t rc; | 968 | ssize_t rc; |
911 | 969 | ||
912 | if (!is_namespace_blk(dev)) | 970 | if (!is_namespace_blk(dev)) |
@@ -916,8 +974,11 @@ static ssize_t sector_size_store(struct device *dev, | |||
916 | nvdimm_bus_lock(dev); | 974 | nvdimm_bus_lock(dev); |
917 | rc = nd_sector_size_store(dev, buf, &nsblk->lbasize, | 975 | rc = nd_sector_size_store(dev, buf, &nsblk->lbasize, |
918 | ns_lbasize_supported); | 976 | ns_lbasize_supported); |
919 | dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__, | 977 | if (rc >= 0) |
920 | rc, buf, buf[len - 1] == '\n' ? "" : "\n"); | 978 | rc = nd_namespace_label_update(nd_region, dev); |
979 | dev_dbg(dev, "%s: result: %zd %s: %s%s", __func__, | ||
980 | rc, rc < 0 ? "tried" : "wrote", buf, | ||
981 | buf[len - 1] == '\n' ? "" : "\n"); | ||
921 | nvdimm_bus_unlock(dev); | 982 | nvdimm_bus_unlock(dev); |
922 | device_unlock(dev); | 983 | device_unlock(dev); |
923 | 984 | ||