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

2794 lines
80 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 *);
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 (vha->e_dbell.db_flags != EDB_ACTIVE) {
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;
}
static void qla_edif_reset_auth_wait(struct fc_port *fcport, int state,
int waitonly)
{
int cnt, max_cnt = 200;
bool traced = false;
fcport->keep_nport_handle = 1;
if (!waitonly) {
qla2x00_set_fcport_disc_state(fcport, state);
qlt_schedule_sess_for_deletion(fcport);
} else {
qla2x00_set_fcport_disc_state(fcport, state);
}
ql_dbg(ql_dbg_edif, fcport->vha, 0xf086,
"%s: waiting for session, max_cnt=%u\n",
__func__, max_cnt);
cnt = 0;
if (waitonly) {
/* Marker wait min 10 msecs. */
msleep(50);
cnt += 50;
}
while (1) {
if (!traced) {
ql_dbg(ql_dbg_edif, fcport->vha, 0xf086,
"%s: session sleep.\n",
__func__);
traced = true;
}
msleep(20);
cnt++;
if (waitonly && (fcport->disc_state == state ||
fcport->disc_state == DSC_LOGIN_COMPLETE))
break;
if (fcport->disc_state == DSC_LOGIN_AUTH_PEND)
break;
if (cnt > max_cnt)
break;
}
if (!waitonly) {
ql_dbg(ql_dbg_edif, fcport->vha, 0xf086,
"%s: waited for session - %8phC, loopid=%x portid=%06x fcport=%p state=%u, cnt=%u\n",
__func__, fcport->port_name, fcport->loop_id,
fcport->d_id.b24, fcport, fcport->disc_state, cnt);
} else {
ql_dbg(ql_dbg_edif, fcport->vha, 0xf086,
"%s: waited ONLY for session - %8phC, loopid=%x portid=%06x fcport=%p state=%u, cnt=%u\n",
__func__, fcport->port_name, fcport->loop_id,
fcport->d_id.b24, fcport, fcport->disc_state, cnt);
}
}
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;
}
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_dbg(ql_dbg_edif, vha, 0x911d, "%s app start\n", __func__);
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 (vha->e_dbell.db_flags != EDB_ACTIVE) {
/* 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__);
}
list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) {
if ((fcport->flags & FCF_FCSP_DEVICE)) {
ql_dbg(ql_dbg_edif, vha, 0xf084,
"%s: sess %p %8phC lid %#04x s_id %06x logout %d\n",
__func__, fcport, fcport->port_name,
fcport->loop_id, fcport->d_id.b24,
fcport->logout_on_delete);
if (atomic_read(&vha->loop_state) == LOOP_DOWN)
break;
if (!fcport->edif.secured_login)
continue;
fcport->edif.app_started = 1;
if (fcport->edif.app_stop ||
(fcport->disc_state != DSC_LOGIN_COMPLETE &&
fcport->disc_state != DSC_LOGIN_PEND &&
fcport->disc_state != DSC_DELETED)) {
/* 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 = 1;
qla_edif_reset_auth_wait(fcport, DSC_LOGIN_PEND, 0);
}
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) +
sizeof(struct app_start_reply);
SET_DID_STATUS(bsg_reply->result, DID_OK);
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)
{
int32_t rval = 0;
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->edif.non_secured_login)
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 rval;
}
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;
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);
qla_edif_reset_auth_wait(fcport, DSC_LOGIN_PEND, 1);
qla24xx_post_prli_work(vha, fcport);
}
errstate_exit:
bsg_job->reply_len = sizeof(struct fc_bsg_reply);
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;
qla_edif_reset_auth_wait(fcport, DSC_LOGIN_PEND, 0);
}
}
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 num_cnt = 1;
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));
num_cnt = app_req.num_ports; /* num of ports alloc'd by app */
app_reply = kzalloc((sizeof(struct app_pinfo_reply) +
sizeof(struct app_pinfo) * num_cnt), GFP_KERNEL);
if (!app_reply) {
SET_DID_STATUS(bsg_reply->result, DID_ERROR);
rval = -1;
} else {
struct fc_port *fcport = NULL, *tf;
uint32_t pcnt = 0;
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].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\n",
fcport->node_name, fcport->port_name, pcnt, fcport->d_id.b24);
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);
}
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) * num_cnt);
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 ret_size, size;
struct app_sinfo_req app_req;
struct app_stats_reply *app_reply;
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);
if (size > bsg_job->reply_payload.payload_len)
ret_size = bsg_job->reply_payload.payload_len;
else
ret_size = size;
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;
uint32_t pcnt = 0;
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_reply->reply_payload_rcv_len =
sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
bsg_job->reply_payload.sg_cnt, app_reply, ret_size);
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\n",
__func__, bsg_job);
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;
bsg_job->reply_len = sizeof(struct fc_bsg_reply);
SET_DID_STATUS(bsg_reply->result, DID_ERROR);
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;
/* 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 (vha->e_dbell.db_flags != EDB_ACTIVE) {
ql_log(ql_log_warn, vha, 0x70a1, "App not started\n");
rval = -EIO;
SET_DID_STATUS(bsg_reply->result, DID_ERROR);
goto done;
}
fcport = qla2x00_find_fcport_by_pid(vha, &sa_frame.port_id);
if (fcport) {
found = 1;
if (sa_frame.flags == QLA_SA_UPDATE_FLAGS_TX_KEY)
fcport->edif.tx_bytes = 0;
if (sa_frame.flags == QLA_SA_UPDATE_FLAGS_RX_KEY)
fcport->edif.rx_bytes = 0;
}
if (!found) {
ql_dbg(ql_dbg_edif, vha, 0x70a3, "Failed to find port= %06x\n",
sa_frame.port_id.b24);
rval = -EINVAL;
SET_DID_STATUS(bsg_reply->result, DID_TARGET_FAILURE);
goto done;
}
/* make sure the nport_handle is valid */
if (fcport->loop_id == FC_NO_LOOP_ID) {
ql_dbg(ql_dbg_edif, vha, 0x70e1,
"%s: %8phN lid=FC_NO_LOOP_ID, spi: 0x%x, DS %d, returning NO_CONNECT\n",
__func__, fcport->port_name, sa_frame.spi,
fcport->disc_state);
rval = -EINVAL;
SET_DID_STATUS(bsg_reply->result, DID_NO_CONNECT);
goto done;
}
/* allocate and queue an sa_ctl */
result = qla24xx_check_sadb_avail_slot(bsg_job, fcport, &sa_frame);
/* failure of bsg */
if (result == INVALID_EDIF_SA_INDEX) {
ql_dbg(ql_dbg_edif, vha, 0x70e1,
"%s: %8phN, skipping update.\n",
__func__, fcport->port_name);
rval = -EINVAL;
SET_DID_STATUS(bsg_reply->result, DID_ERROR);
goto done;
/* rx delete failure */
} else if (result == RX_DELETE_NO_EDIF_SA_INDEX) {
ql_dbg(ql_dbg_edif, vha, 0x70e1,
"%s: %8phN, skipping rx delete.\n",
__func__, fcport->port_name);
SET_DID_STATUS(bsg_reply->result, DID_OK);
goto done;
}
ql_dbg(ql_dbg_edif, vha, 0x70e1,
"%s: %8phN, sa_index in sa_frame: %d flags %xh\n",
__func__, fcport->port_name, sa_frame.fast_sa_index,
sa_frame.flags);
/* looking for rx index and delete */
if (((sa_frame.flags & SAU_FLG_TX) == 0) &&
(sa_frame.flags & SAU_FLG_INV)) {
uint16_t nport_handle = fcport->loop_id;
uint16_t sa_index = sa_frame.fast_sa_index;
/*
* make sure we have an existing rx key, otherwise just process
* this as a straight delete just like TX
* This is NOT a normal case, it indicates an error recovery or key cleanup
* by the ipsec code above us.
*/
edif_entry = qla_edif_list_find_sa_index(fcport, fcport->loop_id);
if (!edif_entry) {
ql_dbg(ql_dbg_edif, vha, 0x911d,
"%s: WARNING: no active sa_index for nport_handle 0x%x, forcing delete for sa_index 0x%x\n",
__func__, fcport->loop_id, sa_index);
goto force_rx_delete;
}
/*
* if we have a forced delete for rx, remove the sa_index from the edif list
* and proceed with normal delete. The rx delay timer should not be running
*/
if ((sa_frame.flags & SAU_FLG_FORCE_DELETE) == SAU_FLG_FORCE_DELETE) {
qla_edif_list_delete_sa_index(fcport, edif_entry);
ql_dbg(ql_dbg_edif, vha, 0x911d,
"%s: FORCE DELETE flag found for nport_handle 0x%x, sa_index 0x%x, forcing DELETE\n",
__func__, fcport->loop_id, sa_index);
kfree(edif_entry);
goto force_rx_delete;
}
/*
* delayed rx delete
*
* if delete_sa_index is not invalid then there is already
* a delayed index in progress, return bsg bad status
*/
if (edif_entry->delete_sa_index != INVALID_EDIF_SA_INDEX) {
struct edif_sa_ctl *sa_ctl;
ql_dbg(ql_dbg_edif, vha, 0x911d,
"%s: delete for lid 0x%x, delete_sa_index %d is pending\n",
__func__, edif_entry->handle, edif_entry->delete_sa_index);
/* free up the sa_ctl that was allocated with the sa_index */
sa_ctl = qla_edif_find_sa_ctl_by_index(fcport, sa_index,
(sa_frame.flags & SAU_FLG_TX));
if (sa_ctl) {
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: freeing sa_ctl for index %d\n",
__func__, sa_ctl->index);
qla_edif_free_sa_ctl(fcport, sa_ctl, sa_ctl->index);
}
/* release the sa_index */
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: freeing sa_index %d, nph: 0x%x\n",
__func__, sa_index, nport_handle);
qla_edif_sadb_delete_sa_index(fcport, nport_handle, sa_index);
rval = -EINVAL;
SET_DID_STATUS(bsg_reply->result, DID_ERROR);
goto done;
}
fcport->edif.rekey_cnt++;
/* configure and start the rx delay timer */
edif_entry->fcport = fcport;
edif_entry->timer.expires = jiffies + RX_DELAY_DELETE_TIMEOUT * HZ;
ql_dbg(ql_dbg_edif, vha, 0x911d,
"%s: adding timer, entry: %p, delete sa_index %d, lid 0x%x to edif_list\n",
__func__, edif_entry, sa_index, nport_handle);
/*
* Start the timer when we queue the delayed rx delete.
* This is an activity timer that goes off if we have not
* received packets with the new sa_index
*/
add_timer(&edif_entry->timer);
/*
* sa_delete for rx key with an active rx key including this one
* add the delete rx sa index to the hash so we can look for it
* in the rsp queue. Do this after making any changes to the
* edif_entry as part of the rx delete.
*/
ql_dbg(ql_dbg_edif, vha, 0x911d,
"%s: delete sa_index %d, lid 0x%x to edif_list. bsg done ptr %p\n",
__func__, sa_index, nport_handle, bsg_job);
edif_entry->delete_sa_index = sa_index;
bsg_job->reply_len = sizeof(struct fc_bsg_reply);
bsg_reply->result = DID_OK << 16;
goto done;
/*
* rx index and update
* add the index to the list and continue with normal update
*/
} else if (((sa_frame.flags & SAU_FLG_TX) == 0) &&
((sa_frame.flags & SAU_FLG_INV) == 0)) {
/* sa_update for rx key */
uint32_t nport_handle = fcport->loop_id;
uint16_t sa_index = sa_frame.fast_sa_index;
int result;
/*
* add the update rx sa index to the hash so we can look for it
* in the rsp queue and continue normally
*/
ql_dbg(ql_dbg_edif, vha, 0x911d,
"%s: adding update sa_index %d, lid 0x%x to edif_list\n",
__func__, sa_index, nport_handle);
result = qla_edif_list_add_sa_update_index(fcport, sa_index,
nport_handle);
if (result) {
ql_dbg(ql_dbg_edif, vha, 0x911d,
"%s: SA_UPDATE failed to add new sa index %d to list for lid 0x%x\n",
__func__, sa_index, nport_handle);
}
}
if (sa_frame.flags & SAU_FLG_GMAC_MODE)
fcport->edif.aes_gmac = 1;
else
fcport->edif.aes_gmac = 0;
force_rx_delete:
/*
* sa_update for both rx and tx keys, sa_delete for tx key
* immediately process the request
*/
sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
if (!sp) {
rval = -ENOMEM;
SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY);
goto done;
}
sp->type = SRB_SA_UPDATE;
sp->name = "bsg_sa_update";
sp->u.bsg_job = bsg_job;
/* sp->free = qla2x00_bsg_sp_free; */
sp->free = qla2x00_rel_sp;
sp->done = qla2x00_bsg_job_done;
iocb_cmd = &sp->u.iocb_cmd;
iocb_cmd->u.sa_update.sa_frame = sa_frame;
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS) {
ql_log(ql_dbg_edif, vha, 0x70e3,
"qla2x00_start_sp failed=%d.\n", rval);
qla2x00_rel_sp(sp);
rval = -EIO;
SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY);
goto done;
}
ql_dbg(ql_dbg_edif, vha, 0x911d,
"%s: %s sent, hdl=%x, portid=%06x.\n",
__func__, sp->name, sp->handle, fcport->d_id.b24);
fcport->edif.rekey_cnt++;
bsg_job->reply_len = sizeof(struct fc_bsg_reply);
SET_DID_STATUS(bsg_reply->result, DID_OK);
return 0;
/*
* send back error status
*/
done:
bsg_job->reply_len = sizeof(struct fc_bsg_reply);
ql_dbg(ql_dbg_edif, vha, 0x911d,
"%s:status: FAIL, result: 0x%x, bsg ptr done %p\n",
__func__, bsg_reply->result, bsg_job);
bsg_job_done(bsg_job, bsg_reply->result,
bsg_reply->reply_payload_rcv_len);
return 0;
}
static void
qla_enode_free(scsi_qla_host_t *vha, struct enode *node)
{
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);
}
/*
* 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 = NULL;
unsigned long flags;
struct list_head *pos, *q;
uint32_t sid;
uint32_t rw_flag;
struct purexevent *purex;
/* secure the list from moving under us */
spin_lock_irqsave(&vha->pur_cinfo.pur_lock, flags);
list_for_each_safe(pos, q, &vha->pur_cinfo.head) {
list_node = list_entry(pos, struct enode, list);
/* node type determines what p1 and p2 are */
purex = &list_node->u.purexinfo;
sid = p1;
rw_flag = p2;
if (purex->pur_info.pur_sid.b24 == sid) {
if (purex->pur_info.pur_pend == 1 &&
rw_flag == PUR_GET) {
/*
* if the receive is in progress
* and its a read/get then can't
* transfer yet
*/
ql_dbg(ql_dbg_edif, vha, 0x9106,
"%s purex xfer in progress for sid=%x\n",
__func__, sid);
} else {
/* found it and its complete */
node_rtn = list_node;
list_del(pos);
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...\n");
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;
}
/* function called when app is stopping */
void
qla_edb_stop(scsi_qla_host_t *vha)
{
if (vha->e_dbell.db_flags != EDB_ACTIVE) {
/* doorbell list not enabled */
ql_dbg(ql_dbg_edif, vha, 0x09102,
"%s doorbell not enabled\n", __func__);
return;
}
}
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];
ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x921f, "%s 256 sa key=%32phN\n",
__func__, sa_update_iocb->sa_key);
} else {
sa_update_iocb->sa_control |= SA_CNTL_KEY128;
for (itr = 0; itr < 16; itr++)
sa_update_iocb->sa_key[itr] = sa_frame->sa_key[itr];
ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x921f, "%s 128 sa key=%16phN\n",
__func__, sa_update_iocb->sa_key);
}
ql_dbg(ql_dbg_edif, vha, 0x921d,
"%s SAU Port ID = %02x%02x%02x, flags=%xh, index=%u, ctl=%xh, SPI 0x%x 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;
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 > 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 allloc 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_pend = 0;
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;
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 (host->e_dbell.db_flags != EDB_ACTIVE ||
(fcport && fcport->loop_id == FC_NO_LOOP_ID)) {
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, p->rx_xchg_addr);
}
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;
} else {
sp->fcport->edif.rx_sa_set = 1;
sp->fcport->edif.rx_sa_pending = 0;
}
} else {
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: %8phN SA update FAILED: sa_index: %d, new_sa_info %d, %02x%02x%02x\n",
__func__, sp->fcport->port_name, pkt->sa_index, pkt->new_sa_info,
pkt->port_id[2], pkt->port_id[1], pkt->port_id[0]);
}
/* for delete, release sa_ctl, sa_index */
if (pkt->flags & SA_FLAG_INVALIDATE) {
/* release the sa_ctl */
sa_ctl = qla_edif_find_sa_ctl_by_index(sp->fcport,
le16_to_cpu(pkt->sa_index), (pkt->flags & SA_FLAG_TX));
if (sa_ctl &&
qla_edif_find_sa_ctl_by_index(sp->fcport, sa_ctl->index,
(pkt->flags & SA_FLAG_TX)) != NULL) {
ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x3063,
"%s: freeing sa_ctl for index %d\n",
__func__, sa_ctl->index);
qla_edif_free_sa_ctl(sp->fcport, sa_ctl, sa_ctl->index);
} else {
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: sa_ctl NOT freed, sa_ctl: %p\n",
__func__, sa_ctl);
}
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: freeing sa_index %d, nph: 0x%x\n",
__func__, le16_to_cpu(pkt->sa_index), nport_handle);
qla_edif_sadb_delete_sa_index(sp->fcport, nport_handle,
le16_to_cpu(pkt->sa_index));
/*
* check for a failed sa_update and remove
* the sadb entry.
*/
} else if (pkt->u.comp_sts) {
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: freeing sa_index %d, nph: 0x%x\n",
__func__, pkt->sa_index, nport_handle);
qla_edif_sadb_delete_sa_index(sp->fcport, nport_handle,
le16_to_cpu(pkt->sa_index));
}
sp->done(sp, 0);
}
/******************
* SADB functions *
******************/
/* allocate/retrieve an sa_index for a given spi */
static uint16_t qla_edif_sadb_get_sa_index(fc_port_t *fcport,
struct qla_sa_update_frame *sa_frame)
{
struct edif_sa_index_entry *entry;
struct list_head *sa_list;
uint16_t sa_index;
int dir = sa_frame->flags & SAU_FLG_TX;
int slot = 0;
int free_slot = -1;
scsi_qla_host_t *vha = fcport->vha;
struct qla_hw_data *ha = vha->hw;
unsigned long flags = 0;
uint16_t nport_handle = fcport->loop_id;
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: entry fc_port: %p, nport_handle: 0x%x\n",
__func__, fcport, nport_handle);
if (dir)
sa_list = &ha->sadb_tx_index_list;
else
sa_list = &ha->sadb_rx_index_list;
entry = qla_edif_sadb_find_sa_index_entry(nport_handle, sa_list);
if (!entry) {
if ((sa_frame->flags & (SAU_FLG_TX | SAU_FLG_INV)) == SAU_FLG_INV) {
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: rx delete request with no entry\n", __func__);
return RX_DELETE_NO_EDIF_SA_INDEX;
}
/* if there is no entry for this nport, add one */
entry = kzalloc((sizeof(struct edif_sa_index_entry)), GFP_ATOMIC);
if (!entry)
return INVALID_EDIF_SA_INDEX;
sa_index = qla_edif_get_sa_index_from_freepool(fcport, dir);
if (sa_index == INVALID_EDIF_SA_INDEX) {
kfree(entry);
return INVALID_EDIF_SA_INDEX;
}
INIT_LIST_HEAD(&entry->next);
entry->handle = nport_handle;
entry->fcport = fcport;
entry->sa_pair[0].spi = sa_frame->spi;
entry->sa_pair[0].sa_index = sa_index;
entry->sa_pair[1].spi = 0;
entry->sa_pair[1].sa_index = INVALID_EDIF_SA_INDEX;
spin_lock_irqsave(&ha->sadb_lock, flags);
list_add_tail(&entry->next, sa_list);
spin_unlock_irqrestore(&ha->sadb_lock, flags);
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: Created new sadb entry for nport_handle 0x%x, spi 0x%x, returning sa_index %d\n",
__func__, nport_handle, sa_frame->spi, sa_index);
return sa_index;
}
spin_lock_irqsave(&ha->sadb_lock, flags);
/* see if we already have an entry for this spi */
for (slot = 0; slot < 2; slot++) {
if (entry->sa_pair[slot].sa_index == INVALID_EDIF_SA_INDEX) {
free_slot = slot;
} else {
if (entry->sa_pair[slot].spi == sa_frame->spi) {
spin_unlock_irqrestore(&ha->sadb_lock, flags);
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: sadb slot %d entry for lid 0x%x, spi 0x%x found, sa_index %d\n",
__func__, slot, entry->handle, sa_frame->spi,
entry->sa_pair[slot].sa_index);
return entry->sa_pair[slot].sa_index;
}
}
}
spin_unlock_irqrestore(&ha->sadb_lock, flags);
/* both slots are used */
if (free_slot == -1) {
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: WARNING: No free slots in sadb for nport_handle 0x%x, spi: 0x%x\n",
__func__, entry->handle, sa_frame->spi);
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: Slot 0 spi: 0x%x sa_index: %d, 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 list_head *pos;
struct list_head *tmp;
struct edif_sa_index_entry *entry;
list_for_each_safe(pos, tmp, &ha->sadb_rx_index_list) {
entry = list_entry(pos, struct edif_sa_index_entry, next);
list_del(&entry->next);
kfree(entry);
}
list_for_each_safe(pos, tmp, &ha->sadb_tx_index_list) {
entry = list_entry(pos, struct edif_sa_index_entry, next);
list_del(&entry->next);
kfree(entry);
}
}
/**************************
* sadb freepool functions
**************************/
/* build the rx and tx sa_index free pools -- only done at fcport init */
int qla_edif_sadb_build_free_pool(struct qla_hw_data *ha)
{
ha->edif_tx_sa_id_map =
kcalloc(BITS_TO_LONGS(EDIF_NUM_SA_INDEX), sizeof(long), GFP_KERNEL);
if (!ha->edif_tx_sa_id_map) {
ql_log_pci(ql_log_fatal, ha->pdev, 0x0009,
"Unable to allocate memory for sadb tx.\n");
return -ENOMEM;
}
ha->edif_rx_sa_id_map =
kcalloc(BITS_TO_LONGS(EDIF_NUM_SA_INDEX), sizeof(long), GFP_KERNEL);
if (!ha->edif_rx_sa_id_map) {
kfree(ha->edif_tx_sa_id_map);
ha->edif_tx_sa_id_map = NULL;
ql_log_pci(ql_log_fatal, ha->pdev, 0x0009,
"Unable to allocate memory for sadb rx.\n");
return -ENOMEM;
}
return 0;
}
/* release the free pool - only done during fcport teardown */
void qla_edif_sadb_release_free_pool(struct qla_hw_data *ha)
{
kfree(ha->edif_tx_sa_id_map);
ha->edif_tx_sa_id_map = NULL;
kfree(ha->edif_rx_sa_id_map);
ha->edif_rx_sa_id_map = NULL;
}
static void __chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
fc_port_t *fcport, uint32_t handle, uint16_t sa_index)
{
struct edif_list_entry *edif_entry;
struct edif_sa_ctl *sa_ctl;
uint16_t delete_sa_index = INVALID_EDIF_SA_INDEX;
unsigned long flags = 0;
uint16_t nport_handle = fcport->loop_id;
uint16_t cached_nport_handle;
spin_lock_irqsave(&fcport->edif.indx_list_lock, flags);
edif_entry = qla_edif_list_find_sa_index(fcport, nport_handle);
if (!edif_entry) {
spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
return; /* no pending delete for this handle */
}
/*
* check for no pending delete for this index or iocb does not
* match rx sa_index
*/
if (edif_entry->delete_sa_index == INVALID_EDIF_SA_INDEX ||
edif_entry->update_sa_index != sa_index) {
spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
return;
}
/*
* wait until we have seen at least EDIF_DELAY_COUNT transfers before
* queueing RX delete
*/
if (edif_entry->count++ < EDIF_RX_DELETE_FILTER_COUNT) {
spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
return;
}
ql_dbg(ql_dbg_edif, vha, 0x5033,
"%s: invalidating delete_sa_index, update_sa_index: 0x%x sa_index: 0x%x, delete_sa_index: 0x%x\n",
__func__, edif_entry->update_sa_index, sa_index, edif_entry->delete_sa_index);
delete_sa_index = edif_entry->delete_sa_index;
edif_entry->delete_sa_index = INVALID_EDIF_SA_INDEX;
cached_nport_handle = edif_entry->handle;
spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
/* sanity check on the nport handle */
if (nport_handle != cached_nport_handle) {
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: POST SA DELETE nport_handle mismatch: lid: 0x%x, edif_entry nph: 0x%x\n",
__func__, nport_handle, cached_nport_handle);
}
/* find the sa_ctl for the delete and schedule the delete */
sa_ctl = qla_edif_find_sa_ctl_by_index(fcport, delete_sa_index, 0);
if (sa_ctl) {
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: POST SA DELETE sa_ctl: %p, index recvd %d\n",
__func__, sa_ctl, sa_index);
ql_dbg(ql_dbg_edif, vha, 0x3063,
"delete index %d, update index: %d, nport handle: 0x%x, handle: 0x%x\n",
delete_sa_index,
edif_entry->update_sa_index, nport_handle, handle);
sa_ctl->flags = EDIF_SA_CTL_FLG_DEL;
set_bit(EDIF_SA_CTL_REPL, &sa_ctl->state);
qla_post_sa_replace_work(fcport->vha, fcport,
nport_handle, sa_ctl);
} else {
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: POST SA DELETE sa_ctl not found for delete_sa_index: %d\n",
__func__, delete_sa_index);
}
}
void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
srb_t *sp, struct sts_entry_24xx *sts24)
{
fc_port_t *fcport = sp->fcport;
/* sa_index used by this iocb */
struct scsi_cmnd *cmd = GET_CMD_SP(sp);
uint32_t handle;
handle = (uint32_t)LSW(sts24->handle);
/* find out if this status iosb is for a scsi read */
if (cmd->sc_data_direction != DMA_FROM_DEVICE)
return;
return __chk_edif_rx_sa_delete_pending(vha, fcport, handle,
le16_to_cpu(sts24->edif_sa_index));
}
void qlt_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, fc_port_t *fcport,
struct ctio7_from_24xx *pkt)
{
__chk_edif_rx_sa_delete_pending(vha, fcport,
pkt->handle, le16_to_cpu(pkt->edif_sa_index));
}
static void qla_parse_auth_els_ctl(struct srb *sp)
{
struct qla_els_pt_arg *a = &sp->u.bsg_cmd.u.els_arg;
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;
}