diff options
Diffstat (limited to 'drivers/nvdimm')
-rw-r--r-- | drivers/nvdimm/Kconfig | 13 | ||||
-rw-r--r-- | drivers/nvdimm/Makefile | 4 | ||||
-rw-r--r-- | drivers/nvdimm/btt.c | 26 | ||||
-rw-r--r-- | drivers/nvdimm/btt_devs.c | 24 | ||||
-rw-r--r-- | drivers/nvdimm/bus.c | 60 | ||||
-rw-r--r-- | drivers/nvdimm/claim.c | 14 | ||||
-rw-r--r-- | drivers/nvdimm/core.c | 9 | ||||
-rw-r--r-- | drivers/nvdimm/dax_devs.c | 27 | ||||
-rw-r--r-- | drivers/nvdimm/dimm_devs.c | 164 | ||||
-rw-r--r-- | drivers/nvdimm/e820.c | 13 | ||||
-rw-r--r-- | drivers/nvdimm/label.c | 5 | ||||
-rw-r--r-- | drivers/nvdimm/namespace_devs.c | 161 | ||||
-rw-r--r-- | drivers/nvdimm/nd-core.h | 76 | ||||
-rw-r--r-- | drivers/nvdimm/nd.h | 33 | ||||
-rw-r--r-- | drivers/nvdimm/of_pmem.c | 15 | ||||
-rw-r--r-- | drivers/nvdimm/pfn.h | 5 | ||||
-rw-r--r-- | drivers/nvdimm/pfn_devs.c | 181 | ||||
-rw-r--r-- | drivers/nvdimm/pmem.c | 53 | ||||
-rw-r--r-- | drivers/nvdimm/region.c | 4 | ||||
-rw-r--r-- | drivers/nvdimm/region_devs.c | 316 | ||||
-rw-r--r-- | drivers/nvdimm/security.c | 203 |
21 files changed, 758 insertions, 648 deletions
diff --git a/drivers/nvdimm/Kconfig b/drivers/nvdimm/Kconfig index a5fde15e91d3..b7d1eb38b27d 100644 --- a/drivers/nvdimm/Kconfig +++ b/drivers/nvdimm/Kconfig @@ -4,6 +4,7 @@ menuconfig LIBNVDIMM depends on PHYS_ADDR_T_64BIT depends on HAS_IOMEM depends on BLK_DEV + select MEMREGION help Generic support for non-volatile memory devices including ACPI-6-NFIT defined resources. On platforms that define an @@ -118,4 +119,16 @@ config NVDIMM_KEYS depends on ENCRYPTED_KEYS depends on (LIBNVDIMM=ENCRYPTED_KEYS) || LIBNVDIMM=m +config NVDIMM_TEST_BUILD + tristate "Build the unit test core" + depends on m + depends on COMPILE_TEST && X86_64 + default m if COMPILE_TEST + help + Build the core of the unit test infrastructure. The result of + this build is non-functional for unit test execution, but it + otherwise helps catch build errors induced by changes to the + core devm_memremap_pages() implementation and other + infrastructure. + endif diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile index cefe233e0b52..29203f3d3069 100644 --- a/drivers/nvdimm/Makefile +++ b/drivers/nvdimm/Makefile @@ -29,3 +29,7 @@ libnvdimm-$(CONFIG_BTT) += btt_devs.o libnvdimm-$(CONFIG_NVDIMM_PFN) += pfn_devs.o libnvdimm-$(CONFIG_NVDIMM_DAX) += dax_devs.o libnvdimm-$(CONFIG_NVDIMM_KEYS) += security.o + +TOOLS := ../../tools +TEST_SRC := $(TOOLS)/testing/nvdimm/test +obj-$(CONFIG_NVDIMM_TEST_BUILD) += $(TEST_SRC)/iomap.o diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c index a8d56887ec88..0d04ea3d9fd7 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -392,9 +392,9 @@ static int btt_flog_write(struct arena_info *arena, u32 lane, u32 sub, arena->freelist[lane].sub = 1 - arena->freelist[lane].sub; if (++(arena->freelist[lane].seq) == 4) arena->freelist[lane].seq = 1; - if (ent_e_flag(ent->old_map)) + if (ent_e_flag(le32_to_cpu(ent->old_map))) arena->freelist[lane].has_err = 1; - arena->freelist[lane].block = le32_to_cpu(ent_lba(ent->old_map)); + arena->freelist[lane].block = ent_lba(le32_to_cpu(ent->old_map)); return ret; } @@ -560,8 +560,8 @@ static int btt_freelist_init(struct arena_info *arena) * FIXME: if error clearing fails during init, we want to make * the BTT read-only */ - if (ent_e_flag(log_new.old_map) && - !ent_normal(log_new.old_map)) { + if (ent_e_flag(le32_to_cpu(log_new.old_map)) && + !ent_normal(le32_to_cpu(log_new.old_map))) { arena->freelist[i].has_err = 1; ret = arena_clear_freelist_error(arena, i); if (ret) @@ -1261,11 +1261,11 @@ static int btt_read_pg(struct btt *btt, struct bio_integrity_payload *bip, ret = btt_data_read(arena, page, off, postmap, cur_len); if (ret) { - int rc; - /* Media error - set the e_flag */ - rc = btt_map_write(arena, premap, postmap, 0, 1, - NVDIMM_IO_ATOMIC); + if (btt_map_write(arena, premap, postmap, 0, 1, NVDIMM_IO_ATOMIC)) + dev_warn_ratelimited(to_dev(arena), + "Error persistently tracking bad blocks at %#x\n", + premap); goto out_rtt; } @@ -1674,7 +1674,8 @@ int nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns) struct nd_region *nd_region; struct btt_sb *btt_sb; struct btt *btt; - size_t rawsize; + size_t size, rawsize; + int rc; if (!nd_btt->uuid || !nd_btt->ndns || !nd_btt->lbasize) { dev_dbg(&nd_btt->dev, "incomplete btt configuration\n"); @@ -1685,6 +1686,11 @@ int nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns) if (!btt_sb) return -ENOMEM; + size = nvdimm_namespace_capacity(ndns); + rc = devm_namespace_enable(&nd_btt->dev, ndns, size); + if (rc) + return rc; + /* * If this returns < 0, that is ok as it just means there wasn't * an existing BTT, and we're creating a new one. We still need to @@ -1693,7 +1699,7 @@ int nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns) */ nd_btt_version(nd_btt, ndns, btt_sb); - rawsize = nvdimm_namespace_capacity(ndns) - nd_btt->initial_offset; + rawsize = size - nd_btt->initial_offset; if (rawsize < ARENA_MIN_SIZE) { dev_dbg(&nd_btt->dev, "%s must be at least %ld bytes\n", dev_name(&ndns->dev), diff --git a/drivers/nvdimm/btt_devs.c b/drivers/nvdimm/btt_devs.c index 3508a79110c7..05feb97e11ce 100644 --- a/drivers/nvdimm/btt_devs.c +++ b/drivers/nvdimm/btt_devs.c @@ -25,17 +25,6 @@ static void nd_btt_release(struct device *dev) kfree(nd_btt); } -static struct device_type nd_btt_device_type = { - .name = "nd_btt", - .release = nd_btt_release, -}; - -bool is_nd_btt(struct device *dev) -{ - return dev->type == &nd_btt_device_type; -} -EXPORT_SYMBOL(is_nd_btt); - struct nd_btt *to_nd_btt(struct device *dev) { struct nd_btt *nd_btt = container_of(dev, struct nd_btt, dev); @@ -178,6 +167,18 @@ static const struct attribute_group *nd_btt_attribute_groups[] = { NULL, }; +static const struct device_type nd_btt_device_type = { + .name = "nd_btt", + .release = nd_btt_release, + .groups = nd_btt_attribute_groups, +}; + +bool is_nd_btt(struct device *dev) +{ + return dev->type == &nd_btt_device_type; +} +EXPORT_SYMBOL(is_nd_btt); + static struct device *__nd_btt_create(struct nd_region *nd_region, unsigned long lbasize, u8 *uuid, struct nd_namespace_common *ndns) @@ -204,7 +205,6 @@ static struct device *__nd_btt_create(struct nd_region *nd_region, dev_set_name(dev, "btt%d.%d", nd_region->id, nd_btt->id); dev->parent = &nd_region->dev; dev->type = &nd_btt_device_type; - dev->groups = nd_btt_attribute_groups; device_initialize(&nd_btt->dev); if (ndns && !__nd_attach_ndns(&nd_btt->dev, ndns, &nd_btt->ndns)) { dev_dbg(&ndns->dev, "failed, already claimed by %s\n", diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index 798c5c4aea9c..a8b515968569 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -95,10 +95,9 @@ static int nvdimm_bus_probe(struct device *dev) rc = nd_drv->probe(dev); debug_nvdimm_unlock(dev); - if (rc == 0) - nd_region_probe_success(nvdimm_bus, dev); - else - nd_region_disable(nvdimm_bus, dev); + if ((rc == 0 || rc == -EOPNOTSUPP) && + dev->parent && is_nd_region(dev->parent)) + nd_region_advance_seeds(to_nd_region(dev->parent), dev); nvdimm_bus_probe_end(nvdimm_bus); dev_dbg(&nvdimm_bus->dev, "END: %s.probe(%s) = %d\n", dev->driver->name, @@ -121,7 +120,6 @@ static int nvdimm_bus_remove(struct device *dev) rc = nd_drv->remove(dev); debug_nvdimm_unlock(dev); } - nd_region_disable(nvdimm_bus, dev); dev_dbg(&nvdimm_bus->dev, "%s.remove(%s) = %d\n", dev->driver->name, dev_name(dev), rc); @@ -182,7 +180,7 @@ static int nvdimm_clear_badblocks_region(struct device *dev, void *data) sector_t sector; /* make sure device is a region */ - if (!is_nd_pmem(dev)) + if (!is_memory(dev)) return 0; nd_region = to_nd_region(dev); @@ -302,9 +300,14 @@ static void nvdimm_bus_release(struct device *dev) kfree(nvdimm_bus); } +static const struct device_type nvdimm_bus_dev_type = { + .release = nvdimm_bus_release, + .groups = nvdimm_bus_attribute_groups, +}; + bool is_nvdimm_bus(struct device *dev) { - return dev->release == nvdimm_bus_release; + return dev->type == &nvdimm_bus_dev_type; } struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev) @@ -357,7 +360,7 @@ struct nvdimm_bus *nvdimm_bus_register(struct device *parent, badrange_init(&nvdimm_bus->badrange); nvdimm_bus->nd_desc = nd_desc; nvdimm_bus->dev.parent = parent; - nvdimm_bus->dev.release = nvdimm_bus_release; + nvdimm_bus->dev.type = &nvdimm_bus_dev_type; nvdimm_bus->dev.groups = nd_desc->attr_groups; nvdimm_bus->dev.bus = &nvdimm_bus_type; nvdimm_bus->dev.of_node = nd_desc->of_node; @@ -400,7 +403,7 @@ static int child_unregister(struct device *dev, void *data) /* We are shutting down. Make state frozen artificially. */ nvdimm_bus_lock(dev); - nvdimm->sec.state = NVDIMM_SECURITY_FROZEN; + set_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags); if (test_and_clear_bit(NDD_WORK_PENDING, &nvdimm->flags)) dev_put = true; nvdimm_bus_unlock(dev); @@ -671,10 +674,9 @@ static struct attribute *nd_device_attributes[] = { /* * nd_device_attribute_group - generic attributes for all devices on an nd bus */ -struct attribute_group nd_device_attribute_group = { +const struct attribute_group nd_device_attribute_group = { .attrs = nd_device_attributes, }; -EXPORT_SYMBOL_GPL(nd_device_attribute_group); static ssize_t numa_node_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -683,28 +685,56 @@ static ssize_t numa_node_show(struct device *dev, } static DEVICE_ATTR_RO(numa_node); +static int nvdimm_dev_to_target_node(struct device *dev) +{ + struct device *parent = dev->parent; + struct nd_region *nd_region = NULL; + + if (is_nd_region(dev)) + nd_region = to_nd_region(dev); + else if (parent && is_nd_region(parent)) + nd_region = to_nd_region(parent); + + if (!nd_region) + return NUMA_NO_NODE; + return nd_region->target_node; +} + +static ssize_t target_node_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", nvdimm_dev_to_target_node(dev)); +} +static DEVICE_ATTR_RO(target_node); + static struct attribute *nd_numa_attributes[] = { &dev_attr_numa_node.attr, + &dev_attr_target_node.attr, NULL, }; static umode_t nd_numa_attr_visible(struct kobject *kobj, struct attribute *a, int n) { + struct device *dev = container_of(kobj, typeof(*dev), kobj); + if (!IS_ENABLED(CONFIG_NUMA)) return 0; + if (a == &dev_attr_target_node.attr && + nvdimm_dev_to_target_node(dev) == NUMA_NO_NODE) + return 0; + return a->mode; } /* * nd_numa_attribute_group - NUMA attributes for all devices on an nd bus */ -struct attribute_group nd_numa_attribute_group = { +const struct attribute_group nd_numa_attribute_group = { .attrs = nd_numa_attributes, .is_visible = nd_numa_attr_visible, }; -EXPORT_SYMBOL_GPL(nd_numa_attribute_group); int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus) { @@ -1229,7 +1259,7 @@ static const struct file_operations nvdimm_bus_fops = { .owner = THIS_MODULE, .open = nd_open, .unlocked_ioctl = bus_ioctl, - .compat_ioctl = bus_ioctl, + .compat_ioctl = compat_ptr_ioctl, .llseek = noop_llseek, }; @@ -1237,7 +1267,7 @@ static const struct file_operations nvdimm_fops = { .owner = THIS_MODULE, .open = nd_open, .unlocked_ioctl = dimm_ioctl, - .compat_ioctl = dimm_ioctl, + .compat_ioctl = compat_ptr_ioctl, .llseek = noop_llseek, }; diff --git a/drivers/nvdimm/claim.c b/drivers/nvdimm/claim.c index 2985ca949912..45964acba944 100644 --- a/drivers/nvdimm/claim.c +++ b/drivers/nvdimm/claim.c @@ -300,13 +300,14 @@ static int nsio_rw_bytes(struct nd_namespace_common *ndns, return rc; } -int devm_nsio_enable(struct device *dev, struct nd_namespace_io *nsio) +int devm_nsio_enable(struct device *dev, struct nd_namespace_io *nsio, + resource_size_t size) { struct resource *res = &nsio->res; struct nd_namespace_common *ndns = &nsio->common; - nsio->size = resource_size(res); - if (!devm_request_mem_region(dev, res->start, resource_size(res), + nsio->size = size; + if (!devm_request_mem_region(dev, res->start, size, dev_name(&ndns->dev))) { dev_warn(dev, "could not reserve region %pR\n", res); return -EBUSY; @@ -318,12 +319,10 @@ int devm_nsio_enable(struct device *dev, struct nd_namespace_io *nsio) nvdimm_badblocks_populate(to_nd_region(ndns->dev.parent), &nsio->bb, &nsio->res); - nsio->addr = devm_memremap(dev, res->start, resource_size(res), - ARCH_MEMREMAP_PMEM); + nsio->addr = devm_memremap(dev, res->start, size, ARCH_MEMREMAP_PMEM); return PTR_ERR_OR_ZERO(nsio->addr); } -EXPORT_SYMBOL_GPL(devm_nsio_enable); void devm_nsio_disable(struct device *dev, struct nd_namespace_io *nsio) { @@ -331,6 +330,5 @@ void devm_nsio_disable(struct device *dev, struct nd_namespace_io *nsio) devm_memunmap(dev, nsio->addr); devm_exit_badblocks(dev, &nsio->bb); - devm_release_mem_region(dev, res->start, resource_size(res)); + devm_release_mem_region(dev, res->start, nsio->size); } -EXPORT_SYMBOL_GPL(devm_nsio_disable); diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c index 9204f1e9fd14..fe9bd6febdd2 100644 --- a/drivers/nvdimm/core.c +++ b/drivers/nvdimm/core.c @@ -385,10 +385,14 @@ static struct attribute *nvdimm_bus_attributes[] = { NULL, }; -struct attribute_group nvdimm_bus_attribute_group = { +static const struct attribute_group nvdimm_bus_attribute_group = { .attrs = nvdimm_bus_attributes, }; -EXPORT_SYMBOL_GPL(nvdimm_bus_attribute_group); + +const struct attribute_group *nvdimm_bus_attribute_groups[] = { + &nvdimm_bus_attribute_group, + NULL, +}; int nvdimm_bus_add_badrange(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) { @@ -455,7 +459,6 @@ static __exit void libnvdimm_exit(void) nd_region_exit(); nvdimm_exit(); nvdimm_bus_exit(); - nd_region_devs_exit(); nvdimm_devs_exit(); } diff --git a/drivers/nvdimm/dax_devs.c b/drivers/nvdimm/dax_devs.c index 6d22b0f83b3b..99965077bac4 100644 --- a/drivers/nvdimm/dax_devs.c +++ b/drivers/nvdimm/dax_devs.c @@ -23,17 +23,6 @@ static void nd_dax_release(struct device *dev) kfree(nd_dax); } -static struct device_type nd_dax_device_type = { - .name = "nd_dax", - .release = nd_dax_release, -}; - -bool is_nd_dax(struct device *dev) -{ - return dev ? dev->type == &nd_dax_device_type : false; -} -EXPORT_SYMBOL(is_nd_dax); - struct nd_dax *to_nd_dax(struct device *dev) { struct nd_dax *nd_dax = container_of(dev, struct nd_dax, nd_pfn.dev); @@ -43,13 +32,18 @@ struct nd_dax *to_nd_dax(struct device *dev) } EXPORT_SYMBOL(to_nd_dax); -static const struct attribute_group *nd_dax_attribute_groups[] = { - &nd_pfn_attribute_group, - &nd_device_attribute_group, - &nd_numa_attribute_group, - NULL, +static const struct device_type nd_dax_device_type = { + .name = "nd_dax", + .release = nd_dax_release, + .groups = nd_pfn_attribute_groups, }; +bool is_nd_dax(struct device *dev) +{ + return dev ? dev->type == &nd_dax_device_type : false; +} +EXPORT_SYMBOL(is_nd_dax); + static struct nd_dax *nd_dax_alloc(struct nd_region *nd_region) { struct nd_pfn *nd_pfn; @@ -69,7 +63,6 @@ static struct nd_dax *nd_dax_alloc(struct nd_region *nd_region) dev = &nd_pfn->dev; dev_set_name(dev, "dax%d.%d", nd_region->id, nd_pfn->id); - dev->groups = nd_dax_attribute_groups; dev->type = &nd_dax_device_type; dev->parent = &nd_region->dev; diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index 29a065e769ea..94ea6dba6b4f 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -202,16 +202,6 @@ static void nvdimm_release(struct device *dev) kfree(nvdimm); } -static struct device_type nvdimm_device_type = { - .name = "nvdimm", - .release = nvdimm_release, -}; - -bool is_nvdimm(struct device *dev) -{ - return dev->type == &nvdimm_device_type; -} - struct nvdimm *to_nvdimm(struct device *dev) { struct nvdimm *nvdimm = container_of(dev, struct nvdimm, dev); @@ -372,106 +362,26 @@ __weak ssize_t security_show(struct device *dev, { struct nvdimm *nvdimm = to_nvdimm(dev); - switch (nvdimm->sec.state) { - case NVDIMM_SECURITY_DISABLED: + if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags)) return sprintf(buf, "disabled\n"); - case NVDIMM_SECURITY_UNLOCKED: + if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags)) return sprintf(buf, "unlocked\n"); - case NVDIMM_SECURITY_LOCKED: + if (test_bit(NVDIMM_SECURITY_LOCKED, &nvdimm->sec.flags)) return sprintf(buf, "locked\n"); - case NVDIMM_SECURITY_FROZEN: - return sprintf(buf, "frozen\n"); - case NVDIMM_SECURITY_OVERWRITE: + if (test_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags)) return sprintf(buf, "overwrite\n"); - default: - return -ENOTTY; - } - return -ENOTTY; } -#define OPS \ - C( OP_FREEZE, "freeze", 1), \ - C( OP_DISABLE, "disable", 2), \ - C( OP_UPDATE, "update", 3), \ - C( OP_ERASE, "erase", 2), \ - C( OP_OVERWRITE, "overwrite", 2), \ - C( OP_MASTER_UPDATE, "master_update", 3), \ - C( OP_MASTER_ERASE, "master_erase", 2) -#undef C -#define C(a, b, c) a -enum nvdimmsec_op_ids { OPS }; -#undef C -#define C(a, b, c) { b, c } -static struct { - const char *name; - int args; -} ops[] = { OPS }; -#undef C - -#define SEC_CMD_SIZE 32 -#define KEY_ID_SIZE 10 - -static ssize_t __security_store(struct device *dev, const char *buf, size_t len) +static ssize_t frozen_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct nvdimm *nvdimm = to_nvdimm(dev); - ssize_t rc; - char cmd[SEC_CMD_SIZE+1], keystr[KEY_ID_SIZE+1], - nkeystr[KEY_ID_SIZE+1]; - unsigned int key, newkey; - int i; - - if (atomic_read(&nvdimm->busy)) - return -EBUSY; - - rc = sscanf(buf, "%"__stringify(SEC_CMD_SIZE)"s" - " %"__stringify(KEY_ID_SIZE)"s" - " %"__stringify(KEY_ID_SIZE)"s", - cmd, keystr, nkeystr); - if (rc < 1) - return -EINVAL; - for (i = 0; i < ARRAY_SIZE(ops); i++) - if (sysfs_streq(cmd, ops[i].name)) - break; - if (i >= ARRAY_SIZE(ops)) - return -EINVAL; - if (ops[i].args > 1) - rc = kstrtouint(keystr, 0, &key); - if (rc >= 0 && ops[i].args > 2) - rc = kstrtouint(nkeystr, 0, &newkey); - if (rc < 0) - return rc; - - if (i == OP_FREEZE) { - dev_dbg(dev, "freeze\n"); - rc = nvdimm_security_freeze(nvdimm); - } else if (i == OP_DISABLE) { - dev_dbg(dev, "disable %u\n", key); - rc = nvdimm_security_disable(nvdimm, key); - } else if (i == OP_UPDATE) { - dev_dbg(dev, "update %u %u\n", key, newkey); - rc = nvdimm_security_update(nvdimm, key, newkey, NVDIMM_USER); - } else if (i == OP_ERASE) { - dev_dbg(dev, "erase %u\n", key); - rc = nvdimm_security_erase(nvdimm, key, NVDIMM_USER); - } else if (i == OP_OVERWRITE) { - dev_dbg(dev, "overwrite %u\n", key); - rc = nvdimm_security_overwrite(nvdimm, key); - } else if (i == OP_MASTER_UPDATE) { - dev_dbg(dev, "master_update %u %u\n", key, newkey); - rc = nvdimm_security_update(nvdimm, key, newkey, - NVDIMM_MASTER); - } else if (i == OP_MASTER_ERASE) { - dev_dbg(dev, "master_erase %u\n", key); - rc = nvdimm_security_erase(nvdimm, key, - NVDIMM_MASTER); - } else - return -EINVAL; - if (rc == 0) - rc = len; - return rc; + return sprintf(buf, "%d\n", test_bit(NVDIMM_SECURITY_FROZEN, + &nvdimm->sec.flags)); } +static DEVICE_ATTR_RO(frozen); static ssize_t security_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) @@ -487,7 +397,7 @@ static ssize_t security_store(struct device *dev, nd_device_lock(dev); nvdimm_bus_lock(dev); wait_nvdimm_bus_probe_idle(dev); - rc = __security_store(dev, buf, len); + rc = nvdimm_security_store(dev, buf, len); nvdimm_bus_unlock(dev); nd_device_unlock(dev); @@ -501,6 +411,7 @@ static struct attribute *nvdimm_attributes[] = { &dev_attr_commands.attr, &dev_attr_available_slots.attr, &dev_attr_security.attr, + &dev_attr_frozen.attr, NULL, }; @@ -509,24 +420,47 @@ static umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n) struct device *dev = container_of(kobj, typeof(*dev), kobj); struct nvdimm *nvdimm = to_nvdimm(dev); - if (a != &dev_attr_security.attr) + if (a != &dev_attr_security.attr && a != &dev_attr_frozen.attr) return a->mode; - if (nvdimm->sec.state < 0) + if (!nvdimm->sec.flags) return 0; - /* Are there any state mutation ops? */ - if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable - || nvdimm->sec.ops->change_key - || nvdimm->sec.ops->erase - || nvdimm->sec.ops->overwrite) + + if (a == &dev_attr_security.attr) { + /* Are there any state mutation ops (make writable)? */ + if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable + || nvdimm->sec.ops->change_key + || nvdimm->sec.ops->erase + || nvdimm->sec.ops->overwrite) + return a->mode; + return 0444; + } + + if (nvdimm->sec.ops->freeze) return a->mode; - return 0444; + return 0; } -struct attribute_group nvdimm_attribute_group = { +static const struct attribute_group nvdimm_attribute_group = { .attrs = nvdimm_attributes, .is_visible = nvdimm_visible, }; -EXPORT_SYMBOL_GPL(nvdimm_attribute_group); + +static const struct attribute_group *nvdimm_attribute_groups[] = { + &nd_device_attribute_group, + &nvdimm_attribute_group, + NULL, +}; + +static const struct device_type nvdimm_device_type = { + .name = "nvdimm", + .release = nvdimm_release, + .groups = nvdimm_attribute_groups, +}; + +bool is_nvdimm(struct device *dev) +{ + return dev->type == &nvdimm_device_type; +} struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, const struct attribute_group **groups, @@ -569,8 +503,8 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, * attribute visibility. */ /* get security state and extended (master) state */ - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER); - nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, NVDIMM_MASTER); + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); + nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER); nd_device_register(dev); return nvdimm; @@ -588,7 +522,7 @@ int nvdimm_security_setup_events(struct device *dev) { struct nvdimm *nvdimm = to_nvdimm(dev); - if (nvdimm->sec.state < 0 || !nvdimm->sec.ops + if (!nvdimm->sec.flags || !nvdimm->sec.ops || !nvdimm->sec.ops->overwrite) return 0; nvdimm->sec.overwrite_state = sysfs_get_dirent(dev->kobj.sd, "security"); @@ -614,7 +548,7 @@ int nvdimm_security_freeze(struct nvdimm *nvdimm) if (!nvdimm->sec.ops || !nvdimm->sec.ops->freeze) return -EOPNOTSUPP; - if (nvdimm->sec.state < 0) + if (!nvdimm->sec.flags) return -EIO; if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { @@ -623,7 +557,7 @@ int nvdimm_security_freeze(struct nvdimm *nvdimm) } rc = nvdimm->sec.ops->freeze(nvdimm); - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER); + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); return rc; } diff --git a/drivers/nvdimm/e820.c b/drivers/nvdimm/e820.c index 87f72f725e4f..e02f60ad6c99 100644 --- a/drivers/nvdimm/e820.c +++ b/drivers/nvdimm/e820.c @@ -8,17 +8,6 @@ #include <linux/libnvdimm.h> #include <linux/module.h> -static const struct attribute_group *e820_pmem_attribute_groups[] = { - &nvdimm_bus_attribute_group, - NULL, -}; - -static const struct attribute_group *e820_pmem_region_attribute_groups[] = { - &nd_region_attribute_group, - &nd_device_attribute_group, - NULL, -}; - static int e820_pmem_remove(struct platform_device *pdev) { struct nvdimm_bus *nvdimm_bus = platform_get_drvdata(pdev); @@ -46,7 +35,6 @@ static int e820_register_one(struct resource *res, void *data) memset(&ndr_desc, 0, sizeof(ndr_desc)); ndr_desc.res = res; - ndr_desc.attr_groups = e820_pmem_region_attribute_groups; ndr_desc.numa_node = e820_range_to_nid(res->start); ndr_desc.target_node = ndr_desc.numa_node; set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags); @@ -62,7 +50,6 @@ static int e820_pmem_probe(struct platform_device *pdev) struct nvdimm_bus *nvdimm_bus; int rc = -ENXIO; - nd_desc.attr_groups = e820_pmem_attribute_groups; nd_desc.provider_name = "e820"; nd_desc.module = THIS_MODULE; nvdimm_bus = nvdimm_bus_register(dev, &nd_desc); diff --git a/drivers/nvdimm/label.c b/drivers/nvdimm/label.c index 73e197babc2f..47a4828b8b31 100644 --- a/drivers/nvdimm/label.c +++ b/drivers/nvdimm/label.c @@ -353,11 +353,6 @@ static bool slot_valid(struct nvdimm_drvdata *ndd, if (slot != __le32_to_cpu(nd_label->slot)) return false; - /* check that DPA allocations are page aligned */ - if ((__le64_to_cpu(nd_label->dpa) - | __le64_to_cpu(nd_label->rawsize)) % SZ_4K) - return false; - /* check checksum */ if (namespace_label_has(ndd, checksum)) { u64 sum, sum_save; diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index a16e52251a30..032dc61725ff 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -44,35 +44,9 @@ static void namespace_blk_release(struct device *dev) kfree(nsblk); } -static const struct device_type namespace_io_device_type = { - .name = "nd_namespace_io", - .release = namespace_io_release, -}; - -static const struct device_type namespace_pmem_device_type = { - .name = "nd_namespace_pmem", - .release = namespace_pmem_release, -}; - -static const struct device_type namespace_blk_device_type = { - .name = "nd_namespace_blk", - .release = namespace_blk_release, -}; - -static bool is_namespace_pmem(const struct device *dev) -{ - return dev ? dev->type == &namespace_pmem_device_type : false; -} - -static bool is_namespace_blk(const struct device *dev) -{ - return dev ? dev->type == &namespace_blk_device_type : false; -} - -static bool is_namespace_io(const struct device *dev) -{ - return dev ? dev->type == &namespace_io_device_type : false; -} +static bool is_namespace_pmem(const struct device *dev); +static bool is_namespace_blk(const struct device *dev); +static bool is_namespace_io(const struct device *dev); static int is_uuid_busy(struct device *dev, void *data) { @@ -1006,10 +980,10 @@ static ssize_t __size_store(struct device *dev, unsigned long long val) return -ENXIO; } - div_u64_rem(val, SZ_4K * nd_region->ndr_mappings, &remainder); + div_u64_rem(val, PAGE_SIZE * nd_region->ndr_mappings, &remainder); if (remainder) { - dev_dbg(dev, "%llu is not %dK aligned\n", val, - (SZ_4K * nd_region->ndr_mappings) / SZ_1K); + dev_dbg(dev, "%llu is not %ldK aligned\n", val, + (PAGE_SIZE * nd_region->ndr_mappings) / SZ_1K); return -EINVAL; } @@ -1329,7 +1303,7 @@ static ssize_t resource_show(struct device *dev, return -ENXIO; return sprintf(buf, "%#llx\n", (unsigned long long) res->start); } -static DEVICE_ATTR_RO(resource); +static DEVICE_ATTR(resource, 0400, resource_show, NULL); static const unsigned long blk_lbasize_supported[] = { 512, 520, 528, 4096, 4104, 4160, 4224, 0 }; @@ -1510,16 +1484,20 @@ static ssize_t holder_show(struct device *dev, } static DEVICE_ATTR_RO(holder); -static ssize_t __holder_class_store(struct device *dev, const char *buf) +static int __holder_class_store(struct device *dev, const char *buf) { struct nd_namespace_common *ndns = to_ndns(dev); if (dev->driver || ndns->claim) return -EBUSY; - if (sysfs_streq(buf, "btt")) - ndns->claim_class = btt_claim_class(dev); - else if (sysfs_streq(buf, "pfn")) + if (sysfs_streq(buf, "btt")) { + int rc = btt_claim_class(dev); + + if (rc < NVDIMM_CCLASS_NONE) + return rc; + ndns->claim_class = rc; + } else if (sysfs_streq(buf, "pfn")) ndns->claim_class = NVDIMM_CCLASS_PFN; else if (sysfs_streq(buf, "dax")) ndns->claim_class = NVDIMM_CCLASS_DAX; @@ -1528,10 +1506,6 @@ static ssize_t __holder_class_store(struct device *dev, const char *buf) else return -EINVAL; - /* btt_claim_class() could've returned an error */ - if (ndns->claim_class < 0) - return ndns->claim_class; - return 0; } @@ -1539,7 +1513,7 @@ static ssize_t holder_class_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct nd_region *nd_region = to_nd_region(dev->parent); - ssize_t rc; + int rc; nd_device_lock(dev); nvdimm_bus_lock(dev); @@ -1547,7 +1521,7 @@ static ssize_t holder_class_store(struct device *dev, rc = __holder_class_store(dev, buf); if (rc >= 0) rc = nd_namespace_label_update(nd_region, dev); - dev_dbg(dev, "%s(%zd)\n", rc < 0 ? "fail " : "", rc); + dev_dbg(dev, "%s(%d)\n", rc < 0 ? "fail " : "", rc); nvdimm_bus_unlock(dev); nd_device_unlock(dev); @@ -1645,11 +1619,8 @@ static umode_t namespace_visible(struct kobject *kobj, { struct device *dev = container_of(kobj, struct device, kobj); - if (a == &dev_attr_resource.attr) { - if (is_namespace_blk(dev)) - return 0; - return 0400; - } + if (a == &dev_attr_resource.attr && is_namespace_blk(dev)) + return 0; if (is_namespace_pmem(dev) || is_namespace_blk(dev)) { if (a == &dev_attr_size.attr) @@ -1680,6 +1651,39 @@ static const struct attribute_group *nd_namespace_attribute_groups[] = { NULL, }; +static const struct device_type namespace_io_device_type = { + .name = "nd_namespace_io", + .release = namespace_io_release, + .groups = nd_namespace_attribute_groups, +}; + +static const struct device_type namespace_pmem_device_type = { + .name = "nd_namespace_pmem", + .release = namespace_pmem_release, + .groups = nd_namespace_attribute_groups, +}; + +static const struct device_type namespace_blk_device_type = { + .name = "nd_namespace_blk", + .release = namespace_blk_release, + .groups = nd_namespace_attribute_groups, +}; + +static bool is_namespace_pmem(const struct device *dev) +{ + return dev ? dev->type == &namespace_pmem_device_type : false; +} + +static bool is_namespace_blk(const struct device *dev) +{ + return dev ? dev->type == &namespace_blk_device_type : false; +} + +static bool is_namespace_io(const struct device *dev) +{ + return dev ? dev->type == &namespace_io_device_type : false; +} + 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; @@ -1759,6 +1763,23 @@ struct nd_namespace_common *nvdimm_namespace_common_probe(struct device *dev) } EXPORT_SYMBOL(nvdimm_namespace_common_probe); +int devm_namespace_enable(struct device *dev, struct nd_namespace_common *ndns, + resource_size_t size) +{ + if (is_namespace_blk(&ndns->dev)) + return 0; + return devm_nsio_enable(dev, to_nd_namespace_io(&ndns->dev), size); +} +EXPORT_SYMBOL_GPL(devm_namespace_enable); + +void devm_namespace_disable(struct device *dev, struct nd_namespace_common *ndns) +{ + if (is_namespace_blk(&ndns->dev)) + return; + devm_nsio_disable(dev, to_nd_namespace_io(&ndns->dev)); +} +EXPORT_SYMBOL_GPL(devm_namespace_disable); + static struct device **create_namespace_io(struct nd_region *nd_region) { struct nd_namespace_io *nsio; @@ -1987,7 +2008,7 @@ static struct device *create_namespace_pmem(struct nd_region *nd_region, nd_mapping = &nd_region->mapping[i]; label_ent = list_first_entry_or_null(&nd_mapping->labels, typeof(*label_ent), list); - label0 = label_ent ? label_ent->label : 0; + label0 = label_ent ? label_ent->label : NULL; if (!label0) { WARN_ON(1); @@ -2078,7 +2099,6 @@ static struct device *nd_namespace_blk_create(struct nd_region *nd_region) } dev_set_name(dev, "namespace%d.%d", nd_region->id, nsblk->id); dev->parent = &nd_region->dev; - dev->groups = nd_namespace_attribute_groups; return &nsblk->common.dev; } @@ -2109,7 +2129,6 @@ static struct device *nd_namespace_pmem_create(struct nd_region *nd_region) return NULL; } dev_set_name(dev, "namespace%d.%d", nd_region->id, nspm->id); - dev->groups = nd_namespace_attribute_groups; nd_namespace_pmem_set_resource(nd_region, nspm, 0); return dev; @@ -2322,8 +2341,9 @@ static struct device **scan_labels(struct nd_region *nd_region) continue; /* skip labels that describe extents outside of the region */ - if (nd_label->dpa < nd_mapping->start || nd_label->dpa > map_end) - continue; + if (__le64_to_cpu(nd_label->dpa) < nd_mapping->start || + __le64_to_cpu(nd_label->dpa) > map_end) + continue; i = add_namespace_resource(nd_region, nd_label, devs, count); if (i < 0) @@ -2462,6 +2482,27 @@ static struct device **create_namespaces(struct nd_region *nd_region) return devs; } +static void deactivate_labels(void *region) +{ + struct nd_region *nd_region = region; + int i; + + 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; + + mutex_lock(&nd_mapping->lock); + nd_mapping_free_labels(nd_mapping); + mutex_unlock(&nd_mapping->lock); + + put_ndd(ndd); + nd_mapping->ndd = NULL; + if (ndd) + atomic_dec(&nvdimm->busy); + } +} + static int init_active_labels(struct nd_region *nd_region) { int i; @@ -2519,16 +2560,17 @@ static int init_active_labels(struct nd_region *nd_region) mutex_unlock(&nd_mapping->lock); } - if (j >= count) - continue; + if (j < count) + break; + } - mutex_lock(&nd_mapping->lock); - nd_mapping_free_labels(nd_mapping); - mutex_unlock(&nd_mapping->lock); + if (i < nd_region->ndr_mappings) { + deactivate_labels(nd_region); return -ENOMEM; } - return 0; + return devm_add_action_or_reset(&nd_region->dev, deactivate_labels, + nd_region); } int nd_region_register_namespaces(struct nd_region *nd_region, int *err) @@ -2585,7 +2627,6 @@ int nd_region_register_namespaces(struct nd_region *nd_region, int *err) if (id < 0) break; dev_set_name(dev, "namespace%d.%d", nd_region->id, id); - dev->groups = nd_namespace_attribute_groups; nd_device_register(dev); } if (i) diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index 0ac52b6eb00e..ddb9d97d9129 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h @@ -39,53 +39,40 @@ struct nvdimm { const char *dimm_id; struct { const struct nvdimm_security_ops *ops; - enum nvdimm_security_state state; - enum nvdimm_security_state ext_state; + unsigned long flags; + unsigned long ext_flags; unsigned int overwrite_tmo; struct kernfs_node *overwrite_state; } sec; struct delayed_work dwork; }; -static inline enum nvdimm_security_state nvdimm_security_state( +static inline unsigned long nvdimm_security_flags( struct nvdimm *nvdimm, enum nvdimm_passphrase_type ptype) { + u64 flags; + const u64 state_flags = 1UL << NVDIMM_SECURITY_DISABLED + | 1UL << NVDIMM_SECURITY_LOCKED + | 1UL << NVDIMM_SECURITY_UNLOCKED + | 1UL << NVDIMM_SECURITY_OVERWRITE; + if (!nvdimm->sec.ops) - return -ENXIO; + return 0; - return nvdimm->sec.ops->state(nvdimm, ptype); + flags = nvdimm->sec.ops->get_flags(nvdimm, ptype); + /* disabled, locked, unlocked, and overwrite are mutually exclusive */ + dev_WARN_ONCE(&nvdimm->dev, hweight64(flags & state_flags) > 1, + "reported invalid security state: %#llx\n", + (unsigned long long) flags); + return flags; } int nvdimm_security_freeze(struct nvdimm *nvdimm); #if IS_ENABLED(CONFIG_NVDIMM_KEYS) -int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid); -int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid, - unsigned int new_keyid, - enum nvdimm_passphrase_type pass_type); -int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid, - enum nvdimm_passphrase_type pass_type); -int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid); +ssize_t nvdimm_security_store(struct device *dev, const char *buf, size_t len); void nvdimm_security_overwrite_query(struct work_struct *work); #else -static inline int nvdimm_security_disable(struct nvdimm *nvdimm, - unsigned int keyid) -{ - return -EOPNOTSUPP; -} -static inline int nvdimm_security_update(struct nvdimm *nvdimm, - unsigned int keyid, - unsigned int new_keyid, - enum nvdimm_passphrase_type pass_type) -{ - return -EOPNOTSUPP; -} -static inline int nvdimm_security_erase(struct nvdimm *nvdimm, - unsigned int keyid, - enum nvdimm_passphrase_type pass_type) -{ - return -EOPNOTSUPP; -} -static inline int nvdimm_security_overwrite(struct nvdimm *nvdimm, - unsigned int keyid) +static inline ssize_t nvdimm_security_store(struct device *dev, + const char *buf, size_t len) { return -EOPNOTSUPP; } @@ -127,22 +114,16 @@ struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev); int __init nvdimm_bus_init(void); void nvdimm_bus_exit(void); void nvdimm_devs_exit(void); -void nd_region_devs_exit(void); -void nd_region_probe_success(struct nvdimm_bus *nvdimm_bus, struct device *dev); struct nd_region; +void nd_region_advance_seeds(struct nd_region *nd_region, struct device *dev); void nd_region_create_ns_seed(struct nd_region *nd_region); void nd_region_create_btt_seed(struct nd_region *nd_region); void nd_region_create_pfn_seed(struct nd_region *nd_region); void nd_region_create_dax_seed(struct nd_region *nd_region); -void nd_region_disable(struct nvdimm_bus *nvdimm_bus, struct device *dev); int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus); void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus); void nd_synchronize(void); -int nvdimm_bus_register_dimms(struct nvdimm_bus *nvdimm_bus); -int nvdimm_bus_register_regions(struct nvdimm_bus *nvdimm_bus); -int nvdimm_bus_init_interleave_sets(struct nvdimm_bus *nvdimm_bus); void __nd_device_register(struct device *dev); -int nd_match_dimm(struct device *dev, void *data); struct nd_label_id; char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags); bool nd_is_uuid_unique(struct device *dev, u8 *uuid); @@ -185,6 +166,23 @@ ssize_t nd_namespace_store(struct device *dev, struct nd_pfn *to_nd_pfn_safe(struct device *dev); bool is_nvdimm_bus(struct device *dev); +#if IS_ENABLED(CONFIG_ND_CLAIM) +int devm_nsio_enable(struct device *dev, struct nd_namespace_io *nsio, + resource_size_t size); +void devm_nsio_disable(struct device *dev, struct nd_namespace_io *nsio); +#else +static inline int devm_nsio_enable(struct device *dev, + struct nd_namespace_io *nsio, resource_size_t size) +{ + return -ENXIO; +} + +static inline void devm_nsio_disable(struct device *dev, + struct nd_namespace_io *nsio) +{ +} +#endif + #ifdef CONFIG_PROVE_LOCKING extern struct class *nd_class; diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 1b9955651379..c9f6a5b5253a 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -212,6 +212,11 @@ struct nd_dax { struct nd_pfn nd_pfn; }; +static inline u32 nd_info_block_reserve(void) +{ + return ALIGN(SZ_8K, PAGE_SIZE); +} + enum nd_async_mode { ND_SYNC, ND_ASYNC, @@ -234,6 +239,9 @@ int __init nd_label_init(void); void nvdimm_exit(void); void nd_region_exit(void); struct nvdimm; +extern const struct attribute_group nd_device_attribute_group; +extern const struct attribute_group nd_numa_attribute_group; +extern const struct attribute_group *nvdimm_bus_attribute_groups[]; struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping); int nvdimm_check_config_data(struct device *dev); int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd); @@ -289,11 +297,7 @@ static inline struct device *nd_btt_create(struct nd_region *nd_region) struct nd_pfn *to_nd_pfn(struct device *dev); #if IS_ENABLED(CONFIG_NVDIMM_PFN) -#ifdef CONFIG_TRANSPARENT_HUGEPAGE -#define PFN_DEFAULT_ALIGNMENT HPAGE_PMD_SIZE -#else -#define PFN_DEFAULT_ALIGNMENT PAGE_SIZE -#endif +#define MAX_NVDIMM_ALIGN 4 int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns); bool is_nd_pfn(struct device *dev); @@ -301,7 +305,7 @@ struct device *nd_pfn_create(struct nd_region *nd_region); struct device *nd_pfn_devinit(struct nd_pfn *nd_pfn, struct nd_namespace_common *ndns); int nd_pfn_validate(struct nd_pfn *nd_pfn, const char *sig); -extern struct attribute_group nd_pfn_attribute_group; +extern const struct attribute_group *nd_pfn_attribute_groups[]; #else static inline int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns) @@ -374,25 +378,20 @@ const char *nvdimm_namespace_disk_name(struct nd_namespace_common *ndns, unsigned int pmem_sector_size(struct nd_namespace_common *ndns); void nvdimm_badblocks_populate(struct nd_region *nd_region, struct badblocks *bb, const struct resource *res); +int devm_namespace_enable(struct device *dev, struct nd_namespace_common *ndns, + resource_size_t size); +void devm_namespace_disable(struct device *dev, + struct nd_namespace_common *ndns); #if IS_ENABLED(CONFIG_ND_CLAIM) +/* max struct page size independent of kernel config */ +#define MAX_STRUCT_PAGE_SIZE 64 int nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap); -int devm_nsio_enable(struct device *dev, struct nd_namespace_io *nsio); -void devm_nsio_disable(struct device *dev, struct nd_namespace_io *nsio); #else static inline int nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap) { return -ENXIO; } -static inline int devm_nsio_enable(struct device *dev, - struct nd_namespace_io *nsio) -{ - return -ENXIO; -} -static inline void devm_nsio_disable(struct device *dev, - struct nd_namespace_io *nsio) -{ -} #endif int nd_blk_region_init(struct nd_region *nd_region); int nd_region_activate(struct nd_region *nd_region); diff --git a/drivers/nvdimm/of_pmem.c b/drivers/nvdimm/of_pmem.c index a0c8dcfa0bf9..8224d1431ea9 100644 --- a/drivers/nvdimm/of_pmem.c +++ b/drivers/nvdimm/of_pmem.c @@ -9,17 +9,6 @@ #include <linux/ioport.h> #include <linux/slab.h> -static const struct attribute_group *region_attr_groups[] = { - &nd_region_attribute_group, - &nd_device_attribute_group, - NULL, -}; - -static const struct attribute_group *bus_attr_groups[] = { - &nvdimm_bus_attribute_group, - NULL, -}; - struct of_pmem_private { struct nvdimm_bus_descriptor bus_desc; struct nvdimm_bus *bus; @@ -41,8 +30,7 @@ static int of_pmem_region_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->bus_desc.attr_groups = bus_attr_groups; - priv->bus_desc.provider_name = "of_pmem"; + priv->bus_desc.provider_name = kstrdup(pdev->name, GFP_KERNEL); priv->bus_desc.module = THIS_MODULE; priv->bus_desc.of_node = np; @@ -66,7 +54,6 @@ static int of_pmem_region_probe(struct platform_device *pdev) * structures so passing a stack pointer is fine. */ memset(&ndr_desc, 0, sizeof(ndr_desc)); - ndr_desc.attr_groups = region_attr_groups; ndr_desc.numa_node = dev_to_node(&pdev->dev); ndr_desc.target_node = ndr_desc.numa_node; ndr_desc.res = &pdev->resource[i]; diff --git a/drivers/nvdimm/pfn.h b/drivers/nvdimm/pfn.h index 7381673b7b70..acb19517f678 100644 --- a/drivers/nvdimm/pfn.h +++ b/drivers/nvdimm/pfn.h @@ -29,7 +29,10 @@ struct nd_pfn_sb { /* minor-version-2 record the base alignment of the mapping */ __le32 align; /* minor-version-3 guarantee the padding and flags are zero */ - u8 padding[4000]; + /* minor-version-4 record the page size and struct page size */ + __le32 page_size; + __le16 page_struct_size; + u8 padding[3994]; __le64 checksum; }; diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index 3e7b11cf1aae..b94f7a7e94b8 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -26,17 +26,6 @@ static void nd_pfn_release(struct device *dev) kfree(nd_pfn); } -static struct device_type nd_pfn_device_type = { - .name = "nd_pfn", - .release = nd_pfn_release, -}; - -bool is_nd_pfn(struct device *dev) -{ - return dev ? dev->type == &nd_pfn_device_type : false; -} -EXPORT_SYMBOL(is_nd_pfn); - struct nd_pfn *to_nd_pfn(struct device *dev) { struct nd_pfn *nd_pfn = container_of(dev, struct nd_pfn, dev); @@ -103,39 +92,42 @@ static ssize_t align_show(struct device *dev, return sprintf(buf, "%ld\n", nd_pfn->align); } -static const unsigned long *nd_pfn_supported_alignments(void) +static unsigned long *nd_pfn_supported_alignments(unsigned long *alignments) { - /* - * This needs to be a non-static variable because the *_SIZE - * macros aren't always constants. - */ - const unsigned long supported_alignments[] = { - PAGE_SIZE, -#ifdef CONFIG_TRANSPARENT_HUGEPAGE - HPAGE_PMD_SIZE, -#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD - HPAGE_PUD_SIZE, -#endif -#endif - 0, - }; - static unsigned long data[ARRAY_SIZE(supported_alignments)]; - memcpy(data, supported_alignments, sizeof(data)); + alignments[0] = PAGE_SIZE; - return data; + if (has_transparent_hugepage()) { + alignments[1] = HPAGE_PMD_SIZE; + if (IS_ENABLED(CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD)) + alignments[2] = HPAGE_PUD_SIZE; + } + + return alignments; +} + +/* + * Use pmd mapping if supported as default alignment + */ +static unsigned long nd_pfn_default_alignment(void) +{ + + if (has_transparent_hugepage()) + return HPAGE_PMD_SIZE; + return PAGE_SIZE; } static ssize_t align_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev); + unsigned long aligns[MAX_NVDIMM_ALIGN] = { [0] = 0, }; ssize_t rc; nd_device_lock(dev); nvdimm_bus_lock(dev); rc = nd_size_select_store(dev, buf, &nd_pfn->align, - nd_pfn_supported_alignments()); + nd_pfn_supported_alignments(aligns)); dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf, buf[len - 1] == '\n' ? "" : "\n"); nvdimm_bus_unlock(dev); @@ -226,7 +218,7 @@ static ssize_t resource_show(struct device *dev, return rc; } -static DEVICE_ATTR_RO(resource); +static DEVICE_ATTR(resource, 0400, resource_show, NULL); static ssize_t size_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -259,7 +251,10 @@ static DEVICE_ATTR_RO(size); static ssize_t supported_alignments_show(struct device *dev, struct device_attribute *attr, char *buf) { - return nd_size_select_show(0, nd_pfn_supported_alignments(), buf); + unsigned long aligns[MAX_NVDIMM_ALIGN] = { [0] = 0, }; + + return nd_size_select_show(0, + nd_pfn_supported_alignments(aligns), buf); } static DEVICE_ATTR_RO(supported_alignments); @@ -274,25 +269,29 @@ static struct attribute *nd_pfn_attributes[] = { NULL, }; -static umode_t pfn_visible(struct kobject *kobj, struct attribute *a, int n) -{ - if (a == &dev_attr_resource.attr) - return 0400; - return a->mode; -} - -struct attribute_group nd_pfn_attribute_group = { +static struct attribute_group nd_pfn_attribute_group = { .attrs = nd_pfn_attributes, - .is_visible = pfn_visible, }; -static const struct attribute_group *nd_pfn_attribute_groups[] = { +const struct attribute_group *nd_pfn_attribute_groups[] = { &nd_pfn_attribute_group, &nd_device_attribute_group, &nd_numa_attribute_group, NULL, }; +static const struct device_type nd_pfn_device_type = { + .name = "nd_pfn", + .release = nd_pfn_release, + .groups = nd_pfn_attribute_groups, +}; + +bool is_nd_pfn(struct device *dev) +{ + return dev ? dev->type == &nd_pfn_device_type : false; +} +EXPORT_SYMBOL(is_nd_pfn); + struct device *nd_pfn_devinit(struct nd_pfn *nd_pfn, struct nd_namespace_common *ndns) { @@ -302,7 +301,7 @@ struct device *nd_pfn_devinit(struct nd_pfn *nd_pfn, return NULL; nd_pfn->mode = PFN_MODE_NONE; - nd_pfn->align = PFN_DEFAULT_ALIGNMENT; + nd_pfn->align = nd_pfn_default_alignment(); dev = &nd_pfn->dev; device_initialize(&nd_pfn->dev); if (ndns && !__nd_attach_ndns(&nd_pfn->dev, ndns, &nd_pfn->ndns)) { @@ -331,7 +330,6 @@ static struct nd_pfn *nd_pfn_alloc(struct nd_region *nd_region) dev = &nd_pfn->dev; dev_set_name(dev, "pfn%d.%d", nd_region->id, nd_pfn->id); - dev->groups = nd_pfn_attribute_groups; dev->type = &nd_pfn_device_type; dev->parent = &nd_region->dev; @@ -376,6 +374,15 @@ static int nd_pfn_clear_memmap_errors(struct nd_pfn *nd_pfn) meta_start = (SZ_4K + sizeof(*pfn_sb)) >> 9; meta_num = (le64_to_cpu(pfn_sb->dataoff) >> 9) - meta_start; + /* + * re-enable the namespace with correct size so that we can access + * the device memmap area. + */ + devm_namespace_disable(&nd_pfn->dev, ndns); + rc = devm_namespace_enable(&nd_pfn->dev, ndns, le64_to_cpu(pfn_sb->dataoff)); + if (rc) + return rc; + do { unsigned long zero_len; u64 nsoff; @@ -412,6 +419,21 @@ static int nd_pfn_clear_memmap_errors(struct nd_pfn *nd_pfn) return 0; } +static bool nd_supported_alignment(unsigned long align) +{ + int i; + unsigned long supported[MAX_NVDIMM_ALIGN] = { [0] = 0, }; + + if (align == 0) + return false; + + nd_pfn_supported_alignments(supported); + for (i = 0; supported[i]; i++) + if (align == supported[i]) + return true; + return false; +} + /** * nd_pfn_validate - read and validate info-block * @nd_pfn: fsdax namespace runtime state / properties @@ -460,6 +482,11 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn, const char *sig) if (__le16_to_cpu(pfn_sb->version_minor) < 2) pfn_sb->align = 0; + if (__le16_to_cpu(pfn_sb->version_minor) < 4) { + pfn_sb->page_struct_size = cpu_to_le16(64); + pfn_sb->page_size = cpu_to_le32(PAGE_SIZE); + } + switch (le32_to_cpu(pfn_sb->mode)) { case PFN_MODE_RAM: case PFN_MODE_PMEM: @@ -475,6 +502,34 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn, const char *sig) align = 1UL << ilog2(offset); mode = le32_to_cpu(pfn_sb->mode); + if ((le32_to_cpu(pfn_sb->page_size) > PAGE_SIZE) && + (mode == PFN_MODE_PMEM)) { + dev_err(&nd_pfn->dev, + "init failed, page size mismatch %d\n", + le32_to_cpu(pfn_sb->page_size)); + return -EOPNOTSUPP; + } + + if ((le16_to_cpu(pfn_sb->page_struct_size) < sizeof(struct page)) && + (mode == PFN_MODE_PMEM)) { + dev_err(&nd_pfn->dev, + "init failed, struct page size mismatch %d\n", + le16_to_cpu(pfn_sb->page_struct_size)); + return -EOPNOTSUPP; + } + + /* + * Check whether the we support the alignment. For Dax if the + * superblock alignment is not matching, we won't initialize + * the device. + */ + if (!nd_supported_alignment(align) && + !memcmp(pfn_sb->signature, DAX_SIG, PFN_SIG_LEN)) { + dev_err(&nd_pfn->dev, "init failed, alignment mismatch: " + "%ld:%ld\n", nd_pfn->align, align); + return -EOPNOTSUPP; + } + if (!nd_pfn->uuid) { /* * When probing a namepace via nd_pfn_probe() the uuid @@ -537,7 +592,7 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn, const char *sig) return -ENXIO; } - return nd_pfn_clear_memmap_errors(nd_pfn); + return 0; } EXPORT_SYMBOL(nd_pfn_validate); @@ -581,11 +636,6 @@ int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns) } EXPORT_SYMBOL(nd_pfn_probe); -static u32 info_block_reserve(void) -{ - return ALIGN(SZ_8K, PAGE_SIZE); -} - /* * We hotplug memory at sub-section granularity, pad the reserved area * from the previous section base to the namespace base address. @@ -599,7 +649,7 @@ static unsigned long init_altmap_base(resource_size_t base) static unsigned long init_altmap_reserve(resource_size_t base) { - unsigned long reserve = info_block_reserve() >> PAGE_SHIFT; + unsigned long reserve = nd_info_block_reserve() >> PAGE_SHIFT; unsigned long base_pfn = PHYS_PFN(base); reserve += base_pfn - SUBSECTION_ALIGN_DOWN(base_pfn); @@ -614,13 +664,15 @@ static int __nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap) u64 offset = le64_to_cpu(pfn_sb->dataoff); u32 start_pad = __le32_to_cpu(pfn_sb->start_pad); u32 end_trunc = __le32_to_cpu(pfn_sb->end_trunc); - u32 reserve = info_block_reserve(); + u32 reserve = nd_info_block_reserve(); struct nd_namespace_common *ndns = nd_pfn->ndns; struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); resource_size_t base = nsio->res.start + start_pad; + resource_size_t end = nsio->res.end - end_trunc; struct vmem_altmap __altmap = { .base_pfn = init_altmap_base(base), .reserve = init_altmap_reserve(base), + .end_pfn = PHYS_PFN(end), }; memcpy(res, &nsio->res, sizeof(*res)); @@ -655,6 +707,7 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) resource_size_t start, size; struct nd_region *nd_region; unsigned long npfns, align; + u32 end_trunc; struct nd_pfn_sb *pfn_sb; phys_addr_t offset; const char *sig; @@ -672,6 +725,8 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) sig = PFN_SIG; rc = nd_pfn_validate(nd_pfn, sig); + if (rc == 0) + return nd_pfn_clear_memmap_errors(nd_pfn); if (rc != -ENODEV) return rc; @@ -696,13 +751,22 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) size = resource_size(&nsio->res); npfns = PHYS_PFN(size - SZ_8K); align = max(nd_pfn->align, (1UL << SUBSECTION_SHIFT)); + end_trunc = start + size - ALIGN_DOWN(start + size, align); if (nd_pfn->mode == PFN_MODE_PMEM) { /* * The altmap should be padded out to the block size used * when populating the vmemmap. This *should* be equal to * PMD_SIZE for most architectures. + * + * Also make sure size of struct page is less than 64. We + * want to make sure we use large enough size here so that + * we don't have a dynamic reserve space depending on + * struct page size. But we also want to make sure we notice + * when we end up adding new elements to struct page. */ - offset = ALIGN(start + SZ_8K + 64 * npfns, align) - start; + BUILD_BUG_ON(sizeof(struct page) > MAX_STRUCT_PAGE_SIZE); + offset = ALIGN(start + SZ_8K + MAX_STRUCT_PAGE_SIZE * npfns, align) + - start; } else if (nd_pfn->mode == PFN_MODE_RAM) offset = ALIGN(start + SZ_8K, align) - start; else @@ -714,7 +778,7 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) return -ENXIO; } - npfns = PHYS_PFN(size - offset); + npfns = PHYS_PFN(size - offset - end_trunc); pfn_sb->mode = cpu_to_le32(nd_pfn->mode); pfn_sb->dataoff = cpu_to_le64(offset); pfn_sb->npfns = cpu_to_le64(npfns); @@ -722,11 +786,18 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) memcpy(pfn_sb->uuid, nd_pfn->uuid, 16); memcpy(pfn_sb->parent_uuid, nd_dev_to_uuid(&ndns->dev), 16); pfn_sb->version_major = cpu_to_le16(1); - pfn_sb->version_minor = cpu_to_le16(3); + pfn_sb->version_minor = cpu_to_le16(4); + pfn_sb->end_trunc = cpu_to_le32(end_trunc); pfn_sb->align = cpu_to_le32(nd_pfn->align); + pfn_sb->page_struct_size = cpu_to_le16(MAX_STRUCT_PAGE_SIZE); + pfn_sb->page_size = cpu_to_le32(PAGE_SIZE); checksum = nd_sb_checksum((struct nd_gen_sb *) pfn_sb); pfn_sb->checksum = cpu_to_le64(checksum); + rc = nd_pfn_clear_memmap_errors(nd_pfn); + if (rc) + return rc; + return nvdimm_write_bytes(ndns, SZ_4K, pfn_sb, sizeof(*pfn_sb), 0); } diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index 4c121dd03dd9..4eae441f86c9 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c @@ -28,7 +28,6 @@ #include "pmem.h" #include "pfn.h" #include "nd.h" -#include "nd-core.h" static struct device *to_dev(struct pmem_device *pmem) { @@ -338,13 +337,7 @@ static void pmem_release_disk(void *__pmem) put_disk(pmem->disk); } -static void pmem_pagemap_page_free(struct page *page) -{ - wake_up_var(&page->_refcount); -} - static const struct dev_pagemap_ops fsdax_pagemap_ops = { - .page_free = pmem_pagemap_page_free, .kill = pmem_pagemap_kill, .cleanup = pmem_pagemap_cleanup, }; @@ -372,6 +365,10 @@ static int pmem_attach_disk(struct device *dev, if (!pmem) return -ENOMEM; + rc = devm_namespace_enable(dev, ndns, nd_info_block_reserve()); + if (rc) + return rc; + /* while nsio_rw_bytes is active, parse a pfn info block if present */ if (is_nd_pfn(dev)) { nd_pfn = to_nd_pfn(dev); @@ -381,7 +378,7 @@ static int pmem_attach_disk(struct device *dev, } /* we're attaching a block device, disable raw namespace access */ - devm_nsio_disable(dev, nsio); + devm_namespace_disable(dev, ndns); dev_set_drvdata(dev, pmem); pmem->phys_addr = res->start; @@ -490,27 +487,53 @@ static int pmem_attach_disk(struct device *dev, static int nd_pmem_probe(struct device *dev) { + int ret; struct nd_namespace_common *ndns; ndns = nvdimm_namespace_common_probe(dev); if (IS_ERR(ndns)) return PTR_ERR(ndns); - if (devm_nsio_enable(dev, to_nd_namespace_io(&ndns->dev))) - return -ENXIO; - if (is_nd_btt(dev)) return nvdimm_namespace_attach_btt(ndns); if (is_nd_pfn(dev)) return pmem_attach_disk(dev, ndns); - /* if we find a valid info-block we'll come back as that personality */ - if (nd_btt_probe(dev, ndns) == 0 || nd_pfn_probe(dev, ndns) == 0 - || nd_dax_probe(dev, ndns) == 0) + ret = devm_namespace_enable(dev, ndns, nd_info_block_reserve()); + if (ret) + return ret; + + ret = nd_btt_probe(dev, ndns); + if (ret == 0) return -ENXIO; - /* ...otherwise we're just a raw pmem device */ + /* + * We have two failure conditions here, there is no + * info reserver block or we found a valid info reserve block + * but failed to initialize the pfn superblock. + * + * For the first case consider namespace as a raw pmem namespace + * and attach a disk. + * + * For the latter, consider this a success and advance the namespace + * seed. + */ + ret = nd_pfn_probe(dev, ndns); + if (ret == 0) + return -ENXIO; + else if (ret == -EOPNOTSUPP) + return ret; + + ret = nd_dax_probe(dev, ndns); + if (ret == 0) + return -ENXIO; + else if (ret == -EOPNOTSUPP) + return ret; + + /* probe complete, attach handles namespace enabling */ + devm_namespace_disable(dev, ndns); + return pmem_attach_disk(dev, ndns); } diff --git a/drivers/nvdimm/region.c b/drivers/nvdimm/region.c index 37bf8719a2a4..0f6978e72e7c 100644 --- a/drivers/nvdimm/region.c +++ b/drivers/nvdimm/region.c @@ -34,7 +34,7 @@ static int nd_region_probe(struct device *dev) if (rc) return rc; - if (is_nd_pmem(&nd_region->dev)) { + if (is_memory(&nd_region->dev)) { struct resource ndr_res; if (devm_init_badblocks(dev, &nd_region->bb)) @@ -123,7 +123,7 @@ static void nd_region_notify(struct device *dev, enum nvdimm_event event) struct nd_region *nd_region = to_nd_region(dev); struct resource res; - if (is_nd_pmem(&nd_region->dev)) { + if (is_memory(&nd_region->dev)) { res.start = nd_region->ndr_start; res.end = nd_region->ndr_start + nd_region->ndr_size - 1; diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index af30cbe7a8ea..a19e535830d9 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -3,6 +3,7 @@ * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. */ #include <linux/scatterlist.h> +#include <linux/memregion.h> #include <linux/highmem.h> #include <linux/sched.h> #include <linux/slab.h> @@ -19,7 +20,6 @@ */ #include <linux/io-64-nonatomic-hi-lo.h> -static DEFINE_IDA(region_ida); static DEFINE_PER_CPU(int, flush_idx); static int nvdimm_map_flush(struct device *dev, struct nvdimm *nvdimm, int dimm, @@ -133,43 +133,13 @@ static void nd_region_release(struct device *dev) put_device(&nvdimm->dev); } free_percpu(nd_region->lane); - ida_simple_remove(®ion_ida, nd_region->id); + memregion_free(nd_region->id); if (is_nd_blk(dev)) kfree(to_nd_blk_region(dev)); else kfree(nd_region); } -static struct device_type nd_blk_device_type = { - .name = "nd_blk", - .release = nd_region_release, -}; - -static struct device_type nd_pmem_device_type = { - .name = "nd_pmem", - .release = nd_region_release, -}; - -static struct device_type nd_volatile_device_type = { - .name = "nd_volatile", - .release = nd_region_release, -}; - -bool is_nd_pmem(struct device *dev) -{ - return dev ? dev->type == &nd_pmem_device_type : false; -} - -bool is_nd_blk(struct device *dev) -{ - return dev ? dev->type == &nd_blk_device_type : false; -} - -bool is_nd_volatile(struct device *dev) -{ - return dev ? dev->type == &nd_volatile_device_type : false; -} - struct nd_region *to_nd_region(struct device *dev) { struct nd_region *nd_region = container_of(dev, struct nd_region, dev); @@ -583,7 +553,7 @@ static ssize_t resource_show(struct device *dev, return sprintf(buf, "%#llx\n", nd_region->ndr_start); } -static DEVICE_ATTR_RO(resource); +static DEVICE_ATTR(resource, 0400, resource_show, NULL); static ssize_t persistence_domain_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -632,15 +602,11 @@ static umode_t region_visible(struct kobject *kobj, struct attribute *a, int n) if (!is_memory(dev) && a == &dev_attr_dax_seed.attr) return 0; - if (!is_nd_pmem(dev) && a == &dev_attr_badblocks.attr) + if (!is_memory(dev) && a == &dev_attr_badblocks.attr) return 0; - if (a == &dev_attr_resource.attr) { - if (is_nd_pmem(dev)) - return 0400; - else - return 0; - } + if (a == &dev_attr_resource.attr && !is_memory(dev)) + return 0; if (a == &dev_attr_deep_flush.attr) { int has_flush = nvdimm_has_flush(nd_region); @@ -674,128 +640,6 @@ static umode_t region_visible(struct kobject *kobj, struct attribute *a, int n) return 0; } -struct attribute_group nd_region_attribute_group = { - .attrs = nd_region_attributes, - .is_visible = region_visible, -}; -EXPORT_SYMBOL_GPL(nd_region_attribute_group); - -u64 nd_region_interleave_set_cookie(struct nd_region *nd_region, - struct nd_namespace_index *nsindex) -{ - struct nd_interleave_set *nd_set = nd_region->nd_set; - - if (!nd_set) - return 0; - - if (nsindex && __le16_to_cpu(nsindex->major) == 1 - && __le16_to_cpu(nsindex->minor) == 1) - return nd_set->cookie1; - return nd_set->cookie2; -} - -u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region) -{ - struct nd_interleave_set *nd_set = nd_region->nd_set; - - if (nd_set) - return nd_set->altcookie; - return 0; -} - -void nd_mapping_free_labels(struct nd_mapping *nd_mapping) -{ - struct nd_label_ent *label_ent, *e; - - lockdep_assert_held(&nd_mapping->lock); - list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { - list_del(&label_ent->list); - kfree(label_ent); - } -} - -/* - * 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_region(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; - - mutex_lock(&nd_mapping->lock); - nd_mapping_free_labels(nd_mapping); - mutex_unlock(&nd_mapping->lock); - - put_ndd(ndd); - nd_mapping->ndd = NULL; - if (ndd) - atomic_dec(&nvdimm->busy); - } - } - if (dev->parent && is_nd_region(dev->parent) && probe) { - nd_region = to_nd_region(dev->parent); - nvdimm_bus_lock(dev); - if (nd_region->ns_seed == dev) - nd_region_create_ns_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) - nd_region_create_ns_seed(nd_region); - nvdimm_bus_unlock(dev); - } - if (is_nd_pfn(dev) && probe) { - struct nd_pfn *nd_pfn = to_nd_pfn(dev); - - nd_region = to_nd_region(dev->parent); - nvdimm_bus_lock(dev); - if (nd_region->pfn_seed == dev) - nd_region_create_pfn_seed(nd_region); - if (nd_region->ns_seed == &nd_pfn->ndns->dev) - nd_region_create_ns_seed(nd_region); - nvdimm_bus_unlock(dev); - } - if (is_nd_dax(dev) && probe) { - struct nd_dax *nd_dax = to_nd_dax(dev); - - nd_region = to_nd_region(dev->parent); - nvdimm_bus_lock(dev); - if (nd_region->dax_seed == dev) - nd_region_create_dax_seed(nd_region); - if (nd_region->ns_seed == &nd_dax->nd_pfn.ndns->dev) - nd_region_create_ns_seed(nd_region); - nvdimm_bus_unlock(dev); - } -} - -void nd_region_probe_success(struct nvdimm_bus *nvdimm_bus, struct device *dev) -{ - nd_region_notify_driver_action(nvdimm_bus, dev, true); -} - -void nd_region_disable(struct nvdimm_bus *nvdimm_bus, struct device *dev) -{ - nd_region_notify_driver_action(nvdimm_bus, dev, false); -} - static ssize_t mappingN(struct device *dev, char *buf, int n) { struct nd_region *nd_region = to_nd_region(dev); @@ -903,11 +747,124 @@ static struct attribute *mapping_attributes[] = { NULL, }; -struct attribute_group nd_mapping_attribute_group = { +static const struct attribute_group nd_mapping_attribute_group = { .is_visible = mapping_visible, .attrs = mapping_attributes, }; -EXPORT_SYMBOL_GPL(nd_mapping_attribute_group); + +static const struct attribute_group nd_region_attribute_group = { + .attrs = nd_region_attributes, + .is_visible = region_visible, +}; + +static const struct attribute_group *nd_region_attribute_groups[] = { + &nd_device_attribute_group, + &nd_region_attribute_group, + &nd_numa_attribute_group, + &nd_mapping_attribute_group, + NULL, +}; + +static const struct device_type nd_blk_device_type = { + .name = "nd_blk", + .release = nd_region_release, + .groups = nd_region_attribute_groups, +}; + +static const struct device_type nd_pmem_device_type = { + .name = "nd_pmem", + .release = nd_region_release, + .groups = nd_region_attribute_groups, +}; + +static const struct device_type nd_volatile_device_type = { + .name = "nd_volatile", + .release = nd_region_release, + .groups = nd_region_attribute_groups, +}; + +bool is_nd_pmem(struct device *dev) +{ + return dev ? dev->type == &nd_pmem_device_type : false; +} + +bool is_nd_blk(struct device *dev) +{ + return dev ? dev->type == &nd_blk_device_type : false; +} + +bool is_nd_volatile(struct device *dev) +{ + return dev ? dev->type == &nd_volatile_device_type : false; +} + +u64 nd_region_interleave_set_cookie(struct nd_region *nd_region, + struct nd_namespace_index *nsindex) +{ + struct nd_interleave_set *nd_set = nd_region->nd_set; + + if (!nd_set) + return 0; + + if (nsindex && __le16_to_cpu(nsindex->major) == 1 + && __le16_to_cpu(nsindex->minor) == 1) + return nd_set->cookie1; + return nd_set->cookie2; +} + +u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region) +{ + struct nd_interleave_set *nd_set = nd_region->nd_set; + + if (nd_set) + return nd_set->altcookie; + return 0; +} + +void nd_mapping_free_labels(struct nd_mapping *nd_mapping) +{ + struct nd_label_ent *label_ent, *e; + + lockdep_assert_held(&nd_mapping->lock); + list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { + list_del(&label_ent->list); + kfree(label_ent); + } +} + +/* + * When a namespace is activated create new seeds for the next + * namespace, or namespace-personality to be configured. + */ +void nd_region_advance_seeds(struct nd_region *nd_region, struct device *dev) +{ + nvdimm_bus_lock(dev); + if (nd_region->ns_seed == dev) { + nd_region_create_ns_seed(nd_region); + } else if (is_nd_btt(dev)) { + struct nd_btt *nd_btt = to_nd_btt(dev); + + if (nd_region->btt_seed == dev) + nd_region_create_btt_seed(nd_region); + if (nd_region->ns_seed == &nd_btt->ndns->dev) + nd_region_create_ns_seed(nd_region); + } else if (is_nd_pfn(dev)) { + struct nd_pfn *nd_pfn = to_nd_pfn(dev); + + if (nd_region->pfn_seed == dev) + nd_region_create_pfn_seed(nd_region); + if (nd_region->ns_seed == &nd_pfn->ndns->dev) + nd_region_create_ns_seed(nd_region); + } else if (is_nd_dax(dev)) { + struct nd_dax *nd_dax = to_nd_dax(dev); + + if (nd_region->dax_seed == dev) + nd_region_create_dax_seed(nd_region); + if (nd_region->ns_seed == &nd_dax->nd_pfn.ndns->dev) + nd_region_create_ns_seed(nd_region); + } + nvdimm_bus_unlock(dev); +} int nd_blk_region_init(struct nd_region *nd_region) { @@ -979,8 +936,8 @@ void nd_region_release_lane(struct nd_region *nd_region, unsigned int lane) EXPORT_SYMBOL(nd_region_release_lane); static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, - struct nd_region_desc *ndr_desc, struct device_type *dev_type, - const char *caller) + struct nd_region_desc *ndr_desc, + const struct device_type *dev_type, const char *caller) { struct nd_region *nd_region; struct device *dev; @@ -992,10 +949,10 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, struct nd_mapping_desc *mapping = &ndr_desc->mapping[i]; struct nvdimm *nvdimm = mapping->nvdimm; - if ((mapping->start | mapping->size) % SZ_4K) { - dev_err(&nvdimm_bus->dev, "%s: %s mapping%d is not 4K aligned\n", - caller, dev_name(&nvdimm->dev), i); - + if ((mapping->start | mapping->size) % PAGE_SIZE) { + dev_err(&nvdimm_bus->dev, + "%s: %s mapping%d is not %ld aligned\n", + caller, dev_name(&nvdimm->dev), i, PAGE_SIZE); return NULL; } @@ -1025,16 +982,15 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, } region_buf = ndbr; } else { - nd_region = kzalloc(sizeof(struct nd_region) - + sizeof(struct nd_mapping) - * ndr_desc->num_mappings, - GFP_KERNEL); + nd_region = kzalloc(struct_size(nd_region, mapping, + ndr_desc->num_mappings), + GFP_KERNEL); region_buf = nd_region; } if (!region_buf) return NULL; - nd_region->id = ida_simple_get(®ion_ida, 0, 0, GFP_KERNEL); + nd_region->id = memregion_alloc(GFP_KERNEL); if (nd_region->id < 0) goto err_id; @@ -1093,7 +1049,7 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, return nd_region; err_percpu: - ida_simple_remove(®ion_ida, nd_region->id); + memregion_free(nd_region->id); err_id: kfree(region_buf); return NULL; @@ -1217,6 +1173,9 @@ EXPORT_SYMBOL_GPL(nvdimm_has_cache); bool is_nvdimm_sync(struct nd_region *nd_region) { + if (is_nd_volatile(&nd_region->dev)) + return true; + return is_nd_pmem(&nd_region->dev) && !test_bit(ND_REGION_ASYNC, &nd_region->flags); } @@ -1262,8 +1221,3 @@ int nd_region_conflict(struct nd_region *nd_region, resource_size_t start, return device_for_each_child(&nvdimm_bus->dev, &ctx, region_conflict); } - -void __exit nd_region_devs_exit(void) -{ - ida_destroy(®ion_ida); -} diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c index a570f2263a42..89b85970912d 100644 --- a/drivers/nvdimm/security.c +++ b/drivers/nvdimm/security.c @@ -158,7 +158,7 @@ static int nvdimm_key_revalidate(struct nvdimm *nvdimm) } nvdimm_put_key(key); - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER); + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); return 0; } @@ -174,9 +174,13 @@ static int __nvdimm_security_unlock(struct nvdimm *nvdimm) lockdep_assert_held(&nvdimm_bus->reconfig_mutex); if (!nvdimm->sec.ops || !nvdimm->sec.ops->unlock - || nvdimm->sec.state < 0) + || !nvdimm->sec.flags) return -EIO; + /* No need to go further if security is disabled */ + if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags)) + return 0; + if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { dev_dbg(dev, "Security operation in progress.\n"); return -EBUSY; @@ -189,7 +193,7 @@ static int __nvdimm_security_unlock(struct nvdimm *nvdimm) * freeze of the security configuration. I.e. if the OS does not * have the key, security is being managed pre-OS. */ - if (nvdimm->sec.state == NVDIMM_SECURITY_UNLOCKED) { + if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags)) { if (!key_revalidate) return 0; @@ -202,7 +206,7 @@ static int __nvdimm_security_unlock(struct nvdimm *nvdimm) rc == 0 ? "success" : "fail"); nvdimm_put_key(key); - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER); + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); return rc; } @@ -217,7 +221,25 @@ int nvdimm_security_unlock(struct device *dev) return rc; } -int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid) +static int check_security_state(struct nvdimm *nvdimm) +{ + struct device *dev = &nvdimm->dev; + + if (test_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags)) { + dev_dbg(dev, "Incorrect security state: %#lx\n", + nvdimm->sec.flags); + return -EIO; + } + + if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { + dev_dbg(dev, "Security operation in progress.\n"); + return -EBUSY; + } + + return 0; +} + +static int security_disable(struct nvdimm *nvdimm, unsigned int keyid) { struct device *dev = &nvdimm->dev; struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); @@ -229,19 +251,12 @@ int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid) lockdep_assert_held(&nvdimm_bus->reconfig_mutex); if (!nvdimm->sec.ops || !nvdimm->sec.ops->disable - || nvdimm->sec.state < 0) + || !nvdimm->sec.flags) return -EOPNOTSUPP; - if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) { - dev_dbg(dev, "Incorrect security state: %d\n", - nvdimm->sec.state); - return -EIO; - } - - if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { - dev_dbg(dev, "Security operation in progress.\n"); - return -EBUSY; - } + rc = check_security_state(nvdimm); + if (rc) + return rc; data = nvdimm_get_user_key_payload(nvdimm, keyid, NVDIMM_BASE_KEY, &key); @@ -253,11 +268,11 @@ int nvdimm_security_disable(struct nvdimm *nvdimm, unsigned int keyid) rc == 0 ? "success" : "fail"); nvdimm_put_key(key); - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER); + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); return rc; } -int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid, +static int security_update(struct nvdimm *nvdimm, unsigned int keyid, unsigned int new_keyid, enum nvdimm_passphrase_type pass_type) { @@ -271,14 +286,12 @@ int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid, lockdep_assert_held(&nvdimm_bus->reconfig_mutex); if (!nvdimm->sec.ops || !nvdimm->sec.ops->change_key - || nvdimm->sec.state < 0) + || !nvdimm->sec.flags) return -EOPNOTSUPP; - if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) { - dev_dbg(dev, "Incorrect security state: %d\n", - nvdimm->sec.state); - return -EIO; - } + rc = check_security_state(nvdimm); + if (rc) + return rc; data = nvdimm_get_user_key_payload(nvdimm, keyid, NVDIMM_BASE_KEY, &key); @@ -301,15 +314,15 @@ int nvdimm_security_update(struct nvdimm *nvdimm, unsigned int keyid, nvdimm_put_key(newkey); nvdimm_put_key(key); if (pass_type == NVDIMM_MASTER) - nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, + nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER); else - nvdimm->sec.state = nvdimm_security_state(nvdimm, + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); return rc; } -int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid, +static int security_erase(struct nvdimm *nvdimm, unsigned int keyid, enum nvdimm_passphrase_type pass_type) { struct device *dev = &nvdimm->dev; @@ -322,26 +335,14 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid, lockdep_assert_held(&nvdimm_bus->reconfig_mutex); if (!nvdimm->sec.ops || !nvdimm->sec.ops->erase - || nvdimm->sec.state < 0) + || !nvdimm->sec.flags) return -EOPNOTSUPP; - if (atomic_read(&nvdimm->busy)) { - dev_dbg(dev, "Unable to secure erase while DIMM active.\n"); - return -EBUSY; - } - - if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) { - dev_dbg(dev, "Incorrect security state: %d\n", - nvdimm->sec.state); - return -EIO; - } - - if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { - dev_dbg(dev, "Security operation in progress.\n"); - return -EBUSY; - } + rc = check_security_state(nvdimm); + if (rc) + return rc; - if (nvdimm->sec.ext_state != NVDIMM_SECURITY_UNLOCKED + if (!test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.ext_flags) && pass_type == NVDIMM_MASTER) { dev_dbg(dev, "Attempt to secure erase in wrong master state.\n"); @@ -359,11 +360,11 @@ int nvdimm_security_erase(struct nvdimm *nvdimm, unsigned int keyid, rc == 0 ? "success" : "fail"); nvdimm_put_key(key); - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER); + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); return rc; } -int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid) +static int security_overwrite(struct nvdimm *nvdimm, unsigned int keyid) { struct device *dev = &nvdimm->dev; struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); @@ -375,29 +376,17 @@ int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid) lockdep_assert_held(&nvdimm_bus->reconfig_mutex); if (!nvdimm->sec.ops || !nvdimm->sec.ops->overwrite - || nvdimm->sec.state < 0) + || !nvdimm->sec.flags) return -EOPNOTSUPP; - if (atomic_read(&nvdimm->busy)) { - dev_dbg(dev, "Unable to overwrite while DIMM active.\n"); - return -EBUSY; - } - if (dev->driver == NULL) { dev_dbg(dev, "Unable to overwrite while DIMM active.\n"); return -EINVAL; } - if (nvdimm->sec.state >= NVDIMM_SECURITY_FROZEN) { - dev_dbg(dev, "Incorrect security state: %d\n", - nvdimm->sec.state); - return -EIO; - } - - if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { - dev_dbg(dev, "Security operation in progress.\n"); - return -EBUSY; - } + rc = check_security_state(nvdimm); + if (rc) + return rc; data = nvdimm_get_user_key_payload(nvdimm, keyid, NVDIMM_BASE_KEY, &key); @@ -412,7 +401,7 @@ int nvdimm_security_overwrite(struct nvdimm *nvdimm, unsigned int keyid) if (rc == 0) { set_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags); set_bit(NDD_WORK_PENDING, &nvdimm->flags); - nvdimm->sec.state = NVDIMM_SECURITY_OVERWRITE; + set_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags); /* * Make sure we don't lose device while doing overwrite * query. @@ -443,7 +432,7 @@ void __nvdimm_security_overwrite_query(struct nvdimm *nvdimm) tmo = nvdimm->sec.overwrite_tmo; if (!nvdimm->sec.ops || !nvdimm->sec.ops->query_overwrite - || nvdimm->sec.state < 0) + || !nvdimm->sec.flags) return; rc = nvdimm->sec.ops->query_overwrite(nvdimm); @@ -467,8 +456,8 @@ void __nvdimm_security_overwrite_query(struct nvdimm *nvdimm) clear_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags); clear_bit(NDD_WORK_PENDING, &nvdimm->flags); put_device(&nvdimm->dev); - nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER); - nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, NVDIMM_MASTER); + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); + nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER); } void nvdimm_security_overwrite_query(struct work_struct *work) @@ -480,3 +469,85 @@ void nvdimm_security_overwrite_query(struct work_struct *work) __nvdimm_security_overwrite_query(nvdimm); nvdimm_bus_unlock(&nvdimm->dev); } + +#define OPS \ + C( OP_FREEZE, "freeze", 1), \ + C( OP_DISABLE, "disable", 2), \ + C( OP_UPDATE, "update", 3), \ + C( OP_ERASE, "erase", 2), \ + C( OP_OVERWRITE, "overwrite", 2), \ + C( OP_MASTER_UPDATE, "master_update", 3), \ + C( OP_MASTER_ERASE, "master_erase", 2) +#undef C +#define C(a, b, c) a +enum nvdimmsec_op_ids { OPS }; +#undef C +#define C(a, b, c) { b, c } +static struct { + const char *name; + int args; +} ops[] = { OPS }; +#undef C + +#define SEC_CMD_SIZE 32 +#define KEY_ID_SIZE 10 + +ssize_t nvdimm_security_store(struct device *dev, const char *buf, size_t len) +{ + struct nvdimm *nvdimm = to_nvdimm(dev); + ssize_t rc; + char cmd[SEC_CMD_SIZE+1], keystr[KEY_ID_SIZE+1], + nkeystr[KEY_ID_SIZE+1]; + unsigned int key, newkey; + int i; + + rc = sscanf(buf, "%"__stringify(SEC_CMD_SIZE)"s" + " %"__stringify(KEY_ID_SIZE)"s" + " %"__stringify(KEY_ID_SIZE)"s", + cmd, keystr, nkeystr); + if (rc < 1) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(ops); i++) + if (sysfs_streq(cmd, ops[i].name)) + break; + if (i >= ARRAY_SIZE(ops)) + return -EINVAL; + if (ops[i].args > 1) + rc = kstrtouint(keystr, 0, &key); + if (rc >= 0 && ops[i].args > 2) + rc = kstrtouint(nkeystr, 0, &newkey); + if (rc < 0) + return rc; + + if (i == OP_FREEZE) { + dev_dbg(dev, "freeze\n"); + rc = nvdimm_security_freeze(nvdimm); + } else if (i == OP_DISABLE) { + dev_dbg(dev, "disable %u\n", key); + rc = security_disable(nvdimm, key); + } else if (i == OP_UPDATE || i == OP_MASTER_UPDATE) { + dev_dbg(dev, "%s %u %u\n", ops[i].name, key, newkey); + rc = security_update(nvdimm, key, newkey, i == OP_UPDATE + ? NVDIMM_USER : NVDIMM_MASTER); + } else if (i == OP_ERASE || i == OP_MASTER_ERASE) { + dev_dbg(dev, "%s %u\n", ops[i].name, key); + if (atomic_read(&nvdimm->busy)) { + dev_dbg(dev, "Unable to secure erase while DIMM active.\n"); + return -EBUSY; + } + rc = security_erase(nvdimm, key, i == OP_ERASE + ? NVDIMM_USER : NVDIMM_MASTER); + } else if (i == OP_OVERWRITE) { + dev_dbg(dev, "overwrite %u\n", key); + if (atomic_read(&nvdimm->busy)) { + dev_dbg(dev, "Unable to overwrite while DIMM active.\n"); + return -EBUSY; + } + rc = security_overwrite(nvdimm, key); + } else + return -EINVAL; + + if (rc == 0) + rc = len; + return rc; +} |