int nd_btt_probe(struct nd_namespace_common *ndns, void *drvdata) { int rc; struct device *dev; struct btt_sb *btt_sb; struct nd_region *nd_region = to_nd_region(ndns->dev.parent); if (ndns->force_raw) return -ENODEV; nvdimm_bus_lock(&ndns->dev); dev = __nd_btt_create(nd_region, 0, NULL, ndns); nvdimm_bus_unlock(&ndns->dev); if (!dev) return -ENOMEM; dev_set_drvdata(dev, drvdata); btt_sb = kzalloc(sizeof(*btt_sb), GFP_KERNEL); rc = __nd_btt_probe(to_nd_btt(dev), ndns, btt_sb); kfree(btt_sb); dev_dbg(&ndns->dev, "%s: btt: %s\n", __func__, rc == 0 ? dev_name(dev) : "<none>"); if (rc < 0) { __nd_btt_detach_ndns(to_nd_btt(dev)); put_device(dev); } return rc; }
static ssize_t sector_size_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nd_btt *nd_btt = to_nd_btt(dev); return nd_sector_size_show(nd_btt->lbasize, btt_lbasize_supported, buf); }
static int is_uuid_busy(struct device *dev, void *data) { u8 *uuid1 = data, *uuid2 = NULL; if (is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); uuid2 = nspm->uuid; } else if (is_namespace_blk(dev)) { struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev); uuid2 = nsblk->uuid; } else if (is_nd_btt(dev)) { struct nd_btt *nd_btt = to_nd_btt(dev); uuid2 = nd_btt->uuid; } else if (is_nd_pfn(dev)) { struct nd_pfn *nd_pfn = to_nd_pfn(dev); uuid2 = nd_pfn->uuid; } if (uuid2 && memcmp(uuid1, uuid2, NSLABEL_UUID_LEN) == 0) return -EBUSY; return 0; }
static bool is_nd_btt_idle(struct device *dev) { struct nd_region *nd_region = to_nd_region(dev->parent); struct nd_btt *nd_btt = to_nd_btt(dev); if (nd_region->btt_seed == dev || nd_btt->ndns || dev->driver) return false; return true; }
static ssize_t uuid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nd_btt *nd_btt = to_nd_btt(dev); if (nd_btt->uuid) return sprintf(buf, "%pUb\n", nd_btt->uuid); return sprintf(buf, "\n"); }
static void nd_btt_release(struct device *dev) { struct nd_region *nd_region = to_nd_region(dev->parent); struct nd_btt *nd_btt = to_nd_btt(dev); dev_dbg(dev, "%s\n", __func__); nd_btt_detach_ndns(nd_btt); ida_simple_remove(&nd_region->btt_ida, nd_btt->id); kfree(nd_btt->uuid); kfree(nd_btt); }
static ssize_t namespace_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nd_btt *nd_btt = to_nd_btt(dev); ssize_t rc; nvdimm_bus_lock(dev); rc = sprintf(buf, "%s\n", nd_btt->ndns ? dev_name(&nd_btt->ndns->dev) : ""); nvdimm_bus_unlock(dev); return rc; }
static ssize_t uuid_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct nd_btt *nd_btt = to_nd_btt(dev); ssize_t rc; device_lock(dev); rc = nd_uuid_store(dev, &nd_btt->uuid, buf, len); dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__, rc, buf, buf[len - 1] == '\n' ? "" : "\n"); device_unlock(dev); return rc ? rc : len; }
/* * Upon successful probe/remove, take/release a reference on the * associated interleave set (if present), and plant new btt + namespace * seeds. Also, on the removal of a BLK region, notify the provider to * disable the region. */ static void nd_region_notify_driver_action(struct nvdimm_bus *nvdimm_bus, struct device *dev, bool probe) { struct nd_region *nd_region; if (!probe && (is_nd_pmem(dev) || is_nd_blk(dev))) { int i; nd_region = to_nd_region(dev); for (i = 0; i < nd_region->ndr_mappings; i++) { struct nd_mapping *nd_mapping = &nd_region->mapping[i]; struct nvdimm_drvdata *ndd = nd_mapping->ndd; struct nvdimm *nvdimm = nd_mapping->nvdimm; kfree(nd_mapping->labels); nd_mapping->labels = NULL; put_ndd(ndd); nd_mapping->ndd = NULL; if (ndd) atomic_dec(&nvdimm->busy); } if (is_nd_pmem(dev)) return; to_nd_blk_region(dev)->disable(nvdimm_bus, dev); } if (dev->parent && is_nd_blk(dev->parent) && probe) { nd_region = to_nd_region(dev->parent); nvdimm_bus_lock(dev); if (nd_region->ns_seed == dev) nd_region_create_blk_seed(nd_region); nvdimm_bus_unlock(dev); } if (is_nd_btt(dev) && probe) { struct nd_btt *nd_btt = to_nd_btt(dev); nd_region = to_nd_region(dev->parent); nvdimm_bus_lock(dev); if (nd_region->btt_seed == dev) nd_region_create_btt_seed(nd_region); if (nd_region->ns_seed == &nd_btt->ndns->dev && is_nd_blk(dev->parent)) nd_region_create_blk_seed(nd_region); nvdimm_bus_unlock(dev); } }
static ssize_t sector_size_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct nd_btt *nd_btt = to_nd_btt(dev); ssize_t rc; device_lock(dev); nvdimm_bus_lock(dev); rc = nd_sector_size_store(dev, buf, &nd_btt->lbasize, btt_lbasize_supported); dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__, rc, buf, buf[len - 1] == '\n' ? "" : "\n"); nvdimm_bus_unlock(dev); device_unlock(dev); return rc ? rc : len; }
static void nd_detach_and_reset(struct device *dev, struct nd_namespace_common **_ndns) { /* detach the namespace and destroy / reset the device */ nd_detach_ndns(dev, _ndns); if (is_idle(dev, *_ndns)) { nd_device_unregister(dev, ND_ASYNC); } else if (is_nd_btt(dev)) { struct nd_btt *nd_btt = to_nd_btt(dev); nd_btt->lbasize = 0; kfree(nd_btt->uuid); nd_btt->uuid = NULL; } else if (is_nd_pfn(dev) || is_nd_dax(dev)) { struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev); kfree(nd_pfn->uuid); nd_pfn->uuid = NULL; nd_pfn->mode = PFN_MODE_NONE; } }
static ssize_t __namespace_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct nd_btt *nd_btt = to_nd_btt(dev); struct nd_namespace_common *ndns; struct device *found; char *name; if (dev->driver) { dev_dbg(dev, "%s: -EBUSY\n", __func__); return -EBUSY; } name = kstrndup(buf, len, GFP_KERNEL); if (!name) return -ENOMEM; strim(name); if (strncmp(name, "namespace", 9) == 0 || strcmp(name, "") == 0) /* pass */; else { len = -EINVAL; goto out; } ndns = nd_btt->ndns; if (strcmp(name, "") == 0) { /* detach the namespace and destroy / reset the btt device */ nd_btt_detach_ndns(nd_btt); if (is_nd_btt_idle(dev)) nd_device_unregister(dev, ND_ASYNC); else { nd_btt->lbasize = 0; kfree(nd_btt->uuid); nd_btt->uuid = NULL; } goto out; } else if (ndns) { dev_dbg(dev, "namespace already set to: %s\n", dev_name(&ndns->dev)); len = -EBUSY; goto out; } found = device_find_child(dev->parent, name, namespace_match); if (!found) { dev_dbg(dev, "'%s' not found under %s\n", name, dev_name(dev->parent)); len = -ENODEV; goto out; } ndns = to_ndns(found); if (__nvdimm_namespace_capacity(ndns) < SZ_16M) { dev_dbg(dev, "%s too small to host btt\n", name); len = -ENXIO; goto out_attach; } WARN_ON_ONCE(!is_nvdimm_bus_locked(&nd_btt->dev)); if (!nd_btt_attach_ndns(nd_btt, ndns)) { dev_dbg(dev, "%s already claimed\n", dev_name(&ndns->dev)); len = -EBUSY; } out_attach: put_device(&ndns->dev); /* from device_find_child */ out: kfree(name); return len; }