[PATCH v3] tests/nvdimm/ndtest: Simulate nvdimm health, DSC and smart-inject
Shivaprasad G Bhat
sbhat at linux.ibm.com
Mon Apr 18 18:59:38 AEST 2022
The 'papr_scm' module and 'papr' implementation in libndctl supports
PDSMs for reporting PAPR NVDIMM health, dirty-shutdown-count and
injecting smart-errors. This patch adds support for those PDSMs in
ndtest module so that PDSM specific paths in libndctl can be exercised.
Signed-off-by: Shivaprasad G Bhat <sbhat at linux.ibm.com>
Signed-off-by: Vaibhav Jain <vaibhav at linux.ibm.com>
---
Changelog:
Since v2:
Link: https://patchwork.kernel.org/project/linux-nvdimm/patch/163454437514.431245.15482985237822269917.stgit@lep8c.aus.stglabs.ibm.com/
* Made it like v1 which had the patches based on the moved header files.
So, this patch depends on the patch moving the header files posted at -
https://patchwork.kernel.org/project/linux-nvdimm/patch/165025666388.2927278.9540058958498766114.stgit@lep8c.aus.stglabs.ibm.com/
Since v1:
Link: https://patchwork.kernel.org/project/linux-nvdimm/list/?series=521767
* Removed the dependency on a header movement patch
tools/testing/nvdimm/test/ndtest.c | 146 ++++++++++++++++++++++++++++++++++++
tools/testing/nvdimm/test/ndtest.h | 7 ++
2 files changed, 153 insertions(+)
diff --git a/tools/testing/nvdimm/test/ndtest.c b/tools/testing/nvdimm/test/ndtest.c
index 5eb946a02c95..b07cbdd1952d 100644
--- a/tools/testing/nvdimm/test/ndtest.c
+++ b/tools/testing/nvdimm/test/ndtest.c
@@ -50,6 +50,10 @@ static struct ndtest_dimm dimm_group1[] = {
.uuid_str = "1e5c75d2-b618-11ea-9aa3-507b9ddc0f72",
.physical_id = 0,
.num_formats = 2,
+ .flags = PAPR_PMEM_HEALTH_NON_CRITICAL,
+ .extension_flags = PDSM_DIMM_DSC_VALID | PDSM_DIMM_HEALTH_RUN_GAUGE_VALID,
+ .dimm_fuel_gauge = 95,
+ .dimm_dsc = 42,
},
{
.size = DIMM_SIZE,
@@ -57,6 +61,10 @@ static struct ndtest_dimm dimm_group1[] = {
.uuid_str = "1c4d43ac-b618-11ea-be80-507b9ddc0f72",
.physical_id = 1,
.num_formats = 2,
+ .flags = PAPR_PMEM_HEALTH_NON_CRITICAL,
+ .extension_flags = PDSM_DIMM_DSC_VALID | PDSM_DIMM_HEALTH_RUN_GAUGE_VALID,
+ .dimm_fuel_gauge = 95,
+ .dimm_dsc = 42,
},
{
.size = DIMM_SIZE,
@@ -64,6 +72,10 @@ static struct ndtest_dimm dimm_group1[] = {
.uuid_str = "a9f17ffc-b618-11ea-b36d-507b9ddc0f72",
.physical_id = 2,
.num_formats = 2,
+ .flags = PAPR_PMEM_HEALTH_NON_CRITICAL,
+ .extension_flags = PDSM_DIMM_DSC_VALID | PDSM_DIMM_HEALTH_RUN_GAUGE_VALID,
+ .dimm_fuel_gauge = 95,
+ .dimm_dsc = 42,
},
{
.size = DIMM_SIZE,
@@ -71,6 +83,10 @@ static struct ndtest_dimm dimm_group1[] = {
.uuid_str = "b6b83b22-b618-11ea-8aae-507b9ddc0f72",
.physical_id = 3,
.num_formats = 2,
+ .flags = PAPR_PMEM_HEALTH_NON_CRITICAL,
+ .extension_flags = PDSM_DIMM_DSC_VALID | PDSM_DIMM_HEALTH_RUN_GAUGE_VALID,
+ .dimm_fuel_gauge = 95,
+ .dimm_dsc = 42,
},
{
.size = DIMM_SIZE,
@@ -237,6 +253,103 @@ static int ndtest_get_config_size(struct ndtest_dimm *dimm, unsigned int buf_len
return 0;
}
+static int ndtest_pdsm_health(struct ndtest_dimm *dimm,
+ union nd_pdsm_payload *payload,
+ unsigned int buf_len)
+{
+ struct nd_papr_pdsm_health *health = &payload->health;
+
+ if (buf_len < sizeof(health))
+ return -EINVAL;
+
+ health->extension_flags = 0;
+ health->dimm_unarmed = !!(dimm->flags & PAPR_PMEM_UNARMED_MASK);
+ health->dimm_bad_shutdown = !!(dimm->flags & PAPR_PMEM_BAD_SHUTDOWN_MASK);
+ health->dimm_bad_restore = !!(dimm->flags & PAPR_PMEM_BAD_RESTORE_MASK);
+ health->dimm_health = PAPR_PDSM_DIMM_HEALTHY;
+
+ if (dimm->flags & PAPR_PMEM_HEALTH_FATAL)
+ health->dimm_health = PAPR_PDSM_DIMM_FATAL;
+ else if (dimm->flags & PAPR_PMEM_HEALTH_CRITICAL)
+ health->dimm_health = PAPR_PDSM_DIMM_CRITICAL;
+ else if (dimm->flags & PAPR_PMEM_HEALTH_UNHEALTHY ||
+ dimm->flags & PAPR_PMEM_HEALTH_NON_CRITICAL)
+ health->dimm_health = PAPR_PDSM_DIMM_UNHEALTHY;
+
+ health->extension_flags = 0;
+ if (dimm->extension_flags & PDSM_DIMM_HEALTH_RUN_GAUGE_VALID) {
+ health->dimm_fuel_gauge = dimm->dimm_fuel_gauge;
+ health->extension_flags |= PDSM_DIMM_HEALTH_RUN_GAUGE_VALID;
+ }
+ if (dimm->extension_flags & PDSM_DIMM_DSC_VALID) {
+ health->dimm_dsc = dimm->dimm_dsc;
+ health->extension_flags |= PDSM_DIMM_DSC_VALID;
+ }
+
+ return 0;
+}
+
+static void smart_notify(struct ndtest_dimm *dimm)
+{
+ struct device *bus = dimm->dev->parent;
+
+ if (!(dimm->flags & PAPR_PMEM_HEALTH_NON_CRITICAL) ||
+ (dimm->flags & PAPR_PMEM_BAD_SHUTDOWN_MASK)) {
+ device_lock(bus);
+ /* send smart notification */
+ if (dimm->notify_handle)
+ sysfs_notify_dirent(dimm->notify_handle);
+ device_unlock(bus);
+ }
+}
+
+static int ndtest_pdsm_smart_inject(struct ndtest_dimm *dimm,
+ union nd_pdsm_payload *payload,
+ unsigned int buf_len)
+{
+ struct nd_papr_pdsm_smart_inject *inj = &payload->smart_inject;
+
+ if (buf_len < sizeof(inj))
+ return -EINVAL;
+
+ if (inj->flags & PDSM_SMART_INJECT_HEALTH_FATAL) {
+ if (inj->fatal_enable)
+ dimm->flags |= PAPR_PMEM_HEALTH_FATAL;
+ else
+ dimm->flags &= ~PAPR_PMEM_HEALTH_FATAL;
+ }
+ if (inj->flags & PDSM_SMART_INJECT_BAD_SHUTDOWN) {
+ if (inj->unsafe_shutdown_enable)
+ dimm->flags |= PAPR_PMEM_SHUTDOWN_DIRTY;
+ else
+ dimm->flags &= ~PAPR_PMEM_SHUTDOWN_DIRTY;
+ }
+ smart_notify(dimm);
+
+ return 0;
+}
+
+static int ndtest_dimm_cmd_call(struct ndtest_dimm *dimm, unsigned int buf_len,
+ void *buf)
+{
+ struct nd_cmd_pkg *call_pkg = buf;
+ unsigned int len = call_pkg->nd_size_in + call_pkg->nd_size_out;
+ struct nd_pkg_pdsm *pdsm = (struct nd_pkg_pdsm *) call_pkg->nd_payload;
+ union nd_pdsm_payload *payload = &(pdsm->payload);
+ unsigned int func = call_pkg->nd_command;
+
+ switch (func) {
+ case PAPR_PDSM_HEALTH:
+ return ndtest_pdsm_health(dimm, payload, len);
+ case PAPR_PDSM_SMART_INJECT:
+ return ndtest_pdsm_smart_inject(dimm, payload, len);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int ndtest_ctl(struct nvdimm_bus_descriptor *nd_desc,
struct nvdimm *nvdimm, unsigned int cmd, void *buf,
unsigned int buf_len, int *cmd_rc)
@@ -266,6 +379,9 @@ static int ndtest_ctl(struct nvdimm_bus_descriptor *nd_desc,
case ND_CMD_SET_CONFIG_DATA:
*cmd_rc = ndtest_config_set(dimm, buf_len, buf);
break;
+ case ND_CMD_CALL:
+ *cmd_rc = ndtest_dimm_cmd_call(dimm, buf_len, buf);
+ break;
default:
return -EINVAL;
}
@@ -482,6 +598,8 @@ static void put_dimms(void *data)
for (i = 0; i < p->config->dimm_count; i++)
if (p->config->dimms[i].dev) {
+ if (p->config->dimms[i].notify_handle)
+ sysfs_put(p->config->dimms[i].notify_handle);
device_unregister(p->config->dimms[i].dev);
p->config->dimms[i].dev = NULL;
}
@@ -694,6 +812,16 @@ static ssize_t flags_show(struct device *dev,
}
static DEVICE_ATTR_RO(flags);
+#define PAPR_PMEM_DIMM_CMD_MASK ((1U << PAPR_PDSM_HEALTH) | (1U << PAPR_PDSM_SMART_INJECT))
+
+static ssize_t dsm_mask_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%#x\n", PAPR_PMEM_DIMM_CMD_MASK);
+}
+
+static DEVICE_ATTR_RO(dsm_mask);
+
static struct attribute *ndtest_nvdimm_attributes[] = {
&dev_attr_nvdimm_show_handle.attr,
&dev_attr_vendor.attr,
@@ -705,6 +833,7 @@ static struct attribute *ndtest_nvdimm_attributes[] = {
&dev_attr_format.attr,
&dev_attr_format1.attr,
&dev_attr_flags.attr,
+ &dev_attr_dsm_mask.attr,
NULL,
};
@@ -724,6 +853,7 @@ static int ndtest_dimm_register(struct ndtest_priv *priv,
{
struct device *dev = &priv->pdev.dev;
unsigned long dimm_flags = dimm->flags;
+ struct kernfs_node *papr_kernfs;
if (dimm->num_formats > 1)
set_bit(NDD_LABELING, &dimm_flags);
@@ -748,6 +878,20 @@ static int ndtest_dimm_register(struct ndtest_priv *priv,
return -ENOMEM;
}
+ nd_synchronize();
+
+ papr_kernfs = sysfs_get_dirent(nvdimm_kobj(dimm->nvdimm)->sd, "papr");
+ if (!papr_kernfs) {
+ pr_err("Could not initialize the notifier handle\n");
+ return 0;
+ }
+
+ dimm->notify_handle = sysfs_get_dirent(papr_kernfs, "flags");
+ sysfs_put(papr_kernfs);
+ if (!dimm->notify_handle) {
+ pr_err("Could not initialize the notifier handle\n");
+ return 0;
+ }
return 0;
}
@@ -819,6 +963,8 @@ static int ndtest_bus_register(struct ndtest_priv *p)
p->bus_desc.provider_name = NULL;
p->bus_desc.attr_groups = ndtest_attribute_groups;
+ set_bit(NVDIMM_FAMILY_PAPR, &p->bus_desc.dimm_family_mask);
+
p->bus = nvdimm_bus_register(&p->pdev.dev, &p->bus_desc);
if (!p->bus) {
dev_err(&p->pdev.dev, "Error creating nvdimm bus %pOF\n", p->dn);
diff --git a/tools/testing/nvdimm/test/ndtest.h b/tools/testing/nvdimm/test/ndtest.h
index 8f27ad6f7319..e18b3b006fa2 100644
--- a/tools/testing/nvdimm/test/ndtest.h
+++ b/tools/testing/nvdimm/test/ndtest.h
@@ -49,6 +49,13 @@ struct ndtest_dimm {
int id;
int fail_cmd_code;
u8 no_alias;
+
+ struct kernfs_node *notify_handle;
+
+ /* SMART Health information */
+ u32 extension_flags;
+ u16 dimm_fuel_gauge;
+ u64 dimm_dsc;
};
struct ndtest_mapping {
More information about the Linuxppc-dev
mailing list