From patchwork Thu Jul 23 18:14:35 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Verma, Vishal L" X-Patchwork-Id: 6855351 X-Patchwork-Delegate: dan.j.williams@gmail.com Return-Path: X-Original-To: patchwork-linux-nvdimm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 7A7A79F358 for ; Thu, 23 Jul 2015 18:14:47 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id E03DD20684 for ; Thu, 23 Jul 2015 18:14:45 +0000 (UTC) Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 03C2320663 for ; Thu, 23 Jul 2015 18:14:44 +0000 (UTC) Received: from ml01.vlan14.01.org (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id EA4DC182A0E; Thu, 23 Jul 2015 11:14:43 -0700 (PDT) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received: from mga03.intel.com (mga03.intel.com [134.134.136.65]) by ml01.01.org (Postfix) with ESMTP id 86525182A0B for ; Thu, 23 Jul 2015 11:14:42 -0700 (PDT) Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by orsmga103.jf.intel.com with ESMTP; 23 Jul 2015 11:14:41 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.15,532,1432623600"; d="scan'208";a="768406852" Received: from vverma7-desk2.amr.corp.intel.com (HELO omniknight.lm.intel.com) ([10.232.112.76]) by fmsmga002.fm.intel.com with ESMTP; 23 Jul 2015 11:14:41 -0700 From: Vishal Verma To: linux-nvdimm@lists.01.org Subject: [ndctl PATCH 2/2] ndctl: Add DSM support for Address Range Scrub commands Date: Thu, 23 Jul 2015 12:14:35 -0600 Message-Id: <1437675275-13999-2-git-send-email-vishal.l.verma@intel.com> X-Mailer: git-send-email 2.4.3 In-Reply-To: <1437675275-13999-1-git-send-email-vishal.l.verma@intel.com> References: <1437675275-13999-1-git-send-email-vishal.l.verma@intel.com> X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.17 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Spam-Status: No, score=-3.8 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_LOW, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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(-) diff --git a/Makefile.am b/Makefile.am index cd6e190..070192f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/lib/libndctl.c b/lib/libndctl.c index 86a3a36..b87c7cf 100644 --- a/lib/libndctl.c +++ b/lib/libndctl.c @@ -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)); diff --git a/lib/libndctl.sym b/lib/libndctl.sym index 3391a09..3d56037 100644 --- a/lib/libndctl.sym +++ b/lib/libndctl.sym @@ -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; diff --git a/lib/ndctl/libndctl.h b/lib/ndctl/libndctl.h index c92910d..4c64f78 100644 --- a/lib/ndctl/libndctl.h +++ b/lib/ndctl/libndctl.h @@ -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, diff --git a/lib/test-libndctl.c b/lib/test-libndctl.c index 8c1d6c9..86441b2 100644 --- a/lib/test-libndctl.c +++ b/lib/test-libndctl.c @@ -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; diff --git a/ndctl.h b/ndctl.h index f4e15f8..076cc92 100644 --- a/ndctl.h +++ b/ndctl.h @@ -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__ */