@@ -32,9 +32,9 @@ SED_PROCESS = \
%.pc: %.pc.in Makefile
$(SED_PROCESS)
-LIBNDCTL_CURRENT=3
+LIBNDCTL_CURRENT=4
LIBNDCTL_REVISION=0
-LIBNDCTL_AGE=0
+LIBNDCTL_AGE=1
pkginclude_HEADERS = lib/ndctl/libndctl.h
lib_LTLIBRARIES = lib/libndctl.la
@@ -338,6 +338,9 @@ struct ndctl_cmd {
} 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];
@@ -1652,6 +1655,13 @@ 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));
@@ -1697,6 +1707,198 @@ 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 int 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 int 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 *)
@@ -1800,13 +2002,6 @@ NDCTL_EXPORT struct ndctl_cmd *ndctl_dimm_cmd_new_cfg_size(struct ndctl_dimm *di
return cmd;
}
-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 struct ndctl_cmd *ndctl_dimm_cmd_new_cfg_read(struct ndctl_cmd *cfg_size)
{
struct ndctl_ctx *ctx = ndctl_bus_get_ctx(cmd_to_bus(cfg_size));
@@ -64,6 +64,14 @@ global:
ndctl_dimm_is_enabled;
ndctl_dimm_disable;
ndctl_dimm_enable;
+ ndctl_bus_cmd_new_ars_cap;
+ ndctl_bus_cmd_new_ars_start;
+ ndctl_bus_cmd_new_ars_status;
+ ndctl_cmd_ars_cap_get_size;
+ ndctl_cmd_ars_in_progress;
+ ndctl_cmd_ars_num_records;
+ ndctl_cmd_ars_get_record_addr;
+ ndctl_cmd_ars_get_record_len;
ndctl_dimm_cmd_new_vendor_specific;
ndctl_cmd_vendor_set_input;
ndctl_cmd_vendor_get_output_size;
@@ -151,6 +151,17 @@ int ndctl_dimm_disable(struct ndctl_dimm *dimm);
int ndctl_dimm_enable(struct ndctl_dimm *dimm);
struct ndctl_cmd;
+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);
+struct ndctl_cmd *ndctl_bus_cmd_new_ars_status(struct ndctl_cmd *ars_cap);
+unsigned int ndctl_cmd_ars_cap_get_size(struct ndctl_cmd *ars_cap);
+unsigned int ndctl_cmd_ars_in_progress(struct ndctl_cmd *ars_status);
+unsigned int ndctl_cmd_ars_num_records(struct ndctl_cmd *ars_stat);
+unsigned int ndctl_cmd_ars_get_record_addr(struct ndctl_cmd *ars_stat,
+ unsigned int rec_index);
+unsigned int ndctl_cmd_ars_get_record_len(struct ndctl_cmd *ars_stat,
+ unsigned int rec_index);
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,
@@ -337,10 +337,14 @@ static struct region regions1[] = {
},
};
-static unsigned long commands0 = 1UL << ND_CMD_GET_CONFIG_SIZE
+static unsigned long dimm_commands0 = 1UL << ND_CMD_GET_CONFIG_SIZE
| 1UL << ND_CMD_GET_CONFIG_DATA
| 1UL << ND_CMD_SET_CONFIG_DATA;
+static unsigned long bus_commands0 = 1UL << ND_CMD_ARS_CAP
+ | 1UL << ND_CMD_ARS_START
+ | 1UL << ND_CMD_ARS_STATUS;
+
static struct ndctl_dimm *get_dimm_by_handle(struct ndctl_bus *bus, unsigned int handle)
{
struct ndctl_dimm *dimm;
@@ -1054,13 +1058,14 @@ static int check_btts(struct ndctl_region *region, struct btt **btts)
}
struct check_cmd {
- int (*check_fn)(struct ndctl_dimm *dimm, struct check_cmd *check);
+ int (*check_fn)(struct ndctl_bus *bus, struct ndctl_dimm *dimm, struct check_cmd *check);
struct ndctl_cmd *cmd;
};
static struct check_cmd *check_cmds;
-static int check_get_config_size(struct ndctl_dimm *dimm, struct check_cmd *check)
+static int check_get_config_size(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
+ struct check_cmd *check)
{
struct ndctl_cmd *cmd;
int rc;
@@ -1098,7 +1103,8 @@ static int check_get_config_size(struct ndctl_dimm *dimm, struct check_cmd *chec
return 0;
}
-static int check_get_config_data(struct ndctl_dimm *dimm, struct check_cmd *check)
+static int check_get_config_data(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
+ struct check_cmd *check)
{
struct ndctl_cmd *cmd_size = check_cmds[ND_CMD_GET_CONFIG_SIZE].cmd;
struct ndctl_cmd *cmd = ndctl_dimm_cmd_new_cfg_read(cmd_size);
@@ -1131,7 +1137,8 @@ static int check_get_config_data(struct ndctl_dimm *dimm, struct check_cmd *chec
return 0;
}
-static int check_set_config_data(struct ndctl_dimm *dimm, struct check_cmd *check)
+static int check_set_config_data(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
+ struct check_cmd *check)
{
struct ndctl_cmd *cmd_read = check_cmds[ND_CMD_GET_CONFIG_DATA].cmd;
struct ndctl_cmd *cmd = ndctl_dimm_cmd_new_cfg_write(cmd_read);
@@ -1198,9 +1205,121 @@ static int check_set_config_data(struct ndctl_dimm *dimm, struct check_cmd *chec
return 0;
}
+static int check_ars_cap(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
+ struct check_cmd *check)
+{
+ struct ndctl_cmd *cmd;
+ int rc;
+
+ if (check->cmd != NULL) {
+ fprintf(stderr, "%s: dimm: %#x expected a NULL command, by default\n",
+ __func__, ndctl_dimm_get_handle(dimm));
+ return -ENXIO;
+ }
+
+ cmd = ndctl_bus_cmd_new_ars_cap(bus, 0, 64);
+ if (!cmd) {
+ fprintf(stderr, "%s: bus: %s failed to create cmd\n",
+ __func__, ndctl_bus_get_provider(bus));
+ return -ENOTTY;
+ }
+
+ rc = ndctl_cmd_submit(cmd);
+ if (rc) {
+ fprintf(stderr, "%s: bus: %s failed to submit cmd: %d\n",
+ __func__, ndctl_bus_get_provider(bus), rc);
+ ndctl_cmd_unref(cmd);
+ return rc;
+ }
+
+ if (ndctl_cmd_ars_cap_get_size(cmd) != 256) {
+ fprintf(stderr, "%s: bus: %s expect size: %d got: %d\n",
+ __func__, ndctl_bus_get_provider(bus), 256,
+ ndctl_cmd_ars_cap_get_size(cmd));
+ ndctl_cmd_unref(cmd);
+ return -ENXIO;
+ }
+
+ check->cmd = cmd;
+ return 0;
+}
+
+static int check_ars_start(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
+ struct check_cmd *check)
+{
+ struct ndctl_cmd *cmd_ars_cap = check_cmds[ND_CMD_ARS_CAP].cmd;
+ struct ndctl_cmd *cmd;
+ int rc;
+
+ if (check->cmd != NULL) {
+ fprintf(stderr, "%s: dimm: %#x expected a NULL command, by default\n",
+ __func__, ndctl_dimm_get_handle(dimm));
+ return -ENXIO;
+ }
+
+ cmd = ndctl_bus_cmd_new_ars_start(cmd_ars_cap, ND_ARS_PERSISTENT);
+ if (!cmd) {
+ fprintf(stderr, "%s: bus: %s failed to create cmd\n",
+ __func__, ndctl_bus_get_provider(bus));
+ return -ENOTTY;
+ }
+
+ rc = ndctl_cmd_submit(cmd);
+ if (rc) {
+ fprintf(stderr, "%s: bus: %s failed to submit cmd: %d\n",
+ __func__, ndctl_bus_get_provider(bus), rc);
+ ndctl_cmd_unref(cmd);
+ return rc;
+ }
+
+ check->cmd = cmd;
+ return 0;
+}
+
+static int check_ars_status(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
+ struct check_cmd *check)
+{
+ struct ndctl_cmd *cmd_ars_cap = check_cmds[ND_CMD_ARS_CAP].cmd;
+ struct ndctl_cmd *cmd;
+ unsigned int i;
+ int rc;
+
+ if (check->cmd != NULL) {
+ fprintf(stderr, "%s: dimm: %#x expected a NULL command, by default\n",
+ __func__, ndctl_dimm_get_handle(dimm));
+ return -ENXIO;
+ }
+ cmd = ndctl_bus_cmd_new_ars_status(cmd_ars_cap);
+ if (!cmd) {
+ fprintf(stderr, "%s: bus: %s failed to create cmd\n",
+ __func__, ndctl_bus_get_provider(bus));
+ return -ENOTTY;
+ }
+
+ do {
+ rc = ndctl_cmd_submit(cmd);
+ if (rc) {
+ fprintf(stderr, "%s: bus: %s failed to submit cmd: %d\n",
+ __func__, ndctl_bus_get_provider(bus), rc);
+ ndctl_cmd_unref(cmd);
+ return rc;
+ }
+ } while (ndctl_cmd_ars_in_progress(cmd));
+
+ for (i = 0; i < ndctl_cmd_ars_num_records(cmd); i++) {
+ fprintf(stderr, "%s: record[%d].addr: 0x%x\n", __func__, i,
+ ndctl_cmd_ars_get_record_addr(cmd, i));
+ fprintf(stderr, "%s: record[%d].length: 0x%x\n", __func__, i,
+ ndctl_cmd_ars_get_record_len(cmd, i));
+ }
+
+ check->cmd = cmd;
+ return 0;
+}
+
#define BITS_PER_LONG 32
static int check_commands(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
- unsigned long commands)
+ unsigned long bus_commands, unsigned long dimm_commands)
{
/*
* For now, by coincidence, these are indexed in test execution
@@ -1210,19 +1329,25 @@ static int check_commands(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
* check_set_config_data can assume that both
* check_get_config_size and check_get_config_data have run
*/
- static struct check_cmd __check_cmds[] = {
+ static struct check_cmd __check_dimm_cmds[] = {
[ND_CMD_GET_CONFIG_SIZE] = { check_get_config_size },
[ND_CMD_GET_CONFIG_DATA] = { check_get_config_data },
[ND_CMD_SET_CONFIG_DATA] = { check_set_config_data },
[ND_CMD_SMART_THRESHOLD] = { },
};
+ static struct check_cmd __check_bus_cmds[] = {
+ [ND_CMD_ARS_CAP] = { check_ars_cap },
+ [ND_CMD_ARS_START] = { check_ars_start },
+ [ND_CMD_ARS_STATUS] = { check_ars_status },
+ };
unsigned int i, rc;
- check_cmds = __check_cmds;
+ /* Check DIMM commands */
+ check_cmds = __check_dimm_cmds;
for (i = 0; i < BITS_PER_LONG; i++) {
struct check_cmd *check = &check_cmds[i];
- if ((commands & (1UL << i)) == 0)
+ if ((dimm_commands & (1UL << i)) == 0)
continue;
if (!ndctl_dimm_is_cmd_supported(dimm, i)) {
fprintf(stderr, "%s: bus: %s dimm%d expected cmd: %s supported\n",
@@ -1235,22 +1360,53 @@ static int check_commands(struct ndctl_bus *bus, struct ndctl_dimm *dimm,
if (!check->check_fn)
continue;
- rc = check->check_fn(dimm, check);
+ rc = check->check_fn(bus, dimm, check);
+ if (rc)
+ break;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(__check_dimm_cmds); i++) {
+ if (__check_dimm_cmds[i].cmd)
+ ndctl_cmd_unref(__check_dimm_cmds[i].cmd);
+ __check_dimm_cmds[i].cmd = NULL;
+ }
+ if (rc)
+ goto out;
+
+ /* Check Bus commands */
+ check_cmds = __check_bus_cmds;
+ for (i = 1; i < BITS_PER_LONG; i++) {
+ struct check_cmd *check = &check_cmds[i];
+
+ if ((bus_commands & (1UL << i)) == 0)
+ continue;
+ if (!ndctl_bus_is_cmd_supported(bus, i)) {
+ fprintf(stderr, "%s: bus: %s expected cmd: %s supported\n",
+ __func__,
+ ndctl_bus_get_provider(bus),
+ ndctl_bus_get_cmd_name(bus, i));
+ return -ENXIO;
+ }
+
+ if (!check->check_fn)
+ continue;
+ rc = check->check_fn(bus, dimm, check);
if (rc)
break;
}
- for (i = 0; i < ARRAY_SIZE(__check_cmds); i++) {
- if (__check_cmds[i].cmd)
- ndctl_cmd_unref(__check_cmds[i].cmd);
- __check_cmds[i].cmd = NULL;
+ for (i = 1; i < ARRAY_SIZE(__check_bus_cmds); i++) {
+ if (__check_bus_cmds[i].cmd)
+ ndctl_cmd_unref(__check_bus_cmds[i].cmd);
+ __check_bus_cmds[i].cmd = NULL;
}
+ out:
return rc;
}
static int check_dimms(struct ndctl_bus *bus, struct dimm *dimms, int n,
- unsigned long commands)
+ unsigned long bus_commands, unsigned long dimm_commands)
{
int i, rc;
@@ -1296,7 +1452,7 @@ static int check_dimms(struct ndctl_bus *bus, struct dimm *dimms, int n,
return -ENXIO;
}
- rc = check_commands(bus, dimm, commands);
+ rc = check_commands(bus, dimm, bus_commands, dimm_commands);
if (rc)
return rc;
}
@@ -1317,7 +1473,8 @@ static int do_test0(struct ndctl_ctx *ctx)
ndctl_region_foreach(bus, region)
ndctl_region_disable_invalidate(region);
- rc = check_dimms(bus, dimms0, ARRAY_SIZE(dimms0), commands0);
+ rc = check_dimms(bus, dimms0, ARRAY_SIZE(dimms0), bus_commands0,
+ dimm_commands0);
if (rc)
return rc;
@@ -1336,7 +1493,7 @@ static int do_test1(struct ndctl_ctx *ctx)
if (!bus)
return -ENXIO;
- rc = check_dimms(bus, dimms1, ARRAY_SIZE(dimms1), 0);
+ rc = check_dimms(bus, dimms1, ARRAY_SIZE(dimms1), 0, 0);
if (rc)
return rc;
@@ -111,6 +111,11 @@ enum {
ND_CMD_VENDOR = 9,
};
+enum {
+ ND_ARS_VOLATILE = 1,
+ ND_ARS_PERSISTENT = 2,
+};
+
static __inline__ const char *nvdimm_bus_cmd_name(unsigned cmd)
{
static const char * const names[] = {
@@ -194,4 +199,9 @@ enum nd_driver_flags {
enum {
ND_MIN_NAMESPACE_SIZE = 0x00400000,
};
+
+enum ars_masks {
+ ARS_STATUS_MASK = 0x0000FFFF,
+ ARS_EXT_STATUS_SHIFT = 16,
+};
#endif /* __NDCTL_H__ */
Add support for the three ARS DSM commands: - Query ARS Capabilities - Queries the firmware to check if a given range supports scrub, and if so, which type (persistent vs. volatile) - Start ARS - Starts a scrub for a given range/type - Query ARS Status - Checks status of a previously started scrub, and provides the error logs if any. The commands are described by the example DSM spec at: http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf Also add these commands to the unit test which tests for canned values expected from the kernel's nfit_test modules. Signed-off-by: Vishal Verma <vishal.l.verma@intel.com> --- Makefile.am | 4 +- lib/libndctl.c | 209 +++++++++++++++++++++++++++++++++++++++++++++++++-- lib/libndctl.sym | 8 ++ lib/ndctl/libndctl.h | 11 +++ lib/test-libndctl.c | 193 ++++++++++++++++++++++++++++++++++++++++++----- ndctl.h | 10 +++ 6 files changed, 408 insertions(+), 27 deletions(-)