blob: 7bb85949033fee30952353e389f9dcac8201c895 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 Cisco Systems, Inc. All rights reserved.
* Copyright 2007 Nuova Systems, Inc. All rights reserved.
*/
#include "fnic.h"
#include "fip.h"
#include <linux/etherdevice.h>
#define FIP_FNIC_RESET_WAIT_COUNT 15
/**
* fnic_fcoe_reset_vlans - Free up the list of discovered vlans
* @fnic: Handle to fnic driver instance
*/
void fnic_fcoe_reset_vlans(struct fnic *fnic)
{
unsigned long flags;
struct fcoe_vlan *vlan, *next;
spin_lock_irqsave(&fnic->vlans_lock, flags);
if (!list_empty(&fnic->vlan_list)) {
list_for_each_entry_safe(vlan, next, &fnic->vlan_list, list) {
list_del(&vlan->list);
kfree(vlan);
}
}
spin_unlock_irqrestore(&fnic->vlans_lock, flags);
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"Reset vlan complete\n");
}
/**
* fnic_fcoe_send_vlan_req - Send FIP vlan request to all FCFs MAC
* @fnic: Handle to fnic driver instance
*/
void fnic_fcoe_send_vlan_req(struct fnic *fnic)
{
uint8_t *frame;
struct fnic_iport_s *iport = &fnic->iport;
struct fnic_stats *fnic_stats = &fnic->fnic_stats;
u64 vlan_tov;
struct fip_vlan_req *pvlan_req;
uint16_t frame_size = sizeof(struct fip_vlan_req);
frame = fdls_alloc_frame(iport);
if (frame == NULL) {
FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
"Failed to allocate frame to send VLAN req");
return;
}
fnic_fcoe_reset_vlans(fnic);
fnic->set_vlan(fnic, 0);
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"set vlan done\n");
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"got MAC 0x%x:%x:%x:%x:%x:%x\n", iport->hwmac[0],
iport->hwmac[1], iport->hwmac[2], iport->hwmac[3],
iport->hwmac[4], iport->hwmac[5]);
pvlan_req = (struct fip_vlan_req *) frame;
*pvlan_req = (struct fip_vlan_req) {
.eth = {.h_dest = FCOE_ALL_FCFS_MAC,
.h_proto = cpu_to_be16(ETH_P_FIP)},
.fip = {.fip_ver = FIP_VER_ENCAPS(FIP_VER),
.fip_op = cpu_to_be16(FIP_OP_VLAN),
.fip_subcode = FIP_SC_REQ,
.fip_dl_len = cpu_to_be16(FIP_VLAN_REQ_LEN)},
.mac_desc = {.fd_desc = {.fip_dtype = FIP_DT_MAC,
.fip_dlen = 2}}
};
memcpy(pvlan_req->eth.h_source, iport->hwmac, ETH_ALEN);
memcpy(pvlan_req->mac_desc.fd_mac, iport->hwmac, ETH_ALEN);
atomic64_inc(&fnic_stats->vlan_stats.vlan_disc_reqs);
iport->fip.state = FDLS_FIP_VLAN_DISCOVERY_STARTED;
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"Send VLAN req\n");
fnic_send_fip_frame(iport, frame, frame_size);
vlan_tov = jiffies + msecs_to_jiffies(FCOE_CTLR_FIPVLAN_TOV);
mod_timer(&fnic->retry_fip_timer, round_jiffies(vlan_tov));
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"fip timer set\n");
}
/**
* fnic_fcoe_process_vlan_resp - Processes the vlan response from one FCF and
* populates VLAN list.
* @fnic: Handle to fnic driver instance
* @fiph: Received FIP frame
*
* Will wait for responses from multiple FCFs until timeout.
*/
void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct fip_header *fiph)
{
struct fip_vlan_notif *vlan_notif = (struct fip_vlan_notif *)fiph;
struct fnic_stats *fnic_stats = &fnic->fnic_stats;
u16 vid;
int num_vlan = 0;
int cur_desc, desc_len;
struct fcoe_vlan *vlan;
struct fip_vlan_desc *vlan_desc;
unsigned long flags;
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"fnic 0x%p got vlan resp\n", fnic);
desc_len = be16_to_cpu(vlan_notif->fip.fip_dl_len);
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"desc_len %d\n", desc_len);
spin_lock_irqsave(&fnic->vlans_lock, flags);
cur_desc = 0;
while (desc_len > 0) {
vlan_desc =
(struct fip_vlan_desc *)(((char *)vlan_notif->vlans_desc)
+ cur_desc * 4);
if (vlan_desc->fd_desc.fip_dtype == FIP_DT_VLAN) {
if (vlan_desc->fd_desc.fip_dlen != 1) {
FNIC_FIP_DBG(KERN_INFO, fnic->host,
fnic->fnic_num,
"Invalid descriptor length(%x) in VLan response\n",
vlan_desc->fd_desc.fip_dlen);
}
num_vlan++;
vid = be16_to_cpu(vlan_desc->fd_vlan);
FNIC_FIP_DBG(KERN_INFO, fnic->host,
fnic->fnic_num,
"process_vlan_resp: FIP VLAN %d\n", vid);
vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
if (!vlan) {
/* retry from timer */
FNIC_FIP_DBG(KERN_INFO, fnic->host,
fnic->fnic_num,
"Mem Alloc failure\n");
spin_unlock_irqrestore(&fnic->vlans_lock,
flags);
goto out;
}
vlan->vid = vid & 0x0fff;
vlan->state = FIP_VLAN_AVAIL;
list_add_tail(&vlan->list, &fnic->vlan_list);
break;
}
FNIC_FIP_DBG(KERN_INFO, fnic->host,
fnic->fnic_num,
"Invalid descriptor type(%x) in VLan response\n",
vlan_desc->fd_desc.fip_dtype);
/*
* Note : received a type=2 descriptor here i.e. FIP
* MAC Address Descriptor
*/
cur_desc += vlan_desc->fd_desc.fip_dlen;
desc_len -= vlan_desc->fd_desc.fip_dlen;
}
/* any VLAN descriptors present ? */
if (num_vlan == 0) {
atomic64_inc(&fnic_stats->vlan_stats.resp_withno_vlanID);
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"fnic 0x%p No VLAN descriptors in FIP VLAN response\n",
fnic);
}
spin_unlock_irqrestore(&fnic->vlans_lock, flags);
out:
return;
}
/**
* fnic_fcoe_start_fcf_discovery - Start FIP FCF discovery in a selected vlan
* @fnic: Handle to fnic driver instance
*/
void fnic_fcoe_start_fcf_discovery(struct fnic *fnic)
{
uint8_t *frame;
struct fnic_iport_s *iport = &fnic->iport;
u64 fcs_tov;
struct fip_discovery *pdisc_sol;
uint16_t frame_size = sizeof(struct fip_discovery);
frame = fdls_alloc_frame(iport);
if (frame == NULL) {
FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
"Failed to allocate frame to start FCF discovery");
return;
}
memset(iport->selected_fcf.fcf_mac, 0, ETH_ALEN);
pdisc_sol = (struct fip_discovery *) frame;
*pdisc_sol = (struct fip_discovery) {
.eth = {.h_dest = FCOE_ALL_FCFS_MAC,
.h_proto = cpu_to_be16(ETH_P_FIP)},
.fip = {
.fip_ver = FIP_VER_ENCAPS(FIP_VER), .fip_op = cpu_to_be16(FIP_OP_DISC),
.fip_subcode = FIP_SC_REQ, .fip_dl_len = cpu_to_be16(FIP_DISC_SOL_LEN),
.fip_flags = cpu_to_be16(FIP_FL_FPMA)},
.mac_desc = {.fd_desc = {.fip_dtype = FIP_DT_MAC, .fip_dlen = 2}},
.name_desc = {.fd_desc = {.fip_dtype = FIP_DT_NAME, .fip_dlen = 3}},
.fcoe_desc = {.fd_desc = {.fip_dtype = FIP_DT_FCOE_SIZE, .fip_dlen = 1},
.fd_size = cpu_to_be16(FCOE_MAX_SIZE)}
};
memcpy(pdisc_sol->eth.h_source, iport->hwmac, ETH_ALEN);
memcpy(pdisc_sol->mac_desc.fd_mac, iport->hwmac, ETH_ALEN);
iport->selected_fcf.fcf_priority = 0xFF;
FNIC_STD_SET_NODE_NAME(&pdisc_sol->name_desc.fd_wwn, iport->wwnn);
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"Start FCF discovery\n");
fnic_send_fip_frame(iport, frame, frame_size);
iport->fip.state = FDLS_FIP_FCF_DISCOVERY_STARTED;
fcs_tov = jiffies + msecs_to_jiffies(FCOE_CTLR_FCS_TOV);
mod_timer(&fnic->retry_fip_timer, round_jiffies(fcs_tov));
}
/**
* fnic_fcoe_fip_discovery_resp - Processes FCF advertisements.
* @fnic: Handle to fnic driver instance
* @fiph: Received frame
*
* FCF advertisements can be:
* solicited - Sent in response of a discover FCF FIP request
* Store the information of the FCF with highest priority.
* Wait until timeout in case of multiple FCFs.
*
* unsolicited - Sent periodically by the FCF for keep alive.
* If FLOGI is in progress or completed and the advertisement is
* received by our selected FCF, refresh the keep alive timer.
*/
void fnic_fcoe_fip_discovery_resp(struct fnic *fnic, struct fip_header *fiph)
{
struct fnic_iport_s *iport = &fnic->iport;
struct fip_disc_adv *disc_adv = (struct fip_disc_adv *)fiph;
u64 fcs_ka_tov;
u64 tov;
int fka_has_changed;
switch (iport->fip.state) {
case FDLS_FIP_FCF_DISCOVERY_STARTED:
if (be16_to_cpu(disc_adv->fip.fip_flags) & FIP_FL_SOL) {
FNIC_FIP_DBG(KERN_INFO, fnic->host,
fnic->fnic_num,
"fnic 0x%p Solicited adv\n", fnic);
if ((disc_adv->prio_desc.fd_pri <
iport->selected_fcf.fcf_priority)
&& (be16_to_cpu(disc_adv->fip.fip_flags) & FIP_FL_AVAIL)) {
FNIC_FIP_DBG(KERN_INFO, fnic->host,
fnic->fnic_num,
"fnic 0x%p FCF Available\n", fnic);
memcpy(iport->selected_fcf.fcf_mac,
disc_adv->mac_desc.fd_mac, ETH_ALEN);
iport->selected_fcf.fcf_priority =
disc_adv->prio_desc.fd_pri;
iport->selected_fcf.fka_adv_period =
be32_to_cpu(disc_adv->fka_adv_desc.fd_fka_period);
FNIC_FIP_DBG(KERN_INFO, fnic->host,
fnic->fnic_num, "adv time %d",
iport->selected_fcf.fka_adv_period);
iport->selected_fcf.ka_disabled =
(disc_adv->fka_adv_desc.fd_flags & 1);
}
}
break;
case FDLS_FIP_FLOGI_STARTED:
case FDLS_FIP_FLOGI_COMPLETE:
if (!(be16_to_cpu(disc_adv->fip.fip_flags) & FIP_FL_SOL)) {
/* same fcf */
if (memcmp
(iport->selected_fcf.fcf_mac,
disc_adv->mac_desc.fd_mac, ETH_ALEN) == 0) {
if (iport->selected_fcf.fka_adv_period !=
be32_to_cpu(disc_adv->fka_adv_desc.fd_fka_period)) {
iport->selected_fcf.fka_adv_period =
be32_to_cpu(disc_adv->fka_adv_desc.fd_fka_period);
FNIC_FIP_DBG(KERN_INFO,
fnic->host,
fnic->fnic_num,
"change fka to %d",
iport->selected_fcf.fka_adv_period);
}
fka_has_changed =
(iport->selected_fcf.ka_disabled == 1)
&& ((disc_adv->fka_adv_desc.fd_flags & 1) ==
0);
iport->selected_fcf.ka_disabled =
(disc_adv->fka_adv_desc.fd_flags & 1);
if (!((iport->selected_fcf.ka_disabled)
|| (iport->selected_fcf.fka_adv_period ==
0))) {
fcs_ka_tov = jiffies
+ 3
*
msecs_to_jiffies(iport->selected_fcf.fka_adv_period);
mod_timer(&fnic->fcs_ka_timer,
round_jiffies(fcs_ka_tov));
} else {
if (timer_pending(&fnic->fcs_ka_timer))
del_timer_sync(&fnic->fcs_ka_timer);
}
if (fka_has_changed) {
if (iport->selected_fcf.fka_adv_period != 0) {
tov =
jiffies +
msecs_to_jiffies(
iport->selected_fcf.fka_adv_period);
mod_timer(&fnic->enode_ka_timer,
round_jiffies(tov));
tov =
jiffies +
msecs_to_jiffies
(FIP_VN_KA_PERIOD);
mod_timer(&fnic->vn_ka_timer,
round_jiffies(tov));
}
}
}
}
break;
default:
break;
} /* end switch */
}
/**
* fnic_fcoe_start_flogi - Send FIP FLOGI to the selected FCF
* @fnic: Handle to fnic driver instance
*/
void fnic_fcoe_start_flogi(struct fnic *fnic)
{
uint8_t *frame;
struct fnic_iport_s *iport = &fnic->iport;
struct fip_flogi *pflogi_req;
u64 flogi_tov;
uint16_t oxid;
uint16_t frame_size = sizeof(struct fip_flogi);
frame = fdls_alloc_frame(iport);
if (frame == NULL) {
FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
"Failed to allocate frame to start FIP FLOGI");
return;
}
pflogi_req = (struct fip_flogi *) frame;
*pflogi_req = (struct fip_flogi) {
.eth = {
.h_proto = cpu_to_be16(ETH_P_FIP)},
.fip = {
.fip_ver = FIP_VER_ENCAPS(FIP_VER),
.fip_op = cpu_to_be16(FIP_OP_LS),
.fip_subcode = FIP_SC_REQ,
.fip_dl_len = cpu_to_be16(FIP_FLOGI_LEN),
.fip_flags = cpu_to_be16(FIP_FL_FPMA)},
.flogi_desc = {
.fd_desc = {.fip_dtype = FIP_DT_FLOGI, .fip_dlen = 36},
.flogi = {
.fchdr = {
.fh_r_ctl = FC_RCTL_ELS_REQ,
.fh_d_id = {0xFF, 0xFF, 0xFE},
.fh_type = FC_TYPE_ELS,
.fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0},
.fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)},
.els = {
.fl_cmd = ELS_FLOGI,
.fl_csp = {
.sp_hi_ver =
FNIC_FC_PH_VER_HI,
.sp_lo_ver =
FNIC_FC_PH_VER_LO,
.sp_bb_cred =
cpu_to_be16
(FNIC_FC_B2B_CREDIT),
.sp_bb_data =
cpu_to_be16
(FNIC_FC_B2B_RDF_SZ)},
.fl_cssp[2].cp_class =
cpu_to_be16(FC_CPC_VALID | FC_CPC_SEQ)
},
}
},
.mac_desc = {.fd_desc = {.fip_dtype = FIP_DT_MAC, .fip_dlen = 2}}
};
memcpy(pflogi_req->eth.h_source, iport->hwmac, ETH_ALEN);
if (iport->usefip)
memcpy(pflogi_req->eth.h_dest, iport->selected_fcf.fcf_mac,
ETH_ALEN);
oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_FLOGI,
&iport->active_oxid_fabric_req);
if (oxid == FNIC_UNASSIGNED_OXID) {
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"Failed to allocate OXID to send FIP FLOGI");
mempool_free(frame, fnic->frame_pool);
return;
}
FNIC_STD_SET_OX_ID(pflogi_req->flogi_desc.flogi.fchdr, oxid);
FNIC_STD_SET_NPORT_NAME(&pflogi_req->flogi_desc.flogi.els.fl_wwpn,
iport->wwpn);
FNIC_STD_SET_NODE_NAME(&pflogi_req->flogi_desc.flogi.els.fl_wwnn,
iport->wwnn);
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"FIP start FLOGI\n");
fnic_send_fip_frame(iport, frame, frame_size);
iport->fip.flogi_retry++;
iport->fip.state = FDLS_FIP_FLOGI_STARTED;
flogi_tov = jiffies + msecs_to_jiffies(fnic->config.flogi_timeout);
mod_timer(&fnic->retry_fip_timer, round_jiffies(flogi_tov));
}
/**
* fnic_fcoe_process_flogi_resp - Processes FLOGI response from FCF.
* @fnic: Handle to fnic driver instance
* @fiph: Received frame
*
* If successful save assigned fc_id and MAC, program firmware
* and start fdls discovery, else restart vlan discovery.
*/
void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph)
{
struct fnic_iport_s *iport = &fnic->iport;
struct fip_flogi_rsp *flogi_rsp = (struct fip_flogi_rsp *)fiph;
int desc_len;
uint32_t s_id;
int frame_type;
uint16_t oxid;
struct fnic_stats *fnic_stats = &fnic->fnic_stats;
struct fc_frame_header *fchdr = &flogi_rsp->rsp_desc.flogi.fchdr;
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"fnic 0x%p FIP FLOGI rsp\n", fnic);
desc_len = be16_to_cpu(flogi_rsp->fip.fip_dl_len);
if (desc_len != 38) {
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"Invalid Descriptor List len (%x). Dropping frame\n",
desc_len);
return;
}
if (!((flogi_rsp->rsp_desc.fd_desc.fip_dtype == 7)
&& (flogi_rsp->rsp_desc.fd_desc.fip_dlen == 36))
|| !((flogi_rsp->mac_desc.fd_desc.fip_dtype == 2)
&& (flogi_rsp->mac_desc.fd_desc.fip_dlen == 2))) {
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"Dropping frame invalid type and len mix\n");
return;
}
frame_type = fnic_fdls_validate_and_get_frame_type(iport, fchdr);
s_id = ntoh24(fchdr->fh_s_id);
if ((fchdr->fh_f_ctl[0] != 0x98)
|| (fchdr->fh_r_ctl != 0x23)
|| (s_id != FC_FID_FLOGI)
|| (frame_type != FNIC_FABRIC_FLOGI_RSP)
|| (fchdr->fh_type != 0x01)) {
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"Dropping invalid frame: s_id %x F %x R %x t %x OX_ID %x\n",
s_id, fchdr->fh_f_ctl[0], fchdr->fh_r_ctl,
fchdr->fh_type, FNIC_STD_GET_OX_ID(fchdr));
return;
}
if (iport->fip.state == FDLS_FIP_FLOGI_STARTED) {
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"fnic 0x%p rsp for pending FLOGI\n", fnic);
oxid = FNIC_STD_GET_OX_ID(fchdr);
fdls_free_oxid(iport, oxid, &iport->active_oxid_fabric_req);
del_timer_sync(&fnic->retry_fip_timer);
if ((be16_to_cpu(flogi_rsp->fip.fip_dl_len) == FIP_FLOGI_LEN)
&& (flogi_rsp->rsp_desc.flogi.els.fl_cmd == ELS_LS_ACC)) {
FNIC_FIP_DBG(KERN_INFO, fnic->host,
fnic->fnic_num,
"fnic 0x%p FLOGI success\n", fnic);
memcpy(iport->fpma, flogi_rsp->mac_desc.fd_mac, ETH_ALEN);
iport->fcid =
ntoh24(flogi_rsp->rsp_desc.flogi.fchdr.fh_d_id);
iport->r_a_tov =
be32_to_cpu(flogi_rsp->rsp_desc.flogi.els.fl_csp.sp_r_a_tov);
iport->e_d_tov =
be32_to_cpu(flogi_rsp->rsp_desc.flogi.els.fl_csp.sp_e_d_tov);
memcpy(fnic->iport.fcfmac, iport->selected_fcf.fcf_mac,
ETH_ALEN);
vnic_dev_add_addr(fnic->vdev, flogi_rsp->mac_desc.fd_mac);
if (fnic_fdls_register_portid(iport, iport->fcid, NULL)
!= 0) {
FNIC_FIP_DBG(KERN_INFO, fnic->host,
fnic->fnic_num,
"fnic 0x%p flogi registration failed\n",
fnic);
return;
}
iport->fip.state = FDLS_FIP_FLOGI_COMPLETE;
iport->state = FNIC_IPORT_STATE_FABRIC_DISC;
FNIC_FIP_DBG(KERN_INFO, fnic->host,
fnic->fnic_num, "iport->state:%d\n",
iport->state);
fnic_fdls_disc_start(iport);
if (!((iport->selected_fcf.ka_disabled)
|| (iport->selected_fcf.fka_adv_period == 0))) {
u64 tov;
tov = jiffies
+
msecs_to_jiffies(iport->selected_fcf.fka_adv_period);
mod_timer(&fnic->enode_ka_timer,
round_jiffies(tov));
tov =
jiffies +
msecs_to_jiffies(FIP_VN_KA_PERIOD);
mod_timer(&fnic->vn_ka_timer,
round_jiffies(tov));
}
} else {
/*
* If there's FLOGI rejects - clear all
* fcf's & restart from scratch
*/
atomic64_inc(&fnic_stats->vlan_stats.flogi_rejects);
/* start FCoE VLAN discovery */
fnic_fcoe_send_vlan_req(fnic);
iport->fip.state = FDLS_FIP_VLAN_DISCOVERY_STARTED;
}
}
}
/**
* fnic_common_fip_cleanup - Clean up FCF info and timers in case of
* link down/CVL
* @fnic: Handle to fnic driver instance
*/
void fnic_common_fip_cleanup(struct fnic *fnic)
{
struct fnic_iport_s *iport = &fnic->iport;
if (!iport->usefip)
return;
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"fnic 0x%p fip cleanup\n", fnic);
iport->fip.state = FDLS_FIP_INIT;
del_timer_sync(&fnic->retry_fip_timer);
del_timer_sync(&fnic->fcs_ka_timer);
del_timer_sync(&fnic->enode_ka_timer);
del_timer_sync(&fnic->vn_ka_timer);
if (!is_zero_ether_addr(iport->fpma))
vnic_dev_del_addr(fnic->vdev, iport->fpma);
memset(iport->fpma, 0, ETH_ALEN);
iport->fcid = 0;
iport->r_a_tov = 0;
iport->e_d_tov = 0;
memset(fnic->iport.fcfmac, 0, ETH_ALEN);
memset(iport->selected_fcf.fcf_mac, 0, ETH_ALEN);
iport->selected_fcf.fcf_priority = 0;
iport->selected_fcf.fka_adv_period = 0;
iport->selected_fcf.ka_disabled = 0;
fnic_fcoe_reset_vlans(fnic);
}
/**
* fnic_fcoe_process_cvl - Processes Clear Virtual Link from FCF.
* @fnic: Handle to fnic driver instance
* @fiph: Received frame
*
* Verify that cvl is received from our current FCF for our assigned MAC
* and clean up and restart the vlan discovery.
*/
void fnic_fcoe_process_cvl(struct fnic *fnic, struct fip_header *fiph)
{
struct fnic_iport_s *iport = &fnic->iport;
struct fip_cvl *cvl_msg = (struct fip_cvl *)fiph;
int i;
int found = false;
int max_count = 0;
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"fnic 0x%p clear virtual link handler\n", fnic);
if (!((cvl_msg->fcf_mac_desc.fd_desc.fip_dtype == 2)
&& (cvl_msg->fcf_mac_desc.fd_desc.fip_dlen == 2))
|| !((cvl_msg->name_desc.fd_desc.fip_dtype == 4)
&& (cvl_msg->name_desc.fd_desc.fip_dlen == 3))) {
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"invalid mix: ft %x fl %x ndt %x ndl %x",
cvl_msg->fcf_mac_desc.fd_desc.fip_dtype,
cvl_msg->fcf_mac_desc.fd_desc.fip_dlen,
cvl_msg->name_desc.fd_desc.fip_dtype,
cvl_msg->name_desc.fd_desc.fip_dlen);
}
if (memcmp
(iport->selected_fcf.fcf_mac, cvl_msg->fcf_mac_desc.fd_mac, ETH_ALEN)
== 0) {
for (i = 0; i < ((be16_to_cpu(fiph->fip_dl_len) / 5) - 1); i++) {
if (!((cvl_msg->vn_ports_desc[i].fd_desc.fip_dtype == 11)
&& (cvl_msg->vn_ports_desc[i].fd_desc.fip_dlen == 5))) {
FNIC_FIP_DBG(KERN_INFO, fnic->host,
fnic->fnic_num,
"Invalid type and len mix type: %d len: %d\n",
cvl_msg->vn_ports_desc[i].fd_desc.fip_dtype,
cvl_msg->vn_ports_desc[i].fd_desc.fip_dlen);
}
if (memcmp
(iport->fpma, cvl_msg->vn_ports_desc[i].fd_mac,
ETH_ALEN) == 0) {
found = true;
break;
}
}
if (!found)
return;
fnic_common_fip_cleanup(fnic);
while (fnic->reset_in_progress == IN_PROGRESS) {
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
wait_for_completion_timeout(&fnic->reset_completion_wait,
msecs_to_jiffies(5000));
spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
max_count++;
if (max_count >= FIP_FNIC_RESET_WAIT_COUNT) {
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"Rthr waited too long. Skipping handle link event %p\n",
fnic);
return;
}
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"fnic reset in progress. Link event needs to wait %p",
fnic);
}
fnic->reset_in_progress = IN_PROGRESS;
fnic_fdls_link_down(iport);
fnic->reset_in_progress = NOT_IN_PROGRESS;
complete(&fnic->reset_completion_wait);
fnic_fcoe_send_vlan_req(fnic);
}
}
/**
* fdls_fip_recv_frame - Demultiplexer for FIP frames
* @fnic: Handle to fnic driver instance
* @frame: Received ethernet frame
*/
int fdls_fip_recv_frame(struct fnic *fnic, void *frame)
{
struct ethhdr *eth = (struct ethhdr *)frame;
struct fip_header *fiph;
u16 op;
u8 sub;
int len = 2048;
if (be16_to_cpu(eth->h_proto) == ETH_P_FIP) {
fiph = (struct fip_header *)(eth + 1);
op = be16_to_cpu(fiph->fip_op);
sub = fiph->fip_subcode;
fnic_debug_dump_fip_frame(fnic, eth, len, "Incoming");
if (op == FIP_OP_DISC && sub == FIP_SC_REP)
fnic_fcoe_fip_discovery_resp(fnic, fiph);
else if (op == FIP_OP_VLAN && sub == FIP_SC_REP)
fnic_fcoe_process_vlan_resp(fnic, fiph);
else if (op == FIP_OP_CTRL && sub == FIP_SC_REP)
fnic_fcoe_process_cvl(fnic, fiph);
else if (op == FIP_OP_LS && sub == FIP_SC_REP)
fnic_fcoe_process_flogi_resp(fnic, fiph);
/* Return true if the frame was a FIP frame */
return true;
}
FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"Not a FIP Frame");
return false;
}
void fnic_work_on_fip_timer(struct work_struct *work)
{
struct fnic *fnic = container_of(work, struct fnic, fip_timer_work);
struct fnic_iport_s *iport = &fnic->iport;
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"FIP timeout\n");
if (iport->fip.state == FDLS_FIP_VLAN_DISCOVERY_STARTED) {
fnic_vlan_discovery_timeout(fnic);
} else if (iport->fip.state == FDLS_FIP_FCF_DISCOVERY_STARTED) {
u8 zmac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"FCF Discovery timeout\n");
if (memcmp(iport->selected_fcf.fcf_mac, zmac, ETH_ALEN) != 0) {
if (iport->flags & FNIC_FIRST_LINK_UP) {
fnic_scsi_fcpio_reset(iport->fnic);
iport->flags &= ~FNIC_FIRST_LINK_UP;
}
fnic_fcoe_start_flogi(fnic);
if (!((iport->selected_fcf.ka_disabled)
|| (iport->selected_fcf.fka_adv_period == 0))) {
u64 fcf_tov;
fcf_tov = jiffies
+ 3
*
msecs_to_jiffies(iport->selected_fcf.fka_adv_period);
mod_timer(&fnic->fcs_ka_timer,
round_jiffies(fcf_tov));
}
} else {
FNIC_FIP_DBG(KERN_INFO, fnic->host,
fnic->fnic_num, "FCF Discovery timeout\n");
fnic_vlan_discovery_timeout(fnic);
}
} else if (iport->fip.state == FDLS_FIP_FLOGI_STARTED) {
fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req);
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"FLOGI timeout\n");
if (iport->fip.flogi_retry < fnic->config.flogi_retries)
fnic_fcoe_start_flogi(fnic);
else
fnic_vlan_discovery_timeout(fnic);
}
}
/**
* fnic_handle_fip_timer - Timeout handler for FIP discover phase.
* @t: Handle to the timer list
*
* Based on the current state, start next phase or restart discovery.
*/
void fnic_handle_fip_timer(struct timer_list *t)
{
struct fnic *fnic = from_timer(fnic, t, retry_fip_timer);
INIT_WORK(&fnic->fip_timer_work, fnic_work_on_fip_timer);
queue_work(fnic_fip_queue, &fnic->fip_timer_work);
}
/**
* fnic_handle_enode_ka_timer - FIP node keep alive.
* @t: Handle to the timer list
*/
void fnic_handle_enode_ka_timer(struct timer_list *t)
{
uint8_t *frame;
struct fnic *fnic = from_timer(fnic, t, enode_ka_timer);
struct fnic_iport_s *iport = &fnic->iport;
struct fip_enode_ka *penode_ka;
u64 enode_ka_tov;
uint16_t frame_size = sizeof(struct fip_enode_ka);
if (iport->fip.state != FDLS_FIP_FLOGI_COMPLETE)
return;
if ((iport->selected_fcf.ka_disabled)
|| (iport->selected_fcf.fka_adv_period == 0)) {
return;
}
frame = fdls_alloc_frame(iport);
if (frame == NULL) {
FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
"Failed to allocate frame to send enode ka");
return;
}
penode_ka = (struct fip_enode_ka *) frame;
*penode_ka = (struct fip_enode_ka) {
.eth = {
.h_proto = cpu_to_be16(ETH_P_FIP)},
.fip = {
.fip_ver = FIP_VER_ENCAPS(FIP_VER),
.fip_op = cpu_to_be16(FIP_OP_CTRL),
.fip_subcode = FIP_SC_REQ,
.fip_dl_len = cpu_to_be16(FIP_ENODE_KA_LEN)},
.mac_desc = {.fd_desc = {.fip_dtype = FIP_DT_MAC, .fip_dlen = 2}}
};
memcpy(penode_ka->eth.h_source, iport->hwmac, ETH_ALEN);
memcpy(penode_ka->eth.h_dest, iport->selected_fcf.fcf_mac, ETH_ALEN);
memcpy(penode_ka->mac_desc.fd_mac, iport->hwmac, ETH_ALEN);
FNIC_FIP_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
"Handle enode KA timer\n");
fnic_send_fip_frame(iport, frame, frame_size);
enode_ka_tov = jiffies
+ msecs_to_jiffies(iport->selected_fcf.fka_adv_period);
mod_timer(&fnic->enode_ka_timer, round_jiffies(enode_ka_tov));
}
/**
* fnic_handle_vn_ka_timer - FIP virtual port keep alive.
* @t: Handle to the timer list
*/
void fnic_handle_vn_ka_timer(struct timer_list *t)
{
uint8_t *frame;
struct fnic *fnic = from_timer(fnic, t, vn_ka_timer);
struct fnic_iport_s *iport = &fnic->iport;
struct fip_vn_port_ka *pvn_port_ka;
u64 vn_ka_tov;
uint8_t fcid[3];
uint16_t frame_size = sizeof(struct fip_vn_port_ka);
if (iport->fip.state != FDLS_FIP_FLOGI_COMPLETE)
return;
if ((iport->selected_fcf.ka_disabled)
|| (iport->selected_fcf.fka_adv_period == 0)) {
return;
}
frame = fdls_alloc_frame(iport);
if (frame == NULL) {
FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num,
"Failed to allocate frame to send vn ka");
return;
}
pvn_port_ka = (struct fip_vn_port_ka *) frame;
*pvn_port_ka = (struct fip_vn_port_ka) {
.eth = {
.h_proto = cpu_to_be16(ETH_P_FIP)},
.fip = {
.fip_ver = FIP_VER_ENCAPS(FIP_VER),
.fip_op = cpu_to_be16(FIP_OP_CTRL),
.fip_subcode = FIP_SC_REQ,
.fip_dl_len = cpu_to_be16(FIP_VN_KA_LEN)},
.mac_desc = {.fd_desc = {.fip_dtype = FIP_DT_MAC, .fip_dlen = 2}},
.vn_port_desc = {.fd_desc = {.fip_dtype = FIP_DT_VN_ID, .fip_dlen = 5}}
};
memcpy(pvn_port_ka->eth.h_source, iport->fpma, ETH_ALEN);
memcpy(pvn_port_ka->eth.h_dest, iport->selected_fcf.fcf_mac, ETH_ALEN);
memcpy(pvn_port_ka->mac_desc.fd_mac, iport->hwmac, ETH_ALEN);
memcpy(pvn_port_ka->vn_port_desc.fd_mac, iport->fpma, ETH_ALEN);
hton24(fcid, iport->fcid);
memcpy(pvn_port_ka->vn_port_desc.fd_fc_id, fcid, 3);
FNIC_STD_SET_NPORT_NAME(&pvn_port_ka->vn_port_desc.fd_wwpn, iport->wwpn);
FNIC_FIP_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num,
"Handle vnport KA timer\n");
fnic_send_fip_frame(iport, frame, frame_size);
vn_ka_tov = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD);
mod_timer(&fnic->vn_ka_timer, round_jiffies(vn_ka_tov));
}
/**
* fnic_vlan_discovery_timeout - Handle vlan discovery timeout
* @fnic: Handle to fnic driver instance
*
* End of VLAN discovery or FCF discovery time window.
* Start the FCF discovery if VLAN was never used.
*/
void fnic_vlan_discovery_timeout(struct fnic *fnic)
{
struct fcoe_vlan *vlan;
struct fnic_iport_s *iport = &fnic->iport;
struct fnic_stats *fnic_stats = &fnic->fnic_stats;
unsigned long flags;
spin_lock_irqsave(&fnic->fnic_lock, flags);
if (fnic->stop_rx_link_events) {
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
return;
}
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
if (!iport->usefip)
return;
spin_lock_irqsave(&fnic->vlans_lock, flags);
if (list_empty(&fnic->vlan_list)) {
/* no vlans available, try again */
spin_unlock_irqrestore(&fnic->vlans_lock, flags);
fnic_fcoe_send_vlan_req(fnic);
return;
}
vlan = list_first_entry(&fnic->vlan_list, struct fcoe_vlan, list);
if (vlan->state == FIP_VLAN_SENT) {
if (vlan->sol_count >= FCOE_CTLR_MAX_SOL) {
/*
* no response on this vlan, remove from the list.
* Try the next vlan
*/
list_del(&vlan->list);
kfree(vlan);
vlan = NULL;
if (list_empty(&fnic->vlan_list)) {
/* we exhausted all vlans, restart vlan disc */
spin_unlock_irqrestore(&fnic->vlans_lock,
flags);
fnic_fcoe_send_vlan_req(fnic);
return;
}
/* check the next vlan */
vlan =
list_first_entry(&fnic->vlan_list, struct fcoe_vlan,
list);
fnic->set_vlan(fnic, vlan->vid);
vlan->state = FIP_VLAN_SENT; /* sent now */
}
atomic64_inc(&fnic_stats->vlan_stats.sol_expiry_count);
} else {
fnic->set_vlan(fnic, vlan->vid);
vlan->state = FIP_VLAN_SENT; /* sent now */
}
vlan->sol_count++;
spin_unlock_irqrestore(&fnic->vlans_lock, flags);
fnic_fcoe_start_fcf_discovery(fnic);
}
/**
* fnic_work_on_fcs_ka_timer - Handle work on FCS keep alive timer.
* @work: the work queue to be serviced
*
* Finish handling fcs_ka_timer in process context.
* Clean up, bring the link down, and restart all FIP discovery.
*/
void fnic_work_on_fcs_ka_timer(struct work_struct *work)
{
struct fnic
*fnic = container_of(work, struct fnic, fip_timer_work);
struct fnic_iport_s *iport = &fnic->iport;
FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"fnic 0x%p fcs ka timeout\n", fnic);
fnic_common_fip_cleanup(fnic);
spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
fnic_fdls_link_down(iport);
iport->state = FNIC_IPORT_STATE_FIP;
spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
fnic_fcoe_send_vlan_req(fnic);
}
/**
* fnic_handle_fcs_ka_timer - Handle FCS keep alive timer.
* @t: Handle to the timer list
*
* No keep alives received from FCF. Clean up, bring the link down
* and restart all the FIP discovery.
*/
void fnic_handle_fcs_ka_timer(struct timer_list *t)
{
struct fnic *fnic = from_timer(fnic, t, fcs_ka_timer);
INIT_WORK(&fnic->fip_timer_work, fnic_work_on_fcs_ka_timer);
queue_work(fnic_fip_queue, &fnic->fip_timer_work);
}