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 16 : struct spdk_nvmf_subsystem *discovery_subsystem;
27 16 : 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 0 : }
38 0 : }
39 0 : }
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 96 : }
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 155 : }
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 33 : struct spdk_nvmf_subsystem *subsystem;
102 33 : struct spdk_nvmf_subsystem_listener *listener;
103 33 : struct spdk_nvmf_discovery_log_page_entry *entry;
104 33 : struct spdk_nvmf_discovery_log_page *disc_log;
105 33 : size_t cur_size;
106 33 : 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 65 : for (subsystem = spdk_nvmf_subsystem_get_first(tgt);
119 65 : subsystem != NULL;
120 32 : subsystem = spdk_nvmf_subsystem_get_next(subsystem)) {
121 63 : 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 0 : }
169 :
170 47 : nvmf_transport_listener_discover(listener->transport, listener->trid, entry);
171 :
172 47 : numrec++;
173 47 : }
174 30 : }
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 : size_t new_size = cur_size + sizeof(*entry);
181 50 : void *new_log_page = realloc(disc_log, new_size);
182 :
183 50 : if (new_log_page == NULL) {
184 0 : SPDK_ERRLOG("Discovery log page memory allocation error\n");
185 0 : break;
186 : }
187 :
188 50 : disc_log = new_log_page;
189 50 : cur_size = new_size;
190 :
191 50 : entry = &disc_log->entries[numrec];
192 50 : memcpy(entry, &referral->entry, sizeof(*entry));
193 :
194 50 : numrec++;
195 50 : }
196 :
197 :
198 33 : disc_log->numrec = numrec;
199 33 : disc_log->genctr = tgt->discovery_genctr;
200 33 : *log_page_size = cur_size;
201 :
202 33 : return disc_log;
203 33 : }
204 :
205 : void
206 33 : 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 33 : size_t copy_len = 0;
211 33 : size_t zero_len = 0;
212 33 : struct iovec *tmp;
213 33 : size_t log_page_size = 0;
214 33 : struct spdk_nvmf_discovery_log_page *discovery_log_page;
215 :
216 33 : 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 33 : if (discovery_log_page) {
220 33 : for (tmp = iov; tmp < iov + iovcnt; tmp++) {
221 33 : copy_len = spdk_min(tmp->iov_len, length);
222 33 : copy_len = spdk_min(log_page_size - offset, copy_len);
223 :
224 33 : memcpy(tmp->iov_base, (char *)discovery_log_page + offset, copy_len);
225 :
226 33 : offset += copy_len;
227 33 : length -= copy_len;
228 33 : zero_len = tmp->iov_len - copy_len;
229 33 : if (log_page_size <= offset || length == 0) {
230 33 : break;
231 : }
232 0 : }
233 : /* Zero out the rest of the payload */
234 33 : if (zero_len) {
235 32 : memset((char *)tmp->iov_base + copy_len, 0, zero_len);
236 32 : }
237 :
238 33 : for (++tmp; tmp < iov + iovcnt; tmp++) {
239 0 : memset((char *)tmp->iov_base, 0, tmp->iov_len);
240 0 : }
241 :
242 33 : free(discovery_log_page);
243 33 : }
244 33 : }
|