xref: /spdk/lib/nvmf/fc_ls.c (revision 6b6dfea6c704a049e553024aa7e44ae916948e20)
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, FCNVME_ASSOC_HOSTNQN_LEN);
291 	memcpy(assoc->sub_nqn, a_cmd->subnqn, FCNVME_ASSOC_HOSTNQN_LEN);
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 	spdk_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 	spdk_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 	struct spdk_nvmf_fc_nport *tgtport = assoc->tgtport;
568 	struct spdk_nvmf_fc_port *fc_port = tgtport->fc_port;
569 
570 	SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Add Connection to poller for "
571 		      "assoc_id 0x%lx conn_id 0x%lx\n", assoc->assoc_id,
572 		      fc_conn->conn_id);
573 
574 	opd = calloc(1, sizeof(struct nvmf_fc_ls_op_ctx));
575 	if (!opd) {
576 		SPDK_ERRLOG("allocate api data for add conn op failed\n");
577 		nvmf_fc_ls_add_conn_failure(assoc, ls_rqst, fc_conn, aq_conn);
578 		return;
579 	}
580 
581 	/* insert conn in association's connection list */
582 	api_data = &opd->u.add_conn;
583 	assoc->conn_count++;
584 
585 	api_data->args.fc_conn = fc_conn;
586 	api_data->args.cb_info.cb_thread = spdk_get_thread();
587 	api_data->args.cb_info.cb_func = nvmf_fc_ls_add_conn_cb;
588 	api_data->args.cb_info.cb_data = (void *)opd;
589 	api_data->assoc = assoc;
590 	api_data->ls_rqst = ls_rqst;
591 	api_data->aq_conn = aq_conn;
592 
593 	SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
594 		      "New QP callback called.\n");
595 
596 	/* Let the nvmf_tgt decide which pollgroup to use. */
597 	fc_conn->create_opd = opd;
598 	fc_port->new_qp_cb(&fc_conn->qpair);
599 }
600 
601 /* Delete association functions */
602 
603 static void
604 nvmf_fc_do_del_assoc_cbs(struct nvmf_fc_ls_op_ctx *opd, int ret)
605 {
606 	struct nvmf_fc_ls_op_ctx *nxt;
607 	struct spdk_nvmf_fc_delete_assoc_api_data *dp;
608 
609 	while (opd) {
610 		dp = &opd->u.del_assoc;
611 
612 		SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "performing delete assoc. callback\n");
613 		dp->del_assoc_cb(dp->del_assoc_cb_data, ret);
614 
615 		nxt = opd->next_op_ctx;
616 		free(opd);
617 		opd = nxt;
618 	}
619 }
620 
621 static void
622 nvmf_fs_send_ls_disconnect_cb(void *hwqp, int32_t status, void *args)
623 {
624 	if (args) {
625 		SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "free disconnect buffers\n");
626 		nvmf_fc_free_srsr_bufs((struct spdk_nvmf_fc_srsr_bufs *)args);
627 	}
628 }
629 
630 static void
631 nvmf_fc_del_all_conns_cb(void *cb_data, enum spdk_nvmf_fc_poller_api_ret ret)
632 {
633 	struct nvmf_fc_ls_op_ctx *opd = (struct nvmf_fc_ls_op_ctx *)cb_data;
634 	struct spdk_nvmf_fc_delete_assoc_api_data *dp = &opd->u.del_assoc;
635 	struct spdk_nvmf_fc_association *assoc = dp->assoc;
636 	struct spdk_nvmf_fc_conn *fc_conn = dp->args.fc_conn;
637 
638 	/* Assumption here is that there will be no error (i.e. ret=success).
639 	 * Since connections are deleted in parallel, nothing can be
640 	 * done anyway if there is an error because we need to complete
641 	 * all connection deletes and callback to caller */
642 
643 	SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
644 		      "Delete all connections for assoc_id 0x%lx, conn_id = %lx\n",
645 		      assoc->assoc_id, fc_conn->conn_id);
646 
647 	/* remove connection from association's connection list */
648 	TAILQ_REMOVE(&assoc->fc_conns, fc_conn, assoc_link);
649 	nvmf_fc_ls_free_connection(fc_conn);
650 
651 	if (--assoc->conn_count == 0) {
652 		/* last connection - remove association from target port's association list */
653 		struct nvmf_fc_ls_op_ctx *cb_opd = (struct nvmf_fc_ls_op_ctx *)assoc->ls_del_op_ctx;
654 
655 		SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
656 			      "remove assoc. %lx\n", assoc->assoc_id);
657 		nvmf_fc_del_assoc_from_tgt_port(assoc);
658 
659 		if (assoc->snd_disconn_bufs &&
660 		    assoc->tgtport->fc_port->hw_port_status == SPDK_FC_PORT_ONLINE) {
661 
662 			struct spdk_nvmf_fc_ls_disconnect_rqst *dc_rqst;
663 			struct spdk_nvmf_fc_srsr_bufs *srsr_bufs;
664 
665 			dc_rqst = (struct spdk_nvmf_fc_ls_disconnect_rqst *)
666 				  assoc->snd_disconn_bufs->rqst;
667 
668 			bzero(dc_rqst, sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst));
669 
670 			/* fill in request descriptor */
671 			dc_rqst->w0.ls_cmd = FCNVME_LS_DISCONNECT;
672 			to_be32(&dc_rqst->desc_list_len,
673 				sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst) -
674 				(2 * sizeof(uint32_t)));
675 
676 			/* fill in disconnect command descriptor */
677 			to_be32(&dc_rqst->disconn_cmd.desc_tag, FCNVME_LSDESC_DISCONN_CMD);
678 			to_be32(&dc_rqst->disconn_cmd.desc_len,
679 				sizeof(struct spdk_nvmf_fc_lsdesc_disconn_cmd) -
680 				(2 * sizeof(uint32_t)));
681 
682 			/* fill in association id descriptor */
683 			to_be32(&dc_rqst->assoc_id.desc_tag, FCNVME_LSDESC_ASSOC_ID),
684 				to_be32(&dc_rqst->assoc_id.desc_len,
685 					sizeof(struct spdk_nvmf_fc_lsdesc_assoc_id) -
686 					(2 * sizeof(uint32_t)));
687 			to_be64(&dc_rqst->assoc_id.association_id, assoc->assoc_id);
688 
689 			srsr_bufs = assoc->snd_disconn_bufs;
690 			assoc->snd_disconn_bufs = NULL;
691 
692 			SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Send LS disconnect\n");
693 			if (spdk_nvmf_fc_xmt_srsr_req(&assoc->tgtport->fc_port->ls_queue,
694 						      srsr_bufs, nvmf_fs_send_ls_disconnect_cb,
695 						      (void *)srsr_bufs)) {
696 				SPDK_ERRLOG("Error sending LS disconnect\n");
697 				assoc->snd_disconn_bufs = srsr_bufs;
698 			}
699 		}
700 
701 		nvmf_fc_ls_free_association(assoc);
702 
703 		/* perform callbacks to all callers to delete association */
704 		nvmf_fc_do_del_assoc_cbs(cb_opd, 0);
705 
706 	}
707 
708 	free(opd);
709 }
710 
711 static void
712 nvmf_fc_kill_io_del_all_conns_cb(void *cb_data, enum spdk_nvmf_fc_poller_api_ret ret)
713 {
714 	struct nvmf_fc_ls_op_ctx *opd = (struct nvmf_fc_ls_op_ctx *)cb_data;
715 
716 	SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Callback after killing outstanding ABTS.");
717 	/*
718 	 * NOTE: We should not access any connection or association related data
719 	 * structures here.
720 	 */
721 	free(opd);
722 }
723 
724 
725 /* Disconnect/delete (association) request functions */
726 
727 static int
728 nvmf_fc_delete_association(struct spdk_nvmf_fc_nport *tgtport,
729 			   uint64_t assoc_id, bool send_abts, bool backend_initiated,
730 			   spdk_nvmf_fc_del_assoc_cb del_assoc_cb,
731 			   void *cb_data, bool from_ls_rqst)
732 {
733 
734 	struct nvmf_fc_ls_op_ctx *opd, *opd_tail, *opd_head = NULL;
735 	struct spdk_nvmf_fc_delete_assoc_api_data *api_data;
736 	struct spdk_nvmf_fc_conn *fc_conn;
737 	struct spdk_nvmf_fc_association *assoc =
738 		nvmf_fc_ls_find_assoc(tgtport, assoc_id);
739 	struct spdk_nvmf_fc_port *fc_port = tgtport->fc_port;
740 	enum spdk_nvmf_fc_object_state assoc_state;
741 
742 	SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Delete association, "
743 		      "assoc_id 0x%lx\n", assoc_id);
744 
745 	if (!assoc) {
746 		SPDK_ERRLOG("Delete association failed: %s\n",
747 			    validation_errors[VERR_NO_ASSOC]);
748 		return VERR_NO_ASSOC;
749 	}
750 
751 	/* create cb context to put in association's list of
752 	 * callbacks to call when delete association is done */
753 	opd = calloc(1, sizeof(struct nvmf_fc_ls_op_ctx));
754 	if (!opd) {
755 		SPDK_ERRLOG("Mem alloc failed for del assoc cb data");
756 		return -ENOMEM;
757 	}
758 
759 	api_data = &opd->u.del_assoc;
760 	api_data->assoc = assoc;
761 	api_data->from_ls_rqst = from_ls_rqst;
762 	api_data->del_assoc_cb = del_assoc_cb;
763 	api_data->del_assoc_cb_data = cb_data;
764 	api_data->args.cb_info.cb_data = opd;
765 	nvmf_fc_ls_append_del_cb_ctx(assoc, opd);
766 
767 	assoc_state = assoc->assoc_state;
768 	if ((assoc_state == SPDK_NVMF_FC_OBJECT_TO_BE_DELETED) &&
769 	    (fc_port->hw_port_status != SPDK_FC_PORT_QUIESCED)) {
770 		/* association already being deleted */
771 		return 0;
772 	}
773 
774 	/* mark assoc. to be deleted */
775 	assoc->assoc_state = SPDK_NVMF_FC_OBJECT_TO_BE_DELETED;
776 
777 	/* create a list of all connection to delete */
778 	TAILQ_FOREACH(fc_conn, &assoc->fc_conns, assoc_link) {
779 		opd = calloc(1, sizeof(struct nvmf_fc_ls_op_ctx));
780 		if (!opd) { /* hopefully this doesn't happen */
781 			SPDK_ERRLOG("Mem alloc failed for del conn op data");
782 			while (opd_head) { /* free any contexts already allocated */
783 				opd = opd_head;
784 				opd_head = opd->next_op_ctx;
785 				free(opd);
786 			}
787 			return -ENOMEM;
788 		}
789 
790 		api_data = &opd->u.del_assoc;
791 		api_data->args.fc_conn = fc_conn;
792 		api_data->assoc = assoc;
793 		api_data->args.send_abts = send_abts;
794 		api_data->args.backend_initiated = backend_initiated;
795 		api_data->args.hwqp = nvmf_fc_get_hwqp_from_conn_id(
796 					      assoc->tgtport->fc_port->io_queues,
797 					      assoc->tgtport->fc_port->num_io_queues,
798 					      fc_conn->conn_id);
799 		api_data->args.cb_info.cb_thread = spdk_get_thread();
800 		if ((fc_port->hw_port_status == SPDK_FC_PORT_QUIESCED) &&
801 		    (assoc_state == SPDK_NVMF_FC_OBJECT_TO_BE_DELETED)) {
802 			/*
803 			 * If there are any connections deletes or IO abts that are
804 			 * stuck because of firmware reset, a second invocation of
805 			 * SPDK_NVMF_FC_POLLER_API_DEL_CONNECTION will result in
806 			 * outstanding connections & requests being killed and
807 			 * their corresponding callbacks being executed.
808 			 */
809 			api_data->args.cb_info.cb_func = nvmf_fc_kill_io_del_all_conns_cb;
810 		} else {
811 			api_data->args.cb_info.cb_func = nvmf_fc_del_all_conns_cb;
812 		}
813 		api_data->args.cb_info.cb_data = opd;
814 		SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
815 			      "conn_id = %lx\n", fc_conn->conn_id);
816 
817 		if (!opd_head) {
818 			opd_head = opd;
819 		} else {
820 			opd_tail->next_op_ctx = opd;
821 		}
822 		opd_tail = opd;
823 	}
824 
825 	/* make poller api calls to delete connetions */
826 	while (opd_head) {
827 		opd = opd_head;
828 		opd_head = opd->next_op_ctx;
829 		api_data = &opd->u.del_assoc;
830 		spdk_nvmf_fc_poller_api_func(api_data->args.hwqp,
831 					     SPDK_NVMF_FC_POLLER_API_DEL_CONNECTION,
832 					     &api_data->args);
833 	}
834 
835 	return 0;
836 }
837 
838 static void
839 nvmf_fc_ls_disconnect_assoc_cb(void *cb_data, uint32_t err)
840 {
841 	struct nvmf_fc_ls_op_ctx *opd = (struct nvmf_fc_ls_op_ctx *)cb_data;
842 	struct spdk_nvmf_fc_ls_disconn_assoc_api_data *dp = &opd->u.disconn_assoc;
843 	struct spdk_nvmf_fc_nport *tgtport = dp->tgtport;
844 	struct spdk_nvmf_fc_ls_rqst *ls_rqst = dp->ls_rqst;
845 
846 	SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Disconnect association callback begin "
847 		      "nport %d\n", tgtport->nport_hdl);
848 	if (err != 0) {
849 		/* send failure response */
850 		struct spdk_nvmf_fc_ls_cr_assoc_rqst *rqst =
851 			(struct spdk_nvmf_fc_ls_cr_assoc_rqst *)ls_rqst->rqstbuf.virt;
852 		struct spdk_nvmf_fc_ls_cr_assoc_acc *acc =
853 			(struct spdk_nvmf_fc_ls_cr_assoc_acc *)ls_rqst->rspbuf.virt;
854 		ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc,
855 				   FCNVME_MAX_LS_BUFFER_SIZE,
856 				   rqst->w0.ls_cmd,
857 				   FCNVME_RJT_RC_UNAB,
858 				   FCNVME_RJT_EXP_NONE,
859 				   0);
860 	}
861 
862 	nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst);
863 
864 	free(opd);
865 	SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Disconnect association callback complete "
866 		      "nport %d err %d\n", tgtport->nport_hdl, err);
867 }
868 
869 static void
870 nvmf_fc_ls_disconnect_assoc(struct spdk_nvmf_fc_nport *tgtport,
871 			    struct spdk_nvmf_fc_ls_rqst *ls_rqst, uint64_t assoc_id)
872 {
873 	struct nvmf_fc_ls_op_ctx *opd;
874 	struct spdk_nvmf_fc_ls_cr_assoc_rqst *rqst =
875 		(struct spdk_nvmf_fc_ls_cr_assoc_rqst *)ls_rqst->rqstbuf.virt;
876 	struct spdk_nvmf_fc_ls_cr_assoc_acc *acc =
877 		(struct spdk_nvmf_fc_ls_cr_assoc_acc *)ls_rqst->rspbuf.virt;
878 	struct spdk_nvmf_fc_ls_disconn_assoc_api_data *api_data;
879 	int ret;
880 	uint8_t reason = 0;
881 
882 	opd = calloc(1, sizeof(struct nvmf_fc_ls_op_ctx));
883 	if (!opd) {
884 		/* send failure response */
885 		SPDK_ERRLOG("Allocate disconn assoc op data failed\n");
886 		reason = FCNVME_RJT_RC_INSUFF_RES;
887 		goto send_rjt;
888 	}
889 
890 	api_data = &opd->u.disconn_assoc;
891 	api_data->tgtport = tgtport;
892 	api_data->ls_rqst = ls_rqst;
893 	ret = nvmf_fc_delete_association(tgtport, assoc_id,
894 					 false, false,
895 					 nvmf_fc_ls_disconnect_assoc_cb,
896 					 api_data, true);
897 	if (!ret) {
898 		return;
899 	}
900 
901 	/* delete association failed */
902 	switch (ret) {
903 	case VERR_NO_ASSOC:
904 		reason = FCNVME_RJT_RC_INV_ASSOC;
905 		break;
906 	case -ENOMEM:
907 		reason = FCNVME_RJT_RC_INSUFF_RES;
908 		break;
909 	default:
910 		reason = FCNVME_RJT_RC_LOGIC;
911 	}
912 
913 	free(opd);
914 
915 send_rjt:
916 	ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc,
917 			   FCNVME_MAX_LS_BUFFER_SIZE,
918 			   rqst->w0.ls_cmd, reason,
919 			   FCNVME_RJT_EXP_NONE, 0);
920 	nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst);
921 }
922 
923 static int
924 nvmf_fc_ls_validate_host(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn)
925 {
926 
927 	if (!spdk_nvmf_subsystem_host_allowed(subsystem, hostnqn)) {
928 		return -EPERM;
929 	}
930 
931 	return 0;
932 }
933 
934 /* **************************** */
935 /* LS Reqeust Handler Functions */
936 
937 static void
938 nvmf_fc_ls_process_cass(uint32_t s_id,
939 			struct spdk_nvmf_fc_nport *tgtport,
940 			struct spdk_nvmf_fc_ls_rqst *ls_rqst)
941 {
942 	struct spdk_nvmf_fc_ls_cr_assoc_rqst *rqst =
943 		(struct spdk_nvmf_fc_ls_cr_assoc_rqst *)ls_rqst->rqstbuf.virt;
944 	struct spdk_nvmf_fc_ls_cr_assoc_acc *acc =
945 		(struct spdk_nvmf_fc_ls_cr_assoc_acc *)ls_rqst->rspbuf.virt;
946 	struct spdk_nvmf_fc_association *assoc;
947 	struct spdk_nvmf_fc_conn *fc_conn;
948 	struct spdk_nvmf_subsystem *subsystem = NULL;
949 	const char *hostnqn = (const char *)rqst->assoc_cmd.hostnqn;
950 	int errmsg_ind = 0;
951 	uint8_t rc = FCNVME_RJT_RC_NONE;
952 	uint8_t ec = FCNVME_RJT_EXP_NONE;
953 	struct spdk_nvmf_transport *transport = spdk_nvmf_tgt_get_transport(ls_rqst->nvmf_tgt,
954 						(enum spdk_nvme_transport_type) SPDK_NVMF_TRTYPE_FC);
955 
956 	SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
957 		      "LS_CASS: ls_rqst_len=%d, desc_list_len=%d, cmd_len=%d, sq_size=%d, "
958 		      "Subnqn: %s, Hostnqn: %s, Tgtport nn:%lx, pn:%lx\n",
959 		      ls_rqst->rqst_len, from_be32(&rqst->desc_list_len),
960 		      from_be32(&rqst->assoc_cmd.desc_len),
961 		      from_be32(&rqst->assoc_cmd.sqsize),
962 		      rqst->assoc_cmd.subnqn, hostnqn,
963 		      tgtport->fc_nodename.u.wwn, tgtport->fc_portname.u.wwn);
964 
965 	if (ls_rqst->rqst_len < FCNVME_LS_CA_CMD_MIN_LEN) {
966 		SPDK_ERRLOG("assoc_cmd req len = %d, should be at least %d\n",
967 			    ls_rqst->rqst_len, FCNVME_LS_CA_CMD_MIN_LEN);
968 		errmsg_ind = VERR_CR_ASSOC_LEN;
969 		rc = FCNVME_RJT_RC_INV_PARAM;
970 		ec = FCNVME_RJT_EXP_INV_LEN;
971 	} else if (from_be32(&rqst->desc_list_len) <
972 		   FCNVME_LS_CA_DESC_LIST_MIN_LEN) {
973 		SPDK_ERRLOG("assoc_cmd desc list len = %d, should be at least %d\n",
974 			    from_be32(&rqst->desc_list_len),
975 			    FCNVME_LS_CA_DESC_LIST_MIN_LEN);
976 		errmsg_ind = VERR_CR_ASSOC_RQST_LEN;
977 		rc = FCNVME_RJT_RC_INV_PARAM;
978 		ec = FCNVME_RJT_EXP_INV_LEN;
979 	} else if (rqst->assoc_cmd.desc_tag !=
980 		   cpu_to_be32(FCNVME_LSDESC_CREATE_ASSOC_CMD)) {
981 		errmsg_ind = VERR_CR_ASSOC_CMD;
982 		rc = FCNVME_RJT_RC_INV_PARAM;
983 	} else if (from_be32(&rqst->assoc_cmd.desc_len) <
984 		   FCNVME_LS_CA_DESC_MIN_LEN) {
985 		SPDK_ERRLOG("assoc_cmd desc len = %d, should be at least %d\n",
986 			    from_be32(&rqst->assoc_cmd.desc_len),
987 			    FCNVME_LS_CA_DESC_MIN_LEN);
988 		errmsg_ind = VERR_CR_ASSOC_CMD_LEN;
989 		rc = FCNVME_RJT_RC_INV_PARAM;
990 		ec = FCNVME_RJT_EXP_INV_LEN;
991 	} else if (!rqst->assoc_cmd.ersp_ratio ||
992 		   (from_be16(&rqst->assoc_cmd.ersp_ratio) >=
993 		    from_be16(&rqst->assoc_cmd.sqsize))) {
994 		errmsg_ind = VERR_ERSP_RATIO;
995 		rc = FCNVME_RJT_RC_INV_PARAM;
996 		ec = FCNVME_RJT_EXP_INV_ESRP;
997 	} else if (from_be16(&rqst->assoc_cmd.sqsize) == 0 ||
998 		   from_be16(&rqst->assoc_cmd.sqsize) > transport->opts.max_aq_depth) {
999 		errmsg_ind = VERR_SQSIZE;
1000 		rc = FCNVME_RJT_RC_INV_PARAM;
1001 		ec = FCNVME_RJT_EXP_SQ_SIZE;
1002 	}
1003 
1004 	if (rc != FCNVME_RJT_RC_NONE) {
1005 		goto rjt_cass;
1006 	}
1007 
1008 	subsystem = spdk_nvmf_tgt_find_subsystem(ls_rqst->nvmf_tgt, rqst->assoc_cmd.subnqn);
1009 	if (subsystem == NULL) {
1010 		errmsg_ind = VERR_SUBNQN;
1011 		rc = FCNVME_RJT_RC_INV_PARAM;
1012 		ec = FCNVME_RJT_EXP_INV_SUBNQN;
1013 		goto rjt_cass;
1014 	}
1015 
1016 	if (nvmf_fc_ls_validate_host(subsystem, hostnqn)) {
1017 		errmsg_ind = VERR_HOSTNQN;
1018 		rc = FCNVME_RJT_RC_INV_HOST;
1019 		ec = FCNVME_RJT_EXP_INV_HOSTNQN;
1020 		goto rjt_cass;
1021 	}
1022 
1023 	/* get new association */
1024 	assoc = nvmf_fc_ls_new_association(s_id, tgtport, ls_rqst->rport,
1025 					   &rqst->assoc_cmd, subsystem,
1026 					   ls_rqst->rpi, transport);
1027 	if (!assoc) {
1028 		errmsg_ind = VERR_ASSOC_ALLOC_FAIL;
1029 		rc = FCNVME_RJT_RC_INSUFF_RES;
1030 		ec = FCNVME_RJT_EXP_NONE;
1031 		goto rjt_cass;
1032 	}
1033 
1034 	/* alloc admin q (i.e. connection) */
1035 	fc_conn = nvmf_fc_ls_new_connection(assoc, 0,
1036 					    from_be16(&rqst->assoc_cmd.ersp_ratio),
1037 					    ls_rqst->rpi,
1038 					    from_be16(&rqst->assoc_cmd.sqsize),
1039 					    tgtport);
1040 	if (!fc_conn) {
1041 		nvmf_fc_ls_free_association(assoc);
1042 		errmsg_ind = VERR_CONN_ALLOC_FAIL;
1043 		rc = FCNVME_RJT_RC_INSUFF_RES;
1044 		ec = FCNVME_RJT_EXP_NONE;
1045 		goto rjt_cass;
1046 	}
1047 
1048 	/* format accept response */
1049 	bzero(acc, sizeof(*acc));
1050 	ls_rqst->rsp_len = sizeof(*acc);
1051 
1052 	nvmf_fc_ls_format_rsp_hdr(acc, FCNVME_LS_ACC,
1053 				  nvmf_fc_lsdesc_len(
1054 					  sizeof(struct spdk_nvmf_fc_ls_cr_assoc_acc)),
1055 				  FCNVME_LS_CREATE_ASSOCIATION);
1056 	to_be32(&acc->assoc_id.desc_tag, FCNVME_LSDESC_ASSOC_ID);
1057 	acc->assoc_id.desc_len =
1058 		nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_assoc_id));
1059 	to_be32(&acc->conn_id.desc_tag, FCNVME_LSDESC_CONN_ID);
1060 	acc->conn_id.desc_len =
1061 		nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_conn_id));
1062 
1063 	/* assign connection to HWQP poller - also sends response */
1064 	nvmf_fc_ls_add_conn_to_poller(assoc, ls_rqst, fc_conn, true);
1065 
1066 	return;
1067 
1068 rjt_cass:
1069 	SPDK_ERRLOG("Create Association LS failed: %s\n", validation_errors[errmsg_ind]);
1070 	ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc, FCNVME_MAX_LS_BUFFER_SIZE,
1071 			   rqst->w0.ls_cmd, rc, ec, 0);
1072 	nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst);
1073 }
1074 
1075 static void
1076 nvmf_fc_ls_process_cioc(struct spdk_nvmf_fc_nport *tgtport,
1077 			struct spdk_nvmf_fc_ls_rqst *ls_rqst)
1078 {
1079 	struct spdk_nvmf_fc_ls_cr_conn_rqst *rqst =
1080 		(struct spdk_nvmf_fc_ls_cr_conn_rqst *)ls_rqst->rqstbuf.virt;
1081 	struct spdk_nvmf_fc_ls_cr_conn_acc *acc =
1082 		(struct spdk_nvmf_fc_ls_cr_conn_acc *)ls_rqst->rspbuf.virt;
1083 	struct spdk_nvmf_fc_association *assoc;
1084 	struct spdk_nvmf_fc_conn *fc_conn = NULL;
1085 	int errmsg_ind = 0;
1086 	uint8_t rc = FCNVME_RJT_RC_NONE;
1087 	uint8_t ec = FCNVME_RJT_EXP_NONE;
1088 	struct spdk_nvmf_transport *transport = spdk_nvmf_tgt_get_transport(ls_rqst->nvmf_tgt,
1089 						(enum spdk_nvme_transport_type) SPDK_NVMF_TRTYPE_FC);
1090 
1091 	SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
1092 		      "LS_CIOC: ls_rqst_len=%d, desc_list_len=%d, cmd_len=%d, "
1093 		      "assoc_id=0x%lx, sq_size=%d, esrp=%d, Tgtport nn:%lx, pn:%lx\n",
1094 		      ls_rqst->rqst_len, from_be32(&rqst->desc_list_len),
1095 		      from_be32(&rqst->connect_cmd.desc_len),
1096 		      from_be64(&rqst->assoc_id.association_id),
1097 		      from_be32(&rqst->connect_cmd.sqsize),
1098 		      from_be32(&rqst->connect_cmd.ersp_ratio),
1099 		      tgtport->fc_nodename.u.wwn, tgtport->fc_portname.u.wwn);
1100 
1101 	if (ls_rqst->rqst_len < sizeof(struct spdk_nvmf_fc_ls_cr_conn_rqst)) {
1102 		errmsg_ind = VERR_CR_CONN_LEN;
1103 		rc = FCNVME_RJT_RC_INV_PARAM;
1104 		ec = FCNVME_RJT_EXP_INV_LEN;
1105 	} else if (rqst->desc_list_len !=
1106 		   nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_ls_cr_conn_rqst))) {
1107 		errmsg_ind = VERR_CR_CONN_RQST_LEN;
1108 		rc = FCNVME_RJT_RC_INV_PARAM;
1109 		ec = FCNVME_RJT_EXP_INV_LEN;
1110 	} else if (rqst->assoc_id.desc_tag !=
1111 		   cpu_to_be32(FCNVME_LSDESC_ASSOC_ID)) {
1112 		errmsg_ind = VERR_ASSOC_ID;
1113 		rc = FCNVME_RJT_RC_INV_PARAM;
1114 	} else if (rqst->assoc_id.desc_len !=
1115 		   nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_assoc_id))) {
1116 		errmsg_ind = VERR_ASSOC_ID_LEN;
1117 		rc = FCNVME_RJT_RC_INV_PARAM;
1118 		ec = FCNVME_RJT_EXP_INV_LEN;
1119 	} else if (rqst->connect_cmd.desc_tag !=
1120 		   cpu_to_be32(FCNVME_LSDESC_CREATE_CONN_CMD)) {
1121 		errmsg_ind = VERR_CR_CONN_CMD;
1122 		rc = FCNVME_RJT_RC_INV_PARAM;
1123 	} else if (rqst->connect_cmd.desc_len !=
1124 		   nvmf_fc_lsdesc_len(
1125 			   sizeof(struct spdk_nvmf_fc_lsdesc_cr_conn_cmd))) {
1126 		errmsg_ind = VERR_CR_CONN_CMD_LEN;
1127 		rc = FCNVME_RJT_RC_INV_PARAM;
1128 		ec = FCNVME_RJT_EXP_INV_LEN;
1129 	} else if (!rqst->connect_cmd.ersp_ratio ||
1130 		   (from_be16(&rqst->connect_cmd.ersp_ratio) >=
1131 		    from_be16(&rqst->connect_cmd.sqsize))) {
1132 		errmsg_ind = VERR_ERSP_RATIO;
1133 		rc = FCNVME_RJT_RC_INV_PARAM;
1134 		ec = FCNVME_RJT_EXP_INV_ESRP;
1135 	} else if (from_be16(&rqst->connect_cmd.sqsize) == 0 ||
1136 		   from_be16(&rqst->connect_cmd.sqsize) > transport->opts.max_queue_depth) {
1137 		errmsg_ind = VERR_SQSIZE;
1138 		rc = FCNVME_RJT_RC_INV_PARAM;
1139 		ec = FCNVME_RJT_EXP_SQ_SIZE;
1140 	}
1141 
1142 	if (rc != FCNVME_RJT_RC_NONE) {
1143 		goto rjt_cioc;
1144 	}
1145 
1146 	/* find association */
1147 	assoc = nvmf_fc_ls_find_assoc(tgtport,
1148 				      from_be64(&rqst->assoc_id.association_id));
1149 	if (!assoc) {
1150 		errmsg_ind = VERR_NO_ASSOC;
1151 		rc = FCNVME_RJT_RC_INV_ASSOC;
1152 	} else if (assoc->assoc_state == SPDK_NVMF_FC_OBJECT_TO_BE_DELETED) {
1153 		/* association is being deleted - don't allow more connections */
1154 		errmsg_ind = VERR_NO_ASSOC;
1155 		rc = FCNVME_RJT_RC_INV_ASSOC;
1156 	} else  if (assoc->conn_count >= transport->opts.max_qpairs_per_ctrlr) {
1157 		errmsg_ind = VERR_CONN_TOO_MANY;
1158 		rc = FCNVME_RJT_RC_INV_PARAM;
1159 		ec =  FCNVME_RJT_EXP_INV_Q_ID;
1160 	}
1161 
1162 	if (rc != FCNVME_RJT_RC_NONE) {
1163 		goto rjt_cioc;
1164 	}
1165 
1166 	fc_conn = nvmf_fc_ls_new_connection(assoc, from_be16(&rqst->connect_cmd.qid),
1167 					    from_be16(&rqst->connect_cmd.ersp_ratio),
1168 					    ls_rqst->rpi,
1169 					    from_be16(&rqst->connect_cmd.sqsize),
1170 					    tgtport);
1171 	if (!fc_conn) {
1172 		errmsg_ind = VERR_CONN_ALLOC_FAIL;
1173 		rc = FCNVME_RJT_RC_INSUFF_RES;
1174 		ec = FCNVME_RJT_EXP_NONE;
1175 		goto rjt_cioc;
1176 	}
1177 
1178 	/* format accept response */
1179 	SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "Formatting LS accept response for "
1180 		      "assoc_id 0x%lx conn_id 0x%lx\n", assoc->assoc_id,
1181 		      fc_conn->conn_id);
1182 	bzero(acc, sizeof(*acc));
1183 	ls_rqst->rsp_len = sizeof(*acc);
1184 	nvmf_fc_ls_format_rsp_hdr(acc, FCNVME_LS_ACC,
1185 				  nvmf_fc_lsdesc_len(
1186 					  sizeof(struct spdk_nvmf_fc_ls_cr_conn_acc)),
1187 				  FCNVME_LS_CREATE_CONNECTION);
1188 	to_be32(&acc->conn_id.desc_tag, FCNVME_LSDESC_CONN_ID);
1189 	acc->conn_id.desc_len =
1190 		nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_conn_id));
1191 
1192 	/* assign connection to HWQP poller - also sends response */
1193 	nvmf_fc_ls_add_conn_to_poller(assoc, ls_rqst, fc_conn, false);
1194 
1195 	return;
1196 
1197 rjt_cioc:
1198 	SPDK_ERRLOG("Create Connection LS failed: %s\n", validation_errors[errmsg_ind]);
1199 
1200 	ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc, FCNVME_MAX_LS_BUFFER_SIZE,
1201 			   rqst->w0.ls_cmd, rc, ec, 0);
1202 	nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst);
1203 }
1204 
1205 static void
1206 nvmf_fc_ls_process_disc(struct spdk_nvmf_fc_nport *tgtport,
1207 			struct spdk_nvmf_fc_ls_rqst *ls_rqst)
1208 {
1209 	struct spdk_nvmf_fc_ls_disconnect_rqst *rqst =
1210 		(struct spdk_nvmf_fc_ls_disconnect_rqst *)ls_rqst->rqstbuf.virt;
1211 	struct spdk_nvmf_fc_ls_disconnect_acc *acc =
1212 		(struct spdk_nvmf_fc_ls_disconnect_acc *)ls_rqst->rspbuf.virt;
1213 	struct spdk_nvmf_fc_association *assoc;
1214 	int errmsg_ind = 0;
1215 	uint8_t rc = FCNVME_RJT_RC_NONE;
1216 	uint8_t ec = FCNVME_RJT_EXP_NONE;
1217 
1218 	SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS,
1219 		      "LS_DISC: ls_rqst_len=%d, desc_list_len=%d, cmd_len=%d,"
1220 		      "assoc_id=0x%lx\n",
1221 		      ls_rqst->rqst_len, from_be32(&rqst->desc_list_len),
1222 		      from_be32(&rqst->disconn_cmd.desc_len),
1223 		      from_be64(&rqst->assoc_id.association_id));
1224 
1225 	if (ls_rqst->rqst_len < sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst)) {
1226 		errmsg_ind = VERR_DISCONN_LEN;
1227 		rc = FCNVME_RJT_RC_INV_PARAM;
1228 		ec = FCNVME_RJT_EXP_INV_LEN;
1229 	} else if (rqst->desc_list_len !=
1230 		   nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_ls_disconnect_rqst))) {
1231 		errmsg_ind = VERR_DISCONN_RQST_LEN;
1232 		rc = FCNVME_RJT_RC_INV_PARAM;
1233 		ec = FCNVME_RJT_EXP_INV_LEN;
1234 	} else if (rqst->assoc_id.desc_tag !=
1235 		   cpu_to_be32(FCNVME_LSDESC_ASSOC_ID)) {
1236 		errmsg_ind = VERR_ASSOC_ID;
1237 		rc = FCNVME_RJT_RC_INV_PARAM;
1238 	} else if (rqst->assoc_id.desc_len !=
1239 		   nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_assoc_id))) {
1240 		errmsg_ind = VERR_ASSOC_ID_LEN;
1241 		rc = FCNVME_RJT_RC_INV_PARAM;
1242 		ec = FCNVME_RJT_EXP_INV_LEN;
1243 	} else if (rqst->disconn_cmd.desc_tag !=
1244 		   cpu_to_be32(FCNVME_LSDESC_DISCONN_CMD)) {
1245 		rc = FCNVME_RJT_RC_INV_PARAM;
1246 		errmsg_ind = VERR_DISCONN_CMD;
1247 	} else if (rqst->disconn_cmd.desc_len !=
1248 		   nvmf_fc_lsdesc_len(sizeof(struct spdk_nvmf_fc_lsdesc_disconn_cmd))) {
1249 		errmsg_ind = VERR_DISCONN_CMD_LEN;
1250 		rc = FCNVME_RJT_RC_INV_PARAM;
1251 		ec = FCNVME_RJT_EXP_INV_LEN;
1252 	}
1253 
1254 	if (rc != FCNVME_RJT_RC_NONE) {
1255 		goto rjt_disc;
1256 	}
1257 
1258 	/* match an active association */
1259 	assoc = nvmf_fc_ls_find_assoc(tgtport,
1260 				      from_be64(&rqst->assoc_id.association_id));
1261 	if (!assoc) {
1262 		errmsg_ind = VERR_NO_ASSOC;
1263 		rc = FCNVME_RJT_RC_INV_ASSOC;
1264 		goto rjt_disc;
1265 	}
1266 
1267 	/* format response */
1268 	bzero(acc, sizeof(*acc));
1269 	ls_rqst->rsp_len = sizeof(*acc);
1270 
1271 	nvmf_fc_ls_format_rsp_hdr(acc, FCNVME_LS_ACC,
1272 				  nvmf_fc_lsdesc_len(
1273 					  sizeof(struct spdk_nvmf_fc_ls_disconnect_acc)),
1274 				  FCNVME_LS_DISCONNECT);
1275 
1276 	nvmf_fc_ls_disconnect_assoc(tgtport, ls_rqst, assoc->assoc_id);
1277 	return;
1278 
1279 rjt_disc:
1280 	SPDK_ERRLOG("Disconnect LS failed: %s\n", validation_errors[errmsg_ind]);
1281 	ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(acc, FCNVME_MAX_LS_BUFFER_SIZE,
1282 			   rqst->w0.ls_cmd, rc, ec, 0);
1283 	nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst);
1284 }
1285 
1286 /* ************************ */
1287 /* external functions       */
1288 
1289 void
1290 spdk_nvmf_fc_ls_init(struct spdk_nvmf_fc_port *fc_port)
1291 {
1292 }
1293 
1294 void
1295 spdk_nvmf_fc_ls_fini(struct spdk_nvmf_fc_port *fc_port)
1296 {
1297 }
1298 
1299 void
1300 spdk_nvmf_fc_handle_ls_rqst(struct spdk_nvmf_fc_ls_rqst *ls_rqst)
1301 {
1302 	struct spdk_nvmf_fc_ls_rqst_w0 *w0 =
1303 		(struct spdk_nvmf_fc_ls_rqst_w0 *)ls_rqst->rqstbuf.virt;
1304 	uint32_t s_id = ls_rqst->s_id;
1305 	struct spdk_nvmf_fc_nport *tgtport = ls_rqst->nport;
1306 
1307 	SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_LS, "LS cmd=%d\n", w0->ls_cmd);
1308 
1309 	switch (w0->ls_cmd) {
1310 	case FCNVME_LS_CREATE_ASSOCIATION:
1311 		nvmf_fc_ls_process_cass(s_id, tgtport, ls_rqst);
1312 		break;
1313 	case FCNVME_LS_CREATE_CONNECTION:
1314 		nvmf_fc_ls_process_cioc(tgtport, ls_rqst);
1315 		break;
1316 	case FCNVME_LS_DISCONNECT:
1317 		nvmf_fc_ls_process_disc(tgtport, ls_rqst);
1318 		break;
1319 	default:
1320 		SPDK_ERRLOG("Invalid LS cmd=%d\n", w0->ls_cmd);
1321 		ls_rqst->rsp_len = nvmf_fc_ls_format_rjt(ls_rqst->rspbuf.virt,
1322 				   FCNVME_MAX_LS_BUFFER_SIZE, w0->ls_cmd,
1323 				   FCNVME_RJT_RC_INVAL, FCNVME_RJT_EXP_NONE, 0);
1324 		nvmf_fc_xmt_ls_rsp(tgtport, ls_rqst);
1325 	}
1326 }
1327 
1328 int
1329 spdk_nvmf_fc_delete_association(struct spdk_nvmf_fc_nport *tgtport,
1330 				uint64_t assoc_id, bool send_abts, bool backend_initiated,
1331 				spdk_nvmf_fc_del_assoc_cb del_assoc_cb,
1332 				void *cb_data)
1333 {
1334 	return nvmf_fc_delete_association(tgtport, assoc_id, send_abts, backend_initiated,
1335 					  del_assoc_cb, cb_data, false);
1336 }
1337 
1338 static void
1339 nvmf_fc_poller_api_cb_event(void *arg)
1340 {
1341 	struct spdk_nvmf_fc_poller_api_cb_info *cb_info =
1342 		(struct spdk_nvmf_fc_poller_api_cb_info *) arg;
1343 
1344 	assert(cb_info != NULL);
1345 	cb_info->cb_func(cb_info->cb_data, cb_info->ret);
1346 }
1347 
1348 static void
1349 nvmf_fc_poller_api_perform_cb(struct spdk_nvmf_fc_poller_api_cb_info *cb_info,
1350 			      enum spdk_nvmf_fc_poller_api_ret ret)
1351 {
1352 	if (cb_info->cb_func && cb_info->cb_thread) {
1353 		cb_info->ret = ret;
1354 		/* callback to master thread */
1355 		spdk_thread_send_msg(cb_info->cb_thread, nvmf_fc_poller_api_cb_event,
1356 				     (void *) cb_info);
1357 	}
1358 }
1359 
1360 static void
1361 nvmf_fc_poller_api_add_connection(void *arg)
1362 {
1363 	enum spdk_nvmf_fc_poller_api_ret ret = SPDK_NVMF_FC_POLLER_API_SUCCESS;
1364 	struct spdk_nvmf_fc_poller_api_add_connection_args *conn_args =
1365 		(struct spdk_nvmf_fc_poller_api_add_connection_args *)arg;
1366 	struct spdk_nvmf_fc_conn *fc_conn;
1367 
1368 	SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, "Poller add connection, conn_id 0x%lx\n",
1369 		      conn_args->fc_conn->conn_id);
1370 
1371 	/* make sure connection is not already in poller's list */
1372 	fc_conn = spdk_nvmf_fc_hwqp_find_fc_conn(conn_args->fc_conn->hwqp,
1373 			conn_args->fc_conn->conn_id);
1374 	if (fc_conn) {
1375 		SPDK_ERRLOG("duplicate connection found");
1376 		ret = SPDK_NVMF_FC_POLLER_API_DUP_CONN_ID;
1377 	} else {
1378 		SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API,
1379 			      "conn_id=%lx", fc_conn->conn_id);
1380 		TAILQ_INSERT_TAIL(&conn_args->fc_conn->hwqp->connection_list,
1381 				  conn_args->fc_conn, link);
1382 	}
1383 
1384 	/* perform callback */
1385 	nvmf_fc_poller_api_perform_cb(&conn_args->cb_info, ret);
1386 }
1387 
1388 static void
1389 nvmf_fc_poller_api_quiesce_queue(void *arg)
1390 {
1391 	struct spdk_nvmf_fc_poller_api_quiesce_queue_args *q_args =
1392 		(struct spdk_nvmf_fc_poller_api_quiesce_queue_args *) arg;
1393 	struct spdk_nvmf_fc_request *fc_req = NULL, *tmp;
1394 
1395 	/* should be already, but make sure queue is quiesced */
1396 	q_args->hwqp->state = SPDK_FC_HWQP_OFFLINE;
1397 
1398 	/*
1399 	 * Kill all the outstanding commands that are in the transfer state and
1400 	 * in the process of being aborted.
1401 	 * We can run into this situation if an adapter reset happens when an I_T Nexus delete
1402 	 * is in progress.
1403 	 */
1404 	TAILQ_FOREACH_SAFE(fc_req, &q_args->hwqp->in_use_reqs, link, tmp) {
1405 		if (spdk_nvmf_fc_req_in_xfer(fc_req) && fc_req->is_aborted == true) {
1406 			spdk_nvmf_fc_poller_api_func(q_args->hwqp, SPDK_NVMF_FC_POLLER_API_REQ_ABORT_COMPLETE,
1407 						     (void *)fc_req);
1408 		}
1409 	}
1410 
1411 	/* perform callback */
1412 	nvmf_fc_poller_api_perform_cb(&q_args->cb_info, SPDK_NVMF_FC_POLLER_API_SUCCESS);
1413 }
1414 
1415 static void
1416 nvmf_fc_poller_api_activate_queue(void *arg)
1417 {
1418 	struct spdk_nvmf_fc_poller_api_quiesce_queue_args *q_args =
1419 		(struct spdk_nvmf_fc_poller_api_quiesce_queue_args *) arg;
1420 
1421 	q_args->hwqp->state = SPDK_FC_HWQP_ONLINE;
1422 
1423 	/* perform callback */
1424 	nvmf_fc_poller_api_perform_cb(&q_args->cb_info, 0);
1425 }
1426 
1427 static void
1428 nvmf_fc_disconnect_qpair_cb(void *ctx)
1429 {
1430 	struct spdk_nvmf_fc_poller_api_cb_info *cb_info = ctx;
1431 	/* perform callback */
1432 	nvmf_fc_poller_api_perform_cb(cb_info, SPDK_NVMF_FC_POLLER_API_SUCCESS);
1433 }
1434 
1435 static void
1436 nvmf_fc_poller_conn_abort_done(void *hwqp, int32_t status, void *cb_args)
1437 {
1438 	struct spdk_nvmf_fc_poller_api_del_connection_args *conn_args = cb_args;
1439 
1440 	if (conn_args->fc_request_cnt) {
1441 		conn_args->fc_request_cnt -= 1;
1442 	}
1443 
1444 	if (!conn_args->fc_request_cnt) {
1445 		if (!TAILQ_EMPTY(&conn_args->hwqp->connection_list)) {
1446 			/* All the requests for this connection are aborted. */
1447 			TAILQ_REMOVE(&conn_args->hwqp->connection_list,	conn_args->fc_conn, link);
1448 
1449 			SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, "Connection deleted, conn_id 0x%lx\n",
1450 				      conn_args->fc_conn->conn_id);
1451 
1452 			if (!conn_args->backend_initiated) {
1453 				/* disconnect qpair from nvmf controller */
1454 				spdk_nvmf_qpair_disconnect(&conn_args->fc_conn->qpair,
1455 							   nvmf_fc_disconnect_qpair_cb, &conn_args->cb_info);
1456 			}
1457 		} else {
1458 			/*
1459 			 * Duplicate connection delete can happen if one is
1460 			 * coming in via an association disconnect and the other
1461 			 * is initiated by a port reset.
1462 			 */
1463 			SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, "Duplicate conn delete.");
1464 			/* perform callback */
1465 			nvmf_fc_poller_api_perform_cb(&conn_args->cb_info, SPDK_NVMF_FC_POLLER_API_SUCCESS);
1466 		}
1467 	}
1468 }
1469 
1470 static void
1471 nvmf_fc_poller_api_del_connection(void *arg)
1472 {
1473 	struct spdk_nvmf_fc_poller_api_del_connection_args *conn_args =
1474 		(struct spdk_nvmf_fc_poller_api_del_connection_args *)arg;
1475 	struct spdk_nvmf_fc_conn *fc_conn;
1476 	struct spdk_nvmf_fc_request *fc_req = NULL, *tmp;
1477 	struct spdk_nvmf_fc_hwqp *hwqp = conn_args->hwqp;
1478 
1479 	SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, "Poller delete connection, conn_id 0x%lx\n",
1480 		      conn_args->fc_conn->conn_id);
1481 
1482 	/* find the connection in poller's list */
1483 	fc_conn = spdk_nvmf_fc_hwqp_find_fc_conn(hwqp, conn_args->fc_conn->conn_id);
1484 	if (!fc_conn) {
1485 		/* perform callback */
1486 		nvmf_fc_poller_api_perform_cb(&conn_args->cb_info, SPDK_NVMF_FC_POLLER_API_NO_CONN_ID);
1487 		return;
1488 	}
1489 
1490 	conn_args->fc_request_cnt = 0;
1491 
1492 	TAILQ_FOREACH_SAFE(fc_req, &hwqp->in_use_reqs, link, tmp) {
1493 		if (fc_req->fc_conn->conn_id == fc_conn->conn_id) {
1494 			if (spdk_nvmf_qpair_is_admin_queue(&fc_conn->qpair) &&
1495 			    (fc_req->req.cmd->nvme_cmd.opc == SPDK_NVME_OPC_ASYNC_EVENT_REQUEST)) {
1496 				/* AER will be cleaned by spdk_nvmf_qpair_disconnect. */
1497 				continue;
1498 			}
1499 
1500 			conn_args->fc_request_cnt += 1;
1501 			spdk_nvmf_fc_request_abort(fc_req, conn_args->send_abts,
1502 						   nvmf_fc_poller_conn_abort_done,
1503 						   conn_args);
1504 		}
1505 	}
1506 
1507 	if (!conn_args->fc_request_cnt) {
1508 		SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API, "Connection deleted.\n");
1509 		TAILQ_REMOVE(&hwqp->connection_list, fc_conn, link);
1510 
1511 		if (!conn_args->backend_initiated) {
1512 			/* disconnect qpair from nvmf controller */
1513 			spdk_nvmf_qpair_disconnect(&fc_conn->qpair, nvmf_fc_disconnect_qpair_cb,
1514 						   &conn_args->cb_info);
1515 		}
1516 	}
1517 }
1518 
1519 static void
1520 nvmf_fc_poller_abts_done(void *hwqp, int32_t status, void *cb_args)
1521 {
1522 	struct spdk_nvmf_fc_poller_api_abts_recvd_args *args = cb_args;
1523 
1524 	SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API,
1525 		      "ABTS poller done, rpi: 0x%x, oxid: 0x%x, rxid: 0x%x\n",
1526 		      args->ctx->rpi, args->ctx->oxid, args->ctx->rxid);
1527 
1528 	nvmf_fc_poller_api_perform_cb(&args->cb_info,
1529 				      SPDK_NVMF_FC_POLLER_API_SUCCESS);
1530 }
1531 
1532 static void
1533 nvmf_fc_poller_api_abts_received(void *arg)
1534 {
1535 	struct spdk_nvmf_fc_poller_api_abts_recvd_args *args = arg;
1536 	struct spdk_nvmf_fc_request *fc_req = NULL;
1537 	struct spdk_nvmf_fc_hwqp *hwqp = args->hwqp;
1538 
1539 	TAILQ_FOREACH(fc_req, &hwqp->in_use_reqs, link) {
1540 		if ((fc_req->rpi == args->ctx->rpi) &&
1541 		    (fc_req->oxid == args->ctx->oxid)) {
1542 			spdk_nvmf_fc_request_abort(fc_req, false,
1543 						   nvmf_fc_poller_abts_done, args);
1544 			return;
1545 		}
1546 	}
1547 
1548 	nvmf_fc_poller_api_perform_cb(&args->cb_info,
1549 				      SPDK_NVMF_FC_POLLER_API_OXID_NOT_FOUND);
1550 }
1551 
1552 static void
1553 nvmf_fc_poller_api_queue_sync(void *arg)
1554 {
1555 	struct spdk_nvmf_fc_poller_api_queue_sync_args *args = arg;
1556 
1557 	SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API,
1558 		      "HWQP sync requested for u_id = 0x%lx\n", args->u_id);
1559 
1560 	/* Add this args to hwqp sync_cb list */
1561 	TAILQ_INSERT_TAIL(&args->hwqp->sync_cbs, args, link);
1562 }
1563 
1564 static void
1565 nvmf_fc_poller_api_queue_sync_done(void *arg)
1566 {
1567 	struct spdk_nvmf_fc_poller_api_queue_sync_done_args *args = arg;
1568 	struct spdk_nvmf_fc_hwqp *hwqp = args->hwqp;
1569 	uint64_t tag = args->tag;
1570 	struct spdk_nvmf_fc_poller_api_queue_sync_args *sync_args = NULL, *tmp = NULL;
1571 
1572 	assert(args != NULL);
1573 
1574 	TAILQ_FOREACH_SAFE(sync_args, &hwqp->sync_cbs, link, tmp) {
1575 		if (sync_args->u_id == tag) {
1576 			/* Queue successfully synced. Remove from cb list */
1577 			TAILQ_REMOVE(&hwqp->sync_cbs, sync_args, link);
1578 
1579 			SPDK_DEBUGLOG(SPDK_LOG_NVMF_FC_POLLER_API,
1580 				      "HWQP sync done for u_id = 0x%lx\n", sync_args->u_id);
1581 
1582 			/* Return the status to poller */
1583 			nvmf_fc_poller_api_perform_cb(&sync_args->cb_info,
1584 						      SPDK_NVMF_FC_POLLER_API_SUCCESS);
1585 			return;
1586 		}
1587 	}
1588 
1589 	free(arg);
1590 	/* note: no callback from this api */
1591 }
1592 
1593 static void
1594 nvmf_fc_poller_api_add_hwqp(void *arg)
1595 {
1596 	struct spdk_nvmf_fc_hwqp *hwqp = (struct spdk_nvmf_fc_hwqp *)arg;
1597 
1598 	hwqp->lcore_id = spdk_env_get_current_core(); /* for tracing purposes only */
1599 	TAILQ_INSERT_TAIL(&hwqp->fgroup->hwqp_list, hwqp, link);
1600 	/* note: no callback from this api */
1601 }
1602 
1603 static void
1604 nvmf_fc_poller_api_remove_hwqp(void *arg)
1605 {
1606 	struct spdk_nvmf_fc_hwqp *hwqp = (struct spdk_nvmf_fc_hwqp *)arg;
1607 	struct spdk_nvmf_fc_poll_group *fgroup = hwqp->fgroup;
1608 
1609 	TAILQ_REMOVE(&fgroup->hwqp_list, hwqp, link);
1610 	hwqp->fgroup = NULL;
1611 	/* note: no callback from this api */
1612 }
1613 
1614 enum spdk_nvmf_fc_poller_api_ret
1615 spdk_nvmf_fc_poller_api_func(struct spdk_nvmf_fc_hwqp *hwqp, enum spdk_nvmf_fc_poller_api api,
1616 			     void *api_args) {
1617 	switch (api)
1618 	{
1619 	case SPDK_NVMF_FC_POLLER_API_ADD_CONNECTION:
1620 				spdk_thread_send_msg(hwqp->thread,
1621 						     nvmf_fc_poller_api_add_connection, api_args);
1622 		break;
1623 
1624 	case SPDK_NVMF_FC_POLLER_API_DEL_CONNECTION:
1625 		spdk_thread_send_msg(hwqp->thread,
1626 				     nvmf_fc_poller_api_del_connection, api_args);
1627 		break;
1628 
1629 	case SPDK_NVMF_FC_POLLER_API_QUIESCE_QUEUE:
1630 		/* quiesce q polling now, don't wait for poller to do it */
1631 		hwqp->state = SPDK_FC_HWQP_OFFLINE;
1632 		spdk_thread_send_msg(hwqp->thread,
1633 				     nvmf_fc_poller_api_quiesce_queue, api_args);
1634 		break;
1635 
1636 	case SPDK_NVMF_FC_POLLER_API_ACTIVATE_QUEUE:
1637 		spdk_thread_send_msg(hwqp->thread,
1638 				     nvmf_fc_poller_api_activate_queue, api_args);
1639 		break;
1640 
1641 	case SPDK_NVMF_FC_POLLER_API_ABTS_RECEIVED:
1642 		spdk_thread_send_msg(hwqp->thread,
1643 				     nvmf_fc_poller_api_abts_received, api_args);
1644 		break;
1645 
1646 	case SPDK_NVMF_FC_POLLER_API_REQ_ABORT_COMPLETE:
1647 		spdk_thread_send_msg(hwqp->thread,
1648 				     spdk_nvmf_fc_request_abort_complete, api_args);
1649 		break;
1650 
1651 	case SPDK_NVMF_FC_POLLER_API_QUEUE_SYNC:
1652 		spdk_thread_send_msg(hwqp->thread,
1653 				     nvmf_fc_poller_api_queue_sync, api_args);
1654 		break;
1655 
1656 	case SPDK_NVMF_FC_POLLER_API_QUEUE_SYNC_DONE:
1657 		spdk_thread_send_msg(hwqp->thread,
1658 				     nvmf_fc_poller_api_queue_sync_done, api_args);
1659 		break;
1660 
1661 	case SPDK_NVMF_FC_POLLER_API_ADD_HWQP:
1662 		spdk_thread_send_msg(hwqp->thread, nvmf_fc_poller_api_add_hwqp, (void *) hwqp);
1663 		break;
1664 
1665 	case SPDK_NVMF_FC_POLLER_API_REMOVE_HWQP:
1666 		spdk_thread_send_msg(hwqp->thread, nvmf_fc_poller_api_remove_hwqp, (void *) hwqp);
1667 		break;
1668 
1669 	case SPDK_NVMF_FC_POLLER_API_ADAPTER_EVENT:
1670 	case SPDK_NVMF_FC_POLLER_API_AEN:
1671 	default:
1672 		SPDK_ERRLOG("BAD ARG!");
1673 		return SPDK_NVMF_FC_POLLER_API_INVALID_ARG;
1674 	}
1675 
1676 	return SPDK_NVMF_FC_POLLER_API_SUCCESS;
1677 }
1678 
1679 SPDK_LOG_REGISTER_COMPONENT("nvmf_fc_poller_api", SPDK_LOG_NVMF_FC_POLLER_API)
1680 SPDK_LOG_REGISTER_COMPONENT("nvmf_fc_ls", SPDK_LOG_NVMF_FC_LS)
1681