linux-stable/drivers/scsi/qla2xxx/qla_edif.c

3478 lines
98 KiB
C
Raw Normal View History

// 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>
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
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";
}
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
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,
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
"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->flags & FCF_FCSP_DEVICE)) {
ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x2058,
"Found secure fcport - nn %8phN pn %8phN portid=0x%x, 0x%x.\n",
f->node_name, f->port_name,
f->d_id.b24, id->b24);
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 + ql_dbg_verbose, vha, 0x911d, "%s app id ok\n", __func__);
return true;
}
ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app id not ok (%x)",
__func__, appid.app_vid);
return false;
}
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
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) {
del_timer(&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);
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
}
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_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 {
ql_dbg(ql_dbg_edif, vha, 0x911e, "%s doorbell already active\n",
__func__);
}
if (N2N_TOPO(vha->hw)) {
if (vha->hw->flags.n2n_fw_acc_sec)
set_bit(N2N_LINK_RESET, &vha->dpc_flags);
else
set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
qla2xxx_wake_dpc(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->edif.app_started = 1;
fcport->login_retry = vha->hw->login_retry_count;
/* no activity */
fcport->edif.app_stop = 0;
ql_dbg(ql_dbg_edif, vha, 0x911e,
"%s wwpn %8phC calling qla_edif_reset_auth_wait\n",
__func__, fcport->port_name);
fcport->edif.app_sess_online = 0;
qlt_schedule_sess_for_deletion(fcport);
qla_edif_sa_ctl_init(vha, fcport);
}
}
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__);
}
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;
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);
/* qla_edif_flush_sa_ctl_lists(fcport); */
fcport->edif.app_started = 0;
}
}
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)
{
int32_t rval = 0;
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));
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, &appplogiok.u.d_id);
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 rval;
}
/**
* 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));
/*
* 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, &appplogifail.u.d_id);
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;
list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) {
if (!(fcport->flags & FCF_FCSP_DEVICE))
continue;
tdid = app_req.remote_pid;
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;
app_reply->ports[pcnt].rekey_count =
fcport->edif.rekey_cnt;
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;
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;
}
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];
ql_dbg(ql_dbg_edif, 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(ql_dbg_edif, 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(ql_dbg_edif, 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) {
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
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;
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(ql_dbg_user, 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;
}
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
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);
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
/* 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
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 (DBELL_INACTIVE(vha)) {
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
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)
{
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);
/* create and initialize doorbell */
init_completion(&vha->e_dbell.dbell);
}
static void
qla_edb_node_free(scsi_qla_host_t *vha, struct edb_node *node)
{
/*
* releases the space held by this edb node entry
* this function does _not_ free the edb node itself
* NB: the edb node entry passed should not be on any list
*
* currently for doorbell there's no additional cleanup
* needed, but here as a placeholder for furture use.
*/
if (!node) {
ql_dbg(ql_dbg_edif, vha, 0x09122,
"%s error - no valid node passed\n", __func__);
return;
}
node->ntype = N_UNDEF;
}
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);
list_del_init(&e->list);
kfree(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);
list_del(&node->list);
kfree(node);
}
spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags);
/* wake up doorbell waiters - they'll be dismissed with error code */
complete_all(&vha->e_dbell.dbell);
}
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);
/* ring doorbell for waiters */
complete(&vha->e_dbell.dbell);
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;
break;
default:
ql_dbg(ql_dbg_edif, vha, 0x09102,
"%s unknown type: %x\n", __func__, dbtype);
qla_edb_node_free(vha, edbnode);
kfree(edbnode);
edbnode = NULL;
break;
}
if (edbnode && (!qla_edb_node_add(vha, edbnode))) {
ql_dbg(ql_dbg_edif, vha, 0x09102,
"%s unable to add dbnode\n", __func__);
qla_edb_node_free(vha, edbnode);
kfree(edbnode);
return;
}
if (edbnode && fcport)
fcport->edif.auth_state = dbtype;
ql_dbg(ql_dbg_edif, vha, 0x09102,
"%s Doorbell produced : type=%d %p\n", __func__, dbtype, edbnode);
}
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(&edbnode->list);
}
spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags);
return edbnode;
}
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;
}
}
}
/*
* app uses separate thread to read this. It'll wait until the doorbell
* is rung by the driver or the max wait time has expired
*/
ssize_t
edif_doorbell_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
struct edb_node *dbnode = NULL;
struct edif_app_dbell *ap = (struct edif_app_dbell *)buf;
uint32_t dat_siz, buf_size, sz;
/* TODO: app currently hardcoded to 256. Will transition to bsg */
sz = 256;
/* stop new threads from waiting if we're not init'd */
if (DBELL_INACTIVE(vha)) {
ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x09122,
"%s error - edif db not enabled\n", __func__);
return 0;
}
if (!vha->hw->flags.edif_enabled) {
/* edif not enabled */
ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x09122,
"%s error - edif not enabled\n", __func__);
return -1;
}
buf_size = 0;
while ((sz - buf_size) >= sizeof(struct edb_node)) {
/* remove the next item from the doorbell list */
dat_siz = 0;
dbnode = qla_edb_getnext(vha);
if (dbnode) {
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_siz += sizeof(ap->port_id);
break;
case VND_CMD_AUTH_STATE_ELS_RCVD:
ap->port_id = dbnode->u.els_sid;
dat_siz += 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_siz += sizeof(struct edif_sa_update_aen);
break;
default:
/* unknown node type, rtn unknown ntype */
ap->event_code = VND_CMD_AUTH_STATE_UNDEF;
memcpy(ap->event_data, &dbnode->ntype, 4);
dat_siz += 4;
break;
}
ql_dbg(ql_dbg_edif, vha, 0x09102,
"%s Doorbell consumed : type=%d %p\n",
__func__, dbnode->ntype, dbnode);
/* we're done with the db node, so free it up */
qla_edb_node_free(vha, dbnode);
kfree(dbnode);
} else {
break;
}
ap->event_data_size = dat_siz;
/* 8bytes = ap->event_code + ap->event_data_size */
buf_size += dat_siz + 8;
ap = (struct edif_app_dbell *)(buf + buf_size);
}
return buf_size;
}
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
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];
} 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) ||
(fcport && EDIF_SESSION_DOWN(fcport))) {
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;
}
/* 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);
}
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
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);
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;
qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_SAUPDATE_COMPL,
QL_VND_SA_STAT_SUCCESS,
QL_VND_TX_SA_KEY, sp->fcport);
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
} 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);
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
}
} 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);
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
}
/* 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;
}
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
}
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);
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;
}
ctx = sp->u.scmd.ct6_ctx =
mempool_alloc(ha->ctx_mempool, GFP_ATOMIC);
if (!ctx) {
ql_log(ql_log_fatal, vha, 0x3010,
"Failed to allocate ctx for cmd=%p.\n", cmd);
goto queuing_error;
}
memset(ctx, 0, sizeof(struct ct6_dsd));
ctx->fcp_cmnd = dma_pool_zalloc(ha->fcp_cmnd_dma_pool,
GFP_ATOMIC, &ctx->fcp_cmnd_dma);
if (!ctx->fcp_cmnd) {
ql_log(ql_log_fatal, vha, 0x3011,
"Failed to allocate fcp_cmnd for cmd=%p.\n", cmd);
goto queuing_error;
}
/* Initialize the DSD list and dma handle */
INIT_LIST_HEAD(&ctx->dsd_list);
ctx->dsd_use_cnt = 0;
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);
sp->flags |= SRB_FCP_CMND_DMA_VALID;
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:
dma_pool_free(ha->fcp_cmnd_dma_pool, ctx->fcp_cmnd, ctx->fcp_cmnd_dma);
queuing_error:
if (tot_dsds)
scsi_dma_unmap(cmd);
if (sp->u.scmd.ct6_ctx) {
mempool_free(sp->u.scmd.ct6_ctx, ha->ctx_mempool);
sp->u.scmd.ct6_ctx = NULL;
}
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);
}
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
/******************
* 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;
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
list_for_each_entry_safe(entry, tmp, &ha->sadb_rx_index_list, next) {
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
list_del(&entry->next);
kfree(entry);
}
list_for_each_entry_safe(entry, tmp, &ha->sadb_tx_index_list, next) {
scsi: qla2xxx: edif: Add key update Some FC adapters from Marvell offer the ability to encrypt data in flight (EDIF). This feature requires an application 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 passed 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. Add a new bsg call (QL_VND_SC_SA_UPDATE) to allow application to allow adding or deleting SADB entries. Driver will not keep the key in memory. It will pass it to HW. It is assumed that application will assign a unique SPI value to this SADB (SA + key). Driver + hardware will assign a handle to track this unique SPI/SADB. Link: https://lore.kernel.org/r/20210624052606.21613-6-njavali@marvell.com Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Co-developed-by: Larry Wisneski <Larry.Wisneski@marvell.com> Signed-off-by: Larry Wisneski <Larry.Wisneski@marvell.com> Co-developed-by: Duane Grigsby <duane.grigsby@marvell.com> Signed-off-by: Duane Grigsby <duane.grigsby@marvell.com> Co-developed-by: Rick Hicksted Jr <rhicksted@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> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-06-24 05:26:00 +00:00
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);
port_id_t d_id;
struct qla_bsg_auth_els_request *p =
(struct qla_bsg_auth_els_request *)bsg_job->request;
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 (fcport->loop_id == FC_NO_LOOP_ID) {
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;
rval = qla2x00_start_sp(sp);
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);
if (rval != QLA_SUCCESS) {
ql_log(ql_log_warn, vha, 0x700e,
"qla2x00_start_sp failed = %d\n", 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)
{
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;
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);
}
}
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);
}