From patchwork Mon May 17 08:40:16 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Santosh Sivaraj X-Patchwork-Id: 12261195 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8A2E4C433B4 for ; Mon, 17 May 2021 08:40:34 +0000 (UTC) Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id CE554610C9 for ; Mon, 17 May 2021 08:40:33 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org CE554610C9 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=fossix.org Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-nvdimm-bounces@lists.01.org Received: from ml01.vlan13.01.org (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 5E3BB100EC1CC; Mon, 17 May 2021 01:40:33 -0700 (PDT) Received-SPF: None (mailfrom) identity=mailfrom; client-ip=2607:f8b0:4864:20::631; helo=mail-pl1-x631.google.com; envelope-from=santosh@fossix.org; receiver= Received: from mail-pl1-x631.google.com (mail-pl1-x631.google.com [IPv6:2607:f8b0:4864:20::631]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id BE9CB100ED4BF for ; Mon, 17 May 2021 01:40:30 -0700 (PDT) Received: by mail-pl1-x631.google.com with SMTP id s4so1174563plg.12 for ; Mon, 17 May 2021 01:40:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fossix-org.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=0BzoAIo2PeDq60VSn07VhjCCw8Fo6LWvUAs/Qq5ud30=; b=O1g8Cqv1pacBUza7PWvIbOplXq2HYpt9Cxeu+q58GStnABLvoTqUF4K6z8FpP8E+Ti o5eNyBK/d6dyIkvmCdSQ2ER4mv4pZ0VJEc0/eQJlyLUe2XlgeWhCPWknMiOjBw5T1o07 lvXrh3IzZ8Nmi57RQUkO8hs8+AqCkmeJ6bagHUntv0HwhTsGM9S3ck8nWNKGcsxamXtM NbwyaIpUP3XNEuuYBj3oG4LuxYKf8HIN0vS1Rl297K0gao7ci8qcLqhx4qXjKrn/QKE7 BBwBokUhMhN2Z55JMGmb484s1WOwxNUiCUWy/nG21yUs7IUwkGe0Xm32KgkaNT7hHe2M 2Mwg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=0BzoAIo2PeDq60VSn07VhjCCw8Fo6LWvUAs/Qq5ud30=; b=hB4+eVrt13VNEJQ7k3c1CXTryrbdefzt8RfYsD4U+fMdV+OPcTJje/ceVZcHEaHvg9 my3TT0sYMaC/0R5rNgC3+yB3Rk3VXmZ0cnGwoI8b8RZ3Yp1b4FHOjixKD1LyZRzHv59m 3w6ylmrg9iV+tyNcY1q815ZD5IvbLbUcPQzJJIUB7l/hH5dmXufyv8jc26Cge0LuxYvL v5TawmH6RoObgvMpvvJcsQrhJdQ+nx+uL8pV5i4CYMC6OlQF2XrnMxn+wU75u6OIIoKT jnvMeDqbFK+iMNJOKVrptHOii9n//JR0EchbDvFRtwCFeeLL9uA8npQ9tfZ+qj8erB89 e42w== X-Gm-Message-State: AOAM533D9UsGx2tjuWp1Gfei5ax7KXKVI03OPWkWpJ6Ly4HmPgK9FyBv XALio5a13UyOQvhKDYEQpbGmfI2DfjXeEg== X-Google-Smtp-Source: ABdhPJzjyDgWbmwT0uurJ/yekSGtk12rYC/sVRvLEKFx58+401Z66xb0LHWtLZdWbQpBjdx+O0uKBA== X-Received: by 2002:a17:90a:4493:: with SMTP id t19mr19802723pjg.217.1621240827816; Mon, 17 May 2021 01:40:27 -0700 (PDT) Received: from desktop.fossix.local ([103.21.79.4]) by smtp.gmail.com with ESMTPSA id u1sm1264361pfc.63.2021.05.17.01.40.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 17 May 2021 01:40:27 -0700 (PDT) From: Santosh Sivaraj To: Linux NVDIMM Subject: [v2 1/2] tests/nvdimm/ndtest: Enable smart tests Date: Mon, 17 May 2021 14:10:16 +0530 Message-Id: <20210517084017.180501-1-santosh@fossix.org> X-Mailer: git-send-email 2.31.1 MIME-Version: 1.0 Message-ID-Hash: DRYGWXBOFNIKEAL45Y3O3PB3TJMYIC5M X-Message-ID-Hash: DRYGWXBOFNIKEAL45Y3O3PB3TJMYIC5M X-MailFrom: santosh@fossix.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header CC: Shivaprasad G Bhat , Harish Sriram , "Aneesh Kumar K.V" , Shivaprasad G Bhat X-Mailman-Version: 3.1.1 Precedence: list List-Id: "Linux-nvdimm developer list." Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: From: Shivaprasad G Bhat The patch adds necessary health related dsm command implementations for the ndctl inject-smart and monitor tests to pass. Signed-off-by: Shivaprasad G Bhat --- tools/testing/nvdimm/test/ndtest.c | 258 +++++++++++++++++++++++++++++ tools/testing/nvdimm/test/ndtest.h | 129 +++++++++++++++ 2 files changed, 387 insertions(+) diff --git a/tools/testing/nvdimm/test/ndtest.c b/tools/testing/nvdimm/test/ndtest.c index 6862915f1fb0..bb47b145466d 100644 --- a/tools/testing/nvdimm/test/ndtest.c +++ b/tools/testing/nvdimm/test/ndtest.c @@ -30,6 +30,8 @@ enum { ((1ul << ND_CMD_GET_CONFIG_SIZE) | \ (1ul << ND_CMD_GET_CONFIG_DATA) | \ (1ul << ND_CMD_SET_CONFIG_DATA) | \ + (1ul << ND_CMD_SMART_THRESHOLD) | \ + (1uL << ND_CMD_SMART) | \ (1ul << ND_CMD_CALL)) #define NFIT_DIMM_HANDLE(node, socket, imc, chan, dimm) \ @@ -41,6 +43,21 @@ static struct ndtest_priv *instances[NUM_INSTANCES]; static struct class *ndtest_dimm_class; static struct gen_pool *ndtest_pool; +static const struct nd_papr_pdsm_health health_defaults = { + .dimm_unarmed = 0, + .dimm_bad_shutdown = 0, + .dimm_health = PAPR_PDSM_DIMM_UNHEALTHY, + .extension_flags = PDSM_DIMM_HEALTH_MEDIA_TEMPERATURE_VALID | PDSM_DIMM_HEALTH_ALARM_VALID | + PDSM_DIMM_HEALTH_CTRL_TEMPERATURE_VALID | PDSM_DIMM_HEALTH_SPARES_VALID | + PDSM_DIMM_HEALTH_RUN_GAUGE_VALID, + .dimm_fuel_gauge = 95, + .media_temperature = 23 * 16, + .ctrl_temperature = 25 * 16, + .spares = 75, + .alarm_flags = ND_PAPR_HEALTH_SPARE_TRIP | + ND_PAPR_HEALTH_TEMP_TRIP, +}; + static struct ndtest_dimm dimm_group1[] = { { .size = DIMM_SIZE, @@ -48,6 +65,16 @@ static struct ndtest_dimm dimm_group1[] = { .uuid_str = "1e5c75d2-b618-11ea-9aa3-507b9ddc0f72", .physical_id = 0, .num_formats = 2, + .flags = PAPR_PMEM_HEALTH_NON_CRITICAL, + .extension_flags = health_defaults.extension_flags, + .dimm_fuel_gauge = health_defaults.dimm_fuel_gauge, + .media_temperature = health_defaults.media_temperature, + .ctrl_temperature = health_defaults.ctrl_temperature, + .spares = health_defaults.spares, + .alarm_flags = health_defaults.alarm_flags, + .media_temperature_threshold = 40 * 16, + .ctrl_temperature_threshold = 30 * 16, + .spares_threshold = 5, }, { .size = DIMM_SIZE, @@ -55,6 +82,16 @@ static struct ndtest_dimm dimm_group1[] = { .uuid_str = "1c4d43ac-b618-11ea-be80-507b9ddc0f72", .physical_id = 1, .num_formats = 2, + .flags = PAPR_PMEM_HEALTH_NON_CRITICAL, + .extension_flags = health_defaults.extension_flags, + .dimm_fuel_gauge = health_defaults.dimm_fuel_gauge, + .media_temperature = health_defaults.media_temperature, + .ctrl_temperature = health_defaults.ctrl_temperature, + .spares = health_defaults.spares, + .alarm_flags = health_defaults.alarm_flags, + .media_temperature_threshold = 40 * 16, + .ctrl_temperature_threshold = 30 * 16, + .spares_threshold = 5, }, { .size = DIMM_SIZE, @@ -62,6 +99,16 @@ static struct ndtest_dimm dimm_group1[] = { .uuid_str = "a9f17ffc-b618-11ea-b36d-507b9ddc0f72", .physical_id = 2, .num_formats = 2, + .flags = PAPR_PMEM_HEALTH_NON_CRITICAL, + .extension_flags = health_defaults.extension_flags, + .dimm_fuel_gauge = health_defaults.dimm_fuel_gauge, + .media_temperature = health_defaults.media_temperature, + .ctrl_temperature = health_defaults.ctrl_temperature, + .spares = health_defaults.spares, + .alarm_flags = health_defaults.alarm_flags, + .media_temperature_threshold = 40 * 16, + .ctrl_temperature_threshold = 30 * 16, + .spares_threshold = 5, }, { .size = DIMM_SIZE, @@ -69,6 +116,16 @@ static struct ndtest_dimm dimm_group1[] = { .uuid_str = "b6b83b22-b618-11ea-8aae-507b9ddc0f72", .physical_id = 3, .num_formats = 2, + .flags = PAPR_PMEM_HEALTH_NON_CRITICAL, + .extension_flags = health_defaults.extension_flags, + .dimm_fuel_gauge = health_defaults.dimm_fuel_gauge, + .media_temperature = health_defaults.media_temperature, + .ctrl_temperature = health_defaults.ctrl_temperature, + .spares = health_defaults.spares, + .alarm_flags = health_defaults.alarm_flags, + .media_temperature_threshold = 40 * 16, + .ctrl_temperature_threshold = 30 * 16, + .spares_threshold = 5, }, { .size = DIMM_SIZE, @@ -296,6 +353,172 @@ static int ndtest_get_config_size(struct ndtest_dimm *dimm, unsigned int buf_len return 0; } +static int ndtest_pdsm_health(struct ndtest_dimm *dimm, + union nd_pdsm_payload *payload, + unsigned int buf_len) +{ + struct nd_papr_pdsm_health *health = &payload->health; + + if (buf_len < sizeof(health)) + return -EINVAL; + + health->extension_flags = 0; + health->dimm_unarmed = !!(dimm->flags & PAPR_PMEM_UNARMED_MASK); + health->dimm_bad_shutdown = !!(dimm->flags & PAPR_PMEM_BAD_SHUTDOWN_MASK); + health->dimm_bad_restore = !!(dimm->flags & PAPR_PMEM_BAD_RESTORE_MASK); + health->dimm_health = PAPR_PDSM_DIMM_HEALTHY; + + if (dimm->flags & PAPR_PMEM_HEALTH_FATAL) + health->dimm_health = PAPR_PDSM_DIMM_FATAL; + else if (dimm->flags & PAPR_PMEM_HEALTH_CRITICAL) + health->dimm_health = PAPR_PDSM_DIMM_CRITICAL; + else if (dimm->flags & PAPR_PMEM_HEALTH_UNHEALTHY || + dimm->flags & PAPR_PMEM_HEALTH_NON_CRITICAL) + health->dimm_health = PAPR_PDSM_DIMM_UNHEALTHY; + + health->extension_flags = 0; + if (dimm->extension_flags & PDSM_DIMM_HEALTH_RUN_GAUGE_VALID) { + health->dimm_fuel_gauge = dimm->dimm_fuel_gauge; + health->extension_flags |= PDSM_DIMM_HEALTH_RUN_GAUGE_VALID; + } + if (dimm->extension_flags & PDSM_DIMM_HEALTH_MEDIA_TEMPERATURE_VALID) { + health->media_temperature = dimm->media_temperature; + health->extension_flags |= PDSM_DIMM_HEALTH_MEDIA_TEMPERATURE_VALID; + } + if (dimm->extension_flags & PDSM_DIMM_HEALTH_CTRL_TEMPERATURE_VALID) { + health->ctrl_temperature = dimm->ctrl_temperature; + health->extension_flags |= PDSM_DIMM_HEALTH_CTRL_TEMPERATURE_VALID; + } + if (dimm->extension_flags & PDSM_DIMM_HEALTH_SPARES_VALID) { + health->spares = dimm->spares; + health->extension_flags |= PDSM_DIMM_HEALTH_SPARES_VALID; + } + if (dimm->extension_flags & PDSM_DIMM_HEALTH_ALARM_VALID) { + health->alarm_flags = dimm->alarm_flags; + health->extension_flags |= PDSM_DIMM_HEALTH_ALARM_VALID; + } + + return 0; +} + +static void smart_notify(struct ndtest_dimm *dimm) +{ + struct device *bus = dimm->dev->parent; + + if (((dimm->alarm_flags & ND_PAPR_HEALTH_SPARE_TRIP) && + dimm->spares <= dimm->spares_threshold) || + ((dimm->alarm_flags & ND_PAPR_HEALTH_TEMP_TRIP) && + dimm->media_temperature >= dimm->media_temperature_threshold) || + ((dimm->alarm_flags & ND_PAPR_HEALTH_CTEMP_TRIP) && + dimm->ctrl_temperature >= dimm->ctrl_temperature_threshold) || + !(dimm->flags & PAPR_PMEM_HEALTH_NON_CRITICAL) || + (dimm->flags & PAPR_PMEM_BAD_SHUTDOWN_MASK)) { + device_lock(bus); + /* send smart notification */ + if (dimm->notify_handle) + sysfs_notify_dirent(dimm->notify_handle); + device_unlock(bus); + } +} + +static int ndtest_pdsm_health_inject(struct ndtest_dimm *dimm, + union nd_pdsm_payload *payload, + unsigned int buf_len) +{ + struct nd_papr_pdsm_health_inject *inj = &payload->inject; + + if (buf_len < sizeof(inj)) + return -EINVAL; + + if (inj->flags & ND_PAPR_HEALTH_INJECT_MTEMP) { + if (inj->mtemp_enable) + dimm->media_temperature = inj->media_temperature; + else + dimm->media_temperature = health_defaults.media_temperature; + } + if (inj->flags & ND_PAPR_HEALTH_INJECT_SPARE) { + if (inj->spares_enable) + dimm->spares = inj->spares; + else + dimm->spares = health_defaults.spares; + } + if (inj->flags & ND_PAPR_HEALTH_INJECT_FATAL) { + if (inj->fatal_enable) + dimm->flags |= PAPR_PMEM_HEALTH_FATAL; + else + dimm->flags &= ~PAPR_PMEM_HEALTH_FATAL; + } + if (inj->flags & ND_PAPR_HEALTH_INJECT_SHUTDOWN) { + if (inj->unsafe_shutdown_enable) + dimm->flags |= PAPR_PMEM_SHUTDOWN_DIRTY; + else + dimm->flags &= ~PAPR_PMEM_SHUTDOWN_DIRTY; + } + smart_notify(dimm); + inj->status = 0; + + return 0; +} + +static int ndtest_pdsm_health_threshold(struct ndtest_dimm *dimm, + union nd_pdsm_payload *payload, + unsigned int buf_len) +{ + struct nd_papr_pdsm_health_threshold *threshold = &payload->threshold; + + if (buf_len < sizeof(threshold)) + return -EINVAL; + + threshold->media_temperature = dimm->media_temperature_threshold; + threshold->ctrl_temperature = dimm->ctrl_temperature_threshold; + threshold->spares = dimm->spares_threshold; + threshold->alarm_control = dimm->alarm_flags; + + return 0; +} + +static int ndtest_pdsm_health_set_threshold(struct ndtest_dimm *dimm, + union nd_pdsm_payload *payload, + unsigned int buf_len) +{ + struct nd_papr_pdsm_health_threshold *threshold = &payload->threshold; + + if (buf_len < sizeof(threshold)) + return -EINVAL; + + dimm->media_temperature_threshold = threshold->media_temperature; + dimm->ctrl_temperature_threshold = threshold->ctrl_temperature; + dimm->spares_threshold = threshold->spares; + dimm->alarm_flags = threshold->alarm_control; + + smart_notify(dimm); + + return 0; +} + +static int ndtest_dimm_cmd_call(struct ndtest_dimm *dimm, unsigned int buf_len, + void *buf) +{ + struct nd_cmd_pkg *call_pkg = buf; + unsigned int len = call_pkg->nd_size_in + call_pkg->nd_size_out; + struct nd_pkg_pdsm *pdsm = (struct nd_pkg_pdsm *) call_pkg->nd_payload; + union nd_pdsm_payload *payload = &(pdsm->payload); + unsigned int func = call_pkg->nd_command; + + switch (func) { + case PAPR_PDSM_HEALTH: + return ndtest_pdsm_health(dimm, payload, len); + case PAPR_PDSM_HEALTH_INJECT: + return ndtest_pdsm_health_inject(dimm, payload, len); + case PAPR_PDSM_HEALTH_THRESHOLD: + return ndtest_pdsm_health_threshold(dimm, payload, len); + case PAPR_PDSM_HEALTH_THRESHOLD_SET: + return ndtest_pdsm_health_set_threshold(dimm, payload, len); + } + + return 0; +} + static int ndtest_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc) @@ -325,6 +548,9 @@ static int ndtest_ctl(struct nvdimm_bus_descriptor *nd_desc, case ND_CMD_SET_CONFIG_DATA: *cmd_rc = ndtest_config_set(dimm, buf_len, buf); break; + case ND_CMD_CALL: + *cmd_rc = ndtest_dimm_cmd_call(dimm, buf_len, buf); + break; default: return -EINVAL; } @@ -826,6 +1052,20 @@ static ssize_t flags_show(struct device *dev, } static DEVICE_ATTR_RO(flags); +#define PAPR_PMEM_DIMM_CMD_MASK \ + ((1U << PAPR_PDSM_HEALTH) \ + | (1U << PAPR_PDSM_HEALTH_INJECT) \ + | (1U << PAPR_PDSM_HEALTH_THRESHOLD) \ + | (1U << PAPR_PDSM_HEALTH_THRESHOLD_SET)) + + +static ssize_t dsm_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%#x\n", PAPR_PMEM_DIMM_CMD_MASK); +} +static DEVICE_ATTR_RO(dsm_mask); + static struct attribute *ndtest_nvdimm_attributes[] = { &dev_attr_nvdimm_show_handle.attr, &dev_attr_vendor.attr, @@ -837,6 +1077,7 @@ static struct attribute *ndtest_nvdimm_attributes[] = { &dev_attr_format.attr, &dev_attr_format1.attr, &dev_attr_flags.attr, + &dev_attr_dsm_mask.attr, NULL, }; @@ -856,6 +1097,7 @@ static int ndtest_dimm_register(struct ndtest_priv *priv, { struct device *dev = &priv->pdev.dev; unsigned long dimm_flags = dimm->flags; + struct kernfs_node *papr_kernfs; if (dimm->num_formats > 1) { set_bit(NDD_ALIASING, &dimm_flags); @@ -882,6 +1124,20 @@ static int ndtest_dimm_register(struct ndtest_priv *priv, return -ENOMEM; } + nd_synchronize(); + + papr_kernfs = sysfs_get_dirent(nvdimm_kobj(dimm->nvdimm)->sd, "papr"); + if (!papr_kernfs) { + pr_err("Could not initialize the notifier handle\n"); + return 0; + } + + dimm->notify_handle = sysfs_get_dirent(papr_kernfs, "flags"); + sysfs_put(papr_kernfs); + if (!dimm->notify_handle) { + pr_err("Could not initialize the notifier handle\n"); + return 0; + } return 0; } @@ -953,6 +1209,8 @@ static int ndtest_bus_register(struct ndtest_priv *p) p->bus_desc.provider_name = NULL; p->bus_desc.attr_groups = ndtest_attribute_groups; + set_bit(NVDIMM_FAMILY_PAPR, &p->bus_desc.dimm_family_mask); + p->bus = nvdimm_bus_register(&p->pdev.dev, &p->bus_desc); if (!p->bus) { dev_err(&p->pdev.dev, "Error creating nvdimm bus %pOF\n", p->dn); diff --git a/tools/testing/nvdimm/test/ndtest.h b/tools/testing/nvdimm/test/ndtest.h index 2c54c9cbb90c..d29638b6a332 100644 --- a/tools/testing/nvdimm/test/ndtest.h +++ b/tools/testing/nvdimm/test/ndtest.h @@ -16,6 +16,8 @@ #define PAPR_PMEM_HEALTH_FATAL (1ULL << (63 - 5)) /* SCM contents cannot persist due to current platform health status */ #define PAPR_PMEM_HEALTH_UNHEALTHY (1ULL << (63 - 6)) +/* SCM device is unable to persist memory contents in certain conditions */ +#define PAPR_PMEM_HEALTH_NON_CRITICAL (1ULL << (63 - 7)) /* Bits status indicators for health bitmap indicating unarmed dimm */ #define PAPR_PMEM_UNARMED_MASK (PAPR_PMEM_UNARMED | \ @@ -38,6 +40,49 @@ struct ndtest_config; +/* DIMM Health extension flag bits */ +#define PDSM_DIMM_HEALTH_RUN_GAUGE_VALID (1 << 0) +#define PDSM_DIMM_HEALTH_MEDIA_TEMPERATURE_VALID (1 << 1) +#define PDSM_DIMM_HEALTH_CTRL_TEMPERATURE_VALID (1 << 2) +#define PDSM_DIMM_HEALTH_SHUTDOWN_COUNT_VALID (1 << 3) +#define PDSM_DIMM_HEALTH_SPARES_VALID (1 << 4) +#define PDSM_DIMM_HEALTH_ALARM_VALID (1 << 5) + +#define PAPR_PDSM_DIMM_HEALTHY 0 + +#define ND_PAPR_HEALTH_SPARE_TRIP (1 << 0) +#define ND_PAPR_HEALTH_TEMP_TRIP (1 << 1) +#define ND_PAPR_HEALTH_CTEMP_TRIP (1 << 2) + +/* DIMM Health inject flag bits */ +#define ND_PAPR_HEALTH_INJECT_MTEMP (1 << 0) +#define ND_PAPR_HEALTH_INJECT_SPARE (1 << 1) +#define ND_PAPR_HEALTH_INJECT_FATAL (1 << 2) +#define ND_PAPR_HEALTH_INJECT_SHUTDOWN (1 << 3) + +/* Various nvdimm health indicators */ +#define PAPR_PDSM_DIMM_HEALTHY 0 +#define PAPR_PDSM_DIMM_UNHEALTHY 1 +#define PAPR_PDSM_DIMM_CRITICAL 2 +#define PAPR_PDSM_DIMM_FATAL 3 + +enum papr_pdsm { + PAPR_PDSM_MIN = 0x0, + PAPR_PDSM_HEALTH, + PAPR_PDSM_INJECT_SET = 11, + PAPR_PDSM_INJECT_CLEAR = 12, + PAPR_PDSM_INJECT_GET = 13, + PAPR_PDSM_HEALTH_INJECT = 14, + PAPR_PDSM_HEALTH_THRESHOLD = 15, + PAPR_PDSM_HEALTH_THRESHOLD_SET = 16, + PAPR_PDSM_MAX, +}; + +enum dimm_type { + NDTEST_REGION_TYPE_PMEM = 0x0, + NDTEST_REGION_TYPE_BLK = 0x1, +}; + struct ndtest_priv { struct platform_device pdev; struct device_node *dn; @@ -80,6 +125,21 @@ struct ndtest_dimm { int id; int fail_cmd_code; u8 no_alias; + + struct kernfs_node *notify_handle; + + /* SMART Health information */ + unsigned long long extension_flags; + __u16 dimm_fuel_gauge; + __u16 media_temperature; + __u16 ctrl_temperature; + __u8 spares; + __u8 alarm_flags; + + /* SMART Health thresholds */ + __u16 media_temperature_threshold; + __u16 ctrl_temperature_threshold; + __u8 spares_threshold; }; struct ndtest_mapping { @@ -106,4 +166,73 @@ struct ndtest_config { u8 num_regions; }; +#define ND_PDSM_PAYLOAD_MAX_SIZE 184 + +struct nd_papr_pdsm_health { + union { + struct { + __u32 extension_flags; + __u8 dimm_unarmed; + __u8 dimm_bad_shutdown; + __u8 dimm_bad_restore; + __u8 dimm_scrubbed; + __u8 dimm_locked; + __u8 dimm_encrypted; + __u16 dimm_health; + + /* Extension flag PDSM_DIMM_HEALTH_RUN_GAUGE_VALID */ + __u16 dimm_fuel_gauge; + __u16 media_temperature; + __u16 ctrl_temperature; + __u8 spares; + __u16 alarm_flags; + }; + __u8 buf[ND_PDSM_PAYLOAD_MAX_SIZE]; + }; +}; + +struct nd_papr_pdsm_health_threshold { + union { + struct { + __u16 alarm_control; + __u8 spares; + __u16 media_temperature; + __u16 ctrl_temperature; + __u32 status; + }; + __u8 buf[ND_PDSM_PAYLOAD_MAX_SIZE]; + }; +}; + +struct nd_papr_pdsm_health_inject { + union { + struct { + __u64 flags; + __u8 mtemp_enable; + __u16 media_temperature; + __u8 ctemp_enable; + __u16 ctrl_temperature; + __u8 spares_enable; + __u8 spares; + __u8 fatal_enable; + __u8 unsafe_shutdown_enable; + __u32 status; + }; + __u8 buf[ND_PDSM_PAYLOAD_MAX_SIZE]; + }; +}; + +union nd_pdsm_payload { + struct nd_papr_pdsm_health health; + struct nd_papr_pdsm_health_inject inject; + struct nd_papr_pdsm_health_threshold threshold; + __u8 buf[ND_PDSM_PAYLOAD_MAX_SIZE]; +} __packed; + +struct nd_pkg_pdsm { + __s32 cmd_status; /* Out: Sub-cmd status returned back */ + __u16 reserved[2]; /* Ignored and to be set as '0' */ + union nd_pdsm_payload payload; +} __packed; + #endif /* NDTEST_H */ From patchwork Mon May 17 08:40:17 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Santosh Sivaraj X-Patchwork-Id: 12261197 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 13C13C433B4 for ; Mon, 17 May 2021 08:40:38 +0000 (UTC) Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 9D45E610C9 for ; Mon, 17 May 2021 08:40:37 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 9D45E610C9 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=fossix.org Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-nvdimm-bounces@lists.01.org Received: from ml01.vlan13.01.org (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 7BECF100EB834; Mon, 17 May 2021 01:40:37 -0700 (PDT) Received-SPF: None (mailfrom) identity=mailfrom; client-ip=2607:f8b0:4864:20::42e; helo=mail-pf1-x42e.google.com; envelope-from=santosh@fossix.org; receiver= Received: from mail-pf1-x42e.google.com (mail-pf1-x42e.google.com [IPv6:2607:f8b0:4864:20::42e]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 615EA100EB831 for ; Mon, 17 May 2021 01:40:34 -0700 (PDT) Received: by mail-pf1-x42e.google.com with SMTP id d16so4435392pfn.12 for ; Mon, 17 May 2021 01:40:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fossix-org.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=9dTjx1xcdhCQg8anHyE7el4qSg3ICYwRiR07kQ0CmXo=; b=WB1mLaH13Rw8NlmFlwpd7vuTgVM7QSBc4fKBbd4Xl1kdwhSEDnEvgNbhwqqvNBiyV+ ZXoMEOna8pTOxSXwQGw1XOBQ4UfXGXfJQ72vpduVHVoyIFx9GdRQujS7HTxelflPg4xh f43Ri37FmeEugiY9SkqjkIgS2aBSAxY8s19S0XSzLuMgIS6I26shAEyoqz7FzDaBRZ13 8zUvPuTZifyv/ExkuEb5RSL9vxNuGqiYC2WVbcPm/TQAFrWWUZEkd7EH+TKFieNy2QVo tlsPqhZr4loN1FijAyM+zAaEokrGmw5lvU/xQZ4FD8AmvJ1gQwRuBfi6plOL/33dEgEk bc6A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=9dTjx1xcdhCQg8anHyE7el4qSg3ICYwRiR07kQ0CmXo=; b=Gzlt3GBkHg2xTlY9lbzSIWa0ISI7ePVQhDs+RnVkMQM73JqXU0tadeH4kSmHMgFtD3 rTVrzFRTl6mj1mHKtSpgDbw7smZKM4dOmYRsjbM/oHQ/aOA8oWYybtVMSuyPXDbV+j8L tfZorkN95FRmuNLxzE+rbe3/X4ufBvcrSSd98VmiKyfWsNGygOmHDY/iTmvZo+3LWgnb S7iEvSgRmLCAbgegq36QUqhyvIQZq98FZJjhtSl640cLQa3QDU8x0cHROsuzdCLFyeJy prAskkGqmshCW2KpXKEPmjOkX2c4wTua1hn/nLYlG1kxji1XWUICqp5B7mOGZSjq9eNI 21eA== X-Gm-Message-State: AOAM530Zd/kAmvVTBERXHMaYpsZsqs+Sgibxhrm5NH9mFkVZM2i6JgFQ cG7ECjTEsHI0CXtR5yVmlxa2/eRHNpM7mA== X-Google-Smtp-Source: ABdhPJzG83tBKR1CBiJNdJAbI5i1+H9YUKPTTp6t8uIiwQXlizmG4TFVU5j6b6+4wlKCZlMlRHpBlQ== X-Received: by 2002:aa7:9885:0:b029:28e:9f7f:f23 with SMTP id r5-20020aa798850000b029028e9f7f0f23mr59843669pfl.75.1621240833524; Mon, 17 May 2021 01:40:33 -0700 (PDT) Received: from desktop.fossix.local ([103.21.79.4]) by smtp.gmail.com with ESMTPSA id u1sm1264361pfc.63.2021.05.17.01.40.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 17 May 2021 01:40:33 -0700 (PDT) From: Santosh Sivaraj To: Linux NVDIMM Subject: [v2 2/2] nvdimm/ndtest: Add support for error injection tests Date: Mon, 17 May 2021 14:10:17 +0530 Message-Id: <20210517084017.180501-2-santosh@fossix.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20210517084017.180501-1-santosh@fossix.org> References: <20210517084017.180501-1-santosh@fossix.org> MIME-Version: 1.0 Message-ID-Hash: ZTYW5GEYABXWF4UJTE2ABOJJIBOL4DOG X-Message-ID-Hash: ZTYW5GEYABXWF4UJTE2ABOJJIBOL4DOG X-MailFrom: santosh@fossix.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header CC: Shivaprasad G Bhat , Harish Sriram , "Aneesh Kumar K.V" X-Mailman-Version: 3.1.1 Precedence: list List-Id: "Linux-nvdimm developer list." Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: Add necessary support for error injection family of tests on non-acpi platforms. Signed-off-by: Santosh Sivaraj --- tools/testing/nvdimm/test/ndtest.c | 455 ++++++++++++++++++++++++++++- tools/testing/nvdimm/test/ndtest.h | 25 ++ 2 files changed, 477 insertions(+), 3 deletions(-) diff --git a/tools/testing/nvdimm/test/ndtest.c b/tools/testing/nvdimm/test/ndtest.c index bb47b145466d..09d98317bf4e 100644 --- a/tools/testing/nvdimm/test/ndtest.c +++ b/tools/testing/nvdimm/test/ndtest.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define pr_fmt(fmt) "ndtest :" fmt #include #include @@ -42,6 +42,7 @@ static DEFINE_SPINLOCK(ndtest_lock); static struct ndtest_priv *instances[NUM_INSTANCES]; static struct class *ndtest_dimm_class; static struct gen_pool *ndtest_pool; +static struct workqueue_struct *ndtest_wq; static const struct nd_papr_pdsm_health health_defaults = { .dimm_unarmed = 0, @@ -496,6 +497,139 @@ static int ndtest_pdsm_health_set_threshold(struct ndtest_dimm *dimm, return 0; } +static void ars_complete_all(struct ndtest_priv *p) +{ + int i; + + for (i = 0; i < p->config->num_regions; i++) { + struct ndtest_region *region = &p->config->regions[i]; + + if (region->region) + nvdimm_region_notify(region->region, + NVDIMM_REVALIDATE_POISON); + } +} + +static void ndtest_scrub(struct work_struct *work) +{ + struct ndtest_priv *p = container_of(work, typeof(struct ndtest_priv), + dwork.work); + struct badrange_entry *be; + int rc, i = 0; + + spin_lock(&p->badrange.lock); + list_for_each_entry(be, &p->badrange.list, list) { + rc = nvdimm_bus_add_badrange(p->bus, be->start, be->length); + if (rc) + dev_err(&p->pdev.dev, "Failed to process ARS records\n"); + else + i++; + } + spin_unlock(&p->badrange.lock); + + if (i == 0) { + queue_delayed_work(ndtest_wq, &p->dwork, HZ); + return; + } + + ars_complete_all(p); + p->scrub_count++; + + mutex_lock(&p->ars_lock); + sysfs_notify_dirent(p->scrub_state); + clear_bit(ARS_BUSY, &p->scrub_flags); + clear_bit(ARS_POLL, &p->scrub_flags); + set_bit(ARS_VALID, &p->scrub_flags); + mutex_unlock(&p->ars_lock); + +} + +static int ndtest_scrub_notify(struct ndtest_priv *p) +{ + if (!test_and_set_bit(ARS_BUSY, &p->scrub_flags)) + queue_delayed_work(ndtest_wq, &p->dwork, HZ); + + return 0; +} + +static int ndtest_ars_inject(struct ndtest_priv *p, + struct nd_cmd_ars_err_inj *inj, + unsigned int buf_len) +{ + int rc; + + if (buf_len != sizeof(*inj)) { + dev_dbg(&p->bus->dev, "buflen: %u, inj size: %lu\n", + buf_len, sizeof(*inj)); + rc = -EINVAL; + goto err; + } + + rc = badrange_add(&p->badrange, inj->err_inj_spa_range_base, + inj->err_inj_spa_range_length); + + if (inj->err_inj_options & (1 << ND_ARS_ERR_INJ_OPT_NOTIFY)) + ndtest_scrub_notify(p); + + inj->status = 0; + + return 0; + +err: + inj->status = NFIT_ARS_INJECT_INVALID; + return rc; +} + +static int ndtest_ars_inject_clear(struct ndtest_priv *p, + struct nd_cmd_ars_err_inj_clr *inj, + unsigned int buf_len) +{ + int rc; + + if (buf_len != sizeof(*inj)) { + rc = -EINVAL; + goto err; + } + + if (inj->err_inj_clr_spa_range_length <= 0) { + rc = -EINVAL; + goto err; + } + + badrange_forget(&p->badrange, inj->err_inj_clr_spa_range_base, + inj->err_inj_clr_spa_range_length); + + inj->status = 0; + return 0; + +err: + inj->status = NFIT_ARS_INJECT_INVALID; + return rc; +} + +static int ndtest_ars_inject_status(struct ndtest_priv *p, + struct nd_cmd_ars_err_inj_stat *stat, + unsigned int buf_len) +{ + struct badrange_entry *be; + int max = SZ_4K / sizeof(struct nd_error_stat_query_record); + int i = 0; + + stat->status = 0; + spin_lock(&p->badrange.lock); + list_for_each_entry(be, &p->badrange.list, list) { + stat->record[i].err_inj_stat_spa_range_base = be->start; + stat->record[i].err_inj_stat_spa_range_length = be->length; + i++; + if (i > max) + break; + } + spin_unlock(&p->badrange.lock); + stat->inj_err_rec_count = i; + + return 0; +} + static int ndtest_dimm_cmd_call(struct ndtest_dimm *dimm, unsigned int buf_len, void *buf) { @@ -519,6 +653,157 @@ static int ndtest_dimm_cmd_call(struct ndtest_dimm *dimm, unsigned int buf_len, return 0; } +static int ndtest_bus_cmd_call(struct nvdimm_bus_descriptor *nd_desc, void *buf, + unsigned int buf_len, int *cmd_rc) +{ + struct nd_cmd_pkg *pkg = buf; + struct ndtest_priv *p = container_of(nd_desc, struct ndtest_priv, + bus_desc); + void *payload = pkg->nd_payload; + unsigned int func = pkg->nd_command; + unsigned int len = pkg->nd_size_in + pkg->nd_size_out; + + switch (func) { + case PAPR_PDSM_INJECT_SET: + return ndtest_ars_inject(p, payload, len); + case PAPR_PDSM_INJECT_CLEAR: + return ndtest_ars_inject_clear(p, payload, len); + case PAPR_PDSM_INJECT_GET: + return ndtest_ars_inject_status(p, payload, len); + } + + return -ENOTTY; +} + +static int ndtest_cmd_ars_cap(struct ndtest_priv *p, struct nd_cmd_ars_cap *cmd, + unsigned int buf_len) +{ + int ars_recs; + + if (buf_len < sizeof(*cmd)) + return -EINVAL; + + /* for testing, only store up to n records that fit within a page */ + ars_recs = SZ_4K / sizeof(struct nd_ars_record); + + cmd->max_ars_out = sizeof(struct nd_cmd_ars_status) + + ars_recs * sizeof(struct nd_ars_record); + cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16; + cmd->clear_err_unit = 256; + p->max_ars = cmd->max_ars_out; + + return 0; +} + +static void post_ars_status(struct ars_state *state, + struct badrange *badrange, u64 addr, u64 len) +{ + struct nd_cmd_ars_status *status; + struct nd_ars_record *record; + struct badrange_entry *be; + u64 end = addr + len - 1; + int i = 0; + + state->deadline = jiffies + 1*HZ; + status = state->ars_status; + status->status = 0; + status->address = addr; + status->length = len; + status->type = ND_ARS_PERSISTENT; + + spin_lock(&badrange->lock); + list_for_each_entry(be, &badrange->list, list) { + u64 be_end = be->start + be->length - 1; + u64 rstart, rend; + + /* skip entries outside the range */ + if (be_end < addr || be->start > end) + continue; + + rstart = (be->start < addr) ? addr : be->start; + rend = (be_end < end) ? be_end : end; + record = &status->records[i]; + record->handle = 0; + record->err_address = rstart; + record->length = rend - rstart + 1; + i++; + } + spin_unlock(&badrange->lock); + + status->num_records = i; + status->out_length = sizeof(struct nd_cmd_ars_status) + + i * sizeof(struct nd_ars_record); +} + +#define NFIT_ARS_STATUS_BUSY (1 << 16) +#define NFIT_ARS_START_BUSY 6 + +static int ndtest_cmd_ars_start(struct ndtest_priv *priv, + struct nd_cmd_ars_start *start, + unsigned int buf_len, int *cmd_rc) +{ + if (buf_len < sizeof(*start)) + return -EINVAL; + + spin_lock(&priv->state.lock); + if (time_before(jiffies, priv->state.deadline)) { + start->status = NFIT_ARS_START_BUSY; + *cmd_rc = -EBUSY; + } else { + start->status = 0; + start->scrub_time = 1; + post_ars_status(&priv->state, &priv->badrange, + start->address, start->length); + *cmd_rc = 0; + } + spin_unlock(&priv->state.lock); + + return 0; +} + +static int ndtest_cmd_ars_status(struct ndtest_priv *priv, + struct nd_cmd_ars_status *status, + unsigned int buf_len, int *cmd_rc) +{ + if (buf_len < priv->state.ars_status->out_length) + return -EINVAL; + + spin_lock(&priv->state.lock); + if (time_before(jiffies, priv->state.deadline)) { + memset(status, 0, buf_len); + status->status = NFIT_ARS_STATUS_BUSY; + status->out_length = sizeof(*status); + *cmd_rc = -EBUSY; + } else { + memcpy(status, priv->state.ars_status, + priv->state.ars_status->out_length); + *cmd_rc = 0; + } + spin_unlock(&priv->state.lock); + + return 0; +} + +static int ndtest_cmd_clear_error(struct ndtest_priv *priv, + struct nd_cmd_clear_error *inj, + unsigned int buf_len, int *cmd_rc) +{ + const u64 mask = 255; + + if (buf_len < sizeof(*inj)) + return -EINVAL; + + if ((inj->address & mask) || (inj->length & mask)) + return -EINVAL; + + badrange_forget(&priv->badrange, inj->address, inj->length); + inj->status = 0; + inj->cleared = inj->length; + *cmd_rc = 0; + + return 0; +} + static int ndtest_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc) @@ -531,8 +816,32 @@ static int ndtest_ctl(struct nvdimm_bus_descriptor *nd_desc, *cmd_rc = 0; - if (!nvdimm) - return -EINVAL; + if (!nvdimm) { + struct ndtest_priv *priv; + + if (!nd_desc) + return -ENOTTY; + + priv = container_of(nd_desc, struct ndtest_priv, bus_desc); + switch (cmd) { + case ND_CMD_CALL: + return ndtest_bus_cmd_call(nd_desc, buf, buf_len, + cmd_rc); + case ND_CMD_ARS_CAP: + return ndtest_cmd_ars_cap(priv, buf, buf_len); + case ND_CMD_ARS_START: + return ndtest_cmd_ars_start(priv, buf, buf_len, cmd_rc); + case ND_CMD_ARS_STATUS: + return ndtest_cmd_ars_status(priv, buf, buf_len, + cmd_rc); + case ND_CMD_CLEAR_ERROR: + return ndtest_cmd_clear_error(priv, buf, buf_len, + cmd_rc); + default: + dev_dbg(&priv->pdev.dev, "Invalid command\n"); + return -ENOTTY; + } + } dimm = nvdimm_provider_data(nvdimm); if (!dimm) @@ -683,6 +992,9 @@ static void *ndtest_alloc_resource(struct ndtest_priv *p, size_t size, return NULL; buf = vmalloc(size); + if (!buf) + return NULL; + if (size >= DIMM_SIZE) __dma = gen_pool_alloc_algo(ndtest_pool, size, gen_pool_first_fit_align, &data); @@ -1052,6 +1364,7 @@ static ssize_t flags_show(struct device *dev, } static DEVICE_ATTR_RO(flags); + #define PAPR_PMEM_DIMM_CMD_MASK \ ((1U << PAPR_PDSM_HEALTH) \ | (1U << PAPR_PDSM_HEALTH_INJECT) \ @@ -1195,11 +1508,102 @@ static const struct attribute_group of_node_attribute_group = { .attrs = of_node_attributes, }; +#define PAPR_PMEM_BUS_DSM_MASK \ + ((1U << PAPR_PDSM_INJECT_SET) \ + | (1U << PAPR_PDSM_INJECT_GET) \ + | (1U << PAPR_PDSM_INJECT_CLEAR)) + +static ssize_t bus_dsm_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%#x\n", PAPR_PMEM_BUS_DSM_MASK); +} +static struct device_attribute dev_attr_bus_dsm_mask = { + .attr = { .name = "dsm_mask", .mode = 0444 }, + .show = bus_dsm_mask_show, +}; + +static ssize_t scrub_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct nvdimm_bus_descriptor *nd_desc; + struct ndtest_priv *p; + ssize_t rc = -ENXIO; + bool busy = 0; + + device_lock(dev); + nd_desc = dev_get_drvdata(dev); + if (!nd_desc) { + device_unlock(dev); + return rc; + } + + p = container_of(nd_desc, struct ndtest_priv, bus_desc); + + mutex_lock(&p->ars_lock); + busy = test_bit(ARS_BUSY, &p->scrub_flags) && + !test_bit(ARS_CANCEL, &p->scrub_flags); + rc = sprintf(buf, "%d%s", p->scrub_count, busy ? "+\n" : "\n"); + if (busy && capable(CAP_SYS_RAWIO) && + !test_and_set_bit(ARS_POLL, &p->scrub_flags)) + mod_delayed_work(ndtest_wq, &p->dwork, HZ); + + mutex_unlock(&p->ars_lock); + + device_unlock(dev); + return rc; +} + +static ssize_t scrub_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct nvdimm_bus_descriptor *nd_desc; + struct ndtest_priv *p; + ssize_t rc = 0; + long val; + + rc = kstrtol(buf, 0, &val); + if (rc) + return rc; + if (val != 1) + return -EINVAL; + device_lock(dev); + nd_desc = dev_get_drvdata(dev); + if (nd_desc) { + p = container_of(nd_desc, struct ndtest_priv, bus_desc); + + ndtest_scrub_notify(p); + } + device_unlock(dev); + + return size; +} +static DEVICE_ATTR_RW(scrub); + +static struct attribute *ndtest_attributes[] = { + &dev_attr_bus_dsm_mask.attr, + &dev_attr_scrub.attr, + NULL, +}; + +static const struct attribute_group ndtest_attribute_group = { + .name = "papr", + .attrs = ndtest_attributes, +}; + static const struct attribute_group *ndtest_attribute_groups[] = { &of_node_attribute_group, + &ndtest_attribute_group, NULL, }; +#define PAPR_PMEM_BUS_CMD_MASK \ + (1UL << ND_CMD_ARS_CAP \ + | 1UL << ND_CMD_ARS_START \ + | 1UL << ND_CMD_ARS_STATUS \ + | 1UL << ND_CMD_CLEAR_ERROR \ + | 1UL << ND_CMD_CALL) + static int ndtest_bus_register(struct ndtest_priv *p) { p->config = &bus_configs[p->pdev.id]; @@ -1207,7 +1611,9 @@ static int ndtest_bus_register(struct ndtest_priv *p) p->bus_desc.ndctl = ndtest_ctl; p->bus_desc.module = THIS_MODULE; p->bus_desc.provider_name = NULL; + p->bus_desc.cmd_mask = PAPR_PMEM_BUS_CMD_MASK; p->bus_desc.attr_groups = ndtest_attribute_groups; + p->bus_desc.bus_family_mask = NVDIMM_FAMILY_PAPR; set_bit(NVDIMM_FAMILY_PAPR, &p->bus_desc.dimm_family_mask); @@ -1228,6 +1634,33 @@ static int ndtest_remove(struct platform_device *pdev) return 0; } +static int ndtest_init_ars(struct ndtest_priv *p) +{ + struct kernfs_node *papr_node; + struct device *bus_dev; + + p->state.ars_status = devm_kzalloc( + &p->pdev.dev, sizeof(struct nd_cmd_ars_status) + SZ_4K, + GFP_KERNEL); + if (!p->state.ars_status) + return -ENOMEM; + + bus_dev = to_nvdimm_bus_dev(p->bus); + papr_node = sysfs_get_dirent(bus_dev->kobj.sd, "papr"); + if (!papr_node) { + dev_err(&p->pdev.dev, "sysfs_get_dirent 'papr' failed\n"); + return -ENOENT; + } + + p->scrub_state = sysfs_get_dirent(papr_node, "scrub"); + if (!p->scrub_state) { + dev_err(&p->pdev.dev, "sysfs_get_dirent 'scrub' failed\n"); + return -ENOENT; + } + + return 0; +} + static int ndtest_probe(struct platform_device *pdev) { struct ndtest_priv *p; @@ -1252,6 +1685,10 @@ static int ndtest_probe(struct platform_device *pdev) if (rc) goto err; + rc = ndtest_init_ars(p); + if (rc) + goto err; + rc = devm_add_action_or_reset(&pdev->dev, put_dimms, p); if (rc) goto err; @@ -1299,6 +1736,7 @@ static void cleanup_devices(void) if (ndtest_pool) gen_pool_destroy(ndtest_pool); + destroy_workqueue(ndtest_wq); if (ndtest_dimm_class) class_destroy(ndtest_dimm_class); @@ -1319,6 +1757,10 @@ static __init int ndtest_init(void) nfit_test_setup(ndtest_resource_lookup, NULL); + ndtest_wq = create_singlethread_workqueue("nfit"); + if (!ndtest_wq) + return -ENOMEM; + ndtest_dimm_class = class_create(THIS_MODULE, "nfit_test_dimm"); if (IS_ERR(ndtest_dimm_class)) { rc = PTR_ERR(ndtest_dimm_class); @@ -1348,6 +1790,7 @@ static __init int ndtest_init(void) } INIT_LIST_HEAD(&priv->resources); + badrange_init(&priv->badrange); pdev = &priv->pdev; pdev->name = KBUILD_MODNAME; pdev->id = i; @@ -1360,6 +1803,11 @@ static __init int ndtest_init(void) get_device(&pdev->dev); instances[i] = priv; + + /* Everything about ARS here */ + INIT_DELAYED_WORK(&priv->dwork, ndtest_scrub); + mutex_init(&priv->ars_lock); + spin_lock_init(&priv->state.lock); } rc = platform_driver_register(&ndtest_driver); @@ -1377,6 +1825,7 @@ static __init int ndtest_init(void) static __exit void ndtest_exit(void) { + flush_workqueue(ndtest_wq); cleanup_devices(); platform_driver_unregister(&ndtest_driver); } diff --git a/tools/testing/nvdimm/test/ndtest.h b/tools/testing/nvdimm/test/ndtest.h index d29638b6a332..d92c4f3df344 100644 --- a/tools/testing/nvdimm/test/ndtest.h +++ b/tools/testing/nvdimm/test/ndtest.h @@ -83,17 +83,34 @@ enum dimm_type { NDTEST_REGION_TYPE_BLK = 0x1, }; +struct ars_state { + struct nd_cmd_ars_status *ars_status; + unsigned long deadline; + spinlock_t lock; +}; + struct ndtest_priv { struct platform_device pdev; struct device_node *dn; struct list_head resources; struct nvdimm_bus_descriptor bus_desc; + struct delayed_work dwork; + struct mutex ars_lock; struct nvdimm_bus *bus; struct ndtest_config *config; + struct ars_state state; + struct badrange badrange; + struct nd_cmd_ars_status *ars_status; + struct kernfs_node *scrub_state; dma_addr_t *dcr_dma; dma_addr_t *label_dma; dma_addr_t *dimm_dma; + + unsigned long scrub_flags; + unsigned long ars_state; + unsigned int max_ars; + int scrub_count; }; struct ndtest_blk_mmio { @@ -235,4 +252,12 @@ struct nd_pkg_pdsm { union nd_pdsm_payload payload; } __packed; +enum scrub_flags { + ARS_BUSY, + ARS_CANCEL, + ARS_VALID, + ARS_POLL, + ARS_FAILED, +}; + #endif /* NDTEST_H */