xref: /spdk/lib/nvmf/ctrlr_discovery.c (revision 5a8c76d991809d2b09d0d68cf3a81951410d5bff)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2017 Intel Corporation.
3  *   All rights reserved.
4  *   Copyright (c) 2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
5  */
6 
7 /*
8  * NVMe over Fabrics discovery service
9  */
10 
11 #include "spdk/stdinc.h"
12 
13 #include "nvmf_internal.h"
14 #include "transport.h"
15 
16 #include "spdk/string.h"
17 #include "spdk/trace.h"
18 #include "spdk/nvmf_spec.h"
19 #include "spdk_internal/assert.h"
20 
21 #include "spdk/log.h"
22 
23 void
24 spdk_nvmf_send_discovery_log_notice(struct spdk_nvmf_tgt *tgt, const char *hostnqn)
25 {
26 	struct spdk_nvmf_subsystem *discovery_subsystem;
27 	struct spdk_nvmf_ctrlr *ctrlr;
28 
29 	tgt->discovery_genctr++;
30 	discovery_subsystem = spdk_nvmf_tgt_find_subsystem(tgt, SPDK_NVMF_DISCOVERY_NQN);
31 
32 	if (discovery_subsystem) {
33 		/** There is a change in discovery log for hosts with given hostnqn */
34 		TAILQ_FOREACH(ctrlr, &discovery_subsystem->ctrlrs, link) {
35 			if (hostnqn == NULL || strcmp(hostnqn, ctrlr->hostnqn) == 0) {
36 				spdk_thread_send_msg(ctrlr->thread, nvmf_ctrlr_async_event_discovery_log_change_notice, ctrlr);
37 			}
38 		}
39 	}
40 }
41 
42 static bool
43 nvmf_discovery_compare_trtype(const struct spdk_nvme_transport_id *trid1,
44 			      const struct spdk_nvme_transport_id *trid2)
45 {
46 	if (trid1->trtype == SPDK_NVME_TRANSPORT_CUSTOM) {
47 		return strcasecmp(trid1->trstring, trid2->trstring) == 0;
48 	} else {
49 		return trid1->trtype == trid2->trtype;
50 	}
51 }
52 
53 static bool
54 nvmf_discovery_compare_tr_addr(const struct spdk_nvme_transport_id *trid1,
55 			       const struct spdk_nvme_transport_id *trid2)
56 {
57 	return trid1->adrfam == trid2->adrfam && strcasecmp(trid1->traddr, trid2->traddr) == 0;
58 }
59 
60 static bool
61 nvmf_discovery_compare_tr_svcid(const struct spdk_nvme_transport_id *trid1,
62 				const struct spdk_nvme_transport_id *trid2)
63 {
64 	return strcasecmp(trid1->trsvcid, trid2->trsvcid) == 0;
65 }
66 
67 static bool
68 nvmf_discovery_compare_trid(uint32_t filter,
69 			    const struct spdk_nvme_transport_id *trid1,
70 			    const struct spdk_nvme_transport_id *trid2)
71 {
72 	if ((filter & SPDK_NVMF_TGT_DISCOVERY_MATCH_TRANSPORT_TYPE) != 0 &&
73 	    !nvmf_discovery_compare_trtype(trid1, trid2)) {
74 		SPDK_DEBUGLOG(nvmf, "transport type mismatch between %d (%s) and %d (%s)\n",
75 			      trid1->trtype, trid1->trstring, trid2->trtype, trid2->trstring);
76 		return false;
77 	}
78 
79 	if ((filter & SPDK_NVMF_TGT_DISCOVERY_MATCH_TRANSPORT_ADDRESS) != 0 &&
80 	    !nvmf_discovery_compare_tr_addr(trid1, trid2)) {
81 		SPDK_DEBUGLOG(nvmf, "transport addr mismatch between %s and %s\n",
82 			      trid1->traddr, trid2->traddr);
83 		return false;
84 	}
85 
86 	if ((filter & SPDK_NVMF_TGT_DISCOVERY_MATCH_TRANSPORT_SVCID) != 0 &&
87 	    !nvmf_discovery_compare_tr_svcid(trid1, trid2)) {
88 		SPDK_DEBUGLOG(nvmf, "transport svcid mismatch between %s and %s\n",
89 			      trid1->trsvcid, trid2->trsvcid);
90 		return false;
91 	}
92 
93 	return true;
94 }
95 
96 static struct spdk_nvmf_discovery_log_page *
97 nvmf_generate_discovery_log(struct spdk_nvmf_tgt *tgt, const char *hostnqn, size_t *log_page_size,
98 			    struct spdk_nvme_transport_id *cmd_source_trid)
99 {
100 	uint64_t numrec = 0;
101 	struct spdk_nvmf_subsystem *subsystem;
102 	struct spdk_nvmf_subsystem_listener *listener;
103 	struct spdk_nvmf_discovery_log_page_entry *entry;
104 	struct spdk_nvmf_discovery_log_page *disc_log;
105 	size_t cur_size;
106 	struct spdk_nvmf_referral *referral;
107 
108 	SPDK_DEBUGLOG(nvmf, "Generating log page for genctr %" PRIu64 "\n",
109 		      tgt->discovery_genctr);
110 
111 	cur_size = sizeof(struct spdk_nvmf_discovery_log_page);
112 	disc_log = calloc(1, cur_size);
113 	if (disc_log == NULL) {
114 		SPDK_ERRLOG("Discovery log page memory allocation error\n");
115 		return NULL;
116 	}
117 
118 	for (subsystem = spdk_nvmf_subsystem_get_first(tgt);
119 	     subsystem != NULL;
120 	     subsystem = spdk_nvmf_subsystem_get_next(subsystem)) {
121 		if ((subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE) ||
122 		    (subsystem->state == SPDK_NVMF_SUBSYSTEM_DEACTIVATING)) {
123 			continue;
124 		}
125 
126 		if (!spdk_nvmf_subsystem_host_allowed(subsystem, hostnqn)) {
127 			continue;
128 		}
129 
130 		for (listener = spdk_nvmf_subsystem_get_first_listener(subsystem); listener != NULL;
131 		     listener = spdk_nvmf_subsystem_get_next_listener(subsystem, listener)) {
132 
133 			if (!nvmf_discovery_compare_trid(tgt->discovery_filter, listener->trid, cmd_source_trid)) {
134 				continue;
135 			}
136 
137 			SPDK_DEBUGLOG(nvmf, "listener %s:%s trtype %s\n", listener->trid->traddr, listener->trid->trsvcid,
138 				      listener->trid->trstring);
139 
140 			size_t new_size = cur_size + sizeof(*entry);
141 			void *new_log_page = realloc(disc_log, new_size);
142 
143 			if (new_log_page == NULL) {
144 				SPDK_ERRLOG("Discovery log page memory allocation error\n");
145 				break;
146 			}
147 
148 			disc_log = new_log_page;
149 			cur_size = new_size;
150 
151 			entry = &disc_log->entries[numrec];
152 			memset(entry, 0, sizeof(*entry));
153 			entry->portid = listener->id;
154 			entry->cntlid = 0xffff;
155 			entry->asqsz = listener->transport->opts.max_aq_depth;
156 			entry->subtype = subsystem->subtype;
157 			snprintf(entry->subnqn, sizeof(entry->subnqn), "%s", subsystem->subnqn);
158 
159 			if (subsystem->subtype == SPDK_NVMF_SUBTYPE_DISCOVERY_CURRENT) {
160 				/* Each listener in the Current Discovery Subsystem provides access
161 				 * to the same Discovery Log Pages, so set the Duplicate Returned
162 				 * Information flag. */
163 				entry->eflags |= SPDK_NVMF_DISCOVERY_LOG_EFLAGS_DUPRETINFO;
164 				/* Since the SPDK NVMeoF target supports Asynchronous Event Request
165 				 * and Keep Alive commands, set the Explicit Persistent Connection
166 				 * Support for Discovery flag. */
167 				entry->eflags |= SPDK_NVMF_DISCOVERY_LOG_EFLAGS_EPCSD;
168 			}
169 
170 			nvmf_transport_listener_discover(listener->transport, listener->trid, entry);
171 
172 			numrec++;
173 		}
174 	}
175 
176 	TAILQ_FOREACH(referral, &tgt->referrals, link) {
177 		SPDK_DEBUGLOG(nvmf, "referral %s:%s trtype %s\n", referral->trid.traddr, referral->trid.trsvcid,
178 			      referral->trid.trstring);
179 
180 		size_t new_size = cur_size + sizeof(*entry);
181 		void *new_log_page = realloc(disc_log, new_size);
182 
183 		if (new_log_page == NULL) {
184 			SPDK_ERRLOG("Discovery log page memory allocation error\n");
185 			break;
186 		}
187 
188 		disc_log = new_log_page;
189 		cur_size = new_size;
190 
191 		entry = &disc_log->entries[numrec];
192 		memcpy(entry, &referral->entry, sizeof(*entry));
193 
194 		numrec++;
195 	}
196 
197 
198 	disc_log->numrec = numrec;
199 	disc_log->genctr = tgt->discovery_genctr;
200 	*log_page_size = cur_size;
201 
202 	return disc_log;
203 }
204 
205 void
206 nvmf_get_discovery_log_page(struct spdk_nvmf_tgt *tgt, const char *hostnqn, struct iovec *iov,
207 			    uint32_t iovcnt, uint64_t offset, uint32_t length,
208 			    struct spdk_nvme_transport_id *cmd_source_trid)
209 {
210 	size_t copy_len = 0;
211 	size_t zero_len = 0;
212 	struct iovec *tmp;
213 	size_t log_page_size = 0;
214 	struct spdk_nvmf_discovery_log_page *discovery_log_page;
215 
216 	discovery_log_page = nvmf_generate_discovery_log(tgt, hostnqn, &log_page_size, cmd_source_trid);
217 
218 	/* Copy the valid part of the discovery log page, if any */
219 	if (discovery_log_page) {
220 		for (tmp = iov; tmp < iov + iovcnt; tmp++) {
221 			copy_len = spdk_min(tmp->iov_len, length);
222 			copy_len = spdk_min(log_page_size - offset, copy_len);
223 
224 			memcpy(tmp->iov_base, (char *)discovery_log_page + offset, copy_len);
225 
226 			offset += copy_len;
227 			length -= copy_len;
228 			zero_len = tmp->iov_len - copy_len;
229 			if (log_page_size <= offset || length == 0) {
230 				break;
231 			}
232 		}
233 		/* Zero out the rest of the payload */
234 		if (zero_len) {
235 			memset((char *)tmp->iov_base + copy_len, 0, zero_len);
236 		}
237 
238 		for (++tmp; tmp < iov + iovcnt; tmp++) {
239 			memset((char *)tmp->iov_base, 0, tmp->iov_len);
240 		}
241 
242 		free(discovery_log_page);
243 	}
244 }
245