blob: 2e37b189cb7559ac4994b783af8883ca10477ddf [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Marvell Fibre Channel HBA Driver
* Copyright (c) 2021 Marvell
*/
#include "qla_def.h"
#include "qla_edif.h"
#include <linux/kthread.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <scsi/scsi_tcq.h>
static struct edif_sa_index_entry *qla_edif_sadb_find_sa_index_entry(uint16_t nport_handle,
struct list_head *sa_list);
static uint16_t qla_edif_sadb_get_sa_index(fc_port_t *fcport,
struct qla_sa_update_frame *sa_frame);
static int qla_edif_sadb_delete_sa_index(fc_port_t *fcport, uint16_t nport_handle,
uint16_t sa_index);
static int qla_pur_get_pending(scsi_qla_host_t *, fc_port_t *, struct bsg_job *);
struct edb_node {
struct list_head list;
uint32_t ntype;
union {
port_id_t plogi_did;
uint32_t async;
port_id_t els_sid;
struct edif_sa_update_aen sa_aen;
} u;
};
static struct els_sub_cmd {
uint16_t cmd;
const char *str;
} sc_str[] = {
{SEND_ELS, "send ELS"},
{SEND_ELS_REPLY, "send ELS Reply"},
{PULL_ELS, "retrieve ELS"},
};
const char *sc_to_str(uint16_t cmd)
{
int i;
struct els_sub_cmd *e;
for (i = 0; i < ARRAY_SIZE(sc_str); i++) {
e = sc_str + i;
if (cmd == e->cmd)
return e->str;
}
return "unknown";
}
static struct edif_list_entry *qla_edif_list_find_sa_index(fc_port_t *fcport,
uint16_t handle)
{
struct edif_list_entry *entry;
struct edif_list_entry *tentry;
struct list_head *indx_list = &fcport->edif.edif_indx_list;
list_for_each_entry_safe(entry, tentry, indx_list, next) {
if (entry->handle == handle)
return entry;
}
return NULL;
}
/* timeout called when no traffic and delayed rx sa_index delete */
static void qla2x00_sa_replace_iocb_timeout(struct timer_list *t)
{
struct edif_list_entry *edif_entry = from_timer(edif_entry, t, timer);
fc_port_t *fcport = edif_entry->fcport;
struct scsi_qla_host *vha = fcport->vha;
struct edif_sa_ctl *sa_ctl;
uint16_t nport_handle;
unsigned long flags = 0;
ql_dbg(ql_dbg_edif, vha, 0x3069,
"%s: nport_handle 0x%x, SA REPL Delay Timeout, %8phC portid=%06x\n",
__func__, edif_entry->handle, fcport->port_name, fcport->d_id.b24);
/*
* if delete_sa_index is valid then no one has serviced this
* delayed delete
*/
spin_lock_irqsave(&fcport->edif.indx_list_lock, flags);
/*
* delete_sa_index is invalidated when we find the new sa_index in
* the incoming data stream. If it is not invalidated then we are
* still looking for the new sa_index because there is no I/O and we
* need to just force the rx delete and move on. Otherwise
* we could get another rekey which will result in an error 66.
*/
if (edif_entry->delete_sa_index != INVALID_EDIF_SA_INDEX) {
uint16_t delete_sa_index = edif_entry->delete_sa_index;
edif_entry->delete_sa_index = INVALID_EDIF_SA_INDEX;
nport_handle = edif_entry->handle;
spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
sa_ctl = qla_edif_find_sa_ctl_by_index(fcport,
delete_sa_index, 0);
if (sa_ctl) {
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: sa_ctl: %p, delete index %d, update index: %d, lid: 0x%x\n",
__func__, sa_ctl, delete_sa_index, edif_entry->update_sa_index,
nport_handle);
sa_ctl->flags = EDIF_SA_CTL_FLG_DEL;
set_bit(EDIF_SA_CTL_REPL, &sa_ctl->state);
qla_post_sa_replace_work(fcport->vha, fcport,
nport_handle, sa_ctl);
} else {
ql_dbg(ql_dbg_edif, vha, 0x3063,
"%s: sa_ctl not found for delete_sa_index: %d\n",
__func__, edif_entry->delete_sa_index);
}
} else {
spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
}
}
/*
* create a new list entry for this nport handle and
* add an sa_update index to the list - called for sa_update
*/
static int qla_edif_list_add_sa_update_index(fc_port_t *fcport,
uint16_t sa_index, uint16_t handle)
{
struct edif_list_entry *entry;
unsigned long flags = 0;
/* if the entry exists, then just update the sa_index */
entry = qla_edif_list_find_sa_index(fcport, handle);
if (entry) {
entry->update_sa_index = sa_index;
entry->count = 0;
return 0;
}
/*
* This is the normal path - there should be no existing entry
* when update is called. The exception is at startup
* when update is called for the first two sa_indexes
* followed by a delete of the first sa_index
*/
entry = kzalloc((sizeof(struct edif_list_entry)), GFP_ATOMIC);
if (!entry)
return -ENOMEM;
INIT_LIST_HEAD(&entry->next);
entry->handle = handle;
entry->update_sa_index = sa_index;
entry->delete_sa_index = INVALID_EDIF_SA_INDEX;
entry->count = 0;
entry->flags = 0;
timer_setup(&entry->timer, qla2x00_sa_replace_iocb_timeout, 0);
spin_lock_irqsave(&fcport->edif.indx_list_lock, flags);
list_add_tail(&entry->next, &fcport->edif.edif_indx_list);
spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
return 0;
}
/* remove an entry from the list */
static void qla_edif_list_delete_sa_index(fc_port_t *fcport, struct edif_list_entry *entry)
{
unsigned long flags = 0;
spin_lock_irqsave(&fcport->edif.indx_list_lock, flags);
list_del(&entry->next);
spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
}
int qla_post_sa_replace_work(struct scsi_qla_host *vha,
fc_port_t *fcport, uint16_t nport_handle, struct edif_sa_ctl *sa_ctl)
{
struct qla_work_evt *e;
e = qla2x00_alloc_work(vha, QLA_EVT_SA_REPLACE);
if (!e)
return QLA_FUNCTION_FAILED;
e->u.sa_update.fcport = fcport;
e->u.sa_update.sa_ctl = sa_ctl;
e->u.sa_update.nport_handle = nport_handle;
fcport->flags |= FCF_ASYNC_ACTIVE;
return qla2x00_post_work(vha, e);
}
static void
qla_edif_sa_ctl_init(scsi_qla_host_t *vha, struct fc_port *fcport)
{
ql_dbg(ql_dbg_edif, vha, 0x2058,
"Init SA_CTL List for fcport - nn %8phN pn %8phN portid=%06x.\n",
fcport->node_name, fcport->port_name, fcport->d_id.b24);
fcport->edif.tx_rekey_cnt = 0;
fcport->edif.rx_rekey_cnt = 0;
fcport->edif.tx_bytes = 0;
fcport->edif.rx_bytes = 0;
}
static int qla_bsg_check(scsi_qla_host_t *vha, struct bsg_job *bsg_job,
fc_port_t *fcport)
{
struct extra_auth_els *p;
struct fc_bsg_reply *bsg_reply = bsg_job->reply;
struct qla_bsg_auth_els_request *req =
(struct qla_bsg_auth_els_request *)bsg_job->request;
if (!vha->hw->flags.edif_enabled) {
ql_dbg(ql_dbg_edif, vha, 0x9105,
"%s edif not enabled\n", __func__);
goto done;
}
if (DBELL_INACTIVE(vha)) {
ql_dbg(ql_dbg_edif, vha, 0x09102,
"%s doorbell not enabled\n", __func__);
goto done;
}
p = &req->e;
/* Get response */
if (p->sub_cmd == PULL_ELS) {
struct qla_bsg_auth_els_reply *rpl =
(struct qla_bsg_auth_els_reply *)bsg_job->reply;
qla_pur_get_pending(vha, fcport, bsg_job);
ql_dbg(ql_dbg_edif, vha, 0x911d,
"%s %s %8phN sid=%x. xchg %x, nb=%xh bsg ptr %p\n",
__func__, sc_to_str(p->sub_cmd), fcport->port_name,
fcport->d_id.b24, rpl->rx_xchg_address,
rpl->r.reply_payload_rcv_len, bsg_job);
goto done;
}
return 0;
done:
bsg_job_done(bsg_job, bsg_reply->result,
bsg_reply->reply_payload_rcv_len);
return -EIO;
}
fc_port_t *
qla2x00_find_fcport_by_pid(scsi_qla_host_t *vha, port_id_t *id)
{
fc_port_t *f, *tf;
f = NULL;
list_for_each_entry_safe(f, tf, &vha->vp_fcports, list) {
if ((f->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_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);
}
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) {
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;
}
static struct edif_sa_ctl *
qla_edif_add_sa_ctl(fc_port_t *fcport, struct qla_sa_update_frame *sa_frame,
int dir)
{
struct edif_sa_ctl *sa_ctl;
struct qla_sa_update_frame *sap;
int index = sa_frame->fast_sa_index;
unsigned long flags = 0;
sa_ctl = kzalloc(sizeof(*sa_ctl), GFP_KERNEL);
if (!sa_ctl) {
/* couldn't get space */
ql_dbg(ql_dbg_edif, fcport->vha, 0x9100,
"unable to allocate SA CTL\n");
return NULL;
}
/*
* need to allocate sa_index here and save it
* in both sa_ctl->index and sa_frame->fast_sa_index;
* If alloc fails then delete sa_ctl and return NULL
*/
INIT_LIST_HEAD(&sa_ctl->next);
sap = &sa_ctl->sa_frame;
*sap = *sa_frame;
sa_ctl->index = index;
sa_ctl->fcport = fcport;
sa_ctl->flags = 0;
sa_ctl->state = 0L;
ql_dbg(ql_dbg_edif, fcport->vha, 0x9100,
"%s: Added sa_ctl %p, index %d, state 0x%lx\n",
__func__, sa_ctl, sa_ctl->index, sa_ctl->state);
spin_lock_irqsave(&fcport->edif.sa_list_lock, flags);
if (dir == SAU_FLG_TX)
list_add_tail(&sa_ctl->next, &fcport->edif.tx_sa_list);
else
list_add_tail(&sa_ctl->next, &fcport->edif.rx_sa_list);
spin_unlock_irqrestore(&fcport->edif.sa_list_lock, flags);
return sa_ctl;
}
void
qla_edif_flush_sa_ctl_lists(fc_port_t *fcport)
{
struct edif_sa_ctl *sa_ctl, *tsa_ctl;
unsigned long flags = 0;
spin_lock_irqsave(&fcport->edif.sa_list_lock, flags);
list_for_each_entry_safe(sa_ctl, tsa_ctl, &fcport->edif.tx_sa_list,
next) {
list_del(&sa_ctl->next);
kfree(sa_ctl);
}
list_for_each_entry_safe(sa_ctl, tsa_ctl, &fcport->edif.rx_sa_list,
next) {
list_del(&sa_ctl->next);
kfree(sa_ctl);
}
spin_unlock_irqrestore(&fcport->edif.sa_list_lock, flags);
}
struct edif_sa_ctl *
qla_edif_find_sa_ctl_by_index(fc_port_t *fcport, int index, int dir)
{
struct edif_sa_ctl *sa_ctl, *tsa_ctl;
struct list_head *sa_list;
if (dir == SAU_FLG_TX)
sa_list = &fcport->edif.tx_sa_list;
else
sa_list = &fcport->edif.rx_sa_list;
list_for_each_entry_safe(sa_ctl, tsa_ctl, sa_list, next) {
if (test_bit(EDIF_SA_CTL_USED, &sa_ctl->state) &&
sa_ctl->index == index)
return sa_ctl;
}
return NULL;
}
/* add the sa to the correct list */
static int
qla24xx_check_sadb_avail_slot(struct bsg_job *bsg_job, fc_port_t *fcport,
struct qla_sa_update_frame *sa_frame)
{
struct edif_sa_ctl *sa_ctl = NULL;
int dir;
uint16_t sa_index;
dir = (sa_frame->flags & SAU_FLG_TX);
/* map the spi to an sa_index */
sa_index = qla_edif_sadb_get_sa_index(fcport, sa_frame);
if (sa_index == RX_DELETE_NO_EDIF_SA_INDEX) {
/* process rx delete */
ql_dbg(ql_dbg_edif, fcport->vha, 0x3063,
"%s: rx delete for lid 0x%x, spi 0x%x, no entry found\n",
__func__, fcport->loop_id, sa_frame->spi);
/* build and send the aen */
fcport->edif.rx_sa_set = 1;
fcport->edif.rx_sa_pending = 0;
qla_edb_eventcreate(fcport->vha,
VND_CMD_AUTH_STATE_SAUPDATE_COMPL,
QL_VND_SA_STAT_SUCCESS,
QL_VND_RX_SA_KEY, fcport);
/* force a return of good bsg status; */
return RX_DELETE_NO_EDIF_SA_INDEX;
} else if (sa_index == INVALID_EDIF_SA_INDEX) {
ql_dbg(ql_dbg_edif, fcport->vha, 0x9100,
"%s: Failed to get sa_index for spi 0x%x, dir: %d\n",
__func__, sa_frame->spi, dir);
return INVALID_EDIF_SA_INDEX;
}
ql_dbg(ql_dbg_edif, fcport->vha, 0x9100,
"%s: index %d allocated to spi 0x%x, dir: %d, nport_handle: 0x%x\n",
__func__, sa_index, sa_frame->spi, dir, fcport->loop_id);
/* This is a local copy of sa_frame. */
sa_frame->fast_sa_index = sa_index;
/* create the sa_ctl */
sa_ctl = qla_edif_add_sa_ctl(fcport, sa_frame, dir);
if (!sa_ctl) {
ql_dbg(ql_dbg_edif, fcport->vha, 0x9100,
"%s: Failed to add sa_ctl for spi 0x%x, dir: %d, sa_index: %d\n",
__func__, sa_frame->spi, dir, sa_index);
return -1;
}
set_bit(EDIF_SA_CTL_USED, &sa_ctl->state);
if (dir == SAU_FLG_TX)
fcport->edif.tx_rekey_cnt++;
else
fcport->edif.rx_rekey_cnt++;
ql_dbg(ql_dbg_edif, fcport->vha, 0x9100,
"%s: Found sa_ctl %p, index %d, state 0x%lx, tx_cnt %d, rx_cnt %d, nport_handle: 0x%x\n",
__func__, sa_ctl, sa_ctl->index, sa_ctl->state,
fcport->edif.tx_rekey_cnt,
fcport->edif.rx_rekey_cnt, fcport->loop_id);
return 0;
}
#define QLA_SA_UPDATE_FLAGS_RX_KEY 0x0
#define QLA_SA_UPDATE_FLAGS_TX_KEY 0x2
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)) {
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;
}
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);
}
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);
} else {
sp->fcport->edif.rx_sa_set = 1;
sp->fcport->edif.rx_sa_pending = 0;
qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_SAUPDATE_COMPL,
QL_VND_SA_STAT_SUCCESS,
QL_VND_RX_SA_KEY, sp->fcport);
}
} else {
ql_dbg(ql_dbg_edif, vha, 0x3063,