18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 68c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 78c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/ndctl.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/fs.h> 138c2ecf20Sopenharmony_ci#include <linux/mm.h> 148c2ecf20Sopenharmony_ci#include "nd-core.h" 158c2ecf20Sopenharmony_ci#include "label.h" 168c2ecf20Sopenharmony_ci#include "pmem.h" 178c2ecf20Sopenharmony_ci#include "nd.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic DEFINE_IDA(dimm_ida); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic bool noblk; 228c2ecf20Sopenharmony_cimodule_param(noblk, bool, 0444); 238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(noblk, "force disable BLK / local alias support"); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * Retrieve bus and dimm handle and return if this bus supports 278c2ecf20Sopenharmony_ci * get_config_data commands 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ciint nvdimm_check_config_data(struct device *dev) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (!nvdimm->cmd_mask || 348c2ecf20Sopenharmony_ci !test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask)) { 358c2ecf20Sopenharmony_ci if (test_bit(NDD_LABELING, &nvdimm->flags)) 368c2ecf20Sopenharmony_ci return -ENXIO; 378c2ecf20Sopenharmony_ci else 388c2ecf20Sopenharmony_ci return -ENOTTY; 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return 0; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int validate_dimm(struct nvdimm_drvdata *ndd) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci int rc; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (!ndd) 498c2ecf20Sopenharmony_ci return -EINVAL; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci rc = nvdimm_check_config_data(ndd->dev); 528c2ecf20Sopenharmony_ci if (rc) 538c2ecf20Sopenharmony_ci dev_dbg(ndd->dev, "%ps: %s error: %d\n", 548c2ecf20Sopenharmony_ci __builtin_return_address(0), __func__, rc); 558c2ecf20Sopenharmony_ci return rc; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/** 598c2ecf20Sopenharmony_ci * nvdimm_init_nsarea - determine the geometry of a dimm's namespace area 608c2ecf20Sopenharmony_ci * @nvdimm: dimm to initialize 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ciint nvdimm_init_nsarea(struct nvdimm_drvdata *ndd) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct nd_cmd_get_config_size *cmd = &ndd->nsarea; 658c2ecf20Sopenharmony_ci struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev); 668c2ecf20Sopenharmony_ci struct nvdimm_bus_descriptor *nd_desc; 678c2ecf20Sopenharmony_ci int rc = validate_dimm(ndd); 688c2ecf20Sopenharmony_ci int cmd_rc = 0; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (rc) 718c2ecf20Sopenharmony_ci return rc; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (cmd->config_size) 748c2ecf20Sopenharmony_ci return 0; /* already valid */ 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci memset(cmd, 0, sizeof(*cmd)); 778c2ecf20Sopenharmony_ci nd_desc = nvdimm_bus->nd_desc; 788c2ecf20Sopenharmony_ci rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev), 798c2ecf20Sopenharmony_ci ND_CMD_GET_CONFIG_SIZE, cmd, sizeof(*cmd), &cmd_rc); 808c2ecf20Sopenharmony_ci if (rc < 0) 818c2ecf20Sopenharmony_ci return rc; 828c2ecf20Sopenharmony_ci return cmd_rc; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ciint nvdimm_get_config_data(struct nvdimm_drvdata *ndd, void *buf, 868c2ecf20Sopenharmony_ci size_t offset, size_t len) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev); 898c2ecf20Sopenharmony_ci struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; 908c2ecf20Sopenharmony_ci int rc = validate_dimm(ndd), cmd_rc = 0; 918c2ecf20Sopenharmony_ci struct nd_cmd_get_config_data_hdr *cmd; 928c2ecf20Sopenharmony_ci size_t max_cmd_size, buf_offset; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (rc) 958c2ecf20Sopenharmony_ci return rc; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (offset + len > ndd->nsarea.config_size) 988c2ecf20Sopenharmony_ci return -ENXIO; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci max_cmd_size = min_t(u32, len, ndd->nsarea.max_xfer); 1018c2ecf20Sopenharmony_ci cmd = kvzalloc(max_cmd_size + sizeof(*cmd), GFP_KERNEL); 1028c2ecf20Sopenharmony_ci if (!cmd) 1038c2ecf20Sopenharmony_ci return -ENOMEM; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci for (buf_offset = 0; len; 1068c2ecf20Sopenharmony_ci len -= cmd->in_length, buf_offset += cmd->in_length) { 1078c2ecf20Sopenharmony_ci size_t cmd_size; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci cmd->in_offset = offset + buf_offset; 1108c2ecf20Sopenharmony_ci cmd->in_length = min(max_cmd_size, len); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci cmd_size = sizeof(*cmd) + cmd->in_length; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev), 1158c2ecf20Sopenharmony_ci ND_CMD_GET_CONFIG_DATA, cmd, cmd_size, &cmd_rc); 1168c2ecf20Sopenharmony_ci if (rc < 0) 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci if (cmd_rc < 0) { 1198c2ecf20Sopenharmony_ci rc = cmd_rc; 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* out_buf should be valid, copy it into our output buffer */ 1248c2ecf20Sopenharmony_ci memcpy(buf + buf_offset, cmd->out_buf, cmd->in_length); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci kvfree(cmd); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return rc; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ciint nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset, 1328c2ecf20Sopenharmony_ci void *buf, size_t len) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci size_t max_cmd_size, buf_offset; 1358c2ecf20Sopenharmony_ci struct nd_cmd_set_config_hdr *cmd; 1368c2ecf20Sopenharmony_ci int rc = validate_dimm(ndd), cmd_rc = 0; 1378c2ecf20Sopenharmony_ci struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev); 1388c2ecf20Sopenharmony_ci struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (rc) 1418c2ecf20Sopenharmony_ci return rc; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (offset + len > ndd->nsarea.config_size) 1448c2ecf20Sopenharmony_ci return -ENXIO; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci max_cmd_size = min_t(u32, len, ndd->nsarea.max_xfer); 1478c2ecf20Sopenharmony_ci cmd = kvzalloc(max_cmd_size + sizeof(*cmd) + sizeof(u32), GFP_KERNEL); 1488c2ecf20Sopenharmony_ci if (!cmd) 1498c2ecf20Sopenharmony_ci return -ENOMEM; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci for (buf_offset = 0; len; len -= cmd->in_length, 1528c2ecf20Sopenharmony_ci buf_offset += cmd->in_length) { 1538c2ecf20Sopenharmony_ci size_t cmd_size; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci cmd->in_offset = offset + buf_offset; 1568c2ecf20Sopenharmony_ci cmd->in_length = min(max_cmd_size, len); 1578c2ecf20Sopenharmony_ci memcpy(cmd->in_buf, buf + buf_offset, cmd->in_length); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* status is output in the last 4-bytes of the command buffer */ 1608c2ecf20Sopenharmony_ci cmd_size = sizeof(*cmd) + cmd->in_length + sizeof(u32); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev), 1638c2ecf20Sopenharmony_ci ND_CMD_SET_CONFIG_DATA, cmd, cmd_size, &cmd_rc); 1648c2ecf20Sopenharmony_ci if (rc < 0) 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci if (cmd_rc < 0) { 1678c2ecf20Sopenharmony_ci rc = cmd_rc; 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci kvfree(cmd); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return rc; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_civoid nvdimm_set_labeling(struct device *dev) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci set_bit(NDD_LABELING, &nvdimm->flags); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_civoid nvdimm_set_locked(struct device *dev) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci set_bit(NDD_LOCKED, &nvdimm->flags); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_civoid nvdimm_clear_locked(struct device *dev) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci clear_bit(NDD_LOCKED, &nvdimm->flags); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic void nvdimm_release(struct device *dev) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci ida_simple_remove(&dimm_ida, nvdimm->id); 2028c2ecf20Sopenharmony_ci kfree(nvdimm); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistruct nvdimm *to_nvdimm(struct device *dev) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = container_of(dev, struct nvdimm, dev); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci WARN_ON(!is_nvdimm(dev)); 2108c2ecf20Sopenharmony_ci return nvdimm; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(to_nvdimm); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistruct nvdimm *nd_blk_region_to_dimm(struct nd_blk_region *ndbr) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct nd_region *nd_region = &ndbr->nd_region; 2178c2ecf20Sopenharmony_ci struct nd_mapping *nd_mapping = &nd_region->mapping[0]; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return nd_mapping->nvdimm; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nd_blk_region_to_dimm); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ciunsigned long nd_blk_memremap_flags(struct nd_blk_region *ndbr) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci /* pmem mapping properties are private to libnvdimm */ 2268c2ecf20Sopenharmony_ci return ARCH_MEMREMAP_PMEM; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nd_blk_memremap_flags); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistruct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = nd_mapping->nvdimm; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev)); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return dev_get_drvdata(&nvdimm->dev); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(to_ndd); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_civoid nvdimm_drvdata_release(struct kref *kref) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct nvdimm_drvdata *ndd = container_of(kref, typeof(*ndd), kref); 2438c2ecf20Sopenharmony_ci struct device *dev = ndd->dev; 2448c2ecf20Sopenharmony_ci struct resource *res, *_r; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci dev_dbg(dev, "trace\n"); 2478c2ecf20Sopenharmony_ci nvdimm_bus_lock(dev); 2488c2ecf20Sopenharmony_ci for_each_dpa_resource_safe(ndd, res, _r) 2498c2ecf20Sopenharmony_ci nvdimm_free_dpa(ndd, res); 2508c2ecf20Sopenharmony_ci nvdimm_bus_unlock(dev); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci kvfree(ndd->data); 2538c2ecf20Sopenharmony_ci kfree(ndd); 2548c2ecf20Sopenharmony_ci put_device(dev); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_civoid get_ndd(struct nvdimm_drvdata *ndd) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci kref_get(&ndd->kref); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_civoid put_ndd(struct nvdimm_drvdata *ndd) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci if (ndd) 2658c2ecf20Sopenharmony_ci kref_put(&ndd->kref, nvdimm_drvdata_release); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ciconst char *nvdimm_name(struct nvdimm *nvdimm) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci return dev_name(&nvdimm->dev); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvdimm_name); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistruct kobject *nvdimm_kobj(struct nvdimm *nvdimm) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci return &nvdimm->dev.kobj; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvdimm_kobj); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ciunsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci return nvdimm->cmd_mask; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvdimm_cmd_mask); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_civoid *nvdimm_provider_data(struct nvdimm *nvdimm) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci if (nvdimm) 2898c2ecf20Sopenharmony_ci return nvdimm->provider_data; 2908c2ecf20Sopenharmony_ci return NULL; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvdimm_provider_data); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic ssize_t commands_show(struct device *dev, 2958c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 2988c2ecf20Sopenharmony_ci int cmd, len = 0; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (!nvdimm->cmd_mask) 3018c2ecf20Sopenharmony_ci return sprintf(buf, "\n"); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci for_each_set_bit(cmd, &nvdimm->cmd_mask, BITS_PER_LONG) 3048c2ecf20Sopenharmony_ci len += sprintf(buf + len, "%s ", nvdimm_cmd_name(cmd)); 3058c2ecf20Sopenharmony_ci len += sprintf(buf + len, "\n"); 3068c2ecf20Sopenharmony_ci return len; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(commands); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic ssize_t flags_show(struct device *dev, 3118c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return sprintf(buf, "%s%s%s\n", 3168c2ecf20Sopenharmony_ci test_bit(NDD_ALIASING, &nvdimm->flags) ? "alias " : "", 3178c2ecf20Sopenharmony_ci test_bit(NDD_LABELING, &nvdimm->flags) ? "label " : "", 3188c2ecf20Sopenharmony_ci test_bit(NDD_LOCKED, &nvdimm->flags) ? "lock " : ""); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(flags); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic ssize_t state_show(struct device *dev, struct device_attribute *attr, 3238c2ecf20Sopenharmony_ci char *buf) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* 3288c2ecf20Sopenharmony_ci * The state may be in the process of changing, userspace should 3298c2ecf20Sopenharmony_ci * quiesce probing if it wants a static answer 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_ci nvdimm_bus_lock(dev); 3328c2ecf20Sopenharmony_ci nvdimm_bus_unlock(dev); 3338c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", atomic_read(&nvdimm->busy) 3348c2ecf20Sopenharmony_ci ? "active" : "idle"); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(state); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic ssize_t __available_slots_show(struct nvdimm_drvdata *ndd, char *buf) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct device *dev; 3418c2ecf20Sopenharmony_ci ssize_t rc; 3428c2ecf20Sopenharmony_ci u32 nfree; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (!ndd) 3458c2ecf20Sopenharmony_ci return -ENXIO; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci dev = ndd->dev; 3488c2ecf20Sopenharmony_ci nvdimm_bus_lock(dev); 3498c2ecf20Sopenharmony_ci nfree = nd_label_nfree(ndd); 3508c2ecf20Sopenharmony_ci if (nfree - 1 > nfree) { 3518c2ecf20Sopenharmony_ci dev_WARN_ONCE(dev, 1, "we ate our last label?\n"); 3528c2ecf20Sopenharmony_ci nfree = 0; 3538c2ecf20Sopenharmony_ci } else 3548c2ecf20Sopenharmony_ci nfree--; 3558c2ecf20Sopenharmony_ci rc = sprintf(buf, "%d\n", nfree); 3568c2ecf20Sopenharmony_ci nvdimm_bus_unlock(dev); 3578c2ecf20Sopenharmony_ci return rc; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic ssize_t available_slots_show(struct device *dev, 3618c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci ssize_t rc; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci nd_device_lock(dev); 3668c2ecf20Sopenharmony_ci rc = __available_slots_show(dev_get_drvdata(dev), buf); 3678c2ecf20Sopenharmony_ci nd_device_unlock(dev); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return rc; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(available_slots); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci__weak ssize_t security_show(struct device *dev, 3748c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (test_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags)) 3798c2ecf20Sopenharmony_ci return sprintf(buf, "overwrite\n"); 3808c2ecf20Sopenharmony_ci if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags)) 3818c2ecf20Sopenharmony_ci return sprintf(buf, "disabled\n"); 3828c2ecf20Sopenharmony_ci if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags)) 3838c2ecf20Sopenharmony_ci return sprintf(buf, "unlocked\n"); 3848c2ecf20Sopenharmony_ci if (test_bit(NVDIMM_SECURITY_LOCKED, &nvdimm->sec.flags)) 3858c2ecf20Sopenharmony_ci return sprintf(buf, "locked\n"); 3868c2ecf20Sopenharmony_ci return -ENOTTY; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic ssize_t frozen_show(struct device *dev, 3908c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", test_bit(NVDIMM_SECURITY_FROZEN, 3958c2ecf20Sopenharmony_ci &nvdimm->sec.flags)); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(frozen); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic ssize_t security_store(struct device *dev, 4008c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t len) 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci ssize_t rc; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* 4068c2ecf20Sopenharmony_ci * Require all userspace triggered security management to be 4078c2ecf20Sopenharmony_ci * done while probing is idle and the DIMM is not in active use 4088c2ecf20Sopenharmony_ci * in any region. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_ci nd_device_lock(dev); 4118c2ecf20Sopenharmony_ci nvdimm_bus_lock(dev); 4128c2ecf20Sopenharmony_ci wait_nvdimm_bus_probe_idle(dev); 4138c2ecf20Sopenharmony_ci rc = nvdimm_security_store(dev, buf, len); 4148c2ecf20Sopenharmony_ci nvdimm_bus_unlock(dev); 4158c2ecf20Sopenharmony_ci nd_device_unlock(dev); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return rc; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(security); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic struct attribute *nvdimm_attributes[] = { 4228c2ecf20Sopenharmony_ci &dev_attr_state.attr, 4238c2ecf20Sopenharmony_ci &dev_attr_flags.attr, 4248c2ecf20Sopenharmony_ci &dev_attr_commands.attr, 4258c2ecf20Sopenharmony_ci &dev_attr_available_slots.attr, 4268c2ecf20Sopenharmony_ci &dev_attr_security.attr, 4278c2ecf20Sopenharmony_ci &dev_attr_frozen.attr, 4288c2ecf20Sopenharmony_ci NULL, 4298c2ecf20Sopenharmony_ci}; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct device *dev = container_of(kobj, typeof(*dev), kobj); 4348c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (a != &dev_attr_security.attr && a != &dev_attr_frozen.attr) 4378c2ecf20Sopenharmony_ci return a->mode; 4388c2ecf20Sopenharmony_ci if (!nvdimm->sec.flags) 4398c2ecf20Sopenharmony_ci return 0; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (a == &dev_attr_security.attr) { 4428c2ecf20Sopenharmony_ci /* Are there any state mutation ops (make writable)? */ 4438c2ecf20Sopenharmony_ci if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable 4448c2ecf20Sopenharmony_ci || nvdimm->sec.ops->change_key 4458c2ecf20Sopenharmony_ci || nvdimm->sec.ops->erase 4468c2ecf20Sopenharmony_ci || nvdimm->sec.ops->overwrite) 4478c2ecf20Sopenharmony_ci return a->mode; 4488c2ecf20Sopenharmony_ci return 0444; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (nvdimm->sec.ops->freeze) 4528c2ecf20Sopenharmony_ci return a->mode; 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic const struct attribute_group nvdimm_attribute_group = { 4578c2ecf20Sopenharmony_ci .attrs = nvdimm_attributes, 4588c2ecf20Sopenharmony_ci .is_visible = nvdimm_visible, 4598c2ecf20Sopenharmony_ci}; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic ssize_t result_show(struct device *dev, struct device_attribute *attr, char *buf) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 4648c2ecf20Sopenharmony_ci enum nvdimm_fwa_result result; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (!nvdimm->fw_ops) 4678c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci nvdimm_bus_lock(dev); 4708c2ecf20Sopenharmony_ci result = nvdimm->fw_ops->activate_result(nvdimm); 4718c2ecf20Sopenharmony_ci nvdimm_bus_unlock(dev); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci switch (result) { 4748c2ecf20Sopenharmony_ci case NVDIMM_FWA_RESULT_NONE: 4758c2ecf20Sopenharmony_ci return sprintf(buf, "none\n"); 4768c2ecf20Sopenharmony_ci case NVDIMM_FWA_RESULT_SUCCESS: 4778c2ecf20Sopenharmony_ci return sprintf(buf, "success\n"); 4788c2ecf20Sopenharmony_ci case NVDIMM_FWA_RESULT_FAIL: 4798c2ecf20Sopenharmony_ci return sprintf(buf, "fail\n"); 4808c2ecf20Sopenharmony_ci case NVDIMM_FWA_RESULT_NOTSTAGED: 4818c2ecf20Sopenharmony_ci return sprintf(buf, "not_staged\n"); 4828c2ecf20Sopenharmony_ci case NVDIMM_FWA_RESULT_NEEDRESET: 4838c2ecf20Sopenharmony_ci return sprintf(buf, "need_reset\n"); 4848c2ecf20Sopenharmony_ci default: 4858c2ecf20Sopenharmony_ci return -ENXIO; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_cistatic DEVICE_ATTR_ADMIN_RO(result); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic ssize_t activate_show(struct device *dev, struct device_attribute *attr, char *buf) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 4938c2ecf20Sopenharmony_ci enum nvdimm_fwa_state state; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (!nvdimm->fw_ops) 4968c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci nvdimm_bus_lock(dev); 4998c2ecf20Sopenharmony_ci state = nvdimm->fw_ops->activate_state(nvdimm); 5008c2ecf20Sopenharmony_ci nvdimm_bus_unlock(dev); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci switch (state) { 5038c2ecf20Sopenharmony_ci case NVDIMM_FWA_IDLE: 5048c2ecf20Sopenharmony_ci return sprintf(buf, "idle\n"); 5058c2ecf20Sopenharmony_ci case NVDIMM_FWA_BUSY: 5068c2ecf20Sopenharmony_ci return sprintf(buf, "busy\n"); 5078c2ecf20Sopenharmony_ci case NVDIMM_FWA_ARMED: 5088c2ecf20Sopenharmony_ci return sprintf(buf, "armed\n"); 5098c2ecf20Sopenharmony_ci default: 5108c2ecf20Sopenharmony_ci return -ENXIO; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic ssize_t activate_store(struct device *dev, struct device_attribute *attr, 5158c2ecf20Sopenharmony_ci const char *buf, size_t len) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 5188c2ecf20Sopenharmony_ci enum nvdimm_fwa_trigger arg; 5198c2ecf20Sopenharmony_ci int rc; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (!nvdimm->fw_ops) 5228c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (sysfs_streq(buf, "arm")) 5258c2ecf20Sopenharmony_ci arg = NVDIMM_FWA_ARM; 5268c2ecf20Sopenharmony_ci else if (sysfs_streq(buf, "disarm")) 5278c2ecf20Sopenharmony_ci arg = NVDIMM_FWA_DISARM; 5288c2ecf20Sopenharmony_ci else 5298c2ecf20Sopenharmony_ci return -EINVAL; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci nvdimm_bus_lock(dev); 5328c2ecf20Sopenharmony_ci rc = nvdimm->fw_ops->arm(nvdimm, arg); 5338c2ecf20Sopenharmony_ci nvdimm_bus_unlock(dev); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (rc < 0) 5368c2ecf20Sopenharmony_ci return rc; 5378c2ecf20Sopenharmony_ci return len; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_cistatic DEVICE_ATTR_ADMIN_RW(activate); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic struct attribute *nvdimm_firmware_attributes[] = { 5428c2ecf20Sopenharmony_ci &dev_attr_activate.attr, 5438c2ecf20Sopenharmony_ci &dev_attr_result.attr, 5448c2ecf20Sopenharmony_ci NULL, 5458c2ecf20Sopenharmony_ci}; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic umode_t nvdimm_firmware_visible(struct kobject *kobj, struct attribute *a, int n) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct device *dev = container_of(kobj, typeof(*dev), kobj); 5508c2ecf20Sopenharmony_ci struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); 5518c2ecf20Sopenharmony_ci struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; 5528c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 5538c2ecf20Sopenharmony_ci enum nvdimm_fwa_capability cap; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (!nd_desc->fw_ops) 5568c2ecf20Sopenharmony_ci return 0; 5578c2ecf20Sopenharmony_ci if (!nvdimm->fw_ops) 5588c2ecf20Sopenharmony_ci return 0; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci nvdimm_bus_lock(dev); 5618c2ecf20Sopenharmony_ci cap = nd_desc->fw_ops->capability(nd_desc); 5628c2ecf20Sopenharmony_ci nvdimm_bus_unlock(dev); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (cap < NVDIMM_FWA_CAP_QUIESCE) 5658c2ecf20Sopenharmony_ci return 0; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci return a->mode; 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic const struct attribute_group nvdimm_firmware_attribute_group = { 5718c2ecf20Sopenharmony_ci .name = "firmware", 5728c2ecf20Sopenharmony_ci .attrs = nvdimm_firmware_attributes, 5738c2ecf20Sopenharmony_ci .is_visible = nvdimm_firmware_visible, 5748c2ecf20Sopenharmony_ci}; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic const struct attribute_group *nvdimm_attribute_groups[] = { 5778c2ecf20Sopenharmony_ci &nd_device_attribute_group, 5788c2ecf20Sopenharmony_ci &nvdimm_attribute_group, 5798c2ecf20Sopenharmony_ci &nvdimm_firmware_attribute_group, 5808c2ecf20Sopenharmony_ci NULL, 5818c2ecf20Sopenharmony_ci}; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic const struct device_type nvdimm_device_type = { 5848c2ecf20Sopenharmony_ci .name = "nvdimm", 5858c2ecf20Sopenharmony_ci .release = nvdimm_release, 5868c2ecf20Sopenharmony_ci .groups = nvdimm_attribute_groups, 5878c2ecf20Sopenharmony_ci}; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cibool is_nvdimm(struct device *dev) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci return dev->type == &nvdimm_device_type; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistruct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, 5958c2ecf20Sopenharmony_ci void *provider_data, const struct attribute_group **groups, 5968c2ecf20Sopenharmony_ci unsigned long flags, unsigned long cmd_mask, int num_flush, 5978c2ecf20Sopenharmony_ci struct resource *flush_wpq, const char *dimm_id, 5988c2ecf20Sopenharmony_ci const struct nvdimm_security_ops *sec_ops, 5998c2ecf20Sopenharmony_ci const struct nvdimm_fw_ops *fw_ops) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL); 6028c2ecf20Sopenharmony_ci struct device *dev; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (!nvdimm) 6058c2ecf20Sopenharmony_ci return NULL; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci nvdimm->id = ida_simple_get(&dimm_ida, 0, 0, GFP_KERNEL); 6088c2ecf20Sopenharmony_ci if (nvdimm->id < 0) { 6098c2ecf20Sopenharmony_ci kfree(nvdimm); 6108c2ecf20Sopenharmony_ci return NULL; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci nvdimm->dimm_id = dimm_id; 6148c2ecf20Sopenharmony_ci nvdimm->provider_data = provider_data; 6158c2ecf20Sopenharmony_ci if (noblk) 6168c2ecf20Sopenharmony_ci flags |= 1 << NDD_NOBLK; 6178c2ecf20Sopenharmony_ci nvdimm->flags = flags; 6188c2ecf20Sopenharmony_ci nvdimm->cmd_mask = cmd_mask; 6198c2ecf20Sopenharmony_ci nvdimm->num_flush = num_flush; 6208c2ecf20Sopenharmony_ci nvdimm->flush_wpq = flush_wpq; 6218c2ecf20Sopenharmony_ci atomic_set(&nvdimm->busy, 0); 6228c2ecf20Sopenharmony_ci dev = &nvdimm->dev; 6238c2ecf20Sopenharmony_ci dev_set_name(dev, "nmem%d", nvdimm->id); 6248c2ecf20Sopenharmony_ci dev->parent = &nvdimm_bus->dev; 6258c2ecf20Sopenharmony_ci dev->type = &nvdimm_device_type; 6268c2ecf20Sopenharmony_ci dev->devt = MKDEV(nvdimm_major, nvdimm->id); 6278c2ecf20Sopenharmony_ci dev->groups = groups; 6288c2ecf20Sopenharmony_ci nvdimm->sec.ops = sec_ops; 6298c2ecf20Sopenharmony_ci nvdimm->fw_ops = fw_ops; 6308c2ecf20Sopenharmony_ci nvdimm->sec.overwrite_tmo = 0; 6318c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&nvdimm->dwork, nvdimm_security_overwrite_query); 6328c2ecf20Sopenharmony_ci /* 6338c2ecf20Sopenharmony_ci * Security state must be initialized before device_add() for 6348c2ecf20Sopenharmony_ci * attribute visibility. 6358c2ecf20Sopenharmony_ci */ 6368c2ecf20Sopenharmony_ci /* get security state and extended (master) state */ 6378c2ecf20Sopenharmony_ci nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); 6388c2ecf20Sopenharmony_ci nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER); 6398c2ecf20Sopenharmony_ci nd_device_register(dev); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci return nvdimm; 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__nvdimm_create); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic void shutdown_security_notify(void *data) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = data; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci sysfs_put(nvdimm->sec.overwrite_state); 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ciint nvdimm_security_setup_events(struct device *dev) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci struct nvdimm *nvdimm = to_nvdimm(dev); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (!nvdimm->sec.flags || !nvdimm->sec.ops 6578c2ecf20Sopenharmony_ci || !nvdimm->sec.ops->overwrite) 6588c2ecf20Sopenharmony_ci return 0; 6598c2ecf20Sopenharmony_ci nvdimm->sec.overwrite_state = sysfs_get_dirent(dev->kobj.sd, "security"); 6608c2ecf20Sopenharmony_ci if (!nvdimm->sec.overwrite_state) 6618c2ecf20Sopenharmony_ci return -ENOMEM; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci return devm_add_action_or_reset(dev, shutdown_security_notify, nvdimm); 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvdimm_security_setup_events); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ciint nvdimm_in_overwrite(struct nvdimm *nvdimm) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci return test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags); 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvdimm_in_overwrite); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ciint nvdimm_security_freeze(struct nvdimm *nvdimm) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci int rc; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev)); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (!nvdimm->sec.ops || !nvdimm->sec.ops->freeze) 6808c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci if (!nvdimm->sec.flags) 6838c2ecf20Sopenharmony_ci return -EIO; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { 6868c2ecf20Sopenharmony_ci dev_warn(&nvdimm->dev, "Overwrite operation in progress.\n"); 6878c2ecf20Sopenharmony_ci return -EBUSY; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci rc = nvdimm->sec.ops->freeze(nvdimm); 6918c2ecf20Sopenharmony_ci nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci return rc; 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic unsigned long dpa_align(struct nd_region *nd_region) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci struct device *dev = &nd_region->dev; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (dev_WARN_ONCE(dev, !is_nvdimm_bus_locked(dev), 7018c2ecf20Sopenharmony_ci "bus lock required for capacity provision\n")) 7028c2ecf20Sopenharmony_ci return 0; 7038c2ecf20Sopenharmony_ci if (dev_WARN_ONCE(dev, !nd_region->ndr_mappings || nd_region->align 7048c2ecf20Sopenharmony_ci % nd_region->ndr_mappings, 7058c2ecf20Sopenharmony_ci "invalid region align %#lx mappings: %d\n", 7068c2ecf20Sopenharmony_ci nd_region->align, nd_region->ndr_mappings)) 7078c2ecf20Sopenharmony_ci return 0; 7088c2ecf20Sopenharmony_ci return nd_region->align / nd_region->ndr_mappings; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ciint alias_dpa_busy(struct device *dev, void *data) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci resource_size_t map_end, blk_start, new; 7148c2ecf20Sopenharmony_ci struct blk_alloc_info *info = data; 7158c2ecf20Sopenharmony_ci struct nd_mapping *nd_mapping; 7168c2ecf20Sopenharmony_ci struct nd_region *nd_region; 7178c2ecf20Sopenharmony_ci struct nvdimm_drvdata *ndd; 7188c2ecf20Sopenharmony_ci struct resource *res; 7198c2ecf20Sopenharmony_ci unsigned long align; 7208c2ecf20Sopenharmony_ci int i; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci if (!is_memory(dev)) 7238c2ecf20Sopenharmony_ci return 0; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci nd_region = to_nd_region(dev); 7268c2ecf20Sopenharmony_ci for (i = 0; i < nd_region->ndr_mappings; i++) { 7278c2ecf20Sopenharmony_ci nd_mapping = &nd_region->mapping[i]; 7288c2ecf20Sopenharmony_ci if (nd_mapping->nvdimm == info->nd_mapping->nvdimm) 7298c2ecf20Sopenharmony_ci break; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci if (i >= nd_region->ndr_mappings) 7338c2ecf20Sopenharmony_ci return 0; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci ndd = to_ndd(nd_mapping); 7368c2ecf20Sopenharmony_ci map_end = nd_mapping->start + nd_mapping->size - 1; 7378c2ecf20Sopenharmony_ci blk_start = nd_mapping->start; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* 7408c2ecf20Sopenharmony_ci * In the allocation case ->res is set to free space that we are 7418c2ecf20Sopenharmony_ci * looking to validate against PMEM aliasing collision rules 7428c2ecf20Sopenharmony_ci * (i.e. BLK is allocated after all aliased PMEM). 7438c2ecf20Sopenharmony_ci */ 7448c2ecf20Sopenharmony_ci if (info->res) { 7458c2ecf20Sopenharmony_ci if (info->res->start >= nd_mapping->start 7468c2ecf20Sopenharmony_ci && info->res->start < map_end) 7478c2ecf20Sopenharmony_ci /* pass */; 7488c2ecf20Sopenharmony_ci else 7498c2ecf20Sopenharmony_ci return 0; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci retry: 7538c2ecf20Sopenharmony_ci /* 7548c2ecf20Sopenharmony_ci * Find the free dpa from the end of the last pmem allocation to 7558c2ecf20Sopenharmony_ci * the end of the interleave-set mapping. 7568c2ecf20Sopenharmony_ci */ 7578c2ecf20Sopenharmony_ci align = dpa_align(nd_region); 7588c2ecf20Sopenharmony_ci if (!align) 7598c2ecf20Sopenharmony_ci return 0; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci for_each_dpa_resource(ndd, res) { 7628c2ecf20Sopenharmony_ci resource_size_t start, end; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci if (strncmp(res->name, "pmem", 4) != 0) 7658c2ecf20Sopenharmony_ci continue; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci start = ALIGN_DOWN(res->start, align); 7688c2ecf20Sopenharmony_ci end = ALIGN(res->end + 1, align) - 1; 7698c2ecf20Sopenharmony_ci if ((start >= blk_start && start < map_end) 7708c2ecf20Sopenharmony_ci || (end >= blk_start && end <= map_end)) { 7718c2ecf20Sopenharmony_ci new = max(blk_start, min(map_end, end) + 1); 7728c2ecf20Sopenharmony_ci if (new != blk_start) { 7738c2ecf20Sopenharmony_ci blk_start = new; 7748c2ecf20Sopenharmony_ci goto retry; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci /* update the free space range with the probed blk_start */ 7808c2ecf20Sopenharmony_ci if (info->res && blk_start > info->res->start) { 7818c2ecf20Sopenharmony_ci info->res->start = max(info->res->start, blk_start); 7828c2ecf20Sopenharmony_ci if (info->res->start > info->res->end) 7838c2ecf20Sopenharmony_ci info->res->end = info->res->start - 1; 7848c2ecf20Sopenharmony_ci return 1; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci info->available -= blk_start - nd_mapping->start; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci return 0; 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci/** 7938c2ecf20Sopenharmony_ci * nd_blk_available_dpa - account the unused dpa of BLK region 7948c2ecf20Sopenharmony_ci * @nd_mapping: container of dpa-resource-root + labels 7958c2ecf20Sopenharmony_ci * 7968c2ecf20Sopenharmony_ci * Unlike PMEM, BLK namespaces can occupy discontiguous DPA ranges, but 7978c2ecf20Sopenharmony_ci * we arrange for them to never start at an lower dpa than the last 7988c2ecf20Sopenharmony_ci * PMEM allocation in an aliased region. 7998c2ecf20Sopenharmony_ci */ 8008c2ecf20Sopenharmony_ciresource_size_t nd_blk_available_dpa(struct nd_region *nd_region) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); 8038c2ecf20Sopenharmony_ci struct nd_mapping *nd_mapping = &nd_region->mapping[0]; 8048c2ecf20Sopenharmony_ci struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 8058c2ecf20Sopenharmony_ci struct blk_alloc_info info = { 8068c2ecf20Sopenharmony_ci .nd_mapping = nd_mapping, 8078c2ecf20Sopenharmony_ci .available = nd_mapping->size, 8088c2ecf20Sopenharmony_ci .res = NULL, 8098c2ecf20Sopenharmony_ci }; 8108c2ecf20Sopenharmony_ci struct resource *res; 8118c2ecf20Sopenharmony_ci unsigned long align; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci if (!ndd) 8148c2ecf20Sopenharmony_ci return 0; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci device_for_each_child(&nvdimm_bus->dev, &info, alias_dpa_busy); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* now account for busy blk allocations in unaliased dpa */ 8198c2ecf20Sopenharmony_ci align = dpa_align(nd_region); 8208c2ecf20Sopenharmony_ci if (!align) 8218c2ecf20Sopenharmony_ci return 0; 8228c2ecf20Sopenharmony_ci for_each_dpa_resource(ndd, res) { 8238c2ecf20Sopenharmony_ci resource_size_t start, end, size; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci if (strncmp(res->name, "blk", 3) != 0) 8268c2ecf20Sopenharmony_ci continue; 8278c2ecf20Sopenharmony_ci start = ALIGN_DOWN(res->start, align); 8288c2ecf20Sopenharmony_ci end = ALIGN(res->end + 1, align) - 1; 8298c2ecf20Sopenharmony_ci size = end - start + 1; 8308c2ecf20Sopenharmony_ci if (size >= info.available) 8318c2ecf20Sopenharmony_ci return 0; 8328c2ecf20Sopenharmony_ci info.available -= size; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci return info.available; 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci/** 8398c2ecf20Sopenharmony_ci * nd_pmem_max_contiguous_dpa - For the given dimm+region, return the max 8408c2ecf20Sopenharmony_ci * contiguous unallocated dpa range. 8418c2ecf20Sopenharmony_ci * @nd_region: constrain available space check to this reference region 8428c2ecf20Sopenharmony_ci * @nd_mapping: container of dpa-resource-root + labels 8438c2ecf20Sopenharmony_ci */ 8448c2ecf20Sopenharmony_ciresource_size_t nd_pmem_max_contiguous_dpa(struct nd_region *nd_region, 8458c2ecf20Sopenharmony_ci struct nd_mapping *nd_mapping) 8468c2ecf20Sopenharmony_ci{ 8478c2ecf20Sopenharmony_ci struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 8488c2ecf20Sopenharmony_ci struct nvdimm_bus *nvdimm_bus; 8498c2ecf20Sopenharmony_ci resource_size_t max = 0; 8508c2ecf20Sopenharmony_ci struct resource *res; 8518c2ecf20Sopenharmony_ci unsigned long align; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci /* if a dimm is disabled the available capacity is zero */ 8548c2ecf20Sopenharmony_ci if (!ndd) 8558c2ecf20Sopenharmony_ci return 0; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci align = dpa_align(nd_region); 8588c2ecf20Sopenharmony_ci if (!align) 8598c2ecf20Sopenharmony_ci return 0; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci nvdimm_bus = walk_to_nvdimm_bus(ndd->dev); 8628c2ecf20Sopenharmony_ci if (__reserve_free_pmem(&nd_region->dev, nd_mapping->nvdimm)) 8638c2ecf20Sopenharmony_ci return 0; 8648c2ecf20Sopenharmony_ci for_each_dpa_resource(ndd, res) { 8658c2ecf20Sopenharmony_ci resource_size_t start, end; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (strcmp(res->name, "pmem-reserve") != 0) 8688c2ecf20Sopenharmony_ci continue; 8698c2ecf20Sopenharmony_ci /* trim free space relative to current alignment setting */ 8708c2ecf20Sopenharmony_ci start = ALIGN(res->start, align); 8718c2ecf20Sopenharmony_ci end = ALIGN_DOWN(res->end + 1, align) - 1; 8728c2ecf20Sopenharmony_ci if (end < start) 8738c2ecf20Sopenharmony_ci continue; 8748c2ecf20Sopenharmony_ci if (end - start + 1 > max) 8758c2ecf20Sopenharmony_ci max = end - start + 1; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci release_free_pmem(nvdimm_bus, nd_mapping); 8788c2ecf20Sopenharmony_ci return max; 8798c2ecf20Sopenharmony_ci} 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci/** 8828c2ecf20Sopenharmony_ci * nd_pmem_available_dpa - for the given dimm+region account unallocated dpa 8838c2ecf20Sopenharmony_ci * @nd_mapping: container of dpa-resource-root + labels 8848c2ecf20Sopenharmony_ci * @nd_region: constrain available space check to this reference region 8858c2ecf20Sopenharmony_ci * @overlap: calculate available space assuming this level of overlap 8868c2ecf20Sopenharmony_ci * 8878c2ecf20Sopenharmony_ci * Validate that a PMEM label, if present, aligns with the start of an 8888c2ecf20Sopenharmony_ci * interleave set and truncate the available size at the lowest BLK 8898c2ecf20Sopenharmony_ci * overlap point. 8908c2ecf20Sopenharmony_ci * 8918c2ecf20Sopenharmony_ci * The expectation is that this routine is called multiple times as it 8928c2ecf20Sopenharmony_ci * probes for the largest BLK encroachment for any single member DIMM of 8938c2ecf20Sopenharmony_ci * the interleave set. Once that value is determined the PMEM-limit for 8948c2ecf20Sopenharmony_ci * the set can be established. 8958c2ecf20Sopenharmony_ci */ 8968c2ecf20Sopenharmony_ciresource_size_t nd_pmem_available_dpa(struct nd_region *nd_region, 8978c2ecf20Sopenharmony_ci struct nd_mapping *nd_mapping, resource_size_t *overlap) 8988c2ecf20Sopenharmony_ci{ 8998c2ecf20Sopenharmony_ci resource_size_t map_start, map_end, busy = 0, available, blk_start; 9008c2ecf20Sopenharmony_ci struct nvdimm_drvdata *ndd = to_ndd(nd_mapping); 9018c2ecf20Sopenharmony_ci struct resource *res; 9028c2ecf20Sopenharmony_ci const char *reason; 9038c2ecf20Sopenharmony_ci unsigned long align; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci if (!ndd) 9068c2ecf20Sopenharmony_ci return 0; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci align = dpa_align(nd_region); 9098c2ecf20Sopenharmony_ci if (!align) 9108c2ecf20Sopenharmony_ci return 0; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci map_start = nd_mapping->start; 9138c2ecf20Sopenharmony_ci map_end = map_start + nd_mapping->size - 1; 9148c2ecf20Sopenharmony_ci blk_start = max(map_start, map_end + 1 - *overlap); 9158c2ecf20Sopenharmony_ci for_each_dpa_resource(ndd, res) { 9168c2ecf20Sopenharmony_ci resource_size_t start, end; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci start = ALIGN_DOWN(res->start, align); 9198c2ecf20Sopenharmony_ci end = ALIGN(res->end + 1, align) - 1; 9208c2ecf20Sopenharmony_ci if (start >= map_start && start < map_end) { 9218c2ecf20Sopenharmony_ci if (strncmp(res->name, "blk", 3) == 0) 9228c2ecf20Sopenharmony_ci blk_start = min(blk_start, 9238c2ecf20Sopenharmony_ci max(map_start, start)); 9248c2ecf20Sopenharmony_ci else if (end > map_end) { 9258c2ecf20Sopenharmony_ci reason = "misaligned to iset"; 9268c2ecf20Sopenharmony_ci goto err; 9278c2ecf20Sopenharmony_ci } else 9288c2ecf20Sopenharmony_ci busy += end - start + 1; 9298c2ecf20Sopenharmony_ci } else if (end >= map_start && end <= map_end) { 9308c2ecf20Sopenharmony_ci if (strncmp(res->name, "blk", 3) == 0) { 9318c2ecf20Sopenharmony_ci /* 9328c2ecf20Sopenharmony_ci * If a BLK allocation overlaps the start of 9338c2ecf20Sopenharmony_ci * PMEM the entire interleave set may now only 9348c2ecf20Sopenharmony_ci * be used for BLK. 9358c2ecf20Sopenharmony_ci */ 9368c2ecf20Sopenharmony_ci blk_start = map_start; 9378c2ecf20Sopenharmony_ci } else 9388c2ecf20Sopenharmony_ci busy += end - start + 1; 9398c2ecf20Sopenharmony_ci } else if (map_start > start && map_start < end) { 9408c2ecf20Sopenharmony_ci /* total eclipse of the mapping */ 9418c2ecf20Sopenharmony_ci busy += nd_mapping->size; 9428c2ecf20Sopenharmony_ci blk_start = map_start; 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci *overlap = map_end + 1 - blk_start; 9478c2ecf20Sopenharmony_ci available = blk_start - map_start; 9488c2ecf20Sopenharmony_ci if (busy < available) 9498c2ecf20Sopenharmony_ci return ALIGN_DOWN(available - busy, align); 9508c2ecf20Sopenharmony_ci return 0; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci err: 9538c2ecf20Sopenharmony_ci nd_dbg_dpa(nd_region, ndd, res, "%s\n", reason); 9548c2ecf20Sopenharmony_ci return 0; 9558c2ecf20Sopenharmony_ci} 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_civoid nvdimm_free_dpa(struct nvdimm_drvdata *ndd, struct resource *res) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev)); 9608c2ecf20Sopenharmony_ci kfree(res->name); 9618c2ecf20Sopenharmony_ci __release_region(&ndd->dpa, res->start, resource_size(res)); 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_cistruct resource *nvdimm_allocate_dpa(struct nvdimm_drvdata *ndd, 9658c2ecf20Sopenharmony_ci struct nd_label_id *label_id, resource_size_t start, 9668c2ecf20Sopenharmony_ci resource_size_t n) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci char *name = kmemdup(label_id, sizeof(*label_id), GFP_KERNEL); 9698c2ecf20Sopenharmony_ci struct resource *res; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci if (!name) 9728c2ecf20Sopenharmony_ci return NULL; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev)); 9758c2ecf20Sopenharmony_ci res = __request_region(&ndd->dpa, start, n, name, 0); 9768c2ecf20Sopenharmony_ci if (!res) 9778c2ecf20Sopenharmony_ci kfree(name); 9788c2ecf20Sopenharmony_ci return res; 9798c2ecf20Sopenharmony_ci} 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci/** 9828c2ecf20Sopenharmony_ci * nvdimm_allocated_dpa - sum up the dpa currently allocated to this label_id 9838c2ecf20Sopenharmony_ci * @nvdimm: container of dpa-resource-root + labels 9848c2ecf20Sopenharmony_ci * @label_id: dpa resource name of the form {pmem|blk}-<human readable uuid> 9858c2ecf20Sopenharmony_ci */ 9868c2ecf20Sopenharmony_ciresource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd, 9878c2ecf20Sopenharmony_ci struct nd_label_id *label_id) 9888c2ecf20Sopenharmony_ci{ 9898c2ecf20Sopenharmony_ci resource_size_t allocated = 0; 9908c2ecf20Sopenharmony_ci struct resource *res; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci for_each_dpa_resource(ndd, res) 9938c2ecf20Sopenharmony_ci if (strcmp(res->name, label_id->id) == 0) 9948c2ecf20Sopenharmony_ci allocated += resource_size(res); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci return allocated; 9978c2ecf20Sopenharmony_ci} 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_cistatic int count_dimms(struct device *dev, void *c) 10008c2ecf20Sopenharmony_ci{ 10018c2ecf20Sopenharmony_ci int *count = c; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci if (is_nvdimm(dev)) 10048c2ecf20Sopenharmony_ci (*count)++; 10058c2ecf20Sopenharmony_ci return 0; 10068c2ecf20Sopenharmony_ci} 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ciint nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count) 10098c2ecf20Sopenharmony_ci{ 10108c2ecf20Sopenharmony_ci int count = 0; 10118c2ecf20Sopenharmony_ci /* Flush any possible dimm registration failures */ 10128c2ecf20Sopenharmony_ci nd_synchronize(); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci device_for_each_child(&nvdimm_bus->dev, &count, count_dimms); 10158c2ecf20Sopenharmony_ci dev_dbg(&nvdimm_bus->dev, "count: %d\n", count); 10168c2ecf20Sopenharmony_ci if (count != dimm_count) 10178c2ecf20Sopenharmony_ci return -ENXIO; 10188c2ecf20Sopenharmony_ci return 0; 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvdimm_bus_check_dimm_count); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_civoid __exit nvdimm_devs_exit(void) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci ida_destroy(&dimm_ida); 10258c2ecf20Sopenharmony_ci} 1026