From b814735f5ca477a6d4f446f00d16da4a18e33bf5 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Fri, 2 Mar 2018 13:20:49 +0100 Subject: acpi, nfit: remove redundant __func__ in dev_dbg Dynamic debug can be instructed to add the function name to the debug output using the +f switch, so there is no need for the nfit module to do it again. If a user decides to add the +f switch for nfit's dynamic debug this results in double prints of the function name like the following: [ 2391.935383] acpi_nfit_ctl: nfit ACPI0012:00: acpi_nfit_ctl:nmem8 cmd: 10: func: 1 input length: 0 Thus remove the stray __func__ printing. Signed-off-by: Johannes Thumshirn Reviewed-by: Ross Zwisler Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 75 +++++++++++++++++++++++------------------------- drivers/acpi/nfit/mce.c | 5 ++-- 2 files changed, 38 insertions(+), 42 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index bbe48ad20886..39ad06143e78 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -476,8 +476,8 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, in_buf.buffer.length = call_pkg->nd_size_in; } - dev_dbg(dev, "%s:%s cmd: %d: func: %d input length: %d\n", - __func__, dimm_name, cmd, func, in_buf.buffer.length); + dev_dbg(dev, "%s cmd: %d: func: %d input length: %d\n", + dimm_name, cmd, func, in_buf.buffer.length); print_hex_dump_debug("nvdimm in ", DUMP_PREFIX_OFFSET, 4, 4, in_buf.buffer.pointer, min_t(u32, 256, in_buf.buffer.length), true); @@ -506,8 +506,7 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, } if (!out_obj) { - dev_dbg(dev, "%s:%s _DSM failed cmd: %s\n", __func__, dimm_name, - cmd_name); + dev_dbg(dev, "%s _DSM failed cmd: %s\n", dimm_name, cmd_name); return -EINVAL; } @@ -528,13 +527,13 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, } if (out_obj->package.type != ACPI_TYPE_BUFFER) { - dev_dbg(dev, "%s:%s unexpected output object type cmd: %s type: %d\n", - __func__, dimm_name, cmd_name, out_obj->type); + dev_dbg(dev, "%s unexpected output object type cmd: %s type: %d\n", + dimm_name, cmd_name, out_obj->type); rc = -EINVAL; goto out; } - dev_dbg(dev, "%s:%s cmd: %s output length: %d\n", __func__, dimm_name, + dev_dbg(dev, "%s cmd: %s output length: %d\n", dimm_name, cmd_name, out_obj->buffer.length); print_hex_dump_debug(cmd_name, DUMP_PREFIX_OFFSET, 4, 4, out_obj->buffer.pointer, @@ -546,14 +545,14 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, out_obj->buffer.length - offset); if (offset + out_size > out_obj->buffer.length) { - dev_dbg(dev, "%s:%s output object underflow cmd: %s field: %d\n", - __func__, dimm_name, cmd_name, i); + dev_dbg(dev, "%s output object underflow cmd: %s field: %d\n", + dimm_name, cmd_name, i); break; } if (in_buf.buffer.length + offset + out_size > buf_len) { - dev_dbg(dev, "%s:%s output overrun cmd: %s field: %d\n", - __func__, dimm_name, cmd_name, i); + dev_dbg(dev, "%s output overrun cmd: %s field: %d\n", + dimm_name, cmd_name, i); rc = -ENXIO; goto out; } @@ -655,7 +654,7 @@ static bool add_spa(struct acpi_nfit_desc *acpi_desc, INIT_LIST_HEAD(&nfit_spa->list); memcpy(nfit_spa->spa, spa, sizeof(*spa)); list_add_tail(&nfit_spa->list, &acpi_desc->spas); - dev_dbg(dev, "%s: spa index: %d type: %s\n", __func__, + dev_dbg(dev, "spa index: %d type: %s\n", spa->range_index, spa_type_name(nfit_spa_type(spa))); return true; @@ -684,8 +683,8 @@ static bool add_memdev(struct acpi_nfit_desc *acpi_desc, INIT_LIST_HEAD(&nfit_memdev->list); memcpy(nfit_memdev->memdev, memdev, sizeof(*memdev)); list_add_tail(&nfit_memdev->list, &acpi_desc->memdevs); - dev_dbg(dev, "%s: memdev handle: %#x spa: %d dcr: %d flags: %#x\n", - __func__, memdev->device_handle, memdev->range_index, + dev_dbg(dev, "memdev handle: %#x spa: %d dcr: %d flags: %#x\n", + memdev->device_handle, memdev->range_index, memdev->region_index, memdev->flags); return true; } @@ -727,7 +726,7 @@ static bool add_dcr(struct acpi_nfit_desc *acpi_desc, INIT_LIST_HEAD(&nfit_dcr->list); memcpy(nfit_dcr->dcr, dcr, sizeof_dcr(dcr)); list_add_tail(&nfit_dcr->list, &acpi_desc->dcrs); - dev_dbg(dev, "%s: dcr index: %d windows: %d\n", __func__, + dev_dbg(dev, "dcr index: %d windows: %d\n", dcr->region_index, dcr->windows); return true; } @@ -754,7 +753,7 @@ static bool add_bdw(struct acpi_nfit_desc *acpi_desc, INIT_LIST_HEAD(&nfit_bdw->list); memcpy(nfit_bdw->bdw, bdw, sizeof(*bdw)); list_add_tail(&nfit_bdw->list, &acpi_desc->bdws); - dev_dbg(dev, "%s: bdw dcr: %d windows: %d\n", __func__, + dev_dbg(dev, "bdw dcr: %d windows: %d\n", bdw->region_index, bdw->windows); return true; } @@ -793,7 +792,7 @@ static bool add_idt(struct acpi_nfit_desc *acpi_desc, INIT_LIST_HEAD(&nfit_idt->list); memcpy(nfit_idt->idt, idt, sizeof_idt(idt)); list_add_tail(&nfit_idt->list, &acpi_desc->idts); - dev_dbg(dev, "%s: idt index: %d num_lines: %d\n", __func__, + dev_dbg(dev, "idt index: %d num_lines: %d\n", idt->interleave_index, idt->line_count); return true; } @@ -833,7 +832,7 @@ static bool add_flush(struct acpi_nfit_desc *acpi_desc, INIT_LIST_HEAD(&nfit_flush->list); memcpy(nfit_flush->flush, flush, sizeof_flush(flush)); list_add_tail(&nfit_flush->list, &acpi_desc->flushes); - dev_dbg(dev, "%s: nfit_flush handle: %d hint_count: %d\n", __func__, + dev_dbg(dev, "nfit_flush handle: %d hint_count: %d\n", flush->device_handle, flush->hint_count); return true; } @@ -846,7 +845,7 @@ static bool add_platform_cap(struct acpi_nfit_desc *acpi_desc, mask = (1 << (pcap->highest_capability + 1)) - 1; acpi_desc->platform_cap = pcap->capabilities & mask; - dev_dbg(dev, "%s: cap: %#x\n", __func__, acpi_desc->platform_cap); + dev_dbg(dev, "cap: %#x\n", acpi_desc->platform_cap); return true; } @@ -893,7 +892,7 @@ static void *add_table(struct acpi_nfit_desc *acpi_desc, return err; break; case ACPI_NFIT_TYPE_SMBIOS: - dev_dbg(dev, "%s: smbios\n", __func__); + dev_dbg(dev, "smbios\n"); break; case ACPI_NFIT_TYPE_CAPABILITIES: if (!add_platform_cap(acpi_desc, table)) @@ -1621,7 +1620,7 @@ void __acpi_nvdimm_notify(struct device *dev, u32 event) struct nfit_mem *nfit_mem; struct acpi_nfit_desc *acpi_desc; - dev_dbg(dev->parent, "%s: %s: event: %d\n", dev_name(dev), __func__, + dev_dbg(dev->parent, "%s: event: %d\n", dev_name(dev), event); if (event != NFIT_NOTIFY_DIMM_HEALTH) { @@ -2338,7 +2337,7 @@ static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus, nvdimm = nd_blk_region_to_dimm(ndbr); nfit_mem = nvdimm_provider_data(nvdimm); if (!nfit_mem || !nfit_mem->dcr || !nfit_mem->bdw) { - dev_dbg(dev, "%s: missing%s%s%s\n", __func__, + dev_dbg(dev, "missing%s%s%s\n", nfit_mem ? "" : " nfit_mem", (nfit_mem && nfit_mem->dcr) ? "" : " dcr", (nfit_mem && nfit_mem->bdw) ? "" : " bdw"); @@ -2357,7 +2356,7 @@ static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus, mmio->addr.base = devm_nvdimm_memremap(dev, nfit_mem->spa_bdw->address, nfit_mem->spa_bdw->length, nd_blk_memremap_flags(ndbr)); if (!mmio->addr.base) { - dev_dbg(dev, "%s: %s failed to map bdw\n", __func__, + dev_dbg(dev, "%s failed to map bdw\n", nvdimm_name(nvdimm)); return -ENOMEM; } @@ -2368,8 +2367,8 @@ static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus, rc = nfit_blk_init_interleave(mmio, nfit_mem->idt_bdw, nfit_mem->memdev_bdw->interleave_ways); if (rc) { - dev_dbg(dev, "%s: %s failed to init bdw interleave\n", - __func__, nvdimm_name(nvdimm)); + dev_dbg(dev, "%s failed to init bdw interleave\n", + nvdimm_name(nvdimm)); return rc; } @@ -2380,7 +2379,7 @@ static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus, mmio->addr.base = devm_nvdimm_ioremap(dev, nfit_mem->spa_dcr->address, nfit_mem->spa_dcr->length); if (!mmio->addr.base) { - dev_dbg(dev, "%s: %s failed to map dcr\n", __func__, + dev_dbg(dev, "%s failed to map dcr\n", nvdimm_name(nvdimm)); return -ENOMEM; } @@ -2391,15 +2390,15 @@ static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus, rc = nfit_blk_init_interleave(mmio, nfit_mem->idt_dcr, nfit_mem->memdev_dcr->interleave_ways); if (rc) { - dev_dbg(dev, "%s: %s failed to init dcr interleave\n", - __func__, nvdimm_name(nvdimm)); + dev_dbg(dev, "%s failed to init dcr interleave\n", + nvdimm_name(nvdimm)); return rc; } rc = acpi_nfit_blk_get_flags(nd_desc, nvdimm, nfit_blk); if (rc < 0) { - dev_dbg(dev, "%s: %s failed get DIMM flags\n", - __func__, nvdimm_name(nvdimm)); + dev_dbg(dev, "%s failed get DIMM flags\n", + nvdimm_name(nvdimm)); return rc; } @@ -2655,8 +2654,7 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc, return 0; if (spa->range_index == 0 && !nfit_spa_is_virtual(spa)) { - dev_dbg(acpi_desc->dev, "%s: detected invalid spa index\n", - __func__); + dev_dbg(acpi_desc->dev, "detected invalid spa index\n"); return 0; } @@ -3142,8 +3140,7 @@ int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *data, acpi_size sz) data = add_table(acpi_desc, &prev, data, end); if (IS_ERR(data)) { - dev_dbg(dev, "%s: nfit table parsing error: %ld\n", __func__, - PTR_ERR(data)); + dev_dbg(dev, "nfit table parsing error: %ld\n", PTR_ERR(data)); rc = PTR_ERR(data); goto out_unlock; } @@ -3259,7 +3256,7 @@ int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, u8 flags) } acpi_desc->ars_start_flags = flags; queue_work(nfit_wq, &acpi_desc->work); - dev_dbg(dev, "%s: ars_scan triggered\n", __func__); + dev_dbg(dev, "ars_scan triggered\n"); mutex_unlock(&acpi_desc->init_mutex); return 0; @@ -3366,8 +3363,8 @@ static int acpi_nfit_add(struct acpi_device *adev) rc = acpi_nfit_init(acpi_desc, obj->buffer.pointer, obj->buffer.length); else - dev_dbg(dev, "%s invalid type %d, ignoring _FIT\n", - __func__, (int) obj->type); + dev_dbg(dev, "invalid type %d, ignoring _FIT\n", + (int) obj->type); kfree(buf.pointer); } else /* skip over the lead-in header table */ @@ -3396,7 +3393,7 @@ static void acpi_nfit_update_notify(struct device *dev, acpi_handle handle) if (!dev->driver) { /* dev->driver may be null if we're being removed */ - dev_dbg(dev, "%s: no driver found for dev\n", __func__); + dev_dbg(dev, "no driver found for dev\n"); return; } @@ -3442,7 +3439,7 @@ static void acpi_nfit_uc_error_notify(struct device *dev, acpi_handle handle) void __acpi_nfit_notify(struct device *dev, acpi_handle handle, u32 event) { - dev_dbg(dev, "%s: event: 0x%x\n", __func__, event); + dev_dbg(dev, "event: 0x%x\n", event); switch (event) { case NFIT_NOTIFY_UPDATE: diff --git a/drivers/acpi/nfit/mce.c b/drivers/acpi/nfit/mce.c index b92921439657..e9626bf6ca29 100644 --- a/drivers/acpi/nfit/mce.c +++ b/drivers/acpi/nfit/mce.c @@ -51,9 +51,8 @@ static int nfit_handle_mce(struct notifier_block *nb, unsigned long val, if ((spa->address + spa->length - 1) < mce->addr) continue; found_match = 1; - dev_dbg(dev, "%s: addr in SPA %d (0x%llx, 0x%llx)\n", - __func__, spa->range_index, spa->address, - spa->length); + dev_dbg(dev, "addr in SPA %d (0x%llx, 0x%llx)\n", + spa->range_index, spa->address, spa->length); /* * We can break at the first match because we're going * to rescan all the SPA ranges. There shouldn't be any -- cgit From 0731de476a37c33485af82d64041c9d193208df8 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 21 Mar 2018 21:22:34 -0700 Subject: nfit: skip region registration for incomplete control regions Per the ACPI specification the only functional purpose for a DIMM Control Region to be mapped into the system physical address space, from an OSPM perspective, is to support block-apertures. However, there are some BIOSen that publish DIMM Control Region SPA entries for pre-boot environment consumption. Undo the kernel policy of generating disabled 'ndblk' regions when this configuration is detected. Cc: Fixes: 1f7df6f88b92 ("libnvdimm, nfit: regions (block-data-window...)") Reviewed-by: Toshi Kani Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 39ad06143e78..4530d89044db 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -2578,7 +2578,7 @@ static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc, struct acpi_nfit_system_address *spa = nfit_spa->spa; struct nd_blk_region_desc *ndbr_desc; struct nfit_mem *nfit_mem; - int blk_valid = 0, rc; + int rc; if (!nvdimm) { dev_err(acpi_desc->dev, "spa%d dimm: %#x not found\n", @@ -2598,15 +2598,14 @@ static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc, if (!nfit_mem || !nfit_mem->bdw) { dev_dbg(acpi_desc->dev, "spa%d %s missing bdw\n", spa->range_index, nvdimm_name(nvdimm)); - } else { - mapping->size = nfit_mem->bdw->capacity; - mapping->start = nfit_mem->bdw->start_address; - ndr_desc->num_lanes = nfit_mem->bdw->windows; - blk_valid = 1; + break; } + mapping->size = nfit_mem->bdw->capacity; + mapping->start = nfit_mem->bdw->start_address; + ndr_desc->num_lanes = nfit_mem->bdw->windows; ndr_desc->mapping = mapping; - ndr_desc->num_mappings = blk_valid; + ndr_desc->num_mappings = 1; ndbr_desc = to_blk_region_desc(ndr_desc); ndbr_desc->enable = acpi_nfit_blk_region_enable; ndbr_desc->do_io = acpi_desc->blk_do_io; -- cgit From 466d1493ea830789a2f063f478aaed2e324f0d3d Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 28 Mar 2018 10:44:50 -0700 Subject: acpi, nfit: rework NVDIMM leaf method detection Some BIOSen do not handle 0-byte transfer lengths for the _LSR and _LSW (label storage read/write) methods. This causes Linux to fallback to the deprecated _DSM path, or otherwise disable label support. Introduce acpi_nvdimm_has_method() to detect whether a method is available rather than calling the method, require _LSI and _LSR to be paired, and require read support before enabling write support. Cc: Fixes: 4b27db7e26cd ("acpi, nfit: add support for the _LS...") Suggested-by: Erik Schmauss Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 41 +++++++++++++++++++++-------------------- drivers/acpi/nfit/nfit.h | 5 ++--- 2 files changed, 23 insertions(+), 23 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 4530d89044db..12fb414fa678 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -196,7 +196,7 @@ static int xlat_nvdimm_status(struct nvdimm *nvdimm, void *buf, unsigned int cmd * In the _LSI, _LSR, _LSW case the locked status is * communicated via the read/write commands */ - if (nfit_mem->has_lsi) + if (nfit_mem->has_lsr) break; if (status >> 16 & ND_CONFIG_LOCKED) @@ -483,7 +483,7 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, min_t(u32, 256, in_buf.buffer.length), true); /* call the BIOS, prefer the named methods over _DSM if available */ - if (nvdimm && cmd == ND_CMD_GET_CONFIG_SIZE && nfit_mem->has_lsi) + if (nvdimm && cmd == ND_CMD_GET_CONFIG_SIZE && nfit_mem->has_lsr) out_obj = acpi_label_info(handle); else if (nvdimm && cmd == ND_CMD_GET_CONFIG_DATA && nfit_mem->has_lsr) { struct nd_cmd_get_config_data_hdr *p = buf; @@ -1653,12 +1653,23 @@ static void acpi_nvdimm_notify(acpi_handle handle, u32 event, void *data) device_unlock(dev->parent); } +static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method) +{ + acpi_handle handle; + acpi_status status; + + status = acpi_get_handle(adev->handle, method, &handle); + + if (ACPI_SUCCESS(status)) + return true; + return false; +} + static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, struct nfit_mem *nfit_mem, u32 device_handle) { struct acpi_device *adev, *adev_dimm; struct device *dev = acpi_desc->dev; - union acpi_object *obj; unsigned long dsm_mask; const guid_t *guid; int i; @@ -1731,25 +1742,15 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, 1ULL << i)) set_bit(i, &nfit_mem->dsm_mask); - obj = acpi_label_info(adev_dimm->handle); - if (obj) { - ACPI_FREE(obj); - nfit_mem->has_lsi = 1; - dev_dbg(dev, "%s: has _LSI\n", dev_name(&adev_dimm->dev)); - } - - obj = acpi_label_read(adev_dimm->handle, 0, 0); - if (obj) { - ACPI_FREE(obj); - nfit_mem->has_lsr = 1; + if (acpi_nvdimm_has_method(adev_dimm, "_LSI") + && acpi_nvdimm_has_method(adev_dimm, "_LSR")) { dev_dbg(dev, "%s: has _LSR\n", dev_name(&adev_dimm->dev)); + nfit_mem->has_lsr = true; } - obj = acpi_label_write(adev_dimm->handle, 0, 0, NULL); - if (obj) { - ACPI_FREE(obj); - nfit_mem->has_lsw = 1; + if (nfit_mem->has_lsr && acpi_nvdimm_has_method(adev_dimm, "_LSW")) { dev_dbg(dev, "%s: has _LSW\n", dev_name(&adev_dimm->dev)); + nfit_mem->has_lsw = true; } return 0; @@ -1838,10 +1839,10 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) cmd_mask |= nfit_mem->dsm_mask & NVDIMM_STANDARD_CMDMASK; } - if (nfit_mem->has_lsi) + if (nfit_mem->has_lsr) { set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask); - if (nfit_mem->has_lsr) set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask); + } if (nfit_mem->has_lsw) set_bit(ND_CMD_SET_CONFIG_DATA, &cmd_mask); diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h index 50d36e166d70..ac9c49463731 100644 --- a/drivers/acpi/nfit/nfit.h +++ b/drivers/acpi/nfit/nfit.h @@ -171,9 +171,8 @@ struct nfit_mem { struct resource *flush_wpq; unsigned long dsm_mask; int family; - u32 has_lsi:1; - u32 has_lsr:1; - u32 has_lsw:1; + bool has_lsr; + bool has_lsw; }; struct acpi_nfit_desc { -- cgit From 8d0d8ed3356aa9ed43b819aaedd39b08ca453007 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 2 Apr 2018 16:49:30 -0700 Subject: nfit: fix region registration vs block-data-window ranges Commit 1cf03c00e7c1 "nfit: scrub and register regions in a workqueue" mistakenly attempts to register a region per BLK aperture. There is nothing to register for individual apertures as they belong as a set to a BLK aperture group that are registered with a corresponding DIMM-control-region. Filter them for registration to prevent some needless devm_kzalloc() allocations. Cc: Fixes: 1cf03c00e7c1 ("nfit: scrub and register regions in a workqueue") Reviewed-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 12fb414fa678..ea9f3e727fef 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -3018,15 +3018,21 @@ static void acpi_nfit_scrub(struct work_struct *work) static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc) { struct nfit_spa *nfit_spa; - int rc; - list_for_each_entry(nfit_spa, &acpi_desc->spas, list) - if (nfit_spa_type(nfit_spa->spa) == NFIT_SPA_DCR) { - /* BLK regions don't need to wait for ars results */ - rc = acpi_nfit_register_region(acpi_desc, nfit_spa); - if (rc) - return rc; - } + list_for_each_entry(nfit_spa, &acpi_desc->spas, list) { + int rc, type = nfit_spa_type(nfit_spa->spa); + + /* PMEM and VMEM will be registered by the ARS workqueue */ + if (type == NFIT_SPA_PM || type == NFIT_SPA_VOLATILE) + continue; + /* BLK apertures belong to BLK region registration below */ + if (type == NFIT_SPA_BDW) + continue; + /* BLK regions don't need to wait for ARS results */ + rc = acpi_nfit_register_region(acpi_desc, nfit_spa); + if (rc) + return rc; + } acpi_desc->ars_start_flags = 0; if (!acpi_desc->cancel) -- cgit From 78727137fdf49edf9f731bde79d7189067b4047a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 2 Apr 2018 16:40:04 -0700 Subject: nfit, address-range-scrub: fix scrub in-progress reporting There is a small window whereby ARS scan requests can schedule work that userspace will miss when polling scrub_show. Hold the init_mutex lock over calls to report the status to close this potential escape. Also, make sure that requests to cancel the ARS workqueue are treated as an idle event. Cc: Cc: Vishal Verma Fixes: 37b137ff8c83 ("nfit, libnvdimm: allow an ARS scrub...") Reviewed-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index ea9f3e727fef..2a1fc3817a81 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -1249,8 +1249,11 @@ static ssize_t scrub_show(struct device *dev, if (nd_desc) { struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); + mutex_lock(&acpi_desc->init_mutex); rc = sprintf(buf, "%d%s", acpi_desc->scrub_count, - (work_busy(&acpi_desc->work)) ? "+\n" : "\n"); + work_busy(&acpi_desc->work) + && !acpi_desc->cancel ? "+\n" : "\n"); + mutex_unlock(&acpi_desc->init_mutex); } device_unlock(dev); return rc; -- cgit From 14c73f997a5e060c6887a80c143021a58975c92a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 2 Apr 2018 15:40:30 -0700 Subject: nfit, address-range-scrub: introduce nfit_spa->ars_state In preparation for re-working the ARS implementation to better handle short vs long ARS runs, introduce nfit_spa->ars_state. For now this just replaces the nfit_spa->ars_required bit-field/flag, but going forward it will be used to track ARS completion and make short vs long ARS requests. Reviewed-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 16 +++++++++------- drivers/acpi/nfit/nfit.h | 11 +++++++++-- 2 files changed, 18 insertions(+), 9 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 2a1fc3817a81..9c56022c216f 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -2808,7 +2808,7 @@ static void acpi_nfit_async_scrub(struct acpi_nfit_desc *acpi_desc, unsigned int tmo = scrub_timeout; int rc; - if (!nfit_spa->ars_required || !nfit_spa->nd_region) + if (!test_bit(ARS_REQ, &nfit_spa->ars_state) || !nfit_spa->nd_region) return; rc = ars_start(acpi_desc, nfit_spa); @@ -3003,7 +3003,7 @@ static void acpi_nfit_scrub(struct work_struct *work) * register them now to make data available. */ if (!nfit_spa->nd_region) { - nfit_spa->ars_required = 1; + set_bit(ARS_REQ, &nfit_spa->ars_state); acpi_nfit_register_region(acpi_desc, nfit_spa); } } @@ -3241,7 +3241,7 @@ static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc, return 0; } -int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, u8 flags) +int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, unsigned long flags) { struct device *dev = acpi_desc->dev; struct nfit_spa *nfit_spa; @@ -3261,9 +3261,11 @@ int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, u8 flags) if (nfit_spa_type(spa) != NFIT_SPA_PM) continue; - nfit_spa->ars_required = 1; + set_bit(ARS_REQ, &nfit_spa->ars_state); } - acpi_desc->ars_start_flags = flags; + acpi_desc->ars_start_flags = 0; + if (test_bit(ARS_SHORT, &flags)) + acpi_desc->ars_start_flags |= ND_ARS_RETURN_PREV_DATA; queue_work(nfit_wq, &acpi_desc->work); dev_dbg(dev, "ars_scan triggered\n"); mutex_unlock(&acpi_desc->init_mutex); @@ -3440,8 +3442,8 @@ static void acpi_nfit_update_notify(struct device *dev, acpi_handle handle) static void acpi_nfit_uc_error_notify(struct device *dev, acpi_handle handle) { struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(dev); - u8 flags = (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON) ? - 0 : ND_ARS_RETURN_PREV_DATA; + unsigned long flags = (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON) ? + 0 : 1 << ARS_SHORT; acpi_nfit_ars_rescan(acpi_desc, flags); } diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h index ac9c49463731..2b97e5f76bdf 100644 --- a/drivers/acpi/nfit/nfit.h +++ b/drivers/acpi/nfit/nfit.h @@ -117,10 +117,17 @@ enum nfit_dimm_notifiers { NFIT_NOTIFY_DIMM_HEALTH = 0x81, }; +enum nfit_ars_state { + ARS_REQ, + ARS_DONE, + ARS_SHORT, + ARS_FAILED, +}; + struct nfit_spa { struct list_head list; struct nd_region *nd_region; - unsigned int ars_required:1; + unsigned long ars_state; u32 clear_err_unit; u32 max_ars; struct acpi_nfit_system_address spa[0]; @@ -243,7 +250,7 @@ struct nfit_blk { extern struct list_head acpi_descs; extern struct mutex acpi_desc_lock; -int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, u8 flags); +int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, unsigned long flags); #ifdef CONFIG_X86_MCE void nfit_mce_register(void); -- cgit From 459d0ddb079c869c986e1bb871c91564a4b8ccfe Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 5 Apr 2018 01:25:02 -0700 Subject: nfit, address-range-scrub: determine one platform max_ars value acpi_nfit_query_poison() is awkward in that it requires an nfit_spa argument in order to determine what max_ars value to use. Instead probe for the minimum max_ars across all scrub-capable ranges in the system and drop the nfit_spa argument. This enables a larger rework / simplification of the ARS state machine whereby the status can be retrieved once and then iterated over all address ranges to reap completions. Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 78 +++++++++++++++++++++++++----------------------- drivers/acpi/nfit/nfit.h | 2 +- 2 files changed, 41 insertions(+), 39 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 9c56022c216f..866853abebea 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -2494,16 +2494,16 @@ static int ars_get_status(struct acpi_nfit_desc *acpi_desc) int rc, cmd_rc; rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_STATUS, ars_status, - acpi_desc->ars_status_size, &cmd_rc); + acpi_desc->max_ars, &cmd_rc); if (rc < 0) return rc; return cmd_rc; } -static int ars_status_process_records(struct acpi_nfit_desc *acpi_desc, - struct nd_cmd_ars_status *ars_status) +static int ars_status_process_records(struct acpi_nfit_desc *acpi_desc) { struct nvdimm_bus *nvdimm_bus = acpi_desc->nvdimm_bus; + struct nd_cmd_ars_status *ars_status = acpi_desc->ars_status; int rc; u32 i; @@ -2739,60 +2739,35 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc, return rc; } -static int ars_status_alloc(struct acpi_nfit_desc *acpi_desc, - u32 max_ars) +static int ars_status_alloc(struct acpi_nfit_desc *acpi_desc) { struct device *dev = acpi_desc->dev; struct nd_cmd_ars_status *ars_status; - if (acpi_desc->ars_status && acpi_desc->ars_status_size >= max_ars) { - memset(acpi_desc->ars_status, 0, acpi_desc->ars_status_size); + if (acpi_desc->ars_status) { + memset(acpi_desc->ars_status, 0, acpi_desc->max_ars); return 0; } - if (acpi_desc->ars_status) - devm_kfree(dev, acpi_desc->ars_status); - acpi_desc->ars_status = NULL; - ars_status = devm_kzalloc(dev, max_ars, GFP_KERNEL); + ars_status = devm_kzalloc(dev, acpi_desc->max_ars, GFP_KERNEL); if (!ars_status) return -ENOMEM; acpi_desc->ars_status = ars_status; - acpi_desc->ars_status_size = max_ars; return 0; } -static int acpi_nfit_query_poison(struct acpi_nfit_desc *acpi_desc, - struct nfit_spa *nfit_spa) +static int acpi_nfit_query_poison(struct acpi_nfit_desc *acpi_desc) { - struct acpi_nfit_system_address *spa = nfit_spa->spa; int rc; - if (!nfit_spa->max_ars) { - struct nd_cmd_ars_cap ars_cap; - - memset(&ars_cap, 0, sizeof(ars_cap)); - rc = ars_get_cap(acpi_desc, &ars_cap, nfit_spa); - if (rc < 0) - return rc; - nfit_spa->max_ars = ars_cap.max_ars_out; - nfit_spa->clear_err_unit = ars_cap.clear_err_unit; - /* check that the supported scrub types match the spa type */ - if (nfit_spa_type(spa) == NFIT_SPA_VOLATILE && - ((ars_cap.status >> 16) & ND_ARS_VOLATILE) == 0) - return -ENOTTY; - else if (nfit_spa_type(spa) == NFIT_SPA_PM && - ((ars_cap.status >> 16) & ND_ARS_PERSISTENT) == 0) - return -ENOTTY; - } - - if (ars_status_alloc(acpi_desc, nfit_spa->max_ars)) + if (ars_status_alloc(acpi_desc)) return -ENOMEM; rc = ars_get_status(acpi_desc); if (rc < 0 && rc != -ENOSPC) return rc; - if (ars_status_process_records(acpi_desc, acpi_desc->ars_status)) + if (ars_status_process_records(acpi_desc)) return -ENOMEM; return 0; @@ -2824,7 +2799,7 @@ static void acpi_nfit_async_scrub(struct acpi_nfit_desc *acpi_desc, if (acpi_desc->cancel) break; - rc = acpi_nfit_query_poison(acpi_desc, nfit_spa); + rc = acpi_nfit_query_poison(acpi_desc); if (rc == -ENOTTY) break; if (rc == -EBUSY && !tmo) { @@ -2925,7 +2900,7 @@ static void acpi_nfit_scrub(struct work_struct *work) */ rc = 0; } else - rc = acpi_nfit_query_poison(acpi_desc, nfit_spa); + rc = acpi_nfit_query_poison(acpi_desc); if (rc == -ENOTTY) { /* no ars capability, just register spa and move on */ @@ -3018,6 +2993,31 @@ static void acpi_nfit_scrub(struct work_struct *work) mutex_unlock(&acpi_desc->init_mutex); } +static void acpi_nfit_init_ars(struct acpi_nfit_desc *acpi_desc, + struct nfit_spa *nfit_spa) +{ + int type = nfit_spa_type(nfit_spa->spa); + struct nd_cmd_ars_cap ars_cap; + int rc; + + memset(&ars_cap, 0, sizeof(ars_cap)); + rc = ars_get_cap(acpi_desc, &ars_cap, nfit_spa); + if (rc < 0) + return; + /* check that the supported scrub types match the spa type */ + if (type == NFIT_SPA_VOLATILE && ((ars_cap.status >> 16) + & ND_ARS_VOLATILE) == 0) + return; + if (type == NFIT_SPA_PM && ((ars_cap.status >> 16) + & ND_ARS_PERSISTENT) == 0) + return; + + nfit_spa->max_ars = ars_cap.max_ars_out; + nfit_spa->clear_err_unit = ars_cap.clear_err_unit; + acpi_desc->max_ars = max(nfit_spa->max_ars, acpi_desc->max_ars); +} + + static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc) { struct nfit_spa *nfit_spa; @@ -3026,8 +3026,10 @@ static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc) int rc, type = nfit_spa_type(nfit_spa->spa); /* PMEM and VMEM will be registered by the ARS workqueue */ - if (type == NFIT_SPA_PM || type == NFIT_SPA_VOLATILE) + if (type == NFIT_SPA_PM || type == NFIT_SPA_VOLATILE) { + acpi_nfit_init_ars(acpi_desc, nfit_spa); continue; + } /* BLK apertures belong to BLK region registration below */ if (type == NFIT_SPA_BDW) continue; diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h index 2b97e5f76bdf..45e7949986a8 100644 --- a/drivers/acpi/nfit/nfit.h +++ b/drivers/acpi/nfit/nfit.h @@ -197,10 +197,10 @@ struct acpi_nfit_desc { struct device *dev; u8 ars_start_flags; struct nd_cmd_ars_status *ars_status; - size_t ars_status_size; struct work_struct work; struct list_head list; struct kernfs_node *scrub_count_state; + unsigned int max_ars; unsigned int scrub_count; unsigned int scrub_mode; unsigned int cancel:1; -- cgit From bc6ba8085842164f2a8dc2e78e23a7167872abbe Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 5 Apr 2018 16:18:55 -0700 Subject: nfit, address-range-scrub: rework and simplify ARS state machine ARS is an operation that can take 10s to 100s of seconds to find media errors that should rarely be present. If the platform crashes due to media errors in persistent memory, the expectation is that the BIOS will report those known errors in a 'short' ARS request. A 'short' ARS request asks platform firmware to return an ARS payload with all known errors, but without issuing a 'long' scrub. At driver init a short request is issued to all PMEM ranges before registering regions. Then, in the background, a long ARS is scheduled for each region. The ARS implementation is simplified to centralize ARS completion work in the ars_complete() helper. The timeout is removed since there is no facility to cancel ARS, and this otherwise arranges for system init to never be blocked waiting for a 'long' ARS. The ars_state flags are used to coordinate ARS requests from driver init, ARS requests from userspace, and ARS requests in response to media error notifications. Given that there is no notification of ARS completion the implementation still needs to poll. It backs off exponentially to a maximum poll period of 30 minutes. Suggested-by: Toshi Kani Co-developed-by: Dave Jiang Signed-off-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 482 +++++++++++++++++++++-------------------------- drivers/acpi/nfit/nfit.h | 4 +- 2 files changed, 218 insertions(+), 268 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 866853abebea..2532294bbd68 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -35,16 +35,6 @@ static bool force_enable_dimms; module_param(force_enable_dimms, bool, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(force_enable_dimms, "Ignore _STA (ACPI DIMM device) status"); -static unsigned int scrub_timeout = NFIT_ARS_TIMEOUT; -module_param(scrub_timeout, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(scrub_timeout, "Initial scrub timeout in seconds"); - -/* after three payloads of overflow, it's dead jim */ -static unsigned int scrub_overflow_abort = 3; -module_param(scrub_overflow_abort, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(scrub_overflow_abort, - "Number of times we overflow ARS results before abort"); - static bool disable_vendor_specific; module_param(disable_vendor_specific, bool, S_IRUGO); MODULE_PARM_DESC(disable_vendor_specific, @@ -1251,7 +1241,7 @@ static ssize_t scrub_show(struct device *dev, mutex_lock(&acpi_desc->init_mutex); rc = sprintf(buf, "%d%s", acpi_desc->scrub_count, - work_busy(&acpi_desc->work) + work_busy(&acpi_desc->dwork.work) && !acpi_desc->cancel ? "+\n" : "\n"); mutex_unlock(&acpi_desc->init_mutex); } @@ -2452,7 +2442,8 @@ static int ars_start(struct acpi_nfit_desc *acpi_desc, struct nfit_spa *nfit_spa memset(&ars_start, 0, sizeof(ars_start)); ars_start.address = spa->address; ars_start.length = spa->length; - ars_start.flags = acpi_desc->ars_start_flags; + if (test_bit(ARS_SHORT, &nfit_spa->ars_state)) + ars_start.flags = ND_ARS_RETURN_PREV_DATA; if (nfit_spa_type(spa) == NFIT_SPA_PM) ars_start.type = ND_ARS_PERSISTENT; else if (nfit_spa_type(spa) == NFIT_SPA_VOLATILE) @@ -2500,6 +2491,52 @@ static int ars_get_status(struct acpi_nfit_desc *acpi_desc) return cmd_rc; } +static void ars_complete(struct acpi_nfit_desc *acpi_desc, + struct nfit_spa *nfit_spa) +{ + struct nd_cmd_ars_status *ars_status = acpi_desc->ars_status; + struct acpi_nfit_system_address *spa = nfit_spa->spa; + struct nd_region *nd_region = nfit_spa->nd_region; + struct device *dev; + + if ((ars_status->address >= spa->address && ars_status->address + < spa->address + spa->length) + || (ars_status->address < spa->address)) { + /* + * Assume that if a scrub starts at an offset from the + * start of nfit_spa that we are in the continuation + * case. + * + * Otherwise, if the scrub covers the spa range, mark + * any pending request complete. + */ + if (ars_status->address + ars_status->length + >= spa->address + spa->length) + /* complete */; + else + return; + } else + return; + + if (test_bit(ARS_DONE, &nfit_spa->ars_state)) + return; + + if (!test_and_clear_bit(ARS_REQ, &nfit_spa->ars_state)) + return; + + if (nd_region) { + dev = nd_region_dev(nd_region); + nvdimm_region_notify(nd_region, NVDIMM_REVALIDATE_POISON); + } else + dev = acpi_desc->dev; + + dev_dbg(dev, "ARS: range %d %s complete\n", spa->range_index, + test_bit(ARS_SHORT, &nfit_spa->ars_state) + ? "short" : "long"); + clear_bit(ARS_SHORT, &nfit_spa->ars_state); + set_bit(ARS_DONE, &nfit_spa->ars_state); +} + static int ars_status_process_records(struct acpi_nfit_desc *acpi_desc) { struct nvdimm_bus *nvdimm_bus = acpi_desc->nvdimm_bus; @@ -2764,6 +2801,7 @@ static int acpi_nfit_query_poison(struct acpi_nfit_desc *acpi_desc) return -ENOMEM; rc = ars_get_status(acpi_desc); + if (rc < 0 && rc != -ENOSPC) return rc; @@ -2773,223 +2811,125 @@ static int acpi_nfit_query_poison(struct acpi_nfit_desc *acpi_desc) return 0; } -static void acpi_nfit_async_scrub(struct acpi_nfit_desc *acpi_desc, - struct nfit_spa *nfit_spa) +static int ars_register(struct acpi_nfit_desc *acpi_desc, struct nfit_spa *nfit_spa, + int *query_rc) { - struct acpi_nfit_system_address *spa = nfit_spa->spa; - unsigned int overflow_retry = scrub_overflow_abort; - u64 init_ars_start = 0, init_ars_len = 0; - struct device *dev = acpi_desc->dev; - unsigned int tmo = scrub_timeout; - int rc; - - if (!test_bit(ARS_REQ, &nfit_spa->ars_state) || !nfit_spa->nd_region) - return; - - rc = ars_start(acpi_desc, nfit_spa); - /* - * If we timed out the initial scan we'll still be busy here, - * and will wait another timeout before giving up permanently. - */ - if (rc < 0 && rc != -EBUSY) - return; + int rc = *query_rc; - do { - u64 ars_start, ars_len; - - if (acpi_desc->cancel) - break; - rc = acpi_nfit_query_poison(acpi_desc); - if (rc == -ENOTTY) - break; - if (rc == -EBUSY && !tmo) { - dev_warn(dev, "range %d ars timeout, aborting\n", - spa->range_index); - break; - } + set_bit(ARS_REQ, &nfit_spa->ars_state); + set_bit(ARS_SHORT, &nfit_spa->ars_state); + switch (rc) { + case 0: + case -EAGAIN: + rc = ars_start(acpi_desc, nfit_spa); if (rc == -EBUSY) { - /* - * Note, entries may be appended to the list - * while the lock is dropped, but the workqueue - * being active prevents entries being deleted / - * freed. - */ - mutex_unlock(&acpi_desc->init_mutex); - ssleep(1); - tmo--; - mutex_lock(&acpi_desc->init_mutex); - continue; - } - - /* we got some results, but there are more pending... */ - if (rc == -ENOSPC && overflow_retry--) { - if (!init_ars_len) { - init_ars_len = acpi_desc->ars_status->length; - init_ars_start = acpi_desc->ars_status->address; - } - rc = ars_continue(acpi_desc); - } - - if (rc < 0) { - dev_warn(dev, "range %d ars continuation failed\n", - spa->range_index); + *query_rc = rc; break; - } - - if (init_ars_len) { - ars_start = init_ars_start; - ars_len = init_ars_len; + } else if (rc == 0) { + rc = acpi_nfit_query_poison(acpi_desc); } else { - ars_start = acpi_desc->ars_status->address; - ars_len = acpi_desc->ars_status->length; + set_bit(ARS_FAILED, &nfit_spa->ars_state); + break; } - dev_dbg(dev, "spa range: %d ars from %#llx + %#llx complete\n", - spa->range_index, ars_start, ars_len); - /* notify the region about new poison entries */ - nvdimm_region_notify(nfit_spa->nd_region, - NVDIMM_REVALIDATE_POISON); + if (rc == -EAGAIN) + clear_bit(ARS_SHORT, &nfit_spa->ars_state); + else if (rc == 0) + ars_complete(acpi_desc, nfit_spa); + break; + case -EBUSY: + case -ENOSPC: break; - } while (1); + default: + set_bit(ARS_FAILED, &nfit_spa->ars_state); + break; + } + + if (test_and_clear_bit(ARS_DONE, &nfit_spa->ars_state)) + set_bit(ARS_REQ, &nfit_spa->ars_state); + + return acpi_nfit_register_region(acpi_desc, nfit_spa); } -static void acpi_nfit_scrub(struct work_struct *work) +static void ars_complete_all(struct acpi_nfit_desc *acpi_desc) { - struct device *dev; - u64 init_scrub_length = 0; struct nfit_spa *nfit_spa; - u64 init_scrub_address = 0; - bool init_ars_done = false; - struct acpi_nfit_desc *acpi_desc; - unsigned int tmo = scrub_timeout; - unsigned int overflow_retry = scrub_overflow_abort; - - acpi_desc = container_of(work, typeof(*acpi_desc), work); - dev = acpi_desc->dev; - - /* - * We scrub in 2 phases. The first phase waits for any platform - * firmware initiated scrubs to complete and then we go search for the - * affected spa regions to mark them scanned. In the second phase we - * initiate a directed scrub for every range that was not scrubbed in - * phase 1. If we're called for a 'rescan', we harmlessly pass through - * the first phase, but really only care about running phase 2, where - * regions can be notified of new poison. - */ - /* process platform firmware initiated scrubs */ - retry: - mutex_lock(&acpi_desc->init_mutex); list_for_each_entry(nfit_spa, &acpi_desc->spas, list) { - struct nd_cmd_ars_status *ars_status; - struct acpi_nfit_system_address *spa; - u64 ars_start, ars_len; - int rc; - - if (acpi_desc->cancel) - break; - - if (nfit_spa->nd_region) - continue; - - if (init_ars_done) { - /* - * No need to re-query, we're now just - * reconciling all the ranges covered by the - * initial scrub - */ - rc = 0; - } else - rc = acpi_nfit_query_poison(acpi_desc); - - if (rc == -ENOTTY) { - /* no ars capability, just register spa and move on */ - acpi_nfit_register_region(acpi_desc, nfit_spa); + if (test_bit(ARS_FAILED, &nfit_spa->ars_state)) continue; - } - - if (rc == -EBUSY && !tmo) { - /* fallthrough to directed scrub in phase 2 */ - dev_warn(dev, "timeout awaiting ars results, continuing...\n"); - break; - } else if (rc == -EBUSY) { - mutex_unlock(&acpi_desc->init_mutex); - ssleep(1); - tmo--; - goto retry; - } + ars_complete(acpi_desc, nfit_spa); + } +} - /* we got some results, but there are more pending... */ - if (rc == -ENOSPC && overflow_retry--) { - ars_status = acpi_desc->ars_status; - /* - * Record the original scrub range, so that we - * can recall all the ranges impacted by the - * initial scrub. - */ - if (!init_scrub_length) { - init_scrub_length = ars_status->length; - init_scrub_address = ars_status->address; - } - rc = ars_continue(acpi_desc); - if (rc == 0) { - mutex_unlock(&acpi_desc->init_mutex); - goto retry; - } - } +static unsigned int __acpi_nfit_scrub(struct acpi_nfit_desc *acpi_desc, + int query_rc) +{ + unsigned int tmo = acpi_desc->scrub_tmo; + struct device *dev = acpi_desc->dev; + struct nfit_spa *nfit_spa; - if (rc < 0) { - /* - * Initial scrub failed, we'll give it one more - * try below... - */ - break; - } + if (acpi_desc->cancel) + return 0; - /* We got some final results, record completed ranges */ - ars_status = acpi_desc->ars_status; - if (init_scrub_length) { - ars_start = init_scrub_address; - ars_len = ars_start + init_scrub_length; - } else { - ars_start = ars_status->address; - ars_len = ars_status->length; - } - spa = nfit_spa->spa; + if (query_rc == -EBUSY) { + dev_dbg(dev, "ARS: ARS busy\n"); + return min(30U * 60U, tmo * 2); + } + if (query_rc == -ENOSPC) { + dev_dbg(dev, "ARS: ARS continue\n"); + ars_continue(acpi_desc); + return 1; + } + if (query_rc && query_rc != -EAGAIN) { + unsigned long long addr, end; - if (!init_ars_done) { - init_ars_done = true; - dev_dbg(dev, "init scrub %#llx + %#llx complete\n", - ars_start, ars_len); - } - if (ars_start <= spa->address && ars_start + ars_len - >= spa->address + spa->length) - acpi_nfit_register_region(acpi_desc, nfit_spa); + addr = acpi_desc->ars_status->address; + end = addr + acpi_desc->ars_status->length; + dev_dbg(dev, "ARS: %llx-%llx failed (%d)\n", addr, end, + query_rc); } - /* - * For all the ranges not covered by an initial scrub we still - * want to see if there are errors, but it's ok to discover them - * asynchronously. - */ + ars_complete_all(acpi_desc); list_for_each_entry(nfit_spa, &acpi_desc->spas, list) { - /* - * Flag all the ranges that still need scrubbing, but - * register them now to make data available. - */ - if (!nfit_spa->nd_region) { - set_bit(ARS_REQ, &nfit_spa->ars_state); - acpi_nfit_register_region(acpi_desc, nfit_spa); + if (test_bit(ARS_FAILED, &nfit_spa->ars_state)) + continue; + if (test_bit(ARS_REQ, &nfit_spa->ars_state)) { + int rc = ars_start(acpi_desc, nfit_spa); + + clear_bit(ARS_DONE, &nfit_spa->ars_state); + dev = nd_region_dev(nfit_spa->nd_region); + dev_dbg(dev, "ARS: range %d ARS start (%d)\n", + nfit_spa->spa->range_index, rc); + if (rc == 0 || rc == -EBUSY) + return 1; + dev_err(dev, "ARS: range %d ARS failed (%d)\n", + nfit_spa->spa->range_index, rc); + set_bit(ARS_FAILED, &nfit_spa->ars_state); } } - acpi_desc->init_complete = 1; + return 0; +} - list_for_each_entry(nfit_spa, &acpi_desc->spas, list) - acpi_nfit_async_scrub(acpi_desc, nfit_spa); - acpi_desc->scrub_count++; - acpi_desc->ars_start_flags = 0; - if (acpi_desc->scrub_count_state) - sysfs_notify_dirent(acpi_desc->scrub_count_state); +static void acpi_nfit_scrub(struct work_struct *work) +{ + struct acpi_nfit_desc *acpi_desc; + unsigned int tmo; + int query_rc; + + acpi_desc = container_of(work, typeof(*acpi_desc), dwork.work); + mutex_lock(&acpi_desc->init_mutex); + query_rc = acpi_nfit_query_poison(acpi_desc); + tmo = __acpi_nfit_scrub(acpi_desc, query_rc); + if (tmo) { + queue_delayed_work(nfit_wq, &acpi_desc->dwork, tmo * HZ); + acpi_desc->scrub_tmo = tmo; + } else { + acpi_desc->scrub_count++; + if (acpi_desc->scrub_count_state) + sysfs_notify_dirent(acpi_desc->scrub_count_state); + } + memset(acpi_desc->ars_status, 0, acpi_desc->max_ars); mutex_unlock(&acpi_desc->init_mutex); } @@ -3015,33 +2955,61 @@ static void acpi_nfit_init_ars(struct acpi_nfit_desc *acpi_desc, nfit_spa->max_ars = ars_cap.max_ars_out; nfit_spa->clear_err_unit = ars_cap.clear_err_unit; acpi_desc->max_ars = max(nfit_spa->max_ars, acpi_desc->max_ars); + clear_bit(ARS_FAILED, &nfit_spa->ars_state); + set_bit(ARS_REQ, &nfit_spa->ars_state); } - static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc) { struct nfit_spa *nfit_spa; + int rc, query_rc; list_for_each_entry(nfit_spa, &acpi_desc->spas, list) { - int rc, type = nfit_spa_type(nfit_spa->spa); - - /* PMEM and VMEM will be registered by the ARS workqueue */ - if (type == NFIT_SPA_PM || type == NFIT_SPA_VOLATILE) { + set_bit(ARS_FAILED, &nfit_spa->ars_state); + switch (nfit_spa_type(nfit_spa->spa)) { + case NFIT_SPA_VOLATILE: + case NFIT_SPA_PM: acpi_nfit_init_ars(acpi_desc, nfit_spa); - continue; + break; } - /* BLK apertures belong to BLK region registration below */ - if (type == NFIT_SPA_BDW) - continue; - /* BLK regions don't need to wait for ARS results */ - rc = acpi_nfit_register_region(acpi_desc, nfit_spa); - if (rc) - return rc; } - acpi_desc->ars_start_flags = 0; - if (!acpi_desc->cancel) - queue_work(nfit_wq, &acpi_desc->work); + /* + * Reap any results that might be pending before starting new + * short requests. + */ + query_rc = acpi_nfit_query_poison(acpi_desc); + if (query_rc == 0) + ars_complete_all(acpi_desc); + + list_for_each_entry(nfit_spa, &acpi_desc->spas, list) + switch (nfit_spa_type(nfit_spa->spa)) { + case NFIT_SPA_VOLATILE: + case NFIT_SPA_PM: + /* register regions and kick off initial ARS run */ + rc = ars_register(acpi_desc, nfit_spa, &query_rc); + if (rc) + return rc; + break; + case NFIT_SPA_BDW: + /* nothing to register */ + break; + case NFIT_SPA_DCR: + case NFIT_SPA_VDISK: + case NFIT_SPA_VCD: + case NFIT_SPA_PDISK: + case NFIT_SPA_PCD: + /* register known regions that don't support ARS */ + rc = acpi_nfit_register_region(acpi_desc, nfit_spa); + if (rc) + return rc; + break; + default: + /* don't register unknown regions */ + break; + } + + queue_delayed_work(nfit_wq, &acpi_desc->dwork, 0); return 0; } @@ -3176,49 +3144,20 @@ int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *data, acpi_size sz) } EXPORT_SYMBOL_GPL(acpi_nfit_init); -struct acpi_nfit_flush_work { - struct work_struct work; - struct completion cmp; -}; - -static void flush_probe(struct work_struct *work) -{ - struct acpi_nfit_flush_work *flush; - - flush = container_of(work, typeof(*flush), work); - complete(&flush->cmp); -} - static int acpi_nfit_flush_probe(struct nvdimm_bus_descriptor *nd_desc) { struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc); struct device *dev = acpi_desc->dev; - struct acpi_nfit_flush_work flush; - int rc; - /* bounce the device lock to flush acpi_nfit_add / acpi_nfit_notify */ + /* Bounce the device lock to flush acpi_nfit_add / acpi_nfit_notify */ device_lock(dev); device_unlock(dev); - /* bounce the init_mutex to make init_complete valid */ + /* Bounce the init_mutex to complete initial registration */ mutex_lock(&acpi_desc->init_mutex); - if (acpi_desc->cancel || acpi_desc->init_complete) { - mutex_unlock(&acpi_desc->init_mutex); - return 0; - } - - /* - * Scrub work could take 10s of seconds, userspace may give up so we - * need to be interruptible while waiting. - */ - INIT_WORK_ONSTACK(&flush.work, flush_probe); - init_completion(&flush.cmp); - queue_work(nfit_wq, &flush.work); mutex_unlock(&acpi_desc->init_mutex); - rc = wait_for_completion_interruptible(&flush.cmp); - cancel_work_sync(&flush.work); - return rc; + return 0; } static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc, @@ -3237,7 +3176,7 @@ static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc, * just needs guarantees that any ars it initiates are not * interrupted by any intervening start reqeusts from userspace. */ - if (work_busy(&acpi_desc->work)) + if (work_busy(&acpi_desc->dwork.work)) return -EBUSY; return 0; @@ -3246,11 +3185,9 @@ static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc, int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, unsigned long flags) { struct device *dev = acpi_desc->dev; + int scheduled = 0, busy = 0; struct nfit_spa *nfit_spa; - if (work_busy(&acpi_desc->work)) - return -EBUSY; - mutex_lock(&acpi_desc->init_mutex); if (acpi_desc->cancel) { mutex_unlock(&acpi_desc->init_mutex); @@ -3258,21 +3195,32 @@ int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, unsigned long flags) } list_for_each_entry(nfit_spa, &acpi_desc->spas, list) { - struct acpi_nfit_system_address *spa = nfit_spa->spa; + int type = nfit_spa_type(nfit_spa->spa); - if (nfit_spa_type(spa) != NFIT_SPA_PM) + if (type != NFIT_SPA_PM && type != NFIT_SPA_VOLATILE) + continue; + if (test_bit(ARS_FAILED, &nfit_spa->ars_state)) continue; - set_bit(ARS_REQ, &nfit_spa->ars_state); + if (test_and_set_bit(ARS_REQ, &nfit_spa->ars_state)) + busy++; + else { + if (test_bit(ARS_SHORT, &flags)) + set_bit(ARS_SHORT, &nfit_spa->ars_state); + scheduled++; + } + } + if (scheduled) { + queue_delayed_work(nfit_wq, &acpi_desc->dwork, 0); + dev_dbg(dev, "ars_scan triggered\n"); } - acpi_desc->ars_start_flags = 0; - if (test_bit(ARS_SHORT, &flags)) - acpi_desc->ars_start_flags |= ND_ARS_RETURN_PREV_DATA; - queue_work(nfit_wq, &acpi_desc->work); - dev_dbg(dev, "ars_scan triggered\n"); mutex_unlock(&acpi_desc->init_mutex); - return 0; + if (scheduled) + return 0; + if (busy) + return -EBUSY; + return -ENOTTY; } void acpi_nfit_desc_init(struct acpi_nfit_desc *acpi_desc, struct device *dev) @@ -3299,7 +3247,8 @@ void acpi_nfit_desc_init(struct acpi_nfit_desc *acpi_desc, struct device *dev) INIT_LIST_HEAD(&acpi_desc->dimms); INIT_LIST_HEAD(&acpi_desc->list); mutex_init(&acpi_desc->init_mutex); - INIT_WORK(&acpi_desc->work, acpi_nfit_scrub); + acpi_desc->scrub_tmo = 1; + INIT_DELAYED_WORK(&acpi_desc->dwork, acpi_nfit_scrub); } EXPORT_SYMBOL_GPL(acpi_nfit_desc_init); @@ -3323,6 +3272,7 @@ void acpi_nfit_shutdown(void *data) mutex_lock(&acpi_desc->init_mutex); acpi_desc->cancel = 1; + cancel_delayed_work_sync(&acpi_desc->dwork); mutex_unlock(&acpi_desc->init_mutex); /* diff --git a/drivers/acpi/nfit/nfit.h b/drivers/acpi/nfit/nfit.h index 45e7949986a8..7d15856a739f 100644 --- a/drivers/acpi/nfit/nfit.h +++ b/drivers/acpi/nfit/nfit.h @@ -197,18 +197,18 @@ struct acpi_nfit_desc { struct device *dev; u8 ars_start_flags; struct nd_cmd_ars_status *ars_status; - struct work_struct work; + struct delayed_work dwork; struct list_head list; struct kernfs_node *scrub_count_state; unsigned int max_ars; unsigned int scrub_count; unsigned int scrub_mode; unsigned int cancel:1; - unsigned int init_complete:1; unsigned long dimm_cmd_force_en; unsigned long bus_cmd_force_en; unsigned long bus_nfit_cmd_force_en; unsigned int platform_cap; + unsigned int scrub_tmo; int (*blk_do_io)(struct nd_blk_region *ndbr, resource_size_t dpa, void *iobuf, u64 len, int rw); }; -- cgit From bca811a7fd5a5c1c0644926161c2843322ab74b3 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 2 Apr 2018 15:28:03 -0700 Subject: nfit, address-range-scrub: add module option to skip initial ars After attempting to quickly retrieve known errors the kernel proceeds to kick off a long running ARS. Add a module option to disable this behavior at initialization time, or at new region discovery time. Otherwise, ARS can be started manually regardless of the state of this setting. Co-developed-by: Dave Jiang Signed-off-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 2532294bbd68..7f3c70fde9a8 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -49,6 +49,10 @@ module_param(default_dsm_family, int, S_IRUGO); MODULE_PARM_DESC(default_dsm_family, "Try this DSM type first when identifying NVDIMM family"); +static bool no_init_ars; +module_param(no_init_ars, bool, 0644); +MODULE_PARM_DESC(no_init_ars, "Skip ARS run at nfit init time"); + LIST_HEAD(acpi_descs); DEFINE_MUTEX(acpi_desc_lock); @@ -2816,6 +2820,9 @@ static int ars_register(struct acpi_nfit_desc *acpi_desc, struct nfit_spa *nfit_ { int rc = *query_rc; + if (no_init_ars) + return acpi_nfit_register_region(acpi_desc, nfit_spa); + set_bit(ARS_REQ, &nfit_spa->ars_state); set_bit(ARS_SHORT, &nfit_spa->ars_state); -- cgit