xref: /spdk/lib/nvmf/subsystem.c (revision b961d9cc12de49251d135307eaa05ec0fc9dd2fa)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "spdk/stdinc.h"
35 
36 #include "nvmf_internal.h"
37 #include "session.h"
38 #include "subsystem.h"
39 #include "transport.h"
40 
41 #include "spdk/string.h"
42 #include "spdk/trace.h"
43 #include "spdk/nvmf_spec.h"
44 
45 #include "spdk_internal/bdev.h"
46 #include "spdk_internal/log.h"
47 
48 bool
49 spdk_nvmf_subsystem_exists(const char *subnqn)
50 {
51 	struct spdk_nvmf_subsystem	*subsystem;
52 
53 	if (!subnqn) {
54 		return false;
55 	}
56 
57 	TAILQ_FOREACH(subsystem, &g_nvmf_tgt.subsystems, entries) {
58 		if (strcmp(subnqn, subsystem->subnqn) == 0) {
59 			return true;
60 		}
61 	}
62 
63 	return false;
64 }
65 
66 struct spdk_nvmf_subsystem *
67 nvmf_find_subsystem(const char *subnqn)
68 {
69 	struct spdk_nvmf_subsystem	*subsystem;
70 
71 	if (!subnqn) {
72 		return NULL;
73 	}
74 
75 	TAILQ_FOREACH(subsystem, &g_nvmf_tgt.subsystems, entries) {
76 		if (strcmp(subnqn, subsystem->subnqn) == 0) {
77 			return subsystem;
78 		}
79 	}
80 
81 	return NULL;
82 }
83 
84 struct spdk_nvmf_subsystem *
85 spdk_nvmf_find_subsystem_with_cntlid(uint16_t cntlid)
86 {
87 	struct spdk_nvmf_subsystem	*subsystem;
88 	struct spdk_nvmf_session 	*session;
89 
90 	TAILQ_FOREACH(subsystem, &g_nvmf_tgt.subsystems, entries) {
91 		TAILQ_FOREACH(session, &subsystem->sessions, link) {
92 			if (session->cntlid == cntlid) {
93 				return subsystem;
94 			}
95 		}
96 	}
97 
98 	return NULL;
99 }
100 
101 bool
102 spdk_nvmf_subsystem_host_allowed(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn)
103 {
104 	struct spdk_nvmf_host *host;
105 
106 	if (!hostnqn) {
107 		return false;
108 	}
109 
110 	if (subsystem->num_hosts == 0) {
111 		/* No hosts means any host can connect */
112 		return true;
113 	}
114 
115 	TAILQ_FOREACH(host, &subsystem->hosts, link) {
116 		if (strcmp(hostnqn, host->nqn) == 0) {
117 			return true;
118 		}
119 	}
120 
121 	return false;
122 }
123 
124 int
125 spdk_nvmf_subsystem_start(struct spdk_nvmf_subsystem *subsystem)
126 {
127 	return subsystem->ops->attach(subsystem);
128 }
129 
130 static bool
131 nvmf_subsystem_removable(struct spdk_nvmf_subsystem *subsystem)
132 {
133 	struct spdk_nvmf_session *session;
134 	struct spdk_nvmf_conn	*conn;
135 
136 	if (subsystem->is_removed) {
137 		TAILQ_FOREACH(session, &subsystem->sessions, link) {
138 			TAILQ_FOREACH(conn, &session->connections, link) {
139 				if (!conn->transport->conn_is_idle(conn)) {
140 					return false;
141 				}
142 			}
143 		}
144 		return true;
145 	}
146 	return false;
147 }
148 
149 void
150 spdk_nvmf_subsystem_poll(struct spdk_nvmf_subsystem *subsystem)
151 {
152 	struct spdk_nvmf_session *session;
153 
154 	/* Check the backing physical device for completions. */
155 	if (subsystem->ops->poll_for_completions) {
156 		subsystem->ops->poll_for_completions(subsystem);
157 	}
158 
159 	TAILQ_FOREACH(session, &subsystem->sessions, link) {
160 		/* For each connection in the session, check for completions */
161 		spdk_nvmf_session_poll(session);
162 	}
163 
164 	if (nvmf_subsystem_removable(subsystem)) {
165 		if (subsystem->ops->detach) {
166 			subsystem->ops->detach(subsystem);
167 		}
168 	}
169 }
170 
171 static bool
172 spdk_nvmf_valid_nqn(const char *nqn)
173 {
174 	size_t len;
175 
176 	len = strlen(nqn);
177 	if (len >= SPDK_NVMF_NQN_MAX_LEN) {
178 		SPDK_ERRLOG("Invalid NQN \"%s\": length %zu > max %d\n", nqn, len, SPDK_NVMF_NQN_MAX_LEN - 1);
179 		return false;
180 	}
181 
182 	if (strncmp(nqn, "nqn.", 4) != 0) {
183 		SPDK_ERRLOG("Invalid NQN \"%s\": NQN must begin with \"nqn.\".\n", nqn);
184 		return false;
185 	}
186 
187 	/* yyyy-mm. */
188 	if (!(isdigit(nqn[4]) && isdigit(nqn[5]) && isdigit(nqn[6]) && isdigit(nqn[7]) &&
189 	      nqn[8] == '-' && isdigit(nqn[9]) && isdigit(nqn[10]) && nqn[11] == '.')) {
190 		SPDK_ERRLOG("Invalid date code in NQN \"%s\"\n", nqn);
191 		return false;
192 	}
193 
194 	return true;
195 }
196 
197 struct spdk_nvmf_subsystem *
198 spdk_nvmf_create_subsystem(const char *nqn,
199 			   enum spdk_nvmf_subtype type,
200 			   enum spdk_nvmf_subsystem_mode mode,
201 			   void *cb_ctx,
202 			   spdk_nvmf_subsystem_connect_fn connect_cb,
203 			   spdk_nvmf_subsystem_disconnect_fn disconnect_cb)
204 {
205 	struct spdk_nvmf_subsystem	*subsystem;
206 
207 	if (!spdk_nvmf_valid_nqn(nqn)) {
208 		return NULL;
209 	}
210 
211 	subsystem = calloc(1, sizeof(struct spdk_nvmf_subsystem));
212 	if (subsystem == NULL) {
213 		return NULL;
214 	}
215 
216 	g_nvmf_tgt.current_subsystem_id++;
217 
218 	subsystem->id = g_nvmf_tgt.current_subsystem_id;
219 	subsystem->subtype = type;
220 	subsystem->mode = mode;
221 	subsystem->cb_ctx = cb_ctx;
222 	subsystem->connect_cb = connect_cb;
223 	subsystem->disconnect_cb = disconnect_cb;
224 	snprintf(subsystem->subnqn, sizeof(subsystem->subnqn), "%s", nqn);
225 	TAILQ_INIT(&subsystem->allowed_listeners);
226 	TAILQ_INIT(&subsystem->hosts);
227 	TAILQ_INIT(&subsystem->sessions);
228 
229 	if (type == SPDK_NVMF_SUBTYPE_DISCOVERY) {
230 		subsystem->ops = &spdk_nvmf_discovery_ctrlr_ops;
231 	} else if (mode == NVMF_SUBSYSTEM_MODE_DIRECT) {
232 		subsystem->ops = &spdk_nvmf_direct_ctrlr_ops;
233 		subsystem->dev.direct.outstanding_admin_cmd_count = 0;
234 	} else {
235 		subsystem->ops = &spdk_nvmf_virtual_ctrlr_ops;
236 	}
237 
238 	TAILQ_INSERT_TAIL(&g_nvmf_tgt.subsystems, subsystem, entries);
239 	g_nvmf_tgt.discovery_genctr++;
240 
241 	return subsystem;
242 }
243 
244 void
245 spdk_nvmf_delete_subsystem(struct spdk_nvmf_subsystem *subsystem)
246 {
247 	struct spdk_nvmf_subsystem_allowed_listener	*allowed_listener, *allowed_listener_tmp;
248 	struct spdk_nvmf_host		*host, *host_tmp;
249 	struct spdk_nvmf_session	*session, *session_tmp;
250 
251 	if (!subsystem) {
252 		return;
253 	}
254 
255 	SPDK_TRACELOG(SPDK_TRACE_NVMF, "subsystem is %p\n", subsystem);
256 
257 	TAILQ_FOREACH_SAFE(allowed_listener,
258 			   &subsystem->allowed_listeners, link, allowed_listener_tmp) {
259 		TAILQ_REMOVE(&subsystem->allowed_listeners, allowed_listener, link);
260 
261 		free(allowed_listener);
262 	}
263 
264 	TAILQ_FOREACH_SAFE(host, &subsystem->hosts, link, host_tmp) {
265 		TAILQ_REMOVE(&subsystem->hosts, host, link);
266 		free(host->nqn);
267 		free(host);
268 		subsystem->num_hosts--;
269 	}
270 
271 	TAILQ_FOREACH_SAFE(session, &subsystem->sessions, link, session_tmp) {
272 		spdk_nvmf_session_destruct(session);
273 	}
274 
275 	if (subsystem->ops->detach) {
276 		subsystem->ops->detach(subsystem);
277 	}
278 
279 	TAILQ_REMOVE(&g_nvmf_tgt.subsystems, subsystem, entries);
280 	g_nvmf_tgt.discovery_genctr++;
281 
282 	free(subsystem);
283 }
284 
285 struct spdk_nvmf_listen_addr *
286 spdk_nvmf_tgt_listen(const char *trname, const char *traddr, const char *trsvcid)
287 {
288 	struct spdk_nvmf_listen_addr *listen_addr;
289 	const struct spdk_nvmf_transport *transport;
290 	int rc;
291 
292 	TAILQ_FOREACH(listen_addr, &g_nvmf_tgt.listen_addrs, link) {
293 		if ((strcmp(listen_addr->trname, trname) == 0) &&
294 		    (strcmp(listen_addr->traddr, traddr) == 0) &&
295 		    (strcmp(listen_addr->trsvcid, trsvcid) == 0)) {
296 			return listen_addr;
297 		}
298 	}
299 
300 	transport = spdk_nvmf_transport_get(trname);
301 	if (!transport) {
302 		SPDK_ERRLOG("Unknown transport '%s'\n", trname);
303 		return NULL;
304 	}
305 
306 	listen_addr = spdk_nvmf_listen_addr_create(trname, traddr, trsvcid);
307 	if (!listen_addr) {
308 		return NULL;
309 	}
310 
311 	rc = transport->listen_addr_add(listen_addr);
312 	if (rc < 0) {
313 		spdk_nvmf_listen_addr_cleanup(listen_addr);
314 		SPDK_ERRLOG("Unable to listen on address '%s'\n", traddr);
315 		return NULL;
316 	}
317 
318 	TAILQ_INSERT_HEAD(&g_nvmf_tgt.listen_addrs, listen_addr, link);
319 	g_nvmf_tgt.discovery_genctr++;
320 
321 	return listen_addr;
322 }
323 
324 int
325 spdk_nvmf_subsystem_add_listener(struct spdk_nvmf_subsystem *subsystem,
326 				 struct spdk_nvmf_listen_addr *listen_addr)
327 {
328 	struct spdk_nvmf_subsystem_allowed_listener *allowed_listener;
329 
330 	allowed_listener = calloc(1, sizeof(*allowed_listener));
331 	if (!allowed_listener) {
332 		return -1;
333 	}
334 
335 	allowed_listener->listen_addr = listen_addr;
336 
337 	TAILQ_INSERT_HEAD(&subsystem->allowed_listeners, allowed_listener, link);
338 
339 	return 0;
340 }
341 
342 /*
343  * TODO: this is the whitelist and will be called during connection setup
344  */
345 bool
346 spdk_nvmf_subsystem_listener_allowed(struct spdk_nvmf_subsystem *subsystem,
347 				     struct spdk_nvmf_listen_addr *listen_addr)
348 {
349 	struct spdk_nvmf_subsystem_allowed_listener *allowed_listener;
350 
351 	if (TAILQ_EMPTY(&subsystem->allowed_listeners)) {
352 		return true;
353 	}
354 
355 	TAILQ_FOREACH(allowed_listener, &subsystem->allowed_listeners, link) {
356 		if (allowed_listener->listen_addr == listen_addr) {
357 			return true;
358 		}
359 	}
360 
361 	return false;
362 }
363 
364 int
365 spdk_nvmf_subsystem_add_host(struct spdk_nvmf_subsystem *subsystem, const char *host_nqn)
366 {
367 	struct spdk_nvmf_host *host;
368 
369 	host = calloc(1, sizeof(*host));
370 	if (!host) {
371 		return -1;
372 	}
373 	host->nqn = strdup(host_nqn);
374 	if (!host->nqn) {
375 		free(host);
376 		return -1;
377 	}
378 
379 	TAILQ_INSERT_HEAD(&subsystem->hosts, host, link);
380 	subsystem->num_hosts++;
381 	g_nvmf_tgt.discovery_genctr++;
382 
383 	return 0;
384 }
385 
386 int
387 nvmf_subsystem_add_ctrlr(struct spdk_nvmf_subsystem *subsystem,
388 			 struct spdk_nvme_ctrlr *ctrlr, const struct spdk_pci_addr *pci_addr)
389 {
390 	subsystem->dev.direct.ctrlr = ctrlr;
391 	subsystem->dev.direct.pci_addr = *pci_addr;
392 
393 	return 0;
394 }
395 
396 static void spdk_nvmf_ctrlr_hot_remove(void *remove_ctx)
397 {
398 	struct spdk_nvmf_subsystem *subsystem = (struct spdk_nvmf_subsystem *)remove_ctx;
399 
400 	subsystem->is_removed = true;
401 }
402 
403 int
404 spdk_nvmf_subsystem_add_ns(struct spdk_nvmf_subsystem *subsystem, struct spdk_bdev *bdev)
405 {
406 	int i = 0;
407 
408 	assert(subsystem->mode == NVMF_SUBSYSTEM_MODE_VIRTUAL);
409 	while (i < MAX_VIRTUAL_NAMESPACE && subsystem->dev.virt.ns_list[i]) {
410 		i++;
411 	}
412 	if (i == MAX_VIRTUAL_NAMESPACE) {
413 		SPDK_ERRLOG("spdk_nvmf_subsystem_add_ns() failed\n");
414 		return -1;
415 	}
416 
417 	if (!spdk_bdev_claim(bdev, spdk_nvmf_ctrlr_hot_remove, subsystem)) {
418 		SPDK_ERRLOG("Subsystem %s: bdev %s is already claimed\n",
419 			    subsystem->subnqn, bdev->name);
420 		return -1;
421 	}
422 
423 	subsystem->dev.virt.ns_list[i] = bdev;
424 	subsystem->dev.virt.ns_count++;
425 	return 0;
426 }
427 
428 int
429 spdk_nvmf_subsystem_set_sn(struct spdk_nvmf_subsystem *subsystem, const char *sn)
430 {
431 	if (subsystem->mode != NVMF_SUBSYSTEM_MODE_VIRTUAL) {
432 		return -1;
433 	}
434 
435 	snprintf(subsystem->dev.virt.sn, sizeof(subsystem->dev.virt.sn), "%s", sn);
436 
437 	return 0;
438 }
439 
440 const char *
441 spdk_nvmf_subsystem_get_nqn(struct spdk_nvmf_subsystem *subsystem)
442 {
443 	return subsystem->subnqn;
444 }
445 
446 /* Workaround for astyle formatting bug */
447 typedef enum spdk_nvmf_subtype nvmf_subtype_t;
448 
449 nvmf_subtype_t
450 spdk_nvmf_subsystem_get_type(struct spdk_nvmf_subsystem *subsystem)
451 {
452 	return subsystem->subtype;
453 }
454 
455 /* Workaround for astyle formatting bug */
456 typedef enum spdk_nvmf_subsystem_mode nvmf_mode_t;
457 
458 nvmf_mode_t
459 spdk_nvmf_subsystem_get_mode(struct spdk_nvmf_subsystem *subsystem)
460 {
461 	return subsystem->mode;
462 }
463