The 'papr_scm' module and 'papr' implementation in libndctl supports
PDSMs for reporting PAPR NVDIMM health, its dirty-shutdown-count and
injecting smart-error. 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 <sb...@linux.ibm.com>
---
The patch depends on the PAPR PDSM smart-inject payload definitions
added with the patch - 
https://www.mail-archive.com/linuxppc-dev@lists.ozlabs.org/msg191337.html

 tools/testing/nvdimm/test/ndtest.c |  149 ++++++++++++++++++++++++++++++++++++
 tools/testing/nvdimm/test/ndtest.h |    7 ++
 2 files changed, 156 insertions(+)

diff --git a/tools/testing/nvdimm/test/ndtest.c 
b/tools/testing/nvdimm/test/ndtest.c
index 00ec2c213061..6622e8adbd11 100644
--- a/tools/testing/nvdimm/test/ndtest.c
+++ b/tools/testing/nvdimm/test/ndtest.c
@@ -14,6 +14,7 @@
 #include <linux/printk.h>
 #include <linux/seq_buf.h>
 #include <linux/papr_scm.h>
+#include <uapi/linux/papr_pdsm.h>
 
 #include "../watermark.h"
 #include "nfit_test.h"
@@ -49,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,
@@ -56,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,
@@ -63,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,
@@ -70,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,
@@ -297,6 +314,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)
@@ -326,6 +440,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;
        }
@@ -615,6 +732,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;
                }
@@ -827,6 +946,18 @@ 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,
@@ -838,6 +969,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,
 };
 
@@ -857,6 +989,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_ALIASING, &dimm_flags);
@@ -883,6 +1016,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;
 }
 
@@ -954,6 +1101,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 {


Reply via email to