1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023 Chelsio Communications, Inc. 5 * Written by: John Baldwin <jhb@FreeBSD.org> 6 */ 7 8 #include <sys/param.h> 9 #include <sys/bus.h> 10 #include <sys/conf.h> 11 #include <sys/malloc.h> 12 #include <dev/nvme/nvme.h> 13 #include <dev/nvmf/nvmf.h> 14 #include <dev/nvmf/nvmf_transport.h> 15 #include <dev/nvmf/host/nvmf_var.h> 16 17 static struct cdev *nvmf_cdev; 18 19 static int 20 nvmf_handoff_host(struct nvmf_handoff_host *hh) 21 { 22 struct nvmf_ivars ivars; 23 device_t dev; 24 int error; 25 26 error = nvmf_init_ivars(&ivars, hh); 27 if (error != 0) 28 return (error); 29 30 bus_topo_lock(); 31 dev = device_add_child(root_bus, "nvme", -1); 32 if (dev == NULL) { 33 bus_topo_unlock(); 34 error = ENXIO; 35 goto out; 36 } 37 38 device_set_ivars(dev, &ivars); 39 error = device_probe_and_attach(dev); 40 device_set_ivars(dev, NULL); 41 if (error != 0) 42 device_delete_child(root_bus, dev); 43 bus_topo_unlock(); 44 45 out: 46 nvmf_free_ivars(&ivars); 47 return (error); 48 } 49 50 static bool 51 nvmf_matches(device_t dev, char *name) 52 { 53 struct nvmf_softc *sc = device_get_softc(dev); 54 55 if (strcmp(device_get_nameunit(dev), name) == 0) 56 return (true); 57 if (strcmp(sc->cdata->subnqn, name) == 0) 58 return (true); 59 return (false); 60 } 61 62 static int 63 nvmf_disconnect_by_name(char *name) 64 { 65 devclass_t dc; 66 device_t dev; 67 int error, unit; 68 bool found; 69 70 found = false; 71 error = 0; 72 bus_topo_lock(); 73 dc = devclass_find("nvme"); 74 if (dc == NULL) 75 goto out; 76 77 for (unit = 0; unit < devclass_get_maxunit(dc); unit++) { 78 dev = devclass_get_device(dc, unit); 79 if (dev == NULL) 80 continue; 81 if (device_get_driver(dev) != &nvme_nvmf_driver) 82 continue; 83 if (device_get_parent(dev) != root_bus) 84 continue; 85 if (name != NULL && !nvmf_matches(dev, name)) 86 continue; 87 88 error = device_delete_child(root_bus, dev); 89 if (error != 0) 90 break; 91 found = true; 92 } 93 out: 94 bus_topo_unlock(); 95 if (error == 0 && !found) 96 error = ENOENT; 97 return (error); 98 } 99 100 static int 101 nvmf_disconnect_host(const char **namep) 102 { 103 char *name; 104 int error; 105 106 name = malloc(PATH_MAX, M_NVMF, M_WAITOK); 107 error = copyinstr(*namep, name, PATH_MAX, NULL); 108 if (error == 0) 109 error = nvmf_disconnect_by_name(name); 110 free(name, M_NVMF); 111 return (error); 112 } 113 114 static int 115 nvmf_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, 116 struct thread *td) 117 { 118 switch (cmd) { 119 case NVMF_HANDOFF_HOST: 120 return (nvmf_handoff_host((struct nvmf_handoff_host *)arg)); 121 case NVMF_DISCONNECT_HOST: 122 return (nvmf_disconnect_host((const char **)arg)); 123 case NVMF_DISCONNECT_ALL: 124 return (nvmf_disconnect_by_name(NULL)); 125 default: 126 return (ENOTTY); 127 } 128 } 129 130 static struct cdevsw nvmf_ctl_cdevsw = { 131 .d_version = D_VERSION, 132 .d_ioctl = nvmf_ctl_ioctl 133 }; 134 135 int 136 nvmf_ctl_load(void) 137 { 138 struct make_dev_args mda; 139 int error; 140 141 make_dev_args_init(&mda); 142 mda.mda_devsw = &nvmf_ctl_cdevsw; 143 mda.mda_uid = UID_ROOT; 144 mda.mda_gid = GID_WHEEL; 145 mda.mda_mode = 0600; 146 error = make_dev_s(&mda, &nvmf_cdev, "nvmf"); 147 if (error != 0) 148 nvmf_cdev = NULL; 149 return (error); 150 } 151 152 void 153 nvmf_ctl_unload(void) 154 { 155 if (nvmf_cdev != NULL) { 156 destroy_dev(nvmf_cdev); 157 nvmf_cdev = NULL; 158 } 159 } 160