From be26f9ae022ad09967be7a83c58ce605014e939a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 1 Feb 2016 17:48:42 -0800 Subject: nfit, tools/testing/nvdimm: add format interface code definitions ACPI 6.1 and JEDEC Annex L Release 3 formalize the format interface code. Add definitions and update their usage in the unit test. Signed-off-by: Dan Williams --- tools/testing/nvdimm/test/nfit.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index b3281dcd4a5d..6e831c4021ff 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -823,6 +823,7 @@ static void nfit_test0_setup(struct nfit_test *t) dcr->device_id = 0; dcr->revision_id = 1; dcr->serial_number = ~handle[0]; + dcr->code = NFIT_FIC_BLK; dcr->windows = 1; dcr->window_size = DCR_SIZE; dcr->command_offset = 0; @@ -839,6 +840,7 @@ static void nfit_test0_setup(struct nfit_test *t) dcr->device_id = 0; dcr->revision_id = 1; dcr->serial_number = ~handle[1]; + dcr->code = NFIT_FIC_BLK; dcr->windows = 1; dcr->window_size = DCR_SIZE; dcr->command_offset = 0; @@ -855,6 +857,7 @@ static void nfit_test0_setup(struct nfit_test *t) dcr->device_id = 0; dcr->revision_id = 1; dcr->serial_number = ~handle[2]; + dcr->code = NFIT_FIC_BLK; dcr->windows = 1; dcr->window_size = DCR_SIZE; dcr->command_offset = 0; @@ -871,6 +874,7 @@ static void nfit_test0_setup(struct nfit_test *t) dcr->device_id = 0; dcr->revision_id = 1; dcr->serial_number = ~handle[3]; + dcr->code = NFIT_FIC_BLK; dcr->windows = 1; dcr->window_size = DCR_SIZE; dcr->command_offset = 0; @@ -967,6 +971,7 @@ static void nfit_test0_setup(struct nfit_test *t) dcr->device_id = 0; dcr->revision_id = 1; dcr->serial_number = ~handle[4]; + dcr->code = NFIT_FIC_BLK; dcr->windows = 1; dcr->window_size = DCR_SIZE; dcr->command_offset = 0; @@ -1136,7 +1141,7 @@ static void nfit_test1_setup(struct nfit_test *t) dcr->device_id = 0; dcr->revision_id = 1; dcr->serial_number = ~0; - dcr->code = 0x201; + dcr->code = NFIT_FIC_BYTE; dcr->windows = 0; dcr->window_size = 0; dcr->command_offset = 0; -- cgit v1.2.3-70-g09d2 From 3b87356f50aa12ae7f9bd60f630eb17fe9b4b253 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 1 Feb 2016 17:45:54 -0800 Subject: nfit, tools/testing/nvdimm: test multiple control regions per-dimm ACPI 6.1 clarifies that "The system shall include an NVDIMM Control Region Structure for every Function Interface in the NVDIMM." Implement this clarification in nfit_test. Signed-off-by: Dan Williams --- tools/testing/nvdimm/test/nfit.c | 118 +++++++++++++++++++++++++++++++-------- 1 file changed, 94 insertions(+), 24 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index 6e831c4021ff..27b808e0489a 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -429,6 +429,8 @@ static int nfit_test0_alloc(struct nfit_test *t) size_t nfit_size = sizeof(struct acpi_nfit_system_address) * NUM_SPA + sizeof(struct acpi_nfit_memory_map) * NUM_MEM + sizeof(struct acpi_nfit_control_region) * NUM_DCR + + offsetof(struct acpi_nfit_control_region, + window_size) * NUM_DCR + sizeof(struct acpi_nfit_data_region) * NUM_BDW + sizeof(struct acpi_nfit_flush_address) * NUM_DCR; int i; @@ -478,7 +480,7 @@ static int nfit_test1_alloc(struct nfit_test *t) { size_t nfit_size = sizeof(struct acpi_nfit_system_address) + sizeof(struct acpi_nfit_memory_map) - + sizeof(struct acpi_nfit_control_region); + + offsetof(struct acpi_nfit_control_region, window_size); t->nfit_buf = test_alloc(t, nfit_size, &t->nfit_dma); if (!t->nfit_buf) @@ -611,7 +613,7 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->physical_id = 0; memdev->region_id = 0; memdev->range_index = 0+1; - memdev->region_index = 0+1; + memdev->region_index = 4+1; memdev->region_size = SPA0_SIZE/2; memdev->region_offset = t->spa_set_dma[0]; memdev->address = 0; @@ -626,7 +628,7 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->physical_id = 1; memdev->region_id = 0; memdev->range_index = 0+1; - memdev->region_index = 1+1; + memdev->region_index = 5+1; memdev->region_size = SPA0_SIZE/2; memdev->region_offset = t->spa_set_dma[0] + SPA0_SIZE/2; memdev->address = 0; @@ -641,7 +643,7 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->physical_id = 0; memdev->region_id = 1; memdev->range_index = 1+1; - memdev->region_index = 0+1; + memdev->region_index = 4+1; memdev->region_size = SPA1_SIZE/4; memdev->region_offset = t->spa_set_dma[1]; memdev->address = SPA0_SIZE/2; @@ -656,7 +658,7 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->physical_id = 1; memdev->region_id = 1; memdev->range_index = 1+1; - memdev->region_index = 1+1; + memdev->region_index = 5+1; memdev->region_size = SPA1_SIZE/4; memdev->region_offset = t->spa_set_dma[1] + SPA1_SIZE/4; memdev->address = SPA0_SIZE/2; @@ -671,7 +673,7 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->physical_id = 2; memdev->region_id = 0; memdev->range_index = 1+1; - memdev->region_index = 2+1; + memdev->region_index = 6+1; memdev->region_size = SPA1_SIZE/4; memdev->region_offset = t->spa_set_dma[1] + 2*SPA1_SIZE/4; memdev->address = SPA0_SIZE/2; @@ -686,7 +688,7 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->physical_id = 3; memdev->region_id = 0; memdev->range_index = 1+1; - memdev->region_index = 3+1; + memdev->region_index = 7+1; memdev->region_size = SPA1_SIZE/4; memdev->region_offset = t->spa_set_dma[1] + 3*SPA1_SIZE/4; memdev->address = SPA0_SIZE/2; @@ -814,7 +816,7 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->interleave_ways = 1; offset = offset + sizeof(struct acpi_nfit_memory_map) * 14; - /* dcr-descriptor0 */ + /* dcr-descriptor0: blk */ dcr = nfit_buf + offset; dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; dcr->header.length = sizeof(struct acpi_nfit_control_region); @@ -831,7 +833,7 @@ static void nfit_test0_setup(struct nfit_test *t) dcr->status_offset = 8; dcr->status_size = 4; - /* dcr-descriptor1 */ + /* dcr-descriptor1: blk */ dcr = nfit_buf + offset + sizeof(struct acpi_nfit_control_region); dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; dcr->header.length = sizeof(struct acpi_nfit_control_region); @@ -848,7 +850,7 @@ static void nfit_test0_setup(struct nfit_test *t) dcr->status_offset = 8; dcr->status_size = 4; - /* dcr-descriptor2 */ + /* dcr-descriptor2: blk */ dcr = nfit_buf + offset + sizeof(struct acpi_nfit_control_region) * 2; dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; dcr->header.length = sizeof(struct acpi_nfit_control_region); @@ -865,7 +867,7 @@ static void nfit_test0_setup(struct nfit_test *t) dcr->status_offset = 8; dcr->status_size = 4; - /* dcr-descriptor3 */ + /* dcr-descriptor3: blk */ dcr = nfit_buf + offset + sizeof(struct acpi_nfit_control_region) * 3; dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; dcr->header.length = sizeof(struct acpi_nfit_control_region); @@ -883,6 +885,63 @@ static void nfit_test0_setup(struct nfit_test *t) dcr->status_size = 4; offset = offset + sizeof(struct acpi_nfit_control_region) * 4; + /* dcr-descriptor0: pmem */ + dcr = nfit_buf + offset; + dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; + dcr->header.length = offsetof(struct acpi_nfit_control_region, + window_size); + dcr->region_index = 4+1; + dcr->vendor_id = 0xabcd; + dcr->device_id = 0; + dcr->revision_id = 1; + dcr->serial_number = ~handle[0]; + dcr->code = NFIT_FIC_BYTEN; + dcr->windows = 0; + + /* dcr-descriptor1: pmem */ + dcr = nfit_buf + offset + offsetof(struct acpi_nfit_control_region, + window_size); + dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; + dcr->header.length = offsetof(struct acpi_nfit_control_region, + window_size); + dcr->region_index = 5+1; + dcr->vendor_id = 0xabcd; + dcr->device_id = 0; + dcr->revision_id = 1; + dcr->serial_number = ~handle[1]; + dcr->code = NFIT_FIC_BYTEN; + dcr->windows = 0; + + /* dcr-descriptor2: pmem */ + dcr = nfit_buf + offset + offsetof(struct acpi_nfit_control_region, + window_size) * 2; + dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; + dcr->header.length = offsetof(struct acpi_nfit_control_region, + window_size); + dcr->region_index = 6+1; + dcr->vendor_id = 0xabcd; + dcr->device_id = 0; + dcr->revision_id = 1; + dcr->serial_number = ~handle[2]; + dcr->code = NFIT_FIC_BYTEN; + dcr->windows = 0; + + /* dcr-descriptor3: pmem */ + dcr = nfit_buf + offset + offsetof(struct acpi_nfit_control_region, + window_size) * 3; + dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; + dcr->header.length = offsetof(struct acpi_nfit_control_region, + window_size); + dcr->region_index = 7+1; + dcr->vendor_id = 0xabcd; + dcr->device_id = 0; + dcr->revision_id = 1; + dcr->serial_number = ~handle[3]; + dcr->code = NFIT_FIC_BYTEN; + dcr->windows = 0; + + offset = offset + offsetof(struct acpi_nfit_control_region, + window_size) * 4; /* bdw0 (spa/dcr0, dimm0) */ bdw = nfit_buf + offset; bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION; @@ -962,11 +1021,11 @@ static void nfit_test0_setup(struct nfit_test *t) if (t->setup_hotplug) { offset = offset + sizeof(struct acpi_nfit_flush_address) * 4; - /* dcr-descriptor4 */ + /* dcr-descriptor4: blk */ dcr = nfit_buf + offset; dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; dcr->header.length = sizeof(struct acpi_nfit_control_region); - dcr->region_index = 4+1; + dcr->region_index = 8+1; dcr->vendor_id = 0xabcd; dcr->device_id = 0; dcr->revision_id = 1; @@ -980,11 +1039,26 @@ static void nfit_test0_setup(struct nfit_test *t) dcr->status_size = 4; offset = offset + sizeof(struct acpi_nfit_control_region); + /* dcr-descriptor4: pmem */ + dcr = nfit_buf + offset; + dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; + dcr->header.length = offsetof(struct acpi_nfit_control_region, + window_size); + dcr->region_index = 9+1; + dcr->vendor_id = 0xabcd; + dcr->device_id = 0; + dcr->revision_id = 1; + dcr->serial_number = ~handle[4]; + dcr->code = NFIT_FIC_BYTEN; + dcr->windows = 0; + + offset = offset + offsetof(struct acpi_nfit_control_region, + window_size); /* bdw4 (spa/dcr4, dimm4) */ bdw = nfit_buf + offset; bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION; bdw->header.length = sizeof(struct acpi_nfit_data_region); - bdw->region_index = 4+1; + bdw->region_index = 8+1; bdw->windows = 1; bdw->offset = 0; bdw->size = BDW_SIZE; @@ -1032,7 +1106,7 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->physical_id = 4; memdev->region_id = 0; memdev->range_index = 10+1; - memdev->region_index = 4+1; + memdev->region_index = 8+1; memdev->region_size = 0; memdev->region_offset = 0; memdev->address = 0; @@ -1048,14 +1122,14 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->physical_id = 4; memdev->region_id = 0; memdev->range_index = 11+1; - memdev->region_index = 4+1; + memdev->region_index = 9+1; memdev->region_size = SPA0_SIZE; memdev->region_offset = t->spa_set_dma[2]; memdev->address = 0; memdev->interleave_index = 0; memdev->interleave_ways = 1; - /* mem-region16 (spa/dcr4, dimm4) */ + /* mem-region16 (spa/bdw4, dimm4) */ memdev = nfit_buf + offset + sizeof(struct acpi_nfit_memory_map) * 2; memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP; @@ -1064,7 +1138,7 @@ static void nfit_test0_setup(struct nfit_test *t) memdev->physical_id = 4; memdev->region_id = 0; memdev->range_index = 12+1; - memdev->region_index = 4+1; + memdev->region_index = 8+1; memdev->region_size = 0; memdev->region_offset = 0; memdev->address = 0; @@ -1135,7 +1209,8 @@ static void nfit_test1_setup(struct nfit_test *t) /* dcr-descriptor0 */ dcr = nfit_buf + offset; dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION; - dcr->header.length = sizeof(struct acpi_nfit_control_region); + dcr->header.length = offsetof(struct acpi_nfit_control_region, + window_size); dcr->region_index = 0+1; dcr->vendor_id = 0xabcd; dcr->device_id = 0; @@ -1143,11 +1218,6 @@ static void nfit_test1_setup(struct nfit_test *t) dcr->serial_number = ~0; dcr->code = NFIT_FIC_BYTE; dcr->windows = 0; - dcr->window_size = 0; - dcr->command_offset = 0; - dcr->command_size = 0; - dcr->status_offset = 0; - dcr->status_size = 0; acpi_desc = &t->acpi_desc; set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en); -- cgit v1.2.3-70-g09d2 From aef25338226660cdd4df908c2eff1abdcfca65e5 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 12 Feb 2016 17:01:11 -0800 Subject: libnvdimm, nfit: centralize command status translation The return value from an 'ndctl_fn' reports the command execution status, i.e. was the command properly formatted and was it successfully submitted to the bus provider. The new 'cmd_rc' parameter allows the bus provider to communicate command specific results, translated into common error codes. Convert the ARS commands to this scheme to: 1/ Consolidate status reporting 2/ Prepare for for expanding ars unit test cases 3/ Make the implementation more generic Cc: Vishal Verma Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 179 ++++++++++++++++++++++++++------------- drivers/acpi/nfit.h | 11 ++- drivers/nvdimm/bus.c | 2 +- drivers/nvdimm/dimm_devs.c | 6 +- include/linux/libnvdimm.h | 2 +- tools/testing/nvdimm/test/nfit.c | 5 +- 6 files changed, 138 insertions(+), 67 deletions(-) (limited to 'tools/testing') diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index 35947ac87644..4dd2b6808df5 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -72,9 +72,80 @@ static struct acpi_device *to_acpi_dev(struct acpi_nfit_desc *acpi_desc) return to_acpi_device(acpi_desc->dev); } +static int xlat_status(void *buf, unsigned int cmd) +{ + struct nd_cmd_ars_status *ars_status; + struct nd_cmd_ars_start *ars_start; + struct nd_cmd_ars_cap *ars_cap; + u16 flags; + + switch (cmd) { + case ND_CMD_ARS_CAP: + ars_cap = buf; + if ((ars_cap->status & 0xffff) == NFIT_ARS_CAP_NONE) + return -ENOTTY; + + /* Command failed */ + if (ars_cap->status & 0xffff) + return -EIO; + + /* No supported scan types for this range */ + flags = ND_ARS_PERSISTENT | ND_ARS_VOLATILE; + if ((ars_cap->status >> 16 & flags) == 0) + return -ENOTTY; + break; + case ND_CMD_ARS_START: + ars_start = buf; + /* ARS is in progress */ + if ((ars_start->status & 0xffff) == NFIT_ARS_START_BUSY) + return -EBUSY; + + /* Command failed */ + if (ars_start->status & 0xffff) + return -EIO; + break; + case ND_CMD_ARS_STATUS: + ars_status = buf; + /* Command failed */ + if (ars_status->status & 0xffff) + return -EIO; + /* Check extended status (Upper two bytes) */ + if (ars_status->status == NFIT_ARS_STATUS_DONE) + return 0; + + /* ARS is in progress */ + if (ars_status->status == NFIT_ARS_STATUS_BUSY) + return -EBUSY; + + /* No ARS performed for the current boot */ + if (ars_status->status == NFIT_ARS_STATUS_NONE) + return -EAGAIN; + + /* + * ARS interrupted, either we overflowed or some other + * agent wants the scan to stop. If we didn't overflow + * then just continue with the returned results. + */ + if (ars_status->status == NFIT_ARS_STATUS_INTR) { + if (ars_status->flags & NFIT_ARS_F_OVERFLOW) + return -ENOSPC; + return 0; + } + + /* Unknown status */ + if (ars_status->status >> 16) + return -EIO; + break; + default: + break; + } + + return 0; +} + static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, unsigned int cmd, void *buf, - unsigned int buf_len) + unsigned int buf_len, int *cmd_rc) { struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc); const struct nd_cmd_desc *desc = NULL; @@ -185,6 +256,8 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, * unfilled in the output buffer */ rc = buf_len - offset - in_buf.buffer.length; + if (cmd_rc) + *cmd_rc = xlat_status(buf, cmd); } else { dev_err(dev, "%s:%s underrun cmd: %s buf_len: %d out_len: %d\n", __func__, dimm_name, cmd_name, buf_len, @@ -1105,7 +1178,7 @@ static void write_blk_ctl(struct nfit_blk *nfit_blk, unsigned int bw, writeq(cmd, mmio->addr.base + offset); wmb_blk(nfit_blk); - if (nfit_blk->dimm_flags & ND_BLK_DCR_LATCH) + if (nfit_blk->dimm_flags & NFIT_BLK_DCR_LATCH) readq(mmio->addr.base + offset); } @@ -1141,7 +1214,7 @@ static int acpi_nfit_blk_single_io(struct nfit_blk *nfit_blk, memcpy_to_pmem(mmio->addr.aperture + offset, iobuf + copied, c); else { - if (nfit_blk->dimm_flags & ND_BLK_READ_FLUSH) + if (nfit_blk->dimm_flags & NFIT_BLK_READ_FLUSH) mmio_flush_range((void __force *) mmio->addr.aperture + offset, c); @@ -1328,13 +1401,13 @@ static int acpi_nfit_blk_get_flags(struct nvdimm_bus_descriptor *nd_desc, memset(&flags, 0, sizeof(flags)); rc = nd_desc->ndctl(nd_desc, nvdimm, ND_CMD_DIMM_FLAGS, &flags, - sizeof(flags)); + sizeof(flags), NULL); if (rc >= 0 && flags.status == 0) nfit_blk->dimm_flags = flags.flags; else if (rc == -ENOTTY) { /* fall back to a conservative default */ - nfit_blk->dimm_flags = ND_BLK_DCR_LATCH | ND_BLK_READ_FLUSH; + nfit_blk->dimm_flags = NFIT_BLK_DCR_LATCH | NFIT_BLK_READ_FLUSH; rc = 0; } else rc = -ENXIO; @@ -1473,19 +1546,27 @@ static void acpi_nfit_blk_region_disable(struct nvdimm_bus *nvdimm_bus, /* devm will free nfit_blk */ } -static int ars_get_cap(struct nvdimm_bus_descriptor *nd_desc, +static int ars_get_cap(struct acpi_nfit_desc *acpi_desc, struct nd_cmd_ars_cap *cmd, u64 addr, u64 length) { + struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc; + int cmd_rc, rc; + cmd->address = addr; cmd->length = length; - - return nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_CAP, cmd, - sizeof(*cmd)); + rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_CAP, cmd, + sizeof(*cmd), &cmd_rc); + if (rc < 0) + return rc; + if (cmd_rc < 0) + return cmd_rc; + return 0; } static int ars_do_start(struct nvdimm_bus_descriptor *nd_desc, struct nd_cmd_ars_start *cmd, u64 addr, u64 length) { + int cmd_rc; int rc; cmd->address = addr; @@ -1494,52 +1575,49 @@ static int ars_do_start(struct nvdimm_bus_descriptor *nd_desc, while (1) { rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_START, cmd, - sizeof(*cmd)); - if (rc) + sizeof(*cmd), &cmd_rc); + + if (rc < 0) return rc; - switch (cmd->status) { - case 0: - return 0; - case 1: - /* ARS unsupported, but we should never get here */ - return 0; - case 6: + + if (cmd_rc == -EBUSY) { /* ARS is in progress */ msleep(1000); - break; - default: - return -ENXIO; + continue; } + + if (cmd_rc < 0) + return cmd_rc; + + return 0; } } static int ars_get_status(struct nvdimm_bus_descriptor *nd_desc, struct nd_cmd_ars_status *cmd, u32 size) { - int rc; + int rc, cmd_rc; while (1) { rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_STATUS, cmd, - size); - if (rc || cmd->status & 0xffff) - return -ENXIO; + size, &cmd_rc); + if (rc < 0) + return rc; - /* Check extended status (Upper two bytes) */ - switch (cmd->status >> 16) { - case 0: - return 0; - case 1: - /* ARS is in progress */ + /* FIXME make async and have a timeout */ + if (cmd_rc == -EBUSY) { msleep(1000); - break; - case 2: - /* No ARS performed for the current boot */ + continue; + } + + if (cmd_rc == -EAGAIN || cmd_rc == 0) return 0; - case 3: - /* TODO: error list overflow support */ - default: + + /* TODO: error list overflow support */ + if (cmd_rc == -ENOSPC) return -ENXIO; - } + + return cmd_rc; } } @@ -1590,28 +1668,11 @@ static int acpi_nfit_find_poison(struct acpi_nfit_desc *acpi_desc, start = ndr_desc->res->start; len = ndr_desc->res->end - ndr_desc->res->start + 1; - /* - * If ARS is unimplemented, unsupported, or if the 'Persistent Memory - * Scrub' flag in extended status is not set, skip this but continue - * initialization - */ - rc = ars_get_cap(nd_desc, ars_cap, start, len); + rc = ars_get_cap(acpi_desc, ars_cap, start, len); if (rc == -ENOTTY) { - dev_dbg(acpi_desc->dev, - "Address Range Scrub is not implemented, won't create an error list\n"); rc = 0; goto out; } - if (rc) - goto out; - - if ((ars_cap->status & 0xffff) || - !(ars_cap->status >> 16 & ND_ARS_PERSISTENT)) { - dev_warn(acpi_desc->dev, - "ARS unsupported (status: 0x%x), won't create an error list\n", - ars_cap->status); - goto out; - } /* * Check if a full-range ARS has been run. If so, use those results @@ -1651,15 +1712,15 @@ static int acpi_nfit_find_poison(struct acpi_nfit_desc *acpi_desc, u64 done, end; rc = ars_do_start(nd_desc, ars_start, cur, remaining); - if (rc) + if (rc < 0) goto out; rc = ars_get_status(nd_desc, ars_status, ars_status_size); - if (rc) + if (rc < 0) goto out; rc = ars_status_process_records(nvdimm_bus, ars_status, cur); - if (rc) + if (rc < 0) goto out; end = min(cur + remaining, diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h index 6689b0aaf194..f45b7d9b1d9e 100644 --- a/drivers/acpi/nfit.h +++ b/drivers/acpi/nfit.h @@ -47,8 +47,15 @@ enum nfit_fic { }; enum { - ND_BLK_READ_FLUSH = 1, - ND_BLK_DCR_LATCH = 2, + NFIT_BLK_READ_FLUSH = 1, + NFIT_BLK_DCR_LATCH = 2, + NFIT_ARS_STATUS_DONE = 0, + NFIT_ARS_STATUS_BUSY = 1 << 16, + NFIT_ARS_STATUS_NONE = 2 << 16, + NFIT_ARS_STATUS_INTR = 3 << 16, + NFIT_ARS_START_BUSY = 6, + NFIT_ARS_CAP_NONE = 1, + NFIT_ARS_F_OVERFLOW = 1, }; struct nfit_spa { diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index 5d28e9405f32..c3ba888e3e3a 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -587,7 +587,7 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, if (rc) goto out_unlock; - rc = nd_desc->ndctl(nd_desc, nvdimm, cmd, buf, buf_len); + rc = nd_desc->ndctl(nd_desc, nvdimm, cmd, buf, buf_len, NULL); if (rc < 0) goto out_unlock; if (copy_to_user(p, buf, buf_len)) diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index 651b8d19d324..c56f88217924 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -75,7 +75,7 @@ int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd) memset(cmd, 0, sizeof(*cmd)); nd_desc = nvdimm_bus->nd_desc; return nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev), - ND_CMD_GET_CONFIG_SIZE, cmd, sizeof(*cmd)); + ND_CMD_GET_CONFIG_SIZE, cmd, sizeof(*cmd), NULL); } int nvdimm_init_config_data(struct nvdimm_drvdata *ndd) @@ -120,7 +120,7 @@ int nvdimm_init_config_data(struct nvdimm_drvdata *ndd) cmd->in_offset = offset; rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev), ND_CMD_GET_CONFIG_DATA, cmd, - cmd->in_length + sizeof(*cmd)); + cmd->in_length + sizeof(*cmd), NULL); if (rc || cmd->status) { rc = -ENXIO; break; @@ -171,7 +171,7 @@ int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset, status = ((void *) cmd) + cmd_size - sizeof(u32); rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev), - ND_CMD_SET_CONFIG_DATA, cmd, cmd_size); + ND_CMD_SET_CONFIG_DATA, cmd, cmd_size, NULL); if (rc || *status) { rc = rc ? rc : -ENXIO; break; diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index 141ffdd59960..f398953270d1 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -48,7 +48,7 @@ struct nvdimm; struct nvdimm_bus_descriptor; typedef int (*ndctl_fn)(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, unsigned int cmd, void *buf, - unsigned int buf_len); + unsigned int buf_len, int *cmd_rc); struct nd_namespace_label; struct nvdimm_drvdata; diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index 27b808e0489a..9ead77970546 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -261,7 +261,7 @@ static int nfit_test_cmd_ars_status(struct nd_cmd_ars_status *nd_cmd, static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, unsigned int cmd, void *buf, - unsigned int buf_len) + unsigned int buf_len, int *cmd_rc) { struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); struct nfit_test *t = container_of(acpi_desc, typeof(*t), acpi_desc); @@ -315,6 +315,9 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, } } + /* TODO: error status tests */ + if (cmd_rc) + *cmd_rc = 0; return rc; } -- cgit v1.2.3-70-g09d2 From a61fe6f7902ecaa89d5e6c709490fc4324927134 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 19 Feb 2016 12:29:32 -0800 Subject: nfit, tools/testing/nvdimm: unify common init for acpi_nfit_desc The nvdimm unit test infrastructure performs its own initialization of an acpi_nfit_desc to specify test overrides over the native implementation. Make it clear which attributes and operations it is overriding by re-using acpi_nfit_init_desc() as a common starting point. Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 44 +++++++++++++++------------------------- drivers/acpi/nfit.h | 2 +- tools/testing/nvdimm/test/nfit.c | 22 +++----------------- 3 files changed, 20 insertions(+), 48 deletions(-) (limited to 'tools/testing') diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index 4dd2b6808df5..76c9444c9c60 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -748,12 +748,11 @@ static struct attribute_group acpi_nfit_attribute_group = { .attrs = acpi_nfit_attributes, }; -const struct attribute_group *acpi_nfit_attribute_groups[] = { +static const struct attribute_group *acpi_nfit_attribute_groups[] = { &nvdimm_bus_attribute_group, &acpi_nfit_attribute_group, NULL, }; -EXPORT_SYMBOL_GPL(acpi_nfit_attribute_groups); static struct acpi_nfit_memory_map *to_nfit_memdev(struct device *dev) { @@ -1962,15 +1961,9 @@ int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, acpi_size sz) } EXPORT_SYMBOL_GPL(acpi_nfit_init); -static struct acpi_nfit_desc *acpi_nfit_desc_init(struct acpi_device *adev) +void acpi_nfit_desc_init(struct acpi_nfit_desc *acpi_desc, struct device *dev) { struct nvdimm_bus_descriptor *nd_desc; - struct acpi_nfit_desc *acpi_desc; - struct device *dev = &adev->dev; - - acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL); - if (!acpi_desc) - return ERR_PTR(-ENOMEM); dev_set_drvdata(dev, acpi_desc); acpi_desc->dev = dev; @@ -1980,12 +1973,6 @@ static struct acpi_nfit_desc *acpi_nfit_desc_init(struct acpi_device *adev) nd_desc->ndctl = acpi_nfit_ctl; nd_desc->attr_groups = acpi_nfit_attribute_groups; - acpi_desc->nvdimm_bus = nvdimm_bus_register(dev, nd_desc); - if (!acpi_desc->nvdimm_bus) { - devm_kfree(dev, acpi_desc); - return ERR_PTR(-ENXIO); - } - INIT_LIST_HEAD(&acpi_desc->spa_maps); INIT_LIST_HEAD(&acpi_desc->spas); INIT_LIST_HEAD(&acpi_desc->dcrs); @@ -1996,9 +1983,8 @@ static struct acpi_nfit_desc *acpi_nfit_desc_init(struct acpi_device *adev) INIT_LIST_HEAD(&acpi_desc->dimms); mutex_init(&acpi_desc->spa_map_mutex); mutex_init(&acpi_desc->init_mutex); - - return acpi_desc; } +EXPORT_SYMBOL_GPL(acpi_nfit_desc_init); static int acpi_nfit_add(struct acpi_device *adev) { @@ -2017,12 +2003,13 @@ static int acpi_nfit_add(struct acpi_device *adev) return 0; } - acpi_desc = acpi_nfit_desc_init(adev); - if (IS_ERR(acpi_desc)) { - dev_err(dev, "%s: error initializing acpi_desc: %ld\n", - __func__, PTR_ERR(acpi_desc)); - return PTR_ERR(acpi_desc); - } + acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL); + if (!acpi_desc) + return -ENOMEM; + acpi_nfit_desc_init(acpi_desc, &adev->dev); + acpi_desc->nvdimm_bus = nvdimm_bus_register(dev, &acpi_desc->nd_desc); + if (!acpi_desc->nvdimm_bus) + return -ENOMEM; /* * Save the acpi header for later and then skip it, @@ -2085,12 +2072,13 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event) } if (!acpi_desc) { - acpi_desc = acpi_nfit_desc_init(adev); - if (IS_ERR(acpi_desc)) { - dev_err(dev, "%s: error initializing acpi_desc: %ld\n", - __func__, PTR_ERR(acpi_desc)); + acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL); + if (!acpi_desc) + goto out_unlock; + acpi_nfit_desc_init(acpi_desc, &adev->dev); + acpi_desc->nvdimm_bus = nvdimm_bus_register(dev, &acpi_desc->nd_desc); + if (!acpi_desc->nvdimm_bus) goto out_unlock; - } } /* Evaluate _FIT */ diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h index f45b7d9b1d9e..524dec0a3adb 100644 --- a/drivers/acpi/nfit.h +++ b/drivers/acpi/nfit.h @@ -195,5 +195,5 @@ static inline struct acpi_nfit_desc *to_acpi_desc( const u8 *to_nfit_uuid(enum nfit_uuids id); int acpi_nfit_init(struct acpi_nfit_desc *nfit, acpi_size sz); -extern const struct attribute_group *acpi_nfit_attribute_groups[]; +void acpi_nfit_desc_init(struct acpi_nfit_desc *acpi_desc, struct device *dev); #endif /* __NFIT_H__ */ diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index 9ead77970546..a66842e61bbc 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -499,7 +499,6 @@ static int nfit_test1_alloc(struct nfit_test *t) static void nfit_test0_setup(struct nfit_test *t) { - struct nvdimm_bus_descriptor *nd_desc; struct acpi_nfit_desc *acpi_desc; struct acpi_nfit_memory_map *memdev; void *nfit_buf = t->nfit_buf; @@ -1165,8 +1164,6 @@ static void nfit_test0_setup(struct nfit_test *t) set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en); - nd_desc = &acpi_desc->nd_desc; - nd_desc->ndctl = nfit_test_ctl; } static void nfit_test1_setup(struct nfit_test *t) @@ -1176,7 +1173,6 @@ static void nfit_test1_setup(struct nfit_test *t) struct acpi_nfit_memory_map *memdev; struct acpi_nfit_control_region *dcr; struct acpi_nfit_system_address *spa; - struct nvdimm_bus_descriptor *nd_desc; struct acpi_nfit_desc *acpi_desc; offset = 0; @@ -1226,8 +1222,6 @@ static void nfit_test1_setup(struct nfit_test *t) set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en); - nd_desc = &acpi_desc->nd_desc; - nd_desc->ndctl = nfit_test_ctl; } static int nfit_test_blk_do_io(struct nd_blk_region *ndbr, resource_size_t dpa, @@ -1310,26 +1304,16 @@ static int nfit_test_probe(struct platform_device *pdev) nfit_test->setup(nfit_test); acpi_desc = &nfit_test->acpi_desc; - acpi_desc->dev = &pdev->dev; + acpi_nfit_desc_init(acpi_desc, &pdev->dev); acpi_desc->nfit = nfit_test->nfit_buf; acpi_desc->blk_do_io = nfit_test_blk_do_io; nd_desc = &acpi_desc->nd_desc; - nd_desc->attr_groups = acpi_nfit_attribute_groups; + nd_desc->provider_name = NULL; + nd_desc->ndctl = nfit_test_ctl; acpi_desc->nvdimm_bus = nvdimm_bus_register(&pdev->dev, nd_desc); if (!acpi_desc->nvdimm_bus) return -ENXIO; - INIT_LIST_HEAD(&acpi_desc->spa_maps); - INIT_LIST_HEAD(&acpi_desc->spas); - INIT_LIST_HEAD(&acpi_desc->dcrs); - INIT_LIST_HEAD(&acpi_desc->bdws); - INIT_LIST_HEAD(&acpi_desc->idts); - INIT_LIST_HEAD(&acpi_desc->flushes); - INIT_LIST_HEAD(&acpi_desc->memdevs); - INIT_LIST_HEAD(&acpi_desc->dimms); - mutex_init(&acpi_desc->spa_map_mutex); - mutex_init(&acpi_desc->init_mutex); - rc = acpi_nfit_init(acpi_desc, nfit_test->nfit_size); if (rc) { nvdimm_bus_unregister(acpi_desc->nvdimm_bus); -- cgit v1.2.3-70-g09d2 From f471f1a7d0aa58c609e665514010650b2afa24b6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 20 Feb 2016 15:12:47 -0800 Subject: tools/testing/nvdimm: expand ars unit testing Simulate platform-firmware-initiated and asynchronous scrub results. This injects poison in the middle of all nfit_test pmem address ranges. Signed-off-by: Dan Williams --- tools/testing/nvdimm/test/nfit.c | 112 +++++++++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 22 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index a66842e61bbc..1555c09efba1 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -151,6 +151,11 @@ struct nfit_test { int (*alloc)(struct nfit_test *t); void (*setup)(struct nfit_test *t); int setup_hotplug; + struct ars_state { + struct nd_cmd_ars_status *ars_status; + unsigned long deadline; + spinlock_t lock; + } ars_state; }; static struct nfit_test *to_nfit_test(struct device *dev) @@ -232,30 +237,72 @@ static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd, return 0; } -static int nfit_test_cmd_ars_start(struct nd_cmd_ars_start *nd_cmd, - unsigned int buf_len) +/* + * Initialize the ars_state to return an ars_result 1 second in the future with + * a 4K error range in the middle of the requested address range. + */ +static void post_ars_status(struct ars_state *ars_state, u64 addr, u64 len) { - if (buf_len < sizeof(*nd_cmd)) + struct nd_cmd_ars_status *ars_status; + struct nd_ars_record *ars_record; + + ars_state->deadline = jiffies + 1*HZ; + ars_status = ars_state->ars_status; + ars_status->status = 0; + ars_status->out_length = sizeof(struct nd_cmd_ars_status) + + sizeof(struct nd_ars_record); + ars_status->address = addr; + ars_status->length = len; + ars_status->type = ND_ARS_PERSISTENT; + ars_status->num_records = 1; + ars_record = &ars_status->records[0]; + ars_record->handle = 0; + ars_record->err_address = addr + len / 2; + ars_record->length = SZ_4K; +} + +static int nfit_test_cmd_ars_start(struct ars_state *ars_state, + struct nd_cmd_ars_start *ars_start, unsigned int buf_len, + int *cmd_rc) +{ + if (buf_len < sizeof(*ars_start)) return -EINVAL; - nd_cmd->status = 0; + spin_lock(&ars_state->lock); + if (time_before(jiffies, ars_state->deadline)) { + ars_start->status = NFIT_ARS_START_BUSY; + *cmd_rc = -EBUSY; + } else { + ars_start->status = 0; + ars_start->scrub_time = 1; + post_ars_status(ars_state, ars_start->address, + ars_start->length); + *cmd_rc = 0; + } + spin_unlock(&ars_state->lock); return 0; } -static int nfit_test_cmd_ars_status(struct nd_cmd_ars_status *nd_cmd, - unsigned int buf_len) +static int nfit_test_cmd_ars_status(struct ars_state *ars_state, + struct nd_cmd_ars_status *ars_status, unsigned int buf_len, + int *cmd_rc) { - if (buf_len < sizeof(*nd_cmd)) + if (buf_len < ars_state->ars_status->out_length) return -EINVAL; - nd_cmd->out_length = sizeof(struct nd_cmd_ars_status); - /* TODO: emit error records */ - nd_cmd->num_records = 0; - nd_cmd->address = 0; - nd_cmd->length = -1ULL; - nd_cmd->status = 0; - + spin_lock(&ars_state->lock); + if (time_before(jiffies, ars_state->deadline)) { + memset(ars_status, 0, buf_len); + ars_status->status = NFIT_ARS_STATUS_BUSY; + ars_status->out_length = sizeof(*ars_status); + *cmd_rc = -EBUSY; + } else { + memcpy(ars_status, ars_state->ars_status, + ars_state->ars_status->out_length); + *cmd_rc = 0; + } + spin_unlock(&ars_state->lock); return 0; } @@ -265,7 +312,11 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, { struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); struct nfit_test *t = container_of(acpi_desc, typeof(*t), acpi_desc); - int i, rc = 0; + int i, rc = 0, __cmd_rc; + + if (!cmd_rc) + cmd_rc = &__cmd_rc; + *cmd_rc = 0; if (nvdimm) { struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); @@ -297,6 +348,8 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, return -ENOTTY; } } else { + struct ars_state *ars_state = &t->ars_state; + if (!nd_desc || !test_bit(cmd, &nd_desc->dsm_mask)) return -ENOTTY; @@ -305,19 +358,18 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, rc = nfit_test_cmd_ars_cap(buf, buf_len); break; case ND_CMD_ARS_START: - rc = nfit_test_cmd_ars_start(buf, buf_len); + rc = nfit_test_cmd_ars_start(ars_state, buf, buf_len, + cmd_rc); break; case ND_CMD_ARS_STATUS: - rc = nfit_test_cmd_ars_status(buf, buf_len); + rc = nfit_test_cmd_ars_status(ars_state, buf, buf_len, + cmd_rc); break; default: return -ENOTTY; } } - /* TODO: error status tests */ - if (cmd_rc) - *cmd_rc = 0; return rc; } @@ -427,6 +479,18 @@ static struct nfit_test_resource *nfit_test_lookup(resource_size_t addr) return NULL; } +static int ars_state_init(struct device *dev, struct ars_state *ars_state) +{ + ars_state->ars_status = devm_kzalloc(dev, + sizeof(struct nd_cmd_ars_status) + + sizeof(struct nd_ars_record) * NFIT_TEST_ARS_RECORDS, + GFP_KERNEL); + if (!ars_state->ars_status) + return -ENOMEM; + spin_lock_init(&ars_state->lock); + return 0; +} + static int nfit_test0_alloc(struct nfit_test *t) { size_t nfit_size = sizeof(struct acpi_nfit_system_address) * NUM_SPA @@ -476,7 +540,7 @@ static int nfit_test0_alloc(struct nfit_test *t) return -ENOMEM; } - return 0; + return ars_state_init(&t->pdev.dev, &t->ars_state); } static int nfit_test1_alloc(struct nfit_test *t) @@ -494,7 +558,7 @@ static int nfit_test1_alloc(struct nfit_test *t) if (!t->spa_set[0]) return -ENOMEM; - return 0; + return ars_state_init(&t->pdev.dev, &t->ars_state); } static void nfit_test0_setup(struct nfit_test *t) @@ -1157,6 +1221,8 @@ static void nfit_test0_setup(struct nfit_test *t) flush->hint_address[0] = t->flush_dma[4]; } + post_ars_status(&t->ars_state, t->spa_set_dma[0], SPA0_SIZE); + acpi_desc = &t->acpi_desc; set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_dsm_force_en); set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_dsm_force_en); @@ -1218,6 +1284,8 @@ static void nfit_test1_setup(struct nfit_test *t) dcr->code = NFIT_FIC_BYTE; dcr->windows = 0; + post_ars_status(&t->ars_state, t->spa_set_dma[0], SPA2_SIZE); + acpi_desc = &t->acpi_desc; set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en); -- cgit v1.2.3-70-g09d2 From d4f323672aa63713b7ca26da418f66cc30d3a41a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 3 Mar 2016 16:08:54 -0800 Subject: nfit, libnvdimm: clear poison command support Add the boiler-plate for a 'clear error' command based on section 9.20.7.6 "Function Index 4 - Clear Uncorrectable Error" from the ACPI 6.1 specification, and add a reference implementation in nfit_test. Reviewed-by: Vishal Verma Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 12 +++++++++++- drivers/nvdimm/bus.c | 19 +++++++++++++++++++ include/uapi/linux/ndctl.h | 13 +++++++++++++ tools/testing/nvdimm/test/nfit.c | 29 +++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index 0def4ebf5d43..c067d7414007 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -87,6 +87,7 @@ static struct acpi_device *to_acpi_dev(struct acpi_nfit_desc *acpi_desc) static int xlat_status(void *buf, unsigned int cmd) { + struct nd_cmd_clear_error *clear_err; struct nd_cmd_ars_status *ars_status; struct nd_cmd_ars_start *ars_start; struct nd_cmd_ars_cap *ars_cap; @@ -149,6 +150,15 @@ static int xlat_status(void *buf, unsigned int cmd) if (ars_status->status >> 16) return -EIO; break; + case ND_CMD_CLEAR_ERROR: + clear_err = buf; + if (clear_err->status & 0xffff) + return -EIO; + if (!clear_err->cleared) + return -EIO; + if (clear_err->length > clear_err->cleared) + return clear_err->cleared; + break; default: break; } @@ -1002,7 +1012,7 @@ static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc) if (!adev) return; - for (i = ND_CMD_ARS_CAP; i <= ND_CMD_ARS_STATUS; i++) + for (i = ND_CMD_ARS_CAP; i <= ND_CMD_CLEAR_ERROR; i++) if (acpi_check_dsm(adev->handle, uuid, 1, 1ULL << i)) set_bit(i, &nd_desc->dsm_mask); } diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index 2e9ac22595ec..cb6fd64b13e3 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -421,6 +421,12 @@ static const struct nd_cmd_desc __nd_cmd_bus_descs[] = { .out_num = 3, .out_sizes = { 4, 4, UINT_MAX, }, }, + [ND_CMD_CLEAR_ERROR] = { + .in_num = 2, + .in_sizes = { 8, 8, }, + .out_num = 3, + .out_sizes = { 4, 4, 8, }, + }, }; const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd) @@ -489,6 +495,13 @@ void wait_nvdimm_bus_probe_idle(struct device *dev) } while (true); } +static int pmem_active(struct device *dev, void *data) +{ + if (is_nd_pmem(dev) && dev->driver) + return -EBUSY; + return 0; +} + /* set_config requires an idle interleave set */ static int nd_cmd_clear_to_send(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, unsigned int cmd) @@ -503,6 +516,11 @@ static int nd_cmd_clear_to_send(struct nvdimm_bus *nvdimm_bus, return rc; } + /* require clear error to go through the pmem driver */ + if (!nvdimm && cmd == ND_CMD_CLEAR_ERROR) + return device_for_each_child(&nvdimm_bus->dev, NULL, + pmem_active); + if (!nvdimm || cmd != ND_CMD_SET_CONFIG_DATA) return 0; @@ -551,6 +569,7 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, case ND_CMD_VENDOR: case ND_CMD_SET_CONFIG_DATA: case ND_CMD_ARS_START: + case ND_CMD_CLEAR_ERROR: dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n", nvdimm ? nvdimm_cmd_name(cmd) : nvdimm_bus_cmd_name(cmd)); diff --git a/include/uapi/linux/ndctl.h b/include/uapi/linux/ndctl.h index cc68b92124d4..7cc28ab05b87 100644 --- a/include/uapi/linux/ndctl.h +++ b/include/uapi/linux/ndctl.h @@ -98,6 +98,14 @@ struct nd_cmd_ars_status { } __packed records[0]; } __packed; +struct nd_cmd_clear_error { + __u64 address; + __u64 length; + __u32 status; + __u8 reserved[4]; + __u64 cleared; +} __packed; + enum { ND_CMD_IMPLEMENTED = 0, @@ -105,6 +113,7 @@ enum { ND_CMD_ARS_CAP = 1, ND_CMD_ARS_START = 2, ND_CMD_ARS_STATUS = 3, + ND_CMD_CLEAR_ERROR = 4, /* per-dimm commands */ ND_CMD_SMART = 1, @@ -129,6 +138,7 @@ static inline const char *nvdimm_bus_cmd_name(unsigned cmd) [ND_CMD_ARS_CAP] = "ars_cap", [ND_CMD_ARS_START] = "ars_start", [ND_CMD_ARS_STATUS] = "ars_status", + [ND_CMD_CLEAR_ERROR] = "clear_error", }; if (cmd < ARRAY_SIZE(names) && names[cmd]) @@ -187,6 +197,9 @@ static inline const char *nvdimm_cmd_name(unsigned cmd) #define ND_IOCTL_ARS_STATUS _IOWR(ND_IOCTL, ND_CMD_ARS_STATUS,\ struct nd_cmd_ars_status) +#define ND_IOCTL_CLEAR_ERROR _IOWR(ND_IOCTL, ND_CMD_CLEAR_ERROR,\ + struct nd_cmd_clear_error) + #define ND_DEVICE_DIMM 1 /* nd_dimm: container for "config data" */ #define ND_DEVICE_REGION_PMEM 2 /* nd_region: (parent of PMEM namespaces) */ #define ND_DEVICE_REGION_BLK 3 /* nd_region: (parent of BLK namespaces) */ diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index 1555c09efba1..3187322eeed7 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -223,6 +223,7 @@ static int nfit_test_cmd_set_config_data(struct nd_cmd_set_config_hdr *nd_cmd, } #define NFIT_TEST_ARS_RECORDS 4 +#define NFIT_TEST_CLEAR_ERR_UNIT 256 static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd, unsigned int buf_len) @@ -233,6 +234,7 @@ static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd, nd_cmd->max_ars_out = sizeof(struct nd_cmd_ars_status) + NFIT_TEST_ARS_RECORDS * sizeof(struct nd_ars_record); nd_cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16; + nd_cmd->clear_err_unit = NFIT_TEST_CLEAR_ERR_UNIT; return 0; } @@ -306,6 +308,28 @@ static int nfit_test_cmd_ars_status(struct ars_state *ars_state, return 0; } +static int nfit_test_cmd_clear_error(struct nd_cmd_clear_error *clear_err, + unsigned int buf_len, int *cmd_rc) +{ + const u64 mask = NFIT_TEST_CLEAR_ERR_UNIT - 1; + if (buf_len < sizeof(*clear_err)) + return -EINVAL; + + if ((clear_err->address & mask) || (clear_err->length & mask)) + return -EINVAL; + + /* + * Report 'all clear' success for all commands even though a new + * scrub will find errors again. This is enough to have the + * error removed from the 'badblocks' tracking in the pmem + * driver. + */ + clear_err->status = 0; + clear_err->cleared = clear_err->length; + *cmd_rc = 0; + return 0; +} + static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc) @@ -365,6 +389,9 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, rc = nfit_test_cmd_ars_status(ars_state, buf, buf_len, cmd_rc); break; + case ND_CMD_CLEAR_ERROR: + rc = nfit_test_cmd_clear_error(buf, buf_len, cmd_rc); + break; default: return -ENOTTY; } @@ -1230,6 +1257,7 @@ static void nfit_test0_setup(struct nfit_test *t) set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en); + set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_dsm_force_en); } static void nfit_test1_setup(struct nfit_test *t) @@ -1290,6 +1318,7 @@ static void nfit_test1_setup(struct nfit_test *t) set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_ARS_START, &acpi_desc->bus_dsm_force_en); set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_dsm_force_en); + set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_dsm_force_en); } static int nfit_test_blk_do_io(struct nd_blk_region *ndbr, resource_size_t dpa, -- cgit v1.2.3-70-g09d2