Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/nvdimm/nvdimm_spa.c
Show First 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | |||||
#include <vm/vm_page.h> | #include <vm/vm_page.h> | ||||
#include <vm/vm_pager.h> | #include <vm/vm_pager.h> | ||||
#include <contrib/dev/acpica/include/acpi.h> | #include <contrib/dev/acpica/include/acpi.h> | ||||
#include <contrib/dev/acpica/include/accommon.h> | #include <contrib/dev/acpica/include/accommon.h> | ||||
#include <contrib/dev/acpica/include/acuuid.h> | #include <contrib/dev/acpica/include/acuuid.h> | ||||
#include <dev/acpica/acpivar.h> | #include <dev/acpica/acpivar.h> | ||||
#include <dev/nvdimm/nvdimm_var.h> | #include <dev/nvdimm/nvdimm_var.h> | ||||
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 { | static struct nvdimm_SPA_uuid_list_elm { | ||||
const char *u_name; | const char *u_name; | ||||
const char *u_id_str; | const char *u_id_str; | ||||
struct uuid u_id; | struct uuid u_id; | ||||
const bool u_usr_acc; | const bool u_usr_acc; | ||||
} nvdimm_SPA_uuid_list[] = { | } nvdimm_SPA_uuid_list[] = { | ||||
[SPA_TYPE_VOLATILE_MEMORY] = { | [SPA_TYPE_VOLATILE_MEMORY] = { | ||||
.u_name = "VOLA MEM ", | .u_name = "VOLA MEM ", | ||||
▲ Show 20 Lines • Show All 183 Lines • ▼ Show 20 Lines | nvdimm_spa_g_all_unmapped(struct SPA_mapping *spa, struct bio *bp, | ||||
else | else | ||||
pmap_copy_pages(bp->bio_ma, bp->bio_ma_offset, ma, | pmap_copy_pages(bp->bio_ma, bp->bio_ma_offset, ma, | ||||
bp->bio_offset & PAGE_MASK, bp->bio_length); | bp->bio_offset & PAGE_MASK, bp->bio_length); | ||||
} | } | ||||
static void | static void | ||||
nvdimm_spa_g_thread(void *arg) | nvdimm_spa_g_thread(void *arg) | ||||
{ | { | ||||
struct SPA_mapping *spa; | struct g_spa_softc *sc; | ||||
struct bio *bp; | struct bio *bp; | ||||
struct uio auio; | struct uio auio; | ||||
struct iovec aiovec; | struct iovec aiovec; | ||||
int error; | int error; | ||||
spa = arg; | sc = arg; | ||||
for (;;) { | for (;;) { | ||||
mtx_lock(&spa->spa_g_mtx); | mtx_lock(&sc->spa_g_mtx); | ||||
for (;;) { | for (;;) { | ||||
bp = bioq_takefirst(&spa->spa_g_queue); | bp = bioq_takefirst(&sc->spa_g_queue); | ||||
if (bp != NULL) | if (bp != NULL) | ||||
break; | break; | ||||
msleep(&spa->spa_g_queue, &spa->spa_g_mtx, PRIBIO, | msleep(&sc->spa_g_queue, &sc->spa_g_mtx, PRIBIO, | ||||
"spa_g", 0); | "spa_g", 0); | ||||
if (!spa->spa_g_proc_run) { | if (!sc->spa_g_proc_run) { | ||||
spa->spa_g_proc_exiting = true; | sc->spa_g_proc_exiting = true; | ||||
wakeup(&spa->spa_g_queue); | wakeup(&sc->spa_g_queue); | ||||
mtx_unlock(&spa->spa_g_mtx); | mtx_unlock(&sc->spa_g_mtx); | ||||
kproc_exit(0); | kproc_exit(0); | ||||
} | } | ||||
continue; | continue; | ||||
} | } | ||||
mtx_unlock(&spa->spa_g_mtx); | mtx_unlock(&sc->spa_g_mtx); | ||||
if (bp->bio_cmd != BIO_READ && bp->bio_cmd != BIO_WRITE && | if (bp->bio_cmd != BIO_READ && bp->bio_cmd != BIO_WRITE && | ||||
bp->bio_cmd != BIO_FLUSH) { | bp->bio_cmd != BIO_FLUSH) { | ||||
error = EOPNOTSUPP; | error = EOPNOTSUPP; | ||||
goto completed; | goto completed; | ||||
} | } | ||||
error = 0; | error = 0; | ||||
if (bp->bio_cmd == BIO_FLUSH) { | if (bp->bio_cmd == BIO_FLUSH) { | ||||
if (spa->spa_kva != NULL) { | if (sc->spa->spa_kva != NULL) { | ||||
pmap_large_map_wb(spa->spa_kva, spa->spa_len); | pmap_large_map_wb(sc->spa->spa_kva, | ||||
sc->spa->spa_len); | |||||
} else { | } else { | ||||
pmap_flush_cache_phys_range( | pmap_flush_cache_phys_range( | ||||
(vm_paddr_t)spa->spa_phys_base, | (vm_paddr_t)sc->spa->spa_phys_base, | ||||
(vm_paddr_t)spa->spa_phys_base + | (vm_paddr_t)sc->spa->spa_phys_base + | ||||
spa->spa_len, nvdimm_spa_memattr(spa)); | sc->spa->spa_len, | ||||
nvdimm_spa_memattr(sc->spa)); | |||||
} | } | ||||
/* | /* | ||||
* XXX flush IMC | * XXX flush IMC | ||||
*/ | */ | ||||
goto completed; | goto completed; | ||||
} | } | ||||
if ((bp->bio_flags & BIO_UNMAPPED) != 0) { | if ((bp->bio_flags & BIO_UNMAPPED) != 0) { | ||||
if (spa->spa_kva != NULL) { | if (sc->spa->spa_kva != NULL) { | ||||
aiovec.iov_base = (char *)spa->spa_kva + | aiovec.iov_base = (char *)sc->spa->spa_kva + | ||||
bp->bio_offset; | bp->bio_offset; | ||||
aiovec.iov_len = bp->bio_length; | aiovec.iov_len = bp->bio_length; | ||||
auio.uio_iov = &aiovec; | auio.uio_iov = &aiovec; | ||||
auio.uio_iovcnt = 1; | auio.uio_iovcnt = 1; | ||||
auio.uio_resid = bp->bio_length; | auio.uio_resid = bp->bio_length; | ||||
auio.uio_offset = bp->bio_offset; | auio.uio_offset = bp->bio_offset; | ||||
auio.uio_segflg = UIO_SYSSPACE; | auio.uio_segflg = UIO_SYSSPACE; | ||||
auio.uio_rw = bp->bio_cmd == BIO_READ ? | auio.uio_rw = bp->bio_cmd == BIO_READ ? | ||||
UIO_WRITE : UIO_READ; | UIO_WRITE : UIO_READ; | ||||
auio.uio_td = curthread; | auio.uio_td = curthread; | ||||
error = uiomove_fromphys(bp->bio_ma, | error = uiomove_fromphys(bp->bio_ma, | ||||
bp->bio_ma_offset, bp->bio_length, &auio); | bp->bio_ma_offset, bp->bio_length, &auio); | ||||
} else { | } else { | ||||
nvdimm_spa_g_all_unmapped(spa, bp, bp->bio_cmd); | nvdimm_spa_g_all_unmapped(sc->spa, bp, | ||||
bp->bio_cmd); | |||||
error = 0; | error = 0; | ||||
} | } | ||||
} else { | } else { | ||||
aiovec.iov_base = bp->bio_data; | aiovec.iov_base = bp->bio_data; | ||||
aiovec.iov_len = bp->bio_length; | aiovec.iov_len = bp->bio_length; | ||||
auio.uio_iov = &aiovec; | auio.uio_iov = &aiovec; | ||||
auio.uio_iovcnt = 1; | auio.uio_iovcnt = 1; | ||||
auio.uio_resid = bp->bio_length; | auio.uio_resid = bp->bio_length; | ||||
auio.uio_offset = bp->bio_offset; | auio.uio_offset = bp->bio_offset; | ||||
auio.uio_segflg = UIO_SYSSPACE; | auio.uio_segflg = UIO_SYSSPACE; | ||||
auio.uio_rw = bp->bio_cmd == BIO_READ ? UIO_READ : | auio.uio_rw = bp->bio_cmd == BIO_READ ? UIO_READ : | ||||
UIO_WRITE; | UIO_WRITE; | ||||
auio.uio_td = curthread; | 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: | completed: | ||||
bp->bio_completed = bp->bio_length; | bp->bio_completed = bp->bio_length; | ||||
g_io_deliver(bp, error); | g_io_deliver(bp, error); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
nvdimm_spa_g_start(struct bio *bp) | 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) { | if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) { | ||||
mtx_lock(&spa->spa_g_stat_mtx); | mtx_lock(&sc->spa_g_stat_mtx); | ||||
devstat_start_transaction_bio(spa->spa_g_devstat, bp); | devstat_start_transaction_bio(sc->spa_g_devstat, bp); | ||||
mtx_unlock(&spa->spa_g_stat_mtx); | mtx_unlock(&sc->spa_g_stat_mtx); | ||||
} | } | ||||
mtx_lock(&spa->spa_g_mtx); | mtx_lock(&sc->spa_g_mtx); | ||||
bioq_disksort(&spa->spa_g_queue, bp); | bioq_disksort(&sc->spa_g_queue, bp); | ||||
wakeup(&spa->spa_g_queue); | wakeup(&sc->spa_g_queue); | ||||
mtx_unlock(&spa->spa_g_mtx); | mtx_unlock(&sc->spa_g_mtx); | ||||
} | } | ||||
static int | static int | ||||
nvdimm_spa_g_access(struct g_provider *pp, int r, int w, int e) | nvdimm_spa_g_access(struct g_provider *pp, int r, int w, int e) | ||||
{ | { | ||||
return (0); | return (0); | ||||
} | } | ||||
static g_init_t nvdimm_spa_g_init; | static int nvdimm_spa_g_destroy_geom(struct gctl_req *req, struct g_class *cp, | ||||
static g_fini_t nvdimm_spa_g_fini; | 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 = { | struct g_class nvdimm_spa_g_class = { | ||||
.name = "SPA", | .name = "SPA", | ||||
.version = G_VERSION, | .version = G_VERSION, | ||||
.start = nvdimm_spa_g_start, | .start = nvdimm_spa_g_start, | ||||
.access = nvdimm_spa_g_access, | .access = nvdimm_spa_g_access, | ||||
.init = nvdimm_spa_g_init, | .destroy_geom = nvdimm_spa_g_destroy_geom, | ||||
.fini = nvdimm_spa_g_fini, | |||||
}; | }; | ||||
DECLARE_GEOM_CLASS(nvdimm_spa_g_class, g_spa); | DECLARE_GEOM_CLASS(nvdimm_spa_g_class, g_spa); | ||||
static int | 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, | nvdimm_spa_init_one(struct SPA_mapping *spa, ACPI_NFIT_SYSTEM_ADDRESS *nfitaddr, | ||||
int spa_type) | int spa_type) | ||||
{ | { | ||||
struct make_dev_args mda; | struct make_dev_args mda; | ||||
struct sglist *spa_sg; | struct sglist *spa_sg; | ||||
int error, error1; | int error, error1; | ||||
spa->spa_type = spa_type; | spa->spa_type = spa_type; | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | nvdimm_spa_init_one(struct SPA_mapping *spa, ACPI_NFIT_SYSTEM_ADDRESS *nfitaddr, | ||||
error = make_dev_s(&mda, &spa->spa_dev, "nvdimm_spa%d", | error = make_dev_s(&mda, &spa->spa_dev, "nvdimm_spa%d", | ||||
spa->spa_nfit_idx); | spa->spa_nfit_idx); | ||||
if (error != 0) { | if (error != 0) { | ||||
printf("NVDIMM SPA%d cannot create devfs node, error %d\n", | printf("NVDIMM SPA%d cannot create devfs node, error %d\n", | ||||
spa->spa_nfit_idx, error); | spa->spa_nfit_idx, error); | ||||
if (error1 == 0) | if (error1 == 0) | ||||
error1 = error; | error1 = error; | ||||
} | } | ||||
error = nvdimm_spa_g_create(spa); | |||||
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) | if (error1 == 0) | ||||
error1 = error; | 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); | |||||
} | |||||
return (error1); | return (error1); | ||||
} | } | ||||
static void | static void | ||||
nvdimm_spa_fini_one(struct SPA_mapping *spa) | 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) { | if (spa->spa_g != NULL) { | ||||
g_topology_lock(); | 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(); | 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) { | if (spa->spa_dev != NULL) { | ||||
destroy_dev(spa->spa_dev); | destroy_dev(spa->spa_dev); | ||||
spa->spa_dev = NULL; | spa->spa_dev = NULL; | ||||
} | } | ||||
vm_object_deallocate(spa->spa_obj); | vm_object_deallocate(spa->spa_obj); | ||||
if (spa->spa_kva != NULL) { | if (spa->spa_kva != NULL) { | ||||
pmap_large_unmap(spa->spa_kva, spa->spa_len); | pmap_large_unmap(spa->spa_kva, spa->spa_len); | ||||
spa->spa_kva = NULL; | 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 | 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) | nvdimm_spa_parse(void *nfitsubtbl, void *arg) | ||||
{ | { | ||||
ACPI_NFIT_SYSTEM_ADDRESS *nfitaddr; | ACPI_NFIT_SYSTEM_ADDRESS *nfitaddr; | ||||
struct nvdimm_root_dev *root; | |||||
struct SPA_mapping *spa; | struct SPA_mapping *spa; | ||||
int error, *i, j; | int error, j; | ||||
i = arg; | root = arg; | ||||
spa = &spa_mappings[*i]; | |||||
nfitaddr = nfitsubtbl; | nfitaddr = nfitsubtbl; | ||||
for (j = 0; j < nitems(nvdimm_SPA_uuid_list); j++) { | for (j = 0; j < nitems(nvdimm_SPA_uuid_list); j++) { | ||||
/* XXXKIB: is ACPI UUID representation compatible ? */ | /* XXXKIB: is ACPI UUID representation compatible ? */ | ||||
if (uuidcmp((struct uuid *)&nfitaddr->RangeGuid, | if (uuidcmp((struct uuid *)&nfitaddr->RangeGuid, | ||||
&nvdimm_SPA_uuid_list[j].u_id) != 0) | &nvdimm_SPA_uuid_list[j].u_id) != 0) | ||||
continue; | continue; | ||||
spa = malloc(sizeof(*spa), M_NVDIMM, M_WAITOK | M_ZERO); | |||||
error = nvdimm_spa_init_one(spa, nfitaddr, j); | error = nvdimm_spa_init_one(spa, nfitaddr, j); | ||||
if (error != 0) | if (error != 0) { | ||||
nvdimm_spa_fini_one(spa); | nvdimm_spa_fini_one(spa); | ||||
free(spa, M_NVDIMM); | |||||
} else { | |||||
SLIST_INSERT_HEAD(&root->spas, spa, link); | |||||
} | |||||
break; | break; | ||||
} | } | ||||
if (j == nitems(nvdimm_SPA_uuid_list) && bootverbose) { | if (j == nitems(nvdimm_SPA_uuid_list) && bootverbose) { | ||||
printf("Unknown SPA UUID %d ", nfitaddr->RangeIndex); | printf("Unknown SPA UUID %d ", nfitaddr->RangeIndex); | ||||
printf_uuid((struct uuid *)&nfitaddr->RangeGuid); | printf_uuid((struct uuid *)&nfitaddr->RangeGuid); | ||||
printf("\n"); | printf("\n"); | ||||
} | } | ||||
(*i)++; | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | 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; | struct nvdimm_SPA_uuid_list_elm *sle; | ||||
int error, i; | int error, i; | ||||
for (i = 0; i < nitems(nvdimm_SPA_uuid_list); i++) { | for (i = 0; i < nitems(nvdimm_SPA_uuid_list); i++) { | ||||
sle = &nvdimm_SPA_uuid_list[i]; | sle = &nvdimm_SPA_uuid_list[i]; | ||||
error = parse_uuid(sle->u_id_str, &sle->u_id); | error = parse_uuid(sle->u_id_str, &sle->u_id); | ||||
if (error != 0) { | if (error != 0) { | ||||
if (bootverbose) | if (bootverbose) | ||||
printf("nvdimm_identify: error %d parsing " | printf("nvdimm_identify: error %d parsing " | ||||
"known SPA UUID %d %s\n", error, i, | "known SPA UUID %d %s\n", error, i, | ||||
sle->u_id_str); | 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 | static int | ||||
nvdimm_spa_g_init(struct g_class *mp __unused) | nvdimm_root_modevent(struct module *mod, int what, void *arg) | ||||
{ | { | ||||
ACPI_TABLE_NFIT *nfitbl; | |||||
ACPI_STATUS status; | |||||
int error; | int error; | ||||
spa_mappings_cnt = 0; | switch (what) { | ||||
spa_mappings = NULL; | case MOD_LOAD: | ||||
if (acpi_disabled("nvdimm")) | nvdimm_root_init(); | ||||
return; | error = 0; | ||||
status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl); | break; | ||||
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); | |||||
} | |||||
static void | case MOD_QUIESCE: | ||||
nvdimm_spa_g_fini(struct g_class *mp __unused) | case MOD_UNLOAD: | ||||
{ | error = 0; | ||||
int i; | break; | ||||
if (spa_mappings == NULL) | default: | ||||
return; | error = EOPNOTSUPP; | ||||
for (i = 0; i < spa_mappings_cnt; i++) | break; | ||||
nvdimm_spa_fini_one(&spa_mappings[i]); | |||||
free(spa_mappings, M_NVDIMM); | |||||
spa_mappings = NULL; | |||||
spa_mappings_cnt = 0; | |||||
} | } | ||||
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); |