xref: /freebsd/sys/dev/nvmf/host/nvmf_ctldev.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
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