@@ -23,6 +23,7 @@ libndctl_la_SOURCES =\
hpe1.c \
msft.c \
hyperv.c \
+ papr_scm.c \
ars.c \
firmware.c \
libndctl.c \
@@ -799,6 +799,28 @@ static void parse_nfit_mem_flags(struct ndctl_dimm *dimm, char *flags)
ndctl_dimm_get_devname(dimm), flags);
}
+static void parse_papr_scm_flags(struct ndctl_dimm *dimm, char *flags)
+{
+ char *start, *end;
+
+ start = flags;
+ while ((end = strchr(start, ' '))) {
+ *end = '\0';
+ if (strcmp(start, "not_armed") == 0)
+ dimm->flags.f_arm = 1;
+ else if (strcmp(start, "flush_fail") == 0)
+ dimm->flags.f_flush = 1;
+ else if (strcmp(start, "restore_fail") == 0)
+ dimm->flags.f_restore = 1;
+ else if (strcmp(start, "smart_notify") == 0)
+ dimm->flags.f_smart = 1;
+ start = end + 1;
+ }
+ if (end != start)
+ dbg(ndctl_dimm_get_ctx(dimm), "%s: %s\n",
+ ndctl_dimm_get_devname(dimm), flags);
+}
+
static void parse_dimm_flags(struct ndctl_dimm *dimm, char *flags)
{
char *start, *end;
@@ -856,6 +878,12 @@ static void *add_bus(void *parent, int id, const char *ctl_base)
bus->revision = strtoul(buf, NULL, 0);
}
+ sprintf(path, "%s/device/of_node/compatible", ctl_base);
+ if (sysfs_read_attr(ctx, path, buf) < 0)
+ bus->has_of_node = 0;
+ else
+ bus->has_of_node = 1;
+
sprintf(path, "%s/device/nfit/dsm_mask", ctl_base);
if (sysfs_read_attr(ctx, path, buf) < 0)
bus->nfit_dsm_mask = 0;
@@ -964,6 +992,10 @@ NDCTL_EXPORT int ndctl_bus_has_nfit(struct ndctl_bus *bus)
return bus->has_nfit;
}
+NDCTL_EXPORT int ndctl_bus_has_of_node(struct ndctl_bus *bus)
+{
+ return bus->has_of_node;
+}
/**
* ndctl_bus_get_major - nd bus character device major number
* @bus: ndctl_bus instance returned from ndctl_bus_get_{first|next}
@@ -1441,6 +1473,47 @@ static int ndctl_bind(struct ndctl_ctx *ctx, struct kmod_module *module,
static int ndctl_unbind(struct ndctl_ctx *ctx, const char *devpath);
static struct kmod_module *to_module(struct ndctl_ctx *ctx, const char *alias);
+static int add_papr_dimm(struct ndctl_dimm *dimm, const char *dimm_base)
+{
+ int rc = -ENODEV;
+ char buf[SYSFS_ATTR_SIZE];
+ struct ndctl_ctx *ctx = dimm->bus->ctx;
+ char *path = calloc(1, strlen(dimm_base) + 100);
+
+ dbg(ctx, "Probing of_pmem dimm %d at %s\n", dimm->id, dimm_base);
+
+ if (!path)
+ return -ENOMEM;
+
+ sprintf(path, "%s/../of_node/compatible", dimm_base);
+ if (sysfs_read_attr(ctx, path, buf) < 0)
+ goto out;
+
+
+ dbg(ctx, "Compatible of_pmem dimm %d at %s\n", dimm->id, buf);
+ /* construct path to the papr compatible dimm flags file */
+ sprintf(path, "%s/papr/flags", dimm_base);
+
+ if (strcmp(buf, "ibm,pmemory") == 0 &&
+ sysfs_read_attr(ctx, path, buf) == 0) {
+
+ dbg(ctx, "Found papr_scm dimm %d at %s\n", dimm->id, buf);
+ dimm->cmd_family = NVDIMM_FAMILY_PAPR_SCM;
+
+ /* Parse dimm flags */
+ parse_papr_scm_flags(dimm, buf);
+
+ /* Allocate monitor mode fd */
+ dimm->health_eventfd = open(path, O_RDONLY|O_CLOEXEC);
+ rc = 0;
+ }
+ /* Ignore dimm if unknown type */
+
+ out:
+ free(path);
+ return rc;
+}
+
static int add_nfit_dimm(struct ndctl_dimm *dimm, const char *dimm_base)
{
int i, rc = -1;
@@ -1619,6 +1692,8 @@ static void *add_dimm(void *parent, int id, const char *dimm_base)
if (ndctl_bus_has_nfit(bus)) {
dimm->formats = formats;
rc = add_nfit_dimm(dimm, dimm_base);
+ } else if (ndctl_bus_has_of_node(bus)) {
+ rc = add_papr_dimm(dimm, dimm_base);
}
if (rc == -ENODEV) {
@@ -1636,6 +1711,9 @@ static void *add_dimm(void *parent, int id, const char *dimm_base)
dimm->ops = msft_dimm_ops;
if (dimm->cmd_family == NVDIMM_FAMILY_HYPERV)
dimm->ops = hyperv_dimm_ops;
+ if (dimm->cmd_family == NVDIMM_FAMILY_PAPR_SCM)
+ dimm->ops = papr_scm_dimm_ops;
+
out:
if (rc) {
err(ctx, "Unable to probe dimm:%d. Err:%d\n", id, rc);
new file mode 100644
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 IBM Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <util/log.h>
+#include <ndctl.h>
+#include <ndctl/libndctl.h>
+#include <lib/private.h>
+
+static bool papr_cmd_is_supported(struct ndctl_dimm *dimm, int cmd)
+{
+ /* Handle this separately to support monitor mode */
+ if (cmd == ND_CMD_SMART)
+ return true;
+
+ return !!(dimm->cmd_mask & (1ULL << cmd));
+}
+
+struct ndctl_dimm_ops * const papr_scm_dimm_ops = &(struct ndctl_dimm_ops) {
+ .cmd_is_supported = papr_cmd_is_supported,
+};
@@ -167,6 +167,7 @@ struct ndctl_bus {
int dimms_init;
int regions_init;
int has_nfit;
+ int has_of_node;
char *bus_path;
char *bus_buf;
size_t buf_len;
@@ -352,6 +353,7 @@ extern struct ndctl_dimm_ops * const intel_dimm_ops;
extern struct ndctl_dimm_ops * const hpe1_dimm_ops;
extern struct ndctl_dimm_ops * const msft_dimm_ops;
extern struct ndctl_dimm_ops * const hyperv_dimm_ops;
+extern struct ndctl_dimm_ops * const papr_scm_dimm_ops;
static inline struct ndctl_bus *cmd_to_bus(struct ndctl_cmd *cmd)
{
@@ -119,6 +119,7 @@ struct ndctl_bus *ndctl_bus_get_next(struct ndctl_bus *bus);
bus = ndctl_bus_get_next(bus))
struct ndctl_ctx *ndctl_bus_get_ctx(struct ndctl_bus *bus);
int ndctl_bus_has_nfit(struct ndctl_bus *bus);
+int ndctl_bus_has_of_node(struct ndctl_bus *bus);
unsigned int ndctl_bus_get_major(struct ndctl_bus *bus);
unsigned int ndctl_bus_get_minor(struct ndctl_bus *bus);
const char *ndctl_bus_get_devname(struct ndctl_bus *bus);
@@ -263,6 +263,7 @@ struct nd_cmd_pkg {
#define NVDIMM_FAMILY_HPE2 2
#define NVDIMM_FAMILY_MSFT 3
#define NVDIMM_FAMILY_HYPERV 4
+#define NVDIMM_FAMILY_PAPR_SCM 5
#define ND_IOCTL_CALL _IOWR(ND_IOCTL, ND_CMD_CALL,\
struct nd_cmd_pkg)
Add necessary scaffolding in libndctl for dimms that support papr_scm specification[1]. Since there can be platforms that support Open-Firmware[2] but not the papr_scm specification, hence the changes proposed first add support for probing if the dimm bus supports Open-Firmware. This is done via querying for sysfs attribute 'of_node' in dimm device sysfs directory. If available newly introduced member 'struct ndctl_bus.has_of_node' is set. During the probe of the dimm and execution of add_dimm(), the newly introduced add_papr_dimm() is called if dimm bus reports supports Open-Firmware. Function add_papr_dimm() queries the 'compatible' device tree attribute and based on its value assign NVDIMM_FAMILY_PAPR_SCM to the dimm command family. In future, based on the contents of 'compatible' attribute more of_pmem dimm families can be queried. We also add support for parsing the dimm flags for NVDIMM_FAMILY_PAPR_SCM supporting nvdimms as described at [3]. A newly introduced function parse_papr_scm_flags() reads the contents of this flag file and sets appropriate flag bits in 'struct ndctl_dimm.flags'. Also we advertise support for monitor mode by allocating a file descriptor to the dimm 'flags' file and assigning it to 'struct ndctl_dimm.health_event_fd'. The dimm-ops implementation for NVDIMM_FAMILY_PAPR_SCM is available in global variable 'papr_scm_dimm_ops' which points to skeleton implementation in newly introduced file 'lib/papr_scm.c'. References: [1] Documentation/powerpc/papr_hcalls.rst https://lore.kernel.org/linux-nvdimm/20200420070711.223545-2-vaibhav@linux.ibm.com/ [2] https://en.wikipedia.org/wiki/Open_Firmware [3] Documentation/ABI/testing/sysfs-bus-papr-scm https://lore.kernel.org/linux-nvdimm/20200420070711.223545-3-vaibhav@linux.ibm.com/ Signed-off-by: Vaibhav Jain <vaibhav@linux.ibm.com> --- Changelog: v3..v4: * None v2..v3: * Renamed add_of_pmem() to add_papr_dimm() [ Aneesh ] v1..v2: * Squashed the patch to parse dimm flags * Updated the dimm flags parsing to add case for 'restore_fail' and 'flush_fail'. * Renamed parse_of_pmem_flags() to parse_papr_scm_flags(). * Updated the path to dimm flags file to 'papr/flags'. * Introduced 'papr_scm.c' file in this patch rather than later in the patch series. * Update add_of_pmem_dimm() to parse dimm flags and enable monitoring only if 'ibm,pmemory' compatible nvdimm is found. --- ndctl/lib/Makefile.am | 1 + ndctl/lib/libndctl.c | 78 +++++++++++++++++++++++++++++++++++++++++++ ndctl/lib/papr_scm.c | 32 ++++++++++++++++++ ndctl/lib/private.h | 2 ++ ndctl/libndctl.h | 1 + ndctl/ndctl.h | 1 + 6 files changed, 115 insertions(+) create mode 100644 ndctl/lib/papr_scm.c