xref: /spdk/lib/nvmf/subsystem.c (revision 0cb95227812ac0ccadad31d05cb0f8c7b7e86ab9)
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 <ctype.h>
35 
36 #include "controller.h"
37 #include "nvmf_internal.h"
38 #include "session.h"
39 #include "subsystem.h"
40 #include "transport.h"
41 #include "spdk/log.h"
42 #include "spdk/string.h"
43 #include "spdk/trace.h"
44 #include "spdk/nvmf_spec.h"
45 
46 static TAILQ_HEAD(, spdk_nvmf_subsystem) g_subsystems = TAILQ_HEAD_INITIALIZER(g_subsystems);
47 
48 struct spdk_nvmf_subsystem *
49 nvmf_find_subsystem(const char *subnqn, const char *hostnqn)
50 {
51 	struct spdk_nvmf_subsystem	*subsystem;
52 	struct spdk_nvmf_host		*host;
53 
54 	if (!subnqn || !hostnqn) {
55 		return NULL;
56 	}
57 
58 	TAILQ_FOREACH(subsystem, &g_subsystems, entries) {
59 		if (strcasecmp(subnqn, subsystem->subnqn) == 0) {
60 			if (subsystem->num_hosts == 0) {
61 				/* No hosts means any host can connect */
62 				return subsystem;
63 			}
64 
65 			TAILQ_FOREACH(host, &subsystem->hosts, link) {
66 				if (strcasecmp(hostnqn, host->nqn) == 0) {
67 					return subsystem;
68 				}
69 			}
70 		}
71 	}
72 
73 	return NULL;
74 }
75 
76 static void
77 spdk_nvmf_subsystem_poller(void *arg)
78 {
79 	struct spdk_nvmf_subsystem *subsystem = arg;
80 	struct nvmf_session *session = subsystem->session;
81 
82 	if (!session) {
83 		/* No active connections, so just return */
84 		return;
85 	}
86 
87 	/* For NVMe subsystems, check the backing physical device for completions. */
88 	if (subsystem->subtype == SPDK_NVMF_SUBTYPE_NVME) {
89 		spdk_nvme_ctrlr_process_admin_completions(subsystem->ctrlr);
90 		spdk_nvme_qpair_process_completions(subsystem->io_qpair, 0);
91 	}
92 
93 	/* For each connection in the session, check for RDMA completions */
94 	spdk_nvmf_session_poll(session);
95 }
96 
97 struct spdk_nvmf_subsystem *
98 nvmf_create_subsystem(int num, const char *name,
99 		      enum spdk_nvmf_subtype subtype,
100 		      uint32_t lcore)
101 {
102 	struct spdk_nvmf_subsystem	*subsystem;
103 
104 	subsystem = calloc(1, sizeof(struct spdk_nvmf_subsystem));
105 	if (subsystem == NULL) {
106 		return NULL;
107 	}
108 
109 	SPDK_TRACELOG(SPDK_TRACE_NVMF, "nvmf_create_subsystem: allocated subsystem %p\n", subsystem);
110 
111 	subsystem->num = num;
112 	subsystem->subtype = subtype;
113 	snprintf(subsystem->subnqn, sizeof(subsystem->subnqn), "%s", name);
114 	TAILQ_INIT(&subsystem->listen_addrs);
115 	TAILQ_INIT(&subsystem->hosts);
116 
117 	subsystem->poller.fn = spdk_nvmf_subsystem_poller;
118 	subsystem->poller.arg = subsystem;
119 	spdk_poller_register(&subsystem->poller, lcore, NULL);
120 
121 	TAILQ_INSERT_HEAD(&g_subsystems, subsystem, entries);
122 
123 	return subsystem;
124 }
125 
126 int
127 nvmf_delete_subsystem(struct spdk_nvmf_subsystem *subsystem)
128 {
129 	struct spdk_nvmf_listen_addr	*listen_addr, *listen_addr_tmp;
130 	struct spdk_nvmf_host		*host, *host_tmp;
131 
132 	if (subsystem == NULL) {
133 		SPDK_TRACELOG(SPDK_TRACE_NVMF,
134 			      "nvmf_delete_subsystem: there is no subsystem\n");
135 		return 0;
136 	}
137 
138 	TAILQ_FOREACH_SAFE(listen_addr, &subsystem->listen_addrs, link, listen_addr_tmp) {
139 		TAILQ_REMOVE(&subsystem->listen_addrs, listen_addr, link);
140 		free(listen_addr->traddr);
141 		free(listen_addr->trsvc);
142 		free(listen_addr);
143 		subsystem->num_listen_addrs--;
144 	}
145 
146 	TAILQ_FOREACH_SAFE(host, &subsystem->hosts, link, host_tmp) {
147 		TAILQ_REMOVE(&subsystem->hosts, host, link);
148 		free(host->nqn);
149 		free(host);
150 		subsystem->num_hosts--;
151 	}
152 
153 	if (subsystem->session) {
154 		spdk_nvmf_session_destruct(subsystem->session);
155 	}
156 
157 	TAILQ_REMOVE(&g_subsystems, subsystem, entries);
158 
159 	free(subsystem);
160 	return 0;
161 }
162 
163 int
164 spdk_nvmf_subsystem_add_listener(struct spdk_nvmf_subsystem *subsystem,
165 				 const struct spdk_nvmf_transport *transport,
166 				 char *traddr, char *trsvc)
167 {
168 	struct spdk_nvmf_listen_addr *listen_addr;
169 
170 	listen_addr = calloc(1, sizeof(*listen_addr));
171 	listen_addr->traddr = strdup(traddr);
172 	listen_addr->trsvc = strdup(trsvc);
173 	listen_addr->transport = transport;
174 
175 	TAILQ_INSERT_HEAD(&subsystem->listen_addrs, listen_addr, link);
176 	subsystem->num_listen_addrs++;
177 
178 	return 0;
179 }
180 
181 int
182 spdk_nvmf_subsystem_add_host(struct spdk_nvmf_subsystem *subsystem, char *host_nqn)
183 {
184 	struct spdk_nvmf_host *host;
185 
186 	host = calloc(1, sizeof(*host));
187 	host->nqn = strdup(host_nqn);
188 
189 	TAILQ_INSERT_HEAD(&subsystem->hosts, host, link);
190 	subsystem->num_hosts++;
191 
192 	return 0;
193 }
194 
195 int
196 nvmf_subsystem_add_ctrlr(struct spdk_nvmf_subsystem *subsystem,
197 			 struct spdk_nvme_ctrlr *ctrlr)
198 {
199 	subsystem->ctrlr = ctrlr;
200 
201 	/* Assume that all I/O will be handled on one thread for now */
202 	subsystem->io_qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, 0);
203 	if (subsystem->io_qpair == NULL) {
204 		SPDK_ERRLOG("spdk_nvme_ctrlr_alloc_io_qpair() failed\n");
205 		return -1;
206 	}
207 
208 	return 0;
209 }
210 
211 int
212 spdk_add_nvmf_discovery_subsystem(void)
213 {
214 	struct spdk_nvmf_subsystem *subsystem;
215 	char *name;
216 
217 	name = strdup(SPDK_NVMF_DISCOVERY_NQN);
218 	if (name == NULL) {
219 		SPDK_ERRLOG("strdup ss_group->name error\n");
220 		return -1;
221 	}
222 
223 	subsystem = nvmf_create_subsystem(0, name, SPDK_NVMF_SUBTYPE_DISCOVERY, rte_get_master_lcore());
224 	if (subsystem == NULL) {
225 		SPDK_ERRLOG("Failed creating discovery nvmf library subsystem\n");
226 		free(name);
227 		return -1;
228 	}
229 
230 	free(name);
231 
232 	return 0;
233 }
234 
235 void
236 spdk_format_discovery_log(struct spdk_nvmf_discovery_log_page *disc_log, uint32_t length)
237 {
238 	int numrec = 0;
239 	struct spdk_nvmf_subsystem *subsystem;
240 	struct spdk_nvmf_listen_addr *listen_addr;
241 	struct spdk_nvmf_discovery_log_page_entry *entry;
242 
243 	TAILQ_FOREACH(subsystem, &g_subsystems, entries) {
244 		if (subsystem->subtype == SPDK_NVMF_SUBTYPE_DISCOVERY) {
245 			continue;
246 		}
247 
248 		TAILQ_FOREACH(listen_addr, &subsystem->listen_addrs, link) {
249 			/* include the discovery log entry */
250 			if (length > sizeof(struct spdk_nvmf_discovery_log_page)) {
251 				if (sizeof(struct spdk_nvmf_discovery_log_page) + (numrec + 1) * sizeof(
252 					    struct spdk_nvmf_discovery_log_page_entry) > length) {
253 					break;
254 				}
255 				entry = &disc_log->entries[numrec];
256 				entry->portid = subsystem->num;
257 				entry->cntlid = 0xffff;
258 				entry->subtype = subsystem->subtype;
259 				snprintf(entry->subnqn, sizeof(entry->subnqn), "%s", subsystem->subnqn);
260 
261 				listen_addr->transport->listen_addr_discover(listen_addr, entry);
262 			}
263 			numrec++;
264 		}
265 	}
266 
267 	disc_log->numrec = numrec;
268 }
269 
270 int
271 spdk_shutdown_nvmf_subsystems(void)
272 {
273 	struct spdk_nvmf_subsystem *subsystem;
274 
275 	while (!TAILQ_EMPTY(&g_subsystems)) {
276 		subsystem = TAILQ_FIRST(&g_subsystems);
277 		TAILQ_REMOVE(&g_subsystems, subsystem, entries);
278 		nvmf_delete_subsystem(subsystem);
279 	}
280 
281 	return 0;
282 }
283