/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2023 Chelsio Communications, Inc. * Written by: John Baldwin */ #include #include #include #include #include #include #include #include static struct cdev *nvmf_cdev; static int nvmf_handoff_host(struct nvmf_handoff_host *hh) { struct nvmf_ivars ivars; device_t dev; int error; error = nvmf_init_ivars(&ivars, hh); if (error != 0) return (error); bus_topo_lock(); dev = device_add_child(root_bus, "nvme", -1); if (dev == NULL) { bus_topo_unlock(); error = ENXIO; goto out; } device_set_ivars(dev, &ivars); error = device_probe_and_attach(dev); device_set_ivars(dev, NULL); if (error != 0) device_delete_child(root_bus, dev); bus_topo_unlock(); out: nvmf_free_ivars(&ivars); return (error); } static bool nvmf_matches(device_t dev, char *name) { struct nvmf_softc *sc = device_get_softc(dev); if (strcmp(device_get_nameunit(dev), name) == 0) return (true); if (strcmp(sc->cdata->subnqn, name) == 0) return (true); return (false); } static int nvmf_disconnect_by_name(char *name) { devclass_t dc; device_t dev; int error, unit; bool found; found = false; error = 0; bus_topo_lock(); dc = devclass_find("nvme"); if (dc == NULL) goto out; for (unit = 0; unit < devclass_get_maxunit(dc); unit++) { dev = devclass_get_device(dc, unit); if (dev == NULL) continue; if (device_get_driver(dev) != &nvme_nvmf_driver) continue; if (device_get_parent(dev) != root_bus) continue; if (name != NULL && !nvmf_matches(dev, name)) continue; error = device_delete_child(root_bus, dev); if (error != 0) break; found = true; } out: bus_topo_unlock(); if (error == 0 && !found) error = ENOENT; return (error); } static int nvmf_disconnect_host(const char **namep) { char *name; int error; name = malloc(PATH_MAX, M_NVMF, M_WAITOK); error = copyinstr(*namep, name, PATH_MAX, NULL); if (error == 0) error = nvmf_disconnect_by_name(name); free(name, M_NVMF); return (error); } static int nvmf_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) { switch (cmd) { case NVMF_HANDOFF_HOST: return (nvmf_handoff_host((struct nvmf_handoff_host *)arg)); case NVMF_DISCONNECT_HOST: return (nvmf_disconnect_host((const char **)arg)); case NVMF_DISCONNECT_ALL: return (nvmf_disconnect_by_name(NULL)); default: return (ENOTTY); } } static struct cdevsw nvmf_ctl_cdevsw = { .d_version = D_VERSION, .d_ioctl = nvmf_ctl_ioctl }; int nvmf_ctl_load(void) { struct make_dev_args mda; int error; make_dev_args_init(&mda); mda.mda_devsw = &nvmf_ctl_cdevsw; mda.mda_uid = UID_ROOT; mda.mda_gid = GID_WHEEL; mda.mda_mode = 0600; error = make_dev_s(&mda, &nvmf_cdev, "nvmf"); if (error != 0) nvmf_cdev = NULL; return (error); } void nvmf_ctl_unload(void) { if (nvmf_cdev != NULL) { destroy_dev(nvmf_cdev); nvmf_cdev = NULL; } }