| // SPDX-License-Identifier: GPL-2.0-only | 
 | /* | 
 |  * Marvell Fibre Channel HBA Driver | 
 |  * Copyright (c)  2021     Marvell | 
 |  */ | 
 | #include "qla_def.h" | 
 | #include "qla_edif.h" | 
 |  | 
 | #include <linux/kthread.h> | 
 | #include <linux/vmalloc.h> | 
 | #include <linux/delay.h> | 
 | #include <scsi/scsi_tcq.h> | 
 |  | 
 | static struct edif_sa_index_entry *qla_edif_sadb_find_sa_index_entry(uint16_t nport_handle, | 
 | 		struct list_head *sa_list); | 
 | static uint16_t qla_edif_sadb_get_sa_index(fc_port_t *fcport, | 
 | 		struct qla_sa_update_frame *sa_frame); | 
 | static int qla_edif_sadb_delete_sa_index(fc_port_t *fcport, uint16_t nport_handle, | 
 | 		uint16_t sa_index); | 
 | static int qla_pur_get_pending(scsi_qla_host_t *, fc_port_t *, struct bsg_job *); | 
 |  | 
 | struct edb_node { | 
 | 	struct  list_head	list; | 
 | 	uint32_t		ntype; | 
 | 	union { | 
 | 		port_id_t	plogi_did; | 
 | 		uint32_t	async; | 
 | 		port_id_t	els_sid; | 
 | 		struct edif_sa_update_aen	sa_aen; | 
 | 	} u; | 
 | }; | 
 |  | 
 | static struct els_sub_cmd { | 
 | 	uint16_t cmd; | 
 | 	const char *str; | 
 | } sc_str[] = { | 
 | 	{SEND_ELS, "send ELS"}, | 
 | 	{SEND_ELS_REPLY, "send ELS Reply"}, | 
 | 	{PULL_ELS, "retrieve ELS"}, | 
 | }; | 
 |  | 
 | const char *sc_to_str(uint16_t cmd) | 
 | { | 
 | 	int i; | 
 | 	struct els_sub_cmd *e; | 
 |  | 
 | 	for (i = 0; i < ARRAY_SIZE(sc_str); i++) { | 
 | 		e = sc_str + i; | 
 | 		if (cmd == e->cmd) | 
 | 			return e->str; | 
 | 	} | 
 | 	return "unknown"; | 
 | } | 
 |  | 
 | static struct edb_node *qla_edb_getnext(scsi_qla_host_t *vha) | 
 | { | 
 | 	unsigned long   flags; | 
 | 	struct edb_node *edbnode = NULL; | 
 |  | 
 | 	spin_lock_irqsave(&vha->e_dbell.db_lock, flags); | 
 |  | 
 | 	/* db nodes are fifo - no qualifications done */ | 
 | 	if (!list_empty(&vha->e_dbell.head)) { | 
 | 		edbnode = list_first_entry(&vha->e_dbell.head, | 
 | 					   struct edb_node, list); | 
 | 		list_del_init(&edbnode->list); | 
 | 	} | 
 |  | 
 | 	spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags); | 
 |  | 
 | 	return edbnode; | 
 | } | 
 |  | 
 | static void qla_edb_node_free(scsi_qla_host_t *vha, struct edb_node *node) | 
 | { | 
 | 	list_del_init(&node->list); | 
 | 	kfree(node); | 
 | } | 
 |  | 
 | static struct edif_list_entry *qla_edif_list_find_sa_index(fc_port_t *fcport, | 
 | 		uint16_t handle) | 
 | { | 
 | 	struct edif_list_entry *entry; | 
 | 	struct edif_list_entry *tentry; | 
 | 	struct list_head *indx_list = &fcport->edif.edif_indx_list; | 
 |  | 
 | 	list_for_each_entry_safe(entry, tentry, indx_list, next) { | 
 | 		if (entry->handle == handle) | 
 | 			return entry; | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /* timeout called when no traffic and delayed rx sa_index delete */ | 
 | static void qla2x00_sa_replace_iocb_timeout(struct timer_list *t) | 
 | { | 
 | 	struct edif_list_entry *edif_entry = from_timer(edif_entry, t, timer); | 
 | 	fc_port_t *fcport = edif_entry->fcport; | 
 | 	struct scsi_qla_host *vha = fcport->vha; | 
 | 	struct  edif_sa_ctl *sa_ctl; | 
 | 	uint16_t nport_handle; | 
 | 	unsigned long flags = 0; | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x3069, | 
 | 	    "%s:  nport_handle 0x%x,  SA REPL Delay Timeout, %8phC portid=%06x\n", | 
 | 	    __func__, edif_entry->handle, fcport->port_name, fcport->d_id.b24); | 
 |  | 
 | 	/* | 
 | 	 * if delete_sa_index is valid then no one has serviced this | 
 | 	 * delayed delete | 
 | 	 */ | 
 | 	spin_lock_irqsave(&fcport->edif.indx_list_lock, flags); | 
 |  | 
 | 	/* | 
 | 	 * delete_sa_index is invalidated when we find the new sa_index in | 
 | 	 * the incoming data stream.  If it is not invalidated then we are | 
 | 	 * still looking for the new sa_index because there is no I/O and we | 
 | 	 * need to just force the rx delete and move on.  Otherwise | 
 | 	 * we could get another rekey which will result in an error 66. | 
 | 	 */ | 
 | 	if (edif_entry->delete_sa_index != INVALID_EDIF_SA_INDEX) { | 
 | 		uint16_t delete_sa_index = edif_entry->delete_sa_index; | 
 |  | 
 | 		edif_entry->delete_sa_index = INVALID_EDIF_SA_INDEX; | 
 | 		nport_handle = edif_entry->handle; | 
 | 		spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags); | 
 |  | 
 | 		sa_ctl = qla_edif_find_sa_ctl_by_index(fcport, | 
 | 		    delete_sa_index, 0); | 
 |  | 
 | 		if (sa_ctl) { | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 			    "%s: sa_ctl: %p, delete index %d, update index: %d, lid: 0x%x\n", | 
 | 			    __func__, sa_ctl, delete_sa_index, edif_entry->update_sa_index, | 
 | 			    nport_handle); | 
 |  | 
 | 			sa_ctl->flags = EDIF_SA_CTL_FLG_DEL; | 
 | 			set_bit(EDIF_SA_CTL_REPL, &sa_ctl->state); | 
 | 			qla_post_sa_replace_work(fcport->vha, fcport, | 
 | 			    nport_handle, sa_ctl); | 
 |  | 
 | 		} else { | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 			    "%s: sa_ctl not found for delete_sa_index: %d\n", | 
 | 			    __func__, edif_entry->delete_sa_index); | 
 | 		} | 
 | 	} else { | 
 | 		spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags); | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  * create a new list entry for this nport handle and | 
 |  * add an sa_update index to the list - called for sa_update | 
 |  */ | 
 | static int qla_edif_list_add_sa_update_index(fc_port_t *fcport, | 
 | 		uint16_t sa_index, uint16_t handle) | 
 | { | 
 | 	struct edif_list_entry *entry; | 
 | 	unsigned long flags = 0; | 
 |  | 
 | 	/* if the entry exists, then just update the sa_index */ | 
 | 	entry = qla_edif_list_find_sa_index(fcport, handle); | 
 | 	if (entry) { | 
 | 		entry->update_sa_index = sa_index; | 
 | 		entry->count = 0; | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * This is the normal path - there should be no existing entry | 
 | 	 * when update is called.  The exception is at startup | 
 | 	 * when update is called for the first two sa_indexes | 
 | 	 * followed by a delete of the first sa_index | 
 | 	 */ | 
 | 	entry = kzalloc((sizeof(struct edif_list_entry)), GFP_ATOMIC); | 
 | 	if (!entry) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	INIT_LIST_HEAD(&entry->next); | 
 | 	entry->handle = handle; | 
 | 	entry->update_sa_index = sa_index; | 
 | 	entry->delete_sa_index = INVALID_EDIF_SA_INDEX; | 
 | 	entry->count = 0; | 
 | 	entry->flags = 0; | 
 | 	timer_setup(&entry->timer, qla2x00_sa_replace_iocb_timeout, 0); | 
 | 	spin_lock_irqsave(&fcport->edif.indx_list_lock, flags); | 
 | 	list_add_tail(&entry->next, &fcport->edif.edif_indx_list); | 
 | 	spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags); | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* remove an entry from the list */ | 
 | static void qla_edif_list_delete_sa_index(fc_port_t *fcport, struct edif_list_entry *entry) | 
 | { | 
 | 	unsigned long flags = 0; | 
 |  | 
 | 	spin_lock_irqsave(&fcport->edif.indx_list_lock, flags); | 
 | 	list_del(&entry->next); | 
 | 	spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags); | 
 | } | 
 |  | 
 | int qla_post_sa_replace_work(struct scsi_qla_host *vha, | 
 | 	 fc_port_t *fcport, uint16_t nport_handle, struct edif_sa_ctl *sa_ctl) | 
 | { | 
 | 	struct qla_work_evt *e; | 
 |  | 
 | 	e = qla2x00_alloc_work(vha, QLA_EVT_SA_REPLACE); | 
 | 	if (!e) | 
 | 		return QLA_FUNCTION_FAILED; | 
 |  | 
 | 	e->u.sa_update.fcport = fcport; | 
 | 	e->u.sa_update.sa_ctl = sa_ctl; | 
 | 	e->u.sa_update.nport_handle = nport_handle; | 
 | 	fcport->flags |= FCF_ASYNC_ACTIVE; | 
 | 	return qla2x00_post_work(vha, e); | 
 | } | 
 |  | 
 | static void | 
 | qla_edif_sa_ctl_init(scsi_qla_host_t *vha, struct fc_port  *fcport) | 
 | { | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x2058, | 
 | 	    "Init SA_CTL List for fcport - nn %8phN pn %8phN portid=%06x.\n", | 
 | 	    fcport->node_name, fcport->port_name, fcport->d_id.b24); | 
 |  | 
 | 	fcport->edif.tx_rekey_cnt = 0; | 
 | 	fcport->edif.rx_rekey_cnt = 0; | 
 |  | 
 | 	fcport->edif.tx_bytes = 0; | 
 | 	fcport->edif.rx_bytes = 0; | 
 | } | 
 |  | 
 | static int qla_bsg_check(scsi_qla_host_t *vha, struct bsg_job *bsg_job, | 
 | fc_port_t *fcport) | 
 | { | 
 | 	struct extra_auth_els *p; | 
 | 	struct fc_bsg_reply *bsg_reply = bsg_job->reply; | 
 | 	struct qla_bsg_auth_els_request *req = | 
 | 	    (struct qla_bsg_auth_els_request *)bsg_job->request; | 
 |  | 
 | 	if (!vha->hw->flags.edif_enabled) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x9105, | 
 | 		    "%s edif not enabled\n", __func__); | 
 | 		goto done; | 
 | 	} | 
 | 	if (DBELL_INACTIVE(vha)) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x09102, | 
 | 		    "%s doorbell not enabled\n", __func__); | 
 | 		goto done; | 
 | 	} | 
 |  | 
 | 	p = &req->e; | 
 |  | 
 | 	/* Get response */ | 
 | 	if (p->sub_cmd == PULL_ELS) { | 
 | 		struct qla_bsg_auth_els_reply *rpl = | 
 | 			(struct qla_bsg_auth_els_reply *)bsg_job->reply; | 
 |  | 
 | 		qla_pur_get_pending(vha, fcport, bsg_job); | 
 |  | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 			"%s %s %8phN sid=%x. xchg %x, nb=%xh bsg ptr %p\n", | 
 | 			__func__, sc_to_str(p->sub_cmd), fcport->port_name, | 
 | 			fcport->d_id.b24, rpl->rx_xchg_address, | 
 | 			rpl->r.reply_payload_rcv_len, bsg_job); | 
 |  | 
 | 		goto done; | 
 | 	} | 
 | 	return 0; | 
 |  | 
 | done: | 
 |  | 
 | 	bsg_job_done(bsg_job, bsg_reply->result, | 
 | 			bsg_reply->reply_payload_rcv_len); | 
 | 	return -EIO; | 
 | } | 
 |  | 
 | fc_port_t * | 
 | qla2x00_find_fcport_by_pid(scsi_qla_host_t *vha, port_id_t *id) | 
 | { | 
 | 	fc_port_t *f, *tf; | 
 |  | 
 | 	f = NULL; | 
 | 	list_for_each_entry_safe(f, tf, &vha->vp_fcports, list) { | 
 | 		if (f->d_id.b24 == id->b24) | 
 | 			return f; | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /** | 
 |  * qla_edif_app_check(): check for valid application id. | 
 |  * @vha: host adapter pointer | 
 |  * @appid: application id | 
 |  * Return: false = fail, true = pass | 
 |  */ | 
 | static bool | 
 | qla_edif_app_check(scsi_qla_host_t *vha, struct app_id appid) | 
 | { | 
 | 	/* check that the app is allow/known to the driver */ | 
 |  | 
 | 	if (appid.app_vid != EDIF_APP_ID) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app id not ok (%x)", | 
 | 		    __func__, appid.app_vid); | 
 | 		return false; | 
 | 	} | 
 |  | 
 | 	if (appid.version != EDIF_VERSION1) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app version is not ok (%x)", | 
 | 		    __func__, appid.version); | 
 | 		return false; | 
 | 	} | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | static void | 
 | qla_edif_free_sa_ctl(fc_port_t *fcport, struct edif_sa_ctl *sa_ctl, | 
 | 	int index) | 
 | { | 
 | 	unsigned long flags = 0; | 
 |  | 
 | 	spin_lock_irqsave(&fcport->edif.sa_list_lock, flags); | 
 | 	list_del(&sa_ctl->next); | 
 | 	spin_unlock_irqrestore(&fcport->edif.sa_list_lock, flags); | 
 | 	if (index >= 512) | 
 | 		fcport->edif.tx_rekey_cnt--; | 
 | 	else | 
 | 		fcport->edif.rx_rekey_cnt--; | 
 | 	kfree(sa_ctl); | 
 | } | 
 |  | 
 | /* return an index to the freepool */ | 
 | static void qla_edif_add_sa_index_to_freepool(fc_port_t *fcport, int dir, | 
 | 		uint16_t sa_index) | 
 | { | 
 | 	void *sa_id_map; | 
 | 	struct scsi_qla_host *vha = fcport->vha; | 
 | 	struct qla_hw_data *ha = vha->hw; | 
 | 	unsigned long flags = 0; | 
 | 	u16 lsa_index = sa_index; | 
 |  | 
 | 	ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x3063, | 
 | 	    "%s: entry\n", __func__); | 
 |  | 
 | 	if (dir) { | 
 | 		sa_id_map = ha->edif_tx_sa_id_map; | 
 | 		lsa_index -= EDIF_TX_SA_INDEX_BASE; | 
 | 	} else { | 
 | 		sa_id_map = ha->edif_rx_sa_id_map; | 
 | 	} | 
 |  | 
 | 	spin_lock_irqsave(&ha->sadb_fp_lock, flags); | 
 | 	clear_bit(lsa_index, sa_id_map); | 
 | 	spin_unlock_irqrestore(&ha->sadb_fp_lock, flags); | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 	    "%s: index %d added to free pool\n", __func__, sa_index); | 
 | } | 
 |  | 
 | static void __qla2x00_release_all_sadb(struct scsi_qla_host *vha, | 
 | 	struct fc_port *fcport, struct edif_sa_index_entry *entry, | 
 | 	int pdir) | 
 | { | 
 | 	struct edif_list_entry *edif_entry; | 
 | 	struct  edif_sa_ctl *sa_ctl; | 
 | 	int i, dir; | 
 | 	int key_cnt = 0; | 
 |  | 
 | 	for (i = 0; i < 2; i++) { | 
 | 		if (entry->sa_pair[i].sa_index == INVALID_EDIF_SA_INDEX) | 
 | 			continue; | 
 |  | 
 | 		if (fcport->loop_id != entry->handle) { | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 			    "%s: ** WARNING %d** entry handle: 0x%x, lid: 0x%x, sa_index: %d\n", | 
 | 			    __func__, i, entry->handle, fcport->loop_id, | 
 | 			    entry->sa_pair[i].sa_index); | 
 | 		} | 
 |  | 
 | 		/* release the sa_ctl */ | 
 | 		sa_ctl = qla_edif_find_sa_ctl_by_index(fcport, | 
 | 				entry->sa_pair[i].sa_index, pdir); | 
 | 		if (sa_ctl && | 
 | 		    qla_edif_find_sa_ctl_by_index(fcport, sa_ctl->index, pdir)) { | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 			    "%s: freeing sa_ctl for index %d\n", __func__, sa_ctl->index); | 
 | 			qla_edif_free_sa_ctl(fcport, sa_ctl, sa_ctl->index); | 
 | 		} else { | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 			    "%s: sa_ctl NOT freed, sa_ctl: %p\n", __func__, sa_ctl); | 
 | 		} | 
 |  | 
 | 		/* Release the index */ | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 			"%s: freeing sa_index %d, nph: 0x%x\n", | 
 | 			__func__, entry->sa_pair[i].sa_index, entry->handle); | 
 |  | 
 | 		dir = (entry->sa_pair[i].sa_index < | 
 | 			EDIF_TX_SA_INDEX_BASE) ? 0 : 1; | 
 | 		qla_edif_add_sa_index_to_freepool(fcport, dir, | 
 | 			entry->sa_pair[i].sa_index); | 
 |  | 
 | 		/* Delete timer on RX */ | 
 | 		if (pdir != SAU_FLG_TX) { | 
 | 			edif_entry = | 
 | 				qla_edif_list_find_sa_index(fcport, entry->handle); | 
 | 			if (edif_entry) { | 
 | 				ql_dbg(ql_dbg_edif, vha, 0x5033, | 
 | 				    "%s: remove edif_entry %p, update_sa_index: 0x%x, delete_sa_index: 0x%x\n", | 
 | 				    __func__, edif_entry, edif_entry->update_sa_index, | 
 | 				    edif_entry->delete_sa_index); | 
 | 				qla_edif_list_delete_sa_index(fcport, edif_entry); | 
 | 				/* | 
 | 				 * valid delete_sa_index indicates there is a rx | 
 | 				 * delayed delete queued | 
 | 				 */ | 
 | 				if (edif_entry->delete_sa_index != | 
 | 						INVALID_EDIF_SA_INDEX) { | 
 | 					timer_shutdown(&edif_entry->timer); | 
 |  | 
 | 					/* build and send the aen */ | 
 | 					fcport->edif.rx_sa_set = 1; | 
 | 					fcport->edif.rx_sa_pending = 0; | 
 | 					qla_edb_eventcreate(vha, | 
 | 							VND_CMD_AUTH_STATE_SAUPDATE_COMPL, | 
 | 							QL_VND_SA_STAT_SUCCESS, | 
 | 							QL_VND_RX_SA_KEY, fcport); | 
 | 				} | 
 | 				ql_dbg(ql_dbg_edif, vha, 0x5033, | 
 | 				    "%s: release edif_entry %p, update_sa_index: 0x%x, delete_sa_index: 0x%x\n", | 
 | 				    __func__, edif_entry, edif_entry->update_sa_index, | 
 | 				    edif_entry->delete_sa_index); | 
 |  | 
 | 				kfree(edif_entry); | 
 | 			} | 
 | 		} | 
 | 		key_cnt++; | 
 | 	} | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 	    "%s: %d %s keys released\n", | 
 | 	    __func__, key_cnt, pdir ? "tx" : "rx"); | 
 | } | 
 |  | 
 | /* find an release all outstanding sadb sa_indicies */ | 
 | void qla2x00_release_all_sadb(struct scsi_qla_host *vha, struct fc_port *fcport) | 
 | { | 
 | 	struct edif_sa_index_entry *entry, *tmp; | 
 | 	struct qla_hw_data *ha = vha->hw; | 
 | 	unsigned long flags; | 
 |  | 
 | 	ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x3063, | 
 | 	    "%s: Starting...\n", __func__); | 
 |  | 
 | 	spin_lock_irqsave(&ha->sadb_lock, flags); | 
 |  | 
 | 	list_for_each_entry_safe(entry, tmp, &ha->sadb_rx_index_list, next) { | 
 | 		if (entry->fcport == fcport) { | 
 | 			list_del(&entry->next); | 
 | 			spin_unlock_irqrestore(&ha->sadb_lock, flags); | 
 | 			__qla2x00_release_all_sadb(vha, fcport, entry, 0); | 
 | 			kfree(entry); | 
 | 			spin_lock_irqsave(&ha->sadb_lock, flags); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	list_for_each_entry_safe(entry, tmp, &ha->sadb_tx_index_list, next) { | 
 | 		if (entry->fcport == fcport) { | 
 | 			list_del(&entry->next); | 
 | 			spin_unlock_irqrestore(&ha->sadb_lock, flags); | 
 |  | 
 | 			__qla2x00_release_all_sadb(vha, fcport, entry, SAU_FLG_TX); | 
 |  | 
 | 			kfree(entry); | 
 | 			spin_lock_irqsave(&ha->sadb_lock, flags); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	spin_unlock_irqrestore(&ha->sadb_lock, flags); | 
 | } | 
 |  | 
 | /** | 
 |  * qla_delete_n2n_sess_and_wait: search for N2N session, tear it down and | 
 |  *    wait for tear down to complete.  In N2N topology, there is only one | 
 |  *    session being active in tracking the remote device. | 
 |  * @vha: host adapter pointer | 
 |  * return code:  0 - found the session and completed the tear down. | 
 |  *	1 - timeout occurred.  Caller to use link bounce to reset. | 
 |  */ | 
 | static int qla_delete_n2n_sess_and_wait(scsi_qla_host_t *vha) | 
 | { | 
 | 	struct fc_port *fcport; | 
 | 	int rc = -EIO; | 
 | 	ulong expire = jiffies + 23 * HZ; | 
 |  | 
 | 	if (!N2N_TOPO(vha->hw)) | 
 | 		return 0; | 
 |  | 
 | 	fcport = NULL; | 
 | 	list_for_each_entry(fcport, &vha->vp_fcports, list) { | 
 | 		if (!fcport->n2n_flag) | 
 | 			continue; | 
 |  | 
 | 		ql_dbg(ql_dbg_disc, fcport->vha, 0x2016, | 
 | 		       "%s reset sess at app start \n", __func__); | 
 |  | 
 | 		qla_edif_sa_ctl_init(vha, fcport); | 
 | 		qlt_schedule_sess_for_deletion(fcport); | 
 |  | 
 | 		while (time_before_eq(jiffies, expire)) { | 
 | 			if (fcport->disc_state != DSC_DELETE_PEND) { | 
 | 				rc = 0; | 
 | 				break; | 
 | 			} | 
 | 			msleep(1); | 
 | 		} | 
 |  | 
 | 		set_bit(RELOGIN_NEEDED, &vha->dpc_flags); | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	return rc; | 
 | } | 
 |  | 
 | /** | 
 |  * qla_edif_app_start:  application has announce its present | 
 |  * @vha: host adapter pointer | 
 |  * @bsg_job: user request | 
 |  * | 
 |  * Set/activate doorbell.  Reset current sessions and re-login with | 
 |  * secure flag. | 
 |  */ | 
 | static int | 
 | qla_edif_app_start(scsi_qla_host_t *vha, struct bsg_job *bsg_job) | 
 | { | 
 | 	int32_t			rval = 0; | 
 | 	struct fc_bsg_reply	*bsg_reply = bsg_job->reply; | 
 | 	struct app_start	appstart; | 
 | 	struct app_start_reply	appreply; | 
 | 	struct fc_port  *fcport, *tf; | 
 |  | 
 | 	ql_log(ql_log_info, vha, 0x1313, | 
 | 	       "EDIF application registration with driver, FC device connections will be re-established.\n"); | 
 |  | 
 | 	sg_copy_to_buffer(bsg_job->request_payload.sg_list, | 
 | 	    bsg_job->request_payload.sg_cnt, &appstart, | 
 | 	    sizeof(struct app_start)); | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app_vid=%x app_start_flags %x\n", | 
 | 	     __func__, appstart.app_info.app_vid, appstart.app_start_flags); | 
 |  | 
 | 	if (DBELL_INACTIVE(vha)) { | 
 | 		/* mark doorbell as active since an app is now present */ | 
 | 		vha->e_dbell.db_flags |= EDB_ACTIVE; | 
 | 	} else { | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	if (N2N_TOPO(vha->hw)) { | 
 | 		list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) | 
 | 			fcport->n2n_link_reset_cnt = 0; | 
 |  | 
 | 		if (vha->hw->flags.n2n_fw_acc_sec) { | 
 | 			bool link_bounce = false; | 
 | 			/* | 
 | 			 * While authentication app was not running, remote device | 
 | 			 * could still try to login with this local port.  Let's | 
 | 			 * reset the session, reconnect and re-authenticate. | 
 | 			 */ | 
 | 			if (qla_delete_n2n_sess_and_wait(vha)) | 
 | 				link_bounce = true; | 
 |  | 
 | 			/* bounce the link to start login */ | 
 | 			if (!vha->hw->flags.n2n_bigger || link_bounce) { | 
 | 				set_bit(N2N_LINK_RESET, &vha->dpc_flags); | 
 | 				qla2xxx_wake_dpc(vha); | 
 | 			} | 
 | 		} else { | 
 | 			qla2x00_wait_for_hba_online(vha); | 
 | 			set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); | 
 | 			qla2xxx_wake_dpc(vha); | 
 | 			qla2x00_wait_for_hba_online(vha); | 
 | 		} | 
 | 	} else { | 
 | 		list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) { | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x2058, | 
 | 			       "FCSP - nn %8phN pn %8phN portid=%06x.\n", | 
 | 			       fcport->node_name, fcport->port_name, | 
 | 			       fcport->d_id.b24); | 
 | 			ql_dbg(ql_dbg_edif, vha, 0xf084, | 
 | 			       "%s: se_sess %p / sess %p from port %8phC " | 
 | 			       "loop_id %#04x s_id %06x logout %d " | 
 | 			       "keep %d els_logo %d disc state %d auth state %d" | 
 | 			       "stop state %d\n", | 
 | 			       __func__, fcport->se_sess, fcport, | 
 | 			       fcport->port_name, fcport->loop_id, | 
 | 			       fcport->d_id.b24, fcport->logout_on_delete, | 
 | 			       fcport->keep_nport_handle, fcport->send_els_logo, | 
 | 			       fcport->disc_state, fcport->edif.auth_state, | 
 | 			       fcport->edif.app_stop); | 
 |  | 
 | 			if (atomic_read(&vha->loop_state) == LOOP_DOWN) | 
 | 				break; | 
 |  | 
 | 			fcport->login_retry = vha->hw->login_retry_count; | 
 |  | 
 | 			fcport->edif.app_stop = 0; | 
 | 			fcport->edif.app_sess_online = 0; | 
 |  | 
 | 			if (fcport->scan_state != QLA_FCPORT_FOUND) | 
 | 				continue; | 
 |  | 
 | 			if (fcport->port_type == FCT_UNKNOWN && | 
 | 			    !fcport->fc4_features) | 
 | 				rval = qla24xx_async_gffid(vha, fcport, true); | 
 |  | 
 | 			if (!rval && !(fcport->fc4_features & FC4_FF_TARGET || | 
 | 			    fcport->port_type & (FCT_TARGET|FCT_NVME_TARGET))) | 
 | 				continue; | 
 |  | 
 | 			rval = 0; | 
 |  | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x911e, | 
 | 			       "%s wwpn %8phC calling qla_edif_reset_auth_wait\n", | 
 | 			       __func__, fcport->port_name); | 
 | 			qlt_schedule_sess_for_deletion(fcport); | 
 | 			qla_edif_sa_ctl_init(vha, fcport); | 
 | 		} | 
 | 		set_bit(RELOGIN_NEEDED, &vha->dpc_flags); | 
 | 	} | 
 |  | 
 | 	if (vha->pur_cinfo.enode_flags != ENODE_ACTIVE) { | 
 | 		/* mark as active since an app is now present */ | 
 | 		vha->pur_cinfo.enode_flags = ENODE_ACTIVE; | 
 | 	} else { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911f, "%s enode already active\n", | 
 | 		     __func__); | 
 | 	} | 
 |  | 
 | out: | 
 | 	appreply.host_support_edif = vha->hw->flags.edif_enabled; | 
 | 	appreply.edif_enode_active = vha->pur_cinfo.enode_flags; | 
 | 	appreply.edif_edb_active = vha->e_dbell.db_flags; | 
 | 	appreply.version = EDIF_VERSION1; | 
 |  | 
 | 	bsg_job->reply_len = sizeof(struct fc_bsg_reply); | 
 |  | 
 | 	SET_DID_STATUS(bsg_reply->result, DID_OK); | 
 |  | 
 | 	bsg_reply->reply_payload_rcv_len = sg_copy_from_buffer(bsg_job->reply_payload.sg_list, | 
 | 							       bsg_job->reply_payload.sg_cnt, | 
 | 							       &appreply, | 
 | 							       sizeof(struct app_start_reply)); | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 	    "%s app start completed with 0x%x\n", | 
 | 	    __func__, rval); | 
 |  | 
 | 	return rval; | 
 | } | 
 |  | 
 | /** | 
 |  * qla_edif_app_stop - app has announced it's exiting. | 
 |  * @vha: host adapter pointer | 
 |  * @bsg_job: user space command pointer | 
 |  * | 
 |  * Free any in flight messages, clear all doorbell events | 
 |  * to application. Reject any message relate to security. | 
 |  */ | 
 | static int | 
 | qla_edif_app_stop(scsi_qla_host_t *vha, struct bsg_job *bsg_job) | 
 | { | 
 | 	struct app_stop         appstop; | 
 | 	struct fc_bsg_reply     *bsg_reply = bsg_job->reply; | 
 | 	struct fc_port  *fcport, *tf; | 
 |  | 
 | 	sg_copy_to_buffer(bsg_job->request_payload.sg_list, | 
 | 	    bsg_job->request_payload.sg_cnt, &appstop, | 
 | 	    sizeof(struct app_stop)); | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x911d, "%s Stopping APP: app_vid=%x\n", | 
 | 	    __func__, appstop.app_info.app_vid); | 
 |  | 
 | 	/* Call db stop and enode stop functions */ | 
 |  | 
 | 	/* if we leave this running short waits are operational < 16 secs */ | 
 | 	qla_enode_stop(vha);        /* stop enode */ | 
 | 	qla_edb_stop(vha);          /* stop db */ | 
 |  | 
 | 	list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) { | 
 | 		if (!(fcport->flags & FCF_FCSP_DEVICE)) | 
 | 			continue; | 
 |  | 
 | 		if (fcport->flags & FCF_FCSP_DEVICE) { | 
 | 			ql_dbg(ql_dbg_edif, vha, 0xf084, | 
 | 			    "%s: sess %p from port %8phC lid %#04x s_id %06x logout %d keep %d els_logo %d\n", | 
 | 			    __func__, fcport, | 
 | 			    fcport->port_name, fcport->loop_id, fcport->d_id.b24, | 
 | 			    fcport->logout_on_delete, fcport->keep_nport_handle, | 
 | 			    fcport->send_els_logo); | 
 |  | 
 | 			if (atomic_read(&vha->loop_state) == LOOP_DOWN) | 
 | 				break; | 
 |  | 
 | 			fcport->edif.app_stop = 1; | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x911e, | 
 | 				"%s wwpn %8phC calling qla_edif_reset_auth_wait\n", | 
 | 				__func__, fcport->port_name); | 
 |  | 
 | 			fcport->send_els_logo = 1; | 
 | 			qlt_schedule_sess_for_deletion(fcport); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	bsg_job->reply_len = sizeof(struct fc_bsg_reply); | 
 | 	SET_DID_STATUS(bsg_reply->result, DID_OK); | 
 |  | 
 | 	/* no return interface to app - it assumes we cleaned up ok */ | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int | 
 | qla_edif_app_chk_sa_update(scsi_qla_host_t *vha, fc_port_t *fcport, | 
 | 		struct app_plogi_reply *appplogireply) | 
 | { | 
 | 	int	ret = 0; | 
 |  | 
 | 	if (!(fcport->edif.rx_sa_set && fcport->edif.tx_sa_set)) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911e, | 
 | 		    "%s: wwpn %8phC Both SA indexes has not been SET TX %d, RX %d.\n", | 
 | 		    __func__, fcport->port_name, fcport->edif.tx_sa_set, | 
 | 		    fcport->edif.rx_sa_set); | 
 | 		appplogireply->prli_status = 0; | 
 | 		ret = 1; | 
 | 	} else  { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911e, | 
 | 		    "%s wwpn %8phC Both SA(s) updated.\n", __func__, | 
 | 		    fcport->port_name); | 
 | 		fcport->edif.rx_sa_set = fcport->edif.tx_sa_set = 0; | 
 | 		fcport->edif.rx_sa_pending = fcport->edif.tx_sa_pending = 0; | 
 | 		appplogireply->prli_status = 1; | 
 | 	} | 
 | 	return ret; | 
 | } | 
 |  | 
 | /** | 
 |  * qla_edif_app_authok - authentication by app succeeded.  Driver can proceed | 
 |  *   with prli | 
 |  * @vha: host adapter pointer | 
 |  * @bsg_job: user request | 
 |  */ | 
 | static int | 
 | qla_edif_app_authok(scsi_qla_host_t *vha, struct bsg_job *bsg_job) | 
 | { | 
 | 	struct auth_complete_cmd appplogiok; | 
 | 	struct app_plogi_reply	appplogireply = {0}; | 
 | 	struct fc_bsg_reply	*bsg_reply = bsg_job->reply; | 
 | 	fc_port_t		*fcport = NULL; | 
 | 	port_id_t		portid = {0}; | 
 |  | 
 | 	sg_copy_to_buffer(bsg_job->request_payload.sg_list, | 
 | 	    bsg_job->request_payload.sg_cnt, &appplogiok, | 
 | 	    sizeof(struct auth_complete_cmd)); | 
 |  | 
 | 	/* silent unaligned access warning */ | 
 | 	portid.b.domain = appplogiok.u.d_id.b.domain; | 
 | 	portid.b.area   = appplogiok.u.d_id.b.area; | 
 | 	portid.b.al_pa  = appplogiok.u.d_id.b.al_pa; | 
 |  | 
 | 	appplogireply.version = EDIF_VERSION1; | 
 | 	switch (appplogiok.type) { | 
 | 	case PL_TYPE_WWPN: | 
 | 		fcport = qla2x00_find_fcport_by_wwpn(vha, | 
 | 		    appplogiok.u.wwpn, 0); | 
 | 		if (!fcport) | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 			    "%s wwpn lookup failed: %8phC\n", | 
 | 			    __func__, appplogiok.u.wwpn); | 
 | 		break; | 
 | 	case PL_TYPE_DID: | 
 | 		fcport = qla2x00_find_fcport_by_pid(vha, &portid); | 
 | 		if (!fcport) | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 			    "%s d_id lookup failed: %x\n", __func__, | 
 | 			    portid.b24); | 
 | 		break; | 
 | 	default: | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 		    "%s undefined type: %x\n", __func__, | 
 | 		    appplogiok.type); | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	if (!fcport) { | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_ERROR); | 
 | 		goto errstate_exit; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * if port is online then this is a REKEY operation | 
 | 	 * Only do sa update checking | 
 | 	 */ | 
 | 	if (atomic_read(&fcport->state) == FCS_ONLINE) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 		    "%s Skipping PRLI complete based on rekey\n", __func__); | 
 | 		appplogireply.prli_status = 1; | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_OK); | 
 | 		qla_edif_app_chk_sa_update(vha, fcport, &appplogireply); | 
 | 		goto errstate_exit; | 
 | 	} | 
 |  | 
 | 	/* make sure in AUTH_PENDING or else reject */ | 
 | 	if (fcport->disc_state != DSC_LOGIN_AUTH_PEND) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911e, | 
 | 		    "%s wwpn %8phC is not in auth pending state (%x)\n", | 
 | 		    __func__, fcport->port_name, fcport->disc_state); | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_OK); | 
 | 		appplogireply.prli_status = 0; | 
 | 		goto errstate_exit; | 
 | 	} | 
 |  | 
 | 	SET_DID_STATUS(bsg_reply->result, DID_OK); | 
 | 	appplogireply.prli_status = 1; | 
 | 	fcport->edif.authok = 1; | 
 | 	if (!(fcport->edif.rx_sa_set && fcport->edif.tx_sa_set)) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911e, | 
 | 		    "%s: wwpn %8phC Both SA indexes has not been SET TX %d, RX %d.\n", | 
 | 		    __func__, fcport->port_name, fcport->edif.tx_sa_set, | 
 | 		    fcport->edif.rx_sa_set); | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_OK); | 
 | 		appplogireply.prli_status = 0; | 
 | 		goto errstate_exit; | 
 |  | 
 | 	} else { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911e, | 
 | 		    "%s wwpn %8phC Both SA(s) updated.\n", __func__, | 
 | 		    fcport->port_name); | 
 | 		fcport->edif.rx_sa_set = fcport->edif.tx_sa_set = 0; | 
 | 		fcport->edif.rx_sa_pending = fcport->edif.tx_sa_pending = 0; | 
 | 	} | 
 |  | 
 | 	if (qla_ini_mode_enabled(vha)) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911e, | 
 | 		    "%s AUTH complete - RESUME with prli for wwpn %8phC\n", | 
 | 		    __func__, fcport->port_name); | 
 | 		qla24xx_post_prli_work(vha, fcport); | 
 | 	} | 
 |  | 
 | errstate_exit: | 
 | 	bsg_job->reply_len = sizeof(struct fc_bsg_reply); | 
 | 	bsg_reply->reply_payload_rcv_len = sg_copy_from_buffer(bsg_job->reply_payload.sg_list, | 
 | 							       bsg_job->reply_payload.sg_cnt, | 
 | 							       &appplogireply, | 
 | 							       sizeof(struct app_plogi_reply)); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /** | 
 |  * qla_edif_app_authfail - authentication by app has failed.  Driver is given | 
 |  *   notice to tear down current session. | 
 |  * @vha: host adapter pointer | 
 |  * @bsg_job: user request | 
 |  */ | 
 | static int | 
 | qla_edif_app_authfail(scsi_qla_host_t *vha, struct bsg_job *bsg_job) | 
 | { | 
 | 	int32_t			rval = 0; | 
 | 	struct auth_complete_cmd appplogifail; | 
 | 	struct fc_bsg_reply	*bsg_reply = bsg_job->reply; | 
 | 	fc_port_t		*fcport = NULL; | 
 | 	port_id_t		portid = {0}; | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app auth fail\n", __func__); | 
 |  | 
 | 	sg_copy_to_buffer(bsg_job->request_payload.sg_list, | 
 | 	    bsg_job->request_payload.sg_cnt, &appplogifail, | 
 | 	    sizeof(struct auth_complete_cmd)); | 
 |  | 
 | 	/* silent unaligned access warning */ | 
 | 	portid.b.domain = appplogifail.u.d_id.b.domain; | 
 | 	portid.b.area   = appplogifail.u.d_id.b.area; | 
 | 	portid.b.al_pa  = appplogifail.u.d_id.b.al_pa; | 
 |  | 
 | 	/* | 
 | 	 * TODO: edif: app has failed this plogi. Inform driver to | 
 | 	 * take any action (if any). | 
 | 	 */ | 
 | 	switch (appplogifail.type) { | 
 | 	case PL_TYPE_WWPN: | 
 | 		fcport = qla2x00_find_fcport_by_wwpn(vha, | 
 | 		    appplogifail.u.wwpn, 0); | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_OK); | 
 | 		break; | 
 | 	case PL_TYPE_DID: | 
 | 		fcport = qla2x00_find_fcport_by_pid(vha, &portid); | 
 | 		if (!fcport) | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 			    "%s d_id lookup failed: %x\n", __func__, | 
 | 			    portid.b24); | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_OK); | 
 | 		break; | 
 | 	default: | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911e, | 
 | 		    "%s undefined type: %x\n", __func__, | 
 | 		    appplogifail.type); | 
 | 		bsg_job->reply_len = sizeof(struct fc_bsg_reply); | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_ERROR); | 
 | 		rval = -1; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 	    "%s fcport is 0x%p\n", __func__, fcport); | 
 |  | 
 | 	if (fcport) { | 
 | 		/* set/reset edif values and flags */ | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911e, | 
 | 		    "%s reset the auth process - %8phC, loopid=%x portid=%06x.\n", | 
 | 		    __func__, fcport->port_name, fcport->loop_id, fcport->d_id.b24); | 
 |  | 
 | 		if (qla_ini_mode_enabled(fcport->vha)) { | 
 | 			fcport->send_els_logo = 1; | 
 | 			qlt_schedule_sess_for_deletion(fcport); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return rval; | 
 | } | 
 |  | 
 | /** | 
 |  * qla_edif_app_getfcinfo - app would like to read session info (wwpn, nportid, | 
 |  *   [initiator|target] mode.  It can specific session with specific nport id or | 
 |  *   all sessions. | 
 |  * @vha: host adapter pointer | 
 |  * @bsg_job: user request pointer | 
 |  */ | 
 | static int | 
 | qla_edif_app_getfcinfo(scsi_qla_host_t *vha, struct bsg_job *bsg_job) | 
 | { | 
 | 	int32_t			rval = 0; | 
 | 	int32_t			pcnt = 0; | 
 | 	struct fc_bsg_reply	*bsg_reply = bsg_job->reply; | 
 | 	struct app_pinfo_req	app_req; | 
 | 	struct app_pinfo_reply	*app_reply; | 
 | 	port_id_t		tdid; | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app get fcinfo\n", __func__); | 
 |  | 
 | 	sg_copy_to_buffer(bsg_job->request_payload.sg_list, | 
 | 	    bsg_job->request_payload.sg_cnt, &app_req, | 
 | 	    sizeof(struct app_pinfo_req)); | 
 |  | 
 | 	app_reply = kzalloc((sizeof(struct app_pinfo_reply) + | 
 | 	    sizeof(struct app_pinfo) * app_req.num_ports), GFP_KERNEL); | 
 |  | 
 | 	if (!app_reply) { | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_ERROR); | 
 | 		rval = -1; | 
 | 	} else { | 
 | 		struct fc_port	*fcport = NULL, *tf; | 
 |  | 
 | 		app_reply->version = EDIF_VERSION1; | 
 |  | 
 | 		list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) { | 
 | 			if (!(fcport->flags & FCF_FCSP_DEVICE)) | 
 | 				continue; | 
 |  | 
 | 			tdid.b.domain = app_req.remote_pid.domain; | 
 | 			tdid.b.area = app_req.remote_pid.area; | 
 | 			tdid.b.al_pa = app_req.remote_pid.al_pa; | 
 |  | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x2058, | 
 | 			    "APP request entry - portid=%06x.\n", tdid.b24); | 
 |  | 
 | 			/* Ran out of space */ | 
 | 			if (pcnt >= app_req.num_ports) | 
 | 				break; | 
 |  | 
 | 			if (tdid.b24 != 0 && tdid.b24 != fcport->d_id.b24) | 
 | 				continue; | 
 |  | 
 | 			if (!N2N_TOPO(vha->hw)) { | 
 | 				if (fcport->scan_state != QLA_FCPORT_FOUND) | 
 | 					continue; | 
 |  | 
 | 				if (fcport->port_type == FCT_UNKNOWN && | 
 | 				    !fcport->fc4_features) | 
 | 					rval = qla24xx_async_gffid(vha, fcport, | 
 | 								   true); | 
 |  | 
 | 				if (!rval && | 
 | 				    !(fcport->fc4_features & FC4_FF_TARGET || | 
 | 				      fcport->port_type & | 
 | 				      (FCT_TARGET | FCT_NVME_TARGET))) | 
 | 					continue; | 
 | 			} | 
 |  | 
 | 			rval = 0; | 
 |  | 
 | 			app_reply->ports[pcnt].version = EDIF_VERSION1; | 
 | 			app_reply->ports[pcnt].remote_type = | 
 | 				VND_CMD_RTYPE_UNKNOWN; | 
 | 			if (fcport->port_type & (FCT_NVME_TARGET | FCT_TARGET)) | 
 | 				app_reply->ports[pcnt].remote_type |= | 
 | 					VND_CMD_RTYPE_TARGET; | 
 | 			if (fcport->port_type & (FCT_NVME_INITIATOR | FCT_INITIATOR)) | 
 | 				app_reply->ports[pcnt].remote_type |= | 
 | 					VND_CMD_RTYPE_INITIATOR; | 
 |  | 
 | 			app_reply->ports[pcnt].remote_pid = fcport->d_id; | 
 |  | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x2058, | 
 | 			    "Found FC_SP fcport - nn %8phN pn %8phN pcnt %d portid=%06x secure %d.\n", | 
 | 			    fcport->node_name, fcport->port_name, pcnt, | 
 | 			    fcport->d_id.b24, fcport->flags & FCF_FCSP_DEVICE); | 
 |  | 
 | 			switch (fcport->edif.auth_state) { | 
 | 			case VND_CMD_AUTH_STATE_ELS_RCVD: | 
 | 				if (fcport->disc_state == DSC_LOGIN_AUTH_PEND) { | 
 | 					fcport->edif.auth_state = VND_CMD_AUTH_STATE_NEEDED; | 
 | 					app_reply->ports[pcnt].auth_state = | 
 | 						VND_CMD_AUTH_STATE_NEEDED; | 
 | 				} else { | 
 | 					app_reply->ports[pcnt].auth_state = | 
 | 						VND_CMD_AUTH_STATE_ELS_RCVD; | 
 | 				} | 
 | 				break; | 
 | 			default: | 
 | 				app_reply->ports[pcnt].auth_state = fcport->edif.auth_state; | 
 | 				break; | 
 | 			} | 
 |  | 
 | 			memcpy(app_reply->ports[pcnt].remote_wwpn, | 
 | 			    fcport->port_name, 8); | 
 |  | 
 | 			app_reply->ports[pcnt].remote_state = | 
 | 				(atomic_read(&fcport->state) == | 
 | 				    FCS_ONLINE ? 1 : 0); | 
 |  | 
 | 			pcnt++; | 
 |  | 
 | 			if (tdid.b24 != 0) | 
 | 				break; | 
 | 		} | 
 | 		app_reply->port_count = pcnt; | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_OK); | 
 | 	} | 
 |  | 
 | 	bsg_job->reply_len = sizeof(struct fc_bsg_reply); | 
 | 	bsg_reply->reply_payload_rcv_len = sg_copy_from_buffer(bsg_job->reply_payload.sg_list, | 
 | 							       bsg_job->reply_payload.sg_cnt, | 
 | 							       app_reply, | 
 | 							       sizeof(struct app_pinfo_reply) + sizeof(struct app_pinfo) * pcnt); | 
 |  | 
 | 	kfree(app_reply); | 
 |  | 
 | 	return rval; | 
 | } | 
 |  | 
 | /** | 
 |  * qla_edif_app_getstats - app would like to read various statistics info | 
 |  * @vha: host adapter pointer | 
 |  * @bsg_job: user request | 
 |  */ | 
 | static int32_t | 
 | qla_edif_app_getstats(scsi_qla_host_t *vha, struct bsg_job *bsg_job) | 
 | { | 
 | 	int32_t			rval = 0; | 
 | 	struct fc_bsg_reply	*bsg_reply = bsg_job->reply; | 
 | 	uint32_t size; | 
 |  | 
 | 	struct app_sinfo_req	app_req; | 
 | 	struct app_stats_reply	*app_reply; | 
 | 	uint32_t pcnt = 0; | 
 |  | 
 | 	sg_copy_to_buffer(bsg_job->request_payload.sg_list, | 
 | 	    bsg_job->request_payload.sg_cnt, &app_req, | 
 | 	    sizeof(struct app_sinfo_req)); | 
 | 	if (app_req.num_ports == 0) { | 
 | 		ql_dbg(ql_dbg_async, vha, 0x911d, | 
 | 		   "%s app did not indicate number of ports to return\n", | 
 | 		    __func__); | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_ERROR); | 
 | 		rval = -1; | 
 | 	} | 
 |  | 
 | 	size = sizeof(struct app_stats_reply) + | 
 | 	    (sizeof(struct app_sinfo) * app_req.num_ports); | 
 |  | 
 | 	app_reply = kzalloc(size, GFP_KERNEL); | 
 | 	if (!app_reply) { | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_ERROR); | 
 | 		rval = -1; | 
 | 	} else { | 
 | 		struct fc_port	*fcport = NULL, *tf; | 
 |  | 
 | 		app_reply->version = EDIF_VERSION1; | 
 |  | 
 | 		list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) { | 
 | 			if (fcport->edif.enable) { | 
 | 				if (pcnt >= app_req.num_ports) | 
 | 					break; | 
 |  | 
 | 				app_reply->elem[pcnt].rekey_count = | 
 | 				    fcport->edif.rekey_cnt; | 
 | 				app_reply->elem[pcnt].tx_bytes = | 
 | 				    fcport->edif.tx_bytes; | 
 | 				app_reply->elem[pcnt].rx_bytes = | 
 | 				    fcport->edif.rx_bytes; | 
 |  | 
 | 				memcpy(app_reply->elem[pcnt].remote_wwpn, | 
 | 				    fcport->port_name, 8); | 
 |  | 
 | 				pcnt++; | 
 | 			} | 
 | 		} | 
 | 		app_reply->elem_count = pcnt; | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_OK); | 
 | 	} | 
 |  | 
 | 	bsg_job->reply_len = sizeof(struct fc_bsg_reply); | 
 | 	bsg_reply->reply_payload_rcv_len = | 
 | 	    sg_copy_from_buffer(bsg_job->reply_payload.sg_list, | 
 | 	       bsg_job->reply_payload.sg_cnt, app_reply, | 
 | 	       sizeof(struct app_stats_reply) + (sizeof(struct app_sinfo) * pcnt)); | 
 |  | 
 | 	kfree(app_reply); | 
 |  | 
 | 	return rval; | 
 | } | 
 |  | 
 | static int32_t | 
 | qla_edif_ack(scsi_qla_host_t *vha, struct bsg_job *bsg_job) | 
 | { | 
 | 	struct fc_port *fcport; | 
 | 	struct aen_complete_cmd ack; | 
 | 	struct fc_bsg_reply     *bsg_reply = bsg_job->reply; | 
 |  | 
 | 	sg_copy_to_buffer(bsg_job->request_payload.sg_list, | 
 | 			  bsg_job->request_payload.sg_cnt, &ack, sizeof(ack)); | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x70cf, | 
 | 	       "%s: %06x event_code %x\n", | 
 | 	       __func__, ack.port_id.b24, ack.event_code); | 
 |  | 
 | 	fcport = qla2x00_find_fcport_by_pid(vha, &ack.port_id); | 
 | 	SET_DID_STATUS(bsg_reply->result, DID_OK); | 
 |  | 
 | 	if (!fcport) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x70cf, | 
 | 		       "%s: unable to find fcport %06x \n", | 
 | 		       __func__, ack.port_id.b24); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	switch (ack.event_code) { | 
 | 	case VND_CMD_AUTH_STATE_SESSION_SHUTDOWN: | 
 | 		fcport->edif.sess_down_acked = 1; | 
 | 		break; | 
 | 	default: | 
 | 		break; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int qla_edif_consume_dbell(scsi_qla_host_t *vha, struct bsg_job *bsg_job) | 
 | { | 
 | 	struct fc_bsg_reply	*bsg_reply = bsg_job->reply; | 
 | 	u32 sg_skip, reply_payload_len; | 
 | 	bool keep; | 
 | 	struct edb_node *dbnode = NULL; | 
 | 	struct edif_app_dbell ap; | 
 | 	int dat_size = 0; | 
 |  | 
 | 	sg_skip = 0; | 
 | 	reply_payload_len = bsg_job->reply_payload.payload_len; | 
 |  | 
 | 	while ((reply_payload_len - sg_skip) >= sizeof(struct edb_node)) { | 
 | 		dbnode = qla_edb_getnext(vha); | 
 | 		if (dbnode) { | 
 | 			keep = true; | 
 | 			dat_size = 0; | 
 | 			ap.event_code = dbnode->ntype; | 
 | 			switch (dbnode->ntype) { | 
 | 			case VND_CMD_AUTH_STATE_SESSION_SHUTDOWN: | 
 | 			case VND_CMD_AUTH_STATE_NEEDED: | 
 | 				ap.port_id = dbnode->u.plogi_did; | 
 | 				dat_size += sizeof(ap.port_id); | 
 | 				break; | 
 | 			case VND_CMD_AUTH_STATE_ELS_RCVD: | 
 | 				ap.port_id = dbnode->u.els_sid; | 
 | 				dat_size += sizeof(ap.port_id); | 
 | 				break; | 
 | 			case VND_CMD_AUTH_STATE_SAUPDATE_COMPL: | 
 | 				ap.port_id = dbnode->u.sa_aen.port_id; | 
 | 				memcpy(&ap.event_data, &dbnode->u, | 
 | 				    sizeof(struct edif_sa_update_aen)); | 
 | 				dat_size += sizeof(struct edif_sa_update_aen); | 
 | 				break; | 
 | 			default: | 
 | 				keep = false; | 
 | 				ql_log(ql_log_warn, vha, 0x09102, | 
 | 					"%s unknown DB type=%d %p\n", | 
 | 					__func__, dbnode->ntype, dbnode); | 
 | 				break; | 
 | 			} | 
 | 			ap.event_data_size = dat_size; | 
 | 			/* 8 = sizeof(ap.event_code + ap.event_data_size) */ | 
 | 			dat_size += 8; | 
 | 			if (keep) | 
 | 				sg_skip += sg_copy_buffer(bsg_job->reply_payload.sg_list, | 
 | 						bsg_job->reply_payload.sg_cnt, | 
 | 						&ap, dat_size, sg_skip, false); | 
 |  | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x09102, | 
 | 				"%s Doorbell consumed : type=%d %p\n", | 
 | 				__func__, dbnode->ntype, dbnode); | 
 |  | 
 | 			kfree(dbnode); | 
 | 		} else { | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	SET_DID_STATUS(bsg_reply->result, DID_OK); | 
 | 	bsg_reply->reply_payload_rcv_len = sg_skip; | 
 | 	bsg_job->reply_len = sizeof(struct fc_bsg_reply); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void __qla_edif_dbell_bsg_done(scsi_qla_host_t *vha, struct bsg_job *bsg_job, | 
 | 	u32 delay) | 
 | { | 
 | 	struct fc_bsg_reply *bsg_reply = bsg_job->reply; | 
 |  | 
 | 	/* small sleep for doorbell events to accumulate */ | 
 | 	if (delay) | 
 | 		msleep(delay); | 
 |  | 
 | 	qla_edif_consume_dbell(vha, bsg_job); | 
 |  | 
 | 	bsg_job_done(bsg_job, bsg_reply->result, bsg_reply->reply_payload_rcv_len); | 
 | } | 
 |  | 
 | static void qla_edif_dbell_bsg_done(scsi_qla_host_t *vha) | 
 | { | 
 | 	unsigned long flags; | 
 | 	struct bsg_job *prev_bsg_job = NULL; | 
 |  | 
 | 	spin_lock_irqsave(&vha->e_dbell.db_lock, flags); | 
 | 	if (vha->e_dbell.dbell_bsg_job) { | 
 | 		prev_bsg_job = vha->e_dbell.dbell_bsg_job; | 
 | 		vha->e_dbell.dbell_bsg_job = NULL; | 
 | 	} | 
 | 	spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags); | 
 |  | 
 | 	if (prev_bsg_job) | 
 | 		__qla_edif_dbell_bsg_done(vha, prev_bsg_job, 0); | 
 | } | 
 |  | 
 | static int | 
 | qla_edif_dbell_bsg(scsi_qla_host_t *vha, struct bsg_job *bsg_job) | 
 | { | 
 | 	unsigned long flags; | 
 | 	bool return_bsg = false; | 
 |  | 
 | 	/* flush previous dbell bsg */ | 
 | 	qla_edif_dbell_bsg_done(vha); | 
 |  | 
 | 	spin_lock_irqsave(&vha->e_dbell.db_lock, flags); | 
 | 	if (list_empty(&vha->e_dbell.head) && DBELL_ACTIVE(vha)) { | 
 | 		/* | 
 | 		 * when the next db event happens, bsg_job will return. | 
 | 		 * Otherwise, timer will return it. | 
 | 		 */ | 
 | 		vha->e_dbell.dbell_bsg_job = bsg_job; | 
 | 		vha->e_dbell.bsg_expire = jiffies + 10 * HZ; | 
 | 	} else { | 
 | 		return_bsg = true; | 
 | 	} | 
 | 	spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags); | 
 |  | 
 | 	if (return_bsg) | 
 | 		__qla_edif_dbell_bsg_done(vha, bsg_job, 1); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int32_t | 
 | qla_edif_app_mgmt(struct bsg_job *bsg_job) | 
 | { | 
 | 	struct fc_bsg_request	*bsg_request = bsg_job->request; | 
 | 	struct fc_bsg_reply	*bsg_reply = bsg_job->reply; | 
 | 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job); | 
 | 	scsi_qla_host_t		*vha = shost_priv(host); | 
 | 	struct app_id		appcheck; | 
 | 	bool done = true; | 
 | 	int32_t         rval = 0; | 
 | 	uint32_t	vnd_sc = bsg_request->rqst_data.h_vendor.vendor_cmd[1]; | 
 | 	u32 level = ql_dbg_edif; | 
 |  | 
 | 	/* doorbell is high traffic */ | 
 | 	if (vnd_sc == QL_VND_SC_READ_DBELL) | 
 | 		level = 0; | 
 |  | 
 | 	ql_dbg(level, vha, 0x911d, "%s vnd subcmd=%x\n", | 
 | 	    __func__, vnd_sc); | 
 |  | 
 | 	sg_copy_to_buffer(bsg_job->request_payload.sg_list, | 
 | 	    bsg_job->request_payload.sg_cnt, &appcheck, | 
 | 	    sizeof(struct app_id)); | 
 |  | 
 | 	if (!vha->hw->flags.edif_enabled || | 
 | 		test_bit(VPORT_DELETE, &vha->dpc_flags)) { | 
 | 		ql_dbg(level, vha, 0x911d, | 
 | 		    "%s edif not enabled or vp delete. bsg ptr done %p. dpc_flags %lx\n", | 
 | 		    __func__, bsg_job, vha->dpc_flags); | 
 |  | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_ERROR); | 
 | 		goto done; | 
 | 	} | 
 |  | 
 | 	if (!qla_edif_app_check(vha, appcheck)) { | 
 | 		ql_dbg(level, vha, 0x911d, | 
 | 		    "%s app checked failed.\n", | 
 | 		    __func__); | 
 |  | 
 | 		bsg_job->reply_len = sizeof(struct fc_bsg_reply); | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_ERROR); | 
 | 		goto done; | 
 | 	} | 
 |  | 
 | 	switch (vnd_sc) { | 
 | 	case QL_VND_SC_SA_UPDATE: | 
 | 		done = false; | 
 | 		rval = qla24xx_sadb_update(bsg_job); | 
 | 		break; | 
 | 	case QL_VND_SC_APP_START: | 
 | 		rval = qla_edif_app_start(vha, bsg_job); | 
 | 		break; | 
 | 	case QL_VND_SC_APP_STOP: | 
 | 		rval = qla_edif_app_stop(vha, bsg_job); | 
 | 		break; | 
 | 	case QL_VND_SC_AUTH_OK: | 
 | 		rval = qla_edif_app_authok(vha, bsg_job); | 
 | 		break; | 
 | 	case QL_VND_SC_AUTH_FAIL: | 
 | 		rval = qla_edif_app_authfail(vha, bsg_job); | 
 | 		break; | 
 | 	case QL_VND_SC_GET_FCINFO: | 
 | 		rval = qla_edif_app_getfcinfo(vha, bsg_job); | 
 | 		break; | 
 | 	case QL_VND_SC_GET_STATS: | 
 | 		rval = qla_edif_app_getstats(vha, bsg_job); | 
 | 		break; | 
 | 	case QL_VND_SC_AEN_COMPLETE: | 
 | 		rval = qla_edif_ack(vha, bsg_job); | 
 | 		break; | 
 | 	case QL_VND_SC_READ_DBELL: | 
 | 		rval = qla_edif_dbell_bsg(vha, bsg_job); | 
 | 		done = false; | 
 | 		break; | 
 | 	default: | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911d, "%s unknown cmd=%x\n", | 
 | 		    __func__, | 
 | 		    bsg_request->rqst_data.h_vendor.vendor_cmd[1]); | 
 | 		rval = EXT_STATUS_INVALID_PARAM; | 
 | 		done = false; | 
 | 		break; | 
 | 	} | 
 |  | 
 | done: | 
 | 	if (done) { | 
 | 		ql_dbg(level, vha, 0x7009, | 
 | 		    "%s: %d  bsg ptr done %p\n", __func__, __LINE__, bsg_job); | 
 | 		bsg_job_done(bsg_job, bsg_reply->result, | 
 | 		    bsg_reply->reply_payload_rcv_len); | 
 | 	} | 
 |  | 
 | 	return rval; | 
 | } | 
 |  | 
 | static struct edif_sa_ctl * | 
 | qla_edif_add_sa_ctl(fc_port_t *fcport, struct qla_sa_update_frame *sa_frame, | 
 | 	int dir) | 
 | { | 
 | 	struct	edif_sa_ctl *sa_ctl; | 
 | 	struct qla_sa_update_frame *sap; | 
 | 	int	index = sa_frame->fast_sa_index; | 
 | 	unsigned long flags = 0; | 
 |  | 
 | 	sa_ctl = kzalloc(sizeof(*sa_ctl), GFP_KERNEL); | 
 | 	if (!sa_ctl) { | 
 | 		/* couldn't get space */ | 
 | 		ql_dbg(ql_dbg_edif, fcport->vha, 0x9100, | 
 | 		    "unable to allocate SA CTL\n"); | 
 | 		return NULL; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * need to allocate sa_index here and save it | 
 | 	 * in both sa_ctl->index and sa_frame->fast_sa_index; | 
 | 	 * If alloc fails then delete sa_ctl and return NULL | 
 | 	 */ | 
 | 	INIT_LIST_HEAD(&sa_ctl->next); | 
 | 	sap = &sa_ctl->sa_frame; | 
 | 	*sap = *sa_frame; | 
 | 	sa_ctl->index = index; | 
 | 	sa_ctl->fcport = fcport; | 
 | 	sa_ctl->flags = 0; | 
 | 	sa_ctl->state = 0L; | 
 | 	ql_dbg(ql_dbg_edif, fcport->vha, 0x9100, | 
 | 	    "%s: Added sa_ctl %p, index %d, state 0x%lx\n", | 
 | 	    __func__, sa_ctl, sa_ctl->index, sa_ctl->state); | 
 | 	spin_lock_irqsave(&fcport->edif.sa_list_lock, flags); | 
 | 	if (dir == SAU_FLG_TX) | 
 | 		list_add_tail(&sa_ctl->next, &fcport->edif.tx_sa_list); | 
 | 	else | 
 | 		list_add_tail(&sa_ctl->next, &fcport->edif.rx_sa_list); | 
 | 	spin_unlock_irqrestore(&fcport->edif.sa_list_lock, flags); | 
 |  | 
 | 	return sa_ctl; | 
 | } | 
 |  | 
 | void | 
 | qla_edif_flush_sa_ctl_lists(fc_port_t *fcport) | 
 | { | 
 | 	struct edif_sa_ctl *sa_ctl, *tsa_ctl; | 
 | 	unsigned long flags = 0; | 
 |  | 
 | 	spin_lock_irqsave(&fcport->edif.sa_list_lock, flags); | 
 |  | 
 | 	list_for_each_entry_safe(sa_ctl, tsa_ctl, &fcport->edif.tx_sa_list, | 
 | 	    next) { | 
 | 		list_del(&sa_ctl->next); | 
 | 		kfree(sa_ctl); | 
 | 	} | 
 |  | 
 | 	list_for_each_entry_safe(sa_ctl, tsa_ctl, &fcport->edif.rx_sa_list, | 
 | 	    next) { | 
 | 		list_del(&sa_ctl->next); | 
 | 		kfree(sa_ctl); | 
 | 	} | 
 |  | 
 | 	spin_unlock_irqrestore(&fcport->edif.sa_list_lock, flags); | 
 | } | 
 |  | 
 | struct edif_sa_ctl * | 
 | qla_edif_find_sa_ctl_by_index(fc_port_t *fcport, int index, int dir) | 
 | { | 
 | 	struct edif_sa_ctl *sa_ctl, *tsa_ctl; | 
 | 	struct list_head *sa_list; | 
 |  | 
 | 	if (dir == SAU_FLG_TX) | 
 | 		sa_list = &fcport->edif.tx_sa_list; | 
 | 	else | 
 | 		sa_list = &fcport->edif.rx_sa_list; | 
 |  | 
 | 	list_for_each_entry_safe(sa_ctl, tsa_ctl, sa_list, next) { | 
 | 		if (test_bit(EDIF_SA_CTL_USED, &sa_ctl->state) && | 
 | 		    sa_ctl->index == index) | 
 | 			return sa_ctl; | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /* add the sa to the correct list */ | 
 | static int | 
 | qla24xx_check_sadb_avail_slot(struct bsg_job *bsg_job, fc_port_t *fcport, | 
 | 	struct qla_sa_update_frame *sa_frame) | 
 | { | 
 | 	struct edif_sa_ctl *sa_ctl = NULL; | 
 | 	int dir; | 
 | 	uint16_t sa_index; | 
 |  | 
 | 	dir = (sa_frame->flags & SAU_FLG_TX); | 
 |  | 
 | 	/* map the spi to an sa_index */ | 
 | 	sa_index = qla_edif_sadb_get_sa_index(fcport, sa_frame); | 
 | 	if (sa_index == RX_DELETE_NO_EDIF_SA_INDEX) { | 
 | 		/* process rx delete */ | 
 | 		ql_dbg(ql_dbg_edif, fcport->vha, 0x3063, | 
 | 		    "%s: rx delete for lid 0x%x, spi 0x%x, no entry found\n", | 
 | 		    __func__, fcport->loop_id, sa_frame->spi); | 
 |  | 
 | 		/* build and send the aen */ | 
 | 		fcport->edif.rx_sa_set = 1; | 
 | 		fcport->edif.rx_sa_pending = 0; | 
 | 		qla_edb_eventcreate(fcport->vha, | 
 | 		    VND_CMD_AUTH_STATE_SAUPDATE_COMPL, | 
 | 		    QL_VND_SA_STAT_SUCCESS, | 
 | 		    QL_VND_RX_SA_KEY, fcport); | 
 |  | 
 | 		/* force a return of good bsg status; */ | 
 | 		return RX_DELETE_NO_EDIF_SA_INDEX; | 
 | 	} else if (sa_index == INVALID_EDIF_SA_INDEX) { | 
 | 		ql_dbg(ql_dbg_edif, fcport->vha, 0x9100, | 
 | 		    "%s: Failed to get sa_index for spi 0x%x, dir: %d\n", | 
 | 		    __func__, sa_frame->spi, dir); | 
 | 		return INVALID_EDIF_SA_INDEX; | 
 | 	} | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, fcport->vha, 0x9100, | 
 | 	    "%s: index %d allocated to spi 0x%x, dir: %d, nport_handle: 0x%x\n", | 
 | 	    __func__, sa_index, sa_frame->spi, dir, fcport->loop_id); | 
 |  | 
 | 	/* This is a local copy of sa_frame. */ | 
 | 	sa_frame->fast_sa_index = sa_index; | 
 | 	/* create the sa_ctl */ | 
 | 	sa_ctl = qla_edif_add_sa_ctl(fcport, sa_frame, dir); | 
 | 	if (!sa_ctl) { | 
 | 		ql_dbg(ql_dbg_edif, fcport->vha, 0x9100, | 
 | 		    "%s: Failed to add sa_ctl for spi 0x%x, dir: %d, sa_index: %d\n", | 
 | 		    __func__, sa_frame->spi, dir, sa_index); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	set_bit(EDIF_SA_CTL_USED, &sa_ctl->state); | 
 |  | 
 | 	if (dir == SAU_FLG_TX) | 
 | 		fcport->edif.tx_rekey_cnt++; | 
 | 	else | 
 | 		fcport->edif.rx_rekey_cnt++; | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, fcport->vha, 0x9100, | 
 | 	    "%s: Found sa_ctl %p, index %d, state 0x%lx, tx_cnt %d, rx_cnt %d, nport_handle: 0x%x\n", | 
 | 	    __func__, sa_ctl, sa_ctl->index, sa_ctl->state, | 
 | 	    fcport->edif.tx_rekey_cnt, | 
 | 	    fcport->edif.rx_rekey_cnt, fcport->loop_id); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | #define QLA_SA_UPDATE_FLAGS_RX_KEY      0x0 | 
 | #define QLA_SA_UPDATE_FLAGS_TX_KEY      0x2 | 
 | #define EDIF_MSLEEP_INTERVAL 100 | 
 | #define EDIF_RETRY_COUNT  50 | 
 |  | 
 | int | 
 | qla24xx_sadb_update(struct bsg_job *bsg_job) | 
 | { | 
 | 	struct	fc_bsg_reply	*bsg_reply = bsg_job->reply; | 
 | 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job); | 
 | 	scsi_qla_host_t *vha = shost_priv(host); | 
 | 	fc_port_t		*fcport = NULL; | 
 | 	srb_t			*sp = NULL; | 
 | 	struct edif_list_entry *edif_entry = NULL; | 
 | 	int			found = 0; | 
 | 	int			rval = 0; | 
 | 	int result = 0, cnt; | 
 | 	struct qla_sa_update_frame sa_frame; | 
 | 	struct srb_iocb *iocb_cmd; | 
 | 	port_id_t portid; | 
 |  | 
 | 	ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x911d, | 
 | 	    "%s entered, vha: 0x%p\n", __func__, vha); | 
 |  | 
 | 	sg_copy_to_buffer(bsg_job->request_payload.sg_list, | 
 | 	    bsg_job->request_payload.sg_cnt, &sa_frame, | 
 | 	    sizeof(struct qla_sa_update_frame)); | 
 |  | 
 | 	/* Check if host is online */ | 
 | 	if (!vha->flags.online) { | 
 | 		ql_log(ql_log_warn, vha, 0x70a1, "Host is not online\n"); | 
 | 		rval = -EIO; | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_ERROR); | 
 | 		goto done; | 
 | 	} | 
 |  | 
 | 	if (DBELL_INACTIVE(vha)) { | 
 | 		ql_log(ql_log_warn, vha, 0x70a1, "App not started\n"); | 
 | 		rval = -EIO; | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_ERROR); | 
 | 		goto done; | 
 | 	} | 
 |  | 
 | 	/* silent unaligned access warning */ | 
 | 	portid.b.domain = sa_frame.port_id.b.domain; | 
 | 	portid.b.area   = sa_frame.port_id.b.area; | 
 | 	portid.b.al_pa  = sa_frame.port_id.b.al_pa; | 
 |  | 
 | 	fcport = qla2x00_find_fcport_by_pid(vha, &portid); | 
 | 	if (fcport) { | 
 | 		found = 1; | 
 | 		if (sa_frame.flags == QLA_SA_UPDATE_FLAGS_TX_KEY) | 
 | 			fcport->edif.tx_bytes = 0; | 
 | 		if (sa_frame.flags == QLA_SA_UPDATE_FLAGS_RX_KEY) | 
 | 			fcport->edif.rx_bytes = 0; | 
 | 	} | 
 |  | 
 | 	if (!found) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x70a3, "Failed to find port= %06x\n", | 
 | 		    sa_frame.port_id.b24); | 
 | 		rval = -EINVAL; | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_NO_CONNECT); | 
 | 		goto done; | 
 | 	} | 
 |  | 
 | 	/* make sure the nport_handle is valid */ | 
 | 	if (fcport->loop_id == FC_NO_LOOP_ID) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x70e1, | 
 | 		    "%s: %8phN lid=FC_NO_LOOP_ID, spi: 0x%x, DS %d, returning NO_CONNECT\n", | 
 | 		    __func__, fcport->port_name, sa_frame.spi, | 
 | 		    fcport->disc_state); | 
 | 		rval = -EINVAL; | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_NO_CONNECT); | 
 | 		goto done; | 
 | 	} | 
 |  | 
 | 	/* allocate and queue an sa_ctl */ | 
 | 	result = qla24xx_check_sadb_avail_slot(bsg_job, fcport, &sa_frame); | 
 |  | 
 | 	/* failure of bsg */ | 
 | 	if (result == INVALID_EDIF_SA_INDEX) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x70e1, | 
 | 		    "%s: %8phN, skipping update.\n", | 
 | 		    __func__, fcport->port_name); | 
 | 		rval = -EINVAL; | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_ERROR); | 
 | 		goto done; | 
 |  | 
 | 	/* rx delete failure */ | 
 | 	} else if (result == RX_DELETE_NO_EDIF_SA_INDEX) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x70e1, | 
 | 		    "%s: %8phN, skipping rx delete.\n", | 
 | 		    __func__, fcport->port_name); | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_OK); | 
 | 		goto done; | 
 | 	} | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x70e1, | 
 | 	    "%s: %8phN, sa_index in sa_frame: %d flags %xh\n", | 
 | 	    __func__, fcport->port_name, sa_frame.fast_sa_index, | 
 | 	    sa_frame.flags); | 
 |  | 
 | 	/* looking for rx index and delete */ | 
 | 	if (((sa_frame.flags & SAU_FLG_TX) == 0) && | 
 | 	    (sa_frame.flags & SAU_FLG_INV)) { | 
 | 		uint16_t nport_handle = fcport->loop_id; | 
 | 		uint16_t sa_index = sa_frame.fast_sa_index; | 
 |  | 
 | 		/* | 
 | 		 * make sure we have an existing rx key, otherwise just process | 
 | 		 * this as a straight delete just like TX | 
 | 		 * This is NOT a normal case, it indicates an error recovery or key cleanup | 
 | 		 * by the ipsec code above us. | 
 | 		 */ | 
 | 		edif_entry = qla_edif_list_find_sa_index(fcport, fcport->loop_id); | 
 | 		if (!edif_entry) { | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 			    "%s: WARNING: no active sa_index for nport_handle 0x%x, forcing delete for sa_index 0x%x\n", | 
 | 			    __func__, fcport->loop_id, sa_index); | 
 | 			goto force_rx_delete; | 
 | 		} | 
 |  | 
 | 		/* | 
 | 		 * if we have a forced delete for rx, remove the sa_index from the edif list | 
 | 		 * and proceed with normal delete.  The rx delay timer should not be running | 
 | 		 */ | 
 | 		if ((sa_frame.flags & SAU_FLG_FORCE_DELETE) == SAU_FLG_FORCE_DELETE) { | 
 | 			qla_edif_list_delete_sa_index(fcport, edif_entry); | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 			    "%s: FORCE DELETE flag found for nport_handle 0x%x, sa_index 0x%x, forcing DELETE\n", | 
 | 			    __func__, fcport->loop_id, sa_index); | 
 | 			kfree(edif_entry); | 
 | 			goto force_rx_delete; | 
 | 		} | 
 |  | 
 | 		/* | 
 | 		 * delayed rx delete | 
 | 		 * | 
 | 		 * if delete_sa_index is not invalid then there is already | 
 | 		 * a delayed index in progress, return bsg bad status | 
 | 		 */ | 
 | 		if (edif_entry->delete_sa_index != INVALID_EDIF_SA_INDEX) { | 
 | 			struct edif_sa_ctl *sa_ctl; | 
 |  | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 			    "%s: delete for lid 0x%x, delete_sa_index %d is pending\n", | 
 | 			    __func__, edif_entry->handle, edif_entry->delete_sa_index); | 
 |  | 
 | 			/* free up the sa_ctl that was allocated with the sa_index */ | 
 | 			sa_ctl = qla_edif_find_sa_ctl_by_index(fcport, sa_index, | 
 | 			    (sa_frame.flags & SAU_FLG_TX)); | 
 | 			if (sa_ctl) { | 
 | 				ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 				    "%s: freeing sa_ctl for index %d\n", | 
 | 				    __func__, sa_ctl->index); | 
 | 				qla_edif_free_sa_ctl(fcport, sa_ctl, sa_ctl->index); | 
 | 			} | 
 |  | 
 | 			/* release the sa_index */ | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 			    "%s: freeing sa_index %d, nph: 0x%x\n", | 
 | 			    __func__, sa_index, nport_handle); | 
 | 			qla_edif_sadb_delete_sa_index(fcport, nport_handle, sa_index); | 
 |  | 
 | 			rval = -EINVAL; | 
 | 			SET_DID_STATUS(bsg_reply->result, DID_ERROR); | 
 | 			goto done; | 
 | 		} | 
 |  | 
 | 		fcport->edif.rekey_cnt++; | 
 |  | 
 | 		/* configure and start the rx delay timer */ | 
 | 		edif_entry->fcport = fcport; | 
 | 		edif_entry->timer.expires = jiffies + RX_DELAY_DELETE_TIMEOUT * HZ; | 
 |  | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 		    "%s: adding timer, entry: %p, delete sa_index %d, lid 0x%x to edif_list\n", | 
 | 		    __func__, edif_entry, sa_index, nport_handle); | 
 |  | 
 | 		/* | 
 | 		 * Start the timer when we queue the delayed rx delete. | 
 | 		 * This is an activity timer that goes off if we have not | 
 | 		 * received packets with the new sa_index | 
 | 		 */ | 
 | 		add_timer(&edif_entry->timer); | 
 |  | 
 | 		/* | 
 | 		 * sa_delete for rx key with an active rx key including this one | 
 | 		 * add the delete rx sa index to the hash so we can look for it | 
 | 		 * in the rsp queue.  Do this after making any changes to the | 
 | 		 * edif_entry as part of the rx delete. | 
 | 		 */ | 
 |  | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 		    "%s: delete sa_index %d, lid 0x%x to edif_list. bsg done ptr %p\n", | 
 | 		    __func__, sa_index, nport_handle, bsg_job); | 
 |  | 
 | 		edif_entry->delete_sa_index = sa_index; | 
 |  | 
 | 		bsg_job->reply_len = sizeof(struct fc_bsg_reply); | 
 | 		bsg_reply->result = DID_OK << 16; | 
 |  | 
 | 		goto done; | 
 |  | 
 | 	/* | 
 | 	 * rx index and update | 
 | 	 * add the index to the list and continue with normal update | 
 | 	 */ | 
 | 	} else if (((sa_frame.flags & SAU_FLG_TX) == 0) && | 
 | 	    ((sa_frame.flags & SAU_FLG_INV) == 0)) { | 
 | 		/* sa_update for rx key */ | 
 | 		uint32_t nport_handle = fcport->loop_id; | 
 | 		uint16_t sa_index = sa_frame.fast_sa_index; | 
 | 		int result; | 
 |  | 
 | 		/* | 
 | 		 * add the update rx sa index to the hash so we can look for it | 
 | 		 * in the rsp queue and continue normally | 
 | 		 */ | 
 |  | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 		    "%s:  adding update sa_index %d, lid 0x%x to edif_list\n", | 
 | 		    __func__, sa_index, nport_handle); | 
 |  | 
 | 		result = qla_edif_list_add_sa_update_index(fcport, sa_index, | 
 | 		    nport_handle); | 
 | 		if (result) { | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 			    "%s: SA_UPDATE failed to add new sa index %d to list for lid 0x%x\n", | 
 | 			    __func__, sa_index, nport_handle); | 
 | 		} | 
 | 	} | 
 | 	if (sa_frame.flags & SAU_FLG_GMAC_MODE) | 
 | 		fcport->edif.aes_gmac = 1; | 
 | 	else | 
 | 		fcport->edif.aes_gmac = 0; | 
 |  | 
 | force_rx_delete: | 
 | 	/* | 
 | 	 * sa_update for both rx and tx keys, sa_delete for tx key | 
 | 	 * immediately process the request | 
 | 	 */ | 
 | 	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); | 
 | 	if (!sp) { | 
 | 		rval = -ENOMEM; | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY); | 
 | 		goto done; | 
 | 	} | 
 |  | 
 | 	sp->type = SRB_SA_UPDATE; | 
 | 	sp->name = "bsg_sa_update"; | 
 | 	sp->u.bsg_job = bsg_job; | 
 | 	/* sp->free = qla2x00_bsg_sp_free; */ | 
 | 	sp->free = qla2x00_rel_sp; | 
 | 	sp->done = qla2x00_bsg_job_done; | 
 | 	iocb_cmd = &sp->u.iocb_cmd; | 
 | 	iocb_cmd->u.sa_update.sa_frame  = sa_frame; | 
 | 	cnt = 0; | 
 | retry: | 
 | 	rval = qla2x00_start_sp(sp); | 
 | 	switch (rval) { | 
 | 	case QLA_SUCCESS: | 
 | 		break; | 
 | 	case EAGAIN: | 
 | 		msleep(EDIF_MSLEEP_INTERVAL); | 
 | 		cnt++; | 
 | 		if (cnt < EDIF_RETRY_COUNT) | 
 | 			goto retry; | 
 |  | 
 | 		fallthrough; | 
 | 	default: | 
 | 		ql_log(ql_dbg_edif, vha, 0x70e3, | 
 | 		       "%s qla2x00_start_sp failed=%d.\n", | 
 | 		       __func__, rval); | 
 |  | 
 | 		qla2x00_rel_sp(sp); | 
 | 		rval = -EIO; | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY); | 
 | 		goto done; | 
 | 	} | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 	    "%s:  %s sent, hdl=%x, portid=%06x.\n", | 
 | 	    __func__, sp->name, sp->handle, fcport->d_id.b24); | 
 |  | 
 | 	fcport->edif.rekey_cnt++; | 
 | 	bsg_job->reply_len = sizeof(struct fc_bsg_reply); | 
 | 	SET_DID_STATUS(bsg_reply->result, DID_OK); | 
 |  | 
 | 	return 0; | 
 |  | 
 | /* | 
 |  * send back error status | 
 |  */ | 
 | done: | 
 | 	bsg_job->reply_len = sizeof(struct fc_bsg_reply); | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 	    "%s:status: FAIL, result: 0x%x, bsg ptr done %p\n", | 
 | 	    __func__, bsg_reply->result, bsg_job); | 
 | 	bsg_job_done(bsg_job, bsg_reply->result, | 
 | 	    bsg_reply->reply_payload_rcv_len); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void | 
 | qla_enode_free(scsi_qla_host_t *vha, struct enode *node) | 
 | { | 
 | 	node->ntype = N_UNDEF; | 
 | 	kfree(node); | 
 | } | 
 |  | 
 | /** | 
 |  * qla_enode_init - initialize enode structs & lock | 
 |  * @vha: host adapter pointer | 
 |  * | 
 |  * should only be called when driver attaching | 
 |  */ | 
 | void | 
 | qla_enode_init(scsi_qla_host_t *vha) | 
 | { | 
 | 	struct	qla_hw_data *ha = vha->hw; | 
 | 	char	name[32]; | 
 |  | 
 | 	if (vha->pur_cinfo.enode_flags == ENODE_ACTIVE) { | 
 | 		/* list still active - error */ | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x09102, "%s enode still active\n", | 
 | 		    __func__); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* initialize lock which protects pur_core & init list */ | 
 | 	spin_lock_init(&vha->pur_cinfo.pur_lock); | 
 | 	INIT_LIST_HEAD(&vha->pur_cinfo.head); | 
 |  | 
 | 	snprintf(name, sizeof(name), "%s_%d_purex", QLA2XXX_DRIVER_NAME, | 
 | 	    ha->pdev->device); | 
 | } | 
 |  | 
 | /** | 
 |  * qla_enode_stop - stop and clear and enode data | 
 |  * @vha: host adapter pointer | 
 |  * | 
 |  * called when app notified it is exiting | 
 |  */ | 
 | void | 
 | qla_enode_stop(scsi_qla_host_t *vha) | 
 | { | 
 | 	unsigned long flags; | 
 | 	struct enode *node, *q; | 
 |  | 
 | 	if (vha->pur_cinfo.enode_flags != ENODE_ACTIVE) { | 
 | 		/* doorbell list not enabled */ | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x09102, | 
 | 		    "%s enode not active\n", __func__); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* grab lock so list doesn't move */ | 
 | 	spin_lock_irqsave(&vha->pur_cinfo.pur_lock, flags); | 
 |  | 
 | 	vha->pur_cinfo.enode_flags &= ~ENODE_ACTIVE; /* mark it not active */ | 
 |  | 
 | 	/* hopefully this is a null list at this point */ | 
 | 	list_for_each_entry_safe(node, q, &vha->pur_cinfo.head, list) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x910f, | 
 | 		    "%s freeing enode type=%x, cnt=%x\n", __func__, node->ntype, | 
 | 		    node->dinfo.nodecnt); | 
 | 		list_del_init(&node->list); | 
 | 		qla_enode_free(vha, node); | 
 | 	} | 
 | 	spin_unlock_irqrestore(&vha->pur_cinfo.pur_lock, flags); | 
 | } | 
 |  | 
 | static void qla_enode_clear(scsi_qla_host_t *vha, port_id_t portid) | 
 | { | 
 | 	unsigned    long flags; | 
 | 	struct enode    *e, *tmp; | 
 | 	struct purexevent   *purex; | 
 | 	LIST_HEAD(enode_list); | 
 |  | 
 | 	if (vha->pur_cinfo.enode_flags != ENODE_ACTIVE) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x09102, | 
 | 		       "%s enode not active\n", __func__); | 
 | 		return; | 
 | 	} | 
 | 	spin_lock_irqsave(&vha->pur_cinfo.pur_lock, flags); | 
 | 	list_for_each_entry_safe(e, tmp, &vha->pur_cinfo.head, list) { | 
 | 		purex = &e->u.purexinfo; | 
 | 		if (purex->pur_info.pur_sid.b24 == portid.b24) { | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 			    "%s free ELS sid=%06x. xchg %x, nb=%xh\n", | 
 | 			    __func__, portid.b24, | 
 | 			    purex->pur_info.pur_rx_xchg_address, | 
 | 			    purex->pur_info.pur_bytes_rcvd); | 
 |  | 
 | 			list_del_init(&e->list); | 
 | 			list_add_tail(&e->list, &enode_list); | 
 | 		} | 
 | 	} | 
 | 	spin_unlock_irqrestore(&vha->pur_cinfo.pur_lock, flags); | 
 |  | 
 | 	list_for_each_entry_safe(e, tmp, &enode_list, list) { | 
 | 		list_del_init(&e->list); | 
 | 		qla_enode_free(vha, e); | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  *  allocate enode struct and populate buffer | 
 |  *  returns: enode pointer with buffers | 
 |  *           NULL on error | 
 |  */ | 
 | static struct enode * | 
 | qla_enode_alloc(scsi_qla_host_t *vha, uint32_t ntype) | 
 | { | 
 | 	struct enode		*node; | 
 | 	struct purexevent	*purex; | 
 |  | 
 | 	node = kzalloc(RX_ELS_SIZE, GFP_ATOMIC); | 
 | 	if (!node) | 
 | 		return NULL; | 
 |  | 
 | 	purex = &node->u.purexinfo; | 
 | 	purex->msgp = (u8 *)(node + 1); | 
 | 	purex->msgp_len = ELS_MAX_PAYLOAD; | 
 |  | 
 | 	node->ntype = ntype; | 
 | 	INIT_LIST_HEAD(&node->list); | 
 | 	return node; | 
 | } | 
 |  | 
 | static void | 
 | qla_enode_add(scsi_qla_host_t *vha, struct enode *ptr) | 
 | { | 
 | 	unsigned long flags; | 
 |  | 
 | 	ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x9109, | 
 | 	    "%s add enode for type=%x, cnt=%x\n", | 
 | 	    __func__, ptr->ntype, ptr->dinfo.nodecnt); | 
 |  | 
 | 	spin_lock_irqsave(&vha->pur_cinfo.pur_lock, flags); | 
 | 	list_add_tail(&ptr->list, &vha->pur_cinfo.head); | 
 | 	spin_unlock_irqrestore(&vha->pur_cinfo.pur_lock, flags); | 
 |  | 
 | 	return; | 
 | } | 
 |  | 
 | static struct enode * | 
 | qla_enode_find(scsi_qla_host_t *vha, uint32_t ntype, uint32_t p1, uint32_t p2) | 
 | { | 
 | 	struct enode		*node_rtn = NULL; | 
 | 	struct enode		*list_node, *q; | 
 | 	unsigned long		flags; | 
 | 	uint32_t		sid; | 
 | 	struct purexevent	*purex; | 
 |  | 
 | 	/* secure the list from moving under us */ | 
 | 	spin_lock_irqsave(&vha->pur_cinfo.pur_lock, flags); | 
 |  | 
 | 	list_for_each_entry_safe(list_node, q, &vha->pur_cinfo.head, list) { | 
 |  | 
 | 		/* node type determines what p1 and p2 are */ | 
 | 		purex = &list_node->u.purexinfo; | 
 | 		sid = p1; | 
 |  | 
 | 		if (purex->pur_info.pur_sid.b24 == sid) { | 
 | 			/* found it and its complete */ | 
 | 			node_rtn = list_node; | 
 | 			list_del(&list_node->list); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	spin_unlock_irqrestore(&vha->pur_cinfo.pur_lock, flags); | 
 |  | 
 | 	return node_rtn; | 
 | } | 
 |  | 
 | /** | 
 |  * qla_pur_get_pending - read/return authentication message sent | 
 |  *  from remote port | 
 |  * @vha: host adapter pointer | 
 |  * @fcport: session pointer | 
 |  * @bsg_job: user request where the message is copy to. | 
 |  */ | 
 | static int | 
 | qla_pur_get_pending(scsi_qla_host_t *vha, fc_port_t *fcport, | 
 | 	struct bsg_job *bsg_job) | 
 | { | 
 | 	struct enode		*ptr; | 
 | 	struct purexevent	*purex; | 
 | 	struct qla_bsg_auth_els_reply *rpl = | 
 | 	    (struct qla_bsg_auth_els_reply *)bsg_job->reply; | 
 |  | 
 | 	bsg_job->reply_len = sizeof(*rpl); | 
 |  | 
 | 	ptr = qla_enode_find(vha, N_PUREX, fcport->d_id.b24, PUR_GET); | 
 | 	if (!ptr) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x9111, | 
 | 		    "%s no enode data found for %8phN sid=%06x\n", | 
 | 		    __func__, fcport->port_name, fcport->d_id.b24); | 
 | 		SET_DID_STATUS(rpl->r.result, DID_IMM_RETRY); | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * enode is now off the linked list and is ours to deal with | 
 | 	 */ | 
 | 	purex = &ptr->u.purexinfo; | 
 |  | 
 | 	/* Copy info back to caller */ | 
 | 	rpl->rx_xchg_address = purex->pur_info.pur_rx_xchg_address; | 
 |  | 
 | 	SET_DID_STATUS(rpl->r.result, DID_OK); | 
 | 	rpl->r.reply_payload_rcv_len = | 
 | 	    sg_pcopy_from_buffer(bsg_job->reply_payload.sg_list, | 
 | 		bsg_job->reply_payload.sg_cnt, purex->msgp, | 
 | 		purex->pur_info.pur_bytes_rcvd, 0); | 
 |  | 
 | 	/* data copy / passback completed - destroy enode */ | 
 | 	qla_enode_free(vha, ptr); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* it is assume qpair lock is held */ | 
 | static int | 
 | qla_els_reject_iocb(scsi_qla_host_t *vha, struct qla_qpair *qp, | 
 | 	struct qla_els_pt_arg *a) | 
 | { | 
 | 	struct els_entry_24xx *els_iocb; | 
 |  | 
 | 	els_iocb = __qla2x00_alloc_iocbs(qp, NULL); | 
 | 	if (!els_iocb) { | 
 | 		ql_log(ql_log_warn, vha, 0x700c, | 
 | 		    "qla2x00_alloc_iocbs failed.\n"); | 
 | 		return QLA_FUNCTION_FAILED; | 
 | 	} | 
 |  | 
 | 	qla_els_pt_iocb(vha, els_iocb, a); | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x0183, | 
 | 	    "Sending ELS reject ox_id %04x s:%06x -> d:%06x\n", | 
 | 	    a->ox_id, a->sid.b24, a->did.b24); | 
 | 	ql_dump_buffer(ql_dbg_edif + ql_dbg_verbose, vha, 0x0185, | 
 | 	    vha->hw->elsrej.c, sizeof(*vha->hw->elsrej.c)); | 
 | 	/* flush iocb to mem before notifying hw doorbell */ | 
 | 	wmb(); | 
 | 	qla2x00_start_iocbs(vha, qp->req); | 
 | 	return 0; | 
 | } | 
 |  | 
 | void | 
 | qla_edb_init(scsi_qla_host_t *vha) | 
 | { | 
 | 	if (DBELL_ACTIVE(vha)) { | 
 | 		/* list already init'd - error */ | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x09102, | 
 | 		    "edif db already initialized, cannot reinit\n"); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* initialize lock which protects doorbell & init list */ | 
 | 	spin_lock_init(&vha->e_dbell.db_lock); | 
 | 	INIT_LIST_HEAD(&vha->e_dbell.head); | 
 | } | 
 |  | 
 | static void qla_edb_clear(scsi_qla_host_t *vha, port_id_t portid) | 
 | { | 
 | 	unsigned long flags; | 
 | 	struct edb_node *e, *tmp; | 
 | 	port_id_t sid; | 
 | 	LIST_HEAD(edb_list); | 
 |  | 
 | 	if (DBELL_INACTIVE(vha)) { | 
 | 		/* doorbell list not enabled */ | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x09102, | 
 | 		       "%s doorbell not enabled\n", __func__); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* grab lock so list doesn't move */ | 
 | 	spin_lock_irqsave(&vha->e_dbell.db_lock, flags); | 
 | 	list_for_each_entry_safe(e, tmp, &vha->e_dbell.head, list) { | 
 | 		switch (e->ntype) { | 
 | 		case VND_CMD_AUTH_STATE_NEEDED: | 
 | 		case VND_CMD_AUTH_STATE_SESSION_SHUTDOWN: | 
 | 			sid = e->u.plogi_did; | 
 | 			break; | 
 | 		case VND_CMD_AUTH_STATE_ELS_RCVD: | 
 | 			sid = e->u.els_sid; | 
 | 			break; | 
 | 		case VND_CMD_AUTH_STATE_SAUPDATE_COMPL: | 
 | 			/* app wants to see this  */ | 
 | 			continue; | 
 | 		default: | 
 | 			ql_log(ql_log_warn, vha, 0x09102, | 
 | 			       "%s unknown node type: %x\n", __func__, e->ntype); | 
 | 			sid.b24 = 0; | 
 | 			break; | 
 | 		} | 
 | 		if (sid.b24 == portid.b24) { | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x910f, | 
 | 			       "%s free doorbell event : node type = %x %p\n", | 
 | 			       __func__, e->ntype, e); | 
 | 			list_del_init(&e->list); | 
 | 			list_add_tail(&e->list, &edb_list); | 
 | 		} | 
 | 	} | 
 | 	spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags); | 
 |  | 
 | 	list_for_each_entry_safe(e, tmp, &edb_list, list) | 
 | 		qla_edb_node_free(vha, e); | 
 | } | 
 |  | 
 | /* function called when app is stopping */ | 
 |  | 
 | void | 
 | qla_edb_stop(scsi_qla_host_t *vha) | 
 | { | 
 | 	unsigned long flags; | 
 | 	struct edb_node *node, *q; | 
 |  | 
 | 	if (DBELL_INACTIVE(vha)) { | 
 | 		/* doorbell list not enabled */ | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x09102, | 
 | 		    "%s doorbell not enabled\n", __func__); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* grab lock so list doesn't move */ | 
 | 	spin_lock_irqsave(&vha->e_dbell.db_lock, flags); | 
 |  | 
 | 	vha->e_dbell.db_flags &= ~EDB_ACTIVE; /* mark it not active */ | 
 | 	/* hopefully this is a null list at this point */ | 
 | 	list_for_each_entry_safe(node, q, &vha->e_dbell.head, list) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x910f, | 
 | 		    "%s freeing edb_node type=%x\n", | 
 | 		    __func__, node->ntype); | 
 | 		qla_edb_node_free(vha, node); | 
 | 	} | 
 | 	spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags); | 
 |  | 
 | 	qla_edif_dbell_bsg_done(vha); | 
 | } | 
 |  | 
 | static struct edb_node * | 
 | qla_edb_node_alloc(scsi_qla_host_t *vha, uint32_t ntype) | 
 | { | 
 | 	struct edb_node	*node; | 
 |  | 
 | 	node = kzalloc(sizeof(*node), GFP_ATOMIC); | 
 | 	if (!node) { | 
 | 		/* couldn't get space */ | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x9100, | 
 | 		    "edb node unable to be allocated\n"); | 
 | 		return NULL; | 
 | 	} | 
 |  | 
 | 	node->ntype = ntype; | 
 | 	INIT_LIST_HEAD(&node->list); | 
 | 	return node; | 
 | } | 
 |  | 
 | /* adds a already allocated enode to the linked list */ | 
 | static bool | 
 | qla_edb_node_add(scsi_qla_host_t *vha, struct edb_node *ptr) | 
 | { | 
 | 	unsigned long		flags; | 
 |  | 
 | 	if (DBELL_INACTIVE(vha)) { | 
 | 		/* doorbell list not enabled */ | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x09102, | 
 | 		    "%s doorbell not enabled\n", __func__); | 
 | 		return false; | 
 | 	} | 
 |  | 
 | 	spin_lock_irqsave(&vha->e_dbell.db_lock, flags); | 
 | 	list_add_tail(&ptr->list, &vha->e_dbell.head); | 
 | 	spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | /* adds event to doorbell list */ | 
 | void | 
 | qla_edb_eventcreate(scsi_qla_host_t *vha, uint32_t dbtype, | 
 | 	uint32_t data, uint32_t data2, fc_port_t	*sfcport) | 
 | { | 
 | 	struct edb_node	*edbnode; | 
 | 	fc_port_t *fcport = sfcport; | 
 | 	port_id_t id; | 
 |  | 
 | 	if (!vha->hw->flags.edif_enabled) { | 
 | 		/* edif not enabled */ | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	if (DBELL_INACTIVE(vha)) { | 
 | 		if (fcport) | 
 | 			fcport->edif.auth_state = dbtype; | 
 | 		/* doorbell list not enabled */ | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x09102, | 
 | 		    "%s doorbell not enabled (type=%d\n", __func__, dbtype); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	edbnode = qla_edb_node_alloc(vha, dbtype); | 
 | 	if (!edbnode) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x09102, | 
 | 		    "%s unable to alloc db node\n", __func__); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	if (!fcport) { | 
 | 		id.b.domain = (data >> 16) & 0xff; | 
 | 		id.b.area = (data >> 8) & 0xff; | 
 | 		id.b.al_pa = data & 0xff; | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x09222, | 
 | 		    "%s: Arrived s_id: %06x\n", __func__, | 
 | 		    id.b24); | 
 | 		fcport = qla2x00_find_fcport_by_pid(vha, &id); | 
 | 		if (!fcport) { | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x09102, | 
 | 			    "%s can't find fcport for sid= 0x%x - ignoring\n", | 
 | 			__func__, id.b24); | 
 | 			kfree(edbnode); | 
 | 			return; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* populate the edb node */ | 
 | 	switch (dbtype) { | 
 | 	case VND_CMD_AUTH_STATE_NEEDED: | 
 | 	case VND_CMD_AUTH_STATE_SESSION_SHUTDOWN: | 
 | 		edbnode->u.plogi_did.b24 = fcport->d_id.b24; | 
 | 		break; | 
 | 	case VND_CMD_AUTH_STATE_ELS_RCVD: | 
 | 		edbnode->u.els_sid.b24 = fcport->d_id.b24; | 
 | 		break; | 
 | 	case VND_CMD_AUTH_STATE_SAUPDATE_COMPL: | 
 | 		edbnode->u.sa_aen.port_id = fcport->d_id; | 
 | 		edbnode->u.sa_aen.status =  data; | 
 | 		edbnode->u.sa_aen.key_type =  data2; | 
 | 		edbnode->u.sa_aen.version = EDIF_VERSION1; | 
 | 		break; | 
 | 	default: | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x09102, | 
 | 			"%s unknown type: %x\n", __func__, dbtype); | 
 | 		kfree(edbnode); | 
 | 		edbnode = NULL; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	if (edbnode) { | 
 | 		if (!qla_edb_node_add(vha, edbnode)) { | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x09102, | 
 | 			    "%s unable to add dbnode\n", __func__); | 
 | 			kfree(edbnode); | 
 | 			return; | 
 | 		} | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x09102, | 
 | 		    "%s Doorbell produced : type=%d %p\n", __func__, dbtype, edbnode); | 
 | 		qla_edif_dbell_bsg_done(vha); | 
 | 		if (fcport) | 
 | 			fcport->edif.auth_state = dbtype; | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | qla_edif_timer(scsi_qla_host_t *vha) | 
 | { | 
 | 	struct qla_hw_data *ha = vha->hw; | 
 |  | 
 | 	if (!vha->vp_idx && N2N_TOPO(ha) && ha->flags.n2n_fw_acc_sec) { | 
 | 		if (DBELL_INACTIVE(vha) && | 
 | 		    ha->edif_post_stop_cnt_down) { | 
 | 			ha->edif_post_stop_cnt_down--; | 
 |  | 
 | 			/* | 
 | 			 * turn off auto 'Plogi Acc + secure=1' feature | 
 | 			 * Set Add FW option[3] | 
 | 			 * BIT_15, if. | 
 | 			 */ | 
 | 			if (ha->edif_post_stop_cnt_down == 0) { | 
 | 				ql_dbg(ql_dbg_async, vha, 0x911d, | 
 | 				       "%s chip reset to turn off PLOGI ACC + secure\n", | 
 | 				       __func__); | 
 | 				set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); | 
 | 			} | 
 | 		} else { | 
 | 			ha->edif_post_stop_cnt_down = 60; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (vha->e_dbell.dbell_bsg_job && time_after_eq(jiffies, vha->e_dbell.bsg_expire)) | 
 | 		qla_edif_dbell_bsg_done(vha); | 
 | } | 
 |  | 
 | static void qla_noop_sp_done(srb_t *sp, int res) | 
 | { | 
 | 	sp->fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); | 
 | 	/* ref: INIT */ | 
 | 	kref_put(&sp->cmd_kref, qla2x00_sp_release); | 
 | } | 
 |  | 
 | /* | 
 |  * Called from work queue | 
 |  * build and send the sa_update iocb to delete an rx sa_index | 
 |  */ | 
 | int | 
 | qla24xx_issue_sa_replace_iocb(scsi_qla_host_t *vha, struct qla_work_evt *e) | 
 | { | 
 | 	srb_t *sp; | 
 | 	fc_port_t	*fcport = NULL; | 
 | 	struct srb_iocb *iocb_cmd = NULL; | 
 | 	int rval = QLA_SUCCESS; | 
 | 	struct	edif_sa_ctl *sa_ctl = e->u.sa_update.sa_ctl; | 
 | 	uint16_t nport_handle = e->u.sa_update.nport_handle; | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x70e6, | 
 | 	    "%s: starting,  sa_ctl: %p\n", __func__, sa_ctl); | 
 |  | 
 | 	if (!sa_ctl) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x70e6, | 
 | 		    "sa_ctl allocation failed\n"); | 
 | 		rval = -ENOMEM; | 
 | 		return rval; | 
 | 	} | 
 |  | 
 | 	fcport = sa_ctl->fcport; | 
 |  | 
 | 	/* Alloc SRB structure */ | 
 | 	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); | 
 | 	if (!sp) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x70e6, | 
 | 		 "SRB allocation failed\n"); | 
 | 		rval = -ENOMEM; | 
 | 		goto done; | 
 | 	} | 
 |  | 
 | 	fcport->flags |= FCF_ASYNC_SENT; | 
 | 	iocb_cmd = &sp->u.iocb_cmd; | 
 | 	iocb_cmd->u.sa_update.sa_ctl = sa_ctl; | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x3073, | 
 | 	    "Enter: SA REPL portid=%06x, sa_ctl %p, index %x, nport_handle: 0x%x\n", | 
 | 	    fcport->d_id.b24, sa_ctl, sa_ctl->index, nport_handle); | 
 | 	/* | 
 | 	 * if this is a sadb cleanup delete, mark it so the isr can | 
 | 	 * take the correct action | 
 | 	 */ | 
 | 	if (sa_ctl->flags & EDIF_SA_CTL_FLG_CLEANUP_DEL) { | 
 | 		/* mark this srb as a cleanup delete */ | 
 | 		sp->flags |= SRB_EDIF_CLEANUP_DELETE; | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x70e6, | 
 | 		    "%s: sp 0x%p flagged as cleanup delete\n", __func__, sp); | 
 | 	} | 
 |  | 
 | 	sp->type = SRB_SA_REPLACE; | 
 | 	sp->name = "SA_REPLACE"; | 
 | 	sp->fcport = fcport; | 
 | 	sp->free = qla2x00_rel_sp; | 
 | 	sp->done = qla_noop_sp_done; | 
 |  | 
 | 	rval = qla2x00_start_sp(sp); | 
 |  | 
 | 	if (rval != QLA_SUCCESS) { | 
 | 		goto done_free_sp; | 
 | 	} | 
 |  | 
 | 	return rval; | 
 | done_free_sp: | 
 | 	kref_put(&sp->cmd_kref, qla2x00_sp_release); | 
 | 	fcport->flags &= ~FCF_ASYNC_SENT; | 
 | done: | 
 | 	fcport->flags &= ~FCF_ASYNC_ACTIVE; | 
 | 	return rval; | 
 | } | 
 |  | 
 | void qla24xx_sa_update_iocb(srb_t *sp, struct sa_update_28xx *sa_update_iocb) | 
 | { | 
 | 	int	itr = 0; | 
 | 	struct	scsi_qla_host		*vha = sp->vha; | 
 | 	struct	qla_sa_update_frame	*sa_frame = | 
 | 		&sp->u.iocb_cmd.u.sa_update.sa_frame; | 
 | 	u8 flags = 0; | 
 |  | 
 | 	switch (sa_frame->flags & (SAU_FLG_INV | SAU_FLG_TX)) { | 
 | 	case 0: | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 		    "%s: EDIF SA UPDATE RX IOCB  vha: 0x%p  index: %d\n", | 
 | 		    __func__, vha, sa_frame->fast_sa_index); | 
 | 		break; | 
 | 	case 1: | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 		    "%s: EDIF SA DELETE RX IOCB  vha: 0x%p  index: %d\n", | 
 | 		    __func__, vha, sa_frame->fast_sa_index); | 
 | 		flags |= SA_FLAG_INVALIDATE; | 
 | 		break; | 
 | 	case 2: | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 		    "%s: EDIF SA UPDATE TX IOCB  vha: 0x%p  index: %d\n", | 
 | 		    __func__, vha, sa_frame->fast_sa_index); | 
 | 		flags |= SA_FLAG_TX; | 
 | 		break; | 
 | 	case 3: | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911d, | 
 | 		    "%s: EDIF SA DELETE TX IOCB  vha: 0x%p  index: %d\n", | 
 | 		    __func__, vha, sa_frame->fast_sa_index); | 
 | 		flags |= SA_FLAG_TX | SA_FLAG_INVALIDATE; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	sa_update_iocb->entry_type = SA_UPDATE_IOCB_TYPE; | 
 | 	sa_update_iocb->entry_count = 1; | 
 | 	sa_update_iocb->sys_define = 0; | 
 | 	sa_update_iocb->entry_status = 0; | 
 | 	sa_update_iocb->handle = sp->handle; | 
 | 	sa_update_iocb->u.nport_handle = cpu_to_le16(sp->fcport->loop_id); | 
 | 	sa_update_iocb->vp_index = sp->fcport->vha->vp_idx; | 
 | 	sa_update_iocb->port_id[0] = sp->fcport->d_id.b.al_pa; | 
 | 	sa_update_iocb->port_id[1] = sp->fcport->d_id.b.area; | 
 | 	sa_update_iocb->port_id[2] = sp->fcport->d_id.b.domain; | 
 |  | 
 | 	sa_update_iocb->flags = flags; | 
 | 	sa_update_iocb->salt = cpu_to_le32(sa_frame->salt); | 
 | 	sa_update_iocb->spi = cpu_to_le32(sa_frame->spi); | 
 | 	sa_update_iocb->sa_index = cpu_to_le16(sa_frame->fast_sa_index); | 
 |  | 
 | 	sa_update_iocb->sa_control |= SA_CNTL_ENC_FCSP; | 
 | 	if (sp->fcport->edif.aes_gmac) | 
 | 		sa_update_iocb->sa_control |= SA_CNTL_AES_GMAC; | 
 |  | 
 | 	if (sa_frame->flags & SAU_FLG_KEY256) { | 
 | 		sa_update_iocb->sa_control |= SA_CNTL_KEY256; | 
 | 		for (itr = 0; itr < 32; itr++) | 
 | 			sa_update_iocb->sa_key[itr] = sa_frame->sa_key[itr]; | 
 | 	} else { | 
 | 		sa_update_iocb->sa_control |= SA_CNTL_KEY128; | 
 | 		for (itr = 0; itr < 16; itr++) | 
 | 			sa_update_iocb->sa_key[itr] = sa_frame->sa_key[itr]; | 
 | 	} | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x921d, | 
 | 	    "%s SAU Port ID = %02x%02x%02x, flags=%xh, index=%u, ctl=%xh, SPI 0x%x flags 0x%x hdl=%x gmac %d\n", | 
 | 	    __func__, sa_update_iocb->port_id[2], sa_update_iocb->port_id[1], | 
 | 	    sa_update_iocb->port_id[0], sa_update_iocb->flags, sa_update_iocb->sa_index, | 
 | 	    sa_update_iocb->sa_control, sa_update_iocb->spi, sa_frame->flags, sp->handle, | 
 | 	    sp->fcport->edif.aes_gmac); | 
 |  | 
 | 	if (sa_frame->flags & SAU_FLG_TX) | 
 | 		sp->fcport->edif.tx_sa_pending = 1; | 
 | 	else | 
 | 		sp->fcport->edif.rx_sa_pending = 1; | 
 |  | 
 | 	sp->fcport->vha->qla_stats.control_requests++; | 
 | } | 
 |  | 
 | void | 
 | qla24xx_sa_replace_iocb(srb_t *sp, struct sa_update_28xx *sa_update_iocb) | 
 | { | 
 | 	struct	scsi_qla_host		*vha = sp->vha; | 
 | 	struct srb_iocb *srb_iocb = &sp->u.iocb_cmd; | 
 | 	struct	edif_sa_ctl		*sa_ctl = srb_iocb->u.sa_update.sa_ctl; | 
 | 	uint16_t nport_handle = sp->fcport->loop_id; | 
 |  | 
 | 	sa_update_iocb->entry_type = SA_UPDATE_IOCB_TYPE; | 
 | 	sa_update_iocb->entry_count = 1; | 
 | 	sa_update_iocb->sys_define = 0; | 
 | 	sa_update_iocb->entry_status = 0; | 
 | 	sa_update_iocb->handle = sp->handle; | 
 |  | 
 | 	sa_update_iocb->u.nport_handle = cpu_to_le16(nport_handle); | 
 |  | 
 | 	sa_update_iocb->vp_index = sp->fcport->vha->vp_idx; | 
 | 	sa_update_iocb->port_id[0] = sp->fcport->d_id.b.al_pa; | 
 | 	sa_update_iocb->port_id[1] = sp->fcport->d_id.b.area; | 
 | 	sa_update_iocb->port_id[2] = sp->fcport->d_id.b.domain; | 
 |  | 
 | 	/* Invalidate the index. salt, spi, control & key are ignore */ | 
 | 	sa_update_iocb->flags = SA_FLAG_INVALIDATE; | 
 | 	sa_update_iocb->salt = 0; | 
 | 	sa_update_iocb->spi = 0; | 
 | 	sa_update_iocb->sa_index = cpu_to_le16(sa_ctl->index); | 
 | 	sa_update_iocb->sa_control = 0; | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x921d, | 
 | 	    "%s SAU DELETE RX Port ID = %02x:%02x:%02x, lid %d flags=%xh, index=%u, hdl=%x\n", | 
 | 	    __func__, sa_update_iocb->port_id[2], sa_update_iocb->port_id[1], | 
 | 	    sa_update_iocb->port_id[0], nport_handle, sa_update_iocb->flags, | 
 | 	    sa_update_iocb->sa_index, sp->handle); | 
 |  | 
 | 	sp->fcport->vha->qla_stats.control_requests++; | 
 | } | 
 |  | 
 | void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp) | 
 | { | 
 | 	struct purex_entry_24xx *p = *pkt; | 
 | 	struct enode		*ptr; | 
 | 	int		sid; | 
 | 	u16 totlen; | 
 | 	struct purexevent	*purex; | 
 | 	struct scsi_qla_host *host = NULL; | 
 | 	int rc; | 
 | 	struct fc_port *fcport; | 
 | 	struct qla_els_pt_arg a; | 
 | 	be_id_t beid; | 
 |  | 
 | 	memset(&a, 0, sizeof(a)); | 
 |  | 
 | 	a.els_opcode = ELS_AUTH_ELS; | 
 | 	a.nport_handle = p->nport_handle; | 
 | 	a.rx_xchg_address = p->rx_xchg_addr; | 
 | 	a.did.b.domain = p->s_id[2]; | 
 | 	a.did.b.area   = p->s_id[1]; | 
 | 	a.did.b.al_pa  = p->s_id[0]; | 
 | 	a.tx_byte_count = a.tx_len = sizeof(struct fc_els_ls_rjt); | 
 | 	a.tx_addr = vha->hw->elsrej.cdma; | 
 | 	a.vp_idx = vha->vp_idx; | 
 | 	a.control_flags = EPD_ELS_RJT; | 
 | 	a.ox_id = le16_to_cpu(p->ox_id); | 
 |  | 
 | 	sid = p->s_id[0] | (p->s_id[1] << 8) | (p->s_id[2] << 16); | 
 |  | 
 | 	totlen = (le16_to_cpu(p->frame_size) & 0x0fff) - PURX_ELS_HEADER_SIZE; | 
 | 	if (le16_to_cpu(p->status_flags) & 0x8000) { | 
 | 		totlen = le16_to_cpu(p->trunc_frame_size); | 
 | 		qla_els_reject_iocb(vha, (*rsp)->qpair, &a); | 
 | 		__qla_consume_iocb(vha, pkt, rsp); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	if (totlen > ELS_MAX_PAYLOAD) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x0910d, | 
 | 		    "%s WARNING: verbose ELS frame received (totlen=%x)\n", | 
 | 		    __func__, totlen); | 
 | 		qla_els_reject_iocb(vha, (*rsp)->qpair, &a); | 
 | 		__qla_consume_iocb(vha, pkt, rsp); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	if (!vha->hw->flags.edif_enabled) { | 
 | 		/* edif support not enabled */ | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x910e, "%s edif not enabled\n", | 
 | 		    __func__); | 
 | 		qla_els_reject_iocb(vha, (*rsp)->qpair, &a); | 
 | 		__qla_consume_iocb(vha, pkt, rsp); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	ptr = qla_enode_alloc(vha, N_PUREX); | 
 | 	if (!ptr) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x09109, | 
 | 		    "WARNING: enode alloc failed for sid=%x\n", | 
 | 		    sid); | 
 | 		qla_els_reject_iocb(vha, (*rsp)->qpair, &a); | 
 | 		__qla_consume_iocb(vha, pkt, rsp); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	purex = &ptr->u.purexinfo; | 
 | 	purex->pur_info.pur_sid = a.did; | 
 | 	purex->pur_info.pur_bytes_rcvd = totlen; | 
 | 	purex->pur_info.pur_rx_xchg_address = le32_to_cpu(p->rx_xchg_addr); | 
 | 	purex->pur_info.pur_nphdl = le16_to_cpu(p->nport_handle); | 
 | 	purex->pur_info.pur_did.b.domain =  p->d_id[2]; | 
 | 	purex->pur_info.pur_did.b.area =  p->d_id[1]; | 
 | 	purex->pur_info.pur_did.b.al_pa =  p->d_id[0]; | 
 | 	purex->pur_info.vp_idx = p->vp_idx; | 
 |  | 
 | 	a.sid = purex->pur_info.pur_did; | 
 |  | 
 | 	rc = __qla_copy_purex_to_buffer(vha, pkt, rsp, purex->msgp, | 
 | 		purex->msgp_len); | 
 | 	if (rc) { | 
 | 		qla_els_reject_iocb(vha, (*rsp)->qpair, &a); | 
 | 		qla_enode_free(vha, ptr); | 
 | 		return; | 
 | 	} | 
 | 	beid.al_pa = purex->pur_info.pur_did.b.al_pa; | 
 | 	beid.area   = purex->pur_info.pur_did.b.area; | 
 | 	beid.domain = purex->pur_info.pur_did.b.domain; | 
 | 	host = qla_find_host_by_d_id(vha, beid); | 
 | 	if (!host) { | 
 | 		ql_log(ql_log_fatal, vha, 0x508b, | 
 | 		    "%s Drop ELS due to unable to find host %06x\n", | 
 | 		    __func__, purex->pur_info.pur_did.b24); | 
 |  | 
 | 		qla_els_reject_iocb(vha, (*rsp)->qpair, &a); | 
 | 		qla_enode_free(vha, ptr); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	fcport = qla2x00_find_fcport_by_pid(host, &purex->pur_info.pur_sid); | 
 |  | 
 | 	if (DBELL_INACTIVE(vha)) { | 
 | 		ql_dbg(ql_dbg_edif, host, 0x0910c, "%s e_dbell.db_flags =%x %06x\n", | 
 | 		    __func__, host->e_dbell.db_flags, | 
 | 		    fcport ? fcport->d_id.b24 : 0); | 
 |  | 
 | 		qla_els_reject_iocb(host, (*rsp)->qpair, &a); | 
 | 		qla_enode_free(host, ptr); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	if (fcport && EDIF_SESSION_DOWN(fcport)) { | 
 | 		ql_dbg(ql_dbg_edif, host, 0x13b6, | 
 | 		    "%s terminate exchange. Send logo to 0x%x\n", | 
 | 		    __func__, a.did.b24); | 
 |  | 
 | 		a.tx_byte_count = a.tx_len = 0; | 
 | 		a.tx_addr = 0; | 
 | 		a.control_flags = EPD_RX_XCHG;  /* EPD_RX_XCHG = terminate cmd */ | 
 | 		qla_els_reject_iocb(host, (*rsp)->qpair, &a); | 
 | 		qla_enode_free(host, ptr); | 
 | 		/* send logo to let remote port knows to tear down session */ | 
 | 		fcport->send_els_logo = 1; | 
 | 		qlt_schedule_sess_for_deletion(fcport); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* add the local enode to the list */ | 
 | 	qla_enode_add(host, ptr); | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, host, 0x0910c, | 
 | 	    "%s COMPLETE purex->pur_info.pur_bytes_rcvd =%xh s:%06x -> d:%06x xchg=%xh\n", | 
 | 	    __func__, purex->pur_info.pur_bytes_rcvd, purex->pur_info.pur_sid.b24, | 
 | 	    purex->pur_info.pur_did.b24, purex->pur_info.pur_rx_xchg_address); | 
 |  | 
 | 	qla_edb_eventcreate(host, VND_CMD_AUTH_STATE_ELS_RCVD, sid, 0, NULL); | 
 | } | 
 |  | 
 | static uint16_t  qla_edif_get_sa_index_from_freepool(fc_port_t *fcport, int dir) | 
 | { | 
 | 	struct scsi_qla_host *vha = fcport->vha; | 
 | 	struct qla_hw_data *ha = vha->hw; | 
 | 	void *sa_id_map; | 
 | 	unsigned long flags = 0; | 
 | 	u16 sa_index; | 
 |  | 
 | 	ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x3063, | 
 | 	    "%s: entry\n", __func__); | 
 |  | 
 | 	if (dir) | 
 | 		sa_id_map = ha->edif_tx_sa_id_map; | 
 | 	else | 
 | 		sa_id_map = ha->edif_rx_sa_id_map; | 
 |  | 
 | 	spin_lock_irqsave(&ha->sadb_fp_lock, flags); | 
 | 	sa_index = find_first_zero_bit(sa_id_map, EDIF_NUM_SA_INDEX); | 
 | 	if (sa_index >=  EDIF_NUM_SA_INDEX) { | 
 | 		spin_unlock_irqrestore(&ha->sadb_fp_lock, flags); | 
 | 		return INVALID_EDIF_SA_INDEX; | 
 | 	} | 
 | 	set_bit(sa_index, sa_id_map); | 
 | 	spin_unlock_irqrestore(&ha->sadb_fp_lock, flags); | 
 |  | 
 | 	if (dir) | 
 | 		sa_index += EDIF_TX_SA_INDEX_BASE; | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 	    "%s: index retrieved from free pool %d\n", __func__, sa_index); | 
 |  | 
 | 	return sa_index; | 
 | } | 
 |  | 
 | /* find an sadb entry for an nport_handle */ | 
 | static struct edif_sa_index_entry * | 
 | qla_edif_sadb_find_sa_index_entry(uint16_t nport_handle, | 
 | 		struct list_head *sa_list) | 
 | { | 
 | 	struct edif_sa_index_entry *entry; | 
 | 	struct edif_sa_index_entry *tentry; | 
 | 	struct list_head *indx_list = sa_list; | 
 |  | 
 | 	list_for_each_entry_safe(entry, tentry, indx_list, next) { | 
 | 		if (entry->handle == nport_handle) | 
 | 			return entry; | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /* remove an sa_index from the nport_handle and return it to the free pool */ | 
 | static int qla_edif_sadb_delete_sa_index(fc_port_t *fcport, uint16_t nport_handle, | 
 | 		uint16_t sa_index) | 
 | { | 
 | 	struct edif_sa_index_entry *entry; | 
 | 	struct list_head *sa_list; | 
 | 	int dir = (sa_index < EDIF_TX_SA_INDEX_BASE) ? 0 : 1; | 
 | 	int slot = 0; | 
 | 	int free_slot_count = 0; | 
 | 	scsi_qla_host_t *vha = fcport->vha; | 
 | 	struct qla_hw_data *ha = vha->hw; | 
 | 	unsigned long flags = 0; | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 	    "%s: entry\n", __func__); | 
 |  | 
 | 	if (dir) | 
 | 		sa_list = &ha->sadb_tx_index_list; | 
 | 	else | 
 | 		sa_list = &ha->sadb_rx_index_list; | 
 |  | 
 | 	entry = qla_edif_sadb_find_sa_index_entry(nport_handle, sa_list); | 
 | 	if (!entry) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 		    "%s: no entry found for nport_handle 0x%x\n", | 
 | 		    __func__, nport_handle); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	spin_lock_irqsave(&ha->sadb_lock, flags); | 
 | 	/* | 
 | 	 * each tx/rx direction has up to 2 sa indexes/slots. 1 slot for in flight traffic | 
 | 	 * the other is use at re-key time. | 
 | 	 */ | 
 | 	for (slot = 0; slot < 2; slot++) { | 
 | 		if (entry->sa_pair[slot].sa_index == sa_index) { | 
 | 			entry->sa_pair[slot].sa_index = INVALID_EDIF_SA_INDEX; | 
 | 			entry->sa_pair[slot].spi = 0; | 
 | 			free_slot_count++; | 
 | 			qla_edif_add_sa_index_to_freepool(fcport, dir, sa_index); | 
 | 		} else if (entry->sa_pair[slot].sa_index == INVALID_EDIF_SA_INDEX) { | 
 | 			free_slot_count++; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (free_slot_count == 2) { | 
 | 		list_del(&entry->next); | 
 | 		kfree(entry); | 
 | 	} | 
 | 	spin_unlock_irqrestore(&ha->sadb_lock, flags); | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 	    "%s: sa_index %d removed, free_slot_count: %d\n", | 
 | 	    __func__, sa_index, free_slot_count); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | void | 
 | qla28xx_sa_update_iocb_entry(scsi_qla_host_t *v, struct req_que *req, | 
 | 	struct sa_update_28xx *pkt) | 
 | { | 
 | 	const char *func = "SA_UPDATE_RESPONSE_IOCB"; | 
 | 	srb_t *sp; | 
 | 	struct edif_sa_ctl *sa_ctl; | 
 | 	int old_sa_deleted = 1; | 
 | 	uint16_t nport_handle; | 
 | 	struct scsi_qla_host *vha; | 
 |  | 
 | 	sp = qla2x00_get_sp_from_handle(v, func, req, pkt); | 
 |  | 
 | 	if (!sp) { | 
 | 		ql_dbg(ql_dbg_edif, v, 0x3063, | 
 | 			"%s: no sp found for pkt\n", __func__); | 
 | 		return; | 
 | 	} | 
 | 	/* use sp->vha due to npiv */ | 
 | 	vha = sp->vha; | 
 |  | 
 | 	switch (pkt->flags & (SA_FLAG_INVALIDATE | SA_FLAG_TX)) { | 
 | 	case 0: | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 		    "%s: EDIF SA UPDATE RX IOCB  vha: 0x%p  index: %d\n", | 
 | 		    __func__, vha, pkt->sa_index); | 
 | 		break; | 
 | 	case 1: | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 		    "%s: EDIF SA DELETE RX IOCB  vha: 0x%p  index: %d\n", | 
 | 		    __func__, vha, pkt->sa_index); | 
 | 		break; | 
 | 	case 2: | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 		    "%s: EDIF SA UPDATE TX IOCB  vha: 0x%p  index: %d\n", | 
 | 		    __func__, vha, pkt->sa_index); | 
 | 		break; | 
 | 	case 3: | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 		    "%s: EDIF SA DELETE TX IOCB  vha: 0x%p  index: %d\n", | 
 | 		    __func__, vha, pkt->sa_index); | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * dig the nport handle out of the iocb, fcport->loop_id can not be trusted | 
 | 	 * to be correct during cleanup sa_update iocbs. | 
 | 	 */ | 
 | 	nport_handle = sp->fcport->loop_id; | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 	    "%s: %8phN comp status=%x old_sa_info=%x new_sa_info=%x lid %d, index=0x%x pkt_flags %xh hdl=%x\n", | 
 | 	    __func__, sp->fcport->port_name, pkt->u.comp_sts, pkt->old_sa_info, pkt->new_sa_info, | 
 | 	    nport_handle, pkt->sa_index, pkt->flags, sp->handle); | 
 |  | 
 | 	/* if rx delete, remove the timer */ | 
 | 	if ((pkt->flags & (SA_FLAG_INVALIDATE | SA_FLAG_TX)) ==  SA_FLAG_INVALIDATE) { | 
 | 		struct edif_list_entry *edif_entry; | 
 |  | 
 | 		sp->fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); | 
 |  | 
 | 		edif_entry = qla_edif_list_find_sa_index(sp->fcport, nport_handle); | 
 | 		if (edif_entry) { | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x5033, | 
 | 			    "%s: removing edif_entry %p, new sa_index: 0x%x\n", | 
 | 			    __func__, edif_entry, pkt->sa_index); | 
 | 			qla_edif_list_delete_sa_index(sp->fcport, edif_entry); | 
 | 			timer_shutdown(&edif_entry->timer); | 
 |  | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x5033, | 
 | 			    "%s: releasing edif_entry %p, new sa_index: 0x%x\n", | 
 | 			    __func__, edif_entry, pkt->sa_index); | 
 |  | 
 | 			kfree(edif_entry); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * if this is a delete for either tx or rx, make sure it succeeded. | 
 | 	 * The new_sa_info field should be 0xffff on success | 
 | 	 */ | 
 | 	if (pkt->flags & SA_FLAG_INVALIDATE) | 
 | 		old_sa_deleted = (le16_to_cpu(pkt->new_sa_info) == 0xffff) ? 1 : 0; | 
 |  | 
 | 	/* Process update and delete the same way */ | 
 |  | 
 | 	/* If this is an sadb cleanup delete, bypass sending events to IPSEC */ | 
 | 	if (sp->flags & SRB_EDIF_CLEANUP_DELETE) { | 
 | 		sp->fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 		    "%s: nph 0x%x, sa_index %d removed from fw\n", | 
 | 		    __func__, sp->fcport->loop_id, pkt->sa_index); | 
 |  | 
 | 	} else if ((pkt->entry_status == 0) && (pkt->u.comp_sts == 0) && | 
 | 	    old_sa_deleted) { | 
 | 		/* | 
 | 		 * Note: Wa are only keeping track of latest SA, | 
 | 		 * so we know when we can start enableing encryption per I/O. | 
 | 		 * If all SA's get deleted, let FW reject the IOCB. | 
 |  | 
 | 		 * TODO: edif: don't set enabled here I think | 
 | 		 * TODO: edif: prli complete is where it should be set | 
 | 		 */ | 
 | 		ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x3063, | 
 | 			"SA(%x)updated for s_id %02x%02x%02x\n", | 
 | 			pkt->new_sa_info, | 
 | 			pkt->port_id[2], pkt->port_id[1], pkt->port_id[0]); | 
 | 		sp->fcport->edif.enable = 1; | 
 | 		if (pkt->flags & SA_FLAG_TX) { | 
 | 			sp->fcport->edif.tx_sa_set = 1; | 
 | 			sp->fcport->edif.tx_sa_pending = 0; | 
 | 			qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_SAUPDATE_COMPL, | 
 | 				QL_VND_SA_STAT_SUCCESS, | 
 | 				QL_VND_TX_SA_KEY, sp->fcport); | 
 | 		} else { | 
 | 			sp->fcport->edif.rx_sa_set = 1; | 
 | 			sp->fcport->edif.rx_sa_pending = 0; | 
 | 			qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_SAUPDATE_COMPL, | 
 | 				QL_VND_SA_STAT_SUCCESS, | 
 | 				QL_VND_RX_SA_KEY, sp->fcport); | 
 | 		} | 
 | 	} else { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 		    "%s: %8phN SA update FAILED: sa_index: %d, new_sa_info %d, %02x%02x%02x\n", | 
 | 		    __func__, sp->fcport->port_name, pkt->sa_index, pkt->new_sa_info, | 
 | 		    pkt->port_id[2], pkt->port_id[1], pkt->port_id[0]); | 
 |  | 
 | 		if (pkt->flags & SA_FLAG_TX) | 
 | 			qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_SAUPDATE_COMPL, | 
 | 				(le16_to_cpu(pkt->u.comp_sts) << 16) | QL_VND_SA_STAT_FAILED, | 
 | 				QL_VND_TX_SA_KEY, sp->fcport); | 
 | 		else | 
 | 			qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_SAUPDATE_COMPL, | 
 | 				(le16_to_cpu(pkt->u.comp_sts) << 16) | QL_VND_SA_STAT_FAILED, | 
 | 				QL_VND_RX_SA_KEY, sp->fcport); | 
 | 	} | 
 |  | 
 | 	/* for delete, release sa_ctl, sa_index */ | 
 | 	if (pkt->flags & SA_FLAG_INVALIDATE) { | 
 | 		/* release the sa_ctl */ | 
 | 		sa_ctl = qla_edif_find_sa_ctl_by_index(sp->fcport, | 
 | 		    le16_to_cpu(pkt->sa_index), (pkt->flags & SA_FLAG_TX)); | 
 | 		if (sa_ctl && | 
 | 		    qla_edif_find_sa_ctl_by_index(sp->fcport, sa_ctl->index, | 
 | 			(pkt->flags & SA_FLAG_TX)) != NULL) { | 
 | 			ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x3063, | 
 | 			    "%s: freeing sa_ctl for index %d\n", | 
 | 			    __func__, sa_ctl->index); | 
 | 			qla_edif_free_sa_ctl(sp->fcport, sa_ctl, sa_ctl->index); | 
 | 		} else { | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 			    "%s: sa_ctl NOT freed, sa_ctl: %p\n", | 
 | 			    __func__, sa_ctl); | 
 | 		} | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 		    "%s: freeing sa_index %d, nph: 0x%x\n", | 
 | 		    __func__, le16_to_cpu(pkt->sa_index), nport_handle); | 
 | 		qla_edif_sadb_delete_sa_index(sp->fcport, nport_handle, | 
 | 		    le16_to_cpu(pkt->sa_index)); | 
 | 	/* | 
 | 	 * check for a failed sa_update and remove | 
 | 	 * the sadb entry. | 
 | 	 */ | 
 | 	} else if (pkt->u.comp_sts) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 		    "%s: freeing sa_index %d, nph: 0x%x\n", | 
 | 		    __func__, pkt->sa_index, nport_handle); | 
 | 		qla_edif_sadb_delete_sa_index(sp->fcport, nport_handle, | 
 | 		    le16_to_cpu(pkt->sa_index)); | 
 | 		switch (le16_to_cpu(pkt->u.comp_sts)) { | 
 | 		case CS_PORT_EDIF_UNAVAIL: | 
 | 		case CS_PORT_EDIF_LOGOUT: | 
 | 			qlt_schedule_sess_for_deletion(sp->fcport); | 
 | 			break; | 
 | 		default: | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	sp->done(sp, 0); | 
 | } | 
 |  | 
 | /** | 
 |  * qla28xx_start_scsi_edif() - Send a SCSI type 6 command to the ISP | 
 |  * @sp: command to send to the ISP | 
 |  * | 
 |  * Return: non-zero if a failure occurred, else zero. | 
 |  */ | 
 | int | 
 | qla28xx_start_scsi_edif(srb_t *sp) | 
 | { | 
 | 	int             nseg; | 
 | 	unsigned long   flags; | 
 | 	struct scsi_cmnd *cmd; | 
 | 	uint32_t        *clr_ptr; | 
 | 	uint32_t        index, i; | 
 | 	uint32_t        handle; | 
 | 	uint16_t        cnt; | 
 | 	int16_t        req_cnt; | 
 | 	uint16_t        tot_dsds; | 
 | 	__be32 *fcp_dl; | 
 | 	uint8_t additional_cdb_len; | 
 | 	struct ct6_dsd *ctx; | 
 | 	struct scsi_qla_host *vha = sp->vha; | 
 | 	struct qla_hw_data *ha = vha->hw; | 
 | 	struct cmd_type_6 *cmd_pkt; | 
 | 	struct dsd64	*cur_dsd; | 
 | 	uint8_t		avail_dsds = 0; | 
 | 	struct scatterlist *sg; | 
 | 	struct req_que *req = sp->qpair->req; | 
 | 	spinlock_t *lock = sp->qpair->qp_lock_ptr; | 
 |  | 
 | 	/* Setup device pointers. */ | 
 | 	cmd = GET_CMD_SP(sp); | 
 |  | 
 | 	/* So we know we haven't pci_map'ed anything yet */ | 
 | 	tot_dsds = 0; | 
 |  | 
 | 	/* Send marker if required */ | 
 | 	if (vha->marker_needed != 0) { | 
 | 		if (qla2x00_marker(vha, sp->qpair, 0, 0, MK_SYNC_ALL) != | 
 | 			QLA_SUCCESS) { | 
 | 			ql_log(ql_log_warn, vha, 0x300c, | 
 | 			    "qla2x00_marker failed for cmd=%p.\n", cmd); | 
 | 			return QLA_FUNCTION_FAILED; | 
 | 		} | 
 | 		vha->marker_needed = 0; | 
 | 	} | 
 |  | 
 | 	/* Acquire ring specific lock */ | 
 | 	spin_lock_irqsave(lock, flags); | 
 |  | 
 | 	/* Check for room in outstanding command list. */ | 
 | 	handle = req->current_outstanding_cmd; | 
 | 	for (index = 1; index < req->num_outstanding_cmds; index++) { | 
 | 		handle++; | 
 | 		if (handle == req->num_outstanding_cmds) | 
 | 			handle = 1; | 
 | 		if (!req->outstanding_cmds[handle]) | 
 | 			break; | 
 | 	} | 
 | 	if (index == req->num_outstanding_cmds) | 
 | 		goto queuing_error; | 
 |  | 
 | 	/* Map the sg table so we have an accurate count of sg entries needed */ | 
 | 	if (scsi_sg_count(cmd)) { | 
 | 		nseg = dma_map_sg(&ha->pdev->dev, scsi_sglist(cmd), | 
 | 		    scsi_sg_count(cmd), cmd->sc_data_direction); | 
 | 		if (unlikely(!nseg)) | 
 | 			goto queuing_error; | 
 | 	} else { | 
 | 		nseg = 0; | 
 | 	} | 
 |  | 
 | 	tot_dsds = nseg; | 
 | 	req_cnt = qla24xx_calc_iocbs(vha, tot_dsds); | 
 |  | 
 | 	sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH; | 
 | 	sp->iores.exch_cnt = 1; | 
 | 	sp->iores.iocb_cnt = req_cnt; | 
 | 	if (qla_get_fw_resources(sp->qpair, &sp->iores)) | 
 | 		goto queuing_error; | 
 |  | 
 | 	if (req->cnt < (req_cnt + 2)) { | 
 | 		cnt = IS_SHADOW_REG_CAPABLE(ha) ? *req->out_ptr : | 
 | 		    rd_reg_dword(req->req_q_out); | 
 | 		if (req->ring_index < cnt) | 
 | 			req->cnt = cnt - req->ring_index; | 
 | 		else | 
 | 			req->cnt = req->length - | 
 | 			    (req->ring_index - cnt); | 
 | 		if (req->cnt < (req_cnt + 2)) | 
 | 			goto queuing_error; | 
 | 	} | 
 |  | 
 | 	if (qla_get_buf(vha, sp->qpair, &sp->u.scmd.buf_dsc)) { | 
 | 		ql_log(ql_log_fatal, vha, 0x3011, | 
 | 		    "Failed to allocate buf for fcp_cmnd for cmd=%p.\n", cmd); | 
 | 		goto queuing_error; | 
 | 	} | 
 |  | 
 | 	sp->flags |= SRB_GOT_BUF; | 
 | 	ctx = &sp->u.scmd.ct6_ctx; | 
 | 	ctx->fcp_cmnd = sp->u.scmd.buf_dsc.buf; | 
 | 	ctx->fcp_cmnd_dma = sp->u.scmd.buf_dsc.buf_dma; | 
 |  | 
 | 	if (cmd->cmd_len > 16) { | 
 | 		additional_cdb_len = cmd->cmd_len - 16; | 
 | 		if ((cmd->cmd_len % 4) != 0) { | 
 | 			/* | 
 | 			 * SCSI command bigger than 16 bytes must be | 
 | 			 * multiple of 4 | 
 | 			 */ | 
 | 			ql_log(ql_log_warn, vha, 0x3012, | 
 | 			    "scsi cmd len %d not multiple of 4 for cmd=%p.\n", | 
 | 			    cmd->cmd_len, cmd); | 
 | 			goto queuing_error_fcp_cmnd; | 
 | 		} | 
 | 		ctx->fcp_cmnd_len = 12 + cmd->cmd_len + 4; | 
 | 	} else { | 
 | 		additional_cdb_len = 0; | 
 | 		ctx->fcp_cmnd_len = 12 + 16 + 4; | 
 | 	} | 
 |  | 
 | 	cmd_pkt = (struct cmd_type_6 *)req->ring_ptr; | 
 | 	cmd_pkt->handle = make_handle(req->id, handle); | 
 |  | 
 | 	/* | 
 | 	 * Zero out remaining portion of packet. | 
 | 	 * tagged queuing modifier -- default is TSK_SIMPLE (0). | 
 | 	 */ | 
 | 	clr_ptr = (uint32_t *)cmd_pkt + 2; | 
 | 	memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); | 
 | 	cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); | 
 |  | 
 | 	/* No data transfer */ | 
 | 	if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) { | 
 | 		cmd_pkt->byte_count = cpu_to_le32(0); | 
 | 		goto no_dsds; | 
 | 	} | 
 |  | 
 | 	/* Set transfer direction */ | 
 | 	if (cmd->sc_data_direction == DMA_TO_DEVICE) { | 
 | 		cmd_pkt->control_flags = cpu_to_le16(CF_WRITE_DATA); | 
 | 		vha->qla_stats.output_bytes += scsi_bufflen(cmd); | 
 | 		vha->qla_stats.output_requests++; | 
 | 		sp->fcport->edif.tx_bytes += scsi_bufflen(cmd); | 
 | 	} else if (cmd->sc_data_direction == DMA_FROM_DEVICE) { | 
 | 		cmd_pkt->control_flags = cpu_to_le16(CF_READ_DATA); | 
 | 		vha->qla_stats.input_bytes += scsi_bufflen(cmd); | 
 | 		vha->qla_stats.input_requests++; | 
 | 		sp->fcport->edif.rx_bytes += scsi_bufflen(cmd); | 
 | 	} | 
 |  | 
 | 	cmd_pkt->control_flags |= cpu_to_le16(CF_EN_EDIF); | 
 | 	cmd_pkt->control_flags &= ~(cpu_to_le16(CF_NEW_SA)); | 
 |  | 
 | 	/* One DSD is available in the Command Type 6 IOCB */ | 
 | 	avail_dsds = 1; | 
 | 	cur_dsd = &cmd_pkt->fcp_dsd; | 
 |  | 
 | 	/* Load data segments */ | 
 | 	scsi_for_each_sg(cmd, sg, tot_dsds, i) { | 
 | 		dma_addr_t      sle_dma; | 
 | 		cont_a64_entry_t *cont_pkt; | 
 |  | 
 | 		/* Allocate additional continuation packets? */ | 
 | 		if (avail_dsds == 0) { | 
 | 			/* | 
 | 			 * Five DSDs are available in the Continuation | 
 | 			 * Type 1 IOCB. | 
 | 			 */ | 
 | 			cont_pkt = qla2x00_prep_cont_type1_iocb(vha, req); | 
 | 			cur_dsd = cont_pkt->dsd; | 
 | 			avail_dsds = 5; | 
 | 		} | 
 |  | 
 | 		sle_dma = sg_dma_address(sg); | 
 | 		put_unaligned_le64(sle_dma, &cur_dsd->address); | 
 | 		cur_dsd->length = cpu_to_le32(sg_dma_len(sg)); | 
 | 		cur_dsd++; | 
 | 		avail_dsds--; | 
 | 	} | 
 |  | 
 | no_dsds: | 
 | 	/* Set NPORT-ID and LUN number*/ | 
 | 	cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); | 
 | 	cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; | 
 | 	cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; | 
 | 	cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; | 
 | 	cmd_pkt->vp_index = sp->vha->vp_idx; | 
 |  | 
 | 	cmd_pkt->entry_type = COMMAND_TYPE_6; | 
 |  | 
 | 	/* Set total data segment count. */ | 
 | 	cmd_pkt->entry_count = (uint8_t)req_cnt; | 
 |  | 
 | 	int_to_scsilun(cmd->device->lun, &cmd_pkt->lun); | 
 | 	host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); | 
 |  | 
 | 	/* build FCP_CMND IU */ | 
 | 	int_to_scsilun(cmd->device->lun, &ctx->fcp_cmnd->lun); | 
 | 	ctx->fcp_cmnd->additional_cdb_len = additional_cdb_len; | 
 |  | 
 | 	if (cmd->sc_data_direction == DMA_TO_DEVICE) | 
 | 		ctx->fcp_cmnd->additional_cdb_len |= 1; | 
 | 	else if (cmd->sc_data_direction == DMA_FROM_DEVICE) | 
 | 		ctx->fcp_cmnd->additional_cdb_len |= 2; | 
 |  | 
 | 	/* Populate the FCP_PRIO. */ | 
 | 	if (ha->flags.fcp_prio_enabled) | 
 | 		ctx->fcp_cmnd->task_attribute |= | 
 | 		    sp->fcport->fcp_prio << 3; | 
 |  | 
 | 	memcpy(ctx->fcp_cmnd->cdb, cmd->cmnd, cmd->cmd_len); | 
 |  | 
 | 	fcp_dl = (__be32 *)(ctx->fcp_cmnd->cdb + 16 + | 
 | 	    additional_cdb_len); | 
 | 	*fcp_dl = htonl((uint32_t)scsi_bufflen(cmd)); | 
 |  | 
 | 	cmd_pkt->fcp_cmnd_dseg_len = cpu_to_le16(ctx->fcp_cmnd_len); | 
 | 	put_unaligned_le64(ctx->fcp_cmnd_dma, &cmd_pkt->fcp_cmnd_dseg_address); | 
 |  | 
 | 	cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd)); | 
 | 	/* Set total data segment count. */ | 
 | 	cmd_pkt->entry_count = (uint8_t)req_cnt; | 
 | 	cmd_pkt->entry_status = 0; | 
 |  | 
 | 	/* Build command packet. */ | 
 | 	req->current_outstanding_cmd = handle; | 
 | 	req->outstanding_cmds[handle] = sp; | 
 | 	sp->handle = handle; | 
 | 	cmd->host_scribble = (unsigned char *)(unsigned long)handle; | 
 | 	req->cnt -= req_cnt; | 
 |  | 
 | 	/* Adjust ring index. */ | 
 | 	wmb(); | 
 | 	req->ring_index++; | 
 | 	if (req->ring_index == req->length) { | 
 | 		req->ring_index = 0; | 
 | 		req->ring_ptr = req->ring; | 
 | 	} else { | 
 | 		req->ring_ptr++; | 
 | 	} | 
 |  | 
 | 	sp->qpair->cmd_cnt++; | 
 | 	/* Set chip new ring index. */ | 
 | 	wrt_reg_dword(req->req_q_in, req->ring_index); | 
 |  | 
 | 	spin_unlock_irqrestore(lock, flags); | 
 |  | 
 | 	return QLA_SUCCESS; | 
 |  | 
 | queuing_error_fcp_cmnd: | 
 | queuing_error: | 
 | 	if (tot_dsds) | 
 | 		scsi_dma_unmap(cmd); | 
 |  | 
 | 	qla_put_buf(sp->qpair, &sp->u.scmd.buf_dsc); | 
 | 	qla_put_fw_resources(sp->qpair, &sp->iores); | 
 | 	spin_unlock_irqrestore(lock, flags); | 
 |  | 
 | 	return QLA_FUNCTION_FAILED; | 
 | } | 
 |  | 
 | /********************************************** | 
 |  * edif update/delete sa_index list functions * | 
 |  **********************************************/ | 
 |  | 
 | /* clear the edif_indx_list for this port */ | 
 | void qla_edif_list_del(fc_port_t *fcport) | 
 | { | 
 | 	struct edif_list_entry *indx_lst; | 
 | 	struct edif_list_entry *tindx_lst; | 
 | 	struct list_head *indx_list = &fcport->edif.edif_indx_list; | 
 | 	unsigned long flags = 0; | 
 |  | 
 | 	spin_lock_irqsave(&fcport->edif.indx_list_lock, flags); | 
 | 	list_for_each_entry_safe(indx_lst, tindx_lst, indx_list, next) { | 
 | 		list_del(&indx_lst->next); | 
 | 		kfree(indx_lst); | 
 | 	} | 
 | 	spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags); | 
 | } | 
 |  | 
 | /****************** | 
 |  * SADB functions * | 
 |  ******************/ | 
 |  | 
 | /* allocate/retrieve an sa_index for a given spi */ | 
 | static uint16_t qla_edif_sadb_get_sa_index(fc_port_t *fcport, | 
 | 		struct qla_sa_update_frame *sa_frame) | 
 | { | 
 | 	struct edif_sa_index_entry *entry; | 
 | 	struct list_head *sa_list; | 
 | 	uint16_t sa_index; | 
 | 	int dir = sa_frame->flags & SAU_FLG_TX; | 
 | 	int slot = 0; | 
 | 	int free_slot = -1; | 
 | 	scsi_qla_host_t *vha = fcport->vha; | 
 | 	struct qla_hw_data *ha = vha->hw; | 
 | 	unsigned long flags = 0; | 
 | 	uint16_t nport_handle = fcport->loop_id; | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 	    "%s: entry  fc_port: %p, nport_handle: 0x%x\n", | 
 | 	    __func__, fcport, nport_handle); | 
 |  | 
 | 	if (dir) | 
 | 		sa_list = &ha->sadb_tx_index_list; | 
 | 	else | 
 | 		sa_list = &ha->sadb_rx_index_list; | 
 |  | 
 | 	entry = qla_edif_sadb_find_sa_index_entry(nport_handle, sa_list); | 
 | 	if (!entry) { | 
 | 		if ((sa_frame->flags & (SAU_FLG_TX | SAU_FLG_INV)) == SAU_FLG_INV) { | 
 | 			ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 			    "%s: rx delete request with no entry\n", __func__); | 
 | 			return RX_DELETE_NO_EDIF_SA_INDEX; | 
 | 		} | 
 |  | 
 | 		/* if there is no entry for this nport, add one */ | 
 | 		entry = kzalloc((sizeof(struct edif_sa_index_entry)), GFP_ATOMIC); | 
 | 		if (!entry) | 
 | 			return INVALID_EDIF_SA_INDEX; | 
 |  | 
 | 		sa_index = qla_edif_get_sa_index_from_freepool(fcport, dir); | 
 | 		if (sa_index == INVALID_EDIF_SA_INDEX) { | 
 | 			kfree(entry); | 
 | 			return INVALID_EDIF_SA_INDEX; | 
 | 		} | 
 |  | 
 | 		INIT_LIST_HEAD(&entry->next); | 
 | 		entry->handle = nport_handle; | 
 | 		entry->fcport = fcport; | 
 | 		entry->sa_pair[0].spi = sa_frame->spi; | 
 | 		entry->sa_pair[0].sa_index = sa_index; | 
 | 		entry->sa_pair[1].spi = 0; | 
 | 		entry->sa_pair[1].sa_index = INVALID_EDIF_SA_INDEX; | 
 | 		spin_lock_irqsave(&ha->sadb_lock, flags); | 
 | 		list_add_tail(&entry->next, sa_list); | 
 | 		spin_unlock_irqrestore(&ha->sadb_lock, flags); | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 		    "%s: Created new sadb entry for nport_handle 0x%x, spi 0x%x, returning sa_index %d\n", | 
 | 		    __func__, nport_handle, sa_frame->spi, sa_index); | 
 |  | 
 | 		return sa_index; | 
 | 	} | 
 |  | 
 | 	spin_lock_irqsave(&ha->sadb_lock, flags); | 
 |  | 
 | 	/* see if we already have an entry for this spi */ | 
 | 	for (slot = 0; slot < 2; slot++) { | 
 | 		if (entry->sa_pair[slot].sa_index == INVALID_EDIF_SA_INDEX) { | 
 | 			free_slot = slot; | 
 | 		} else { | 
 | 			if (entry->sa_pair[slot].spi == sa_frame->spi) { | 
 | 				spin_unlock_irqrestore(&ha->sadb_lock, flags); | 
 | 				ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 				    "%s: sadb slot %d entry for lid 0x%x, spi 0x%x found, sa_index %d\n", | 
 | 				    __func__, slot, entry->handle, sa_frame->spi, | 
 | 				    entry->sa_pair[slot].sa_index); | 
 | 				return entry->sa_pair[slot].sa_index; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	spin_unlock_irqrestore(&ha->sadb_lock, flags); | 
 |  | 
 | 	/* both slots are used */ | 
 | 	if (free_slot == -1) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 		    "%s: WARNING: No free slots in sadb for nport_handle 0x%x, spi: 0x%x\n", | 
 | 		    __func__, entry->handle, sa_frame->spi); | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 		    "%s: Slot 0  spi: 0x%x  sa_index: %d,  Slot 1  spi: 0x%x  sa_index: %d\n", | 
 | 		    __func__, entry->sa_pair[0].spi, entry->sa_pair[0].sa_index, | 
 | 		    entry->sa_pair[1].spi, entry->sa_pair[1].sa_index); | 
 |  | 
 | 		return INVALID_EDIF_SA_INDEX; | 
 | 	} | 
 |  | 
 | 	/* there is at least one free slot, use it */ | 
 | 	sa_index = qla_edif_get_sa_index_from_freepool(fcport, dir); | 
 | 	if (sa_index == INVALID_EDIF_SA_INDEX) { | 
 | 		ql_dbg(ql_dbg_edif, fcport->vha, 0x3063, | 
 | 		    "%s: empty freepool!!\n", __func__); | 
 | 		return INVALID_EDIF_SA_INDEX; | 
 | 	} | 
 |  | 
 | 	spin_lock_irqsave(&ha->sadb_lock, flags); | 
 | 	entry->sa_pair[free_slot].spi = sa_frame->spi; | 
 | 	entry->sa_pair[free_slot].sa_index = sa_index; | 
 | 	spin_unlock_irqrestore(&ha->sadb_lock, flags); | 
 | 	ql_dbg(ql_dbg_edif, fcport->vha, 0x3063, | 
 | 	    "%s: sadb slot %d entry for nport_handle 0x%x, spi 0x%x added, returning sa_index %d\n", | 
 | 	    __func__, free_slot, entry->handle, sa_frame->spi, sa_index); | 
 |  | 
 | 	return sa_index; | 
 | } | 
 |  | 
 | /* release any sadb entries -- only done at teardown */ | 
 | void qla_edif_sadb_release(struct qla_hw_data *ha) | 
 | { | 
 | 	struct edif_sa_index_entry *entry, *tmp; | 
 |  | 
 | 	list_for_each_entry_safe(entry, tmp, &ha->sadb_rx_index_list, next) { | 
 | 		list_del(&entry->next); | 
 | 		kfree(entry); | 
 | 	} | 
 |  | 
 | 	list_for_each_entry_safe(entry, tmp, &ha->sadb_tx_index_list, next) { | 
 | 		list_del(&entry->next); | 
 | 		kfree(entry); | 
 | 	} | 
 | } | 
 |  | 
 | /************************** | 
 |  * sadb freepool functions | 
 |  **************************/ | 
 |  | 
 | /* build the rx and tx sa_index free pools -- only done at fcport init */ | 
 | int qla_edif_sadb_build_free_pool(struct qla_hw_data *ha) | 
 | { | 
 | 	ha->edif_tx_sa_id_map = | 
 | 	    kcalloc(BITS_TO_LONGS(EDIF_NUM_SA_INDEX), sizeof(long), GFP_KERNEL); | 
 |  | 
 | 	if (!ha->edif_tx_sa_id_map) { | 
 | 		ql_log_pci(ql_log_fatal, ha->pdev, 0x0009, | 
 | 		    "Unable to allocate memory for sadb tx.\n"); | 
 | 		return -ENOMEM; | 
 | 	} | 
 |  | 
 | 	ha->edif_rx_sa_id_map = | 
 | 	    kcalloc(BITS_TO_LONGS(EDIF_NUM_SA_INDEX), sizeof(long), GFP_KERNEL); | 
 | 	if (!ha->edif_rx_sa_id_map) { | 
 | 		kfree(ha->edif_tx_sa_id_map); | 
 | 		ha->edif_tx_sa_id_map = NULL; | 
 | 		ql_log_pci(ql_log_fatal, ha->pdev, 0x0009, | 
 | 		    "Unable to allocate memory for sadb rx.\n"); | 
 | 		return -ENOMEM; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* release the free pool - only done during fcport teardown */ | 
 | void qla_edif_sadb_release_free_pool(struct qla_hw_data *ha) | 
 | { | 
 | 	kfree(ha->edif_tx_sa_id_map); | 
 | 	ha->edif_tx_sa_id_map = NULL; | 
 | 	kfree(ha->edif_rx_sa_id_map); | 
 | 	ha->edif_rx_sa_id_map = NULL; | 
 | } | 
 |  | 
 | static void __chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, | 
 | 		fc_port_t *fcport, uint32_t handle, uint16_t sa_index) | 
 | { | 
 | 	struct edif_list_entry *edif_entry; | 
 | 	struct edif_sa_ctl *sa_ctl; | 
 | 	uint16_t delete_sa_index = INVALID_EDIF_SA_INDEX; | 
 | 	unsigned long flags = 0; | 
 | 	uint16_t nport_handle = fcport->loop_id; | 
 | 	uint16_t cached_nport_handle; | 
 |  | 
 | 	spin_lock_irqsave(&fcport->edif.indx_list_lock, flags); | 
 | 	edif_entry = qla_edif_list_find_sa_index(fcport, nport_handle); | 
 | 	if (!edif_entry) { | 
 | 		spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags); | 
 | 		return;		/* no pending delete for this handle */ | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * check for no pending delete for this index or iocb does not | 
 | 	 * match rx sa_index | 
 | 	 */ | 
 | 	if (edif_entry->delete_sa_index == INVALID_EDIF_SA_INDEX || | 
 | 	    edif_entry->update_sa_index != sa_index) { | 
 | 		spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * wait until we have seen at least EDIF_DELAY_COUNT transfers before | 
 | 	 * queueing RX delete | 
 | 	 */ | 
 | 	if (edif_entry->count++ < EDIF_RX_DELETE_FILTER_COUNT) { | 
 | 		spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	ql_dbg(ql_dbg_edif, vha, 0x5033, | 
 | 	    "%s: invalidating delete_sa_index,  update_sa_index: 0x%x sa_index: 0x%x, delete_sa_index: 0x%x\n", | 
 | 	    __func__, edif_entry->update_sa_index, sa_index, edif_entry->delete_sa_index); | 
 |  | 
 | 	delete_sa_index = edif_entry->delete_sa_index; | 
 | 	edif_entry->delete_sa_index = INVALID_EDIF_SA_INDEX; | 
 | 	cached_nport_handle = edif_entry->handle; | 
 | 	spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags); | 
 |  | 
 | 	/* sanity check on the nport handle */ | 
 | 	if (nport_handle != cached_nport_handle) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 		    "%s: POST SA DELETE nport_handle mismatch: lid: 0x%x, edif_entry nph: 0x%x\n", | 
 | 		    __func__, nport_handle, cached_nport_handle); | 
 | 	} | 
 |  | 
 | 	/* find the sa_ctl for the delete and schedule the delete */ | 
 | 	sa_ctl = qla_edif_find_sa_ctl_by_index(fcport, delete_sa_index, 0); | 
 | 	if (sa_ctl) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 		    "%s: POST SA DELETE sa_ctl: %p, index recvd %d\n", | 
 | 		    __func__, sa_ctl, sa_index); | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 		    "delete index %d, update index: %d, nport handle: 0x%x, handle: 0x%x\n", | 
 | 		    delete_sa_index, | 
 | 		    edif_entry->update_sa_index, nport_handle, handle); | 
 |  | 
 | 		sa_ctl->flags = EDIF_SA_CTL_FLG_DEL; | 
 | 		set_bit(EDIF_SA_CTL_REPL, &sa_ctl->state); | 
 | 		qla_post_sa_replace_work(fcport->vha, fcport, | 
 | 		    nport_handle, sa_ctl); | 
 | 	} else { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x3063, | 
 | 		    "%s: POST SA DELETE sa_ctl not found for delete_sa_index: %d\n", | 
 | 		    __func__, delete_sa_index); | 
 | 	} | 
 | } | 
 |  | 
 | void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, | 
 | 		srb_t *sp, struct sts_entry_24xx *sts24) | 
 | { | 
 | 	fc_port_t *fcport = sp->fcport; | 
 | 	/* sa_index used by this iocb */ | 
 | 	struct scsi_cmnd *cmd = GET_CMD_SP(sp); | 
 | 	uint32_t handle; | 
 |  | 
 | 	handle = (uint32_t)LSW(sts24->handle); | 
 |  | 
 | 	/* find out if this status iosb is for a scsi read */ | 
 | 	if (cmd->sc_data_direction != DMA_FROM_DEVICE) | 
 | 		return; | 
 |  | 
 | 	return __chk_edif_rx_sa_delete_pending(vha, fcport, handle, | 
 | 	   le16_to_cpu(sts24->edif_sa_index)); | 
 | } | 
 |  | 
 | void qlt_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, fc_port_t *fcport, | 
 | 		struct ctio7_from_24xx *pkt) | 
 | { | 
 | 	__chk_edif_rx_sa_delete_pending(vha, fcport, | 
 | 	    pkt->handle, le16_to_cpu(pkt->edif_sa_index)); | 
 | } | 
 |  | 
 | static void qla_parse_auth_els_ctl(struct srb *sp) | 
 | { | 
 | 	struct qla_els_pt_arg *a = &sp->u.bsg_cmd.u.els_arg; | 
 | 	struct bsg_job *bsg_job = sp->u.bsg_cmd.bsg_job; | 
 | 	struct fc_bsg_request *request = bsg_job->request; | 
 | 	struct qla_bsg_auth_els_request *p = | 
 | 	    (struct qla_bsg_auth_els_request *)bsg_job->request; | 
 |  | 
 | 	a->tx_len = a->tx_byte_count = sp->remap.req.len; | 
 | 	a->tx_addr = sp->remap.req.dma; | 
 | 	a->rx_len = a->rx_byte_count = sp->remap.rsp.len; | 
 | 	a->rx_addr = sp->remap.rsp.dma; | 
 |  | 
 | 	if (p->e.sub_cmd == SEND_ELS_REPLY) { | 
 | 		a->control_flags = p->e.extra_control_flags << 13; | 
 | 		a->rx_xchg_address = cpu_to_le32(p->e.extra_rx_xchg_address); | 
 | 		if (p->e.extra_control_flags == BSG_CTL_FLAG_LS_ACC) | 
 | 			a->els_opcode = ELS_LS_ACC; | 
 | 		else if (p->e.extra_control_flags == BSG_CTL_FLAG_LS_RJT) | 
 | 			a->els_opcode = ELS_LS_RJT; | 
 | 	} | 
 | 	a->did = sp->fcport->d_id; | 
 | 	a->els_opcode =  request->rqst_data.h_els.command_code; | 
 | 	a->nport_handle = cpu_to_le16(sp->fcport->loop_id); | 
 | 	a->vp_idx = sp->vha->vp_idx; | 
 | } | 
 |  | 
 | int qla_edif_process_els(scsi_qla_host_t *vha, struct bsg_job *bsg_job) | 
 | { | 
 | 	struct fc_bsg_request *bsg_request = bsg_job->request; | 
 | 	struct fc_bsg_reply *bsg_reply = bsg_job->reply; | 
 | 	fc_port_t *fcport = NULL; | 
 | 	struct qla_hw_data *ha = vha->hw; | 
 | 	srb_t *sp; | 
 | 	int rval =  (DID_ERROR << 16), cnt; | 
 | 	port_id_t d_id; | 
 | 	struct qla_bsg_auth_els_request *p = | 
 | 	    (struct qla_bsg_auth_els_request *)bsg_job->request; | 
 | 	struct qla_bsg_auth_els_reply *rpl = | 
 | 	    (struct qla_bsg_auth_els_reply *)bsg_job->reply; | 
 |  | 
 | 	rpl->version = EDIF_VERSION1; | 
 |  | 
 | 	d_id.b.al_pa = bsg_request->rqst_data.h_els.port_id[2]; | 
 | 	d_id.b.area = bsg_request->rqst_data.h_els.port_id[1]; | 
 | 	d_id.b.domain = bsg_request->rqst_data.h_els.port_id[0]; | 
 |  | 
 | 	/* find matching d_id in fcport list */ | 
 | 	fcport = qla2x00_find_fcport_by_pid(vha, &d_id); | 
 | 	if (!fcport) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x911a, | 
 | 		    "%s fcport not find online portid=%06x.\n", | 
 | 		    __func__, d_id.b24); | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_ERROR); | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	if (qla_bsg_check(vha, bsg_job, fcport)) | 
 | 		return 0; | 
 |  | 
 | 	if (EDIF_SESS_DELETE(fcport)) { | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x910d, | 
 | 		    "%s ELS code %x, no loop id.\n", __func__, | 
 | 		    bsg_request->rqst_data.r_els.els_code); | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_BAD_TARGET); | 
 | 		return -ENXIO; | 
 | 	} | 
 |  | 
 | 	if (!vha->flags.online) { | 
 | 		ql_log(ql_log_warn, vha, 0x7005, "Host not online.\n"); | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_BAD_TARGET); | 
 | 		rval = -EIO; | 
 | 		goto done; | 
 | 	} | 
 |  | 
 | 	/* pass through is supported only for ISP 4Gb or higher */ | 
 | 	if (!IS_FWI2_CAPABLE(ha)) { | 
 | 		ql_dbg(ql_dbg_user, vha, 0x7001, | 
 | 		    "ELS passthru not supported for ISP23xx based adapters.\n"); | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_BAD_TARGET); | 
 | 		rval = -EPERM; | 
 | 		goto done; | 
 | 	} | 
 |  | 
 | 	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); | 
 | 	if (!sp) { | 
 | 		ql_dbg(ql_dbg_user, vha, 0x7004, | 
 | 		    "Failed get sp pid=%06x\n", fcport->d_id.b24); | 
 | 		rval = -ENOMEM; | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY); | 
 | 		goto done; | 
 | 	} | 
 |  | 
 | 	sp->remap.req.len = bsg_job->request_payload.payload_len; | 
 | 	sp->remap.req.buf = dma_pool_alloc(ha->purex_dma_pool, | 
 | 	    GFP_KERNEL, &sp->remap.req.dma); | 
 | 	if (!sp->remap.req.buf) { | 
 | 		ql_dbg(ql_dbg_user, vha, 0x7005, | 
 | 		    "Failed allocate request dma len=%x\n", | 
 | 		    bsg_job->request_payload.payload_len); | 
 | 		rval = -ENOMEM; | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY); | 
 | 		goto done_free_sp; | 
 | 	} | 
 |  | 
 | 	sp->remap.rsp.len = bsg_job->reply_payload.payload_len; | 
 | 	sp->remap.rsp.buf = dma_pool_alloc(ha->purex_dma_pool, | 
 | 	    GFP_KERNEL, &sp->remap.rsp.dma); | 
 | 	if (!sp->remap.rsp.buf) { | 
 | 		ql_dbg(ql_dbg_user, vha, 0x7006, | 
 | 		    "Failed allocate response dma len=%x\n", | 
 | 		    bsg_job->reply_payload.payload_len); | 
 | 		rval = -ENOMEM; | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY); | 
 | 		goto done_free_remap_req; | 
 | 	} | 
 | 	sg_copy_to_buffer(bsg_job->request_payload.sg_list, | 
 | 	    bsg_job->request_payload.sg_cnt, sp->remap.req.buf, | 
 | 	    sp->remap.req.len); | 
 | 	sp->remap.remapped = true; | 
 |  | 
 | 	sp->type = SRB_ELS_CMD_HST_NOLOGIN; | 
 | 	sp->name = "SPCN_BSG_HST_NOLOGIN"; | 
 | 	sp->u.bsg_cmd.bsg_job = bsg_job; | 
 | 	qla_parse_auth_els_ctl(sp); | 
 |  | 
 | 	sp->free = qla2x00_bsg_sp_free; | 
 | 	sp->done = qla2x00_bsg_job_done; | 
 |  | 
 | 	cnt = 0; | 
 | retry: | 
 | 	rval = qla2x00_start_sp(sp); | 
 | 	switch (rval) { | 
 | 	case QLA_SUCCESS: | 
 | 		ql_dbg(ql_dbg_edif, vha, 0x700a, | 
 | 		       "%s %s %8phN xchg %x ctlflag %x hdl %x reqlen %xh bsg ptr %p\n", | 
 | 		       __func__, sc_to_str(p->e.sub_cmd), fcport->port_name, | 
 | 		       p->e.extra_rx_xchg_address, p->e.extra_control_flags, | 
 | 		       sp->handle, sp->remap.req.len, bsg_job); | 
 | 		break; | 
 | 	case EAGAIN: | 
 | 		msleep(EDIF_MSLEEP_INTERVAL); | 
 | 		cnt++; | 
 | 		if (cnt < EDIF_RETRY_COUNT) | 
 | 			goto retry; | 
 | 		fallthrough; | 
 | 	default: | 
 | 		ql_log(ql_log_warn, vha, 0x700e, | 
 | 		    "%s qla2x00_start_sp failed = %d\n", __func__, rval); | 
 | 		SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY); | 
 | 		rval = -EIO; | 
 | 		goto done_free_remap_rsp; | 
 | 	} | 
 | 	return rval; | 
 |  | 
 | done_free_remap_rsp: | 
 | 	dma_pool_free(ha->purex_dma_pool, sp->remap.rsp.buf, | 
 | 	    sp->remap.rsp.dma); | 
 | done_free_remap_req: | 
 | 	dma_pool_free(ha->purex_dma_pool, sp->remap.req.buf, | 
 | 	    sp->remap.req.dma); | 
 | done_free_sp: | 
 | 	qla2x00_rel_sp(sp); | 
 |  | 
 | done: | 
 | 	return rval; | 
 | } | 
 |  | 
 | void qla_edif_sess_down(struct scsi_qla_host *vha, struct fc_port *sess) | 
 | { | 
 | 	u16 cnt = 0; | 
 |  | 
 | 	if (sess->edif.app_sess_online && DBELL_ACTIVE(vha)) { | 
 | 		ql_dbg(ql_dbg_disc, vha, 0xf09c, | 
 | 			"%s: sess %8phN send port_offline event\n", | 
 | 			__func__, sess->port_name); | 
 | 		sess->edif.app_sess_online = 0; | 
 | 		sess->edif.sess_down_acked = 0; | 
 | 		qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_SESSION_SHUTDOWN, | 
 | 		    sess->d_id.b24, 0, sess); | 
 | 		qla2x00_post_aen_work(vha, FCH_EVT_PORT_OFFLINE, sess->d_id.b24); | 
 |  | 
 | 		while (!READ_ONCE(sess->edif.sess_down_acked) && | 
 | 		       !test_bit(VPORT_DELETE, &vha->dpc_flags)) { | 
 | 			msleep(100); | 
 | 			cnt++; | 
 | 			if (cnt > 100) | 
 | 				break; | 
 | 		} | 
 | 		sess->edif.sess_down_acked = 0; | 
 | 		ql_dbg(ql_dbg_disc, vha, 0xf09c, | 
 | 		       "%s: sess %8phN port_offline event completed\n", | 
 | 		       __func__, sess->port_name); | 
 | 	} | 
 | } | 
 |  | 
 | void qla_edif_clear_appdata(struct scsi_qla_host *vha, struct fc_port *fcport) | 
 | { | 
 | 	if (!(fcport->flags & FCF_FCSP_DEVICE)) | 
 | 		return; | 
 |  | 
 | 	qla_edb_clear(vha, fcport->d_id); | 
 | 	qla_enode_clear(vha, fcport->d_id); | 
 | } |