Message ID | 20151110003451.18262.34316.stgit@dwillia2-desk3.amr.corp.intel.com (mailing list archive) |
---|---|
State | Accepted |
Commit | 7dc12d2fc133 |
Headers | show |
Dan Williams <dan.j.williams@intel.com> writes: > Kernel support for the address range scrub (ARS) set of commands was > added in the v4.3 kernel development cycle, while the base libndctl > functionality is based on the kernel provided api as of v4.2. Support > in the library for any functionality past the base v4.2 expectations > needs to be dynamically detected and enabled. > > Add autoconf machinery to do the detection as well as an override to > force use of the local ndctl.h definitions (--enable-local). Move the > ARS routines to their own source file so they can be compiled in/out as > a complete unit. > > This fixes compilation errors like the following: > lib/libndctl.c: In function 'ndctl_bus_cmd_new_ars_start': > lib/libndctl.c:1768:35: error: 'ARS_STATUS_MASK' undeclared (first use in this function) > if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) { It might make more sense to copy the header file from the kernel sources into the ndctl sources. Then, always enable the functionality at build time and auto-detect at runtime. Cheers, Jeff > Cc: Vishal Verma <vishal.l.verma@intel.com> > Reported-by: Robert Elliott <elliott@hpe.com> > Signed-off-by: Dan Williams <dan.j.williams@intel.com> > --- > Makefile.am | 4 + > configure.ac | 36 ++++++- > lib/libndctl-ars.c | 207 ++++++++++++++++++++++++++++++++++++++++ > lib/libndctl-private.h | 60 ++++++++++++ > lib/libndctl.c | 249 ------------------------------------------------ > lib/ndctl/libndctl.h | 49 +++++++++ > lib/test-libndctl.c | 23 ++++ > 7 files changed, 375 insertions(+), 253 deletions(-) > create mode 100644 lib/libndctl-ars.c > > diff --git a/Makefile.am b/Makefile.am > index e5b4b4980100..e998c5766631 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -53,6 +53,10 @@ lib_libndctl_la_SOURCES =\ > lib/libndctl.c > lib_libndctl_la_LIBADD = $(UDEV_LIBS) $(UUID_LIBS) $(KMOD_LIBS) > > +if ENABLE_ARS > +lib_libndctl_la_SOURCES += lib/libndctl-ars.c > +endif > + > bin_PROGRAMS = ndctl > > ndctl_SOURCES = ndctl.c \ > diff --git a/configure.ac b/configure.ac > index deaa94b0338b..c1c0f684e35c 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -73,10 +73,38 @@ PKG_CHECK_MODULES([KMOD], [libkmod]) > PKG_CHECK_MODULES([UDEV], [libudev]) > PKG_CHECK_MODULES([UUID], [uuid]) > > -AC_CHECK_HEADER([linux/ndctl.h], > - [AC_DEFINE([HAVE_NDCTL_H], [1], > - [Define to 1 if you have <linux/ndctl.h>.])], > - []) > +AC_ARG_ENABLE([local], > + AS_HELP_STRING([--enable-local], [build with local ndctl.h @<:@default=system@:>@]), > + [], [enable_local=no]) > + > +AS_IF([test "x$enable_local" = "xyes"], [], [ > + AC_CHECK_HEADER([linux/ndctl.h], [ > + AC_DEFINE([HAVE_NDCTL_H], [1], > + [Define to 1 if you have <linux/ndctl.h>.]) > + ], []) > + ] > +) > + > +# when building against kernel headers check version specific features > +AC_MSG_CHECKING([for ARS support]) > +AC_LANG(C) > +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ > + #ifdef HAVE_NDCTL_H > + #include <linux/ndctl.h> > + #else > + #include "ndctl.h" > + #endif > + ]], [[ > + int x = ARS_STATUS_MASK; > + ]] > + )], [AC_MSG_RESULT([yes]) > + enable_ars=yes > + AC_DEFINE([HAVE_NDCTL_ARS], [1], > + [Define to 1 if ndctl.h has ARS support.]) > + ], [AC_MSG_RESULT([no])] > +) > +AM_CONDITIONAL([ENABLE_ARS], [test "x$enable_ars" = "xyes"]) > + > AC_CHECK_HEADERS_ONCE([linux/version.h]) > > AC_CHECK_FUNCS([ \ > diff --git a/lib/libndctl-ars.c b/lib/libndctl-ars.c > new file mode 100644 > index 000000000000..fd988996501d > --- /dev/null > +++ b/lib/libndctl-ars.c > @@ -0,0 +1,207 @@ > +/* > + * Copyright (c) 2014-2015, Intel 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 <stdlib.h> > +#include <ndctl/libndctl.h> > +#include "libndctl-private.h" > + > +NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_cap(struct ndctl_bus *bus, > + unsigned long long address, unsigned long long len) > +{ > + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); > + struct ndctl_cmd *cmd; > + size_t size; > + > + if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_CAP)) { > + dbg(ctx, "unsupported cmd\n"); > + return NULL; > + } > + > + size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_cap); > + cmd = calloc(1, size); > + if (!cmd) > + return NULL; > + > + cmd->bus = bus; > + ndctl_cmd_ref(cmd); > + cmd->type = ND_CMD_ARS_CAP; > + cmd->size = size; > + cmd->status = 1; > + cmd->firmware_status = &cmd->ars_cap->status; > + cmd->ars_cap->address = address; > + cmd->ars_cap->length = len; > + > + return cmd; > +} > + > +NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_start(struct ndctl_cmd *ars_cap, > + int type) > +{ > + struct ndctl_bus *bus = ars_cap->bus; > + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); > + struct ndctl_cmd *cmd; > + size_t size; > + > + if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_START)) { > + dbg(ctx, "unsupported cmd\n"); > + return NULL; > + } > + if (ars_cap->status != 0) { > + dbg(ctx, "expected sucessfully completed ars_cap command\n"); > + return NULL; > + } > + if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) { > + dbg(ctx, "expected sucessfully completed ars_cap command\n"); > + return NULL; > + } > + if (!(*ars_cap->firmware_status >> ARS_EXT_STATUS_SHIFT & type)) { > + dbg(ctx, "ars_cap does not show requested type as supported\n"); > + return NULL; > + } > + > + size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_start); > + cmd = calloc(1, size); > + if (!cmd) > + return NULL; > + > + cmd->bus = bus; > + ndctl_cmd_ref(cmd); > + cmd->type = ND_CMD_ARS_START; > + cmd->size = size; > + cmd->status = 1; > + cmd->firmware_status = &cmd->ars_start->status; > + cmd->ars_start->address = ars_cap->ars_cap->address; > + cmd->ars_start->length = ars_cap->ars_cap->length; > + cmd->ars_start->type = type; > + > + return cmd; > +} > + > +NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_status(struct ndctl_cmd *ars_cap) > +{ > + struct nd_cmd_ars_cap *ars_cap_cmd = ars_cap->ars_cap; > + struct ndctl_bus *bus = ars_cap->bus; > + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); > + struct ndctl_cmd *cmd; > + size_t size; > + > + if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_CAP)) { > + dbg(ctx, "unsupported cmd\n"); > + return NULL; > + } > + if (ars_cap->status != 0) { > + dbg(ctx, "expected sucessfully completed ars_cap command\n"); > + return NULL; > + } > + if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) { > + dbg(ctx, "expected sucessfully completed ars_cap command\n"); > + return NULL; > + } > + if (ars_cap_cmd->max_ars_out == 0) { > + dbg(ctx, "invalid ars_cap\n"); > + return NULL; > + } > + > + size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_status) + > + ars_cap_cmd->max_ars_out; > + cmd = calloc(1, size); > + if (!cmd) > + return NULL; > + > + cmd->bus = bus; > + ndctl_cmd_ref(cmd); > + cmd->type = ND_CMD_ARS_STATUS; > + cmd->size = size; > + cmd->status = 1; > + cmd->firmware_status = &cmd->ars_status->status; > + > + return cmd; > +} > + > +NDCTL_EXPORT unsigned int ndctl_cmd_ars_cap_get_size(struct ndctl_cmd *ars_cap) > +{ > + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_cap)); > + > + if (ars_cap->type == ND_CMD_ARS_CAP && ars_cap->status == 0) { > + dbg(ctx, "max_ars_out: %d\n", > + ars_cap->ars_cap->max_ars_out); > + return ars_cap->ars_cap->max_ars_out; > + } > + > + dbg(ctx, "invalid ars_cap\n"); > + return 0; > +} > + > +NDCTL_EXPORT unsigned int ndctl_cmd_ars_in_progress(struct ndctl_cmd *ars_stat) > +{ > + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); > + int rc; > + > + if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) { > + rc = (((*ars_stat->firmware_status >> ARS_EXT_STATUS_SHIFT) == 1) ? 1 : 0); > + /* > + * If in-progress, invalidate the ndctl_cmd, so that if we're > + * called again without a fresh ars_stat command, we fail. > + */ > + if (rc) > + ars_stat->status = 1; > + return rc; > + } > + > + dbg(ctx, "invalid ars_status\n"); > + return 0; > +} > + > +NDCTL_EXPORT unsigned int ndctl_cmd_ars_num_records(struct ndctl_cmd *ars_stat) > +{ > + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); > + > + if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) > + return ars_stat->ars_status->num_records; > + > + dbg(ctx, "invalid ars_status\n"); > + return 0; > +} > + > +NDCTL_EXPORT unsigned long long ndctl_cmd_ars_get_record_addr( > + struct ndctl_cmd *ars_stat, unsigned int rec_index) > +{ > + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); > + > + if (rec_index >= ars_stat->ars_status->num_records) { > + dbg(ctx, "invalid record index\n"); > + return 0; > + } > + > + if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) > + return ars_stat->ars_status->records[rec_index].err_address; > + > + dbg(ctx, "invalid ars_status\n"); > + return 0; > +} > + > +NDCTL_EXPORT unsigned long long ndctl_cmd_ars_get_record_len( > + struct ndctl_cmd *ars_stat, unsigned int rec_index) > +{ > + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); > + > + if (rec_index >= ars_stat->ars_status->num_records) { > + dbg(ctx, "invalid record index\n"); > + return 0; > + } > + > + if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) > + return ars_stat->ars_status->records[rec_index].length; > + > + dbg(ctx, "invalid ars_status\n"); > + return 0; > +} > diff --git a/lib/libndctl-private.h b/lib/libndctl-private.h > index 78fbb2d46b42..d3cca9401b45 100644 > --- a/lib/libndctl-private.h > +++ b/lib/libndctl-private.h > @@ -13,11 +13,14 @@ > #ifndef _LIBNDCTL_PRIVATE_H_ > #define _LIBNDCTL_PRIVATE_H_ > > +#include <errno.h> > #include <stdbool.h> > #include <syslog.h> > +#include <string.h> > #include <libudev.h> > #include <libkmod.h> > #include <uuid/uuid.h> > +#include <ccan/array_size/array_size.h> > #ifdef HAVE_NDCTL_H > #include <linux/ndctl.h> > #else > @@ -117,6 +120,63 @@ struct namespace_label { > le32 unused; > }; > > +/** > + * struct ndctl_cmd - device-specific-method (_DSM ioctl) container > + * @dimm: set if the command is relative to a dimm, NULL otherwise > + * @bus: set if the command is relative to a bus (like ARS), NULL otherwise > + * @refcount: reference for passing command buffer around > + * @type: cmd number > + * @size: total size of the ndctl_cmd allocation > + * @status: negative if failed, 0 if success, > 0 if never submitted > + * @firmware_status: NFIT command output status code > + * @iter: iterator for multi-xfer commands > + * @source: source cmd of an inherited iter.total_buf > + * > + * For dynamically sized commands like 'get_config', 'set_config', or > + * 'vendor', @size encompasses the entire buffer for the command input > + * and response output data. > + * > + * A command may only specify one of @source, or @iter.total_buf, not both. > + */ > +enum { > + READ, WRITE, > +}; > +struct ndctl_cmd { > + struct ndctl_dimm *dimm; > + struct ndctl_bus *bus; > + int refcount; > + int type; > + int size; > + int status; > + u32 *firmware_status; > + struct ndctl_cmd_iter { > + u32 *offset; > + u8 *data; /* pointer to the data buffer location in cmd */ > + u32 max_xfer; > + char *total_buf; > + u32 total_xfer; > + int dir; > + } iter; > + struct ndctl_cmd *source; > + union { > + struct nd_cmd_ars_cap ars_cap[0]; > + struct nd_cmd_ars_start ars_start[0]; > + struct nd_cmd_ars_status ars_status[0]; > + struct nd_cmd_get_config_size get_size[0]; > + struct nd_cmd_get_config_data_hdr get_data[0]; > + struct nd_cmd_set_config_hdr set_data[0]; > + struct nd_cmd_vendor_hdr vendor[0]; > + char cmd_buf[0]; > + }; > +}; > + > +static inline struct ndctl_bus *cmd_to_bus(struct ndctl_cmd *cmd) > +{ > + if (cmd->dimm) > + return ndctl_dimm_get_bus(cmd->dimm); > + return cmd->bus; > +} > + > static inline void __attribute__((always_inline, format(printf, 2, 3))) > ndctl_log_null(struct ndctl_ctx *ctx, const char *format, ...) {} > > diff --git a/lib/libndctl.c b/lib/libndctl.c > index 7db5b6013ab1..6ebb4eb3d3cd 100644 > --- a/lib/libndctl.c > +++ b/lib/libndctl.c > @@ -296,56 +296,6 @@ struct ndctl_ctx { > unsigned long timeout; > }; > > -/** > - * struct ndctl_cmd - device-specific-method (_DSM ioctl) container > - * @dimm: set if the command is relative to a dimm, NULL otherwise > - * @bus: set if the command is relative to a bus (like ARS), NULL otherwise > - * @refcount: reference for passing command buffer around > - * @type: cmd number > - * @size: total size of the ndctl_cmd allocation > - * @status: negative if failed, 0 if success, > 0 if never submitted > - * @firmware_status: NFIT command output status code > - * @iter: iterator for multi-xfer commands > - * @source: source cmd of an inherited iter.total_buf > - * > - * For dynamically sized commands like 'get_config', 'set_config', or > - * 'vendor', @size encompasses the entire buffer for the command input > - * and response output data. > - * > - * A command may only specify one of @source, or @iter.total_buf, not both. > - */ > -enum { > - READ, WRITE, > -}; > -struct ndctl_cmd { > - struct ndctl_dimm *dimm; > - struct ndctl_bus *bus; > - int refcount; > - int type; > - int size; > - int status; > - u32 *firmware_status; > - struct ndctl_cmd_iter { > - u32 *offset; > - u8 *data; /* pointer to the data buffer location in cmd */ > - u32 max_xfer; > - char *total_buf; > - u32 total_xfer; > - int dir; > - } iter; > - struct ndctl_cmd *source; > - union { > - struct nd_cmd_ars_cap ars_cap[0]; > - struct nd_cmd_ars_start ars_start[0]; > - struct nd_cmd_ars_status ars_status[0]; > - struct nd_cmd_get_config_size get_size[0]; > - struct nd_cmd_get_config_data_hdr get_data[0]; > - struct nd_cmd_set_config_hdr set_data[0]; > - struct nd_cmd_vendor_hdr vendor[0]; > - char cmd_buf[0]; > - }; > -}; > - > void ndctl_log(struct ndctl_ctx *ctx, > int priority, const char *file, int line, const char *fn, > const char *format, ...) > @@ -1668,13 +1618,6 @@ static const char *ndctl_device_type_name(int type) > } > } > > -static struct ndctl_bus *cmd_to_bus(struct ndctl_cmd *cmd) > -{ > - if (cmd->dimm) > - return ndctl_dimm_get_bus(cmd->dimm); > - return cmd->bus; > -} > - > NDCTL_EXPORT const char *ndctl_region_get_type_name(struct ndctl_region *region) > { > return ndctl_device_type_name(ndctl_region_get_type(region)); > @@ -1720,198 +1663,6 @@ NDCTL_EXPORT struct ndctl_dimm *ndctl_region_get_next_dimm(struct ndctl_region * > return NULL; > } > > -NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_cap(struct ndctl_bus *bus, > - unsigned long long address, unsigned long long len) > -{ > - struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); > - struct ndctl_cmd *cmd; > - size_t size; > - > - if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_CAP)) { > - dbg(ctx, "unsupported cmd\n"); > - return NULL; > - } > - > - size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_cap); > - cmd = calloc(1, size); > - if (!cmd) > - return NULL; > - > - cmd->bus = bus; > - ndctl_cmd_ref(cmd); > - cmd->type = ND_CMD_ARS_CAP; > - cmd->size = size; > - cmd->status = 1; > - cmd->firmware_status = &cmd->ars_cap->status; > - cmd->ars_cap->address = address; > - cmd->ars_cap->length = len; > - > - return cmd; > -} > - > -NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_start(struct ndctl_cmd *ars_cap, > - int type) > -{ > - struct ndctl_bus *bus = ars_cap->bus; > - struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); > - struct ndctl_cmd *cmd; > - size_t size; > - > - if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_START)) { > - dbg(ctx, "unsupported cmd\n"); > - return NULL; > - } > - if (ars_cap->status != 0) { > - dbg(ctx, "expected sucessfully completed ars_cap command\n"); > - return NULL; > - } > - if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) { > - dbg(ctx, "expected sucessfully completed ars_cap command\n"); > - return NULL; > - } > - if (!(*ars_cap->firmware_status >> ARS_EXT_STATUS_SHIFT & type)) { > - dbg(ctx, "ars_cap does not show requested type as supported\n"); > - return NULL; > - } > - > - size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_start); > - cmd = calloc(1, size); > - if (!cmd) > - return NULL; > - > - cmd->bus = bus; > - ndctl_cmd_ref(cmd); > - cmd->type = ND_CMD_ARS_START; > - cmd->size = size; > - cmd->status = 1; > - cmd->firmware_status = &cmd->ars_start->status; > - cmd->ars_start->address = ars_cap->ars_cap->address; > - cmd->ars_start->length = ars_cap->ars_cap->length; > - cmd->ars_start->type = type; > - > - return cmd; > -} > - > -NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_status(struct ndctl_cmd *ars_cap) > -{ > - struct nd_cmd_ars_cap *ars_cap_cmd = ars_cap->ars_cap; > - struct ndctl_bus *bus = ars_cap->bus; > - struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); > - struct ndctl_cmd *cmd; > - size_t size; > - > - if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_CAP)) { > - dbg(ctx, "unsupported cmd\n"); > - return NULL; > - } > - if (ars_cap->status != 0) { > - dbg(ctx, "expected sucessfully completed ars_cap command\n"); > - return NULL; > - } > - if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) { > - dbg(ctx, "expected sucessfully completed ars_cap command\n"); > - return NULL; > - } > - if (ars_cap_cmd->max_ars_out == 0) { > - dbg(ctx, "invalid ars_cap\n"); > - return NULL; > - } > - > - size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_status) + > - ars_cap_cmd->max_ars_out; > - cmd = calloc(1, size); > - if (!cmd) > - return NULL; > - > - cmd->bus = bus; > - ndctl_cmd_ref(cmd); > - cmd->type = ND_CMD_ARS_STATUS; > - cmd->size = size; > - cmd->status = 1; > - cmd->firmware_status = &cmd->ars_status->status; > - > - return cmd; > -} > - > -NDCTL_EXPORT unsigned int ndctl_cmd_ars_cap_get_size(struct ndctl_cmd *ars_cap) > -{ > - struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_cap)); > - > - if (ars_cap->type == ND_CMD_ARS_CAP && ars_cap->status == 0) { > - dbg(ctx, "max_ars_out: %d\n", > - ars_cap->ars_cap->max_ars_out); > - return ars_cap->ars_cap->max_ars_out; > - } > - > - dbg(ctx, "invalid ars_cap\n"); > - return 0; > -} > - > -NDCTL_EXPORT unsigned int ndctl_cmd_ars_in_progress(struct ndctl_cmd *ars_stat) > -{ > - struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); > - int rc; > - > - if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) { > - rc = (((*ars_stat->firmware_status >> ARS_EXT_STATUS_SHIFT) == 1) ? 1 : 0); > - /* > - * If in-progress, invalidate the ndctl_cmd, so that if we're > - * called again without a fresh ars_stat command, we fail. > - */ > - if (rc) > - ars_stat->status = 1; > - return rc; > - } > - > - dbg(ctx, "invalid ars_status\n"); > - return 0; > -} > - > -NDCTL_EXPORT unsigned int ndctl_cmd_ars_num_records(struct ndctl_cmd *ars_stat) > -{ > - struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); > - > - if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) > - return ars_stat->ars_status->num_records; > - > - dbg(ctx, "invalid ars_status\n"); > - return 0; > -} > - > -NDCTL_EXPORT unsigned long long ndctl_cmd_ars_get_record_addr( > - struct ndctl_cmd *ars_stat, unsigned int rec_index) > -{ > - struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); > - > - if (rec_index >= ars_stat->ars_status->num_records) { > - dbg(ctx, "invalid record index\n"); > - return 0; > - } > - > - if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) > - return ars_stat->ars_status->records[rec_index].err_address; > - > - dbg(ctx, "invalid ars_status\n"); > - return 0; > -} > - > -NDCTL_EXPORT unsigned long long ndctl_cmd_ars_get_record_len( > - struct ndctl_cmd *ars_stat, unsigned int rec_index) > -{ > - struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); > - > - if (rec_index >= ars_stat->ars_status->num_records) { > - dbg(ctx, "invalid record index\n"); > - return 0; > - } > - > - if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) > - return ars_stat->ars_status->records[rec_index].length; > - > - dbg(ctx, "invalid ars_status\n"); > - return 0; > -} > - > static struct nd_cmd_vendor_tail *to_vendor_tail(struct ndctl_cmd *cmd) > { > struct nd_cmd_vendor_tail *tail = (struct nd_cmd_vendor_tail *) > diff --git a/lib/ndctl/libndctl.h b/lib/ndctl/libndctl.h > index 31118c646520..cec036fc3386 100644 > --- a/lib/ndctl/libndctl.h > +++ b/lib/ndctl/libndctl.h > @@ -149,6 +149,7 @@ int ndctl_dimm_disable(struct ndctl_dimm *dimm); > int ndctl_dimm_enable(struct ndctl_dimm *dimm); > > struct ndctl_cmd; > +#ifdef HAVE_NDCTL_ARS > struct ndctl_cmd *ndctl_bus_cmd_new_ars_cap(struct ndctl_bus *bus, > unsigned long long address, unsigned long long len); > struct ndctl_cmd *ndctl_bus_cmd_new_ars_start(struct ndctl_cmd *ars_cap, int type); > @@ -160,6 +161,54 @@ unsigned long long ndctl_cmd_ars_get_record_addr(struct ndctl_cmd *ars_stat, > unsigned int rec_index); > unsigned long long ndctl_cmd_ars_get_record_len(struct ndctl_cmd *ars_stat, > unsigned int rec_index); > + > +#else > +static inline struct ndctl_cmd *ndctl_bus_cmd_new_ars_cap(struct ndctl_bus *bus, > + unsigned long long address, unsigned long long len) > +{ > + return NULL; > +} > + > +static inline struct ndctl_cmd *ndctl_bus_cmd_new_ars_start( > + struct ndctl_cmd *ars_cap, int type) > +{ > + return NULL; > +} > + > +static inline struct ndctl_cmd *ndctl_bus_cmd_new_ars_status( > + struct ndctl_cmd *ars_cap) > +{ > + return NULL; > +} > + > +static inline unsigned int ndctl_cmd_ars_cap_get_size(struct ndctl_cmd *ars_cap) > +{ > + return 0; > +} > + > +static inline unsigned int ndctl_cmd_ars_in_progress(struct ndctl_cmd *ars_status) > +{ > + return 0; > +} > + > +static inline unsigned int ndctl_cmd_ars_num_records(struct ndctl_cmd *ars_stat) > +{ > + return 0; > +} > + > +static inline unsigned long long ndctl_cmd_ars_get_record_addr( > + struct ndctl_cmd *ars_stat, unsigned int rec_index) > +{ > + return 0; > +} > + > +static inline unsigned long long ndctl_cmd_ars_get_record_len( > + struct ndctl_cmd *ars_stat, unsigned int rec_index) > +{ > + 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, > diff --git a/lib/test-libndctl.c b/lib/test-libndctl.c > index b0221335606e..5f03ba594815 100644 > --- a/lib/test-libndctl.c > +++ b/lib/test-libndctl.c > @@ -1249,6 +1249,7 @@ static int check_set_config_data(struct ndctl_bus *bus, struct ndctl_dimm *dimm, > return 0; > } > > +#ifdef HAVE_NDCTL_ARS > static int check_ars_cap(struct ndctl_bus *bus, struct ndctl_dimm *dimm, > struct check_cmd *check) > { > @@ -1360,6 +1361,28 @@ static int check_ars_status(struct ndctl_bus *bus, struct ndctl_dimm *dimm, > check->cmd = cmd; > return 0; > } > +#else > +static int check_ars_cap(struct ndctl_bus *bus, struct ndctl_dimm *dimm, > + struct check_cmd *check) > +{ > + fprintf(stderr, "%s: HAVE_NDCTL_ARS disabled, skipping\n", __func__); > + return 0; > +} > + > +static int check_ars_start(struct ndctl_bus *bus, struct ndctl_dimm *dimm, > + struct check_cmd *check) > +{ > + fprintf(stderr, "%s: HAVE_NDCTL_ARS disabled, skipping\n", __func__); > + return 0; > +} > + > +static int check_ars_status(struct ndctl_bus *bus, struct ndctl_dimm *dimm, > + struct check_cmd *check) > +{ > + fprintf(stderr, "%s: HAVE_NDCTL_ARS disabled, skipping\n", __func__); > + return 0; > +} > +#endif > > #define BITS_PER_LONG 32 > static int check_commands(struct ndctl_bus *bus, struct ndctl_dimm *dimm, > > _______________________________________________ > Linux-nvdimm mailing list > Linux-nvdimm@lists.01.org > https://lists.01.org/mailman/listinfo/linux-nvdimm
"Elliott, Robert (Persistent Memory)" <elliott@hpe.com> writes: >> It might make more sense to copy the header file from the kernel >> sources >> into the ndctl sources. Then, always enable the functionality at >> build time and auto-detect at runtime. > > The kernel at the time of the build could be older (not just newer) > than the ndctl package. Hi, Rob, Sorry, but I don't know what point you're trying to make. Could you elaborate? Cheers, Jeff
On Tue, 2015-11-10 at 09:35 -0500, Jeff Moyer wrote: > Dan Williams <dan.j.williams@intel.com> writes: > > > Kernel support for the address range scrub (ARS) set of commands was > > added in the v4.3 kernel development cycle, while the base libndctl > > functionality is based on the kernel provided api as of > > v4.2. Support > > in the library for any functionality past the base v4.2 expectations > > needs to be dynamically detected and enabled. > > > > Add autoconf machinery to do the detection as well as an override to > > force use of the local ndctl.h definitions (--enable-local). Move > > the > > ARS routines to their own source file so they can be compiled in/out > > as > > a complete unit. > > > > This fixes compilation errors like the following: > > lib/libndctl.c: In function 'ndctl_bus_cmd_new_ars_start': > > lib/libndctl.c:1768:35: error: 'ARS_STATUS_MASK' undeclared > > (first use in this function) > > if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) { > > It might make more sense to copy the header file from the kernel > sources > into the ndctl sources. Then, always enable the functionality at > build > time and auto-detect at runtime. If we are on 4.2 (without any ARS features in the kernel header), and we just copy that header to build new ndctl source (with ARS) it will fail as above. I think this is what Rob was trying to say too.. -Vishal
On Tue, Nov 10, 2015 at 12:41 PM, Verma, Vishal L <vishal.l.verma@intel.com> wrote: > On Tue, 2015-11-10 at 09:35 -0500, Jeff Moyer wrote: >> Dan Williams <dan.j.williams@intel.com> writes: >> >> > Kernel support for the address range scrub (ARS) set of commands was >> > added in the v4.3 kernel development cycle, while the base libndctl >> > functionality is based on the kernel provided api as of >> > v4.2. Support >> > in the library for any functionality past the base v4.2 expectations >> > needs to be dynamically detected and enabled. >> > >> > Add autoconf machinery to do the detection as well as an override to >> > force use of the local ndctl.h definitions (--enable-local). Move >> > the >> > ARS routines to their own source file so they can be compiled in/out >> > as >> > a complete unit. >> > >> > This fixes compilation errors like the following: >> > lib/libndctl.c: In function 'ndctl_bus_cmd_new_ars_start': >> > lib/libndctl.c:1768:35: error: 'ARS_STATUS_MASK' undeclared >> > (first use in this function) >> > if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) { >> >> It might make more sense to copy the header file from the kernel >> sources >> into the ndctl sources. Then, always enable the functionality at >> build >> time and auto-detect at runtime. > > If we are on 4.2 (without any ARS features in the kernel header), and we > just copy that header to build new ndctl source (with ARS) it will fail > as above. I think this is what Rob was trying to say too.. The failure mode is less predictable. It shouldn't break, but it's cleaner to disable a whole class of things that aren't expected to work in a known / static way.
diff --git a/Makefile.am b/Makefile.am index e5b4b4980100..e998c5766631 100644 --- a/Makefile.am +++ b/Makefile.am @@ -53,6 +53,10 @@ lib_libndctl_la_SOURCES =\ lib/libndctl.c lib_libndctl_la_LIBADD = $(UDEV_LIBS) $(UUID_LIBS) $(KMOD_LIBS) +if ENABLE_ARS +lib_libndctl_la_SOURCES += lib/libndctl-ars.c +endif + bin_PROGRAMS = ndctl ndctl_SOURCES = ndctl.c \ diff --git a/configure.ac b/configure.ac index deaa94b0338b..c1c0f684e35c 100644 --- a/configure.ac +++ b/configure.ac @@ -73,10 +73,38 @@ PKG_CHECK_MODULES([KMOD], [libkmod]) PKG_CHECK_MODULES([UDEV], [libudev]) PKG_CHECK_MODULES([UUID], [uuid]) -AC_CHECK_HEADER([linux/ndctl.h], - [AC_DEFINE([HAVE_NDCTL_H], [1], - [Define to 1 if you have <linux/ndctl.h>.])], - []) +AC_ARG_ENABLE([local], + AS_HELP_STRING([--enable-local], [build with local ndctl.h @<:@default=system@:>@]), + [], [enable_local=no]) + +AS_IF([test "x$enable_local" = "xyes"], [], [ + AC_CHECK_HEADER([linux/ndctl.h], [ + AC_DEFINE([HAVE_NDCTL_H], [1], + [Define to 1 if you have <linux/ndctl.h>.]) + ], []) + ] +) + +# when building against kernel headers check version specific features +AC_MSG_CHECKING([for ARS support]) +AC_LANG(C) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #ifdef HAVE_NDCTL_H + #include <linux/ndctl.h> + #else + #include "ndctl.h" + #endif + ]], [[ + int x = ARS_STATUS_MASK; + ]] + )], [AC_MSG_RESULT([yes]) + enable_ars=yes + AC_DEFINE([HAVE_NDCTL_ARS], [1], + [Define to 1 if ndctl.h has ARS support.]) + ], [AC_MSG_RESULT([no])] +) +AM_CONDITIONAL([ENABLE_ARS], [test "x$enable_ars" = "xyes"]) + AC_CHECK_HEADERS_ONCE([linux/version.h]) AC_CHECK_FUNCS([ \ diff --git a/lib/libndctl-ars.c b/lib/libndctl-ars.c new file mode 100644 index 000000000000..fd988996501d --- /dev/null +++ b/lib/libndctl-ars.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2014-2015, Intel 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 <stdlib.h> +#include <ndctl/libndctl.h> +#include "libndctl-private.h" + +NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_cap(struct ndctl_bus *bus, + unsigned long long address, unsigned long long len) +{ + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); + struct ndctl_cmd *cmd; + size_t size; + + if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_CAP)) { + dbg(ctx, "unsupported cmd\n"); + return NULL; + } + + size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_cap); + cmd = calloc(1, size); + if (!cmd) + return NULL; + + cmd->bus = bus; + ndctl_cmd_ref(cmd); + cmd->type = ND_CMD_ARS_CAP; + cmd->size = size; + cmd->status = 1; + cmd->firmware_status = &cmd->ars_cap->status; + cmd->ars_cap->address = address; + cmd->ars_cap->length = len; + + return cmd; +} + +NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_start(struct ndctl_cmd *ars_cap, + int type) +{ + struct ndctl_bus *bus = ars_cap->bus; + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); + struct ndctl_cmd *cmd; + size_t size; + + if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_START)) { + dbg(ctx, "unsupported cmd\n"); + return NULL; + } + if (ars_cap->status != 0) { + dbg(ctx, "expected sucessfully completed ars_cap command\n"); + return NULL; + } + if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) { + dbg(ctx, "expected sucessfully completed ars_cap command\n"); + return NULL; + } + if (!(*ars_cap->firmware_status >> ARS_EXT_STATUS_SHIFT & type)) { + dbg(ctx, "ars_cap does not show requested type as supported\n"); + return NULL; + } + + size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_start); + cmd = calloc(1, size); + if (!cmd) + return NULL; + + cmd->bus = bus; + ndctl_cmd_ref(cmd); + cmd->type = ND_CMD_ARS_START; + cmd->size = size; + cmd->status = 1; + cmd->firmware_status = &cmd->ars_start->status; + cmd->ars_start->address = ars_cap->ars_cap->address; + cmd->ars_start->length = ars_cap->ars_cap->length; + cmd->ars_start->type = type; + + return cmd; +} + +NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_status(struct ndctl_cmd *ars_cap) +{ + struct nd_cmd_ars_cap *ars_cap_cmd = ars_cap->ars_cap; + struct ndctl_bus *bus = ars_cap->bus; + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); + struct ndctl_cmd *cmd; + size_t size; + + if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_CAP)) { + dbg(ctx, "unsupported cmd\n"); + return NULL; + } + if (ars_cap->status != 0) { + dbg(ctx, "expected sucessfully completed ars_cap command\n"); + return NULL; + } + if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) { + dbg(ctx, "expected sucessfully completed ars_cap command\n"); + return NULL; + } + if (ars_cap_cmd->max_ars_out == 0) { + dbg(ctx, "invalid ars_cap\n"); + return NULL; + } + + size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_status) + + ars_cap_cmd->max_ars_out; + cmd = calloc(1, size); + if (!cmd) + return NULL; + + cmd->bus = bus; + ndctl_cmd_ref(cmd); + cmd->type = ND_CMD_ARS_STATUS; + cmd->size = size; + cmd->status = 1; + cmd->firmware_status = &cmd->ars_status->status; + + return cmd; +} + +NDCTL_EXPORT unsigned int ndctl_cmd_ars_cap_get_size(struct ndctl_cmd *ars_cap) +{ + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_cap)); + + if (ars_cap->type == ND_CMD_ARS_CAP && ars_cap->status == 0) { + dbg(ctx, "max_ars_out: %d\n", + ars_cap->ars_cap->max_ars_out); + return ars_cap->ars_cap->max_ars_out; + } + + dbg(ctx, "invalid ars_cap\n"); + return 0; +} + +NDCTL_EXPORT unsigned int ndctl_cmd_ars_in_progress(struct ndctl_cmd *ars_stat) +{ + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); + int rc; + + if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) { + rc = (((*ars_stat->firmware_status >> ARS_EXT_STATUS_SHIFT) == 1) ? 1 : 0); + /* + * If in-progress, invalidate the ndctl_cmd, so that if we're + * called again without a fresh ars_stat command, we fail. + */ + if (rc) + ars_stat->status = 1; + return rc; + } + + dbg(ctx, "invalid ars_status\n"); + return 0; +} + +NDCTL_EXPORT unsigned int ndctl_cmd_ars_num_records(struct ndctl_cmd *ars_stat) +{ + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); + + if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) + return ars_stat->ars_status->num_records; + + dbg(ctx, "invalid ars_status\n"); + return 0; +} + +NDCTL_EXPORT unsigned long long ndctl_cmd_ars_get_record_addr( + struct ndctl_cmd *ars_stat, unsigned int rec_index) +{ + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); + + if (rec_index >= ars_stat->ars_status->num_records) { + dbg(ctx, "invalid record index\n"); + return 0; + } + + if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) + return ars_stat->ars_status->records[rec_index].err_address; + + dbg(ctx, "invalid ars_status\n"); + return 0; +} + +NDCTL_EXPORT unsigned long long ndctl_cmd_ars_get_record_len( + struct ndctl_cmd *ars_stat, unsigned int rec_index) +{ + struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); + + if (rec_index >= ars_stat->ars_status->num_records) { + dbg(ctx, "invalid record index\n"); + return 0; + } + + if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) + return ars_stat->ars_status->records[rec_index].length; + + dbg(ctx, "invalid ars_status\n"); + return 0; +} diff --git a/lib/libndctl-private.h b/lib/libndctl-private.h index 78fbb2d46b42..d3cca9401b45 100644 --- a/lib/libndctl-private.h +++ b/lib/libndctl-private.h @@ -13,11 +13,14 @@ #ifndef _LIBNDCTL_PRIVATE_H_ #define _LIBNDCTL_PRIVATE_H_ +#include <errno.h> #include <stdbool.h> #include <syslog.h> +#include <string.h> #include <libudev.h> #include <libkmod.h> #include <uuid/uuid.h> +#include <ccan/array_size/array_size.h> #ifdef HAVE_NDCTL_H #include <linux/ndctl.h> #else @@ -117,6 +120,63 @@ struct namespace_label { le32 unused; }; +/** + * struct ndctl_cmd - device-specific-method (_DSM ioctl) container + * @dimm: set if the command is relative to a dimm, NULL otherwise + * @bus: set if the command is relative to a bus (like ARS), NULL otherwise + * @refcount: reference for passing command buffer around + * @type: cmd number + * @size: total size of the ndctl_cmd allocation + * @status: negative if failed, 0 if success, > 0 if never submitted + * @firmware_status: NFIT command output status code + * @iter: iterator for multi-xfer commands + * @source: source cmd of an inherited iter.total_buf + * + * For dynamically sized commands like 'get_config', 'set_config', or + * 'vendor', @size encompasses the entire buffer for the command input + * and response output data. + * + * A command may only specify one of @source, or @iter.total_buf, not both. + */ +enum { + READ, WRITE, +}; +struct ndctl_cmd { + struct ndctl_dimm *dimm; + struct ndctl_bus *bus; + int refcount; + int type; + int size; + int status; + u32 *firmware_status; + struct ndctl_cmd_iter { + u32 *offset; + u8 *data; /* pointer to the data buffer location in cmd */ + u32 max_xfer; + char *total_buf; + u32 total_xfer; + int dir; + } iter; + struct ndctl_cmd *source; + union { + struct nd_cmd_ars_cap ars_cap[0]; + struct nd_cmd_ars_start ars_start[0]; + struct nd_cmd_ars_status ars_status[0]; + struct nd_cmd_get_config_size get_size[0]; + struct nd_cmd_get_config_data_hdr get_data[0]; + struct nd_cmd_set_config_hdr set_data[0]; + struct nd_cmd_vendor_hdr vendor[0]; + char cmd_buf[0]; + }; +}; + +static inline struct ndctl_bus *cmd_to_bus(struct ndctl_cmd *cmd) +{ + if (cmd->dimm) + return ndctl_dimm_get_bus(cmd->dimm); + return cmd->bus; +} + static inline void __attribute__((always_inline, format(printf, 2, 3))) ndctl_log_null(struct ndctl_ctx *ctx, const char *format, ...) {} diff --git a/lib/libndctl.c b/lib/libndctl.c index 7db5b6013ab1..6ebb4eb3d3cd 100644 --- a/lib/libndctl.c +++ b/lib/libndctl.c @@ -296,56 +296,6 @@ struct ndctl_ctx { unsigned long timeout; }; -/** - * struct ndctl_cmd - device-specific-method (_DSM ioctl) container - * @dimm: set if the command is relative to a dimm, NULL otherwise - * @bus: set if the command is relative to a bus (like ARS), NULL otherwise - * @refcount: reference for passing command buffer around - * @type: cmd number - * @size: total size of the ndctl_cmd allocation - * @status: negative if failed, 0 if success, > 0 if never submitted - * @firmware_status: NFIT command output status code - * @iter: iterator for multi-xfer commands - * @source: source cmd of an inherited iter.total_buf - * - * For dynamically sized commands like 'get_config', 'set_config', or - * 'vendor', @size encompasses the entire buffer for the command input - * and response output data. - * - * A command may only specify one of @source, or @iter.total_buf, not both. - */ -enum { - READ, WRITE, -}; -struct ndctl_cmd { - struct ndctl_dimm *dimm; - struct ndctl_bus *bus; - int refcount; - int type; - int size; - int status; - u32 *firmware_status; - struct ndctl_cmd_iter { - u32 *offset; - u8 *data; /* pointer to the data buffer location in cmd */ - u32 max_xfer; - char *total_buf; - u32 total_xfer; - int dir; - } iter; - struct ndctl_cmd *source; - union { - struct nd_cmd_ars_cap ars_cap[0]; - struct nd_cmd_ars_start ars_start[0]; - struct nd_cmd_ars_status ars_status[0]; - struct nd_cmd_get_config_size get_size[0]; - struct nd_cmd_get_config_data_hdr get_data[0]; - struct nd_cmd_set_config_hdr set_data[0]; - struct nd_cmd_vendor_hdr vendor[0]; - char cmd_buf[0]; - }; -}; - void ndctl_log(struct ndctl_ctx *ctx, int priority, const char *file, int line, const char *fn, const char *format, ...) @@ -1668,13 +1618,6 @@ static const char *ndctl_device_type_name(int type) } } -static struct ndctl_bus *cmd_to_bus(struct ndctl_cmd *cmd) -{ - if (cmd->dimm) - return ndctl_dimm_get_bus(cmd->dimm); - return cmd->bus; -} - NDCTL_EXPORT const char *ndctl_region_get_type_name(struct ndctl_region *region) { return ndctl_device_type_name(ndctl_region_get_type(region)); @@ -1720,198 +1663,6 @@ NDCTL_EXPORT struct ndctl_dimm *ndctl_region_get_next_dimm(struct ndctl_region * return NULL; } -NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_cap(struct ndctl_bus *bus, - unsigned long long address, unsigned long long len) -{ - struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); - struct ndctl_cmd *cmd; - size_t size; - - if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_CAP)) { - dbg(ctx, "unsupported cmd\n"); - return NULL; - } - - size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_cap); - cmd = calloc(1, size); - if (!cmd) - return NULL; - - cmd->bus = bus; - ndctl_cmd_ref(cmd); - cmd->type = ND_CMD_ARS_CAP; - cmd->size = size; - cmd->status = 1; - cmd->firmware_status = &cmd->ars_cap->status; - cmd->ars_cap->address = address; - cmd->ars_cap->length = len; - - return cmd; -} - -NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_start(struct ndctl_cmd *ars_cap, - int type) -{ - struct ndctl_bus *bus = ars_cap->bus; - struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); - struct ndctl_cmd *cmd; - size_t size; - - if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_START)) { - dbg(ctx, "unsupported cmd\n"); - return NULL; - } - if (ars_cap->status != 0) { - dbg(ctx, "expected sucessfully completed ars_cap command\n"); - return NULL; - } - if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) { - dbg(ctx, "expected sucessfully completed ars_cap command\n"); - return NULL; - } - if (!(*ars_cap->firmware_status >> ARS_EXT_STATUS_SHIFT & type)) { - dbg(ctx, "ars_cap does not show requested type as supported\n"); - return NULL; - } - - size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_start); - cmd = calloc(1, size); - if (!cmd) - return NULL; - - cmd->bus = bus; - ndctl_cmd_ref(cmd); - cmd->type = ND_CMD_ARS_START; - cmd->size = size; - cmd->status = 1; - cmd->firmware_status = &cmd->ars_start->status; - cmd->ars_start->address = ars_cap->ars_cap->address; - cmd->ars_start->length = ars_cap->ars_cap->length; - cmd->ars_start->type = type; - - return cmd; -} - -NDCTL_EXPORT struct ndctl_cmd *ndctl_bus_cmd_new_ars_status(struct ndctl_cmd *ars_cap) -{ - struct nd_cmd_ars_cap *ars_cap_cmd = ars_cap->ars_cap; - struct ndctl_bus *bus = ars_cap->bus; - struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus); - struct ndctl_cmd *cmd; - size_t size; - - if (!ndctl_bus_is_cmd_supported(bus, ND_CMD_ARS_CAP)) { - dbg(ctx, "unsupported cmd\n"); - return NULL; - } - if (ars_cap->status != 0) { - dbg(ctx, "expected sucessfully completed ars_cap command\n"); - return NULL; - } - if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) { - dbg(ctx, "expected sucessfully completed ars_cap command\n"); - return NULL; - } - if (ars_cap_cmd->max_ars_out == 0) { - dbg(ctx, "invalid ars_cap\n"); - return NULL; - } - - size = sizeof(*cmd) + sizeof(struct nd_cmd_ars_status) + - ars_cap_cmd->max_ars_out; - cmd = calloc(1, size); - if (!cmd) - return NULL; - - cmd->bus = bus; - ndctl_cmd_ref(cmd); - cmd->type = ND_CMD_ARS_STATUS; - cmd->size = size; - cmd->status = 1; - cmd->firmware_status = &cmd->ars_status->status; - - return cmd; -} - -NDCTL_EXPORT unsigned int ndctl_cmd_ars_cap_get_size(struct ndctl_cmd *ars_cap) -{ - struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_cap)); - - if (ars_cap->type == ND_CMD_ARS_CAP && ars_cap->status == 0) { - dbg(ctx, "max_ars_out: %d\n", - ars_cap->ars_cap->max_ars_out); - return ars_cap->ars_cap->max_ars_out; - } - - dbg(ctx, "invalid ars_cap\n"); - return 0; -} - -NDCTL_EXPORT unsigned int ndctl_cmd_ars_in_progress(struct ndctl_cmd *ars_stat) -{ - struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); - int rc; - - if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) { - rc = (((*ars_stat->firmware_status >> ARS_EXT_STATUS_SHIFT) == 1) ? 1 : 0); - /* - * If in-progress, invalidate the ndctl_cmd, so that if we're - * called again without a fresh ars_stat command, we fail. - */ - if (rc) - ars_stat->status = 1; - return rc; - } - - dbg(ctx, "invalid ars_status\n"); - return 0; -} - -NDCTL_EXPORT unsigned int ndctl_cmd_ars_num_records(struct ndctl_cmd *ars_stat) -{ - struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); - - if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) - return ars_stat->ars_status->num_records; - - dbg(ctx, "invalid ars_status\n"); - return 0; -} - -NDCTL_EXPORT unsigned long long ndctl_cmd_ars_get_record_addr( - struct ndctl_cmd *ars_stat, unsigned int rec_index) -{ - struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); - - if (rec_index >= ars_stat->ars_status->num_records) { - dbg(ctx, "invalid record index\n"); - return 0; - } - - if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) - return ars_stat->ars_status->records[rec_index].err_address; - - dbg(ctx, "invalid ars_status\n"); - return 0; -} - -NDCTL_EXPORT unsigned long long ndctl_cmd_ars_get_record_len( - struct ndctl_cmd *ars_stat, unsigned int rec_index) -{ - struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(ars_stat)); - - if (rec_index >= ars_stat->ars_status->num_records) { - dbg(ctx, "invalid record index\n"); - return 0; - } - - if (ars_stat->type == ND_CMD_ARS_STATUS && ars_stat->status == 0) - return ars_stat->ars_status->records[rec_index].length; - - dbg(ctx, "invalid ars_status\n"); - return 0; -} - static struct nd_cmd_vendor_tail *to_vendor_tail(struct ndctl_cmd *cmd) { struct nd_cmd_vendor_tail *tail = (struct nd_cmd_vendor_tail *) diff --git a/lib/ndctl/libndctl.h b/lib/ndctl/libndctl.h index 31118c646520..cec036fc3386 100644 --- a/lib/ndctl/libndctl.h +++ b/lib/ndctl/libndctl.h @@ -149,6 +149,7 @@ int ndctl_dimm_disable(struct ndctl_dimm *dimm); int ndctl_dimm_enable(struct ndctl_dimm *dimm); struct ndctl_cmd; +#ifdef HAVE_NDCTL_ARS struct ndctl_cmd *ndctl_bus_cmd_new_ars_cap(struct ndctl_bus *bus, unsigned long long address, unsigned long long len); struct ndctl_cmd *ndctl_bus_cmd_new_ars_start(struct ndctl_cmd *ars_cap, int type); @@ -160,6 +161,54 @@ unsigned long long ndctl_cmd_ars_get_record_addr(struct ndctl_cmd *ars_stat, unsigned int rec_index); unsigned long long ndctl_cmd_ars_get_record_len(struct ndctl_cmd *ars_stat, unsigned int rec_index); + +#else +static inline struct ndctl_cmd *ndctl_bus_cmd_new_ars_cap(struct ndctl_bus *bus, + unsigned long long address, unsigned long long len) +{ + return NULL; +} + +static inline struct ndctl_cmd *ndctl_bus_cmd_new_ars_start( + struct ndctl_cmd *ars_cap, int type) +{ + return NULL; +} + +static inline struct ndctl_cmd *ndctl_bus_cmd_new_ars_status( + struct ndctl_cmd *ars_cap) +{ + return NULL; +} + +static inline unsigned int ndctl_cmd_ars_cap_get_size(struct ndctl_cmd *ars_cap) +{ + return 0; +} + +static inline unsigned int ndctl_cmd_ars_in_progress(struct ndctl_cmd *ars_status) +{ + return 0; +} + +static inline unsigned int ndctl_cmd_ars_num_records(struct ndctl_cmd *ars_stat) +{ + return 0; +} + +static inline unsigned long long ndctl_cmd_ars_get_record_addr( + struct ndctl_cmd *ars_stat, unsigned int rec_index) +{ + return 0; +} + +static inline unsigned long long ndctl_cmd_ars_get_record_len( + struct ndctl_cmd *ars_stat, unsigned int rec_index) +{ + 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, diff --git a/lib/test-libndctl.c b/lib/test-libndctl.c index b0221335606e..5f03ba594815 100644 --- a/lib/test-libndctl.c +++ b/lib/test-libndctl.c @@ -1249,6 +1249,7 @@ static int check_set_config_data(struct ndctl_bus *bus, struct ndctl_dimm *dimm, return 0; } +#ifdef HAVE_NDCTL_ARS static int check_ars_cap(struct ndctl_bus *bus, struct ndctl_dimm *dimm, struct check_cmd *check) { @@ -1360,6 +1361,28 @@ static int check_ars_status(struct ndctl_bus *bus, struct ndctl_dimm *dimm, check->cmd = cmd; return 0; } +#else +static int check_ars_cap(struct ndctl_bus *bus, struct ndctl_dimm *dimm, + struct check_cmd *check) +{ + fprintf(stderr, "%s: HAVE_NDCTL_ARS disabled, skipping\n", __func__); + return 0; +} + +static int check_ars_start(struct ndctl_bus *bus, struct ndctl_dimm *dimm, + struct check_cmd *check) +{ + fprintf(stderr, "%s: HAVE_NDCTL_ARS disabled, skipping\n", __func__); + return 0; +} + +static int check_ars_status(struct ndctl_bus *bus, struct ndctl_dimm *dimm, + struct check_cmd *check) +{ + fprintf(stderr, "%s: HAVE_NDCTL_ARS disabled, skipping\n", __func__); + return 0; +} +#endif #define BITS_PER_LONG 32 static int check_commands(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
Kernel support for the address range scrub (ARS) set of commands was added in the v4.3 kernel development cycle, while the base libndctl functionality is based on the kernel provided api as of v4.2. Support in the library for any functionality past the base v4.2 expectations needs to be dynamically detected and enabled. Add autoconf machinery to do the detection as well as an override to force use of the local ndctl.h definitions (--enable-local). Move the ARS routines to their own source file so they can be compiled in/out as a complete unit. This fixes compilation errors like the following: lib/libndctl.c: In function 'ndctl_bus_cmd_new_ars_start': lib/libndctl.c:1768:35: error: 'ARS_STATUS_MASK' undeclared (first use in this function) if ((*ars_cap->firmware_status & ARS_STATUS_MASK) != 0) { Cc: Vishal Verma <vishal.l.verma@intel.com> Reported-by: Robert Elliott <elliott@hpe.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com> --- Makefile.am | 4 + configure.ac | 36 ++++++- lib/libndctl-ars.c | 207 ++++++++++++++++++++++++++++++++++++++++ lib/libndctl-private.h | 60 ++++++++++++ lib/libndctl.c | 249 ------------------------------------------------ lib/ndctl/libndctl.h | 49 +++++++++ lib/test-libndctl.c | 23 ++++ 7 files changed, 375 insertions(+), 253 deletions(-) create mode 100644 lib/libndctl-ars.c