Index: sys/dev/nvdimm/nvdimm_spa.c =================================================================== --- sys/dev/nvdimm/nvdimm_spa.c +++ sys/dev/nvdimm/nvdimm_spa.c @@ -64,19 +64,6 @@ #include #include -struct SPA_mapping *spa_mappings; -int spa_mappings_cnt; - -static int -nvdimm_spa_count(void *nfitsubtbl __unused, void *arg) -{ - int *cnt; - - cnt = arg; - (*cnt)++; - return (0); -} - static struct nvdimm_SPA_uuid_list_elm { const char *u_name; const char *u_id_str; @@ -276,30 +263,30 @@ static void nvdimm_spa_g_thread(void *arg) { - struct SPA_mapping *spa; + struct g_spa_softc *sc; struct bio *bp; struct uio auio; struct iovec aiovec; int error; - spa = arg; + sc = arg; for (;;) { - mtx_lock(&spa->spa_g_mtx); + mtx_lock(&sc->spa_g_mtx); for (;;) { - bp = bioq_takefirst(&spa->spa_g_queue); + bp = bioq_takefirst(&sc->spa_g_queue); if (bp != NULL) break; - msleep(&spa->spa_g_queue, &spa->spa_g_mtx, PRIBIO, + msleep(&sc->spa_g_queue, &sc->spa_g_mtx, PRIBIO, "spa_g", 0); - if (!spa->spa_g_proc_run) { - spa->spa_g_proc_exiting = true; - wakeup(&spa->spa_g_queue); - mtx_unlock(&spa->spa_g_mtx); + if (!sc->spa_g_proc_run) { + sc->spa_g_proc_exiting = true; + wakeup(&sc->spa_g_queue); + mtx_unlock(&sc->spa_g_mtx); kproc_exit(0); } continue; } - mtx_unlock(&spa->spa_g_mtx); + mtx_unlock(&sc->spa_g_mtx); if (bp->bio_cmd != BIO_READ && bp->bio_cmd != BIO_WRITE && bp->bio_cmd != BIO_FLUSH) { error = EOPNOTSUPP; @@ -308,13 +295,15 @@ error = 0; if (bp->bio_cmd == BIO_FLUSH) { - if (spa->spa_kva != NULL) { - pmap_large_map_wb(spa->spa_kva, spa->spa_len); + if (sc->spa->spa_kva != NULL) { + pmap_large_map_wb(sc->spa->spa_kva, + sc->spa->spa_len); } else { pmap_flush_cache_phys_range( - (vm_paddr_t)spa->spa_phys_base, - (vm_paddr_t)spa->spa_phys_base + - spa->spa_len, nvdimm_spa_memattr(spa)); + (vm_paddr_t)sc->spa->spa_phys_base, + (vm_paddr_t)sc->spa->spa_phys_base + + sc->spa->spa_len, + nvdimm_spa_memattr(sc->spa)); } /* * XXX flush IMC @@ -323,8 +312,8 @@ } if ((bp->bio_flags & BIO_UNMAPPED) != 0) { - if (spa->spa_kva != NULL) { - aiovec.iov_base = (char *)spa->spa_kva + + if (sc->spa->spa_kva != NULL) { + aiovec.iov_base = (char *)sc->spa->spa_kva + bp->bio_offset; aiovec.iov_len = bp->bio_length; auio.uio_iov = &aiovec; @@ -338,7 +327,8 @@ error = uiomove_fromphys(bp->bio_ma, bp->bio_ma_offset, bp->bio_length, &auio); } else { - nvdimm_spa_g_all_unmapped(spa, bp, bp->bio_cmd); + nvdimm_spa_g_all_unmapped(sc->spa, bp, + bp->bio_cmd); error = 0; } } else { @@ -352,9 +342,9 @@ auio.uio_rw = bp->bio_cmd == BIO_READ ? UIO_READ : UIO_WRITE; auio.uio_td = curthread; - error = nvdimm_spa_uio(spa, &auio); + error = nvdimm_spa_uio(sc->spa, &auio); } - devstat_end_transaction_bio(spa->spa_g_devstat, bp); + devstat_end_transaction_bio(sc->spa_g_devstat, bp); completed: bp->bio_completed = bp->bio_length; g_io_deliver(bp, error); @@ -364,18 +354,18 @@ static void nvdimm_spa_g_start(struct bio *bp) { - struct SPA_mapping *spa; + struct g_spa_softc *sc; - spa = bp->bio_to->geom->softc; + sc = bp->bio_to->geom->softc; if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) { - mtx_lock(&spa->spa_g_stat_mtx); - devstat_start_transaction_bio(spa->spa_g_devstat, bp); - mtx_unlock(&spa->spa_g_stat_mtx); + mtx_lock(&sc->spa_g_stat_mtx); + devstat_start_transaction_bio(sc->spa_g_devstat, bp); + mtx_unlock(&sc->spa_g_stat_mtx); } - mtx_lock(&spa->spa_g_mtx); - bioq_disksort(&spa->spa_g_queue, bp); - wakeup(&spa->spa_g_queue); - mtx_unlock(&spa->spa_g_mtx); + mtx_lock(&sc->spa_g_mtx); + bioq_disksort(&sc->spa_g_queue, bp); + wakeup(&sc->spa_g_queue); + mtx_unlock(&sc->spa_g_mtx); } static int @@ -385,19 +375,78 @@ return (0); } -static g_init_t nvdimm_spa_g_init; -static g_fini_t nvdimm_spa_g_fini; +static int nvdimm_spa_g_destroy_geom(struct gctl_req *req, struct g_class *cp, + struct g_geom *gp) +{ + struct g_spa_softc *sc; + + sc = gp->softc; + mtx_lock(&sc->spa_g_mtx); + sc->spa_g_proc_run = false; + wakeup(&sc->spa_g_queue); + while (!sc->spa_g_proc_exiting) + msleep(&sc->spa_g_queue, &sc->spa_g_mtx, PRIBIO, "spa_e", 0); + mtx_unlock(&sc->spa_g_mtx); + g_topology_assert(); + g_wither_geom(gp, ENXIO); + sc->spa_p = NULL; + if (sc->spa_g_devstat != NULL) { + devstat_remove_entry(sc->spa_g_devstat); + sc->spa_g_devstat = NULL; + } + sc->spa->spa_g = NULL; + free(sc, M_NVDIMM); + gp->softc = NULL; + + return (0); +} struct g_class nvdimm_spa_g_class = { .name = "SPA", .version = G_VERSION, .start = nvdimm_spa_g_start, .access = nvdimm_spa_g_access, - .init = nvdimm_spa_g_init, - .fini = nvdimm_spa_g_fini, + .destroy_geom = nvdimm_spa_g_destroy_geom, }; DECLARE_GEOM_CLASS(nvdimm_spa_g_class, g_spa); +static int +nvdimm_spa_g_create(struct SPA_mapping *spa) +{ + struct g_spa_softc *sc; + int error; + + sc = malloc(sizeof(*sc), M_NVDIMM, M_WAITOK | M_ZERO); + sc->spa = spa; + bioq_init(&sc->spa_g_queue); + mtx_init(&sc->spa_g_mtx, "spag", NULL, MTX_DEF); + mtx_init(&sc->spa_g_stat_mtx, "spagst", NULL, MTX_DEF); + sc->spa_g_proc_run = true; + sc->spa_g_proc_exiting = false; + error = kproc_create(nvdimm_spa_g_thread, sc, &sc->spa_g_proc, 0, 0, + "g_spa%d", spa->spa_nfit_idx); + if (error != 0) { + printf("NVDIMM SPA%d cannot create geom worker, error %d\n", + spa->spa_nfit_idx, error); + return (error); + } + g_topology_lock(); + spa->spa_g = g_new_geomf(&nvdimm_spa_g_class, "spa%d", + spa->spa_nfit_idx); + spa->spa_g->softc = sc; + sc->spa_p = g_new_providerf(spa->spa_g, "spa%d", spa->spa_nfit_idx); + sc->spa_p->mediasize = spa->spa_len; + sc->spa_p->sectorsize = DEV_BSIZE; + sc->spa_p->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE | + G_PF_ACCEPT_UNMAPPED; + g_error_provider(sc->spa_p, 0); + sc->spa_g_devstat = devstat_new_entry("spa", spa->spa_nfit_idx, + DEV_BSIZE, DEVSTAT_ALL_SUPPORTED, DEVSTAT_TYPE_DIRECT, + DEVSTAT_PRIORITY_MAX); + g_topology_unlock(); + return (0); +} + static int nvdimm_spa_init_one(struct SPA_mapping *spa, ACPI_NFIT_SYSTEM_ADDRESS *nfitaddr, int spa_type) @@ -464,35 +513,9 @@ if (error1 == 0) error1 = error; } - - bioq_init(&spa->spa_g_queue); - mtx_init(&spa->spa_g_mtx, "spag", NULL, MTX_DEF); - mtx_init(&spa->spa_g_stat_mtx, "spagst", NULL, MTX_DEF); - spa->spa_g_proc_run = true; - spa->spa_g_proc_exiting = false; - error = kproc_create(nvdimm_spa_g_thread, spa, &spa->spa_g_proc, 0, 0, - "g_spa%d", spa->spa_nfit_idx); - if (error != 0) { - printf("NVDIMM SPA%d cannot create geom worker, error %d\n", - spa->spa_nfit_idx, error); - if (error1 == 0) - error1 = error; - } else { - g_topology_assert(); - spa->spa_g = g_new_geomf(&nvdimm_spa_g_class, "spa%d", - spa->spa_nfit_idx); - spa->spa_g->softc = spa; - spa->spa_p = g_new_providerf(spa->spa_g, "spa%d", - spa->spa_nfit_idx); - spa->spa_p->mediasize = spa->spa_len; - spa->spa_p->sectorsize = DEV_BSIZE; - spa->spa_p->flags |= G_PF_DIRECT_SEND | G_PF_DIRECT_RECEIVE | - G_PF_ACCEPT_UNMAPPED; - g_error_provider(spa->spa_p, 0); - spa->spa_g_devstat = devstat_new_entry("spa", spa->spa_nfit_idx, - DEV_BSIZE, DEVSTAT_ALL_SUPPORTED, DEVSTAT_TYPE_DIRECT, - DEVSTAT_PRIORITY_MAX); - } + error = nvdimm_spa_g_create(spa); + if (error1 == 0) + error1 = error; return (error1); } @@ -500,22 +523,10 @@ nvdimm_spa_fini_one(struct SPA_mapping *spa) { - mtx_lock(&spa->spa_g_mtx); - spa->spa_g_proc_run = false; - wakeup(&spa->spa_g_queue); - while (!spa->spa_g_proc_exiting) - msleep(&spa->spa_g_queue, &spa->spa_g_mtx, PRIBIO, "spa_e", 0); - mtx_unlock(&spa->spa_g_mtx); if (spa->spa_g != NULL) { g_topology_lock(); - g_wither_geom(spa->spa_g, ENXIO); + nvdimm_spa_g_destroy_geom(NULL, spa->spa_g->class, spa->spa_g); g_topology_unlock(); - spa->spa_g = NULL; - spa->spa_p = NULL; - } - if (spa->spa_g_devstat != NULL) { - devstat_remove_entry(spa->spa_g_devstat); - spa->spa_g_devstat = NULL; } if (spa->spa_dev != NULL) { destroy_dev(spa->spa_dev); @@ -526,19 +537,34 @@ pmap_large_unmap(spa->spa_kva, spa->spa_len); spa->spa_kva = NULL; } - mtx_destroy(&spa->spa_g_mtx); - mtx_destroy(&spa->spa_g_stat_mtx); +} + + +static char *nvdimm_root_id[] = {"ACPI0012", NULL}; + +static int +nvdimm_root_probe(device_t dev) +{ + int rv; + + 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_spa_parse(void *nfitsubtbl, void *arg) { ACPI_NFIT_SYSTEM_ADDRESS *nfitaddr; + struct nvdimm_root_dev *root; struct SPA_mapping *spa; - int error, *i, j; + int error, j; - i = arg; - spa = &spa_mappings[*i]; + root = arg; nfitaddr = nfitsubtbl; for (j = 0; j < nitems(nvdimm_SPA_uuid_list); j++) { @@ -546,9 +572,14 @@ if (uuidcmp((struct uuid *)&nfitaddr->RangeGuid, &nvdimm_SPA_uuid_list[j].u_id) != 0) continue; + spa = malloc(sizeof(*spa), M_NVDIMM, M_WAITOK | M_ZERO); error = nvdimm_spa_init_one(spa, nfitaddr, j); - if (error != 0) + if (error != 0) { nvdimm_spa_fini_one(spa); + free(spa, M_NVDIMM); + } else { + SLIST_INSERT_HEAD(&root->spas, spa, link); + } break; } if (j == nitems(nvdimm_SPA_uuid_list) && bootverbose) { @@ -556,12 +587,61 @@ printf_uuid((struct uuid *)&nfitaddr->RangeGuid); printf("\n"); } - (*i)++; return (0); } static int -nvdimm_spa_init1(ACPI_TABLE_NFIT *nfitbl) +nvdimm_root_attach(device_t dev) +{ + ACPI_TABLE_NFIT *nfitbl; + ACPI_STATUS status; + struct nvdimm_root_dev *root; + int error; + + error = 0; + root = device_get_softc(dev); + root->nv_root_dev = dev; + status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl); + if (ACPI_FAILURE(status)) { + device_printf(dev, "NFIT table not found\n"); + return (ENXIO); + } + error = nvdimm_iterate_nfit(nfitbl, ACPI_NFIT_TYPE_SYSTEM_ADDRESS, + nvdimm_spa_parse, root); + AcpiPutTable(&nfitbl->Header); + return (error); +} + +static int +nvdimm_root_detach(device_t dev) +{ + struct nvdimm_root_dev *root; + struct SPA_mapping *spa, *next; + + root = device_get_softc(dev); + SLIST_FOREACH_SAFE(spa, &root->spas, link, next) { + nvdimm_spa_fini_one(spa); + SLIST_REMOVE_HEAD(&root->spas, link); + free(spa, M_NVDIMM); + } + return (0); +} + +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_END +}; + +static driver_t nvdimm_root_driver = { + "nvdimm_root", + nvdimm_root_methods, + sizeof(struct nvdimm_root_dev), +}; + +static void +nvdimm_root_init(void) { struct nvdimm_SPA_uuid_list_elm *sle; int error, i; @@ -574,60 +654,36 @@ printf("nvdimm_identify: error %d parsing " "known SPA UUID %d %s\n", error, i, sle->u_id_str); - return (error); + return; } } - - error = nvdimm_iterate_nfit(nfitbl, ACPI_NFIT_TYPE_SYSTEM_ADDRESS, - nvdimm_spa_count, &spa_mappings_cnt); - if (error != 0) - return (error); - spa_mappings = malloc(sizeof(struct SPA_mapping) * spa_mappings_cnt, - M_NVDIMM, M_WAITOK | M_ZERO); - i = 0; - error = nvdimm_iterate_nfit(nfitbl, ACPI_NFIT_TYPE_SYSTEM_ADDRESS, - nvdimm_spa_parse, &i); - if (error != 0) { - free(spa_mappings, M_NVDIMM); - spa_mappings = NULL; - return (error); - } - return (0); } -static void -nvdimm_spa_g_init(struct g_class *mp __unused) +static int +nvdimm_root_modevent(struct module *mod, int what, void *arg) { - ACPI_TABLE_NFIT *nfitbl; - ACPI_STATUS status; int error; - spa_mappings_cnt = 0; - spa_mappings = NULL; - if (acpi_disabled("nvdimm")) - return; - status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl); - if (ACPI_FAILURE(status)) { - if (bootverbose) - printf("nvdimm_spa_g_init: cannot find NFIT\n"); - return; - } - error = nvdimm_spa_init1(nfitbl); - if (error != 0) - printf("nvdimm_spa_g_init: error %d\n", error); - AcpiPutTable(&nfitbl->Header); -} + switch (what) { + case MOD_LOAD: + nvdimm_root_init(); + error = 0; + break; -static void -nvdimm_spa_g_fini(struct g_class *mp __unused) -{ - int i; + case MOD_QUIESCE: + case MOD_UNLOAD: + error = 0; + break; - if (spa_mappings == NULL) - return; - for (i = 0; i < spa_mappings_cnt; i++) - nvdimm_spa_fini_one(&spa_mappings[i]); - free(spa_mappings, M_NVDIMM); - spa_mappings = NULL; - spa_mappings_cnt = 0; + default: + error = EOPNOTSUPP; + break; + } + + return (error); } + +static devclass_t nvdimm_root_devclass; +DRIVER_MODULE(nvdimm_root, acpi, nvdimm_root_driver, nvdimm_root_devclass, + nvdimm_root_modevent, NULL); +MODULE_DEPEND(nvdimm_root, acpi, 1, 1, 1); Index: sys/dev/nvdimm/nvdimm_var.h =================================================================== --- sys/dev/nvdimm/nvdimm_var.h +++ sys/dev/nvdimm/nvdimm_var.h @@ -34,6 +34,11 @@ typedef uint32_t nfit_handle_t; +struct nvdimm_root_dev { + device_t nv_root_dev; + SLIST_HEAD(, SPA_mapping) spas; +}; + struct nvdimm_dev { device_t nv_dev; nfit_handle_t nv_handle; @@ -54,6 +59,7 @@ }; struct SPA_mapping { + SLIST_ENTRY(SPA_mapping) link; enum SPA_mapping_type spa_type; int spa_domain; int spa_nfit_idx; @@ -61,15 +67,19 @@ uint64_t spa_len; uint64_t spa_efi_mem_flags; void *spa_kva; + struct vm_object *spa_obj; struct cdev *spa_dev; struct g_geom *spa_g; +}; + +struct g_spa_softc { + struct SPA_mapping *spa; 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; }; @@ -79,9 +89,6 @@ 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);