From 8c2f7e8658df1d3b7cbfa62706941d14c715823a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 25 Jun 2015 04:20:04 -0400 Subject: libnvdimm: infrastructure for btt devices NVDIMM namespaces, in addition to accepting "struct bio" based requests, also have the capability to perform byte-aligned accesses. By default only the bio/block interface is used. However, if another driver can make effective use of the byte-aligned capability it can claim namespace interface and use the byte-aligned ->rw_bytes() interface. The BTT driver is the initial first consumer of this mechanism to allow adding atomic sector update semantics to a pmem or blk namespace. This patch is the sysfs infrastructure to allow configuring a BTT instance for a namespace. Enabling that BTT and performing i/o is in a subsequent patch. Cc: Greg KH Cc: Neil Brown Signed-off-by: Dan Williams --- drivers/nvdimm/namespace_devs.c | 204 +++++++++++++++++++++++++++++++++------- 1 file changed, 170 insertions(+), 34 deletions(-) (limited to 'drivers/nvdimm/namespace_devs.c') diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index 50b502b1908e..2c50a0719f8d 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -102,7 +102,7 @@ static ssize_t __alt_name_store(struct device *dev, const char *buf, } else return -ENXIO; - if (dev->driver) + if (dev->driver || to_ndns(dev)->claim) return -EBUSY; input = kmemdup(buf, len + 1, GFP_KERNEL); @@ -133,7 +133,7 @@ out: static resource_size_t nd_namespace_blk_size(struct nd_namespace_blk *nsblk) { - struct nd_region *nd_region = to_nd_region(nsblk->dev.parent); + struct nd_region *nd_region = to_nd_region(nsblk->common.dev.parent); struct nd_mapping *nd_mapping = &nd_region->mapping[0]; struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); struct nd_label_id label_id; @@ -152,9 +152,9 @@ static resource_size_t nd_namespace_blk_size(struct nd_namespace_blk *nsblk) static int nd_namespace_label_update(struct nd_region *nd_region, struct device *dev) { - dev_WARN_ONCE(dev, dev->driver, + dev_WARN_ONCE(dev, dev->driver || to_ndns(dev)->claim, "namespace must be idle during label update\n"); - if (dev->driver) + if (dev->driver || to_ndns(dev)->claim) return 0; /* @@ -666,7 +666,7 @@ static ssize_t __size_store(struct device *dev, unsigned long long val) u8 *uuid = NULL; int rc, i; - if (dev->driver) + if (dev->driver || to_ndns(dev)->claim) return -EBUSY; if (is_namespace_pmem(dev)) { @@ -733,12 +733,16 @@ static ssize_t __size_store(struct device *dev, unsigned long long val) nd_namespace_pmem_set_size(nd_region, nspm, val * nd_region->ndr_mappings); } else if (is_namespace_blk(dev)) { + struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev); + /* * Try to delete the namespace if we deleted all of its - * allocation and this is not the seed device for the - * region. + * allocation, this is not the seed device for the + * region, and it is not actively claimed by a btt + * instance. */ - if (val == 0 && nd_region->ns_seed != dev) + if (val == 0 && nd_region->ns_seed != dev + && !nsblk->common.claim) nd_device_unregister(dev, ND_ASYNC); } @@ -789,26 +793,42 @@ static ssize_t size_store(struct device *dev, return rc < 0 ? rc : len; } -static ssize_t size_show(struct device *dev, - struct device_attribute *attr, char *buf) +resource_size_t __nvdimm_namespace_capacity(struct nd_namespace_common *ndns) { - unsigned long long size = 0; + struct device *dev = &ndns->dev; - nvdimm_bus_lock(dev); if (is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); - size = resource_size(&nspm->nsio.res); + return resource_size(&nspm->nsio.res); } else if (is_namespace_blk(dev)) { - size = nd_namespace_blk_size(to_nd_namespace_blk(dev)); + return nd_namespace_blk_size(to_nd_namespace_blk(dev)); } else if (is_namespace_io(dev)) { struct nd_namespace_io *nsio = to_nd_namespace_io(dev); - size = resource_size(&nsio->res); - } - nvdimm_bus_unlock(dev); + return resource_size(&nsio->res); + } else + WARN_ONCE(1, "unknown namespace type\n"); + return 0; +} + +resource_size_t nvdimm_namespace_capacity(struct nd_namespace_common *ndns) +{ + resource_size_t size; - return sprintf(buf, "%llu\n", size); + nvdimm_bus_lock(&ndns->dev); + size = __nvdimm_namespace_capacity(ndns); + nvdimm_bus_unlock(&ndns->dev); + + return size; +} +EXPORT_SYMBOL(nvdimm_namespace_capacity); + +static ssize_t size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%llu\n", (unsigned long long) + nvdimm_namespace_capacity(to_ndns(dev))); } static DEVICE_ATTR(size, S_IRUGO, size_show, size_store); @@ -897,8 +917,8 @@ static ssize_t uuid_store(struct device *dev, { struct nd_region *nd_region = to_nd_region(dev->parent); u8 *uuid = NULL; + ssize_t rc = 0; u8 **ns_uuid; - ssize_t rc; if (is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); @@ -914,7 +934,10 @@ static ssize_t uuid_store(struct device *dev, device_lock(dev); nvdimm_bus_lock(dev); wait_nvdimm_bus_probe_idle(dev); - rc = nd_uuid_store(dev, &uuid, buf, len); + if (to_ndns(dev)->claim) + rc = -EBUSY; + if (rc >= 0) + rc = nd_uuid_store(dev, &uuid, buf, len); if (rc >= 0) rc = namespace_update_uuid(nd_region, dev, uuid, ns_uuid); if (rc >= 0) @@ -971,15 +994,18 @@ static ssize_t sector_size_store(struct device *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 = 0; if (!is_namespace_blk(dev)) return -ENXIO; device_lock(dev); nvdimm_bus_lock(dev); - rc = nd_sector_size_store(dev, buf, &nsblk->lbasize, - ns_lbasize_supported); + if (to_ndns(dev)->claim) + rc = -EBUSY; + if (rc >= 0) + rc = nd_sector_size_store(dev, buf, &nsblk->lbasize, + ns_lbasize_supported); if (rc >= 0) rc = nd_namespace_label_update(nd_region, dev); dev_dbg(dev, "%s: result: %zd %s: %s%s", __func__, @@ -1034,12 +1060,48 @@ static ssize_t dpa_extents_show(struct device *dev, } static DEVICE_ATTR_RO(dpa_extents); +static ssize_t holder_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nd_namespace_common *ndns = to_ndns(dev); + ssize_t rc; + + device_lock(dev); + rc = sprintf(buf, "%s\n", ndns->claim ? dev_name(ndns->claim) : ""); + device_unlock(dev); + + return rc; +} +static DEVICE_ATTR_RO(holder); + +static ssize_t force_raw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + bool force_raw; + int rc = strtobool(buf, &force_raw); + + if (rc) + return rc; + + to_ndns(dev)->force_raw = force_raw; + return len; +} + +static ssize_t force_raw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", to_ndns(dev)->force_raw); +} +static DEVICE_ATTR_RW(force_raw); + static struct attribute *nd_namespace_attributes[] = { &dev_attr_nstype.attr, &dev_attr_size.attr, &dev_attr_uuid.attr, + &dev_attr_holder.attr, &dev_attr_resource.attr, &dev_attr_alt_name.attr, + &dev_attr_force_raw.attr, &dev_attr_sector_size.attr, &dev_attr_dpa_extents.attr, NULL, @@ -1066,7 +1128,9 @@ static umode_t namespace_visible(struct kobject *kobj, return a->mode; } - if (a == &dev_attr_nstype.attr || a == &dev_attr_size.attr) + if (a == &dev_attr_nstype.attr || a == &dev_attr_size.attr + || a == &dev_attr_holder.attr + || a == &dev_attr_force_raw.attr) return a->mode; return 0; @@ -1083,6 +1147,66 @@ static const struct attribute_group *nd_namespace_attribute_groups[] = { NULL, }; +struct nd_namespace_common *nvdimm_namespace_common_probe(struct device *dev) +{ + struct nd_btt *nd_btt = is_nd_btt(dev) ? to_nd_btt(dev) : NULL; + struct nd_namespace_common *ndns; + resource_size_t size; + + if (nd_btt) { + ndns = nd_btt->ndns; + if (!ndns) + return ERR_PTR(-ENODEV); + + /* + * Flush any in-progess probes / removals in the driver + * for the raw personality of this namespace. + */ + device_lock(&ndns->dev); + device_unlock(&ndns->dev); + if (ndns->dev.driver) { + dev_dbg(&ndns->dev, "is active, can't bind %s\n", + dev_name(&nd_btt->dev)); + return ERR_PTR(-EBUSY); + } + if (dev_WARN_ONCE(&ndns->dev, ndns->claim != &nd_btt->dev, + "host (%s) vs claim (%s) mismatch\n", + dev_name(&nd_btt->dev), + dev_name(ndns->claim))) + return ERR_PTR(-ENXIO); + } else { + ndns = to_ndns(dev); + if (ndns->claim) { + dev_dbg(dev, "claimed by %s, failing probe\n", + dev_name(ndns->claim)); + + return ERR_PTR(-ENXIO); + } + } + + size = nvdimm_namespace_capacity(ndns); + if (size < ND_MIN_NAMESPACE_SIZE) { + dev_dbg(&ndns->dev, "%pa, too small must be at least %#x\n", + &size, ND_MIN_NAMESPACE_SIZE); + return ERR_PTR(-ENODEV); + } + + if (is_namespace_pmem(&ndns->dev)) { + struct nd_namespace_pmem *nspm; + + nspm = to_nd_namespace_pmem(&ndns->dev); + if (!nspm->uuid) { + dev_dbg(&ndns->dev, "%s: uuid not set\n", __func__); + return ERR_PTR(-ENODEV); + } + } else if (is_namespace_blk(&ndns->dev)) { + return ERR_PTR(-ENODEV); /* TODO */ + } + + return ndns; +} +EXPORT_SYMBOL(nvdimm_namespace_common_probe); + static struct device **create_namespace_io(struct nd_region *nd_region) { struct nd_namespace_io *nsio; @@ -1099,7 +1223,7 @@ static struct device **create_namespace_io(struct nd_region *nd_region) return NULL; } - dev = &nsio->dev; + dev = &nsio->common.dev; dev->type = &namespace_io_device_type; dev->parent = &nd_region->dev; res = &nsio->res; @@ -1313,7 +1437,7 @@ static struct device **create_namespace_pmem(struct nd_region *nd_region) if (!nspm) return NULL; - dev = &nspm->nsio.dev; + dev = &nspm->nsio.common.dev; dev->type = &namespace_pmem_device_type; dev->parent = &nd_region->dev; res = &nspm->nsio.res; @@ -1346,7 +1470,7 @@ static struct device **create_namespace_pmem(struct nd_region *nd_region) return devs; err: - namespace_pmem_release(&nspm->nsio.dev); + namespace_pmem_release(&nspm->nsio.common.dev); return NULL; } @@ -1385,7 +1509,7 @@ static struct device *nd_namespace_blk_create(struct nd_region *nd_region) if (!nsblk) return NULL; - dev = &nsblk->dev; + dev = &nsblk->common.dev; dev->type = &namespace_blk_device_type; nsblk->id = ida_simple_get(&nd_region->ns_ida, 0, 0, GFP_KERNEL); if (nsblk->id < 0) { @@ -1396,7 +1520,7 @@ static struct device *nd_namespace_blk_create(struct nd_region *nd_region) dev->parent = &nd_region->dev; dev->groups = nd_namespace_attribute_groups; - return &nsblk->dev; + return &nsblk->common.dev; } void nd_region_create_blk_seed(struct nd_region *nd_region) @@ -1413,6 +1537,18 @@ void nd_region_create_blk_seed(struct nd_region *nd_region) nd_device_register(nd_region->ns_seed); } +void nd_region_create_btt_seed(struct nd_region *nd_region) +{ + WARN_ON(!is_nvdimm_bus_locked(&nd_region->dev)); + nd_region->btt_seed = nd_btt_create(nd_region); + /* + * Seed creation failures are not fatal, provisioning is simply + * disabled until memory becomes available + */ + if (!nd_region->btt_seed) + dev_err(&nd_region->dev, "failed to create btt namespace\n"); +} + static struct device **create_namespace_blk(struct nd_region *nd_region) { struct nd_mapping *nd_mapping = &nd_region->mapping[0]; @@ -1446,7 +1582,7 @@ static struct device **create_namespace_blk(struct nd_region *nd_region) if (!res) goto err; nd_dbg_dpa(nd_region, ndd, res, "%s assign\n", - dev_name(&nsblk->dev)); + dev_name(&nsblk->common.dev)); break; } } @@ -1462,7 +1598,7 @@ static struct device **create_namespace_blk(struct nd_region *nd_region) nsblk = kzalloc(sizeof(*nsblk), GFP_KERNEL); if (!nsblk) goto err; - dev = &nsblk->dev; + dev = &nsblk->common.dev; dev->type = &namespace_blk_device_type; dev->parent = &nd_region->dev; dev_set_name(dev, "namespace%d.%d", nd_region->id, count); @@ -1482,7 +1618,7 @@ static struct device **create_namespace_blk(struct nd_region *nd_region) if (!res) goto err; nd_dbg_dpa(nd_region, ndd, res, "%s assign\n", - dev_name(&nsblk->dev)); + dev_name(&nsblk->common.dev)); } dev_dbg(&nd_region->dev, "%s: discovered %d blk namespace%s\n", @@ -1503,7 +1639,7 @@ static struct device **create_namespace_blk(struct nd_region *nd_region) nsblk = kzalloc(sizeof(*nsblk), GFP_KERNEL); if (!nsblk) goto err; - dev = &nsblk->dev; + dev = &nsblk->common.dev; dev->type = &namespace_blk_device_type; dev->parent = &nd_region->dev; devs[count++] = dev; @@ -1514,7 +1650,7 @@ static struct device **create_namespace_blk(struct nd_region *nd_region) err: for (i = 0; i < count; i++) { nsblk = to_nd_namespace_blk(devs[i]); - namespace_blk_release(&nsblk->dev); + namespace_blk_release(&nsblk->common.dev); } kfree(devs); return NULL; -- cgit v1.2.3