nvme_sim: Attach as a child of nvme
Rather than registering as a consumer of the nvme controller, hook into the child device and use that. This is a small regression at the moment: we don't fail the device when that happens at runtime, and we don't handle new namespaces when they arrive (though that feature is currently fragile). Sponsored by: Netflix Differential Revision: https://reviews.freebsd.org/D51384
This commit is contained in:
+86
-54
@@ -304,61 +304,101 @@ nvme_sim_poll(struct cam_sim *sim)
|
||||
nvme_ctrlr_poll(sim2ctrlr(sim));
|
||||
}
|
||||
|
||||
static void *
|
||||
nvme_sim_new_controller(struct nvme_controller *ctrlr)
|
||||
static void
|
||||
nvme_sim_ns_change(struct nvme_namespace *ns, struct nvme_sim_softc *sc)
|
||||
{
|
||||
union ccb *ccb;
|
||||
|
||||
/*
|
||||
* We map the NVMe namespace idea onto the CAM unit LUN. For each new
|
||||
* namespace, we create a new CAM path for it. We then rescan the path
|
||||
* to get it to enumerate.
|
||||
*/
|
||||
ccb = xpt_alloc_ccb();
|
||||
if (xpt_create_path(&ccb->ccb_h.path, /*periph*/NULL,
|
||||
cam_sim_path(sc->s_sim), 0, ns->id) != CAM_REQ_CMP) {
|
||||
printf("unable to create path for rescan\n");
|
||||
xpt_free_ccb(ccb);
|
||||
return;
|
||||
}
|
||||
xpt_rescan(ccb);
|
||||
}
|
||||
|
||||
static int
|
||||
nvme_sim_probe(device_t dev)
|
||||
{
|
||||
if (nvme_use_nvd)
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "nvme cam");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
nvme_sim_attach(device_t dev)
|
||||
{
|
||||
struct nvme_sim_softc *sc;
|
||||
struct cam_devq *devq;
|
||||
int max_trans;
|
||||
int err = ENOMEM;
|
||||
struct nvme_sim_softc *sc = device_get_softc(dev);
|
||||
struct nvme_controller *ctrlr = device_get_ivars(dev);
|
||||
|
||||
max_trans = ctrlr->max_hw_pend_io;
|
||||
devq = cam_simq_alloc(max_trans);
|
||||
if (devq == NULL)
|
||||
return (NULL);
|
||||
return (ENOMEM);
|
||||
|
||||
sc = malloc(sizeof(*sc), M_NVME, M_ZERO | M_WAITOK);
|
||||
sc->s_ctrlr = ctrlr;
|
||||
|
||||
sc->s_sim = cam_sim_alloc(nvme_sim_action, nvme_sim_poll,
|
||||
"nvme", sc, device_get_unit(ctrlr->dev),
|
||||
"nvme", sc, device_get_unit(dev),
|
||||
NULL, max_trans, max_trans, devq);
|
||||
if (sc->s_sim == NULL) {
|
||||
printf("Failed to allocate a sim\n");
|
||||
device_printf(dev, "Failed to allocate a sim\n");
|
||||
cam_simq_free(devq);
|
||||
goto err1;
|
||||
}
|
||||
if (xpt_bus_register(sc->s_sim, ctrlr->dev, 0) != CAM_SUCCESS) {
|
||||
printf("Failed to create a bus\n");
|
||||
if (xpt_bus_register(sc->s_sim, dev, 0) != CAM_SUCCESS) {
|
||||
err = ENXIO;
|
||||
device_printf(dev, "Failed to create a bus\n");
|
||||
goto err2;
|
||||
}
|
||||
if (xpt_create_path(&sc->s_path, /*periph*/NULL, cam_sim_path(sc->s_sim),
|
||||
CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
|
||||
printf("Failed to create a path\n");
|
||||
err = ENXIO;
|
||||
device_printf(dev, "Failed to create a path\n");
|
||||
goto err3;
|
||||
}
|
||||
|
||||
return (sc);
|
||||
for (int i = 0; i < min(ctrlr->cdata.nn, NVME_MAX_NAMESPACES); i++) {
|
||||
struct nvme_namespace *ns = &ctrlr->ns[i];
|
||||
|
||||
if (ns->data.nsze == 0)
|
||||
continue;
|
||||
nvme_sim_ns_change(ns, sc);
|
||||
}
|
||||
|
||||
return (0);
|
||||
err3:
|
||||
xpt_bus_deregister(cam_sim_path(sc->s_sim));
|
||||
err2:
|
||||
cam_sim_free(sc->s_sim, /*free_devq*/TRUE);
|
||||
err1:
|
||||
free(sc, M_NVME);
|
||||
return (NULL);
|
||||
return (err);
|
||||
}
|
||||
|
||||
static void *
|
||||
nvme_sim_ns_change(struct nvme_namespace *ns, void *sc_arg)
|
||||
static int
|
||||
nvme_sim_fail(device_t dev, struct nvme_namespace *ns)
|
||||
{
|
||||
struct nvme_sim_softc *sc = sc_arg;
|
||||
struct nvme_sim_softc *sc = device_get_softc(dev);
|
||||
struct cam_path *tmppath;
|
||||
union ccb *ccb;
|
||||
|
||||
if (xpt_create_path(&tmppath, /*periph*/NULL,
|
||||
cam_sim_path(sc->s_sim), 0, ns->id) != CAM_REQ_CMP) {
|
||||
printf("unable to create path for rescan\n");
|
||||
return (NULL);
|
||||
device_printf(dev, "unable to create path for rescan\n");
|
||||
return (ENOMEM);
|
||||
}
|
||||
/*
|
||||
* If it's gone, then signal that and leave.
|
||||
@@ -366,13 +406,13 @@ nvme_sim_ns_change(struct nvme_namespace *ns, void *sc_arg)
|
||||
if (ns->flags & NVME_NS_GONE) {
|
||||
xpt_async(AC_LOST_DEVICE, tmppath, NULL);
|
||||
xpt_free_path(tmppath);
|
||||
return (sc_arg);
|
||||
return (0);
|
||||
}
|
||||
|
||||
ccb = xpt_alloc_ccb_nowait();
|
||||
if (ccb == NULL) {
|
||||
printf("unable to alloc CCB for rescan\n");
|
||||
return (NULL);
|
||||
device_printf(dev, "unable to alloc CCB for rescan\n");
|
||||
return (ENOMEM);
|
||||
}
|
||||
ccb->ccb_h.path = tmppath;
|
||||
|
||||
@@ -383,45 +423,37 @@ nvme_sim_ns_change(struct nvme_namespace *ns, void *sc_arg)
|
||||
*/
|
||||
xpt_rescan(ccb);
|
||||
|
||||
return (sc_arg);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
nvme_sim_controller_fail(void *ctrlr_arg)
|
||||
static int
|
||||
nvme_sim_detach(device_t dev)
|
||||
{
|
||||
struct nvme_sim_softc *sc = ctrlr_arg;
|
||||
struct nvme_controller *ctrlr = device_get_ivars(dev);
|
||||
|
||||
xpt_async(AC_LOST_DEVICE, sc->s_path, NULL);
|
||||
xpt_free_path(sc->s_path);
|
||||
xpt_bus_deregister(cam_sim_path(sc->s_sim));
|
||||
cam_sim_free(sc->s_sim, /*free_devq*/TRUE);
|
||||
free(sc, M_NVME);
|
||||
for (int i = 0; i < min(ctrlr->cdata.nn, NVME_MAX_NAMESPACES); i++) {
|
||||
struct nvme_namespace *ns = &ctrlr->ns[i];
|
||||
|
||||
if (ns->data.nsze == 0)
|
||||
continue;
|
||||
nvme_sim_fail(dev, ns);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct nvme_consumer *consumer_cookie;
|
||||
static device_method_t nvme_sim_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, nvme_sim_probe),
|
||||
DEVMETHOD(device_attach, nvme_sim_attach),
|
||||
DEVMETHOD(device_detach, nvme_sim_detach),
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
static void
|
||||
nvme_sim_init(void *dummy __unused)
|
||||
{
|
||||
if (nvme_use_nvd)
|
||||
return;
|
||||
static driver_t nvme_sim_driver = {
|
||||
"nvme_sim",
|
||||
nvme_sim_methods,
|
||||
sizeof(struct nvme_sim_softc),
|
||||
};
|
||||
|
||||
consumer_cookie = nvme_register_consumer(nvme_sim_ns_change,
|
||||
nvme_sim_new_controller, NULL, nvme_sim_controller_fail);
|
||||
}
|
||||
|
||||
SYSINIT(nvme_sim_register, SI_SUB_DRIVERS, SI_ORDER_ANY,
|
||||
nvme_sim_init, NULL);
|
||||
|
||||
static void
|
||||
nvme_sim_uninit(void *dummy __unused)
|
||||
{
|
||||
if (nvme_use_nvd)
|
||||
return;
|
||||
/* XXX Cleanup */
|
||||
|
||||
nvme_unregister_consumer(consumer_cookie);
|
||||
}
|
||||
|
||||
SYSUNINIT(nvme_sim_unregister, SI_SUB_DRIVERS, SI_ORDER_ANY,
|
||||
nvme_sim_uninit, NULL);
|
||||
DRIVER_MODULE(nvme_sim, nvme, nvme_sim_driver, NULL, NULL);
|
||||
MODULE_VERSION(nvme_shim, 1);
|
||||
|
||||
Reference in New Issue
Block a user