Index: head/sys/dev/nvdimm/nvdimm.c =================================================================== --- head/sys/dev/nvdimm/nvdimm.c (revision 343142) +++ head/sys/dev/nvdimm/nvdimm.c (revision 343143) @@ -1,423 +1,364 @@ /*- * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. + * Copyright (c) 2018, 2019 Intel Corporation * * This software was developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_acpi.h" #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define _COMPONENT ACPI_OEM ACPI_MODULE_NAME("NVDIMM") static devclass_t nvdimm_devclass; -static device_t *nvdimm_devs; -static int nvdimm_devcnt; +static devclass_t nvdimm_root_devclass; MALLOC_DEFINE(M_NVDIMM, "nvdimm", "NVDIMM driver memory"); struct nvdimm_dev * nvdimm_find_by_handle(nfit_handle_t nv_handle) { - device_t dev; - struct nvdimm_dev *res, *nv; - int i; + struct nvdimm_dev *res; + device_t *dimms; + int i, error, num_dimms; res = NULL; - for (i = 0; i < nvdimm_devcnt; i++) { - dev = nvdimm_devs[i]; - if (dev == NULL) - continue; - nv = device_get_softc(dev); - if (nv->nv_handle == nv_handle) { - res = nv; + error = devclass_get_devices(nvdimm_devclass, &dimms, &num_dimms); + if (error != 0) + return (NULL); + for (i = 0; i < num_dimms; i++) { + if (nvdimm_root_get_device_handle(dimms[i]) == nv_handle) { + res = device_get_softc(dimms[i]); break; } } + free(dimms, M_TEMP); return (res); } static int nvdimm_parse_flush_addr(void *nfitsubtbl, void *arg) { ACPI_NFIT_FLUSH_ADDRESS *nfitflshaddr; struct nvdimm_dev *nv; int i; nfitflshaddr = nfitsubtbl; nv = arg; if (nfitflshaddr->DeviceHandle != nv->nv_handle) return (0); MPASS(nv->nv_flush_addr == NULL && nv->nv_flush_addr_cnt == 0); - nv->nv_flush_addr = malloc(nfitflshaddr->HintCount * sizeof(uint64_t *), - M_NVDIMM, M_WAITOK); + nv->nv_flush_addr = mallocarray(nfitflshaddr->HintCount, + sizeof(uint64_t *), M_NVDIMM, M_WAITOK); for (i = 0; i < nfitflshaddr->HintCount; i++) nv->nv_flush_addr[i] = (uint64_t *)nfitflshaddr->HintAddress[i]; nv->nv_flush_addr_cnt = nfitflshaddr->HintCount; return (0); } int nvdimm_iterate_nfit(ACPI_TABLE_NFIT *nfitbl, enum AcpiNfitType type, int (*cb)(void *, void *), void *arg) { ACPI_NFIT_HEADER *nfithdr; ACPI_NFIT_SYSTEM_ADDRESS *nfitaddr; ACPI_NFIT_MEMORY_MAP *nfitmap; ACPI_NFIT_INTERLEAVE *nfitintrl; ACPI_NFIT_SMBIOS *nfitsmbios; ACPI_NFIT_CONTROL_REGION *nfitctlreg; ACPI_NFIT_DATA_REGION *nfitdtreg; ACPI_NFIT_FLUSH_ADDRESS *nfitflshaddr; char *ptr; int error; error = 0; for (ptr = (char *)(nfitbl + 1); ptr < (char *)nfitbl + nfitbl->Header.Length; ptr += nfithdr->Length) { nfithdr = (ACPI_NFIT_HEADER *)ptr; if (nfithdr->Type != type) continue; switch (nfithdr->Type) { case ACPI_NFIT_TYPE_SYSTEM_ADDRESS: nfitaddr = __containerof(nfithdr, ACPI_NFIT_SYSTEM_ADDRESS, Header); error = cb(nfitaddr, arg); break; case ACPI_NFIT_TYPE_MEMORY_MAP: nfitmap = __containerof(nfithdr, ACPI_NFIT_MEMORY_MAP, Header); error = cb(nfitmap, arg); break; case ACPI_NFIT_TYPE_INTERLEAVE: nfitintrl = __containerof(nfithdr, ACPI_NFIT_INTERLEAVE, Header); error = cb(nfitintrl, arg); break; case ACPI_NFIT_TYPE_SMBIOS: nfitsmbios = __containerof(nfithdr, ACPI_NFIT_SMBIOS, Header); error = cb(nfitsmbios, arg); break; case ACPI_NFIT_TYPE_CONTROL_REGION: nfitctlreg = __containerof(nfithdr, ACPI_NFIT_CONTROL_REGION, Header); error = cb(nfitctlreg, arg); break; case ACPI_NFIT_TYPE_DATA_REGION: nfitdtreg = __containerof(nfithdr, ACPI_NFIT_DATA_REGION, Header); error = cb(nfitdtreg, arg); break; case ACPI_NFIT_TYPE_FLUSH_ADDRESS: nfitflshaddr = __containerof(nfithdr, ACPI_NFIT_FLUSH_ADDRESS, Header); error = cb(nfitflshaddr, arg); break; case ACPI_NFIT_TYPE_RESERVED: default: if (bootverbose) printf("NFIT subtype %d unknown\n", nfithdr->Type); error = 0; break; } if (error != 0) break; } return (error); } -static ACPI_STATUS -nvdimm_walk_dev(ACPI_HANDLE handle, UINT32 level, void *ctx, void **st) +static int +nvdimm_probe(device_t dev) { - ACPI_STATUS status; - struct nvdimm_ns_walk_ctx *wctx; - wctx = ctx; - status = wctx->func(handle, wctx->arg); - return_ACPI_STATUS(status); + return (BUS_PROBE_NOWILDCARD); } -static ACPI_STATUS -nvdimm_walk_root(ACPI_HANDLE handle, UINT32 level, void *ctx, void **st) +static int +nvdimm_attach(device_t dev) { + struct nvdimm_dev *nv; + ACPI_TABLE_NFIT *nfitbl; + ACPI_HANDLE handle; ACPI_STATUS status; - if (!acpi_MatchHid(handle, "ACPI0012")) - return_ACPI_STATUS(AE_OK); - status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 100, - nvdimm_walk_dev, NULL, ctx, NULL); - if (ACPI_FAILURE(status)) - return_ACPI_STATUS(status); - return_ACPI_STATUS(AE_CTRL_TERMINATE); + nv = device_get_softc(dev); + handle = nvdimm_root_get_acpi_handle(dev); + if (handle == NULL) + return (EINVAL); + nv->nv_dev = dev; + nv->nv_handle = nvdimm_root_get_device_handle(dev); + + status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl); + if (ACPI_FAILURE(status)) { + if (bootverbose) + device_printf(dev, "cannot get NFIT\n"); + return (ENXIO); + } + nvdimm_iterate_nfit(nfitbl, ACPI_NFIT_TYPE_FLUSH_ADDRESS, + nvdimm_parse_flush_addr, nv); + AcpiPutTable(&nfitbl->Header); + return (0); } -static ACPI_STATUS -nvdimm_foreach_acpi(ACPI_STATUS (*func)(ACPI_HANDLE, void *), void *arg) +static int +nvdimm_detach(device_t dev) { - struct nvdimm_ns_walk_ctx wctx; - ACPI_STATUS status; + struct nvdimm_dev *nv; - wctx.func = func; - wctx.arg = arg; - status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 100, - nvdimm_walk_root, NULL, &wctx, NULL); - return_ACPI_STATUS(status); + nv = device_get_softc(dev); + free(nv->nv_flush_addr, M_NVDIMM); + return (0); } -static ACPI_STATUS -nvdimm_count_devs(ACPI_HANDLE handle __unused, void *arg) +static int +nvdimm_suspend(device_t dev) { - int *cnt; - cnt = arg; - (*cnt)++; + return (0); +} - ACPI_BUFFER name; - ACPI_STATUS status; - if (bootverbose) { - name.Length = ACPI_ALLOCATE_BUFFER; - status = AcpiGetName(handle, ACPI_FULL_PATHNAME, &name); - if (ACPI_FAILURE(status)) - return_ACPI_STATUS(status); - printf("nvdimm: enumerated %s\n", (char *)name.Pointer); - AcpiOsFree(name.Pointer); - } +static int +nvdimm_resume(device_t dev) +{ - return_ACPI_STATUS(AE_OK); + return (0); } -struct nvdimm_create_dev_arg { - device_t acpi0; - int *cnt; -}; - static ACPI_STATUS -nvdimm_create_dev(ACPI_HANDLE handle, void *arg) +nvdimm_root_create_dev(ACPI_HANDLE handle, UINT32 nesting_level, void *context, + void **return_value) { - struct nvdimm_create_dev_arg *narg; - device_t child; - int idx; + ACPI_STATUS status; + ACPI_DEVICE_INFO *device_info; + device_t parent, child; + uintptr_t *ivars; - narg = arg; - idx = *(narg->cnt); - child = device_find_child(narg->acpi0, "nvdimm", idx); - if (child == NULL) - child = BUS_ADD_CHILD(narg->acpi0, 1, "nvdimm", idx); + parent = context; + child = BUS_ADD_CHILD(parent, 100, "nvdimm", -1); if (child == NULL) { - if (bootverbose) - device_printf(narg->acpi0, - "failed to create nvdimm%d\n", idx); + device_printf(parent, "failed to create nvdimm\n"); return_ACPI_STATUS(AE_ERROR); } - acpi_set_handle(child, handle); - KASSERT(nvdimm_devs[idx] == NULL, ("nvdimm_devs[%d] not NULL", idx)); - nvdimm_devs[idx] = child; - - (*(narg->cnt))++; - return_ACPI_STATUS(AE_OK); -} - -static bool -nvdimm_init(void) -{ - ACPI_STATUS status; - - if (nvdimm_devcnt != 0) - return (true); - if (acpi_disabled("nvdimm")) - return (false); - status = nvdimm_foreach_acpi(nvdimm_count_devs, &nvdimm_devcnt); + status = AcpiGetObjectInfo(handle, &device_info); if (ACPI_FAILURE(status)) { - if (bootverbose) - printf("nvdimm_init: count failed\n"); - return (false); + device_printf(parent, "failed to get nvdimm device info\n"); + return_ACPI_STATUS(AE_ERROR); } - nvdimm_devs = malloc(nvdimm_devcnt * sizeof(device_t), M_NVDIMM, - M_WAITOK | M_ZERO); - return (true); + ivars = mallocarray(NVDIMM_ROOT_IVAR_MAX - 1, sizeof(uintptr_t), + M_NVDIMM, M_ZERO | M_WAITOK); + device_set_ivars(child, ivars); + nvdimm_root_set_acpi_handle(child, handle); + nvdimm_root_set_device_handle(child, device_info->Address); + return_ACPI_STATUS(AE_OK); } -static void -nvdimm_identify(driver_t *driver, device_t parent) -{ - struct nvdimm_create_dev_arg narg; - ACPI_STATUS status; - int i; +static char *nvdimm_root_id[] = {"ACPI0012", NULL}; - if (!nvdimm_init()) - return; - narg.acpi0 = parent; - narg.cnt = &i; - i = 0; - status = nvdimm_foreach_acpi(nvdimm_create_dev, &narg); - if (ACPI_FAILURE(status) && bootverbose) - printf("nvdimm_identify: create failed\n"); -} - static int -nvdimm_probe(device_t dev) +nvdimm_root_probe(device_t dev) { + int rv; - return (BUS_PROBE_NOWILDCARD); + if (acpi_disabled("nvdimm")) + return (ENXIO); + rv = ACPI_ID_PROBE(device_get_parent(dev), dev, nvdimm_root_id, NULL); + if (rv <= 0) + device_set_desc(dev, "ACPI NVDIMM root device"); + + return (rv); } static int -nvdimm_attach(device_t dev) +nvdimm_root_attach(device_t dev) { - struct nvdimm_dev *nv; - ACPI_TABLE_NFIT *nfitbl; ACPI_HANDLE handle; ACPI_STATUS status; - int i; + int error; - nv = device_get_softc(dev); handle = acpi_get_handle(dev); - if (handle == NULL) - return (EINVAL); - nv->nv_dev = dev; - for (i = 0; i < nvdimm_devcnt; i++) { - if (nvdimm_devs[i] == dev) { - nv->nv_devs_idx = i; - break; - } - } - MPASS(i < nvdimm_devcnt); - if (ACPI_FAILURE(acpi_GetInteger(handle, "_ADR", &nv->nv_handle))) { - device_printf(dev, "cannot get handle\n"); - return (ENXIO); - } - - status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl); - if (ACPI_FAILURE(status)) { - if (bootverbose) - device_printf(dev, "cannot get NFIT\n"); - return (ENXIO); - } - nvdimm_iterate_nfit(nfitbl, ACPI_NFIT_TYPE_FLUSH_ADDRESS, - nvdimm_parse_flush_addr, nv); - AcpiPutTable(&nfitbl->Header); - return (0); + status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, handle, 1, + nvdimm_root_create_dev, NULL, dev, NULL); + if (ACPI_FAILURE(status)) + device_printf(dev, "failed adding children\n"); + error = bus_generic_attach(dev); + return (error); } static int -nvdimm_detach(device_t dev) +nvdimm_root_detach(device_t dev) { - struct nvdimm_dev *nv; + device_t *children; + int i, error, num_children; - nv = device_get_softc(dev); - nvdimm_devs[nv->nv_devs_idx] = NULL; - free(nv->nv_flush_addr, M_NVDIMM); - return (0); + error = bus_generic_detach(dev); + if (error != 0) + return (error); + error = device_get_children(dev, &children, &num_children); + if (error != 0) + return (error); + for (i = 0; i < num_children; i++) + free(device_get_ivars(children[i]), M_NVDIMM); + free(children, M_TEMP); + error = device_delete_children(dev); + return (error); } static int -nvdimm_suspend(device_t dev) +nvdimm_root_read_ivar(device_t dev, device_t child, int index, + uintptr_t *result) { + if (index < 0 || index >= NVDIMM_ROOT_IVAR_MAX) + return (ENOENT); + *result = ((uintptr_t *)device_get_ivars(child))[index]; return (0); } static int -nvdimm_resume(device_t dev) +nvdimm_root_write_ivar(device_t dev, device_t child, int index, + uintptr_t value) { + if (index < 0 || index >= NVDIMM_ROOT_IVAR_MAX) + return (ENOENT); + ((uintptr_t *)device_get_ivars(child))[index] = value; return (0); } static device_method_t nvdimm_methods[] = { - DEVMETHOD(device_identify, nvdimm_identify), DEVMETHOD(device_probe, nvdimm_probe), DEVMETHOD(device_attach, nvdimm_attach), DEVMETHOD(device_detach, nvdimm_detach), DEVMETHOD(device_suspend, nvdimm_suspend), DEVMETHOD(device_resume, nvdimm_resume), DEVMETHOD_END }; static driver_t nvdimm_driver = { "nvdimm", nvdimm_methods, sizeof(struct nvdimm_dev), }; -static void -nvdimm_fini(void) -{ +static device_method_t nvdimm_root_methods[] = { + DEVMETHOD(device_probe, nvdimm_root_probe), + DEVMETHOD(device_attach, nvdimm_root_attach), + DEVMETHOD(device_detach, nvdimm_root_detach), + DEVMETHOD(bus_add_child, bus_generic_add_child), + DEVMETHOD(bus_read_ivar, nvdimm_root_read_ivar), + DEVMETHOD(bus_write_ivar, nvdimm_root_write_ivar), + DEVMETHOD_END +}; - free(nvdimm_devs, M_NVDIMM); - nvdimm_devs = NULL; - nvdimm_devcnt = 0; -} +static driver_t nvdimm_root_driver = { + "nvdimm_root", + nvdimm_root_methods, +}; -static int -nvdimm_modev(struct module *mod, int what, void *arg) -{ - int error; - - switch (what) { - case MOD_LOAD: - error = 0; - break; - - case MOD_UNLOAD: - nvdimm_fini(); - error = 0; - break; - - case MOD_QUIESCE: - error = 0; - break; - - default: - error = EOPNOTSUPP; - break; - } - - return (error); -} - -DRIVER_MODULE(nvdimm, acpi, nvdimm_driver, nvdimm_devclass, nvdimm_modev, NULL); +DRIVER_MODULE(nvdimm_root, acpi, nvdimm_root_driver, nvdimm_root_devclass, NULL, + NULL); +DRIVER_MODULE(nvdimm, nvdimm_root, nvdimm_driver, nvdimm_devclass, NULL, NULL); MODULE_DEPEND(nvdimm, acpi, 1, 1, 1); Index: head/sys/dev/nvdimm/nvdimm_var.h =================================================================== --- head/sys/dev/nvdimm/nvdimm_var.h (revision 343142) +++ head/sys/dev/nvdimm/nvdimm_var.h (revision 343143) @@ -1,91 +1,95 @@ /*- * Copyright (c) 2017 The FreeBSD Foundation * All rights reserved. + * Copyright (c) 2018, 2019 Intel Corporation * * This software was developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef __DEV_NVDIMM_VAR_H__ #define __DEV_NVDIMM_VAR_H__ typedef uint32_t nfit_handle_t; +enum nvdimm_root_ivar { + NVDIMM_ROOT_IVAR_ACPI_HANDLE, + NVDIMM_ROOT_IVAR_DEVICE_HANDLE, + NVDIMM_ROOT_IVAR_MAX, +}; +__BUS_ACCESSOR(nvdimm_root, acpi_handle, NVDIMM_ROOT, ACPI_HANDLE, ACPI_HANDLE) +__BUS_ACCESSOR(nvdimm_root, device_handle, NVDIMM_ROOT, DEVICE_HANDLE, + nfit_handle_t) + struct nvdimm_dev { device_t nv_dev; nfit_handle_t nv_handle; uint64_t **nv_flush_addr; int nv_flush_addr_cnt; - int nv_devs_idx; }; enum SPA_mapping_type { SPA_TYPE_VOLATILE_MEMORY = 0, SPA_TYPE_PERSISTENT_MEMORY = 1, SPA_TYPE_CONTROL_REGION = 2, SPA_TYPE_DATA_REGION = 3, SPA_TYPE_VOLATILE_VIRTUAL_DISK = 4, SPA_TYPE_VOLATILE_VIRTUAL_CD = 5, SPA_TYPE_PERSISTENT_VIRTUAL_DISK= 6, SPA_TYPE_PERSISTENT_VIRTUAL_CD = 7, }; struct SPA_mapping { enum SPA_mapping_type spa_type; int spa_domain; int spa_nfit_idx; uint64_t spa_phys_base; uint64_t spa_len; uint64_t spa_efi_mem_flags; void *spa_kva; struct cdev *spa_dev; struct g_geom *spa_g; struct g_provider *spa_p; struct bio_queue_head spa_g_queue; struct mtx spa_g_mtx; struct mtx spa_g_stat_mtx; struct devstat *spa_g_devstat; struct proc *spa_g_proc; struct vm_object *spa_obj; bool spa_g_proc_run; bool spa_g_proc_exiting; -}; - -struct nvdimm_ns_walk_ctx { - ACPI_STATUS (*func)(ACPI_HANDLE, void *); - void *arg; }; extern struct SPA_mapping *spa_mappings; extern int spa_mappings_cnt; MALLOC_DECLARE(M_NVDIMM); struct nvdimm_dev *nvdimm_find_by_handle(nfit_handle_t nv_handle); int nvdimm_iterate_nfit(ACPI_TABLE_NFIT *nfitbl, enum AcpiNfitType type, int (*cb)(void *, void *), void *arg); #endif /* __DEV_NVDIMM_VAR_H__ */