linux-scsi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Himanshu Madhani <himanshu.madhani@oracle.com>
To: Nilesh Javali <njavali@marvell.com>, martin.petersen@oracle.com
Cc: linux-scsi@vger.kernel.org, GR-QLogic-Storage-Upstream@marvell.com
Subject: Re: [PATCH v2 05/10] qla2xxx: Add key update
Date: Thu, 3 Jun 2021 17:55:56 -0500	[thread overview]
Message-ID: <2a1e742c-1e77-98e4-a7ca-6bd8ebd16d12@oracle.com> (raw)
In-Reply-To: <20210531070545.32072-6-njavali@marvell.com>



On 5/31/21 2:05 AM, Nilesh Javali wrote:
> From: Quinn Tran <qutran@marvell.com>
> 
> Latest FC adapter from Marvell has the ability to encrypt
> data in flight (EDIF) feature. This feature require an
> application (ex: ipsec, etc) to act as an authenticator.
> 
> As part of the authentication process, the authentication
> application will generate a SADB entry (Security Association/SA,
> key, SPI value, etc). This SADB is then pass to driver
> to be programmed into hardware. There will be a pair of
> SADB's (Tx and Rx) for each connection.
> 
> After some period, the application can choose to change the
> key. At that time, a new set of SADB pair is given to driver.
> The old set of SADB will be deleted.
> 
> This patch add a new bsg call (QL_VND_SC_SA_UPDATE) to allow
> application to allow add | delete of SADB.  Driver will not
> keep the key in memory. It will pass it to HW.
> 
> It is assume that application will assign a unique SPI value
> to this SADB(SA + key). Driver + HW will assign a handle
> to track this unique SPI/SADB.
> 
> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com>
> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com>
> Signed-off-by: Rick Hicksted Jr <rhicksted@marvell.com>
> Signed-off-by: Quinn Tran <qutran@marvell.com>
> Signed-off-by: Nilesh Javali <njavali@marvell.com>
> ---
>   drivers/scsi/qla2xxx/qla_def.h    |   69 ++
>   drivers/scsi/qla2xxx/qla_edif.c   | 1482 ++++++++++++++++++++++++++++-
>   drivers/scsi/qla2xxx/qla_edif.h   |   61 ++
>   drivers/scsi/qla2xxx/qla_fw.h     |    1 +
>   drivers/scsi/qla2xxx/qla_gbl.h    |   21 +
>   drivers/scsi/qla2xxx/qla_init.c   |   11 +
>   drivers/scsi/qla2xxx/qla_iocb.c   |    6 +
>   drivers/scsi/qla2xxx/qla_isr.c    |   10 +
>   drivers/scsi/qla2xxx/qla_os.c     |   20 +
>   drivers/scsi/qla2xxx/qla_target.h |    2 +-
>   10 files changed, 1681 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
> index e47a7b3618d6..164b87fd66ba 100644
> --- a/drivers/scsi/qla2xxx/qla_def.h
> +++ b/drivers/scsi/qla2xxx/qla_def.h
> @@ -401,6 +401,7 @@ struct srb_cmd {
>   #define SRB_CRC_CTX_DSD_VALID		BIT_5	/* DIF: dsd_list valid */
>   #define SRB_WAKEUP_ON_COMP		BIT_6
>   #define SRB_DIF_BUNDL_DMA_VALID		BIT_7   /* DIF: DMA list valid */
> +#define SRB_EDIF_CLEANUP_DELETE		BIT_9
>   
>   /* To identify if a srb is of T10-CRC type. @sp => srb_t pointer */
>   #define IS_PROT_IO(sp)	(sp->flags & SRB_CRC_CTX_DSD_VALID)
> @@ -595,6 +596,10 @@ struct srb_iocb {
>   			u16 cmd;
>   			u16 vp_index;
>   		} ctrlvp;
> +		struct {
> +			struct edif_sa_ctl	*sa_ctl;
> +			struct qla_sa_update_frame sa_frame;
> +		} sa_update;
>   	} u;
>   
>   	struct timer_list timer;
> @@ -2616,15 +2621,26 @@ typedef struct fc_port {
>   		uint16_t	app_stop:2;
>   		uint16_t	app_started:1;
>   		uint16_t	secured_login:1;
> +		uint16_t	aes_gmac:1;
>   		uint16_t	app_sess_online:1;
>   		uint16_t	rekey_cnt;	// num of times rekeyed
>   		uint8_t		auth_state;	/* cureent auth state */
> +		uint8_t		tx_sa_set;
> +		uint8_t		rx_sa_set;
> +		uint8_t		tx_sa_pending;
> +		uint8_t		rx_sa_pending;
>   		uint32_t	tx_rekey_cnt;
>   		uint32_t	rx_rekey_cnt;
>   		// delayed rx delete data structure list
>   		uint64_t	tx_bytes;
>   		uint64_t	rx_bytes;
>   		uint8_t		non_secured_login;
> +		struct list_head edif_indx_list;
> +		spinlock_t  indx_list_lock;  // protects the edif index list
> +
> +		struct list_head tx_sa_list;
> +		struct list_head rx_sa_list;
> +		spinlock_t	sa_list_lock;       /* protects list */
>   	} edif;
>   } fc_port_t;
>   

cleanup/fix comment styles

> @@ -2680,6 +2696,7 @@ static const char * const port_dstate_str[] = {
>   #define FCF_CONF_COMP_SUPPORTED BIT_4
>   #define FCF_ASYNC_ACTIVE	BIT_5
>   #define FCF_FCSP_DEVICE		BIT_6
> +#define FCF_EDIF_DELETE		BIT_7
>   
>   /* No loop ID flag. */
>   #define FC_NO_LOOP_ID		0x1000
> @@ -3450,6 +3467,7 @@ enum qla_work_type {
>   	QLA_EVT_SP_RETRY,
>   	QLA_EVT_IIDMA,
>   	QLA_EVT_ELS_PLOGI,
> +	QLA_EVT_SA_REPLACE,
>   };
>   
>   
> @@ -3508,6 +3526,11 @@ struct qla_work_evt {
>   			u8 fc4_type;
>   			srb_t *sp;
>   		} gpnft;
> +		struct {
> +			struct edif_sa_ctl	*sa_ctl;
> +			fc_port_t *fcport;
> +			uint16_t nport_handle;
> +		} sa_update;
>   	 } u;
>   };
>   
> @@ -4682,6 +4705,16 @@ struct qla_hw_data {
>   	pci_error_state_t pci_error_state;
>   	struct dma_pool *purex_dma_pool;
>   	struct btree_head32 host_map;
> +
> +	#define EDIF_NUM_SA_INDEX	512
> +	#define EDIF_TX_SA_INDEX_BASE	EDIF_NUM_SA_INDEX

fix indentation for the #define

> +	void *edif_rx_sa_id_map;
> +	void *edif_tx_sa_id_map;
> +	spinlock_t sadb_fp_lock;
> +
> +	struct list_head sadb_tx_index_list;
> +	struct list_head sadb_rx_index_list;
> +	spinlock_t sadb_lock;	/* protects list */
>   	struct els_reject elsrej;
>   };
>   
> @@ -5157,7 +5190,43 @@ enum nexus_wait_type {
>   	WAIT_LUN,
>   };
>   
> +#define INVALID_EDIF_SA_INDEX	0xffff
> +#define RX_DELETE_NO_EDIF_SA_INDEX	0xfffe
> +
>   #define QLA_SKIP_HANDLE QLA_TGT_SKIP_HANDLE
> +
> +/* edif hash element */
> +struct edif_list_entry {
> +	uint16_t handle;			/* nport_handle */
> +	uint32_t update_sa_index;
> +	uint32_t delete_sa_index;
> +	uint32_t count;				/* counter for filtering sa_index */
> +#define EDIF_ENTRY_FLAGS_CLEANUP	0x01	/* this index is being cleaned up */
> +	uint32_t flags;				/* used by sadb cleanup code */
> +	fc_port_t *fcport;			/* needed by rx delay timer function */
> +	struct timer_list timer;		/* rx delay timer */
> +	struct list_head next;
> +};
> +
> +#define EDIF_TX_INDX_BASE 512
> +#define EDIF_RX_INDX_BASE 0
> +#define EDIF_RX_DELETE_FILTER_COUNT 3	/* delay queuing rx delete until this many */
> +
> +/* entry in the sa_index free pool */
> +
> +struct sa_index_pair {
> +	uint16_t sa_index;
> +	uint32_t spi;
> +};
> +
> +/* edif sa_index data structure */
> +struct edif_sa_index_entry {
> +	struct sa_index_pair sa_pair[2];
> +	fc_port_t *fcport;
> +	uint16_t handle;
> +	struct list_head next;
> +};
> +
>   /* Refer to SNIA SFF 8247 */
>   struct sff_8247_a0 {
>   	u8 txid;	/* transceiver id */
> diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c
> index df8dff447c6a..4c5cc99bdbd4 100644
> --- a/drivers/scsi/qla2xxx/qla_edif.c
> +++ b/drivers/scsi/qla2xxx/qla_edif.c
> @@ -4,13 +4,19 @@
>    * Copyright (c)  2021     Marvell
>    */
>   #include "qla_def.h"
> -//#include "qla_edif.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 *);
>   
>   static struct els_sub_cmd {
> @@ -35,6 +41,147 @@ const char *sc_to_str(uint16_t cmd)
>   	return "unknown";
>   }
>   
> +/* find an edif list entry for an nport_handle */
> +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;
> +
remove empty line

> +	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: POST SA DELETE TIMEOUT  sa_ctl: %p, delete index %d, update index: %d, lid: 0x%x\n",

fix indentation

> +			    __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: POST SA DELETE TIMEOUT  sa_ctl not found for delete_sa_index: %d\n",
> +			    __func__, edif_entry->delete_sa_index);

ditto here.. fix indentation

> +		}
> +	} 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)
>   {
> @@ -198,6 +345,171 @@ static void qla_edif_reset_auth_wait(struct fc_port *fcport, int state,
>   	}
>   }
>   
> +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, fcport nport_handle: 0x%x, sa_index: %d\n",

fix indentation

> +				__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: removing edif_entry %p, update_sa_index: 0x%x, delete_sa_index: 0x%x\n",

same here... fix indentation

> +					__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) {
> +					del_timer(&edif_entry->timer);
> +
> +					/* build and send the aen */
> +					fcport->edif.rx_sa_set = 1;
> +					fcport->edif.rx_sa_pending = 0;
> +				}
> +				ql_dbg(ql_dbg_edif, vha, 0x5033,
> +	"%s: releasing edif_entry %p, update_sa_index: 0x%x, delete_sa_index: 0x%x\n",

ditto here as well.. fix indentation

> +					__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);
> +}
> +
>   /*
>    * event that the app has started. Clear and start doorbell
>    */
> @@ -565,6 +877,10 @@ qla_edif_app_mgmt(struct bsg_job *bsg_job)
>   	}
>   
>   	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;
> @@ -598,6 +914,435 @@ qla_edif_app_mgmt(struct bsg_job *bsg_job)
>   	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);

add new line here

> +	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;

add new line here

> +	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;
> +	}

add new line here

> +	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;
> +
> +		/* 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);

add new line here

> +	return 0;
> +}
> +
> +#define QLA_SA_UPDATE_FLAGS_RX_KEY      0x0
> +#define QLA_SA_UPDATE_FLAGS_TX_KEY      0x2
> +
> +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;
> +	struct qla_sa_update_frame sa_frame;
> +	struct srb_iocb *iocb_cmd;
> +
> +	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 (vha->e_dbell.db_flags != EDB_ACTIVE) {
> +		ql_log(ql_log_warn, vha, 0x70a1, "App not started\n");
> +		rval = -EIO;
> +		SET_DID_STATUS(bsg_reply->result, DID_ERROR);
> +		goto done;
> +	}
> +
> +	fcport = qla2x00_find_fcport_by_pid(vha, &sa_frame.port_id);
> +	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_TARGET_FAILURE);
> +		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;
> +
> +	rval = qla2x00_start_sp(sp);
> +	if (rval != QLA_SUCCESS) {
> +		ql_log(ql_dbg_edif, vha, 0x70e3,
> +		    "qla2x00_start_sp failed=%d.\n", 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)
>   {
> @@ -860,6 +1605,200 @@ qla_edb_stop(scsi_qla_host_t *vha)
>   	}
>   }
>   
> +static void qla_noop_sp_done(srb_t *sp, int res)
> +{
> +	sp->free(sp);
> +}
> +
> +/*
> + * 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");
> +		return -ENOMEM;
> +	}
> +
> +	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");
> +		return -ENOMEM;
> +	}
> +
> +	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)
> +		rval = QLA_FUNCTION_FAILED;
> +
> +	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];
> +
> +		ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x921f, "%s 256 sa key=%32phN\n",
> +		    __func__, sa_update_iocb->sa_key);
> +	} 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 +  ql_dbg_verbose, vha, 0x921f, "%s 128 sa key=%16phN\n",
> +		    __func__, sa_update_iocb->sa_key);
> +	}
> +
> +	ql_dbg(ql_dbg_edif, vha, 0x921d,
> +"%s SAU Port ID = %02x:%02x:%02x, flags=%xh, index=%u, ctl=%xh, SPI 0x%x user 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;
> @@ -987,6 +1926,547 @@ void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp)
>   	    purex->pur_info.pur_did.b24, p->rx_xchg_addr);
>   }
>   
> +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);
> +	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);
> +			del_timer(&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;
> +		} else {
> +			sp->fcport->edif.rx_sa_set = 1;
> +			sp->fcport->edif.rx_sa_pending = 0;
> +		}
> +	} else {
> +		ql_dbg(ql_dbg_edif, vha, 0x3063,
> +		    "%s: %8phN SA update FAILED: sa_index: %d, new_sa_info %d, %02x%02x%02x -- dumping\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]);
> +	}
> +
> +	/* 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));
> +	}
> +
> +	sp->done(sp, 0);
> +}
> +
> +/******************
> + * 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\n",
> +			__func__, entry->sa_pair[0].spi,
> +			entry->sa_pair[0].sa_index);
> +		ql_dbg(ql_dbg_edif, vha, 0x3063,
> +			"%s:   Slot 1  spi: 0x%x  sa_index: %d\n",
> +			__func__, 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 list_head *pos;
> +	struct list_head *tmp;
> +	struct edif_sa_index_entry *entry;
> +
> +	list_for_each_safe(pos, tmp, &ha->sadb_rx_index_list) {
> +		entry = list_entry(pos, struct edif_sa_index_entry, next);
> +		list_del(&entry->next);
> +		kfree(entry);
> +	}
> +
> +	list_for_each_safe(pos, tmp, &ha->sadb_tx_index_list) {
> +		entry = list_entry(pos, struct edif_sa_index_entry, 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;
> diff --git a/drivers/scsi/qla2xxx/qla_edif.h b/drivers/scsi/qla2xxx/qla_edif.h
> index 12607218df17..799446ea9fbc 100644
> --- a/drivers/scsi/qla2xxx/qla_edif.h
> +++ b/drivers/scsi/qla2xxx/qla_edif.h
> @@ -8,6 +8,27 @@
>   
>   struct qla_scsi_host;
>   
> +#define EDIF_MAX_INDEX	2048
> +struct edif_sa_ctl {
> +	struct list_head next;
> +	uint16_t	del_index;
> +	uint16_t	index;
> +	uint16_t	slot;
> +	uint16_t	flags;
> +#define	EDIF_SA_CTL_FLG_REPL		BIT_0
> +#define	EDIF_SA_CTL_FLG_DEL		BIT_1
> +#define EDIF_SA_CTL_FLG_CLEANUP_DEL BIT_4
> +	// Invalidate Index bit and mirrors QLA_SA_UPDATE_FLAGS_DELETE
> +	unsigned long   state;
> +#define EDIF_SA_CTL_USED	1	/* Active Sa update  */
> +#define EDIF_SA_CTL_PEND	2	/* Waiting for slot */
> +#define EDIF_SA_CTL_REPL	3	/* Active Replace and Delete */
> +#define EDIF_SA_CTL_DEL		4	/* Delete Pending */
> +	struct fc_port	*fcport;
> +	struct bsg_job *bsg_job;
> +	struct qla_sa_update_frame sa_frame;
> +};
> +
>   enum enode_flags_t {
>   	ENODE_ACTIVE = 0x1,	// means that app has started
>   };
> @@ -29,6 +50,46 @@ struct edif_dbell {
>   	struct	completion	dbell;		/* doorbell ring */
>   };
>   
> +#define SA_UPDATE_IOCB_TYPE            0x71    /* Security Association Update IOCB entry */
> +struct sa_update_28xx {
> +	uint8_t entry_type;             /* Entry type. */
> +	uint8_t entry_count;            /* Entry count. */
> +	uint8_t sys_define;             /* System Defined. */
> +	uint8_t entry_status;           /* Entry Status. */
> +
> +	uint32_t handle;                /* IOCB System handle. */
> +
> +	union {
> +		__le16 nport_handle;  /* in: N_PORT handle. */
> +		__le16 comp_sts;              /* out: completion status */
> +#define CS_PORT_EDIF_SUPP_NOT_RDY 0x64
> +#define CS_PORT_EDIF_INV_REQ      0x66
> +	} u;
> +	uint8_t vp_index;
> +	uint8_t reserved_1;
> +	uint8_t port_id[3];
> +	uint8_t flags;
> +#define SA_FLAG_INVALIDATE BIT_0
> +#define SA_FLAG_TX	   BIT_1 // 1=tx, 0=rx
> +
> +	uint8_t sa_key[32];     /* 256 bit key */
> +	__le32 salt;
> +	__le32 spi;
> +	uint8_t sa_control;
> +#define SA_CNTL_ENC_FCSP        (1 << 3)
> +#define SA_CNTL_ENC_OPD         (2 << 3)
> +#define SA_CNTL_ENC_MSK         (3 << 3)  // mask bits 4,3
> +#define SA_CNTL_AES_GMAC	(1 << 2)
> +#define SA_CNTL_KEY256          (2 << 0)
> +#define SA_CNTL_KEY128          0
> +
> +	uint8_t reserved_2;
> +	__le16 sa_index;   // reserve: bit 11-15
> +	__le16 old_sa_info;
> +	__le16 new_sa_info;
> +};
> +
> +#define        NUM_ENTRIES     256
>   #define        MAX_PAYLOAD     1024
>   #define        PUR_GET         1
>   
> diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
> index 49df418030e4..c067cd202dc4 100644
> --- a/drivers/scsi/qla2xxx/qla_fw.h
> +++ b/drivers/scsi/qla2xxx/qla_fw.h
> @@ -611,6 +611,7 @@ struct sts_entry_24xx {
>   	union {
>   		__le16 reserved_1;
>   		__le16	nvme_rsp_pyld_len;
> +		__le16 edif_sa_index;	 /* edif sa_index used for initiator read data */
>   	};
>   
>   	__le16	state_flags;		/* State flags. */
> diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
> index a4cb8092e97e..f4a98d92c4b3 100644
> --- a/drivers/scsi/qla2xxx/qla_gbl.h
> +++ b/drivers/scsi/qla2xxx/qla_gbl.h
> @@ -130,6 +130,13 @@ void qla24xx_free_purex_item(struct purex_item *item);
>   extern bool qla24xx_risc_firmware_invalid(uint32_t *);
>   void qla_init_iocb_limit(scsi_qla_host_t *);
>   
> +void qla_edif_sadb_release(struct qla_hw_data *ha);
> +int qla_edif_sadb_build_free_pool(struct qla_hw_data *ha);
> +void qla_edif_sadb_release_free_pool(struct qla_hw_data *ha);
> +void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
> +		srb_t *sp, struct sts_entry_24xx *sts24);
> +void qlt_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, fc_port_t *fcport,
> +		struct ctio7_from_24xx *ctio);
>   int qla_edif_process_els(scsi_qla_host_t *vha, struct bsg_job *bsgjob);
>   const char *sc_to_str(uint16_t cmd);
>   
> @@ -240,6 +247,8 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
>   			       struct purex_item *pkt);
>   void qla_pci_set_eeh_busy(struct scsi_qla_host *);
>   void qla_schedule_eeh_work(struct scsi_qla_host *);
> +struct edif_sa_ctl *qla_edif_find_sa_ctl_by_index(fc_port_t *fcport,
> +						  int index, int dir);
>   
>   /*
>    * Global Functions in qla_mid.c source file.
> @@ -315,6 +324,8 @@ extern int qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *, srb_t *,
>   	struct dsd64 *, uint16_t, struct qla_tgt_cmd *);
>   extern int qla24xx_get_one_block_sg(uint32_t, struct qla2_sgx *, uint32_t *);
>   extern int qla24xx_configure_prot_mode(srb_t *, uint16_t *);
> +extern int qla24xx_issue_sa_replace_iocb(scsi_qla_host_t *vha,
> +	struct qla_work_evt *e);
>   
>   /*
>    * Global Function Prototypes in qla_mbx.c source file.
> @@ -888,6 +899,9 @@ extern int qla2x00_issue_iocb_timeout(scsi_qla_host_t *, void *,
>   	dma_addr_t, size_t, uint32_t);
>   extern int qla2x00_get_idma_speed(scsi_qla_host_t *, uint16_t,
>   	uint16_t *, uint16_t *);
> +extern int qla24xx_sadb_update(struct bsg_job *bsg_job);
> +extern 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);
>   
>   /* 83xx related functions */
>   void qla83xx_fw_dump(scsi_qla_host_t *vha);
> @@ -960,12 +974,19 @@ extern void qla_nvme_abort_process_comp_status
>   
>   /* nvme.c */
>   void qla_nvme_unregister_remote_port(struct fc_port *fcport);
> +
> +/* qla_edif.c */
>   fc_port_t *qla2x00_find_fcport_by_pid(scsi_qla_host_t *vha, port_id_t *id);
>   void qla_edb_stop(scsi_qla_host_t *vha);
>   int32_t qla_edif_app_mgmt(struct bsg_job *bsg_job);
>   void qla_enode_init(scsi_qla_host_t *vha);
>   void qla_enode_stop(scsi_qla_host_t *vha);
> +void qla_edif_flush_sa_ctl_lists(fc_port_t *fcport);
> +void qla24xx_sa_update_iocb(srb_t *sp, struct sa_update_28xx *sa_update_iocb);
> +void qla24xx_sa_replace_iocb(srb_t *sp, struct sa_update_28xx *sa_update_iocb);
>   void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp);
> +void qla28xx_sa_update_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
> +		struct sa_update_28xx *pkt);
>   void qla_handle_els_plogi_done(scsi_qla_host_t *vha, struct event_arg *ea);
>   
>   #define QLA2XX_HW_ERROR			BIT_0
> diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
> index 0de250570e39..97da4ebadc33 100644
> --- a/drivers/scsi/qla2xxx/qla_init.c
> +++ b/drivers/scsi/qla2xxx/qla_init.c
> @@ -5073,6 +5073,17 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
>   	INIT_LIST_HEAD(&fcport->sess_cmd_list);
>   	spin_lock_init(&fcport->sess_cmd_lock);
>   
> +	spin_lock_init(&fcport->edif.sa_list_lock);
> +	INIT_LIST_HEAD(&fcport->edif.tx_sa_list);
> +	INIT_LIST_HEAD(&fcport->edif.rx_sa_list);
> +
> +	if (vha->e_dbell.db_flags == EDB_ACTIVE)
> +		fcport->edif.app_started = 1;
> +
> +	// edif rx delete data structure
> +	spin_lock_init(&fcport->edif.indx_list_lock);
> +	INIT_LIST_HEAD(&fcport->edif.edif_indx_list);
> +
>   	return fcport;
>   }
>   
> diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
> index 6f996fb5e8f9..168e7832bdfd 100644
> --- a/drivers/scsi/qla2xxx/qla_iocb.c
> +++ b/drivers/scsi/qla2xxx/qla_iocb.c
> @@ -3889,6 +3889,12 @@ qla2x00_start_sp(srb_t *sp)
>   	case SRB_PRLO_CMD:
>   		qla24xx_prlo_iocb(sp, pkt);
>   		break;
> +	case SRB_SA_UPDATE:
> +		qla24xx_sa_update_iocb(sp, pkt);
> +		break;
> +	case SRB_SA_REPLACE:
> +		qla24xx_sa_replace_iocb(sp, pkt);
> +		break;
>   	default:
>   		break;
>   	}
> diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
> index ea7635af03a8..dcbee5cf4306 100644
> --- a/drivers/scsi/qla2xxx/qla_isr.c
> +++ b/drivers/scsi/qla2xxx/qla_isr.c
> @@ -3174,6 +3174,8 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
>   	}
>   
>   	/* Fast path completion. */
> +	qla_chk_edif_rx_sa_delete_pending(vha, sp, sts24);
> +
>   	if (comp_status == CS_COMPLETE && scsi_status == 0) {
>   		qla2x00_process_completed_request(vha, req, handle);
>   
> @@ -3568,6 +3570,9 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
>   		}
>   		break;
>   
> +	case SA_UPDATE_IOCB_TYPE:
> +		return 1;	// let sa_update_iocb_entry cleanup everything
> +
>   	case ABTS_RESP_24XX:
>   	case CTIO_TYPE7:
>   	case CTIO_CRC2:
> @@ -3858,6 +3863,11 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
>   				       purex_entry->els_frame_payload[3]);
>   			}
>   			break;
> +		case SA_UPDATE_IOCB_TYPE:
> +			qla28xx_sa_update_iocb_entry(vha, rsp->req,
> +				(struct sa_update_28xx *)pkt);
> +			break;
> +
>   		default:
>   			/* Type Not Supported. */
>   			ql_dbg(ql_dbg_async, vha, 0x5042,
> diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
> index 6be06b994c43..51be079fadd7 100644
> --- a/drivers/scsi/qla2xxx/qla_os.c
> +++ b/drivers/scsi/qla2xxx/qla_os.c
> @@ -2835,6 +2835,20 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
>   	spin_lock_init(&ha->tgt.sess_lock);
>   	spin_lock_init(&ha->tgt.atio_lock);
>   
> +	// edif sadb
> +	spin_lock_init(&ha->sadb_lock);
> +	INIT_LIST_HEAD(&ha->sadb_tx_index_list);
> +	INIT_LIST_HEAD(&ha->sadb_rx_index_list);
> +
> +	// edif sa_index free pool
> +	spin_lock_init(&ha->sadb_fp_lock);
> +
> +	// build the sadb sa_index free pool
> +	if (qla_edif_sadb_build_free_pool(ha)) {
> +		kfree(ha);
> +		goto  disable_device;
> +	}
> +
>   	atomic_set(&ha->nvme_active_aen_cnt, 0);
>   
>   	/* Clear our data area */
> @@ -3868,6 +3882,9 @@ qla2x00_free_device(scsi_qla_host_t *vha)
>   
>   	qla82xx_md_free(vha);
>   
> +	qla_edif_sadb_release_free_pool(ha);
> +	qla_edif_sadb_release(ha);
> +
>   	qla2x00_free_queues(ha);
>   }
>   
> @@ -5375,6 +5392,9 @@ qla2x00_do_work(struct scsi_qla_host *vha)
>   			qla24xx_els_dcmd2_iocb(vha, ELS_DCMD_PLOGI,
>   			    e->u.fcport.fcport, false);
>   			break;
> +		case QLA_EVT_SA_REPLACE:
> +			qla24xx_issue_sa_replace_iocb(vha, e);
> +			break;
>   		}
>   
>   		if (rc == EAGAIN) {
> diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h
> index 01620f3eab39..8a319b78cdf6 100644
> --- a/drivers/scsi/qla2xxx/qla_target.h
> +++ b/drivers/scsi/qla2xxx/qla_target.h
> @@ -446,7 +446,7 @@ struct ctio7_from_24xx {
>   	uint8_t  vp_index;
>   	uint8_t  reserved1[5];
>   	__le32	exchange_address;
> -	__le16	reserved2;
> +	__le16	edif_sa_index;
>   	__le16	flags;
>   	__le32	residual;
>   	__le16	ox_id;
> 

-- 
Himanshu Madhani                                Oracle Linux Engineering

  parent reply	other threads:[~2021-06-03 22:56 UTC|newest]

Thread overview: 36+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-31  7:05 [PATCH v2 00/10] qla2xxx: Add EDIF support Nilesh Javali
2021-05-31  7:05 ` [PATCH v2 01/10] qla2xxx: Add start + stop bsg's Nilesh Javali
2021-06-01 12:33   ` Hannes Reinecke
2021-06-04 18:40     ` Quinn Tran
2021-06-03 18:09   ` Himanshu Madhani
2021-06-04 19:04     ` Quinn Tran
2021-05-31  7:05 ` [PATCH v2 02/10] qla2xxx: Add getfcinfo and statistic bsg's Nilesh Javali
2021-06-01 12:35   ` Hannes Reinecke
2021-06-03 18:35   ` Himanshu Madhani
2021-06-04 19:45     ` Quinn Tran
2021-05-31  7:05 ` [PATCH v2 03/10] qla2xxx: Add send, receive and accept for auth_els Nilesh Javali
2021-06-01 12:47   ` Hannes Reinecke
2021-06-04 19:41     ` Quinn Tran
2021-06-03 20:43   ` Himanshu Madhani
2021-05-31  7:05 ` [PATCH v2 04/10] qla2xxx: Add extraction of auth_els from the wire Nilesh Javali
2021-06-01 12:51   ` Hannes Reinecke
2021-06-03 21:10   ` Himanshu Madhani
2021-05-31  7:05 ` [PATCH v2 05/10] qla2xxx: Add key update Nilesh Javali
2021-06-01 13:02   ` Hannes Reinecke
2021-06-11 18:30     ` Quinn Tran
2021-06-03 22:55   ` Himanshu Madhani [this message]
2021-05-31  7:05 ` [PATCH v2 06/10] qla2xxx: Add authentication pass + fail bsg's Nilesh Javali
2021-06-01 13:03   ` Hannes Reinecke
2021-06-04 12:56   ` Himanshu Madhani
2021-05-31  7:05 ` [PATCH v2 07/10] qla2xxx: Add detection of secure device Nilesh Javali
2021-06-01 13:07   ` Hannes Reinecke
2021-06-04 13:18   ` Himanshu Madhani
2021-05-31  7:05 ` [PATCH v2 08/10] qla2xxx: Add doorbell notification for app Nilesh Javali
2021-06-01 13:11   ` Hannes Reinecke
2021-06-11 20:53     ` Quinn Tran
2021-06-04 14:46   ` Himanshu Madhani
2021-05-31  7:05 ` [PATCH v2 09/10] qla2xxx: Add encryption to IO path Nilesh Javali
2021-06-01 13:14   ` Hannes Reinecke
2021-06-04 16:09   ` Himanshu Madhani
2021-05-31  7:05 ` [PATCH v2 10/10] qla2xxx: Update version to 10.02.00.107-k Nilesh Javali
2021-06-01 18:09 ` [PATCH v2 00/10] qla2xxx: Add EDIF support Himanshu Madhani

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2a1e742c-1e77-98e4-a7ca-6bd8ebd16d12@oracle.com \
    --to=himanshu.madhani@oracle.com \
    --cc=GR-QLogic-Storage-Upstream@marvell.com \
    --cc=linux-scsi@vger.kernel.org \
    --cc=martin.petersen@oracle.com \
    --cc=njavali@marvell.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).