Line data Source code
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 16 : 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 16 : tgt->discovery_genctr++;
30 16 : discovery_subsystem = spdk_nvmf_tgt_find_subsystem(tgt, SPDK_NVMF_DISCOVERY_NQN);
31 :
32 16 : if (discovery_subsystem) {
33 : /** There is a change in discovery log for hosts with given hostnqn */
34 0 : TAILQ_FOREACH(ctrlr, &discovery_subsystem->ctrlrs, link) {
35 0 : if (hostnqn == NULL || strcmp(hostnqn, ctrlr->hostnqn) == 0) {
36 0 : spdk_thread_send_msg(ctrlr->thread, nvmf_ctrlr_async_event_discovery_log_change_notice, ctrlr);
37 : }
38 : }
39 : }
40 16 : }
41 :
42 : static bool
43 96 : nvmf_discovery_compare_trtype(const struct spdk_nvme_transport_id *trid1,
44 : const struct spdk_nvme_transport_id *trid2)
45 : {
46 96 : if (trid1->trtype == SPDK_NVME_TRANSPORT_CUSTOM) {
47 0 : return strcasecmp(trid1->trstring, trid2->trstring) == 0;
48 : } else {
49 96 : return trid1->trtype == trid2->trtype;
50 : }
51 : }
52 :
53 : static bool
54 78 : nvmf_discovery_compare_tr_addr(const struct spdk_nvme_transport_id *trid1,
55 : const struct spdk_nvme_transport_id *trid2)
56 : {
57 78 : return trid1->adrfam == trid2->adrfam && strcasecmp(trid1->traddr, trid2->traddr) == 0;
58 : }
59 :
60 : static bool
61 40 : nvmf_discovery_compare_tr_svcid(const struct spdk_nvme_transport_id *trid1,
62 : const struct spdk_nvme_transport_id *trid2)
63 : {
64 40 : return strcasecmp(trid1->trsvcid, trid2->trsvcid) == 0;
65 : }
66 :
67 : static bool
68 155 : 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 155 : if ((filter & SPDK_NVMF_TGT_DISCOVERY_MATCH_TRANSPORT_TYPE) != 0 &&
73 96 : !nvmf_discovery_compare_trtype(trid1, trid2)) {
74 48 : SPDK_DEBUGLOG(nvmf, "transport type mismatch between %d (%s) and %d (%s)\n",
75 : trid1->trtype, trid1->trstring, trid2->trtype, trid2->trstring);
76 48 : return false;
77 : }
78 :
79 107 : if ((filter & SPDK_NVMF_TGT_DISCOVERY_MATCH_TRANSPORT_ADDRESS) != 0 &&
80 78 : !nvmf_discovery_compare_tr_addr(trid1, trid2)) {
81 38 : SPDK_DEBUGLOG(nvmf, "transport addr mismatch between %s and %s\n",
82 : trid1->traddr, trid2->traddr);
83 38 : return false;
84 : }
85 :
86 69 : if ((filter & SPDK_NVMF_TGT_DISCOVERY_MATCH_TRANSPORT_SVCID) != 0 &&
87 40 : !nvmf_discovery_compare_tr_svcid(trid1, trid2)) {
88 22 : SPDK_DEBUGLOG(nvmf, "transport svcid mismatch between %s and %s\n",
89 : trid1->trsvcid, trid2->trsvcid);
90 22 : return false;
91 : }
92 :
93 47 : return true;
94 : }
95 :
96 : static struct spdk_nvmf_discovery_log_page *
97 33 : 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 33 : 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 33 : SPDK_DEBUGLOG(nvmf, "Generating log page for genctr %" PRIu64 "\n",
109 : tgt->discovery_genctr);
110 :
111 33 : cur_size = sizeof(struct spdk_nvmf_discovery_log_page);
112 33 : disc_log = calloc(1, cur_size);
113 33 : if (disc_log == NULL) {
114 0 : SPDK_ERRLOG("Discovery log page memory allocation error\n");
115 0 : return NULL;
116 : }
117 :
118 33 : for (subsystem = spdk_nvmf_subsystem_get_first(tgt);
119 65 : subsystem != NULL;
120 32 : subsystem = spdk_nvmf_subsystem_get_next(subsystem)) {
121 32 : if ((subsystem->state == SPDK_NVMF_SUBSYSTEM_INACTIVE) ||
122 31 : (subsystem->state == SPDK_NVMF_SUBSYSTEM_DEACTIVATING)) {
123 1 : continue;
124 : }
125 :
126 31 : if (!spdk_nvmf_subsystem_host_allowed(subsystem, hostnqn)) {
127 1 : continue;
128 : }
129 :
130 185 : for (listener = spdk_nvmf_subsystem_get_first_listener(subsystem); listener != NULL;
131 155 : listener = spdk_nvmf_subsystem_get_next_listener(subsystem, listener)) {
132 :
133 155 : if (!nvmf_discovery_compare_trid(tgt->discovery_filter, listener->trid, cmd_source_trid)) {
134 108 : continue;
135 : }
136 :
137 47 : SPDK_DEBUGLOG(nvmf, "listener %s:%s trtype %s\n", listener->trid->traddr, listener->trid->trsvcid,
138 : listener->trid->trstring);
139 :
140 47 : size_t new_size = cur_size + sizeof(*entry);
141 47 : void *new_log_page = realloc(disc_log, new_size);
142 :
143 47 : if (new_log_page == NULL) {
144 0 : SPDK_ERRLOG("Discovery log page memory allocation error\n");
145 0 : break;
146 : }
147 :
148 47 : disc_log = new_log_page;
149 47 : cur_size = new_size;
150 :
151 47 : entry = &disc_log->entries[numrec];
152 47 : memset(entry, 0, sizeof(*entry));
153 47 : entry->portid = listener->id;
154 47 : entry->cntlid = 0xffff;
155 47 : entry->asqsz = listener->transport->opts.max_aq_depth;
156 47 : entry->subtype = subsystem->subtype;
157 47 : snprintf(entry->subnqn, sizeof(entry->subnqn), "%s", subsystem->subnqn);
158 :
159 47 : 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 0 : 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 0 : entry->eflags |= SPDK_NVMF_DISCOVERY_LOG_EFLAGS_EPCSD;
168 : }
169 :
170 47 : nvmf_transport_listener_discover(listener->transport, listener->trid, entry);
171 :
172 47 : numrec++;
173 : }
174 : }
175 :
176 83 : TAILQ_FOREACH(referral, &tgt->referrals, link) {
177 50 : SPDK_DEBUGLOG(nvmf, "referral %s:%s trtype %s\n", referral->trid.traddr, referral->trid.trsvcid,
178 : referral->trid.trstring);
179 :
180 50 : if (!spdk_nvmf_referral_host_allowed(referral, hostnqn)) {
181 :
182 0 : continue;
183 : }
184 50 : size_t new_size = cur_size + sizeof(*entry);
185 50 : void *new_log_page = realloc(disc_log, new_size);
186 :
187 50 : if (new_log_page == NULL) {
188 0 : SPDK_ERRLOG("Discovery log page memory allocation error\n");
189 0 : break;
190 : }
191 :
192 50 : disc_log = new_log_page;
193 50 : cur_size = new_size;
194 :
195 50 : entry = &disc_log->entries[numrec];
196 50 : memcpy(entry, &referral->entry, sizeof(*entry));
197 :
198 50 : numrec++;
199 : }
200 :
201 :
202 33 : disc_log->numrec = numrec;
203 33 : disc_log->genctr = tgt->discovery_genctr;
204 33 : *log_page_size = cur_size;
205 :
206 33 : return disc_log;
207 : }
208 :
209 : void
210 33 : nvmf_get_discovery_log_page(struct spdk_nvmf_tgt *tgt, const char *hostnqn, struct iovec *iov,
211 : uint32_t iovcnt, uint64_t offset, uint32_t length,
212 : struct spdk_nvme_transport_id *cmd_source_trid)
213 : {
214 33 : size_t copy_len = 0;
215 33 : size_t zero_len = 0;
216 : struct iovec *tmp;
217 33 : size_t log_page_size = 0;
218 : struct spdk_nvmf_discovery_log_page *discovery_log_page;
219 :
220 33 : discovery_log_page = nvmf_generate_discovery_log(tgt, hostnqn, &log_page_size, cmd_source_trid);
221 :
222 : /* Copy the valid part of the discovery log page, if any */
223 33 : if (discovery_log_page) {
224 33 : for (tmp = iov; tmp < iov + iovcnt; tmp++) {
225 33 : copy_len = spdk_min(tmp->iov_len, length);
226 33 : copy_len = spdk_min(log_page_size - offset, copy_len);
227 :
228 33 : memcpy(tmp->iov_base, (char *)discovery_log_page + offset, copy_len);
229 :
230 33 : offset += copy_len;
231 33 : length -= copy_len;
232 33 : zero_len = tmp->iov_len - copy_len;
233 33 : if (log_page_size <= offset || length == 0) {
234 : break;
235 : }
236 : }
237 : /* Zero out the rest of the payload */
238 33 : if (zero_len) {
239 32 : memset((char *)tmp->iov_base + copy_len, 0, zero_len);
240 : }
241 :
242 33 : for (++tmp; tmp < iov + iovcnt; tmp++) {
243 0 : memset((char *)tmp->iov_base, 0, tmp->iov_len);
244 : }
245 :
246 33 : free(discovery_log_page);
247 : }
248 33 : }
|