@@ -162,6 +162,25 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
)
AM_CONDITIONAL([ENABLE_CLEAR_ERROR], [test "x$enable_clear_err" = "xyes"])
+AC_MSG_CHECKING([for TRANSLATE SPA support])
+AC_LANG(C)
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+ #ifdef HAVE_NDCTL_H
+ #include <linux/ndctl.h>
+ #else
+ #include "ndctl/ndctl.h"
+ #endif
+ ]], [[
+ int x = ND_CMD_TRANS_SPA;
+ ]]
+ )], [AC_MSG_RESULT([yes])
+ enable_trans_spa=yes
+ AC_DEFINE([HAVE_NDCTL_TRANS_SPA], [1],
+ [Define to 1 if ndctl.h has TRANSLATE SPA support.])
+ ], [AC_MSG_RESULT([no])]
+)
+AM_CONDITIONAL([ENABLE_TRANS_SPA], [test "x$enable_trans_spa" = "xyes"])
+
AC_MSG_CHECKING([for device DAX support])
AC_LANG(C)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@@ -196,6 +196,7 @@ struct ndctl_cmd {
#ifdef HAVE_NDCTL_CLEAR_ERROR
struct nd_cmd_clear_error clear_err[0];
#endif
+ struct nd_cmd_trans_spa trans_spa[0];
struct ndn_pkg_hpe1 hpe1[0];
struct ndn_pkg_msft msft[0];
struct nd_cmd_smart smart[0];
@@ -250,6 +251,12 @@ static const int nd_cmd_clear_error = ND_CMD_CLEAR_ERROR;
static const int nd_cmd_clear_error;
#endif
+#ifdef HAVE_NDCTL_TRANS_SPA
+static const int nd_cmd_trans_spa = ND_CMD_TRANS_SPA;
+#else
+static const int nd_cmd_trans_spa;
+#endif
+
static inline struct ndctl_bus *cmd_to_bus(struct ndctl_cmd *cmd)
{
if (cmd->dimm)
@@ -744,7 +744,9 @@ static int to_dsm_index(const char *name, int dimm)
end_cmd = ND_CMD_CALL;
cmd_name_fn = nvdimm_cmd_name;
} else {
- end_cmd = nd_cmd_clear_error;
+ end_cmd = nd_cmd_trans_spa;
+ if (!end_cmd)
+ end_cmd = nd_cmd_clear_error;
if (!end_cmd)
end_cmd = nd_cmd_ars_status;
cmd_name_fn = nvdimm_bus_cmd_name;
@@ -1943,6 +1945,102 @@ NDCTL_EXPORT struct badblock *ndctl_region_get_first_badblock(struct ndctl_regio
return ndctl_region_get_next_badblock(region);
}
+#ifdef HAVE_NDCTL_TRANS_SPA
+NDCTL_EXPORT int ndctl_bus_has_trans_spa(struct ndctl_bus *bus)
+{
+ if (!bus)
+ return 0;
+
+ return ndctl_bus_is_cmd_supported(bus, ND_CMD_TRANS_SPA);
+}
+
+static struct ndctl_cmd *ndctl_bus_cmd_new_trans_spa(struct ndctl_bus *bus)
+{
+ struct ndctl_cmd *cmd;
+ size_t size, spa_length;
+
+ spa_length = sizeof(struct nd_cmd_trans_spa)
+ + sizeof(struct nd_nvdimm_device) * ND_MIRROR_MAX_WAY;
+ size = sizeof(*cmd) + spa_length;
+ cmd = calloc(1, size);
+ if (!cmd)
+ return NULL;
+
+ cmd->bus = bus;
+ ndctl_cmd_ref(cmd);
+ cmd->type = ND_CMD_TRANS_SPA;
+ cmd->size = size;
+ cmd->status = 1;
+ cmd->firmware_status = &cmd->trans_spa->status;
+ cmd->trans_spa->trans_length = spa_length;
+
+ return cmd;
+}
+
+static int ndctl_bus_cmd_get_trans_spa(struct ndctl_cmd *cmd,
+ unsigned int *handles, unsigned long long *dpas)
+{
+ int i;
+ int num_nvdimms;
+
+ if (cmd->trans_spa->status == ND_TRANS_SPA_STATUS_INVALID_SPA)
+ return -EINVAL;
+
+ num_nvdimms = cmd->trans_spa->num_nvdimms;
+ for (i = 0; i < num_nvdimms; i++) {
+ handles[i] = cmd->trans_spa->devices[i].nfit_device_handle;
+ dpas[i] = cmd->trans_spa->devices[i].dpa;
+ }
+
+ ndctl_cmd_unref(cmd);
+ return 0;
+}
+
+NDCTL_EXPORT int ndctl_bus_cmd_trans_spa(struct ndctl_bus *bus,
+ unsigned long long addr, unsigned int *handles, unsigned long long *dpas)
+{
+
+ struct ndctl_cmd *cmd;
+ int rc;
+
+ cmd = ndctl_bus_cmd_new_trans_spa(bus);
+ cmd->trans_spa->spa = addr;
+
+ rc = ndctl_cmd_submit(cmd);
+ if (rc) {
+ ndctl_cmd_unref(cmd);
+ return rc;
+ }
+
+ rc = ndctl_bus_cmd_get_trans_spa(cmd, handles, dpas);
+
+ return rc;
+}
+
+NDCTL_EXPORT int ndctl_dimms_get_by_spa(struct ndctl_bus *bus, unsigned long long spa,
+ struct ndctl_dimm **dimms)
+{
+ int i, rc;
+ unsigned int handles[ND_MIRROR_MAX_WAY];
+ unsigned long long dpas[ND_MIRROR_MAX_WAY];
+
+ if (!bus || !spa || !dimms)
+ return -EINVAL;
+
+ memset(handles, 0, sizeof(handles));
+ memset(dpas, 0, sizeof(dpas));
+
+ rc = ndctl_bus_cmd_trans_spa(bus, spa, &handles[0], &dpas[0]);
+ if (rc)
+ return rc;
+
+ for (i = 0; i < ND_MIRROR_MAX_WAY && handles[i]; i++)
+ dimms[i] = ndctl_dimm_get_by_handle(bus, handles[i]);
+
+ return 0;
+}
+#endif /* HAVE_NDCTL_TRANS_SPA */
+
static struct nd_cmd_vendor_tail *to_vendor_tail(struct ndctl_cmd *cmd)
{
struct nd_cmd_vendor_tail *tail = (struct nd_cmd_vendor_tail *)
@@ -2314,6 +2412,9 @@ static int to_ioctl_cmd(int cmd, int dimm)
#ifdef HAVE_NDCTL_CLEAR_ERROR
case ND_CMD_CLEAR_ERROR: return ND_IOCTL_CLEAR_ERROR;
#endif
+#ifdef HAVE_NDCTL_TRANS_SPA
+ case ND_CMD_TRANS_SPA: return ND_CMD_TRANS_SPA;
+#endif
default:
return 0;
};
@@ -36,6 +36,9 @@ global:
ndctl_bus_get_provider;
ndctl_bus_get_ctx;
ndctl_bus_wait_probe;
+ ndctl_bus_has_trans_spa;
+ ndctl_bus_cmd_trans_spa;
+ ndctl_dimms_get_by_spa;
ndctl_dimm_get_first;
ndctl_dimm_get_next;
ndctl_dimm_get_handle;
@@ -347,6 +347,29 @@ static inline unsigned int ndctl_cmd_smart_threshold_get_spares(
}
#endif
+#if HAVE_NDCTL_TRANS_SPA == 1
+int ndctl_bus_has_trans_spa(struct ndctl_bus *bus);
+int ndctl_bus_cmd_trans_spa(struct ndctl_bus *bus,
+ unsigned long long addr, unsigned int *handles, unsigned long long *dpas);
+int ndctl_dimms_get_by_spa(struct ndctl_bus *bus,
+ unsigned long long spa, struct ndctl_dimm **dimms);
+#else
+static inline int ndctl_bus_has_trans_spa(struct ndctl_bus *bus)
+{
+ return 0;
+}
+static inline int ndctl_bus_cmd_trans_spa(struct ndctl_bus *bus,
+ unsigned long long addr, unsigned int *handles, unsigned long long *dpas)
+{
+ return 0;
+}
+static inline int ndctl_dimms_get_by_spa(struct ndctl_bus *bus,
+ unsigned long long spa, struct ndctl_dimm **dimms)
+{
+ return 0;
+}
+#endif
+
struct ndctl_cmd *ndctl_dimm_cmd_new_vendor_specific(struct ndctl_dimm *dimm,
unsigned int opcode, size_t input_size, size_t output_size);
ssize_t ndctl_cmd_vendor_set_input(struct ndctl_cmd *cmd, void *buf,
@@ -35,6 +35,9 @@ struct nd_cmd_smart {
#define ND_SMART_CRITICAL_HEALTH (1 << 1)
#define ND_SMART_FATAL_HEALTH (1 << 2)
+#define ND_MIRROR_MAX_WAY 4 /* XXX: assume max mirroring way */
+#define ND_TRANS_SPA_STATUS_INVALID_SPA 2
+
struct nd_smart_payload {
__u32 flags;
__u8 reserved0[4];
@@ -190,6 +193,7 @@ enum {
ND_CMD_ARS_START = 2,
ND_CMD_ARS_STATUS = 3,
ND_CMD_CLEAR_ERROR = 4,
+ ND_CMD_TRANS_SPA = 5,
/* per-dimm commands */
ND_CMD_SMART = 1,
@@ -217,6 +221,7 @@ static __inline__ const char *nvdimm_bus_cmd_name(unsigned cmd)
[ND_CMD_ARS_START] = "ars_start",
[ND_CMD_ARS_STATUS] = "ars_status",
[ND_CMD_CLEAR_ERROR] = "clear_error",
+ [ND_CMD_TRANS_SPA] = "trans_spa",
[ND_CMD_CALL] = "cmd_call",
};
@@ -280,6 +285,9 @@ static __inline__ const char *nvdimm_cmd_name(unsigned cmd)
#define ND_IOCTL_CLEAR_ERROR _IOWR(ND_IOCTL, ND_CMD_CLEAR_ERROR,\
struct nd_cmd_clear_error)
+#define ND_IOCTL_TRANS_SPA _IOWR(ND_IOCTL, ND_CMD_TRANS_SPA,\
+ struct nd_cmd_trans_spa)
+
#define ND_DEVICE_DIMM 1 /* nd_dimm: container for "config data" */
#define ND_DEVICE_REGION_PMEM 2 /* nd_region: (parent of PMEM namespaces) */
#define ND_DEVICE_REGION_BLK 3 /* nd_region: (parent of BLK namespaces) */
ndctl:libndctl Make interfaces to use Translate SPA. This patch makes 3 new interfaces : - to ask bus has translate SPA feature. - to call translate SPA. - to find DIMM by SPA address. Note) I'm not sure how many buffer should be prepared, because it depends on max # of mirroring way. This patch assume maxmum # is 4 way. Signed-off-by: Yasunori Goto <y-goto@jp.fujitsu.com> --- configure.ac | 19 ++++++++ ndctl/lib/libndctl-private.h | 7 +++ ndctl/lib/libndctl.c | 103 ++++++++++++++++++++++++++++++++++++++++++- ndctl/lib/libndctl.sym | 3 ++ ndctl/libndctl.h.in | 23 ++++++++++ ndctl/ndctl.h | 8 ++++ 6 files changed, 162 insertions(+), 1 deletion(-)