Commit f524bf27 authored by Dan Williams's avatar Dan Williams

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: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent 1b40e09a
...@@ -132,6 +132,55 @@ int nvdimm_init_config_data(struct nvdimm_drvdata *ndd) ...@@ -132,6 +132,55 @@ int nvdimm_init_config_data(struct nvdimm_drvdata *ndd)
return rc; return rc;
} }
int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset,
void *buf, size_t len)
{
int rc = validate_dimm(ndd);
size_t max_cmd_size, buf_offset;
struct nd_cmd_set_config_hdr *cmd;
struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
if (rc)
return rc;
if (!ndd->data)
return -ENXIO;
if (offset + len > ndd->nsarea.config_size)
return -ENXIO;
max_cmd_size = min_t(u32, PAGE_SIZE, len);
max_cmd_size = min_t(u32, max_cmd_size, ndd->nsarea.max_xfer);
cmd = kzalloc(max_cmd_size + sizeof(*cmd) + sizeof(u32), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
for (buf_offset = 0; len; len -= cmd->in_length,
buf_offset += cmd->in_length) {
size_t cmd_size;
u32 *status;
cmd->in_offset = offset + buf_offset;
cmd->in_length = min(max_cmd_size, len);
memcpy(cmd->in_buf, buf + buf_offset, cmd->in_length);
/* status is output in the last 4-bytes of the command buffer */
cmd_size = sizeof(*cmd) + cmd->in_length + sizeof(u32);
status = ((void *) cmd) + cmd_size - sizeof(u32);
rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
ND_CMD_SET_CONFIG_DATA, cmd, cmd_size);
if (rc || *status) {
rc = rc ? rc : -ENXIO;
break;
}
}
kfree(cmd);
return rc;
}
static void nvdimm_release(struct device *dev) static void nvdimm_release(struct device *dev)
{ {
struct nvdimm *nvdimm = to_nvdimm(dev); struct nvdimm *nvdimm = to_nvdimm(dev);
......
This diff is collapsed.
...@@ -34,6 +34,7 @@ enum { ...@@ -34,6 +34,7 @@ enum {
BTTINFO_MAJOR_VERSION = 1, BTTINFO_MAJOR_VERSION = 1,
ND_LABEL_MIN_SIZE = 512 * 129, /* see sizeof_namespace_index() */ ND_LABEL_MIN_SIZE = 512 * 129, /* see sizeof_namespace_index() */
ND_LABEL_ID_SIZE = 50, ND_LABEL_ID_SIZE = 50,
ND_NSINDEX_INIT = 0x1,
}; };
static const char NSINDEX_SIGNATURE[] = "NAMESPACE_INDEX\0"; static const char NSINDEX_SIGNATURE[] = "NAMESPACE_INDEX\0";
...@@ -127,4 +128,9 @@ void nd_label_copy(struct nvdimm_drvdata *ndd, struct nd_namespace_index *dst, ...@@ -127,4 +128,9 @@ void nd_label_copy(struct nvdimm_drvdata *ndd, struct nd_namespace_index *dst,
size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd); size_t sizeof_namespace_index(struct nvdimm_drvdata *ndd);
int nd_label_active_count(struct nvdimm_drvdata *ndd); int nd_label_active_count(struct nvdimm_drvdata *ndd);
struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n); struct nd_namespace_label *nd_label_active(struct nvdimm_drvdata *ndd, int n);
u32 nd_label_nfree(struct nvdimm_drvdata *ndd);
struct nd_region;
struct nd_namespace_pmem;
int nd_pmem_namespace_label_update(struct nd_region *nd_region,
struct nd_namespace_pmem *nspm, resource_size_t size);
#endif /* __LABEL_H__ */ #endif /* __LABEL_H__ */
...@@ -149,20 +149,53 @@ static resource_size_t nd_namespace_blk_size(struct nd_namespace_blk *nsblk) ...@@ -149,20 +149,53 @@ static resource_size_t nd_namespace_blk_size(struct nd_namespace_blk *nsblk)
return size; return size;
} }
static int nd_namespace_label_update(struct nd_region *nd_region,
struct device *dev)
{
dev_WARN_ONCE(dev, dev->driver,
"namespace must be idle during label update\n");
if (dev->driver)
return 0;
/*
* Only allow label writes that will result in a valid namespace
* or deletion of an existing namespace.
*/
if (is_namespace_pmem(dev)) {
struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
struct resource *res = &nspm->nsio.res;
resource_size_t size = resource_size(res);
if (size == 0 && nspm->uuid)
/* delete allocation */;
else if (!nspm->uuid)
return 0;
return nd_pmem_namespace_label_update(nd_region, nspm, size);
} else if (is_namespace_blk(dev)) {
/* TODO: implement blk labels */
return 0;
} else
return -ENXIO;
}
static ssize_t alt_name_store(struct device *dev, static ssize_t alt_name_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len) struct device_attribute *attr, const char *buf, size_t len)
{ {
struct nd_region *nd_region = to_nd_region(dev->parent);
ssize_t rc; ssize_t rc;
device_lock(dev); device_lock(dev);
nvdimm_bus_lock(dev); nvdimm_bus_lock(dev);
wait_nvdimm_bus_probe_idle(dev); wait_nvdimm_bus_probe_idle(dev);
rc = __alt_name_store(dev, buf, len); rc = __alt_name_store(dev, buf, len);
if (rc >= 0)
rc = nd_namespace_label_update(nd_region, dev);
dev_dbg(dev, "%s: %s(%zd)\n", __func__, rc < 0 ? "fail " : "", rc); dev_dbg(dev, "%s: %s(%zd)\n", __func__, rc < 0 ? "fail " : "", rc);
nvdimm_bus_unlock(dev); nvdimm_bus_unlock(dev);
device_unlock(dev); device_unlock(dev);
return rc; return rc < 0 ? rc : len;
} }
static ssize_t alt_name_show(struct device *dev, 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,6 +742,7 @@ static ssize_t __size_store(struct device *dev, unsigned long long val)
static ssize_t size_store(struct device *dev, static ssize_t size_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len) struct device_attribute *attr, const char *buf, size_t len)
{ {
struct nd_region *nd_region = to_nd_region(dev->parent);
unsigned long long val; unsigned long long val;
u8 **uuid = NULL; u8 **uuid = NULL;
int rc; int rc;
...@@ -721,6 +755,8 @@ static ssize_t size_store(struct device *dev, ...@@ -721,6 +755,8 @@ static ssize_t size_store(struct device *dev,
nvdimm_bus_lock(dev); nvdimm_bus_lock(dev);
wait_nvdimm_bus_probe_idle(dev); wait_nvdimm_bus_probe_idle(dev);
rc = __size_store(dev, val); rc = __size_store(dev, val);
if (rc >= 0)
rc = nd_namespace_label_update(nd_region, dev);
if (is_namespace_pmem(dev)) { if (is_namespace_pmem(dev)) {
struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
...@@ -744,7 +780,7 @@ static ssize_t size_store(struct device *dev, ...@@ -744,7 +780,7 @@ static ssize_t size_store(struct device *dev,
nvdimm_bus_unlock(dev); nvdimm_bus_unlock(dev);
device_unlock(dev); device_unlock(dev);
return rc ? rc : len; return rc < 0 ? rc : len;
} }
static ssize_t size_show(struct device *dev, static ssize_t size_show(struct device *dev,
...@@ -804,17 +840,34 @@ static int namespace_update_uuid(struct nd_region *nd_region, ...@@ -804,17 +840,34 @@ static int namespace_update_uuid(struct nd_region *nd_region,
u32 flags = is_namespace_blk(dev) ? NSLABEL_FLAG_LOCAL : 0; u32 flags = is_namespace_blk(dev) ? NSLABEL_FLAG_LOCAL : 0;
struct nd_label_id old_label_id; struct nd_label_id old_label_id;
struct nd_label_id new_label_id; struct nd_label_id new_label_id;
int i, rc; int i;
rc = nd_is_uuid_unique(dev, new_uuid) ? 0 : -EINVAL; if (!nd_is_uuid_unique(dev, new_uuid))
if (rc) { return -EINVAL;
kfree(new_uuid);
return rc;
}
if (*old_uuid == NULL) if (*old_uuid == NULL)
goto out; goto out;
/*
* If we've already written a label with this uuid, then it's
* too late to rename because we can't reliably update the uuid
* without losing the old namespace. Userspace must delete this
* namespace to abandon the old uuid.
*/
for (i = 0; i < nd_region->ndr_mappings; i++) {
struct nd_mapping *nd_mapping = &nd_region->mapping[i];
/*
* This check by itself is sufficient because old_uuid
* would be NULL above if this uuid did not exist in the
* currently written set.
*
* FIXME: can we delete uuid with zero dpa allocated?
*/
if (nd_mapping->labels)
return -EBUSY;
}
nd_label_gen_id(&old_label_id, *old_uuid, flags); nd_label_gen_id(&old_label_id, *old_uuid, flags);
nd_label_gen_id(&new_label_id, new_uuid, flags); nd_label_gen_id(&new_label_id, new_uuid, flags);
for (i = 0; i < nd_region->ndr_mappings; i++) { for (i = 0; i < nd_region->ndr_mappings; i++) {
...@@ -858,12 +911,16 @@ static ssize_t uuid_store(struct device *dev, ...@@ -858,12 +911,16 @@ static ssize_t uuid_store(struct device *dev,
rc = nd_uuid_store(dev, &uuid, buf, len); rc = nd_uuid_store(dev, &uuid, buf, len);
if (rc >= 0) if (rc >= 0)
rc = namespace_update_uuid(nd_region, dev, uuid, ns_uuid); rc = namespace_update_uuid(nd_region, dev, uuid, ns_uuid);
if (rc >= 0)
rc = nd_namespace_label_update(nd_region, dev);
else
kfree(uuid);
dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__, dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
rc, buf, buf[len - 1] == '\n' ? "" : "\n"); rc, buf, buf[len - 1] == '\n' ? "" : "\n");
nvdimm_bus_unlock(dev); nvdimm_bus_unlock(dev);
device_unlock(dev); device_unlock(dev);
return rc ? rc : len; return rc < 0 ? rc : len;
} }
static DEVICE_ATTR_RW(uuid); static DEVICE_ATTR_RW(uuid);
...@@ -907,6 +964,7 @@ static ssize_t sector_size_store(struct device *dev, ...@@ -907,6 +964,7 @@ static ssize_t sector_size_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len) struct device_attribute *attr, const char *buf, size_t len)
{ {
struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev); struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
struct nd_region *nd_region = to_nd_region(dev->parent);
ssize_t rc; ssize_t rc;
if (!is_namespace_blk(dev)) if (!is_namespace_blk(dev))
...@@ -916,8 +974,11 @@ static ssize_t sector_size_store(struct device *dev, ...@@ -916,8 +974,11 @@ static ssize_t sector_size_store(struct device *dev,
nvdimm_bus_lock(dev); nvdimm_bus_lock(dev);
rc = nd_sector_size_store(dev, buf, &nsblk->lbasize, rc = nd_sector_size_store(dev, buf, &nsblk->lbasize,
ns_lbasize_supported); ns_lbasize_supported);
dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__, if (rc >= 0)
rc, buf, buf[len - 1] == '\n' ? "" : "\n"); rc = nd_namespace_label_update(nd_region, dev);
dev_dbg(dev, "%s: result: %zd %s: %s%s", __func__,
rc, rc < 0 ? "tried" : "wrote", buf,
buf[len - 1] == '\n' ? "" : "\n");
nvdimm_bus_unlock(dev); nvdimm_bus_unlock(dev);
device_unlock(dev); device_unlock(dev);
......
...@@ -93,6 +93,7 @@ static inline unsigned nd_inc_seq(unsigned seq) ...@@ -93,6 +93,7 @@ static inline unsigned nd_inc_seq(unsigned seq)
return next[seq & 3]; return next[seq & 3];
} }
enum nd_async_mode { enum nd_async_mode {
ND_SYNC, ND_SYNC,
ND_ASYNC, ND_ASYNC,
...@@ -115,6 +116,8 @@ struct nvdimm; ...@@ -115,6 +116,8 @@ struct nvdimm;
struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping); struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping);
int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd); int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd);
int nvdimm_init_config_data(struct nvdimm_drvdata *ndd); int nvdimm_init_config_data(struct nvdimm_drvdata *ndd);
int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset,
void *buf, size_t len);
struct nd_region *to_nd_region(struct device *dev); struct nd_region *to_nd_region(struct device *dev);
int nd_region_to_nstype(struct nd_region *nd_region); int nd_region_to_nstype(struct nd_region *nd_region);
int nd_region_register_namespaces(struct nd_region *nd_region, int *err); int nd_region_register_namespaces(struct nd_region *nd_region, int *err);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment