Index: sys/dev/nvdimm/nvdimm.c =================================================================== --- sys/dev/nvdimm/nvdimm.c +++ sys/dev/nvdimm/nvdimm.c @@ -43,6 +43,8 @@ #include #include #include +#include +#include #include #include @@ -332,12 +334,21 @@ nvdimm_attach(device_t dev) { struct nvdimm_dev *nv; + struct sysctl_ctx_list *ctx; + struct sysctl_oid *oid; + struct sysctl_oid_list *children; + struct sbuf *sb; ACPI_TABLE_NFIT *nfitbl; ACPI_HANDLE handle; ACPI_STATUS status; - int error; + ACPI_NFIT_MEMORY_MAP **maps; + int error, i, num_maps; + uint16_t flags; nv = device_get_softc(dev); + ctx = device_get_sysctl_ctx(dev); + oid = device_get_sysctl_tree(dev); + children = SYSCTL_CHILDREN(oid); handle = nvdimm_root_get_acpi_handle(dev); MPASS(handle != NULL); nv->nv_dev = dev; @@ -351,6 +362,57 @@ } acpi_nfit_get_flush_addrs(nfitbl, nv->nv_handle, &nv->nv_flush_addr, &nv->nv_flush_addr_cnt); + + /* + * Each NVDIMM should have at least one memory map associated with it. + * If any of the maps have one of the error flags set, reflect that in + * the overall status. + */ + acpi_nfit_get_memory_maps_by_dimm(nfitbl, nv->nv_handle, &maps, + &num_maps); + if (num_maps == 0) { + free(nv->nv_flush_addr, M_NVDIMM); + free(maps, M_NVDIMM); + device_printf(dev, "cannot find memory map\n"); + return (ENXIO); + } + flags = 0; + for (i = 0; i < num_maps; i++) { + flags |= maps[i]->Flags; + } + free(maps, M_NVDIMM); + + /* sbuf_new_auto(9) is M_WAITOK; no need to check for NULL. */ + sb = sbuf_new_auto(); + (void) sbuf_printf(sb, "0x%b", flags, + "\20" + "\001SAVE_FAILED" + "\002RESTORE_FAILED" + "\003FLUSH_FAILED" + "\004NOT_ARMED" + "\005HEALTH_OBSERVED" + "\006HEALTH_ENABLED" + "\007MAP_FAILED"); + error = sbuf_finish(sb); + if (error != 0) { + sbuf_delete(sb); + free(nv->nv_flush_addr, M_NVDIMM); + device_printf(dev, "cannot convert flags to string\n"); + return (error); + } + /* strdup(9) is M_WAITOK; no need to check for NULL. */ + nv->nv_flags_str = strdup(sbuf_data(sb), M_NVDIMM); + sbuf_delete(sb); + SYSCTL_ADD_STRING(ctx, children, OID_AUTO, "flags", + CTLFLAG_RD | CTLFLAG_MPSAFE, nv->nv_flags_str, 0, + "NVDIMM State Flags"); + /* + * Anything other than HEALTH_ENABLED indicates a fault condition of + * some kind, so log if that's seen. + */ + if ((flags & ~ACPI_NFIT_MEM_HEALTH_ENABLED) != 0) + device_printf(dev, "flags: %s\n", nv->nv_flags_str); + AcpiPutTable(&nfitbl->Header); error = read_label_area_size(nv); if (error == 0) { @@ -370,6 +432,7 @@ struct nvdimm_label_entry *label, *next; nv = device_get_softc(dev); + free(nv->nv_flags_str, M_NVDIMM); free(nv->nv_flush_addr, M_NVDIMM); free(nv->label_index, M_NVDIMM); SLIST_FOREACH_SAFE(label, &nv->labels, link, next) { Index: sys/dev/nvdimm/nvdimm_nfit.c =================================================================== --- sys/dev/nvdimm/nvdimm_nfit.c +++ sys/dev/nvdimm/nvdimm_nfit.c @@ -201,3 +201,13 @@ for (i = 0; i < subtable->HintCount; i++) (*listp)[i] = (uint64_t *)(intptr_t)subtable->HintAddress[i]; } + +void +acpi_nfit_get_memory_maps_by_dimm(ACPI_TABLE_NFIT *nfitbl, nfit_handle_t dimm, + ACPI_NFIT_MEMORY_MAP ***listp, int *countp) +{ + + malloc_find_matches(nfitbl, ACPI_NFIT_TYPE_MEMORY_MAP, + offsetof(ACPI_NFIT_MEMORY_MAP, DeviceHandle), UINT32_MAX, dimm, + (void ***)listp, countp); +} Index: sys/dev/nvdimm/nvdimm_var.h =================================================================== --- sys/dev/nvdimm/nvdimm_var.h +++ sys/dev/nvdimm/nvdimm_var.h @@ -93,6 +93,7 @@ device_t nv_dev; nfit_handle_t nv_handle; uint64_t **nv_flush_addr; + char *nv_flags_str; int nv_flush_addr_cnt; uint32_t label_area_size; uint32_t max_label_xfer; @@ -163,6 +164,8 @@ uint16_t control_region_index, ACPI_NFIT_CONTROL_REGION **out); void acpi_nfit_get_flush_addrs(ACPI_TABLE_NFIT *nfitbl, nfit_handle_t dimm, uint64_t ***listp, int *countp); +void acpi_nfit_get_memory_maps_by_dimm(ACPI_TABLE_NFIT *nfitbl, + nfit_handle_t dimm, ACPI_NFIT_MEMORY_MAP ***listp, int *countp); enum SPA_mapping_type nvdimm_spa_type_from_name(const char *); enum SPA_mapping_type nvdimm_spa_type_from_uuid(struct uuid *); bool nvdimm_spa_type_user_accessible(enum SPA_mapping_type);