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