From 8cc6ddfcafbb7e32ff025f7d9551ecf9649c12cd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 5 Apr 2016 15:26:50 -0700 Subject: libnvdimm, nfit: report multiple interface codes per-dimm Starting with ACPI 6.1 an NFIT table will report multiple 'NVDIMM Control Region Structure' instances per-dimm, one for each supported format interface. Report that code in the following format in sysfs: nmemX/nfit/formats nmemX/nfit/format nmemX/nfit/format1 nmemX/nfit/format2 ... nmemX/nfit/formatN Where format2 - formatN are theoretical as there are no known DIMMs with support for more than two interface formats. This layout is compatible with existing libndctl binaries that only expect one code per-dimm as they will ignore nmemX/nfit/formats and nmemX/nfit/formatN. Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++--- drivers/acpi/nfit.h | 1 + 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index d0f35e63640b..db0f806b8388 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -655,6 +655,7 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, if (!nfit_mem) return -ENOMEM; INIT_LIST_HEAD(&nfit_mem->list); + nfit_mem->acpi_desc = acpi_desc; list_add(&nfit_mem->list, &acpi_desc->dimms); } @@ -838,6 +839,18 @@ static ssize_t device_show(struct device *dev, } static DEVICE_ATTR_RO(device); +static int num_nvdimm_formats(struct nvdimm *nvdimm) +{ + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + int formats = 0; + + if (nfit_mem->memdev_pmem) + formats++; + if (nfit_mem->memdev_bdw) + formats++; + return formats; +} + static ssize_t format_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -847,6 +860,55 @@ static ssize_t format_show(struct device *dev, } static DEVICE_ATTR_RO(format); +static ssize_t format1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 handle; + ssize_t rc = -ENXIO; + struct nfit_mem *nfit_mem; + struct nfit_memdev *nfit_memdev; + struct acpi_nfit_desc *acpi_desc; + struct nvdimm *nvdimm = to_nvdimm(dev); + struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev); + + nfit_mem = nvdimm_provider_data(nvdimm); + acpi_desc = nfit_mem->acpi_desc; + handle = to_nfit_memdev(dev)->device_handle; + + /* assumes DIMMs have at most 2 published interface codes */ + mutex_lock(&acpi_desc->init_mutex); + list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { + struct acpi_nfit_memory_map *memdev = nfit_memdev->memdev; + struct nfit_dcr *nfit_dcr; + + if (memdev->device_handle != handle) + continue; + + list_for_each_entry(nfit_dcr, &acpi_desc->dcrs, list) { + if (nfit_dcr->dcr->region_index != memdev->region_index) + continue; + if (nfit_dcr->dcr->code == dcr->code) + continue; + rc = sprintf(buf, "%#x\n", nfit_dcr->dcr->code); + break; + } + if (rc != ENXIO) + break; + } + mutex_unlock(&acpi_desc->init_mutex); + return rc; +} +static DEVICE_ATTR_RO(format1); + +static ssize_t formats_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvdimm *nvdimm = to_nvdimm(dev); + + return sprintf(buf, "%d\n", num_nvdimm_formats(nvdimm)); +} +static DEVICE_ATTR_RO(formats); + static ssize_t serial_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -876,6 +938,8 @@ static struct attribute *acpi_nfit_dimm_attributes[] = { &dev_attr_vendor.attr, &dev_attr_device.attr, &dev_attr_format.attr, + &dev_attr_formats.attr, + &dev_attr_format1.attr, &dev_attr_serial.attr, &dev_attr_rev_id.attr, &dev_attr_flags.attr, @@ -886,11 +950,13 @@ static umode_t acpi_nfit_dimm_attr_visible(struct kobject *kobj, struct attribute *a, int n) { struct device *dev = container_of(kobj, struct device, kobj); + struct nvdimm *nvdimm = to_nvdimm(dev); - if (to_nfit_dcr(dev)) - return a->mode; - else + if (!to_nfit_dcr(dev)) + return 0; + if (a == &dev_attr_format1.attr && num_nvdimm_formats(nvdimm) <= 1) return 0; + return a->mode; } static struct attribute_group acpi_nfit_dimm_attribute_group = { diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h index c75576b2d50e..5201840c1147 100644 --- a/drivers/acpi/nfit.h +++ b/drivers/acpi/nfit.h @@ -109,6 +109,7 @@ struct nfit_mem { struct nfit_flush *nfit_flush; struct list_head list; struct acpi_device *adev; + struct acpi_nfit_desc *acpi_desc; unsigned long dsm_mask; }; -- cgit v1.2.3-55-g7522 From baa51277cf5dc844089ea2f6e0f78b1c5ca665d8 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 5 Apr 2016 17:40:52 -0700 Subject: libnvdimm, test: add mock SMART data payload Provide simulated SMART data to enable the ndctl implementation of SMART data retrieval and parsing. The payload is defined here, "Section 4.1 SMART and Health Info (Function Index 1)": http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf Signed-off-by: Dan Williams --- drivers/nvdimm/bus.c | 3 +++ include/uapi/linux/ndctl.h | 36 +++++++++++++++++++++++++++++++- tools/testing/nvdimm/test/nfit.c | 44 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index 19f822d7f652..8111b1299515 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -783,6 +783,9 @@ int __init nvdimm_bus_init(void) { int rc; + BUILD_BUG_ON(sizeof(struct nd_smart_payload) != 128); + BUILD_BUG_ON(sizeof(struct nd_smart_threshold_payload) != 8); + rc = bus_register(&nvdimm_bus_type); if (rc) return rc; diff --git a/include/uapi/linux/ndctl.h b/include/uapi/linux/ndctl.h index 7cc28ab05b87..59c61e018a86 100644 --- a/include/uapi/linux/ndctl.h +++ b/include/uapi/linux/ndctl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, Intel Corporation. + * Copyright (c) 2014-2016, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, @@ -20,11 +20,45 @@ struct nd_cmd_smart { __u8 data[128]; } __packed; +#define ND_SMART_HEALTH_VALID (1 << 0) +#define ND_SMART_TEMP_VALID (1 << 1) +#define ND_SMART_SPARES_VALID (1 << 2) +#define ND_SMART_ALARM_VALID (1 << 3) +#define ND_SMART_USED_VALID (1 << 4) +#define ND_SMART_SHUTDOWN_VALID (1 << 5) +#define ND_SMART_VENDOR_VALID (1 << 6) +#define ND_SMART_TEMP_TRIP (1 << 0) +#define ND_SMART_SPARE_TRIP (1 << 1) +#define ND_SMART_NON_CRITICAL_HEALTH (1 << 0) +#define ND_SMART_CRITICAL_HEALTH (1 << 1) +#define ND_SMART_FATAL_HEALTH (1 << 2) + +struct nd_smart_payload { + __u32 flags; + __u8 reserved0[4]; + __u8 health; + __u16 temperature; + __u8 spares; + __u8 alarm_flags; + __u8 life_used; + __u8 shutdown_state; + __u8 reserved1; + __u32 vendor_size; + __u8 vendor_data[108]; +} __packed; + struct nd_cmd_smart_threshold { __u32 status; __u8 data[8]; } __packed; +struct nd_smart_threshold_payload { + __u16 alarm_control; + __u16 temperature; + __u8 spares; + __u8 reserved[3]; +} __packed; + struct nd_cmd_dimm_flags { __u32 status; __u32 flags; diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index 3187322eeed7..d1c98d4386d4 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -330,6 +330,42 @@ static int nfit_test_cmd_clear_error(struct nd_cmd_clear_error *clear_err, return 0; } +static int nfit_test_cmd_smart(struct nd_cmd_smart *smart, unsigned int buf_len) +{ + static const struct nd_smart_payload smart_data = { + .flags = ND_SMART_HEALTH_VALID | ND_SMART_TEMP_VALID + | ND_SMART_SPARES_VALID | ND_SMART_ALARM_VALID + | ND_SMART_USED_VALID | ND_SMART_SHUTDOWN_VALID, + .health = ND_SMART_NON_CRITICAL_HEALTH, + .temperature = 23 * 16, + .spares = 75, + .alarm_flags = ND_SMART_SPARE_TRIP | ND_SMART_TEMP_TRIP, + .life_used = 5, + .shutdown_state = 0, + .vendor_size = 0, + }; + + if (buf_len < sizeof(*smart)) + return -EINVAL; + memcpy(smart->data, &smart_data, sizeof(smart_data)); + return 0; +} + +static int nfit_test_cmd_smart_threshold(struct nd_cmd_smart_threshold *smart_t, + unsigned int buf_len) +{ + static const struct nd_smart_threshold_payload smart_t_data = { + .alarm_control = ND_SMART_SPARE_TRIP | ND_SMART_TEMP_TRIP, + .temperature = 40 * 16, + .spares = 5, + }; + + if (buf_len < sizeof(*smart_t)) + return -EINVAL; + memcpy(smart_t->data, &smart_t_data, sizeof(smart_t_data)); + 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) @@ -368,6 +404,12 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, rc = nfit_test_cmd_set_config_data(buf, buf_len, t->label[i]); break; + case ND_CMD_SMART: + rc = nfit_test_cmd_smart(buf, buf_len); + break; + case ND_CMD_SMART_THRESHOLD: + rc = nfit_test_cmd_smart_threshold(buf, buf_len); + break; default: return -ENOTTY; } @@ -1254,10 +1296,12 @@ static void nfit_test0_setup(struct nfit_test *t) 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); set_bit(ND_CMD_SET_CONFIG_DATA, &acpi_desc->dimm_dsm_force_en); + set_bit(ND_CMD_SMART, &acpi_desc->dimm_dsm_force_en); 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); + set_bit(ND_CMD_SMART_THRESHOLD, &acpi_desc->dimm_dsm_force_en); } static void nfit_test1_setup(struct nfit_test *t) -- cgit v1.2.3-55-g7522 From 8259542348d93da6a04eed979047b1fd1ca72abe Mon Sep 17 00:00:00 2001 From: Lee, Chun-Yi Date: Thu, 21 Jan 2016 20:32:10 +0800 Subject: libnvdimm, nfit: Use ACPI_SIG_NFIT instead of hard coded string It's minor but that's still better to use ACPI_SIG_NFIT instead of hard coded string. Cc: Ross Zwisler Cc: "Rafael J. Wysocki" Cc: Len Brown Signed-off-by: Lee, Chun-Yi Acked-by: Ross Zwisler Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index db0f806b8388..a434b58c5e11 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -2372,7 +2372,7 @@ static int acpi_nfit_add(struct acpi_device *adev) acpi_size sz; int rc; - status = acpi_get_table_with_size("NFIT", 0, &tbl, &sz); + status = acpi_get_table_with_size(ACPI_SIG_NFIT, 0, &tbl, &sz); if (ACPI_FAILURE(status)) { /* This is ok, we could have an nvdimm hotplugged later */ dev_dbg(dev, "failed to find NFIT at startup\n"); -- cgit v1.2.3-55-g7522 From 9dec4892ca9afd6aad3c9c9e6c17480ecbd04440 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 22 Apr 2016 12:26:05 -0700 Subject: libnvdimm, btt: add btt startup debug Report the reason for btt probe failures when debug is enabled. Signed-off-by: Dan Williams --- drivers/nvdimm/btt.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c index f068b6513cd2..af09d6c26709 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -1388,11 +1388,15 @@ int nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns) struct btt *btt; size_t rawsize; - if (!nd_btt->uuid || !nd_btt->ndns || !nd_btt->lbasize) + if (!nd_btt->uuid || !nd_btt->ndns || !nd_btt->lbasize) { + dev_dbg(&nd_btt->dev, "incomplete btt configuration\n"); return -ENODEV; + } rawsize = nvdimm_namespace_capacity(ndns) - SZ_4K; if (rawsize < ARENA_MIN_SIZE) { + dev_dbg(&nd_btt->dev, "%s must be at least %ld bytes\n", + dev_name(&ndns->dev), ARENA_MIN_SIZE + SZ_4K); return -ENXIO; } nd_region = to_nd_region(nd_btt->dev.parent); -- cgit v1.2.3-55-g7522