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
nvmf_handoff_host(struct nvmf_ioc_nv * nv)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
nvmf_matches(device_t dev,char * name)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
nvmf_disconnect_by_name(char * name)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
nvmf_disconnect_host(const char ** namep)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
nvmf_ctl_ioctl(struct cdev * dev,u_long cmd,caddr_t arg,int flag,struct thread * td)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
nvmf_ctl_load(void)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
nvmf_ctl_unload(void)154 nvmf_ctl_unload(void)
155 {
156 if (nvmf_cdev != NULL) {
157 destroy_dev(nvmf_cdev);
158 nvmf_cdev = NULL;
159 }
160 }
161