]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | /* |
2 | * BSD LICENSE | |
3 | * | |
4 | * Copyright (c) 2018-2019 Broadcom. All Rights Reserved. | |
5 | * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. | |
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/env.h" | |
35 | #include "spdk/assert.h" | |
36 | #include "spdk/nvmf.h" | |
37 | #include "spdk/nvmf_spec.h" | |
38 | #include "spdk/string.h" | |
39 | #include "spdk/trace.h" | |
40 | #include "spdk/util.h" | |
41 | #include "spdk/endian.h" | |
42 | #include "spdk_internal/log.h" | |
43 | #include "nvmf_internal.h" | |
44 | #include "transport.h" | |
45 | ||
46 | #include "nvmf_fc.h" | |
47 | #include "fc_lld.h" | |
48 | ||
49 | /* set to 1 to send ls disconnect in response to ls disconnect from host (per standard) */ | |
50 | #define NVMF_FC_LS_SEND_LS_DISCONNECT 0 | |
51 | ||
52 | /* Validation Error indexes into the string table below */ | |
53 | enum { | |
54 | VERR_NO_ERROR = 0, | |
55 | VERR_CR_ASSOC_LEN = 1, | |
56 | VERR_CR_ASSOC_RQST_LEN = 2, | |
57 | VERR_CR_ASSOC_CMD = 3, | |
58 | VERR_CR_ASSOC_CMD_LEN = 4, | |
59 | VERR_ERSP_RATIO = 5, | |
60 | VERR_ASSOC_ALLOC_FAIL = 6, | |
61 | VERR_CONN_ALLOC_FAIL = 7, | |
62 | VERR_CR_CONN_LEN = 8, | |
63 | VERR_CR_CONN_RQST_LEN = 9, | |
64 | VERR_ASSOC_ID = 10, | |
65 | VERR_ASSOC_ID_LEN = 11, | |
66 | VERR_NO_ASSOC = 12, | |
67 | VERR_CONN_ID = 13, | |
68 | VERR_CONN_ID_LEN = 14, | |
69 | VERR_NO_CONN = 15, | |
70 | VERR_CR_CONN_CMD = 16, | |
71 | VERR_CR_CONN_CMD_LEN = 17, | |
72 | VERR_DISCONN_LEN = 18, | |
73 | VERR_DISCONN_RQST_LEN = 19, | |
74 | VERR_DISCONN_CMD = 20, | |
75 | VERR_DISCONN_CMD_LEN = 21, | |
76 | VERR_DISCONN_SCOPE = 22, | |
77 | VERR_RS_LEN = 23, | |
78 | VERR_RS_RQST_LEN = 24, | |
79 | VERR_RS_CMD = 25, | |
80 | VERR_RS_CMD_LEN = 26, | |
81 | VERR_RS_RCTL = 27, | |
82 | VERR_RS_RO = 28, | |
83 | VERR_CONN_TOO_MANY = 29, | |
84 | VERR_SUBNQN = 30, | |
85 | VERR_HOSTNQN = 31, | |
86 | VERR_SQSIZE = 32, | |
87 | VERR_NO_RPORT = 33, | |
88 | VERR_SUBLISTENER = 34, | |
89 | }; | |
90 | ||
91 | static char *validation_errors[] = { | |
92 | "OK", | |
93 | "Bad CR_ASSOC Length", | |
94 | "Bad CR_ASSOC Rqst Length", | |
95 | "Not CR_ASSOC Cmd", | |
96 | "Bad CR_ASSOC Cmd Length", | |
97 | "Bad Ersp Ratio", | |
98 | "Association Allocation Failed", | |
99 | "Queue Allocation Failed", | |
100 | "Bad CR_CONN Length", | |
101 | "Bad CR_CONN Rqst Length", | |
102 | "Not Association ID", | |
103 | "Bad Association ID Length", | |
104 | "No Association", | |
105 | "Not Connection ID", | |
106 | "Bad Connection ID Length", | |
107 | "No Connection", | |
108 | "Not CR_CONN Cmd", | |
109 | "Bad CR_CONN Cmd Length", | |
110 | "Bad DISCONN Length", | |
111 | "Bad DISCONN Rqst Length", | |
112 | "Not DISCONN Cmd", | |
113 | "Bad DISCONN Cmd Length", | |
114 | "Bad Disconnect Scope", | |
115 | "Bad RS Length", | |
116 | "Bad RS Rqst Length", | |
117 | "Not RS Cmd", | |
118 | "Bad RS Cmd Length", | |
119 | "Bad RS R_CTL", | |
120 | "Bad RS Relative Offset", | |
121 | "Too many connections for association", | |
122 | "Invalid subnqn or subsystem not found", | |
123 | "Invalid hostnqn or subsystem doesn't allow host", | |
124 | "SQ size = 0 or too big", | |
125 | "No Remote Port", | |
126 | "Bad Subsystem Port", | |
127 | }; | |
128 | ||
129 | static inline void | |
130 | nvmf_fc_add_assoc_to_tgt_port(struct spdk_nvmf_fc_nport *tgtport, | |
131 | struct spdk_nvmf_fc_association *assoc, | |
132 | struct spdk_nvmf_fc_remote_port_info *rport); | |
133 | ||
134 | static inline FCNVME_BE32 cpu_to_be32(uint32_t in) | |
135 | { | |
136 | uint32_t t; | |
137 | ||
138 | to_be32(&t, in); | |
139 | return (FCNVME_BE32)t; | |
140 | } | |
141 | ||
142 | static inline FCNVME_BE32 nvmf_fc_lsdesc_len(size_t sz) | |
143 | { | |
144 | uint32_t t; | |
145 | ||
146 | to_be32(&t, sz - (2 * sizeof(uint32_t))); | |
147 | return (FCNVME_BE32)t; | |
148 | } | |
149 | ||
150 | static void | |
151 | nvmf_fc_ls_format_rsp_hdr(void *buf, uint8_t ls_cmd, uint32_t desc_len, | |
152 | uint8_t rqst_ls_cmd) | |
153 | { | |
154 | struct spdk_nvmf_fc_ls_acc_hdr *acc_hdr = buf; | |
155 | ||
156 | acc_hdr->w0.ls_cmd = ls_cmd; | |
157 | acc_hdr->desc_list_len = desc_len; | |
158 | to_be32(&acc_hdr->rqst.desc_tag, FCNVME_LSDESC_RQST); | |
159 | acc_hdr->rqst.desc_len = | |
160 | nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_rqst)); | |
161 | acc_hdr->rqst.w0.ls_cmd = rqst_ls_cmd; | |
162 | } | |
163 | ||
164 | static int | |
165 | nvmf_fc_ls_format_rjt(void *buf, uint16_t buflen, uint8_t ls_cmd, | |
166 | uint8_t reason, uint8_t explanation, uint8_t vendor) | |
167 | { | |
168 | struct spdk_nvmf_fc_ls_rjt *rjt = buf; | |
169 | ||
170 | bzero(buf, sizeof(struct spdk_nvmf_fc_ls_rjt)); | |
171 | nvmf_fc_ls_format_rsp_hdr(buf, FCNVME_LSDESC_RQST, | |
172 | nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_ls_rjt)), | |
173 | ls_cmd); | |
174 | to_be32(&rjt->rjt.desc_tag, FCNVME_LSDESC_RJT); | |
175 | rjt->rjt.desc_len = nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_rjt)); | |
176 | rjt->rjt.reason_code = reason; | |
177 | rjt->rjt.reason_explanation = explanation; | |
178 | rjt->rjt.vendor = vendor; | |
179 | ||
180 | return sizeof(struct spdk_nvmf_fc_ls_rjt); | |
181 | } | |
182 | ||
183 | /* ************************************************** */ | |
184 | /* Allocators/Deallocators (assocations, connections, */ | |
185 | /* poller API data) */ | |
186 | ||
187 | static inline void | |
188 | nvmf_fc_ls_free_association(struct spdk_nvmf_fc_association *assoc) | |
189 | { | |
190 | struct spdk_nvmf_fc_conn *fc_conn; | |
191 | ||
192 | /* return the q slots of the conns for the association */ | |
193 | TAILQ_FOREACH(fc_conn, &assoc->avail_fc_conns, assoc_avail_link) { | |
194 | if (fc_conn->conn_id != NVMF_FC_INVALID_CONN_ID) { | |
195 | nvmf_fc_release_conn(fc_conn->hwqp, fc_conn->conn_id, | |
196 | fc_conn->max_queue_depth); | |
197 | } | |
198 | } | |
199 | ||
200 | /* free assocation's send disconnect buffer */ | |
201 | if (assoc->snd_disconn_bufs) { | |
202 | nvmf_fc_free_srsr_bufs(assoc->snd_disconn_bufs); | |
203 | } | |
204 | ||
205 | /* free assocation's connections */ | |
206 | free(assoc->conns_buf); | |
207 | ||
208 | /* free the association */ | |
209 | free(assoc); | |
210 | } | |
211 | ||
212 | static int | |
213 | nvmf_fc_ls_alloc_connections(struct spdk_nvmf_fc_association *assoc, | |
214 | struct spdk_nvmf_transport *nvmf_transport) | |
215 | { | |
216 | uint32_t i; | |
217 | struct spdk_nvmf_fc_conn *fc_conn; | |
218 | ||
219 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Pre-alloc %d qpairs for host NQN %s\n", | |
220 | nvmf_transport->opts.max_qpairs_per_ctrlr, assoc->host_nqn); | |
221 | ||
222 | /* allocate memory for all connections at once */ | |
223 | assoc->conns_buf = calloc(nvmf_transport->opts.max_qpairs_per_ctrlr + 1, | |
224 | sizeof(struct spdk_nvmf_fc_conn)); | |
225 | if (assoc->conns_buf == NULL) { | |
226 | SPDK_ERRLOG("Out of memory for connections for new association\n"); | |
227 | return -ENOMEM; | |
228 | } | |
229 | ||
230 | for (i = 0; i < nvmf_transport->opts.max_qpairs_per_ctrlr; i++) { | |
231 | fc_conn = assoc->conns_buf + (i * sizeof(struct spdk_nvmf_fc_conn)); | |
232 | fc_conn->conn_id = NVMF_FC_INVALID_CONN_ID; | |
233 | fc_conn->qpair.state = SPDK_NVMF_QPAIR_UNINITIALIZED; | |
234 | fc_conn->qpair.transport = nvmf_transport; | |
235 | ||
236 | TAILQ_INSERT_TAIL(&assoc->avail_fc_conns, fc_conn, assoc_avail_link); | |
237 | } | |
238 | ||
239 | return 0; | |
240 | } | |
241 | ||
242 | static struct spdk_nvmf_fc_association * | |
243 | nvmf_fc_ls_new_association(uint32_t s_id, | |
244 | struct spdk_nvmf_fc_nport *tgtport, | |
245 | struct spdk_nvmf_fc_remote_port_info *rport, | |
246 | struct spdk_nvmf_fc_lsdesc_cr_assoc_cmd *a_cmd, | |
247 | struct spdk_nvmf_subsystem *subsys, | |
248 | uint16_t rpi, | |
249 | struct spdk_nvmf_transport *nvmf_transport) | |
250 | { | |
251 | struct spdk_nvmf_fc_association *assoc; | |
252 | int rc; | |
253 | ||
254 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, | |
255 | "New Association request for port %d nport %d rpi 0x%x\n", | |
256 | tgtport->fc_port->port_hdl, tgtport->nport_hdl, rpi); | |
257 | ||
258 | assert(rport); | |
259 | if (!rport) { | |
260 | SPDK_ERRLOG("rport is null.\n"); | |
261 | return NULL; | |
262 | } | |
263 | ||
264 | assoc = calloc(1, sizeof(struct spdk_nvmf_fc_association)); | |
265 | if (!assoc) { | |
266 | SPDK_ERRLOG("unable to allocate memory for new association\n"); | |
267 | return NULL; | |
268 | } | |
269 | ||
270 | /* initialize association */ | |
271 | #if (NVMF_FC_LS_SEND_LS_DISCONNECT == 1) | |
272 | /* allocate buffers to send LS disconnect command to host */ | |
273 | assoc->snd_disconn_bufs = | |
274 | nvmf_fc_alloc_srsr_bufs(sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst), | |
275 | sizeof(struct spdk_nvmf_fc_ls_rjt)); | |
276 | if (!assoc->snd_disconn_bufs) { | |
277 | SPDK_ERRLOG("no dma memory for association's ls disconnect bufs\n"); | |
278 | free(assoc); | |
279 | return NULL; | |
280 | } | |
281 | ||
282 | assoc->snd_disconn_bufs->rpi = rpi; | |
283 | #endif | |
284 | assoc->s_id = s_id; | |
285 | assoc->tgtport = tgtport; | |
286 | assoc->rport = rport; | |
287 | assoc->subsystem = subsys; | |
288 | assoc->assoc_state = SPDK_NVMF_FC_OBJECT_CREATED; | |
289 | memcpy(assoc->host_id, a_cmd->hostid, FCNVME_ASSOC_HOSTID_LEN); | |
290 | memcpy(assoc->host_nqn, a_cmd->hostnqn, SPDK_NVME_NQN_FIELD_SIZE); | |
291 | memcpy(assoc->sub_nqn, a_cmd->subnqn, SPDK_NVME_NQN_FIELD_SIZE); | |
292 | TAILQ_INIT(&assoc->fc_conns); | |
293 | TAILQ_INIT(&assoc->avail_fc_conns); | |
294 | assoc->ls_del_op_ctx = NULL; | |
295 | ||
296 | /* allocate and assign connections for association */ | |
297 | rc = nvmf_fc_ls_alloc_connections(assoc, nvmf_transport); | |
298 | if (rc != 0) { | |
299 | nvmf_fc_ls_free_association(assoc); | |
300 | return NULL; | |
301 | } | |
302 | ||
303 | /* add association to target port's association list */ | |
304 | nvmf_fc_add_assoc_to_tgt_port(tgtport, assoc, rport); | |
305 | return assoc; | |
306 | } | |
307 | ||
308 | static inline void | |
309 | nvmf_fc_ls_append_del_cb_ctx(struct spdk_nvmf_fc_association *assoc, | |
310 | struct nvmf_fc_ls_op_ctx *opd) | |
311 | { | |
312 | /* append to delete assoc callback list */ | |
313 | if (!assoc->ls_del_op_ctx) { | |
314 | assoc->ls_del_op_ctx = (void *)opd; | |
315 | } else { | |
316 | struct nvmf_fc_ls_op_ctx *nxt = | |
317 | (struct nvmf_fc_ls_op_ctx *) assoc->ls_del_op_ctx; | |
318 | while (nxt->next_op_ctx) { | |
319 | nxt = nxt->next_op_ctx; | |
320 | } | |
321 | nxt->next_op_ctx = opd; | |
322 | } | |
323 | } | |
324 | ||
325 | static struct spdk_nvmf_fc_conn * | |
326 | nvmf_fc_ls_new_connection(struct spdk_nvmf_fc_association *assoc, uint16_t qid, | |
327 | uint16_t esrp_ratio, uint16_t rpi, uint16_t sq_size, | |
328 | struct spdk_nvmf_fc_nport *tgtport) | |
329 | { | |
330 | struct spdk_nvmf_fc_conn *fc_conn; | |
331 | ||
332 | fc_conn = TAILQ_FIRST(&assoc->avail_fc_conns); | |
333 | if (!fc_conn) { | |
334 | SPDK_ERRLOG("out of connections for association %p\n", assoc); | |
335 | return NULL; | |
336 | } | |
337 | ||
338 | /* Remove from avail list and add to in use. */ | |
339 | TAILQ_REMOVE(&assoc->avail_fc_conns, fc_conn, assoc_avail_link); | |
340 | TAILQ_INSERT_TAIL(&assoc->fc_conns, fc_conn, assoc_link); | |
341 | ||
342 | if (qid == 0) { | |
343 | /* AdminQ connection. */ | |
344 | assoc->aq_conn = fc_conn; | |
345 | } | |
346 | ||
347 | fc_conn->qpair.qid = qid; | |
348 | fc_conn->qpair.sq_head_max = sq_size; | |
349 | TAILQ_INIT(&fc_conn->qpair.outstanding); | |
350 | fc_conn->esrp_ratio = esrp_ratio; | |
351 | fc_conn->fc_assoc = assoc; | |
352 | fc_conn->rpi = rpi; | |
353 | fc_conn->max_queue_depth = sq_size + 1; | |
354 | ||
355 | /* save target port trid in connection (for subsystem | |
356 | * listener validation in fabric connect command) | |
357 | */ | |
358 | nvmf_fc_create_trid(&fc_conn->trid, tgtport->fc_nodename.u.wwn, | |
359 | tgtport->fc_portname.u.wwn); | |
360 | ||
361 | return fc_conn; | |
362 | } | |
363 | ||
364 | static inline void | |
365 | nvmf_fc_ls_free_connection(struct spdk_nvmf_fc_conn *fc_conn) | |
366 | { | |
367 | TAILQ_INSERT_TAIL(&fc_conn->fc_assoc->avail_fc_conns, fc_conn, assoc_avail_link); | |
368 | } | |
369 | ||
370 | /* End - Allocators/Deallocators (assocations, connections, */ | |
371 | /* poller API data) */ | |
372 | /* ******************************************************** */ | |
373 | ||
374 | static inline struct spdk_nvmf_fc_association * | |
375 | nvmf_fc_ls_find_assoc(struct spdk_nvmf_fc_nport *tgtport, uint64_t assoc_id) | |
376 | { | |
377 | struct spdk_nvmf_fc_association *assoc = NULL; | |
378 | ||
379 | TAILQ_FOREACH(assoc, &tgtport->fc_associations, link) { | |
380 | if (assoc->assoc_id == assoc_id) { | |
381 | if (assoc->assoc_state == SPDK_NVMF_FC_OBJECT_ZOMBIE) { | |
382 | assoc = NULL; | |
383 | } | |
384 | break; | |
385 | } | |
386 | } | |
387 | return assoc; | |
388 | } | |
389 | ||
390 | static inline void | |
391 | nvmf_fc_add_assoc_to_tgt_port(struct spdk_nvmf_fc_nport *tgtport, | |
392 | struct spdk_nvmf_fc_association *assoc, | |
393 | struct spdk_nvmf_fc_remote_port_info *rport) | |
394 | { | |
395 | TAILQ_INSERT_TAIL(&tgtport->fc_associations, assoc, link); | |
396 | tgtport->assoc_count++; | |
397 | rport->assoc_count++; | |
398 | } | |
399 | ||
400 | static inline void | |
401 | nvmf_fc_del_assoc_from_tgt_port(struct spdk_nvmf_fc_association *assoc) | |
402 | { | |
403 | struct spdk_nvmf_fc_nport *tgtport = assoc->tgtport; | |
404 | ||
405 | TAILQ_REMOVE(&tgtport->fc_associations, assoc, link); | |
406 | tgtport->assoc_count--; | |
407 | assoc->rport->assoc_count--; | |
408 | } | |
409 | ||
410 | static void | |
411 | nvmf_fc_ls_rsp_fail_del_conn_cb(void *cb_data, enum spdk_nvmf_fc_poller_api_ret ret) | |
412 | { | |
413 | struct nvmf_fc_ls_op_ctx *opd = | |
414 | (struct nvmf_fc_ls_op_ctx *)cb_data; | |
415 | struct spdk_nvmf_fc_ls_del_conn_api_data *dp = &opd->u.del_conn; | |
416 | struct spdk_nvmf_fc_association *assoc = dp->assoc; | |
417 | struct spdk_nvmf_fc_conn *fc_conn = dp->args.fc_conn; | |
418 | ||
419 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Delete Connection callback " | |
420 | "for assoc_id 0x%lx conn_id 0x%lx\n", assoc->assoc_id, | |
421 | fc_conn->conn_id); | |
422 | ||
423 | if (dp->aq_conn) { | |
424 | /* delete association */ | |
425 | nvmf_fc_del_assoc_from_tgt_port(assoc); | |
426 | nvmf_fc_ls_free_association(assoc); | |
427 | } else { | |
428 | /* remove connection from association's connection list */ | |
429 | TAILQ_REMOVE(&assoc->fc_conns, fc_conn, assoc_link); | |
430 | nvmf_fc_ls_free_connection(fc_conn); | |
431 | } | |
432 | ||
433 | free(opd); | |
434 | } | |
435 | ||
436 | static void | |
437 | nvmf_fc_handle_xmt_ls_rsp_failure(struct spdk_nvmf_fc_association *assoc, | |
438 | struct spdk_nvmf_fc_conn *fc_conn, | |
439 | bool aq_conn) | |
440 | { | |
441 | struct spdk_nvmf_fc_ls_del_conn_api_data *api_data; | |
442 | struct nvmf_fc_ls_op_ctx *opd = NULL; | |
443 | ||
444 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Transmit LS response failure " | |
445 | "for assoc_id 0x%lx conn_id 0x%lx\n", assoc->assoc_id, | |
446 | fc_conn->conn_id); | |
447 | ||
448 | ||
449 | /* create context for delete connection API */ | |
450 | opd = calloc(1, sizeof(struct nvmf_fc_ls_op_ctx)); | |
451 | if (!opd) { /* hopefully this doesn't happen - if so, we leak the connection */ | |
452 | SPDK_ERRLOG("Mem alloc failed for del conn op data"); | |
453 | return; | |
454 | } | |
455 | ||
456 | api_data = &opd->u.del_conn; | |
457 | api_data->assoc = assoc; | |
458 | api_data->ls_rqst = NULL; | |
459 | api_data->aq_conn = aq_conn; | |
460 | api_data->args.fc_conn = fc_conn; | |
461 | api_data->args.send_abts = false; | |
462 | api_data->args.hwqp = fc_conn->hwqp; | |
463 | api_data->args.cb_info.cb_thread = spdk_get_thread(); | |
464 | api_data->args.cb_info.cb_func = nvmf_fc_ls_rsp_fail_del_conn_cb; | |
465 | api_data->args.cb_info.cb_data = opd; | |
466 | ||
467 | nvmf_fc_poller_api_func(api_data->args.hwqp, | |
468 | SPDK_NVMF_FC_POLLER_API_DEL_CONNECTION, | |
469 | &api_data->args); | |
470 | } | |
471 | ||
472 | /* callback from poller's ADD_Connection event */ | |
473 | static void | |
474 | nvmf_fc_ls_add_conn_cb(void *cb_data, enum spdk_nvmf_fc_poller_api_ret ret) | |
475 | { | |
476 | struct nvmf_fc_ls_op_ctx *opd = | |
477 | (struct nvmf_fc_ls_op_ctx *)cb_data; | |
478 | struct spdk_nvmf_fc_ls_add_conn_api_data *dp = &opd->u.add_conn; | |
479 | struct spdk_nvmf_fc_association *assoc = dp->assoc; | |
480 | struct spdk_nvmf_fc_nport *tgtport = assoc->tgtport; | |
481 | struct spdk_nvmf_fc_conn *fc_conn = dp->args.fc_conn; | |
482 | struct spdk_nvmf_fc_ls_rqst *ls_rqst = dp->ls_rqst; | |
483 | ||
484 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, | |
485 | "add_conn_cb: assoc_id = 0x%lx, conn_id = 0x%lx\n", | |
486 | assoc->assoc_id, fc_conn->conn_id); | |
487 | ||
488 | fc_conn->create_opd = NULL; | |
489 | ||
490 | if (assoc->assoc_state == SPDK_NVMF_FC_OBJECT_TO_BE_DELETED) { | |
491 | /* association is already being deleted - don't continue */ | |
492 | free(opd); | |
493 | return; | |
494 | } | |
495 | ||
496 | if (dp->aq_conn) { | |
497 | struct spdk_nvmf_fc_ls_cr_assoc_acc *assoc_acc = | |
498 | (struct spdk_nvmf_fc_ls_cr_assoc_acc *)ls_rqst->rspbuf.virt; | |
499 | /* put connection and association ID in response */ | |
500 | to_be64(&assoc_acc->conn_id.connection_id, fc_conn->conn_id); | |
501 | assoc_acc->assoc_id.association_id = assoc_acc->conn_id.connection_id; | |
502 | } else { | |
503 | struct spdk_nvmf_fc_ls_cr_conn_acc *conn_acc = | |
504 | (struct spdk_nvmf_fc_ls_cr_conn_acc *)ls_rqst->rspbuf.virt; | |
505 | /* put connection ID in response */ | |
506 | to_be64(&conn_acc->conn_id.connection_id, fc_conn->conn_id); | |
507 | } | |
508 | ||
509 | /* send LS response */ | |
510 | if (nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst) != 0) { | |
511 | SPDK_ERRLOG("Send LS response for %s failed - cleaning up\n", | |
512 | dp->aq_conn ? "association" : "connection"); | |
513 | nvmf_fc_handle_xmt_ls_rsp_failure(assoc, fc_conn, | |
514 | dp->aq_conn); | |
515 | } else { | |
516 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, | |
517 | "LS response (conn_id 0x%lx) sent\n", fc_conn->conn_id); | |
518 | } | |
519 | ||
520 | free(opd); | |
521 | } | |
522 | ||
523 | void | |
524 | nvmf_fc_ls_add_conn_failure( | |
525 | struct spdk_nvmf_fc_association *assoc, | |
526 | struct spdk_nvmf_fc_ls_rqst *ls_rqst, | |
527 | struct spdk_nvmf_fc_conn *fc_conn, | |
528 | bool aq_conn) | |
529 | { | |
530 | struct spdk_nvmf_fc_ls_cr_assoc_rqst *rqst; | |
531 | struct spdk_nvmf_fc_ls_cr_assoc_acc *acc; | |
532 | struct spdk_nvmf_fc_nport *tgtport = assoc->tgtport; | |
533 | ||
534 | if (fc_conn->create_opd) { | |
535 | free(fc_conn->create_opd); | |
536 | fc_conn->create_opd = NULL; | |
537 | } | |
538 | ||
539 | rqst = (struct spdk_nvmf_fc_ls_cr_assoc_rqst *)ls_rqst->rqstbuf.virt; | |
540 | acc = (struct spdk_nvmf_fc_ls_cr_assoc_acc *)ls_rqst->rspbuf.virt; | |
541 | ||
542 | /* send failure response */ | |
543 | ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc, | |
544 | FCNVME_MAX_LS_BUFFER_SIZE, rqst->w0.ls_cmd, | |
545 | FCNVME_RJT_RC_INSUFF_RES, | |
546 | FCNVME_RJT_EXP_NONE, 0); | |
547 | ||
548 | nvmf_fc_ls_free_connection(fc_conn); | |
549 | if (aq_conn) { | |
550 | nvmf_fc_del_assoc_from_tgt_port(assoc); | |
551 | nvmf_fc_ls_free_association(assoc); | |
552 | } | |
553 | ||
554 | nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst); | |
555 | } | |
556 | ||
557 | ||
558 | static void | |
559 | nvmf_fc_ls_add_conn_to_poller( | |
560 | struct spdk_nvmf_fc_association *assoc, | |
561 | struct spdk_nvmf_fc_ls_rqst *ls_rqst, | |
562 | struct spdk_nvmf_fc_conn *fc_conn, | |
563 | bool aq_conn) | |
564 | { | |
565 | struct nvmf_fc_ls_op_ctx *opd; | |
566 | struct spdk_nvmf_fc_ls_add_conn_api_data *api_data; | |
567 | ||
568 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Add Connection to poller for " | |
569 | "assoc_id 0x%lx conn_id 0x%lx\n", assoc->assoc_id, | |
570 | fc_conn->conn_id); | |
571 | ||
572 | opd = calloc(1, sizeof(struct nvmf_fc_ls_op_ctx)); | |
573 | if (!opd) { | |
574 | SPDK_ERRLOG("allocate api data for add conn op failed\n"); | |
575 | nvmf_fc_ls_add_conn_failure(assoc, ls_rqst, fc_conn, aq_conn); | |
576 | return; | |
577 | } | |
578 | ||
579 | /* insert conn in association's connection list */ | |
580 | api_data = &opd->u.add_conn; | |
581 | assoc->conn_count++; | |
582 | ||
583 | api_data->args.fc_conn = fc_conn; | |
584 | api_data->args.cb_info.cb_thread = spdk_get_thread(); | |
585 | api_data->args.cb_info.cb_func = nvmf_fc_ls_add_conn_cb; | |
586 | api_data->args.cb_info.cb_data = (void *)opd; | |
587 | api_data->assoc = assoc; | |
588 | api_data->ls_rqst = ls_rqst; | |
589 | api_data->aq_conn = aq_conn; | |
590 | ||
591 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, | |
592 | "New QP callback called.\n"); | |
593 | ||
594 | /* Let the nvmf_tgt decide which pollgroup to use. */ | |
595 | fc_conn->create_opd = opd; | |
596 | spdk_nvmf_tgt_new_qpair(ls_rqst->nvmf_tgt, &fc_conn->qpair); | |
597 | } | |
598 | ||
599 | /* Delete association functions */ | |
600 | ||
601 | static void | |
602 | nvmf_fc_do_del_assoc_cbs(struct nvmf_fc_ls_op_ctx *opd, int ret) | |
603 | { | |
604 | struct nvmf_fc_ls_op_ctx *nxt; | |
605 | struct spdk_nvmf_fc_delete_assoc_api_data *dp; | |
606 | ||
607 | while (opd) { | |
608 | dp = &opd->u.del_assoc; | |
609 | ||
610 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "performing delete assoc. callback\n"); | |
611 | dp->del_assoc_cb(dp->del_assoc_cb_data, ret); | |
612 | ||
613 | nxt = opd->next_op_ctx; | |
614 | free(opd); | |
615 | opd = nxt; | |
616 | } | |
617 | } | |
618 | ||
619 | static void | |
620 | nvmf_fs_send_ls_disconnect_cb(void *hwqp, int32_t status, void *args) | |
621 | { | |
622 | if (args) { | |
623 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "free disconnect buffers\n"); | |
624 | nvmf_fc_free_srsr_bufs((struct spdk_nvmf_fc_srsr_bufs *)args); | |
625 | } | |
626 | } | |
627 | ||
628 | static void | |
629 | nvmf_fc_del_all_conns_cb(void *cb_data, enum spdk_nvmf_fc_poller_api_ret ret) | |
630 | { | |
631 | struct nvmf_fc_ls_op_ctx *opd = (struct nvmf_fc_ls_op_ctx *)cb_data; | |
632 | struct spdk_nvmf_fc_delete_assoc_api_data *dp = &opd->u.del_assoc; | |
633 | struct spdk_nvmf_fc_association *assoc = dp->assoc; | |
634 | struct spdk_nvmf_fc_conn *fc_conn = dp->args.fc_conn; | |
635 | ||
636 | /* Assumption here is that there will be no error (i.e. ret=success). | |
637 | * Since connections are deleted in parallel, nothing can be | |
638 | * done anyway if there is an error because we need to complete | |
639 | * all connection deletes and callback to caller */ | |
640 | ||
641 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, | |
642 | "Delete all connections for assoc_id 0x%lx, conn_id = %lx\n", | |
643 | assoc->assoc_id, fc_conn->conn_id); | |
644 | ||
645 | /* remove connection from association's connection list */ | |
646 | TAILQ_REMOVE(&assoc->fc_conns, fc_conn, assoc_link); | |
647 | nvmf_fc_ls_free_connection(fc_conn); | |
648 | ||
649 | if (--assoc->conn_count == 0) { | |
650 | /* last connection - remove association from target port's association list */ | |
651 | struct nvmf_fc_ls_op_ctx *cb_opd = (struct nvmf_fc_ls_op_ctx *)assoc->ls_del_op_ctx; | |
652 | ||
653 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, | |
654 | "remove assoc. %lx\n", assoc->assoc_id); | |
655 | nvmf_fc_del_assoc_from_tgt_port(assoc); | |
656 | ||
657 | if (assoc->snd_disconn_bufs && | |
658 | assoc->tgtport->fc_port->hw_port_status == SPDK_FC_PORT_ONLINE) { | |
659 | ||
660 | struct spdk_nvmf_fc_ls_disconnect_rqst *dc_rqst; | |
661 | struct spdk_nvmf_fc_srsr_bufs *srsr_bufs; | |
662 | ||
663 | dc_rqst = (struct spdk_nvmf_fc_ls_disconnect_rqst *) | |
664 | assoc->snd_disconn_bufs->rqst; | |
665 | ||
666 | bzero(dc_rqst, sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst)); | |
667 | ||
668 | /* fill in request descriptor */ | |
669 | dc_rqst->w0.ls_cmd = FCNVME_LS_DISCONNECT; | |
670 | to_be32(&dc_rqst->desc_list_len, | |
671 | sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst) - | |
672 | (2 * sizeof(uint32_t))); | |
673 | ||
674 | /* fill in disconnect command descriptor */ | |
675 | to_be32(&dc_rqst->disconn_cmd.desc_tag, FCNVME_LSDESC_DISCONN_CMD); | |
676 | to_be32(&dc_rqst->disconn_cmd.desc_len, | |
677 | sizeof(struct spdk_nvmf_fc_lsdesc_disconn_cmd) - | |
678 | (2 * sizeof(uint32_t))); | |
679 | ||
680 | /* fill in association id descriptor */ | |
681 | to_be32(&dc_rqst->assoc_id.desc_tag, FCNVME_LSDESC_ASSOC_ID), | |
682 | to_be32(&dc_rqst->assoc_id.desc_len, | |
683 | sizeof(struct spdk_nvmf_fc_lsdesc_assoc_id) - | |
684 | (2 * sizeof(uint32_t))); | |
685 | to_be64(&dc_rqst->assoc_id.association_id, assoc->assoc_id); | |
686 | ||
687 | srsr_bufs = assoc->snd_disconn_bufs; | |
688 | assoc->snd_disconn_bufs = NULL; | |
689 | ||
690 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Send LS disconnect\n"); | |
691 | if (nvmf_fc_xmt_srsr_req(&assoc->tgtport->fc_port->ls_queue, | |
692 | srsr_bufs, nvmf_fs_send_ls_disconnect_cb, | |
693 | (void *)srsr_bufs)) { | |
694 | SPDK_ERRLOG("Error sending LS disconnect\n"); | |
695 | assoc->snd_disconn_bufs = srsr_bufs; | |
696 | } | |
697 | } | |
698 | ||
699 | nvmf_fc_ls_free_association(assoc); | |
700 | ||
701 | /* perform callbacks to all callers to delete association */ | |
702 | nvmf_fc_do_del_assoc_cbs(cb_opd, 0); | |
703 | ||
704 | } | |
705 | ||
706 | free(opd); | |
707 | } | |
708 | ||
709 | static void | |
710 | nvmf_fc_kill_io_del_all_conns_cb(void *cb_data, enum spdk_nvmf_fc_poller_api_ret ret) | |
711 | { | |
712 | struct nvmf_fc_ls_op_ctx *opd = (struct nvmf_fc_ls_op_ctx *)cb_data; | |
713 | ||
714 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Callback after killing outstanding ABTS."); | |
715 | /* | |
716 | * NOTE: We should not access any connection or association related data | |
717 | * structures here. | |
718 | */ | |
719 | free(opd); | |
720 | } | |
721 | ||
722 | ||
723 | /* Disconnect/delete (association) request functions */ | |
724 | ||
725 | static int | |
726 | _nvmf_fc_delete_association(struct spdk_nvmf_fc_nport *tgtport, | |
727 | uint64_t assoc_id, bool send_abts, bool backend_initiated, | |
728 | spdk_nvmf_fc_del_assoc_cb del_assoc_cb, | |
729 | void *cb_data, bool from_ls_rqst) | |
730 | { | |
731 | ||
732 | struct nvmf_fc_ls_op_ctx *opd, *opd_tail, *opd_head = NULL; | |
733 | struct spdk_nvmf_fc_delete_assoc_api_data *api_data; | |
734 | struct spdk_nvmf_fc_conn *fc_conn; | |
735 | struct spdk_nvmf_fc_association *assoc = | |
736 | nvmf_fc_ls_find_assoc(tgtport, assoc_id); | |
737 | struct spdk_nvmf_fc_port *fc_port = tgtport->fc_port; | |
738 | enum spdk_nvmf_fc_object_state assoc_state; | |
739 | ||
740 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Delete association, " | |
741 | "assoc_id 0x%lx\n", assoc_id); | |
742 | ||
743 | if (!assoc) { | |
744 | SPDK_ERRLOG("Delete association failed: %s\n", | |
745 | validation_errors[VERR_NO_ASSOC]); | |
746 | return VERR_NO_ASSOC; | |
747 | } | |
748 | ||
749 | /* create cb context to put in association's list of | |
750 | * callbacks to call when delete association is done */ | |
751 | opd = calloc(1, sizeof(struct nvmf_fc_ls_op_ctx)); | |
752 | if (!opd) { | |
753 | SPDK_ERRLOG("Mem alloc failed for del assoc cb data"); | |
754 | return -ENOMEM; | |
755 | } | |
756 | ||
757 | api_data = &opd->u.del_assoc; | |
758 | api_data->assoc = assoc; | |
759 | api_data->from_ls_rqst = from_ls_rqst; | |
760 | api_data->del_assoc_cb = del_assoc_cb; | |
761 | api_data->del_assoc_cb_data = cb_data; | |
762 | api_data->args.cb_info.cb_data = opd; | |
763 | nvmf_fc_ls_append_del_cb_ctx(assoc, opd); | |
764 | ||
765 | assoc_state = assoc->assoc_state; | |
766 | if ((assoc_state == SPDK_NVMF_FC_OBJECT_TO_BE_DELETED) && | |
767 | (fc_port->hw_port_status != SPDK_FC_PORT_QUIESCED)) { | |
768 | /* association already being deleted */ | |
769 | return 0; | |
770 | } | |
771 | ||
772 | /* mark assoc. to be deleted */ | |
773 | assoc->assoc_state = SPDK_NVMF_FC_OBJECT_TO_BE_DELETED; | |
774 | ||
775 | /* create a list of all connection to delete */ | |
776 | TAILQ_FOREACH(fc_conn, &assoc->fc_conns, assoc_link) { | |
777 | opd = calloc(1, sizeof(struct nvmf_fc_ls_op_ctx)); | |
778 | if (!opd) { /* hopefully this doesn't happen */ | |
779 | SPDK_ERRLOG("Mem alloc failed for del conn op data"); | |
780 | while (opd_head) { /* free any contexts already allocated */ | |
781 | opd = opd_head; | |
782 | opd_head = opd->next_op_ctx; | |
783 | free(opd); | |
784 | } | |
785 | return -ENOMEM; | |
786 | } | |
787 | ||
788 | api_data = &opd->u.del_assoc; | |
789 | api_data->args.fc_conn = fc_conn; | |
790 | api_data->assoc = assoc; | |
791 | api_data->args.send_abts = send_abts; | |
792 | api_data->args.backend_initiated = backend_initiated; | |
793 | api_data->args.hwqp = nvmf_fc_get_hwqp_from_conn_id( | |
794 | assoc->tgtport->fc_port->io_queues, | |
795 | assoc->tgtport->fc_port->num_io_queues, | |
796 | fc_conn->conn_id); | |
797 | api_data->args.cb_info.cb_thread = spdk_get_thread(); | |
798 | if ((fc_port->hw_port_status == SPDK_FC_PORT_QUIESCED) && | |
799 | (assoc_state == SPDK_NVMF_FC_OBJECT_TO_BE_DELETED)) { | |
800 | /* | |
801 | * If there are any connections deletes or IO abts that are | |
802 | * stuck because of firmware reset, a second invocation of | |
803 | * SPDK_NVMF_FC_POLLER_API_DEL_CONNECTION will result in | |
804 | * outstanding connections & requests being killed and | |
805 | * their corresponding callbacks being executed. | |
806 | */ | |
807 | api_data->args.cb_info.cb_func = nvmf_fc_kill_io_del_all_conns_cb; | |
808 | } else { | |
809 | api_data->args.cb_info.cb_func = nvmf_fc_del_all_conns_cb; | |
810 | } | |
811 | api_data->args.cb_info.cb_data = opd; | |
812 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, | |
813 | "conn_id = %lx\n", fc_conn->conn_id); | |
814 | ||
815 | if (!opd_head) { | |
816 | opd_head = opd; | |
817 | } else { | |
818 | opd_tail->next_op_ctx = opd; | |
819 | } | |
820 | opd_tail = opd; | |
821 | } | |
822 | ||
823 | /* make poller api calls to delete connetions */ | |
824 | while (opd_head) { | |
825 | opd = opd_head; | |
826 | opd_head = opd->next_op_ctx; | |
827 | api_data = &opd->u.del_assoc; | |
828 | nvmf_fc_poller_api_func(api_data->args.hwqp, | |
829 | SPDK_NVMF_FC_POLLER_API_DEL_CONNECTION, | |
830 | &api_data->args); | |
831 | } | |
832 | ||
833 | return 0; | |
834 | } | |
835 | ||
836 | static void | |
837 | nvmf_fc_ls_disconnect_assoc_cb(void *cb_data, uint32_t err) | |
838 | { | |
839 | struct nvmf_fc_ls_op_ctx *opd = (struct nvmf_fc_ls_op_ctx *)cb_data; | |
840 | struct spdk_nvmf_fc_ls_disconn_assoc_api_data *dp = &opd->u.disconn_assoc; | |
841 | struct spdk_nvmf_fc_nport *tgtport = dp->tgtport; | |
842 | struct spdk_nvmf_fc_ls_rqst *ls_rqst = dp->ls_rqst; | |
843 | ||
844 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Disconnect association callback begin " | |
845 | "nport %d\n", tgtport->nport_hdl); | |
846 | if (err != 0) { | |
847 | /* send failure response */ | |
848 | struct spdk_nvmf_fc_ls_cr_assoc_rqst *rqst = | |
849 | (struct spdk_nvmf_fc_ls_cr_assoc_rqst *)ls_rqst->rqstbuf.virt; | |
850 | struct spdk_nvmf_fc_ls_cr_assoc_acc *acc = | |
851 | (struct spdk_nvmf_fc_ls_cr_assoc_acc *)ls_rqst->rspbuf.virt; | |
852 | ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc, | |
853 | FCNVME_MAX_LS_BUFFER_SIZE, | |
854 | rqst->w0.ls_cmd, | |
855 | FCNVME_RJT_RC_UNAB, | |
856 | FCNVME_RJT_EXP_NONE, | |
857 | 0); | |
858 | } | |
859 | ||
860 | nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst); | |
861 | ||
862 | free(opd); | |
863 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Disconnect association callback complete " | |
864 | "nport %d err %d\n", tgtport->nport_hdl, err); | |
865 | } | |
866 | ||
867 | static void | |
868 | nvmf_fc_ls_disconnect_assoc(struct spdk_nvmf_fc_nport *tgtport, | |
869 | struct spdk_nvmf_fc_ls_rqst *ls_rqst, uint64_t assoc_id) | |
870 | { | |
871 | struct nvmf_fc_ls_op_ctx *opd; | |
872 | struct spdk_nvmf_fc_ls_cr_assoc_rqst *rqst = | |
873 | (struct spdk_nvmf_fc_ls_cr_assoc_rqst *)ls_rqst->rqstbuf.virt; | |
874 | struct spdk_nvmf_fc_ls_cr_assoc_acc *acc = | |
875 | (struct spdk_nvmf_fc_ls_cr_assoc_acc *)ls_rqst->rspbuf.virt; | |
876 | struct spdk_nvmf_fc_ls_disconn_assoc_api_data *api_data; | |
877 | int ret; | |
878 | uint8_t reason = 0; | |
879 | ||
880 | opd = calloc(1, sizeof(struct nvmf_fc_ls_op_ctx)); | |
881 | if (!opd) { | |
882 | /* send failure response */ | |
883 | SPDK_ERRLOG("Allocate disconn assoc op data failed\n"); | |
884 | reason = FCNVME_RJT_RC_INSUFF_RES; | |
885 | goto send_rjt; | |
886 | } | |
887 | ||
888 | api_data = &opd->u.disconn_assoc; | |
889 | api_data->tgtport = tgtport; | |
890 | api_data->ls_rqst = ls_rqst; | |
891 | ret = _nvmf_fc_delete_association(tgtport, assoc_id, | |
892 | false, false, | |
893 | nvmf_fc_ls_disconnect_assoc_cb, | |
894 | api_data, true); | |
895 | if (!ret) { | |
896 | return; | |
897 | } | |
898 | ||
899 | /* delete association failed */ | |
900 | switch (ret) { | |
901 | case VERR_NO_ASSOC: | |
902 | reason = FCNVME_RJT_RC_INV_ASSOC; | |
903 | break; | |
904 | case -ENOMEM: | |
905 | reason = FCNVME_RJT_RC_INSUFF_RES; | |
906 | break; | |
907 | default: | |
908 | reason = FCNVME_RJT_RC_LOGIC; | |
909 | } | |
910 | ||
911 | free(opd); | |
912 | ||
913 | send_rjt: | |
914 | ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc, | |
915 | FCNVME_MAX_LS_BUFFER_SIZE, | |
916 | rqst->w0.ls_cmd, reason, | |
917 | FCNVME_RJT_EXP_NONE, 0); | |
918 | nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst); | |
919 | } | |
920 | ||
921 | static int | |
922 | nvmf_fc_ls_validate_host(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn) | |
923 | { | |
924 | ||
925 | if (!spdk_nvmf_subsystem_host_allowed(subsystem, hostnqn)) { | |
926 | return -EPERM; | |
927 | } | |
928 | ||
929 | return 0; | |
930 | } | |
931 | ||
932 | /* **************************** */ | |
933 | /* LS Reqeust Handler Functions */ | |
934 | ||
935 | static void | |
936 | nvmf_fc_ls_process_cass(uint32_t s_id, | |
937 | struct spdk_nvmf_fc_nport *tgtport, | |
938 | struct spdk_nvmf_fc_ls_rqst *ls_rqst) | |
939 | { | |
940 | struct spdk_nvmf_fc_ls_cr_assoc_rqst *rqst = | |
941 | (struct spdk_nvmf_fc_ls_cr_assoc_rqst *)ls_rqst->rqstbuf.virt; | |
942 | struct spdk_nvmf_fc_ls_cr_assoc_acc *acc = | |
943 | (struct spdk_nvmf_fc_ls_cr_assoc_acc *)ls_rqst->rspbuf.virt; | |
944 | struct spdk_nvmf_fc_association *assoc; | |
945 | struct spdk_nvmf_fc_conn *fc_conn; | |
946 | struct spdk_nvmf_subsystem *subsystem = NULL; | |
947 | const char *hostnqn = (const char *)rqst->assoc_cmd.hostnqn; | |
948 | int errmsg_ind = 0; | |
949 | uint8_t rc = FCNVME_RJT_RC_NONE; | |
950 | uint8_t ec = FCNVME_RJT_EXP_NONE; | |
951 | struct spdk_nvmf_transport *transport = spdk_nvmf_tgt_get_transport(ls_rqst->nvmf_tgt, | |
952 | SPDK_NVME_TRANSPORT_NAME_FC); | |
953 | ||
954 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, | |
955 | "LS_CASS: ls_rqst_len=%d, desc_list_len=%d, cmd_len=%d, sq_size=%d, " | |
956 | "Subnqn: %s, Hostnqn: %s, Tgtport nn:%lx, pn:%lx\n", | |
957 | ls_rqst->rqst_len, from_be32(&rqst->desc_list_len), | |
958 | from_be32(&rqst->assoc_cmd.desc_len), | |
959 | from_be32(&rqst->assoc_cmd.sqsize), | |
960 | rqst->assoc_cmd.subnqn, hostnqn, | |
961 | tgtport->fc_nodename.u.wwn, tgtport->fc_portname.u.wwn); | |
962 | ||
963 | if (ls_rqst->rqst_len < FCNVME_LS_CA_CMD_MIN_LEN) { | |
964 | SPDK_ERRLOG("assoc_cmd req len = %d, should be at least %d\n", | |
965 | ls_rqst->rqst_len, FCNVME_LS_CA_CMD_MIN_LEN); | |
966 | errmsg_ind = VERR_CR_ASSOC_LEN; | |
967 | rc = FCNVME_RJT_RC_INV_PARAM; | |
968 | ec = FCNVME_RJT_EXP_INV_LEN; | |
969 | } else if (from_be32(&rqst->desc_list_len) < | |
970 | FCNVME_LS_CA_DESC_LIST_MIN_LEN) { | |
971 | SPDK_ERRLOG("assoc_cmd desc list len = %d, should be at least %d\n", | |
972 | from_be32(&rqst->desc_list_len), | |
973 | FCNVME_LS_CA_DESC_LIST_MIN_LEN); | |
974 | errmsg_ind = VERR_CR_ASSOC_RQST_LEN; | |
975 | rc = FCNVME_RJT_RC_INV_PARAM; | |
976 | ec = FCNVME_RJT_EXP_INV_LEN; | |
977 | } else if (rqst->assoc_cmd.desc_tag != | |
978 | cpu_to_be32(FCNVME_LSDESC_CREATE_ASSOC_CMD)) { | |
979 | errmsg_ind = VERR_CR_ASSOC_CMD; | |
980 | rc = FCNVME_RJT_RC_INV_PARAM; | |
981 | } else if (from_be32(&rqst->assoc_cmd.desc_len) < | |
982 | FCNVME_LS_CA_DESC_MIN_LEN) { | |
983 | SPDK_ERRLOG("assoc_cmd desc len = %d, should be at least %d\n", | |
984 | from_be32(&rqst->assoc_cmd.desc_len), | |
985 | FCNVME_LS_CA_DESC_MIN_LEN); | |
986 | errmsg_ind = VERR_CR_ASSOC_CMD_LEN; | |
987 | rc = FCNVME_RJT_RC_INV_PARAM; | |
988 | ec = FCNVME_RJT_EXP_INV_LEN; | |
989 | } else if (!rqst->assoc_cmd.ersp_ratio || | |
990 | (from_be16(&rqst->assoc_cmd.ersp_ratio) >= | |
991 | from_be16(&rqst->assoc_cmd.sqsize))) { | |
992 | errmsg_ind = VERR_ERSP_RATIO; | |
993 | rc = FCNVME_RJT_RC_INV_PARAM; | |
994 | ec = FCNVME_RJT_EXP_INV_ESRP; | |
995 | } else if (from_be16(&rqst->assoc_cmd.sqsize) == 0 || | |
996 | from_be16(&rqst->assoc_cmd.sqsize) > transport->opts.max_aq_depth) { | |
997 | errmsg_ind = VERR_SQSIZE; | |
998 | rc = FCNVME_RJT_RC_INV_PARAM; | |
999 | ec = FCNVME_RJT_EXP_SQ_SIZE; | |
1000 | } | |
1001 | ||
1002 | if (rc != FCNVME_RJT_RC_NONE) { | |
1003 | goto rjt_cass; | |
1004 | } | |
1005 | ||
1006 | subsystem = spdk_nvmf_tgt_find_subsystem(ls_rqst->nvmf_tgt, rqst->assoc_cmd.subnqn); | |
1007 | if (subsystem == NULL) { | |
1008 | errmsg_ind = VERR_SUBNQN; | |
1009 | rc = FCNVME_RJT_RC_INV_PARAM; | |
1010 | ec = FCNVME_RJT_EXP_INV_SUBNQN; | |
1011 | goto rjt_cass; | |
1012 | } | |
1013 | ||
1014 | if (nvmf_fc_ls_validate_host(subsystem, hostnqn)) { | |
1015 | errmsg_ind = VERR_HOSTNQN; | |
1016 | rc = FCNVME_RJT_RC_INV_HOST; | |
1017 | ec = FCNVME_RJT_EXP_INV_HOSTNQN; | |
1018 | goto rjt_cass; | |
1019 | } | |
1020 | ||
1021 | /* get new association */ | |
1022 | assoc = nvmf_fc_ls_new_association(s_id, tgtport, ls_rqst->rport, | |
1023 | &rqst->assoc_cmd, subsystem, | |
1024 | ls_rqst->rpi, transport); | |
1025 | if (!assoc) { | |
1026 | errmsg_ind = VERR_ASSOC_ALLOC_FAIL; | |
1027 | rc = FCNVME_RJT_RC_INSUFF_RES; | |
1028 | ec = FCNVME_RJT_EXP_NONE; | |
1029 | goto rjt_cass; | |
1030 | } | |
1031 | ||
1032 | /* alloc admin q (i.e. connection) */ | |
1033 | fc_conn = nvmf_fc_ls_new_connection(assoc, 0, | |
1034 | from_be16(&rqst->assoc_cmd.ersp_ratio), | |
1035 | ls_rqst->rpi, | |
1036 | from_be16(&rqst->assoc_cmd.sqsize), | |
1037 | tgtport); | |
1038 | if (!fc_conn) { | |
1039 | nvmf_fc_ls_free_association(assoc); | |
1040 | errmsg_ind = VERR_CONN_ALLOC_FAIL; | |
1041 | rc = FCNVME_RJT_RC_INSUFF_RES; | |
1042 | ec = FCNVME_RJT_EXP_NONE; | |
1043 | goto rjt_cass; | |
1044 | } | |
1045 | ||
1046 | /* format accept response */ | |
1047 | bzero(acc, sizeof(*acc)); | |
1048 | ls_rqst->rsp_len = sizeof(*acc); | |
1049 | ||
1050 | nvmf_fc_ls_format_rsp_hdr(acc, FCNVME_LS_ACC, | |
1051 | nvmf_fc_lsdesc_len( | |
1052 | sizeof(struct spdk_nvmf_fc_ls_cr_assoc_acc)), | |
1053 | FCNVME_LS_CREATE_ASSOCIATION); | |
1054 | to_be32(&acc->assoc_id.desc_tag, FCNVME_LSDESC_ASSOC_ID); | |
1055 | acc->assoc_id.desc_len = | |
1056 | nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_assoc_id)); | |
1057 | to_be32(&acc->conn_id.desc_tag, FCNVME_LSDESC_CONN_ID); | |
1058 | acc->conn_id.desc_len = | |
1059 | nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_conn_id)); | |
1060 | ||
1061 | /* assign connection to HWQP poller - also sends response */ | |
1062 | nvmf_fc_ls_add_conn_to_poller(assoc, ls_rqst, fc_conn, true); | |
1063 | ||
1064 | return; | |
1065 | ||
1066 | rjt_cass: | |
1067 | SPDK_ERRLOG("Create Association LS failed: %s\n", validation_errors[errmsg_ind]); | |
1068 | ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc, FCNVME_MAX_LS_BUFFER_SIZE, | |
1069 | rqst->w0.ls_cmd, rc, ec, 0); | |
1070 | nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst); | |
1071 | } | |
1072 | ||
1073 | static void | |
1074 | nvmf_fc_ls_process_cioc(struct spdk_nvmf_fc_nport *tgtport, | |
1075 | struct spdk_nvmf_fc_ls_rqst *ls_rqst) | |
1076 | { | |
1077 | struct spdk_nvmf_fc_ls_cr_conn_rqst *rqst = | |
1078 | (struct spdk_nvmf_fc_ls_cr_conn_rqst *)ls_rqst->rqstbuf.virt; | |
1079 | struct spdk_nvmf_fc_ls_cr_conn_acc *acc = | |
1080 | (struct spdk_nvmf_fc_ls_cr_conn_acc *)ls_rqst->rspbuf.virt; | |
1081 | struct spdk_nvmf_fc_association *assoc; | |
1082 | struct spdk_nvmf_fc_conn *fc_conn = NULL; | |
1083 | int errmsg_ind = 0; | |
1084 | uint8_t rc = FCNVME_RJT_RC_NONE; | |
1085 | uint8_t ec = FCNVME_RJT_EXP_NONE; | |
1086 | struct spdk_nvmf_transport *transport = spdk_nvmf_tgt_get_transport(ls_rqst->nvmf_tgt, | |
1087 | SPDK_NVME_TRANSPORT_NAME_FC); | |
1088 | ||
1089 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, | |
1090 | "LS_CIOC: ls_rqst_len=%d, desc_list_len=%d, cmd_len=%d, " | |
1091 | "assoc_id=0x%lx, sq_size=%d, esrp=%d, Tgtport nn:%lx, pn:%lx\n", | |
1092 | ls_rqst->rqst_len, from_be32(&rqst->desc_list_len), | |
1093 | from_be32(&rqst->connect_cmd.desc_len), | |
1094 | from_be64(&rqst->assoc_id.association_id), | |
1095 | from_be32(&rqst->connect_cmd.sqsize), | |
1096 | from_be32(&rqst->connect_cmd.ersp_ratio), | |
1097 | tgtport->fc_nodename.u.wwn, tgtport->fc_portname.u.wwn); | |
1098 | ||
1099 | if (ls_rqst->rqst_len < sizeof(struct spdk_nvmf_fc_ls_cr_conn_rqst)) { | |
1100 | errmsg_ind = VERR_CR_CONN_LEN; | |
1101 | rc = FCNVME_RJT_RC_INV_PARAM; | |
1102 | ec = FCNVME_RJT_EXP_INV_LEN; | |
1103 | } else if (rqst->desc_list_len != | |
1104 | nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_ls_cr_conn_rqst))) { | |
1105 | errmsg_ind = VERR_CR_CONN_RQST_LEN; | |
1106 | rc = FCNVME_RJT_RC_INV_PARAM; | |
1107 | ec = FCNVME_RJT_EXP_INV_LEN; | |
1108 | } else if (rqst->assoc_id.desc_tag != | |
1109 | cpu_to_be32(FCNVME_LSDESC_ASSOC_ID)) { | |
1110 | errmsg_ind = VERR_ASSOC_ID; | |
1111 | rc = FCNVME_RJT_RC_INV_PARAM; | |
1112 | } else if (rqst->assoc_id.desc_len != | |
1113 | nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_assoc_id))) { | |
1114 | errmsg_ind = VERR_ASSOC_ID_LEN; | |
1115 | rc = FCNVME_RJT_RC_INV_PARAM; | |
1116 | ec = FCNVME_RJT_EXP_INV_LEN; | |
1117 | } else if (rqst->connect_cmd.desc_tag != | |
1118 | cpu_to_be32(FCNVME_LSDESC_CREATE_CONN_CMD)) { | |
1119 | errmsg_ind = VERR_CR_CONN_CMD; | |
1120 | rc = FCNVME_RJT_RC_INV_PARAM; | |
1121 | } else if (rqst->connect_cmd.desc_len != | |
1122 | nvmf_fc_lsdesc_len( | |
1123 | sizeof(struct spdk_nvmf_fc_lsdesc_cr_conn_cmd))) { | |
1124 | errmsg_ind = VERR_CR_CONN_CMD_LEN; | |
1125 | rc = FCNVME_RJT_RC_INV_PARAM; | |
1126 | ec = FCNVME_RJT_EXP_INV_LEN; | |
1127 | } else if (!rqst->connect_cmd.ersp_ratio || | |
1128 | (from_be16(&rqst->connect_cmd.ersp_ratio) >= | |
1129 | from_be16(&rqst->connect_cmd.sqsize))) { | |
1130 | errmsg_ind = VERR_ERSP_RATIO; | |
1131 | rc = FCNVME_RJT_RC_INV_PARAM; | |
1132 | ec = FCNVME_RJT_EXP_INV_ESRP; | |
1133 | } else if (from_be16(&rqst->connect_cmd.sqsize) == 0 || | |
1134 | from_be16(&rqst->connect_cmd.sqsize) > transport->opts.max_queue_depth) { | |
1135 | errmsg_ind = VERR_SQSIZE; | |
1136 | rc = FCNVME_RJT_RC_INV_PARAM; | |
1137 | ec = FCNVME_RJT_EXP_SQ_SIZE; | |
1138 | } | |
1139 | ||
1140 | if (rc != FCNVME_RJT_RC_NONE) { | |
1141 | goto rjt_cioc; | |
1142 | } | |
1143 | ||
1144 | /* find association */ | |
1145 | assoc = nvmf_fc_ls_find_assoc(tgtport, | |
1146 | from_be64(&rqst->assoc_id.association_id)); | |
1147 | if (!assoc) { | |
1148 | errmsg_ind = VERR_NO_ASSOC; | |
1149 | rc = FCNVME_RJT_RC_INV_ASSOC; | |
1150 | } else if (assoc->assoc_state == SPDK_NVMF_FC_OBJECT_TO_BE_DELETED) { | |
1151 | /* association is being deleted - don't allow more connections */ | |
1152 | errmsg_ind = VERR_NO_ASSOC; | |
1153 | rc = FCNVME_RJT_RC_INV_ASSOC; | |
1154 | } else if (assoc->conn_count >= transport->opts.max_qpairs_per_ctrlr) { | |
1155 | errmsg_ind = VERR_CONN_TOO_MANY; | |
1156 | rc = FCNVME_RJT_RC_INV_PARAM; | |
1157 | ec = FCNVME_RJT_EXP_INV_Q_ID; | |
1158 | } | |
1159 | ||
1160 | if (rc != FCNVME_RJT_RC_NONE) { | |
1161 | goto rjt_cioc; | |
1162 | } | |
1163 | ||
1164 | fc_conn = nvmf_fc_ls_new_connection(assoc, from_be16(&rqst->connect_cmd.qid), | |
1165 | from_be16(&rqst->connect_cmd.ersp_ratio), | |
1166 | ls_rqst->rpi, | |
1167 | from_be16(&rqst->connect_cmd.sqsize), | |
1168 | tgtport); | |
1169 | if (!fc_conn) { | |
1170 | errmsg_ind = VERR_CONN_ALLOC_FAIL; | |
1171 | rc = FCNVME_RJT_RC_INSUFF_RES; | |
1172 | ec = FCNVME_RJT_EXP_NONE; | |
1173 | goto rjt_cioc; | |
1174 | } | |
1175 | ||
1176 | /* format accept response */ | |
1177 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Formatting LS accept response for " | |
1178 | "assoc_id 0x%lx conn_id 0x%lx\n", assoc->assoc_id, | |
1179 | fc_conn->conn_id); | |
1180 | bzero(acc, sizeof(*acc)); | |
1181 | ls_rqst->rsp_len = sizeof(*acc); | |
1182 | nvmf_fc_ls_format_rsp_hdr(acc, FCNVME_LS_ACC, | |
1183 | nvmf_fc_lsdesc_len( | |
1184 | sizeof(struct spdk_nvmf_fc_ls_cr_conn_acc)), | |
1185 | FCNVME_LS_CREATE_CONNECTION); | |
1186 | to_be32(&acc->conn_id.desc_tag, FCNVME_LSDESC_CONN_ID); | |
1187 | acc->conn_id.desc_len = | |
1188 | nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_conn_id)); | |
1189 | ||
1190 | /* assign connection to HWQP poller - also sends response */ | |
1191 | nvmf_fc_ls_add_conn_to_poller(assoc, ls_rqst, fc_conn, false); | |
1192 | ||
1193 | return; | |
1194 | ||
1195 | rjt_cioc: | |
1196 | SPDK_ERRLOG("Create Connection LS failed: %s\n", validation_errors[errmsg_ind]); | |
1197 | ||
1198 | ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc, FCNVME_MAX_LS_BUFFER_SIZE, | |
1199 | rqst->w0.ls_cmd, rc, ec, 0); | |
1200 | nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst); | |
1201 | } | |
1202 | ||
1203 | static void | |
1204 | nvmf_fc_ls_process_disc(struct spdk_nvmf_fc_nport *tgtport, | |
1205 | struct spdk_nvmf_fc_ls_rqst *ls_rqst) | |
1206 | { | |
1207 | struct spdk_nvmf_fc_ls_disconnect_rqst *rqst = | |
1208 | (struct spdk_nvmf_fc_ls_disconnect_rqst *)ls_rqst->rqstbuf.virt; | |
1209 | struct spdk_nvmf_fc_ls_disconnect_acc *acc = | |
1210 | (struct spdk_nvmf_fc_ls_disconnect_acc *)ls_rqst->rspbuf.virt; | |
1211 | struct spdk_nvmf_fc_association *assoc; | |
1212 | int errmsg_ind = 0; | |
1213 | uint8_t rc = FCNVME_RJT_RC_NONE; | |
1214 | uint8_t ec = FCNVME_RJT_EXP_NONE; | |
1215 | ||
1216 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, | |
1217 | "LS_DISC: ls_rqst_len=%d, desc_list_len=%d, cmd_len=%d," | |
1218 | "assoc_id=0x%lx\n", | |
1219 | ls_rqst->rqst_len, from_be32(&rqst->desc_list_len), | |
1220 | from_be32(&rqst->disconn_cmd.desc_len), | |
1221 | from_be64(&rqst->assoc_id.association_id)); | |
1222 | ||
1223 | if (ls_rqst->rqst_len < sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst)) { | |
1224 | errmsg_ind = VERR_DISCONN_LEN; | |
1225 | rc = FCNVME_RJT_RC_INV_PARAM; | |
1226 | ec = FCNVME_RJT_EXP_INV_LEN; | |
1227 | } else if (rqst->desc_list_len != | |
1228 | nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst))) { | |
1229 | errmsg_ind = VERR_DISCONN_RQST_LEN; | |
1230 | rc = FCNVME_RJT_RC_INV_PARAM; | |
1231 | ec = FCNVME_RJT_EXP_INV_LEN; | |
1232 | } else if (rqst->assoc_id.desc_tag != | |
1233 | cpu_to_be32(FCNVME_LSDESC_ASSOC_ID)) { | |
1234 | errmsg_ind = VERR_ASSOC_ID; | |
1235 | rc = FCNVME_RJT_RC_INV_PARAM; | |
1236 | } else if (rqst->assoc_id.desc_len != | |
1237 | nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_assoc_id))) { | |
1238 | errmsg_ind = VERR_ASSOC_ID_LEN; | |
1239 | rc = FCNVME_RJT_RC_INV_PARAM; | |
1240 | ec = FCNVME_RJT_EXP_INV_LEN; | |
1241 | } else if (rqst->disconn_cmd.desc_tag != | |
1242 | cpu_to_be32(FCNVME_LSDESC_DISCONN_CMD)) { | |
1243 | rc = FCNVME_RJT_RC_INV_PARAM; | |
1244 | errmsg_ind = VERR_DISCONN_CMD; | |
1245 | } else if (rqst->disconn_cmd.desc_len != | |
1246 | nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_disconn_cmd))) { | |
1247 | errmsg_ind = VERR_DISCONN_CMD_LEN; | |
1248 | rc = FCNVME_RJT_RC_INV_PARAM; | |
1249 | ec = FCNVME_RJT_EXP_INV_LEN; | |
1250 | } | |
1251 | ||
1252 | if (rc != FCNVME_RJT_RC_NONE) { | |
1253 | goto rjt_disc; | |
1254 | } | |
1255 | ||
1256 | /* match an active association */ | |
1257 | assoc = nvmf_fc_ls_find_assoc(tgtport, | |
1258 | from_be64(&rqst->assoc_id.association_id)); | |
1259 | if (!assoc) { | |
1260 | errmsg_ind = VERR_NO_ASSOC; | |
1261 | rc = FCNVME_RJT_RC_INV_ASSOC; | |
1262 | goto rjt_disc; | |
1263 | } | |
1264 | ||
1265 | /* format response */ | |
1266 | bzero(acc, sizeof(*acc)); | |
1267 | ls_rqst->rsp_len = sizeof(*acc); | |
1268 | ||
1269 | nvmf_fc_ls_format_rsp_hdr(acc, FCNVME_LS_ACC, | |
1270 | nvmf_fc_lsdesc_len( | |
1271 | sizeof(struct spdk_nvmf_fc_ls_disconnect_acc)), | |
1272 | FCNVME_LS_DISCONNECT); | |
1273 | ||
1274 | nvmf_fc_ls_disconnect_assoc(tgtport, ls_rqst, assoc->assoc_id); | |
1275 | return; | |
1276 | ||
1277 | rjt_disc: | |
1278 | SPDK_ERRLOG("Disconnect LS failed: %s\n", validation_errors[errmsg_ind]); | |
1279 | ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc, FCNVME_MAX_LS_BUFFER_SIZE, | |
1280 | rqst->w0.ls_cmd, rc, ec, 0); | |
1281 | nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst); | |
1282 | } | |
1283 | ||
1284 | /* ************************ */ | |
1285 | /* external functions */ | |
1286 | ||
1287 | void | |
1288 | nvmf_fc_ls_init(struct spdk_nvmf_fc_port *fc_port) | |
1289 | { | |
1290 | } | |
1291 | ||
1292 | void | |
1293 | nvmf_fc_ls_fini(struct spdk_nvmf_fc_port *fc_port) | |
1294 | { | |
1295 | } | |
1296 | ||
1297 | void | |
1298 | nvmf_fc_handle_ls_rqst(struct spdk_nvmf_fc_ls_rqst *ls_rqst) | |
1299 | { | |
1300 | struct spdk_nvmf_fc_ls_rqst_w0 *w0 = | |
1301 | (struct spdk_nvmf_fc_ls_rqst_w0 *)ls_rqst->rqstbuf.virt; | |
1302 | uint32_t s_id = ls_rqst->s_id; | |
1303 | struct spdk_nvmf_fc_nport *tgtport = ls_rqst->nport; | |
1304 | ||
1305 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "LS cmd=%d\n", w0->ls_cmd); | |
1306 | ||
1307 | switch (w0->ls_cmd) { | |
1308 | case FCNVME_LS_CREATE_ASSOCIATION: | |
1309 | nvmf_fc_ls_process_cass(s_id, tgtport, ls_rqst); | |
1310 | break; | |
1311 | case FCNVME_LS_CREATE_CONNECTION: | |
1312 | nvmf_fc_ls_process_cioc(tgtport, ls_rqst); | |
1313 | break; | |
1314 | case FCNVME_LS_DISCONNECT: | |
1315 | nvmf_fc_ls_process_disc(tgtport, ls_rqst); | |
1316 | break; | |
1317 | default: | |
1318 | SPDK_ERRLOG("Invalid LS cmd=%d\n", w0->ls_cmd); | |
1319 | ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(ls_rqst->rspbuf.virt, | |
1320 | FCNVME_MAX_LS_BUFFER_SIZE, w0->ls_cmd, | |
1321 | FCNVME_RJT_RC_INVAL, FCNVME_RJT_EXP_NONE, 0); | |
1322 | nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst); | |
1323 | } | |
1324 | } | |
1325 | ||
1326 | int | |
1327 | nvmf_fc_delete_association(struct spdk_nvmf_fc_nport *tgtport, | |
1328 | uint64_t assoc_id, bool send_abts, bool backend_initiated, | |
1329 | spdk_nvmf_fc_del_assoc_cb del_assoc_cb, | |
1330 | void *cb_data) | |
1331 | { | |
1332 | return _nvmf_fc_delete_association(tgtport, assoc_id, send_abts, backend_initiated, | |
1333 | del_assoc_cb, cb_data, false); | |
1334 | } | |
1335 | ||
1336 | static void | |
1337 | nvmf_fc_poller_api_cb_event(void *arg) | |
1338 | { | |
1339 | struct spdk_nvmf_fc_poller_api_cb_info *cb_info = | |
1340 | (struct spdk_nvmf_fc_poller_api_cb_info *) arg; | |
1341 | ||
1342 | assert(cb_info != NULL); | |
1343 | cb_info->cb_func(cb_info->cb_data, cb_info->ret); | |
1344 | } | |
1345 | ||
1346 | static void | |
1347 | nvmf_fc_poller_api_perform_cb(struct spdk_nvmf_fc_poller_api_cb_info *cb_info, | |
1348 | enum spdk_nvmf_fc_poller_api_ret ret) | |
1349 | { | |
1350 | if (cb_info->cb_func && cb_info->cb_thread) { | |
1351 | cb_info->ret = ret; | |
1352 | /* callback to master thread */ | |
1353 | spdk_thread_send_msg(cb_info->cb_thread, nvmf_fc_poller_api_cb_event, | |
1354 | (void *) cb_info); | |
1355 | } | |
1356 | } | |
1357 | ||
1358 | static void | |
1359 | nvmf_fc_poller_api_add_connection(void *arg) | |
1360 | { | |
1361 | enum spdk_nvmf_fc_poller_api_ret ret = SPDK_NVMF_FC_POLLER_API_SUCCESS; | |
1362 | struct spdk_nvmf_fc_poller_api_add_connection_args *conn_args = | |
1363 | (struct spdk_nvmf_fc_poller_api_add_connection_args *)arg; | |
1364 | struct spdk_nvmf_fc_conn *fc_conn; | |
1365 | ||
1366 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, "Poller add connection, conn_id 0x%lx\n", | |
1367 | conn_args->fc_conn->conn_id); | |
1368 | ||
1369 | /* make sure connection is not already in poller's list */ | |
1370 | fc_conn = nvmf_fc_hwqp_find_fc_conn(conn_args->fc_conn->hwqp, | |
1371 | conn_args->fc_conn->conn_id); | |
1372 | if (fc_conn) { | |
1373 | SPDK_ERRLOG("duplicate connection found"); | |
1374 | ret = SPDK_NVMF_FC_POLLER_API_DUP_CONN_ID; | |
1375 | } else { | |
1376 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, | |
1377 | "conn_id=%lx", fc_conn->conn_id); | |
1378 | TAILQ_INSERT_TAIL(&conn_args->fc_conn->hwqp->connection_list, | |
1379 | conn_args->fc_conn, link); | |
1380 | } | |
1381 | ||
1382 | /* perform callback */ | |
1383 | nvmf_fc_poller_api_perform_cb(&conn_args->cb_info, ret); | |
1384 | } | |
1385 | ||
1386 | static void | |
1387 | nvmf_fc_poller_api_quiesce_queue(void *arg) | |
1388 | { | |
1389 | struct spdk_nvmf_fc_poller_api_quiesce_queue_args *q_args = | |
1390 | (struct spdk_nvmf_fc_poller_api_quiesce_queue_args *) arg; | |
1391 | struct spdk_nvmf_fc_request *fc_req = NULL, *tmp; | |
1392 | ||
1393 | /* should be already, but make sure queue is quiesced */ | |
1394 | q_args->hwqp->state = SPDK_FC_HWQP_OFFLINE; | |
1395 | ||
1396 | /* | |
1397 | * Kill all the outstanding commands that are in the transfer state and | |
1398 | * in the process of being aborted. | |
1399 | * We can run into this situation if an adapter reset happens when an I_T Nexus delete | |
1400 | * is in progress. | |
1401 | */ | |
1402 | TAILQ_FOREACH_SAFE(fc_req, &q_args->hwqp->in_use_reqs, link, tmp) { | |
1403 | if (nvmf_fc_req_in_xfer(fc_req) && fc_req->is_aborted == true) { | |
1404 | nvmf_fc_poller_api_func(q_args->hwqp, SPDK_NVMF_FC_POLLER_API_REQ_ABORT_COMPLETE, | |
1405 | (void *)fc_req); | |
1406 | } | |
1407 | } | |
1408 | ||
1409 | /* perform callback */ | |
1410 | nvmf_fc_poller_api_perform_cb(&q_args->cb_info, SPDK_NVMF_FC_POLLER_API_SUCCESS); | |
1411 | } | |
1412 | ||
1413 | static void | |
1414 | nvmf_fc_poller_api_activate_queue(void *arg) | |
1415 | { | |
1416 | struct spdk_nvmf_fc_poller_api_quiesce_queue_args *q_args = | |
1417 | (struct spdk_nvmf_fc_poller_api_quiesce_queue_args *) arg; | |
1418 | ||
1419 | q_args->hwqp->state = SPDK_FC_HWQP_ONLINE; | |
1420 | ||
1421 | /* perform callback */ | |
1422 | nvmf_fc_poller_api_perform_cb(&q_args->cb_info, 0); | |
1423 | } | |
1424 | ||
1425 | static void | |
1426 | nvmf_fc_disconnect_qpair_cb(void *ctx) | |
1427 | { | |
1428 | struct spdk_nvmf_fc_poller_api_cb_info *cb_info = ctx; | |
1429 | /* perform callback */ | |
1430 | nvmf_fc_poller_api_perform_cb(cb_info, SPDK_NVMF_FC_POLLER_API_SUCCESS); | |
1431 | } | |
1432 | ||
1433 | static void | |
1434 | nvmf_fc_poller_conn_abort_done(void *hwqp, int32_t status, void *cb_args) | |
1435 | { | |
1436 | struct spdk_nvmf_fc_poller_api_del_connection_args *conn_args = cb_args; | |
1437 | ||
1438 | if (conn_args->fc_request_cnt) { | |
1439 | conn_args->fc_request_cnt -= 1; | |
1440 | } | |
1441 | ||
1442 | if (!conn_args->fc_request_cnt) { | |
1443 | if (!TAILQ_EMPTY(&conn_args->hwqp->connection_list)) { | |
1444 | /* All the requests for this connection are aborted. */ | |
1445 | TAILQ_REMOVE(&conn_args->hwqp->connection_list, conn_args->fc_conn, link); | |
1446 | ||
1447 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, "Connection deleted, conn_id 0x%lx\n", | |
1448 | conn_args->fc_conn->conn_id); | |
1449 | ||
1450 | if (!conn_args->backend_initiated) { | |
1451 | /* disconnect qpair from nvmf controller */ | |
1452 | spdk_nvmf_qpair_disconnect(&conn_args->fc_conn->qpair, | |
1453 | nvmf_fc_disconnect_qpair_cb, &conn_args->cb_info); | |
1454 | } | |
1455 | } else { | |
1456 | /* | |
1457 | * Duplicate connection delete can happen if one is | |
1458 | * coming in via an association disconnect and the other | |
1459 | * is initiated by a port reset. | |
1460 | */ | |
1461 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, "Duplicate conn delete."); | |
1462 | /* perform callback */ | |
1463 | nvmf_fc_poller_api_perform_cb(&conn_args->cb_info, SPDK_NVMF_FC_POLLER_API_SUCCESS); | |
1464 | } | |
1465 | } | |
1466 | } | |
1467 | ||
1468 | static void | |
1469 | nvmf_fc_poller_api_del_connection(void *arg) | |
1470 | { | |
1471 | struct spdk_nvmf_fc_poller_api_del_connection_args *conn_args = | |
1472 | (struct spdk_nvmf_fc_poller_api_del_connection_args *)arg; | |
1473 | struct spdk_nvmf_fc_conn *fc_conn; | |
1474 | struct spdk_nvmf_fc_request *fc_req = NULL, *tmp; | |
1475 | struct spdk_nvmf_fc_hwqp *hwqp = conn_args->hwqp; | |
1476 | ||
1477 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, "Poller delete connection, conn_id 0x%lx\n", | |
1478 | conn_args->fc_conn->conn_id); | |
1479 | ||
1480 | /* find the connection in poller's list */ | |
1481 | fc_conn = nvmf_fc_hwqp_find_fc_conn(hwqp, conn_args->fc_conn->conn_id); | |
1482 | if (!fc_conn) { | |
1483 | /* perform callback */ | |
1484 | nvmf_fc_poller_api_perform_cb(&conn_args->cb_info, SPDK_NVMF_FC_POLLER_API_NO_CONN_ID); | |
1485 | return; | |
1486 | } | |
1487 | ||
1488 | conn_args->fc_request_cnt = 0; | |
1489 | ||
1490 | TAILQ_FOREACH_SAFE(fc_req, &hwqp->in_use_reqs, link, tmp) { | |
1491 | if (fc_req->fc_conn->conn_id == fc_conn->conn_id) { | |
1492 | if (nvmf_qpair_is_admin_queue(&fc_conn->qpair) && | |
1493 | (fc_req->req.cmd->nvme_cmd.opc == SPDK_NVME_OPC_ASYNC_EVENT_REQUEST)) { | |
1494 | /* AER will be cleaned by spdk_nvmf_qpair_disconnect. */ | |
1495 | continue; | |
1496 | } | |
1497 | ||
1498 | conn_args->fc_request_cnt += 1; | |
1499 | nvmf_fc_request_abort(fc_req, conn_args->send_abts, | |
1500 | nvmf_fc_poller_conn_abort_done, | |
1501 | conn_args); | |
1502 | } | |
1503 | } | |
1504 | ||
1505 | if (!conn_args->fc_request_cnt) { | |
1506 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, "Connection deleted.\n"); | |
1507 | TAILQ_REMOVE(&hwqp->connection_list, fc_conn, link); | |
1508 | ||
1509 | if (!conn_args->backend_initiated) { | |
1510 | /* disconnect qpair from nvmf controller */ | |
1511 | spdk_nvmf_qpair_disconnect(&fc_conn->qpair, nvmf_fc_disconnect_qpair_cb, | |
1512 | &conn_args->cb_info); | |
1513 | } | |
1514 | } | |
1515 | } | |
1516 | ||
1517 | static void | |
1518 | nvmf_fc_poller_abts_done(void *hwqp, int32_t status, void *cb_args) | |
1519 | { | |
1520 | struct spdk_nvmf_fc_poller_api_abts_recvd_args *args = cb_args; | |
1521 | ||
1522 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, | |
1523 | "ABTS poller done, rpi: 0x%x, oxid: 0x%x, rxid: 0x%x\n", | |
1524 | args->ctx->rpi, args->ctx->oxid, args->ctx->rxid); | |
1525 | ||
1526 | nvmf_fc_poller_api_perform_cb(&args->cb_info, | |
1527 | SPDK_NVMF_FC_POLLER_API_SUCCESS); | |
1528 | } | |
1529 | ||
1530 | static void | |
1531 | nvmf_fc_poller_api_abts_received(void *arg) | |
1532 | { | |
1533 | struct spdk_nvmf_fc_poller_api_abts_recvd_args *args = arg; | |
1534 | struct spdk_nvmf_fc_request *fc_req = NULL; | |
1535 | struct spdk_nvmf_fc_hwqp *hwqp = args->hwqp; | |
1536 | ||
1537 | TAILQ_FOREACH(fc_req, &hwqp->in_use_reqs, link) { | |
1538 | if ((fc_req->rpi == args->ctx->rpi) && | |
1539 | (fc_req->oxid == args->ctx->oxid)) { | |
1540 | nvmf_fc_request_abort(fc_req, false, | |
1541 | nvmf_fc_poller_abts_done, args); | |
1542 | return; | |
1543 | } | |
1544 | } | |
1545 | ||
1546 | nvmf_fc_poller_api_perform_cb(&args->cb_info, | |
1547 | SPDK_NVMF_FC_POLLER_API_OXID_NOT_FOUND); | |
1548 | } | |
1549 | ||
1550 | static void | |
1551 | nvmf_fc_poller_api_queue_sync(void *arg) | |
1552 | { | |
1553 | struct spdk_nvmf_fc_poller_api_queue_sync_args *args = arg; | |
1554 | ||
1555 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, | |
1556 | "HWQP sync requested for u_id = 0x%lx\n", args->u_id); | |
1557 | ||
1558 | /* Add this args to hwqp sync_cb list */ | |
1559 | TAILQ_INSERT_TAIL(&args->hwqp->sync_cbs, args, link); | |
1560 | } | |
1561 | ||
1562 | static void | |
1563 | nvmf_fc_poller_api_queue_sync_done(void *arg) | |
1564 | { | |
1565 | struct spdk_nvmf_fc_poller_api_queue_sync_done_args *args = arg; | |
1566 | struct spdk_nvmf_fc_hwqp *hwqp = args->hwqp; | |
1567 | uint64_t tag = args->tag; | |
1568 | struct spdk_nvmf_fc_poller_api_queue_sync_args *sync_args = NULL, *tmp = NULL; | |
1569 | ||
1570 | assert(args != NULL); | |
1571 | ||
1572 | TAILQ_FOREACH_SAFE(sync_args, &hwqp->sync_cbs, link, tmp) { | |
1573 | if (sync_args->u_id == tag) { | |
1574 | /* Queue successfully synced. Remove from cb list */ | |
1575 | TAILQ_REMOVE(&hwqp->sync_cbs, sync_args, link); | |
1576 | ||
1577 | SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, | |
1578 | "HWQP sync done for u_id = 0x%lx\n", sync_args->u_id); | |
1579 | ||
1580 | /* Return the status to poller */ | |
1581 | nvmf_fc_poller_api_perform_cb(&sync_args->cb_info, | |
1582 | SPDK_NVMF_FC_POLLER_API_SUCCESS); | |
1583 | return; | |
1584 | } | |
1585 | } | |
1586 | ||
1587 | free(arg); | |
1588 | /* note: no callback from this api */ | |
1589 | } | |
1590 | ||
1591 | static void | |
1592 | nvmf_fc_poller_api_add_hwqp(void *arg) | |
1593 | { | |
1594 | struct spdk_nvmf_fc_hwqp *hwqp = (struct spdk_nvmf_fc_hwqp *)arg; | |
1595 | ||
1596 | hwqp->lcore_id = spdk_env_get_current_core(); /* for tracing purposes only */ | |
1597 | TAILQ_INSERT_TAIL(&hwqp->fgroup->hwqp_list, hwqp, link); | |
1598 | /* note: no callback from this api */ | |
1599 | } | |
1600 | ||
1601 | static void | |
1602 | nvmf_fc_poller_api_remove_hwqp(void *arg) | |
1603 | { | |
1604 | struct spdk_nvmf_fc_hwqp *hwqp = (struct spdk_nvmf_fc_hwqp *)arg; | |
1605 | struct spdk_nvmf_fc_poll_group *fgroup = hwqp->fgroup; | |
1606 | ||
1607 | TAILQ_REMOVE(&fgroup->hwqp_list, hwqp, link); | |
1608 | hwqp->fgroup = NULL; | |
1609 | /* note: no callback from this api */ | |
1610 | } | |
1611 | ||
1612 | enum spdk_nvmf_fc_poller_api_ret | |
1613 | nvmf_fc_poller_api_func(struct spdk_nvmf_fc_hwqp *hwqp, enum spdk_nvmf_fc_poller_api api, | |
1614 | void *api_args) { | |
1615 | switch (api) | |
1616 | { | |
1617 | case SPDK_NVMF_FC_POLLER_API_ADD_CONNECTION: | |
1618 | spdk_thread_send_msg(hwqp->thread, | |
1619 | nvmf_fc_poller_api_add_connection, api_args); | |
1620 | break; | |
1621 | ||
1622 | case SPDK_NVMF_FC_POLLER_API_DEL_CONNECTION: | |
1623 | spdk_thread_send_msg(hwqp->thread, | |
1624 | nvmf_fc_poller_api_del_connection, api_args); | |
1625 | break; | |
1626 | ||
1627 | case SPDK_NVMF_FC_POLLER_API_QUIESCE_QUEUE: | |
1628 | /* quiesce q polling now, don't wait for poller to do it */ | |
1629 | hwqp->state = SPDK_FC_HWQP_OFFLINE; | |
1630 | spdk_thread_send_msg(hwqp->thread, | |
1631 | nvmf_fc_poller_api_quiesce_queue, api_args); | |
1632 | break; | |
1633 | ||
1634 | case SPDK_NVMF_FC_POLLER_API_ACTIVATE_QUEUE: | |
1635 | spdk_thread_send_msg(hwqp->thread, | |
1636 | nvmf_fc_poller_api_activate_queue, api_args); | |
1637 | break; | |
1638 | ||
1639 | case SPDK_NVMF_FC_POLLER_API_ABTS_RECEIVED: | |
1640 | spdk_thread_send_msg(hwqp->thread, | |
1641 | nvmf_fc_poller_api_abts_received, api_args); | |
1642 | break; | |
1643 | ||
1644 | case SPDK_NVMF_FC_POLLER_API_REQ_ABORT_COMPLETE: | |
1645 | spdk_thread_send_msg(hwqp->thread, | |
1646 | nvmf_fc_request_abort_complete, api_args); | |
1647 | break; | |
1648 | ||
1649 | case SPDK_NVMF_FC_POLLER_API_QUEUE_SYNC: | |
1650 | spdk_thread_send_msg(hwqp->thread, | |
1651 | nvmf_fc_poller_api_queue_sync, api_args); | |
1652 | break; | |
1653 | ||
1654 | case SPDK_NVMF_FC_POLLER_API_QUEUE_SYNC_DONE: | |
1655 | spdk_thread_send_msg(hwqp->thread, | |
1656 | nvmf_fc_poller_api_queue_sync_done, api_args); | |
1657 | break; | |
1658 | ||
1659 | case SPDK_NVMF_FC_POLLER_API_ADD_HWQP: | |
1660 | spdk_thread_send_msg(hwqp->thread, nvmf_fc_poller_api_add_hwqp, (void *) hwqp); | |
1661 | break; | |
1662 | ||
1663 | case SPDK_NVMF_FC_POLLER_API_REMOVE_HWQP: | |
1664 | spdk_thread_send_msg(hwqp->thread, nvmf_fc_poller_api_remove_hwqp, (void *) hwqp); | |
1665 | break; | |
1666 | ||
1667 | case SPDK_NVMF_FC_POLLER_API_ADAPTER_EVENT: | |
1668 | case SPDK_NVMF_FC_POLLER_API_AEN: | |
1669 | default: | |
1670 | SPDK_ERRLOG("BAD ARG!"); | |
1671 | return SPDK_NVMF_FC_POLLER_API_INVALID_ARG; | |
1672 | } | |
1673 | ||
1674 | return SPDK_NVMF_FC_POLLER_API_SUCCESS; | |
1675 | } | |
1676 | ||
1677 | SPDK_LOG_REGISTER_COMPONENT("nvmf_fc_poller_api", SPDK_LOG_NVMF_FC_POLLER_API) | |
1678 | SPDK_LOG_REGISTER_COMPONENT("nvmf_fc_ls", SPDK_LOG_NVMF_FC_LS) |