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