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