10fd71780f
This is mostly update of the usual drivers: qla2xxx, ufs, smartpqi, lpfc, hisi_sas, qedf, mpt3sas; plus a whole load of minor updates. The only core change this time around is the addition of request batching for virtio. Since batching requires an additional flag to use, it should be invisible to the rest of the drivers. Signed-off-by: James E.J. Bottomley <jejb@linux.ibm.com> -----BEGIN PGP SIGNATURE----- iJwEABMIAEQWIQTnYEDbdso9F2cI+arnQslM7pishQUCXYQE/yYcamFtZXMuYm90 dG9tbGV5QGhhbnNlbnBhcnRuZXJzaGlwLmNvbQAKCRDnQslM7pishXs9AP4usPY5 OpMlF6OiKFNeJrCdhCScVghf9uHbc7UA6cP+EgD/bCtRgcDe1ZjOTYWdeTwvwWqA ltWYonnv6Lg3b1f9yqI= =jRC/ -----END PGP SIGNATURE----- Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi Pull SCSI updates from James Bottomley: "This is mostly update of the usual drivers: qla2xxx, ufs, smartpqi, lpfc, hisi_sas, qedf, mpt3sas; plus a whole load of minor updates. The only core change this time around is the addition of request batching for virtio. Since batching requires an additional flag to use, it should be invisible to the rest of the drivers" * tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi: (264 commits) scsi: hisi_sas: Fix the conflict between device gone and host reset scsi: hisi_sas: Add BIST support for phy loopback scsi: hisi_sas: Add hisi_sas_debugfs_alloc() to centralise allocation scsi: hisi_sas: Remove some unused function arguments scsi: hisi_sas: Remove redundant work declaration scsi: hisi_sas: Remove hisi_sas_hw.slot_complete scsi: hisi_sas: Assign NCQ tag for all NCQ commands scsi: hisi_sas: Update all the registers after suspend and resume scsi: hisi_sas: Retry 3 times TMF IO for SAS disks when init device scsi: hisi_sas: Remove sleep after issue phy reset if sas_smp_phy_control() fails scsi: hisi_sas: Directly return when running I_T_nexus reset if phy disabled scsi: hisi_sas: Use true/false as input parameter of sas_phy_reset() scsi: hisi_sas: add debugfs auto-trigger for internal abort time out scsi: virtio_scsi: unplug LUNs when events missed scsi: scsi_dh_rdac: zero cdb in send_mode_select() scsi: fcoe: fix null-ptr-deref Read in fc_release_transport scsi: ufs-hisi: use devm_platform_ioremap_resource() to simplify code scsi: ufshcd: use devm_platform_ioremap_resource() to simplify code scsi: hisi_sas: use devm_platform_ioremap_resource() to simplify code scsi: ufs: Use kmemdup in ufshcd_read_string_desc() ...
3002 lines
77 KiB
C
3002 lines
77 KiB
C
/* bnx2fc_fcoe.c: QLogic Linux FCoE offload driver.
|
|
* This file contains the code that interacts with libfc, libfcoe,
|
|
* cnic modules to create FCoE instances, send/receive non-offloaded
|
|
* FIP/FCoE packets, listen to link events etc.
|
|
*
|
|
* Copyright (c) 2008-2013 Broadcom Corporation
|
|
* Copyright (c) 2014-2016 QLogic Corporation
|
|
* Copyright (c) 2016-2017 Cavium Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation.
|
|
*
|
|
* Written by: Bhanu Prakash Gollapudi (bprakash@broadcom.com)
|
|
*/
|
|
|
|
#include "bnx2fc.h"
|
|
|
|
static struct list_head adapter_list;
|
|
static struct list_head if_list;
|
|
static u32 adapter_count;
|
|
static DEFINE_MUTEX(bnx2fc_dev_lock);
|
|
DEFINE_PER_CPU(struct bnx2fc_percpu_s, bnx2fc_percpu);
|
|
|
|
#define DRV_MODULE_NAME "bnx2fc"
|
|
#define DRV_MODULE_VERSION BNX2FC_VERSION
|
|
#define DRV_MODULE_RELDATE "October 15, 2015"
|
|
|
|
|
|
static char version[] =
|
|
"QLogic FCoE Driver " DRV_MODULE_NAME \
|
|
" v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
|
|
|
|
|
|
MODULE_AUTHOR("Bhanu Prakash Gollapudi <bprakash@broadcom.com>");
|
|
MODULE_DESCRIPTION("QLogic FCoE Driver");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_VERSION(DRV_MODULE_VERSION);
|
|
|
|
#define BNX2FC_MAX_QUEUE_DEPTH 256
|
|
#define BNX2FC_MIN_QUEUE_DEPTH 32
|
|
#define FCOE_WORD_TO_BYTE 4
|
|
|
|
static struct scsi_transport_template *bnx2fc_transport_template;
|
|
static struct scsi_transport_template *bnx2fc_vport_xport_template;
|
|
|
|
struct workqueue_struct *bnx2fc_wq;
|
|
|
|
/* bnx2fc structure needs only one instance of the fcoe_percpu_s structure.
|
|
* Here the io threads are per cpu but the l2 thread is just one
|
|
*/
|
|
struct fcoe_percpu_s bnx2fc_global;
|
|
DEFINE_SPINLOCK(bnx2fc_global_lock);
|
|
|
|
static struct cnic_ulp_ops bnx2fc_cnic_cb;
|
|
static struct libfc_function_template bnx2fc_libfc_fcn_templ;
|
|
static struct scsi_host_template bnx2fc_shost_template;
|
|
static struct fc_function_template bnx2fc_transport_function;
|
|
static struct fcoe_sysfs_function_template bnx2fc_fcoe_sysfs_templ;
|
|
static struct fc_function_template bnx2fc_vport_xport_function;
|
|
static int bnx2fc_create(struct net_device *netdev, enum fip_mode fip_mode);
|
|
static void __bnx2fc_destroy(struct bnx2fc_interface *interface);
|
|
static int bnx2fc_destroy(struct net_device *net_device);
|
|
static int bnx2fc_enable(struct net_device *netdev);
|
|
static int bnx2fc_disable(struct net_device *netdev);
|
|
|
|
/* fcoe_syfs control interface handlers */
|
|
static int bnx2fc_ctlr_alloc(struct net_device *netdev);
|
|
static int bnx2fc_ctlr_enabled(struct fcoe_ctlr_device *cdev);
|
|
|
|
static void bnx2fc_recv_frame(struct sk_buff *skb);
|
|
|
|
static void bnx2fc_start_disc(struct bnx2fc_interface *interface);
|
|
static int bnx2fc_shost_config(struct fc_lport *lport, struct device *dev);
|
|
static int bnx2fc_lport_config(struct fc_lport *lport);
|
|
static int bnx2fc_em_config(struct fc_lport *lport, struct bnx2fc_hba *hba);
|
|
static int bnx2fc_bind_adapter_devices(struct bnx2fc_hba *hba);
|
|
static void bnx2fc_unbind_adapter_devices(struct bnx2fc_hba *hba);
|
|
static int bnx2fc_bind_pcidev(struct bnx2fc_hba *hba);
|
|
static void bnx2fc_unbind_pcidev(struct bnx2fc_hba *hba);
|
|
static struct fc_lport *bnx2fc_if_create(struct bnx2fc_interface *interface,
|
|
struct device *parent, int npiv);
|
|
static void bnx2fc_destroy_work(struct work_struct *work);
|
|
|
|
static struct bnx2fc_hba *bnx2fc_hba_lookup(struct net_device *phys_dev);
|
|
static struct bnx2fc_interface *bnx2fc_interface_lookup(struct net_device
|
|
*phys_dev);
|
|
static inline void bnx2fc_interface_put(struct bnx2fc_interface *interface);
|
|
static struct bnx2fc_hba *bnx2fc_find_hba_for_cnic(struct cnic_dev *cnic);
|
|
|
|
static int bnx2fc_fw_init(struct bnx2fc_hba *hba);
|
|
static void bnx2fc_fw_destroy(struct bnx2fc_hba *hba);
|
|
|
|
static void bnx2fc_port_shutdown(struct fc_lport *lport);
|
|
static void bnx2fc_stop(struct bnx2fc_interface *interface);
|
|
static int __init bnx2fc_mod_init(void);
|
|
static void __exit bnx2fc_mod_exit(void);
|
|
|
|
unsigned int bnx2fc_debug_level;
|
|
module_param_named(debug_logging, bnx2fc_debug_level, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(debug_logging,
|
|
"Option to enable extended logging,\n"
|
|
"\t\tDefault is 0 - no logging.\n"
|
|
"\t\t0x01 - SCSI cmd error, cleanup.\n"
|
|
"\t\t0x02 - Session setup, cleanup, etc.\n"
|
|
"\t\t0x04 - lport events, link, mtu, etc.\n"
|
|
"\t\t0x08 - ELS logs.\n"
|
|
"\t\t0x10 - fcoe L2 fame related logs.\n"
|
|
"\t\t0xff - LOG all messages.");
|
|
|
|
uint bnx2fc_devloss_tmo;
|
|
module_param_named(devloss_tmo, bnx2fc_devloss_tmo, uint, S_IRUGO);
|
|
MODULE_PARM_DESC(devloss_tmo, " Change devloss_tmo for the remote ports "
|
|
"attached via bnx2fc.");
|
|
|
|
uint bnx2fc_max_luns = BNX2FC_MAX_LUN;
|
|
module_param_named(max_luns, bnx2fc_max_luns, uint, S_IRUGO);
|
|
MODULE_PARM_DESC(max_luns, " Change the default max_lun per SCSI host. Default "
|
|
"0xffff.");
|
|
|
|
uint bnx2fc_queue_depth;
|
|
module_param_named(queue_depth, bnx2fc_queue_depth, uint, S_IRUGO);
|
|
MODULE_PARM_DESC(queue_depth, " Change the default queue depth of SCSI devices "
|
|
"attached via bnx2fc.");
|
|
|
|
uint bnx2fc_log_fka;
|
|
module_param_named(log_fka, bnx2fc_log_fka, uint, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(log_fka, " Print message to kernel log when fcoe is "
|
|
"initiating a FIP keep alive when debug logging is enabled.");
|
|
|
|
static inline struct net_device *bnx2fc_netdev(const struct fc_lport *lport)
|
|
{
|
|
return ((struct bnx2fc_interface *)
|
|
((struct fcoe_port *)lport_priv(lport))->priv)->netdev;
|
|
}
|
|
|
|
static void bnx2fc_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev)
|
|
{
|
|
struct fcoe_ctlr_device *ctlr_dev =
|
|
fcoe_fcf_dev_to_ctlr_dev(fcf_dev);
|
|
struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev);
|
|
struct bnx2fc_interface *fcoe = fcoe_ctlr_priv(ctlr);
|
|
|
|
fcf_dev->vlan_id = fcoe->vlan_id;
|
|
}
|
|
|
|
static void bnx2fc_clean_rx_queue(struct fc_lport *lp)
|
|
{
|
|
struct fcoe_percpu_s *bg;
|
|
struct fcoe_rcv_info *fr;
|
|
struct sk_buff_head *list;
|
|
struct sk_buff *skb, *next;
|
|
|
|
bg = &bnx2fc_global;
|
|
spin_lock_bh(&bg->fcoe_rx_list.lock);
|
|
list = &bg->fcoe_rx_list;
|
|
skb_queue_walk_safe(list, skb, next) {
|
|
fr = fcoe_dev_from_skb(skb);
|
|
if (fr->fr_dev == lp) {
|
|
__skb_unlink(skb, list);
|
|
kfree_skb(skb);
|
|
}
|
|
}
|
|
spin_unlock_bh(&bg->fcoe_rx_list.lock);
|
|
}
|
|
|
|
int bnx2fc_get_paged_crc_eof(struct sk_buff *skb, int tlen)
|
|
{
|
|
int rc;
|
|
spin_lock(&bnx2fc_global_lock);
|
|
rc = fcoe_get_paged_crc_eof(skb, tlen, &bnx2fc_global);
|
|
spin_unlock(&bnx2fc_global_lock);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void bnx2fc_abort_io(struct fc_lport *lport)
|
|
{
|
|
/*
|
|
* This function is no-op for bnx2fc, but we do
|
|
* not want to leave it as NULL either, as libfc
|
|
* can call the default function which is
|
|
* fc_fcp_abort_io.
|
|
*/
|
|
}
|
|
|
|
static void bnx2fc_cleanup(struct fc_lport *lport)
|
|
{
|
|
struct fcoe_port *port = lport_priv(lport);
|
|
struct bnx2fc_interface *interface = port->priv;
|
|
struct bnx2fc_hba *hba = interface->hba;
|
|
struct bnx2fc_rport *tgt;
|
|
int i;
|
|
|
|
BNX2FC_MISC_DBG("Entered %s\n", __func__);
|
|
mutex_lock(&hba->hba_mutex);
|
|
spin_lock_bh(&hba->hba_lock);
|
|
for (i = 0; i < BNX2FC_NUM_MAX_SESS; i++) {
|
|
tgt = hba->tgt_ofld_list[i];
|
|
if (tgt) {
|
|
/* Cleanup IOs belonging to requested vport */
|
|
if (tgt->port == port) {
|
|
spin_unlock_bh(&hba->hba_lock);
|
|
BNX2FC_TGT_DBG(tgt, "flush/cleanup\n");
|
|
bnx2fc_flush_active_ios(tgt);
|
|
spin_lock_bh(&hba->hba_lock);
|
|
}
|
|
}
|
|
}
|
|
spin_unlock_bh(&hba->hba_lock);
|
|
mutex_unlock(&hba->hba_mutex);
|
|
}
|
|
|
|
static int bnx2fc_xmit_l2_frame(struct bnx2fc_rport *tgt,
|
|
struct fc_frame *fp)
|
|
{
|
|
struct fc_rport_priv *rdata = tgt->rdata;
|
|
struct fc_frame_header *fh;
|
|
int rc = 0;
|
|
|
|
fh = fc_frame_header_get(fp);
|
|
BNX2FC_TGT_DBG(tgt, "Xmit L2 frame rport = 0x%x, oxid = 0x%x, "
|
|
"r_ctl = 0x%x\n", rdata->ids.port_id,
|
|
ntohs(fh->fh_ox_id), fh->fh_r_ctl);
|
|
if ((fh->fh_type == FC_TYPE_ELS) &&
|
|
(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
|
|
|
|
switch (fc_frame_payload_op(fp)) {
|
|
case ELS_ADISC:
|
|
rc = bnx2fc_send_adisc(tgt, fp);
|
|
break;
|
|
case ELS_LOGO:
|
|
rc = bnx2fc_send_logo(tgt, fp);
|
|
break;
|
|
case ELS_RLS:
|
|
rc = bnx2fc_send_rls(tgt, fp);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if ((fh->fh_type == FC_TYPE_BLS) &&
|
|
(fh->fh_r_ctl == FC_RCTL_BA_ABTS))
|
|
BNX2FC_TGT_DBG(tgt, "ABTS frame\n");
|
|
else {
|
|
BNX2FC_TGT_DBG(tgt, "Send L2 frame type 0x%x "
|
|
"rctl 0x%x thru non-offload path\n",
|
|
fh->fh_type, fh->fh_r_ctl);
|
|
return -ENODEV;
|
|
}
|
|
if (rc)
|
|
return -ENOMEM;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_xmit - bnx2fc's FCoE frame transmit function
|
|
*
|
|
* @lport: the associated local port
|
|
* @fp: the fc_frame to be transmitted
|
|
*/
|
|
static int bnx2fc_xmit(struct fc_lport *lport, struct fc_frame *fp)
|
|
{
|
|
struct ethhdr *eh;
|
|
struct fcoe_crc_eof *cp;
|
|
struct sk_buff *skb;
|
|
struct fc_frame_header *fh;
|
|
struct bnx2fc_interface *interface;
|
|
struct fcoe_ctlr *ctlr;
|
|
struct bnx2fc_hba *hba;
|
|
struct fcoe_port *port;
|
|
struct fcoe_hdr *hp;
|
|
struct bnx2fc_rport *tgt;
|
|
struct fc_stats *stats;
|
|
u8 sof, eof;
|
|
u32 crc;
|
|
unsigned int hlen, tlen, elen;
|
|
int wlen, rc = 0;
|
|
|
|
port = (struct fcoe_port *)lport_priv(lport);
|
|
interface = port->priv;
|
|
ctlr = bnx2fc_to_ctlr(interface);
|
|
hba = interface->hba;
|
|
|
|
fh = fc_frame_header_get(fp);
|
|
|
|
skb = fp_skb(fp);
|
|
if (!lport->link_up) {
|
|
BNX2FC_HBA_DBG(lport, "bnx2fc_xmit link down\n");
|
|
kfree_skb(skb);
|
|
return 0;
|
|
}
|
|
|
|
if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ)) {
|
|
if (!ctlr->sel_fcf) {
|
|
BNX2FC_HBA_DBG(lport, "FCF not selected yet!\n");
|
|
kfree_skb(skb);
|
|
return -EINVAL;
|
|
}
|
|
if (fcoe_ctlr_els_send(ctlr, lport, skb))
|
|
return 0;
|
|
}
|
|
|
|
sof = fr_sof(fp);
|
|
eof = fr_eof(fp);
|
|
|
|
/*
|
|
* Snoop the frame header to check if the frame is for
|
|
* an offloaded session
|
|
*/
|
|
/*
|
|
* tgt_ofld_list access is synchronized using
|
|
* both hba mutex and hba lock. Atleast hba mutex or
|
|
* hba lock needs to be held for read access.
|
|
*/
|
|
|
|
spin_lock_bh(&hba->hba_lock);
|
|
tgt = bnx2fc_tgt_lookup(port, ntoh24(fh->fh_d_id));
|
|
if (tgt && (test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags))) {
|
|
/* This frame is for offloaded session */
|
|
BNX2FC_HBA_DBG(lport, "xmit: Frame is for offloaded session "
|
|
"port_id = 0x%x\n", ntoh24(fh->fh_d_id));
|
|
spin_unlock_bh(&hba->hba_lock);
|
|
rc = bnx2fc_xmit_l2_frame(tgt, fp);
|
|
if (rc != -ENODEV) {
|
|
kfree_skb(skb);
|
|
return rc;
|
|
}
|
|
} else {
|
|
spin_unlock_bh(&hba->hba_lock);
|
|
}
|
|
|
|
elen = sizeof(struct ethhdr);
|
|
hlen = sizeof(struct fcoe_hdr);
|
|
tlen = sizeof(struct fcoe_crc_eof);
|
|
wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
|
|
|
|
skb->ip_summed = CHECKSUM_NONE;
|
|
crc = fcoe_fc_crc(fp);
|
|
|
|
/* copy port crc and eof to the skb buff */
|
|
if (skb_is_nonlinear(skb)) {
|
|
skb_frag_t *frag;
|
|
if (bnx2fc_get_paged_crc_eof(skb, tlen)) {
|
|
kfree_skb(skb);
|
|
return -ENOMEM;
|
|
}
|
|
frag = &skb_shinfo(skb)->frags[skb_shinfo(skb)->nr_frags - 1];
|
|
cp = kmap_atomic(skb_frag_page(frag)) + skb_frag_off(frag);
|
|
} else {
|
|
cp = skb_put(skb, tlen);
|
|
}
|
|
|
|
memset(cp, 0, sizeof(*cp));
|
|
cp->fcoe_eof = eof;
|
|
cp->fcoe_crc32 = cpu_to_le32(~crc);
|
|
if (skb_is_nonlinear(skb)) {
|
|
kunmap_atomic(cp);
|
|
cp = NULL;
|
|
}
|
|
|
|
/* adjust skb network/transport offsets to match mac/fcoe/port */
|
|
skb_push(skb, elen + hlen);
|
|
skb_reset_mac_header(skb);
|
|
skb_reset_network_header(skb);
|
|
skb->mac_len = elen;
|
|
skb->protocol = htons(ETH_P_FCOE);
|
|
skb->dev = interface->netdev;
|
|
|
|
/* fill up mac and fcoe headers */
|
|
eh = eth_hdr(skb);
|
|
eh->h_proto = htons(ETH_P_FCOE);
|
|
if (ctlr->map_dest)
|
|
fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
|
|
else
|
|
/* insert GW address */
|
|
memcpy(eh->h_dest, ctlr->dest_addr, ETH_ALEN);
|
|
|
|
if (unlikely(ctlr->flogi_oxid != FC_XID_UNKNOWN))
|
|
memcpy(eh->h_source, ctlr->ctl_src_addr, ETH_ALEN);
|
|
else
|
|
memcpy(eh->h_source, port->data_src_addr, ETH_ALEN);
|
|
|
|
hp = (struct fcoe_hdr *)(eh + 1);
|
|
memset(hp, 0, sizeof(*hp));
|
|
if (FC_FCOE_VER)
|
|
FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER);
|
|
hp->fcoe_sof = sof;
|
|
|
|
/* fcoe lso, mss is in max_payload which is non-zero for FCP data */
|
|
if (lport->seq_offload && fr_max_payload(fp)) {
|
|
skb_shinfo(skb)->gso_type = SKB_GSO_FCOE;
|
|
skb_shinfo(skb)->gso_size = fr_max_payload(fp);
|
|
} else {
|
|
skb_shinfo(skb)->gso_type = 0;
|
|
skb_shinfo(skb)->gso_size = 0;
|
|
}
|
|
|
|
/*update tx stats */
|
|
stats = per_cpu_ptr(lport->stats, get_cpu());
|
|
stats->TxFrames++;
|
|
stats->TxWords += wlen;
|
|
put_cpu();
|
|
|
|
/* send down to lld */
|
|
fr_dev(fp) = lport;
|
|
if (port->fcoe_pending_queue.qlen)
|
|
fcoe_check_wait_queue(lport, skb);
|
|
else if (fcoe_start_io(skb))
|
|
fcoe_check_wait_queue(lport, skb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_rcv - This is bnx2fc's receive function called by NET_RX_SOFTIRQ
|
|
*
|
|
* @skb: the receive socket buffer
|
|
* @dev: associated net device
|
|
* @ptype: context
|
|
* @olddev: last device
|
|
*
|
|
* This function receives the packet and builds FC frame and passes it up
|
|
*/
|
|
static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev,
|
|
struct packet_type *ptype, struct net_device *olddev)
|
|
{
|
|
struct fc_lport *lport;
|
|
struct bnx2fc_interface *interface;
|
|
struct fcoe_ctlr *ctlr;
|
|
struct fcoe_rcv_info *fr;
|
|
struct fcoe_percpu_s *bg;
|
|
struct sk_buff *tmp_skb;
|
|
|
|
interface = container_of(ptype, struct bnx2fc_interface,
|
|
fcoe_packet_type);
|
|
ctlr = bnx2fc_to_ctlr(interface);
|
|
lport = ctlr->lp;
|
|
|
|
if (unlikely(lport == NULL)) {
|
|
printk(KERN_ERR PFX "bnx2fc_rcv: lport is NULL\n");
|
|
goto err;
|
|
}
|
|
|
|
tmp_skb = skb_share_check(skb, GFP_ATOMIC);
|
|
if (!tmp_skb)
|
|
goto err;
|
|
|
|
skb = tmp_skb;
|
|
|
|
if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
|
|
printk(KERN_ERR PFX "bnx2fc_rcv: Wrong FC type frame\n");
|
|
goto err;
|
|
}
|
|
|
|
/*
|
|
* Check for minimum frame length, and make sure required FCoE
|
|
* and FC headers are pulled into the linear data area.
|
|
*/
|
|
if (unlikely((skb->len < FCOE_MIN_FRAME) ||
|
|
!pskb_may_pull(skb, FCOE_HEADER_LEN)))
|
|
goto err;
|
|
|
|
skb_set_transport_header(skb, sizeof(struct fcoe_hdr));
|
|
|
|
fr = fcoe_dev_from_skb(skb);
|
|
fr->fr_dev = lport;
|
|
|
|
bg = &bnx2fc_global;
|
|
spin_lock(&bg->fcoe_rx_list.lock);
|
|
|
|
__skb_queue_tail(&bg->fcoe_rx_list, skb);
|
|
if (bg->fcoe_rx_list.qlen == 1)
|
|
wake_up_process(bg->kthread);
|
|
|
|
spin_unlock(&bg->fcoe_rx_list.lock);
|
|
|
|
return 0;
|
|
err:
|
|
kfree_skb(skb);
|
|
return -1;
|
|
}
|
|
|
|
static int bnx2fc_l2_rcv_thread(void *arg)
|
|
{
|
|
struct fcoe_percpu_s *bg = arg;
|
|
struct sk_buff *skb;
|
|
|
|
set_user_nice(current, MIN_NICE);
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
while (!kthread_should_stop()) {
|
|
schedule();
|
|
spin_lock_bh(&bg->fcoe_rx_list.lock);
|
|
while ((skb = __skb_dequeue(&bg->fcoe_rx_list)) != NULL) {
|
|
spin_unlock_bh(&bg->fcoe_rx_list.lock);
|
|
bnx2fc_recv_frame(skb);
|
|
spin_lock_bh(&bg->fcoe_rx_list.lock);
|
|
}
|
|
__set_current_state(TASK_INTERRUPTIBLE);
|
|
spin_unlock_bh(&bg->fcoe_rx_list.lock);
|
|
}
|
|
__set_current_state(TASK_RUNNING);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void bnx2fc_recv_frame(struct sk_buff *skb)
|
|
{
|
|
u32 fr_len;
|
|
struct fc_lport *lport;
|
|
struct fcoe_rcv_info *fr;
|
|
struct fc_stats *stats;
|
|
struct fc_frame_header *fh;
|
|
struct fcoe_crc_eof crc_eof;
|
|
struct fc_frame *fp;
|
|
struct fc_lport *vn_port;
|
|
struct fcoe_port *port, *phys_port;
|
|
u8 *mac = NULL;
|
|
u8 *dest_mac = NULL;
|
|
struct fcoe_hdr *hp;
|
|
struct bnx2fc_interface *interface;
|
|
struct fcoe_ctlr *ctlr;
|
|
|
|
fr = fcoe_dev_from_skb(skb);
|
|
lport = fr->fr_dev;
|
|
if (unlikely(lport == NULL)) {
|
|
printk(KERN_ERR PFX "Invalid lport struct\n");
|
|
kfree_skb(skb);
|
|
return;
|
|
}
|
|
|
|
if (skb_is_nonlinear(skb))
|
|
skb_linearize(skb);
|
|
mac = eth_hdr(skb)->h_source;
|
|
dest_mac = eth_hdr(skb)->h_dest;
|
|
|
|
/* Pull the header */
|
|
hp = (struct fcoe_hdr *) skb_network_header(skb);
|
|
fh = (struct fc_frame_header *) skb_transport_header(skb);
|
|
skb_pull(skb, sizeof(struct fcoe_hdr));
|
|
fr_len = skb->len - sizeof(struct fcoe_crc_eof);
|
|
|
|
fp = (struct fc_frame *)skb;
|
|
fc_frame_init(fp);
|
|
fr_dev(fp) = lport;
|
|
fr_sof(fp) = hp->fcoe_sof;
|
|
if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
|
|
kfree_skb(skb);
|
|
return;
|
|
}
|
|
fr_eof(fp) = crc_eof.fcoe_eof;
|
|
fr_crc(fp) = crc_eof.fcoe_crc32;
|
|
if (pskb_trim(skb, fr_len)) {
|
|
kfree_skb(skb);
|
|
return;
|
|
}
|
|
|
|
phys_port = lport_priv(lport);
|
|
interface = phys_port->priv;
|
|
ctlr = bnx2fc_to_ctlr(interface);
|
|
|
|
fh = fc_frame_header_get(fp);
|
|
|
|
if (ntoh24(&dest_mac[3]) != ntoh24(fh->fh_d_id)) {
|
|
BNX2FC_HBA_DBG(lport, "FC frame d_id mismatch with MAC %pM.\n",
|
|
dest_mac);
|
|
kfree_skb(skb);
|
|
return;
|
|
}
|
|
|
|
vn_port = fc_vport_id_lookup(lport, ntoh24(fh->fh_d_id));
|
|
if (vn_port) {
|
|
port = lport_priv(vn_port);
|
|
if (!ether_addr_equal(port->data_src_addr, dest_mac)) {
|
|
BNX2FC_HBA_DBG(lport, "fpma mismatch\n");
|
|
kfree_skb(skb);
|
|
return;
|
|
}
|
|
}
|
|
if (ctlr->state) {
|
|
if (!ether_addr_equal(mac, ctlr->dest_addr)) {
|
|
BNX2FC_HBA_DBG(lport, "Wrong source address: mac:%pM dest_addr:%pM.\n",
|
|
mac, ctlr->dest_addr);
|
|
kfree_skb(skb);
|
|
return;
|
|
}
|
|
}
|
|
if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA &&
|
|
fh->fh_type == FC_TYPE_FCP) {
|
|
/* Drop FCP data. We dont this in L2 path */
|
|
kfree_skb(skb);
|
|
return;
|
|
}
|
|
if (fh->fh_r_ctl == FC_RCTL_ELS_REQ &&
|
|
fh->fh_type == FC_TYPE_ELS) {
|
|
switch (fc_frame_payload_op(fp)) {
|
|
case ELS_LOGO:
|
|
if (ntoh24(fh->fh_s_id) == FC_FID_FLOGI) {
|
|
/* drop non-FIP LOGO */
|
|
kfree_skb(skb);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fh->fh_r_ctl == FC_RCTL_BA_ABTS) {
|
|
/* Drop incoming ABTS */
|
|
kfree_skb(skb);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If the destination ID from the frame header does not match what we
|
|
* have on record for lport and the search for a NPIV port came up
|
|
* empty then this is not addressed to our port so simply drop it.
|
|
*/
|
|
if (lport->port_id != ntoh24(fh->fh_d_id) && !vn_port) {
|
|
BNX2FC_HBA_DBG(lport, "Dropping frame due to destination mismatch: lport->port_id=%x fh->d_id=%x.\n",
|
|
lport->port_id, ntoh24(fh->fh_d_id));
|
|
kfree_skb(skb);
|
|
return;
|
|
}
|
|
|
|
stats = per_cpu_ptr(lport->stats, smp_processor_id());
|
|
stats->RxFrames++;
|
|
stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
|
|
|
|
if (le32_to_cpu(fr_crc(fp)) !=
|
|
~crc32(~0, skb->data, fr_len)) {
|
|
if (stats->InvalidCRCCount < 5)
|
|
printk(KERN_WARNING PFX "dropping frame with "
|
|
"CRC error\n");
|
|
stats->InvalidCRCCount++;
|
|
kfree_skb(skb);
|
|
return;
|
|
}
|
|
fc_exch_recv(lport, fp);
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_percpu_io_thread - thread per cpu for ios
|
|
*
|
|
* @arg: ptr to bnx2fc_percpu_info structure
|
|
*/
|
|
static int bnx2fc_percpu_io_thread(void *arg)
|
|
{
|
|
struct bnx2fc_percpu_s *p = arg;
|
|
struct bnx2fc_work *work, *tmp;
|
|
LIST_HEAD(work_list);
|
|
|
|
set_user_nice(current, MIN_NICE);
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
while (!kthread_should_stop()) {
|
|
schedule();
|
|
spin_lock_bh(&p->fp_work_lock);
|
|
while (!list_empty(&p->work_list)) {
|
|
list_splice_init(&p->work_list, &work_list);
|
|
spin_unlock_bh(&p->fp_work_lock);
|
|
|
|
list_for_each_entry_safe(work, tmp, &work_list, list) {
|
|
list_del_init(&work->list);
|
|
bnx2fc_process_cq_compl(work->tgt, work->wqe);
|
|
kfree(work);
|
|
}
|
|
|
|
spin_lock_bh(&p->fp_work_lock);
|
|
}
|
|
__set_current_state(TASK_INTERRUPTIBLE);
|
|
spin_unlock_bh(&p->fp_work_lock);
|
|
}
|
|
__set_current_state(TASK_RUNNING);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct fc_host_statistics *bnx2fc_get_host_stats(struct Scsi_Host *shost)
|
|
{
|
|
struct fc_host_statistics *bnx2fc_stats;
|
|
struct fc_lport *lport = shost_priv(shost);
|
|
struct fcoe_port *port = lport_priv(lport);
|
|
struct bnx2fc_interface *interface = port->priv;
|
|
struct bnx2fc_hba *hba = interface->hba;
|
|
struct fcoe_statistics_params *fw_stats;
|
|
int rc = 0;
|
|
|
|
fw_stats = (struct fcoe_statistics_params *)hba->stats_buffer;
|
|
if (!fw_stats)
|
|
return NULL;
|
|
|
|
mutex_lock(&hba->hba_stats_mutex);
|
|
|
|
bnx2fc_stats = fc_get_host_stats(shost);
|
|
|
|
init_completion(&hba->stat_req_done);
|
|
if (bnx2fc_send_stat_req(hba))
|
|
goto unlock_stats_mutex;
|
|
rc = wait_for_completion_timeout(&hba->stat_req_done, (2 * HZ));
|
|
if (!rc) {
|
|
BNX2FC_HBA_DBG(lport, "FW stat req timed out\n");
|
|
goto unlock_stats_mutex;
|
|
}
|
|
BNX2FC_STATS(hba, rx_stat2, fc_crc_cnt);
|
|
bnx2fc_stats->invalid_crc_count += hba->bfw_stats.fc_crc_cnt;
|
|
BNX2FC_STATS(hba, tx_stat, fcoe_tx_pkt_cnt);
|
|
bnx2fc_stats->tx_frames += hba->bfw_stats.fcoe_tx_pkt_cnt;
|
|
BNX2FC_STATS(hba, tx_stat, fcoe_tx_byte_cnt);
|
|
bnx2fc_stats->tx_words += ((hba->bfw_stats.fcoe_tx_byte_cnt) / 4);
|
|
BNX2FC_STATS(hba, rx_stat0, fcoe_rx_pkt_cnt);
|
|
bnx2fc_stats->rx_frames += hba->bfw_stats.fcoe_rx_pkt_cnt;
|
|
BNX2FC_STATS(hba, rx_stat0, fcoe_rx_byte_cnt);
|
|
bnx2fc_stats->rx_words += ((hba->bfw_stats.fcoe_rx_byte_cnt) / 4);
|
|
|
|
bnx2fc_stats->dumped_frames = 0;
|
|
bnx2fc_stats->lip_count = 0;
|
|
bnx2fc_stats->nos_count = 0;
|
|
bnx2fc_stats->loss_of_sync_count = 0;
|
|
bnx2fc_stats->loss_of_signal_count = 0;
|
|
bnx2fc_stats->prim_seq_protocol_err_count = 0;
|
|
|
|
memcpy(&hba->prev_stats, hba->stats_buffer,
|
|
sizeof(struct fcoe_statistics_params));
|
|
|
|
unlock_stats_mutex:
|
|
mutex_unlock(&hba->hba_stats_mutex);
|
|
return bnx2fc_stats;
|
|
}
|
|
|
|
static int bnx2fc_shost_config(struct fc_lport *lport, struct device *dev)
|
|
{
|
|
struct fcoe_port *port = lport_priv(lport);
|
|
struct bnx2fc_interface *interface = port->priv;
|
|
struct bnx2fc_hba *hba = interface->hba;
|
|
struct Scsi_Host *shost = lport->host;
|
|
int rc = 0;
|
|
|
|
shost->max_cmd_len = BNX2FC_MAX_CMD_LEN;
|
|
shost->max_lun = bnx2fc_max_luns;
|
|
shost->max_id = BNX2FC_MAX_FCP_TGT;
|
|
shost->max_channel = 0;
|
|
if (lport->vport)
|
|
shost->transportt = bnx2fc_vport_xport_template;
|
|
else
|
|
shost->transportt = bnx2fc_transport_template;
|
|
|
|
/* Add the new host to SCSI-ml */
|
|
rc = scsi_add_host(lport->host, dev);
|
|
if (rc) {
|
|
printk(KERN_ERR PFX "Error on scsi_add_host\n");
|
|
return rc;
|
|
}
|
|
if (!lport->vport)
|
|
fc_host_max_npiv_vports(lport->host) = USHRT_MAX;
|
|
snprintf(fc_host_symbolic_name(lport->host), 256,
|
|
"%s (QLogic %s) v%s over %s",
|
|
BNX2FC_NAME, hba->chip_num, BNX2FC_VERSION,
|
|
interface->netdev->name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bnx2fc_link_ok(struct fc_lport *lport)
|
|
{
|
|
struct fcoe_port *port = lport_priv(lport);
|
|
struct bnx2fc_interface *interface = port->priv;
|
|
struct bnx2fc_hba *hba = interface->hba;
|
|
struct net_device *dev = hba->phys_dev;
|
|
int rc = 0;
|
|
|
|
if ((dev->flags & IFF_UP) && netif_carrier_ok(dev))
|
|
clear_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state);
|
|
else {
|
|
set_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state);
|
|
rc = -1;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_get_link_state - get network link state
|
|
*
|
|
* @hba: adapter instance pointer
|
|
*
|
|
* updates adapter structure flag based on netdev state
|
|
*/
|
|
void bnx2fc_get_link_state(struct bnx2fc_hba *hba)
|
|
{
|
|
if (test_bit(__LINK_STATE_NOCARRIER, &hba->phys_dev->state))
|
|
set_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state);
|
|
else
|
|
clear_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state);
|
|
}
|
|
|
|
static int bnx2fc_net_config(struct fc_lport *lport, struct net_device *netdev)
|
|
{
|
|
struct bnx2fc_hba *hba;
|
|
struct bnx2fc_interface *interface;
|
|
struct fcoe_ctlr *ctlr;
|
|
struct fcoe_port *port;
|
|
u64 wwnn, wwpn;
|
|
|
|
port = lport_priv(lport);
|
|
interface = port->priv;
|
|
ctlr = bnx2fc_to_ctlr(interface);
|
|
hba = interface->hba;
|
|
|
|
/* require support for get_pauseparam ethtool op. */
|
|
if (!hba->phys_dev->ethtool_ops ||
|
|
!hba->phys_dev->ethtool_ops->get_pauseparam)
|
|
return -EOPNOTSUPP;
|
|
|
|
if (fc_set_mfs(lport, BNX2FC_MFS))
|
|
return -EINVAL;
|
|
|
|
skb_queue_head_init(&port->fcoe_pending_queue);
|
|
port->fcoe_pending_queue_active = 0;
|
|
timer_setup(&port->timer, fcoe_queue_timer, 0);
|
|
|
|
fcoe_link_speed_update(lport);
|
|
|
|
if (!lport->vport) {
|
|
if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN))
|
|
wwnn = fcoe_wwn_from_mac(ctlr->ctl_src_addr,
|
|
1, 0);
|
|
BNX2FC_HBA_DBG(lport, "WWNN = 0x%llx\n", wwnn);
|
|
fc_set_wwnn(lport, wwnn);
|
|
|
|
if (fcoe_get_wwn(netdev, &wwpn, NETDEV_FCOE_WWPN))
|
|
wwpn = fcoe_wwn_from_mac(ctlr->ctl_src_addr,
|
|
2, 0);
|
|
|
|
BNX2FC_HBA_DBG(lport, "WWPN = 0x%llx\n", wwpn);
|
|
fc_set_wwpn(lport, wwpn);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void bnx2fc_destroy_timer(struct timer_list *t)
|
|
{
|
|
struct bnx2fc_hba *hba = from_timer(hba, t, destroy_timer);
|
|
|
|
printk(KERN_ERR PFX "ERROR:bnx2fc_destroy_timer - "
|
|
"Destroy compl not received!!\n");
|
|
set_bit(BNX2FC_FLAG_DESTROY_CMPL, &hba->flags);
|
|
wake_up_interruptible(&hba->destroy_wait);
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_indicate_netevent - Generic netdev event handler
|
|
*
|
|
* @context: adapter structure pointer
|
|
* @event: event type
|
|
* @vlan_id: vlan id - associated vlan id with this event
|
|
*
|
|
* Handles NETDEV_UP, NETDEV_DOWN, NETDEV_GOING_DOWN,NETDEV_CHANGE and
|
|
* NETDEV_CHANGE_MTU events. Handle NETDEV_UNREGISTER only for vlans.
|
|
*/
|
|
static void bnx2fc_indicate_netevent(void *context, unsigned long event,
|
|
u16 vlan_id)
|
|
{
|
|
struct bnx2fc_hba *hba = (struct bnx2fc_hba *)context;
|
|
struct fcoe_ctlr_device *cdev;
|
|
struct fc_lport *lport;
|
|
struct fc_lport *vport;
|
|
struct bnx2fc_interface *interface, *tmp;
|
|
struct fcoe_ctlr *ctlr;
|
|
int wait_for_upload = 0;
|
|
u32 link_possible = 1;
|
|
|
|
if (vlan_id != 0 && event != NETDEV_UNREGISTER)
|
|
return;
|
|
|
|
switch (event) {
|
|
case NETDEV_UP:
|
|
if (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state))
|
|
printk(KERN_ERR "indicate_netevent: "\
|
|
"hba is not UP!!\n");
|
|
break;
|
|
|
|
case NETDEV_DOWN:
|
|
clear_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state);
|
|
clear_bit(ADAPTER_STATE_UP, &hba->adapter_state);
|
|
link_possible = 0;
|
|
break;
|
|
|
|
case NETDEV_GOING_DOWN:
|
|
set_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state);
|
|
link_possible = 0;
|
|
break;
|
|
|
|
case NETDEV_CHANGE:
|
|
break;
|
|
|
|
case NETDEV_UNREGISTER:
|
|
if (!vlan_id)
|
|
return;
|
|
mutex_lock(&bnx2fc_dev_lock);
|
|
list_for_each_entry_safe(interface, tmp, &if_list, list) {
|
|
if (interface->hba == hba &&
|
|
interface->vlan_id == (vlan_id & VLAN_VID_MASK))
|
|
__bnx2fc_destroy(interface);
|
|
}
|
|
mutex_unlock(&bnx2fc_dev_lock);
|
|
|
|
/* Ensure ALL destroy work has been completed before return */
|
|
flush_workqueue(bnx2fc_wq);
|
|
return;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
mutex_lock(&bnx2fc_dev_lock);
|
|
list_for_each_entry(interface, &if_list, list) {
|
|
|
|
if (interface->hba != hba)
|
|
continue;
|
|
|
|
ctlr = bnx2fc_to_ctlr(interface);
|
|
lport = ctlr->lp;
|
|
BNX2FC_HBA_DBG(lport, "netevent handler - event=%s %ld\n",
|
|
interface->netdev->name, event);
|
|
|
|
fcoe_link_speed_update(lport);
|
|
|
|
cdev = fcoe_ctlr_to_ctlr_dev(ctlr);
|
|
|
|
if (link_possible && !bnx2fc_link_ok(lport)) {
|
|
switch (cdev->enabled) {
|
|
case FCOE_CTLR_DISABLED:
|
|
pr_info("Link up while interface is disabled.\n");
|
|
break;
|
|
case FCOE_CTLR_ENABLED:
|
|
case FCOE_CTLR_UNUSED:
|
|
/* Reset max recv frame size to default */
|
|
fc_set_mfs(lport, BNX2FC_MFS);
|
|
/*
|
|
* ctlr link up will only be handled during
|
|
* enable to avoid sending discovery
|
|
* solicitation on a stale vlan
|
|
*/
|
|
if (interface->enabled)
|
|
fcoe_ctlr_link_up(ctlr);
|
|
};
|
|
} else if (fcoe_ctlr_link_down(ctlr)) {
|
|
switch (cdev->enabled) {
|
|
case FCOE_CTLR_DISABLED:
|
|
pr_info("Link down while interface is disabled.\n");
|
|
break;
|
|
case FCOE_CTLR_ENABLED:
|
|
case FCOE_CTLR_UNUSED:
|
|
mutex_lock(&lport->lp_mutex);
|
|
list_for_each_entry(vport, &lport->vports, list)
|
|
fc_host_port_type(vport->host) =
|
|
FC_PORTTYPE_UNKNOWN;
|
|
mutex_unlock(&lport->lp_mutex);
|
|
fc_host_port_type(lport->host) =
|
|
FC_PORTTYPE_UNKNOWN;
|
|
per_cpu_ptr(lport->stats,
|
|
get_cpu())->LinkFailureCount++;
|
|
put_cpu();
|
|
fcoe_clean_pending_queue(lport);
|
|
wait_for_upload = 1;
|
|
};
|
|
}
|
|
}
|
|
mutex_unlock(&bnx2fc_dev_lock);
|
|
|
|
if (wait_for_upload) {
|
|
clear_bit(ADAPTER_STATE_READY, &hba->adapter_state);
|
|
init_waitqueue_head(&hba->shutdown_wait);
|
|
BNX2FC_MISC_DBG("indicate_netevent "
|
|
"num_ofld_sess = %d\n",
|
|
hba->num_ofld_sess);
|
|
hba->wait_for_link_down = 1;
|
|
wait_event_interruptible(hba->shutdown_wait,
|
|
(hba->num_ofld_sess == 0));
|
|
BNX2FC_MISC_DBG("wakeup - num_ofld_sess = %d\n",
|
|
hba->num_ofld_sess);
|
|
hba->wait_for_link_down = 0;
|
|
|
|
if (signal_pending(current))
|
|
flush_signals(current);
|
|
}
|
|
}
|
|
|
|
static int bnx2fc_libfc_config(struct fc_lport *lport)
|
|
{
|
|
|
|
/* Set the function pointers set by bnx2fc driver */
|
|
memcpy(&lport->tt, &bnx2fc_libfc_fcn_templ,
|
|
sizeof(struct libfc_function_template));
|
|
fc_elsct_init(lport);
|
|
fc_exch_init(lport);
|
|
fc_disc_init(lport);
|
|
fc_disc_config(lport, lport);
|
|
return 0;
|
|
}
|
|
|
|
static int bnx2fc_em_config(struct fc_lport *lport, struct bnx2fc_hba *hba)
|
|
{
|
|
int fcoe_min_xid, fcoe_max_xid;
|
|
|
|
fcoe_min_xid = hba->max_xid + 1;
|
|
if (nr_cpu_ids <= 2)
|
|
fcoe_max_xid = hba->max_xid + FCOE_XIDS_PER_CPU_OFFSET;
|
|
else
|
|
fcoe_max_xid = hba->max_xid + FCOE_MAX_XID_OFFSET;
|
|
if (!fc_exch_mgr_alloc(lport, FC_CLASS_3, fcoe_min_xid,
|
|
fcoe_max_xid, NULL)) {
|
|
printk(KERN_ERR PFX "em_config:fc_exch_mgr_alloc failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bnx2fc_lport_config(struct fc_lport *lport)
|
|
{
|
|
lport->link_up = 0;
|
|
lport->qfull = 0;
|
|
lport->max_retry_count = BNX2FC_MAX_RETRY_CNT;
|
|
lport->max_rport_retry_count = BNX2FC_MAX_RPORT_RETRY_CNT;
|
|
lport->e_d_tov = 2 * 1000;
|
|
lport->r_a_tov = 10 * 1000;
|
|
|
|
lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
|
|
FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
|
|
lport->does_npiv = 1;
|
|
|
|
memset(&lport->rnid_gen, 0, sizeof(struct fc_els_rnid_gen));
|
|
lport->rnid_gen.rnid_atype = BNX2FC_RNID_HBA;
|
|
|
|
/* alloc stats structure */
|
|
if (fc_lport_init_stats(lport))
|
|
return -ENOMEM;
|
|
|
|
/* Finish fc_lport configuration */
|
|
fc_lport_config(lport);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_fip_recv - handle a received FIP frame.
|
|
*
|
|
* @skb: the received skb
|
|
* @dev: associated &net_device
|
|
* @ptype: the &packet_type structure which was used to register this handler.
|
|
* @orig_dev: original receive &net_device, in case @ dev is a bond.
|
|
*
|
|
* Returns: 0 for success
|
|
*/
|
|
static int bnx2fc_fip_recv(struct sk_buff *skb, struct net_device *dev,
|
|
struct packet_type *ptype,
|
|
struct net_device *orig_dev)
|
|
{
|
|
struct bnx2fc_interface *interface;
|
|
struct fcoe_ctlr *ctlr;
|
|
interface = container_of(ptype, struct bnx2fc_interface,
|
|
fip_packet_type);
|
|
ctlr = bnx2fc_to_ctlr(interface);
|
|
fcoe_ctlr_recv(ctlr, skb);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_update_src_mac - Update Ethernet MAC filters.
|
|
*
|
|
* @fip: FCoE controller.
|
|
* @old: Unicast MAC address to delete if the MAC is non-zero.
|
|
* @new: Unicast MAC address to add.
|
|
*
|
|
* Remove any previously-set unicast MAC filter.
|
|
* Add secondary FCoE MAC address filter for our OUI.
|
|
*/
|
|
static void bnx2fc_update_src_mac(struct fc_lport *lport, u8 *addr)
|
|
{
|
|
struct fcoe_port *port = lport_priv(lport);
|
|
|
|
memcpy(port->data_src_addr, addr, ETH_ALEN);
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_get_src_mac - return the ethernet source address for an lport
|
|
*
|
|
* @lport: libfc port
|
|
*/
|
|
static u8 *bnx2fc_get_src_mac(struct fc_lport *lport)
|
|
{
|
|
struct fcoe_port *port;
|
|
|
|
port = (struct fcoe_port *)lport_priv(lport);
|
|
return port->data_src_addr;
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_fip_send - send an Ethernet-encapsulated FIP frame.
|
|
*
|
|
* @fip: FCoE controller.
|
|
* @skb: FIP Packet.
|
|
*/
|
|
static void bnx2fc_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
|
|
{
|
|
struct fip_header *fiph;
|
|
struct ethhdr *eth_hdr;
|
|
u16 op;
|
|
u8 sub;
|
|
|
|
fiph = (struct fip_header *) ((void *)skb->data + 2 * ETH_ALEN + 2);
|
|
eth_hdr = (struct ethhdr *)skb_mac_header(skb);
|
|
op = ntohs(fiph->fip_op);
|
|
sub = fiph->fip_subcode;
|
|
|
|
if (op == FIP_OP_CTRL && sub == FIP_SC_SOL && bnx2fc_log_fka)
|
|
BNX2FC_MISC_DBG("Sending FKA from %pM to %pM.\n",
|
|
eth_hdr->h_source, eth_hdr->h_dest);
|
|
|
|
skb->dev = bnx2fc_from_ctlr(fip)->netdev;
|
|
dev_queue_xmit(skb);
|
|
}
|
|
|
|
static int bnx2fc_vport_create(struct fc_vport *vport, bool disabled)
|
|
{
|
|
struct Scsi_Host *shost = vport_to_shost(vport);
|
|
struct fc_lport *n_port = shost_priv(shost);
|
|
struct fcoe_port *port = lport_priv(n_port);
|
|
struct bnx2fc_interface *interface = port->priv;
|
|
struct net_device *netdev = interface->netdev;
|
|
struct fc_lport *vn_port;
|
|
int rc;
|
|
char buf[32];
|
|
|
|
rc = fcoe_validate_vport_create(vport);
|
|
if (rc) {
|
|
fcoe_wwn_to_str(vport->port_name, buf, sizeof(buf));
|
|
printk(KERN_ERR PFX "Failed to create vport, "
|
|
"WWPN (0x%s) already exists\n",
|
|
buf);
|
|
return rc;
|
|
}
|
|
|
|
if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &interface->hba->flags)) {
|
|
printk(KERN_ERR PFX "vn ports cannot be created on"
|
|
"this interface\n");
|
|
return -EIO;
|
|
}
|
|
rtnl_lock();
|
|
mutex_lock(&bnx2fc_dev_lock);
|
|
vn_port = bnx2fc_if_create(interface, &vport->dev, 1);
|
|
mutex_unlock(&bnx2fc_dev_lock);
|
|
rtnl_unlock();
|
|
|
|
if (!vn_port) {
|
|
printk(KERN_ERR PFX "bnx2fc_vport_create (%s) failed\n",
|
|
netdev->name);
|
|
return -EIO;
|
|
}
|
|
|
|
if (bnx2fc_devloss_tmo)
|
|
fc_host_dev_loss_tmo(vn_port->host) = bnx2fc_devloss_tmo;
|
|
|
|
if (disabled) {
|
|
fc_vport_set_state(vport, FC_VPORT_DISABLED);
|
|
} else {
|
|
vn_port->boot_time = jiffies;
|
|
fc_lport_init(vn_port);
|
|
fc_fabric_login(vn_port);
|
|
fc_vport_setlink(vn_port);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void bnx2fc_free_vport(struct bnx2fc_hba *hba, struct fc_lport *lport)
|
|
{
|
|
struct bnx2fc_lport *blport, *tmp;
|
|
|
|
spin_lock_bh(&hba->hba_lock);
|
|
list_for_each_entry_safe(blport, tmp, &hba->vports, list) {
|
|
if (blport->lport == lport) {
|
|
list_del(&blport->list);
|
|
kfree(blport);
|
|
}
|
|
}
|
|
spin_unlock_bh(&hba->hba_lock);
|
|
}
|
|
|
|
static int bnx2fc_vport_destroy(struct fc_vport *vport)
|
|
{
|
|
struct Scsi_Host *shost = vport_to_shost(vport);
|
|
struct fc_lport *n_port = shost_priv(shost);
|
|
struct fc_lport *vn_port = vport->dd_data;
|
|
struct fcoe_port *port = lport_priv(vn_port);
|
|
struct bnx2fc_interface *interface = port->priv;
|
|
struct fc_lport *v_port;
|
|
bool found = false;
|
|
|
|
mutex_lock(&n_port->lp_mutex);
|
|
list_for_each_entry(v_port, &n_port->vports, list)
|
|
if (v_port->vport == vport) {
|
|
found = true;
|
|
break;
|
|
}
|
|
|
|
if (!found) {
|
|
mutex_unlock(&n_port->lp_mutex);
|
|
return -ENOENT;
|
|
}
|
|
list_del(&vn_port->list);
|
|
mutex_unlock(&n_port->lp_mutex);
|
|
bnx2fc_free_vport(interface->hba, port->lport);
|
|
bnx2fc_port_shutdown(port->lport);
|
|
bnx2fc_interface_put(interface);
|
|
queue_work(bnx2fc_wq, &port->destroy_work);
|
|
return 0;
|
|
}
|
|
|
|
static int bnx2fc_vport_disable(struct fc_vport *vport, bool disable)
|
|
{
|
|
struct fc_lport *lport = vport->dd_data;
|
|
|
|
if (disable) {
|
|
fc_vport_set_state(vport, FC_VPORT_DISABLED);
|
|
fc_fabric_logoff(lport);
|
|
} else {
|
|
lport->boot_time = jiffies;
|
|
fc_fabric_login(lport);
|
|
fc_vport_setlink(lport);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int bnx2fc_interface_setup(struct bnx2fc_interface *interface)
|
|
{
|
|
struct net_device *netdev = interface->netdev;
|
|
struct net_device *physdev = interface->hba->phys_dev;
|
|
struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
|
|
struct netdev_hw_addr *ha;
|
|
int sel_san_mac = 0;
|
|
|
|
/* setup Source MAC Address */
|
|
rcu_read_lock();
|
|
for_each_dev_addr(physdev, ha) {
|
|
BNX2FC_MISC_DBG("net_config: ha->type = %d, fip_mac = ",
|
|
ha->type);
|
|
printk(KERN_INFO "%2x:%2x:%2x:%2x:%2x:%2x\n", ha->addr[0],
|
|
ha->addr[1], ha->addr[2], ha->addr[3],
|
|
ha->addr[4], ha->addr[5]);
|
|
|
|
if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
|
|
(is_valid_ether_addr(ha->addr))) {
|
|
memcpy(ctlr->ctl_src_addr, ha->addr,
|
|
ETH_ALEN);
|
|
sel_san_mac = 1;
|
|
BNX2FC_MISC_DBG("Found SAN MAC\n");
|
|
}
|
|
}
|
|
rcu_read_unlock();
|
|
|
|
if (!sel_san_mac)
|
|
return -ENODEV;
|
|
|
|
interface->fip_packet_type.func = bnx2fc_fip_recv;
|
|
interface->fip_packet_type.type = htons(ETH_P_FIP);
|
|
interface->fip_packet_type.dev = netdev;
|
|
dev_add_pack(&interface->fip_packet_type);
|
|
|
|
interface->fcoe_packet_type.func = bnx2fc_rcv;
|
|
interface->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
|
|
interface->fcoe_packet_type.dev = netdev;
|
|
dev_add_pack(&interface->fcoe_packet_type);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bnx2fc_attach_transport(void)
|
|
{
|
|
bnx2fc_transport_template =
|
|
fc_attach_transport(&bnx2fc_transport_function);
|
|
|
|
if (bnx2fc_transport_template == NULL) {
|
|
printk(KERN_ERR PFX "Failed to attach FC transport\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
bnx2fc_vport_xport_template =
|
|
fc_attach_transport(&bnx2fc_vport_xport_function);
|
|
if (bnx2fc_vport_xport_template == NULL) {
|
|
printk(KERN_ERR PFX
|
|
"Failed to attach FC transport for vport\n");
|
|
fc_release_transport(bnx2fc_transport_template);
|
|
bnx2fc_transport_template = NULL;
|
|
return -ENODEV;
|
|
}
|
|
return 0;
|
|
}
|
|
static void bnx2fc_release_transport(void)
|
|
{
|
|
fc_release_transport(bnx2fc_transport_template);
|
|
fc_release_transport(bnx2fc_vport_xport_template);
|
|
bnx2fc_transport_template = NULL;
|
|
bnx2fc_vport_xport_template = NULL;
|
|
}
|
|
|
|
static void bnx2fc_interface_release(struct kref *kref)
|
|
{
|
|
struct fcoe_ctlr_device *ctlr_dev;
|
|
struct bnx2fc_interface *interface;
|
|
struct fcoe_ctlr *ctlr;
|
|
struct net_device *netdev;
|
|
|
|
interface = container_of(kref, struct bnx2fc_interface, kref);
|
|
BNX2FC_MISC_DBG("Interface is being released\n");
|
|
|
|
ctlr = bnx2fc_to_ctlr(interface);
|
|
ctlr_dev = fcoe_ctlr_to_ctlr_dev(ctlr);
|
|
netdev = interface->netdev;
|
|
|
|
/* tear-down FIP controller */
|
|
if (test_and_clear_bit(BNX2FC_CTLR_INIT_DONE, &interface->if_flags))
|
|
fcoe_ctlr_destroy(ctlr);
|
|
|
|
fcoe_ctlr_device_delete(ctlr_dev);
|
|
|
|
dev_put(netdev);
|
|
module_put(THIS_MODULE);
|
|
}
|
|
|
|
static inline void bnx2fc_interface_get(struct bnx2fc_interface *interface)
|
|
{
|
|
kref_get(&interface->kref);
|
|
}
|
|
|
|
static inline void bnx2fc_interface_put(struct bnx2fc_interface *interface)
|
|
{
|
|
kref_put(&interface->kref, bnx2fc_interface_release);
|
|
}
|
|
static void bnx2fc_hba_destroy(struct bnx2fc_hba *hba)
|
|
{
|
|
/* Free the command manager */
|
|
if (hba->cmd_mgr) {
|
|
bnx2fc_cmd_mgr_free(hba->cmd_mgr);
|
|
hba->cmd_mgr = NULL;
|
|
}
|
|
kfree(hba->tgt_ofld_list);
|
|
bnx2fc_unbind_pcidev(hba);
|
|
kfree(hba);
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_hba_create - create a new bnx2fc hba
|
|
*
|
|
* @cnic: pointer to cnic device
|
|
*
|
|
* Creates a new FCoE hba on the given device.
|
|
*
|
|
*/
|
|
static struct bnx2fc_hba *bnx2fc_hba_create(struct cnic_dev *cnic)
|
|
{
|
|
struct bnx2fc_hba *hba;
|
|
struct fcoe_capabilities *fcoe_cap;
|
|
int rc;
|
|
|
|
hba = kzalloc(sizeof(*hba), GFP_KERNEL);
|
|
if (!hba) {
|
|
printk(KERN_ERR PFX "Unable to allocate hba structure\n");
|
|
return NULL;
|
|
}
|
|
spin_lock_init(&hba->hba_lock);
|
|
mutex_init(&hba->hba_mutex);
|
|
mutex_init(&hba->hba_stats_mutex);
|
|
|
|
hba->cnic = cnic;
|
|
|
|
hba->max_tasks = cnic->max_fcoe_exchanges;
|
|
hba->elstm_xids = (hba->max_tasks / 2);
|
|
hba->max_outstanding_cmds = hba->elstm_xids;
|
|
hba->max_xid = (hba->max_tasks - 1);
|
|
|
|
rc = bnx2fc_bind_pcidev(hba);
|
|
if (rc) {
|
|
printk(KERN_ERR PFX "create_adapter: bind error\n");
|
|
goto bind_err;
|
|
}
|
|
hba->phys_dev = cnic->netdev;
|
|
hba->next_conn_id = 0;
|
|
|
|
hba->tgt_ofld_list =
|
|
kcalloc(BNX2FC_NUM_MAX_SESS, sizeof(struct bnx2fc_rport *),
|
|
GFP_KERNEL);
|
|
if (!hba->tgt_ofld_list) {
|
|
printk(KERN_ERR PFX "Unable to allocate tgt offload list\n");
|
|
goto tgtofld_err;
|
|
}
|
|
|
|
hba->num_ofld_sess = 0;
|
|
|
|
hba->cmd_mgr = bnx2fc_cmd_mgr_alloc(hba);
|
|
if (!hba->cmd_mgr) {
|
|
printk(KERN_ERR PFX "em_config:bnx2fc_cmd_mgr_alloc failed\n");
|
|
goto cmgr_err;
|
|
}
|
|
fcoe_cap = &hba->fcoe_cap;
|
|
|
|
fcoe_cap->capability1 = BNX2FC_TM_MAX_SQES <<
|
|
FCOE_IOS_PER_CONNECTION_SHIFT;
|
|
fcoe_cap->capability1 |= BNX2FC_NUM_MAX_SESS <<
|
|
FCOE_LOGINS_PER_PORT_SHIFT;
|
|
fcoe_cap->capability2 = hba->max_outstanding_cmds <<
|
|
FCOE_NUMBER_OF_EXCHANGES_SHIFT;
|
|
fcoe_cap->capability2 |= BNX2FC_MAX_NPIV <<
|
|
FCOE_NPIV_WWN_PER_PORT_SHIFT;
|
|
fcoe_cap->capability3 = BNX2FC_NUM_MAX_SESS <<
|
|
FCOE_TARGETS_SUPPORTED_SHIFT;
|
|
fcoe_cap->capability3 |= hba->max_outstanding_cmds <<
|
|
FCOE_OUTSTANDING_COMMANDS_SHIFT;
|
|
fcoe_cap->capability4 = FCOE_CAPABILITY4_STATEFUL;
|
|
|
|
init_waitqueue_head(&hba->shutdown_wait);
|
|
init_waitqueue_head(&hba->destroy_wait);
|
|
INIT_LIST_HEAD(&hba->vports);
|
|
|
|
return hba;
|
|
|
|
cmgr_err:
|
|
kfree(hba->tgt_ofld_list);
|
|
tgtofld_err:
|
|
bnx2fc_unbind_pcidev(hba);
|
|
bind_err:
|
|
kfree(hba);
|
|
return NULL;
|
|
}
|
|
|
|
static struct bnx2fc_interface *
|
|
bnx2fc_interface_create(struct bnx2fc_hba *hba,
|
|
struct net_device *netdev,
|
|
enum fip_mode fip_mode)
|
|
{
|
|
struct fcoe_ctlr_device *ctlr_dev;
|
|
struct bnx2fc_interface *interface;
|
|
struct fcoe_ctlr *ctlr;
|
|
int size;
|
|
int rc = 0;
|
|
|
|
size = (sizeof(*interface) + sizeof(struct fcoe_ctlr));
|
|
ctlr_dev = fcoe_ctlr_device_add(&netdev->dev, &bnx2fc_fcoe_sysfs_templ,
|
|
size);
|
|
if (!ctlr_dev) {
|
|
printk(KERN_ERR PFX "Unable to allocate interface structure\n");
|
|
return NULL;
|
|
}
|
|
ctlr = fcoe_ctlr_device_priv(ctlr_dev);
|
|
ctlr->cdev = ctlr_dev;
|
|
interface = fcoe_ctlr_priv(ctlr);
|
|
dev_hold(netdev);
|
|
kref_init(&interface->kref);
|
|
interface->hba = hba;
|
|
interface->netdev = netdev;
|
|
|
|
/* Initialize FIP */
|
|
fcoe_ctlr_init(ctlr, fip_mode);
|
|
ctlr->send = bnx2fc_fip_send;
|
|
ctlr->update_mac = bnx2fc_update_src_mac;
|
|
ctlr->get_src_addr = bnx2fc_get_src_mac;
|
|
set_bit(BNX2FC_CTLR_INIT_DONE, &interface->if_flags);
|
|
|
|
rc = bnx2fc_interface_setup(interface);
|
|
if (!rc)
|
|
return interface;
|
|
|
|
fcoe_ctlr_destroy(ctlr);
|
|
dev_put(netdev);
|
|
fcoe_ctlr_device_delete(ctlr_dev);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_if_create - Create FCoE instance on a given interface
|
|
*
|
|
* @interface: FCoE interface to create a local port on
|
|
* @parent: Device pointer to be the parent in sysfs for the SCSI host
|
|
* @npiv: Indicates if the port is vport or not
|
|
*
|
|
* Creates a fc_lport instance and a Scsi_Host instance and configure them.
|
|
*
|
|
* Returns: Allocated fc_lport or an error pointer
|
|
*/
|
|
static struct fc_lport *bnx2fc_if_create(struct bnx2fc_interface *interface,
|
|
struct device *parent, int npiv)
|
|
{
|
|
struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
|
|
struct fc_lport *lport, *n_port;
|
|
struct fcoe_port *port;
|
|
struct Scsi_Host *shost;
|
|
struct fc_vport *vport = dev_to_vport(parent);
|
|
struct bnx2fc_lport *blport;
|
|
struct bnx2fc_hba *hba = interface->hba;
|
|
int rc = 0;
|
|
|
|
blport = kzalloc(sizeof(struct bnx2fc_lport), GFP_KERNEL);
|
|
if (!blport) {
|
|
BNX2FC_HBA_DBG(ctlr->lp, "Unable to alloc blport\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Allocate Scsi_Host structure */
|
|
bnx2fc_shost_template.can_queue = hba->max_outstanding_cmds;
|
|
if (!npiv)
|
|
lport = libfc_host_alloc(&bnx2fc_shost_template, sizeof(*port));
|
|
else
|
|
lport = libfc_vport_create(vport, sizeof(*port));
|
|
|
|
if (!lport) {
|
|
printk(KERN_ERR PFX "could not allocate scsi host structure\n");
|
|
goto free_blport;
|
|
}
|
|
shost = lport->host;
|
|
port = lport_priv(lport);
|
|
port->lport = lport;
|
|
port->priv = interface;
|
|
port->get_netdev = bnx2fc_netdev;
|
|
INIT_WORK(&port->destroy_work, bnx2fc_destroy_work);
|
|
|
|
/* Configure fcoe_port */
|
|
rc = bnx2fc_lport_config(lport);
|
|
if (rc)
|
|
goto lp_config_err;
|
|
|
|
if (npiv) {
|
|
printk(KERN_ERR PFX "Setting vport names, 0x%llX 0x%llX\n",
|
|
vport->node_name, vport->port_name);
|
|
fc_set_wwnn(lport, vport->node_name);
|
|
fc_set_wwpn(lport, vport->port_name);
|
|
}
|
|
/* Configure netdev and networking properties of the lport */
|
|
rc = bnx2fc_net_config(lport, interface->netdev);
|
|
if (rc) {
|
|
printk(KERN_ERR PFX "Error on bnx2fc_net_config\n");
|
|
goto lp_config_err;
|
|
}
|
|
|
|
rc = bnx2fc_shost_config(lport, parent);
|
|
if (rc) {
|
|
printk(KERN_ERR PFX "Couldn't configure shost for %s\n",
|
|
interface->netdev->name);
|
|
goto lp_config_err;
|
|
}
|
|
|
|
/* Initialize the libfc library */
|
|
rc = bnx2fc_libfc_config(lport);
|
|
if (rc) {
|
|
printk(KERN_ERR PFX "Couldn't configure libfc\n");
|
|
goto shost_err;
|
|
}
|
|
fc_host_port_type(lport->host) = FC_PORTTYPE_UNKNOWN;
|
|
|
|
if (bnx2fc_devloss_tmo)
|
|
fc_host_dev_loss_tmo(shost) = bnx2fc_devloss_tmo;
|
|
|
|
/* Allocate exchange manager */
|
|
if (!npiv)
|
|
rc = bnx2fc_em_config(lport, hba);
|
|
else {
|
|
shost = vport_to_shost(vport);
|
|
n_port = shost_priv(shost);
|
|
rc = fc_exch_mgr_list_clone(n_port, lport);
|
|
}
|
|
|
|
if (rc) {
|
|
printk(KERN_ERR PFX "Error on bnx2fc_em_config\n");
|
|
goto shost_err;
|
|
}
|
|
|
|
bnx2fc_interface_get(interface);
|
|
|
|
spin_lock_bh(&hba->hba_lock);
|
|
blport->lport = lport;
|
|
list_add_tail(&blport->list, &hba->vports);
|
|
spin_unlock_bh(&hba->hba_lock);
|
|
|
|
return lport;
|
|
|
|
shost_err:
|
|
scsi_remove_host(shost);
|
|
lp_config_err:
|
|
scsi_host_put(lport->host);
|
|
free_blport:
|
|
kfree(blport);
|
|
return NULL;
|
|
}
|
|
|
|
static void bnx2fc_net_cleanup(struct bnx2fc_interface *interface)
|
|
{
|
|
/* Dont listen for Ethernet packets anymore */
|
|
__dev_remove_pack(&interface->fcoe_packet_type);
|
|
__dev_remove_pack(&interface->fip_packet_type);
|
|
synchronize_net();
|
|
}
|
|
|
|
static void bnx2fc_interface_cleanup(struct bnx2fc_interface *interface)
|
|
{
|
|
struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
|
|
struct fc_lport *lport = ctlr->lp;
|
|
struct fcoe_port *port = lport_priv(lport);
|
|
struct bnx2fc_hba *hba = interface->hba;
|
|
|
|
/* Stop the transmit retry timer */
|
|
del_timer_sync(&port->timer);
|
|
|
|
/* Free existing transmit skbs */
|
|
fcoe_clean_pending_queue(lport);
|
|
|
|
bnx2fc_net_cleanup(interface);
|
|
|
|
bnx2fc_free_vport(hba, lport);
|
|
}
|
|
|
|
static void bnx2fc_if_destroy(struct fc_lport *lport)
|
|
{
|
|
|
|
/* Free queued packets for the receive thread */
|
|
bnx2fc_clean_rx_queue(lport);
|
|
|
|
/* Detach from scsi-ml */
|
|
fc_remove_host(lport->host);
|
|
scsi_remove_host(lport->host);
|
|
|
|
/*
|
|
* Note that only the physical lport will have the exchange manager.
|
|
* for vports, this function is NOP
|
|
*/
|
|
fc_exch_mgr_free(lport);
|
|
|
|
/* Free memory used by statistical counters */
|
|
fc_lport_free_stats(lport);
|
|
|
|
/* Release Scsi_Host */
|
|
scsi_host_put(lport->host);
|
|
}
|
|
|
|
static void __bnx2fc_destroy(struct bnx2fc_interface *interface)
|
|
{
|
|
struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
|
|
struct fc_lport *lport = ctlr->lp;
|
|
struct fcoe_port *port = lport_priv(lport);
|
|
|
|
bnx2fc_interface_cleanup(interface);
|
|
bnx2fc_stop(interface);
|
|
list_del(&interface->list);
|
|
bnx2fc_interface_put(interface);
|
|
queue_work(bnx2fc_wq, &port->destroy_work);
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_destroy - Destroy a bnx2fc FCoE interface
|
|
*
|
|
* @buffer: The name of the Ethernet interface to be destroyed
|
|
* @kp: The associated kernel parameter
|
|
*
|
|
* Called from sysfs.
|
|
*
|
|
* Returns: 0 for success
|
|
*/
|
|
static int bnx2fc_destroy(struct net_device *netdev)
|
|
{
|
|
struct bnx2fc_interface *interface = NULL;
|
|
struct workqueue_struct *timer_work_queue;
|
|
struct fcoe_ctlr *ctlr;
|
|
int rc = 0;
|
|
|
|
rtnl_lock();
|
|
mutex_lock(&bnx2fc_dev_lock);
|
|
|
|
interface = bnx2fc_interface_lookup(netdev);
|
|
ctlr = bnx2fc_to_ctlr(interface);
|
|
if (!interface || !ctlr->lp) {
|
|
rc = -ENODEV;
|
|
printk(KERN_ERR PFX "bnx2fc_destroy: interface or lport not found\n");
|
|
goto netdev_err;
|
|
}
|
|
|
|
timer_work_queue = interface->timer_work_queue;
|
|
__bnx2fc_destroy(interface);
|
|
destroy_workqueue(timer_work_queue);
|
|
|
|
netdev_err:
|
|
mutex_unlock(&bnx2fc_dev_lock);
|
|
rtnl_unlock();
|
|
return rc;
|
|
}
|
|
|
|
static void bnx2fc_destroy_work(struct work_struct *work)
|
|
{
|
|
struct fcoe_port *port;
|
|
struct fc_lport *lport;
|
|
|
|
port = container_of(work, struct fcoe_port, destroy_work);
|
|
lport = port->lport;
|
|
|
|
BNX2FC_HBA_DBG(lport, "Entered bnx2fc_destroy_work\n");
|
|
|
|
bnx2fc_if_destroy(lport);
|
|
}
|
|
|
|
static void bnx2fc_unbind_adapter_devices(struct bnx2fc_hba *hba)
|
|
{
|
|
bnx2fc_free_fw_resc(hba);
|
|
bnx2fc_free_task_ctx(hba);
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_bind_adapter_devices - binds bnx2fc adapter with the associated
|
|
* pci structure
|
|
*
|
|
* @hba: Adapter instance
|
|
*/
|
|
static int bnx2fc_bind_adapter_devices(struct bnx2fc_hba *hba)
|
|
{
|
|
if (bnx2fc_setup_task_ctx(hba))
|
|
goto mem_err;
|
|
|
|
if (bnx2fc_setup_fw_resc(hba))
|
|
goto mem_err;
|
|
|
|
return 0;
|
|
mem_err:
|
|
bnx2fc_unbind_adapter_devices(hba);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static int bnx2fc_bind_pcidev(struct bnx2fc_hba *hba)
|
|
{
|
|
struct cnic_dev *cnic;
|
|
struct pci_dev *pdev;
|
|
|
|
if (!hba->cnic) {
|
|
printk(KERN_ERR PFX "cnic is NULL\n");
|
|
return -ENODEV;
|
|
}
|
|
cnic = hba->cnic;
|
|
pdev = hba->pcidev = cnic->pcidev;
|
|
if (!hba->pcidev)
|
|
return -ENODEV;
|
|
|
|
switch (pdev->device) {
|
|
case PCI_DEVICE_ID_NX2_57710:
|
|
strncpy(hba->chip_num, "BCM57710", BCM_CHIP_LEN);
|
|
break;
|
|
case PCI_DEVICE_ID_NX2_57711:
|
|
strncpy(hba->chip_num, "BCM57711", BCM_CHIP_LEN);
|
|
break;
|
|
case PCI_DEVICE_ID_NX2_57712:
|
|
case PCI_DEVICE_ID_NX2_57712_MF:
|
|
case PCI_DEVICE_ID_NX2_57712_VF:
|
|
strncpy(hba->chip_num, "BCM57712", BCM_CHIP_LEN);
|
|
break;
|
|
case PCI_DEVICE_ID_NX2_57800:
|
|
case PCI_DEVICE_ID_NX2_57800_MF:
|
|
case PCI_DEVICE_ID_NX2_57800_VF:
|
|
strncpy(hba->chip_num, "BCM57800", BCM_CHIP_LEN);
|
|
break;
|
|
case PCI_DEVICE_ID_NX2_57810:
|
|
case PCI_DEVICE_ID_NX2_57810_MF:
|
|
case PCI_DEVICE_ID_NX2_57810_VF:
|
|
strncpy(hba->chip_num, "BCM57810", BCM_CHIP_LEN);
|
|
break;
|
|
case PCI_DEVICE_ID_NX2_57840:
|
|
case PCI_DEVICE_ID_NX2_57840_MF:
|
|
case PCI_DEVICE_ID_NX2_57840_VF:
|
|
case PCI_DEVICE_ID_NX2_57840_2_20:
|
|
case PCI_DEVICE_ID_NX2_57840_4_10:
|
|
strncpy(hba->chip_num, "BCM57840", BCM_CHIP_LEN);
|
|
break;
|
|
default:
|
|
pr_err(PFX "Unknown device id 0x%x\n", pdev->device);
|
|
break;
|
|
}
|
|
pci_dev_get(hba->pcidev);
|
|
return 0;
|
|
}
|
|
|
|
static void bnx2fc_unbind_pcidev(struct bnx2fc_hba *hba)
|
|
{
|
|
if (hba->pcidev) {
|
|
hba->chip_num[0] = '\0';
|
|
pci_dev_put(hba->pcidev);
|
|
}
|
|
hba->pcidev = NULL;
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_ulp_get_stats - cnic callback to populate FCoE stats
|
|
*
|
|
* @handle: transport handle pointing to adapter struture
|
|
*/
|
|
static int bnx2fc_ulp_get_stats(void *handle)
|
|
{
|
|
struct bnx2fc_hba *hba = handle;
|
|
struct cnic_dev *cnic;
|
|
struct fcoe_stats_info *stats_addr;
|
|
|
|
if (!hba)
|
|
return -EINVAL;
|
|
|
|
cnic = hba->cnic;
|
|
stats_addr = &cnic->stats_addr->fcoe_stat;
|
|
if (!stats_addr)
|
|
return -EINVAL;
|
|
|
|
strncpy(stats_addr->version, BNX2FC_VERSION,
|
|
sizeof(stats_addr->version));
|
|
stats_addr->txq_size = BNX2FC_SQ_WQES_MAX;
|
|
stats_addr->rxq_size = BNX2FC_CQ_WQES_MAX;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* bnx2fc_ulp_start - cnic callback to initialize & start adapter instance
|
|
*
|
|
* @handle: transport handle pointing to adapter structure
|
|
*
|
|
* This function maps adapter structure to pcidev structure and initiates
|
|
* firmware handshake to enable/initialize on-chip FCoE components.
|
|
* This bnx2fc - cnic interface api callback is used after following
|
|
* conditions are met -
|
|
* a) underlying network interface is up (marked by event NETDEV_UP
|
|
* from netdev
|
|
* b) bnx2fc adatper structure is registered.
|
|
*/
|
|
static void bnx2fc_ulp_start(void *handle)
|
|
{
|
|
struct bnx2fc_hba *hba = handle;
|
|
struct bnx2fc_interface *interface;
|
|
struct fcoe_ctlr *ctlr;
|
|
struct fc_lport *lport;
|
|
|
|
mutex_lock(&bnx2fc_dev_lock);
|
|
|
|
if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &hba->flags))
|
|
bnx2fc_fw_init(hba);
|
|
|
|
BNX2FC_MISC_DBG("bnx2fc started.\n");
|
|
|
|
list_for_each_entry(interface, &if_list, list) {
|
|
if (interface->hba == hba) {
|
|
ctlr = bnx2fc_to_ctlr(interface);
|
|
lport = ctlr->lp;
|
|
/* Kick off Fabric discovery*/
|
|
printk(KERN_ERR PFX "ulp_init: start discovery\n");
|
|
lport->tt.frame_send = bnx2fc_xmit;
|
|
bnx2fc_start_disc(interface);
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&bnx2fc_dev_lock);
|
|
}
|
|
|
|
static void bnx2fc_port_shutdown(struct fc_lport *lport)
|
|
{
|
|
BNX2FC_MISC_DBG("Entered %s\n", __func__);
|
|
fc_fabric_logoff(lport);
|
|
fc_lport_destroy(lport);
|
|
}
|
|
|
|
static void bnx2fc_stop(struct bnx2fc_interface *interface)
|
|
{
|
|
struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
|
|
struct fc_lport *lport;
|
|
struct fc_lport *vport;
|
|
|
|
if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &interface->hba->flags))
|
|
return;
|
|
|
|
lport = ctlr->lp;
|
|
bnx2fc_port_shutdown(lport);
|
|
|
|
mutex_lock(&lport->lp_mutex);
|
|
list_for_each_entry(vport, &lport->vports, list)
|
|
fc_host_port_type(vport->host) =
|
|
FC_PORTTYPE_UNKNOWN;
|
|
mutex_unlock(&lport->lp_mutex);
|
|
fc_host_port_type(lport->host) = FC_PORTTYPE_UNKNOWN;
|
|
fcoe_ctlr_link_down(ctlr);
|
|
fcoe_clean_pending_queue(lport);
|
|
}
|
|
|
|
static int bnx2fc_fw_init(struct bnx2fc_hba *hba)
|
|
{
|
|
#define BNX2FC_INIT_POLL_TIME (1000 / HZ)
|
|
int rc = -1;
|
|
int i = HZ;
|
|
|
|
rc = bnx2fc_bind_adapter_devices(hba);
|
|
if (rc) {
|
|
printk(KERN_ALERT PFX
|
|
"bnx2fc_bind_adapter_devices failed - rc = %d\n", rc);
|
|
goto err_out;
|
|
}
|
|
|
|
rc = bnx2fc_send_fw_fcoe_init_msg(hba);
|
|
if (rc) {
|
|
printk(KERN_ALERT PFX
|
|
"bnx2fc_send_fw_fcoe_init_msg failed - rc = %d\n", rc);
|
|
goto err_unbind;
|
|
}
|
|
|
|
/*
|
|
* Wait until the adapter init message is complete, and adapter
|
|
* state is UP.
|
|
*/
|
|
while (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state) && i--)
|
|
msleep(BNX2FC_INIT_POLL_TIME);
|
|
|
|
if (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state)) {
|
|
printk(KERN_ERR PFX "bnx2fc_start: %s failed to initialize. "
|
|
"Ignoring...\n",
|
|
hba->cnic->netdev->name);
|
|
rc = -1;
|
|
goto err_unbind;
|
|
}
|
|
|
|
|
|
set_bit(BNX2FC_FLAG_FW_INIT_DONE, &hba->flags);
|
|
return 0;
|
|
|
|
err_unbind:
|
|
bnx2fc_unbind_adapter_devices(hba);
|
|
err_out:
|
|
return rc;
|
|
}
|
|
|
|
static void bnx2fc_fw_destroy(struct bnx2fc_hba *hba)
|
|
{
|
|
if (test_and_clear_bit(BNX2FC_FLAG_FW_INIT_DONE, &hba->flags)) {
|
|
if (bnx2fc_send_fw_fcoe_destroy_msg(hba) == 0) {
|
|
timer_setup(&hba->destroy_timer, bnx2fc_destroy_timer,
|
|
0);
|
|
hba->destroy_timer.expires = BNX2FC_FW_TIMEOUT +
|
|
jiffies;
|
|
add_timer(&hba->destroy_timer);
|
|
wait_event_interruptible(hba->destroy_wait,
|
|
test_bit(BNX2FC_FLAG_DESTROY_CMPL,
|
|
&hba->flags));
|
|
clear_bit(BNX2FC_FLAG_DESTROY_CMPL, &hba->flags);
|
|
/* This should never happen */
|
|
if (signal_pending(current))
|
|
flush_signals(current);
|
|
|
|
del_timer_sync(&hba->destroy_timer);
|
|
}
|
|
bnx2fc_unbind_adapter_devices(hba);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_ulp_stop - cnic callback to shutdown adapter instance
|
|
*
|
|
* @handle: transport handle pointing to adapter structure
|
|
*
|
|
* Driver checks if adapter is already in shutdown mode, if not start
|
|
* the shutdown process.
|
|
*/
|
|
static void bnx2fc_ulp_stop(void *handle)
|
|
{
|
|
struct bnx2fc_hba *hba = handle;
|
|
struct bnx2fc_interface *interface;
|
|
|
|
printk(KERN_ERR "ULP_STOP\n");
|
|
|
|
mutex_lock(&bnx2fc_dev_lock);
|
|
if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &hba->flags))
|
|
goto exit;
|
|
list_for_each_entry(interface, &if_list, list) {
|
|
if (interface->hba == hba)
|
|
bnx2fc_stop(interface);
|
|
}
|
|
BUG_ON(hba->num_ofld_sess != 0);
|
|
|
|
mutex_lock(&hba->hba_mutex);
|
|
clear_bit(ADAPTER_STATE_UP, &hba->adapter_state);
|
|
clear_bit(ADAPTER_STATE_GOING_DOWN,
|
|
&hba->adapter_state);
|
|
|
|
clear_bit(ADAPTER_STATE_READY, &hba->adapter_state);
|
|
mutex_unlock(&hba->hba_mutex);
|
|
|
|
bnx2fc_fw_destroy(hba);
|
|
exit:
|
|
mutex_unlock(&bnx2fc_dev_lock);
|
|
}
|
|
|
|
static void bnx2fc_start_disc(struct bnx2fc_interface *interface)
|
|
{
|
|
struct fcoe_ctlr *ctlr = bnx2fc_to_ctlr(interface);
|
|
struct fc_lport *lport;
|
|
int wait_cnt = 0;
|
|
|
|
BNX2FC_MISC_DBG("Entered %s\n", __func__);
|
|
/* Kick off FIP/FLOGI */
|
|
if (!test_bit(BNX2FC_FLAG_FW_INIT_DONE, &interface->hba->flags)) {
|
|
printk(KERN_ERR PFX "Init not done yet\n");
|
|
return;
|
|
}
|
|
|
|
lport = ctlr->lp;
|
|
BNX2FC_HBA_DBG(lport, "calling fc_fabric_login\n");
|
|
|
|
if (!bnx2fc_link_ok(lport) && interface->enabled) {
|
|
BNX2FC_HBA_DBG(lport, "ctlr_link_up\n");
|
|
fcoe_ctlr_link_up(ctlr);
|
|
fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT;
|
|
set_bit(ADAPTER_STATE_READY, &interface->hba->adapter_state);
|
|
}
|
|
|
|
/* wait for the FCF to be selected before issuing FLOGI */
|
|
while (!ctlr->sel_fcf) {
|
|
msleep(250);
|
|
/* give up after 3 secs */
|
|
if (++wait_cnt > 12)
|
|
break;
|
|
}
|
|
|
|
/* Reset max receive frame size to default */
|
|
if (fc_set_mfs(lport, BNX2FC_MFS))
|
|
return;
|
|
|
|
fc_lport_init(lport);
|
|
fc_fabric_login(lport);
|
|
}
|
|
|
|
|
|
/**
|
|
* bnx2fc_ulp_init - Initialize an adapter instance
|
|
*
|
|
* @dev : cnic device handle
|
|
* Called from cnic_register_driver() context to initialize all
|
|
* enumerated cnic devices. This routine allocates adapter structure
|
|
* and other device specific resources.
|
|
*/
|
|
static void bnx2fc_ulp_init(struct cnic_dev *dev)
|
|
{
|
|
struct bnx2fc_hba *hba;
|
|
int rc = 0;
|
|
|
|
BNX2FC_MISC_DBG("Entered %s\n", __func__);
|
|
/* bnx2fc works only when bnx2x is loaded */
|
|
if (!test_bit(CNIC_F_BNX2X_CLASS, &dev->flags) ||
|
|
(dev->max_fcoe_conn == 0)) {
|
|
printk(KERN_ERR PFX "bnx2fc FCoE not supported on %s,"
|
|
" flags: %lx fcoe_conn: %d\n",
|
|
dev->netdev->name, dev->flags, dev->max_fcoe_conn);
|
|
return;
|
|
}
|
|
|
|
hba = bnx2fc_hba_create(dev);
|
|
if (!hba) {
|
|
printk(KERN_ERR PFX "hba initialization failed\n");
|
|
return;
|
|
}
|
|
|
|
pr_info(PFX "FCoE initialized for %s.\n", dev->netdev->name);
|
|
|
|
/* Add HBA to the adapter list */
|
|
mutex_lock(&bnx2fc_dev_lock);
|
|
list_add_tail(&hba->list, &adapter_list);
|
|
adapter_count++;
|
|
mutex_unlock(&bnx2fc_dev_lock);
|
|
|
|
dev->fcoe_cap = &hba->fcoe_cap;
|
|
clear_bit(BNX2FC_CNIC_REGISTERED, &hba->reg_with_cnic);
|
|
rc = dev->register_device(dev, CNIC_ULP_FCOE,
|
|
(void *) hba);
|
|
if (rc)
|
|
printk(KERN_ERR PFX "register_device failed, rc = %d\n", rc);
|
|
else
|
|
set_bit(BNX2FC_CNIC_REGISTERED, &hba->reg_with_cnic);
|
|
}
|
|
|
|
/* Assumes rtnl_lock and the bnx2fc_dev_lock are already taken */
|
|
static int __bnx2fc_disable(struct fcoe_ctlr *ctlr)
|
|
{
|
|
struct bnx2fc_interface *interface = fcoe_ctlr_priv(ctlr);
|
|
|
|
if (interface->enabled == true) {
|
|
if (!ctlr->lp) {
|
|
pr_err(PFX "__bnx2fc_disable: lport not found\n");
|
|
return -ENODEV;
|
|
} else {
|
|
interface->enabled = false;
|
|
fcoe_ctlr_link_down(ctlr);
|
|
fcoe_clean_pending_queue(ctlr->lp);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Deperecated: Use bnx2fc_enabled()
|
|
*/
|
|
static int bnx2fc_disable(struct net_device *netdev)
|
|
{
|
|
struct bnx2fc_interface *interface;
|
|
struct fcoe_ctlr *ctlr;
|
|
int rc = 0;
|
|
|
|
rtnl_lock();
|
|
mutex_lock(&bnx2fc_dev_lock);
|
|
|
|
interface = bnx2fc_interface_lookup(netdev);
|
|
ctlr = bnx2fc_to_ctlr(interface);
|
|
|
|
if (!interface) {
|
|
rc = -ENODEV;
|
|
pr_err(PFX "bnx2fc_disable: interface not found\n");
|
|
} else {
|
|
rc = __bnx2fc_disable(ctlr);
|
|
}
|
|
mutex_unlock(&bnx2fc_dev_lock);
|
|
rtnl_unlock();
|
|
return rc;
|
|
}
|
|
|
|
static uint bnx2fc_npiv_create_vports(struct fc_lport *lport,
|
|
struct cnic_fc_npiv_tbl *npiv_tbl)
|
|
{
|
|
struct fc_vport_identifiers vpid;
|
|
uint i, created = 0;
|
|
u64 wwnn = 0;
|
|
char wwpn_str[32];
|
|
char wwnn_str[32];
|
|
|
|
if (npiv_tbl->count > MAX_NPIV_ENTRIES) {
|
|
BNX2FC_HBA_DBG(lport, "Exceeded count max of npiv table\n");
|
|
goto done;
|
|
}
|
|
|
|
/* Sanity check the first entry to make sure it's not 0 */
|
|
if (wwn_to_u64(npiv_tbl->wwnn[0]) == 0 &&
|
|
wwn_to_u64(npiv_tbl->wwpn[0]) == 0) {
|
|
BNX2FC_HBA_DBG(lport, "First NPIV table entries invalid.\n");
|
|
goto done;
|
|
}
|
|
|
|
vpid.roles = FC_PORT_ROLE_FCP_INITIATOR;
|
|
vpid.vport_type = FC_PORTTYPE_NPIV;
|
|
vpid.disable = false;
|
|
|
|
for (i = 0; i < npiv_tbl->count; i++) {
|
|
wwnn = wwn_to_u64(npiv_tbl->wwnn[i]);
|
|
if (wwnn == 0) {
|
|
/*
|
|
* If we get a 0 element from for the WWNN then assume
|
|
* the WWNN should be the same as the physical port.
|
|
*/
|
|
wwnn = lport->wwnn;
|
|
}
|
|
vpid.node_name = wwnn;
|
|
vpid.port_name = wwn_to_u64(npiv_tbl->wwpn[i]);
|
|
scnprintf(vpid.symbolic_name, sizeof(vpid.symbolic_name),
|
|
"NPIV[%u]:%016llx-%016llx",
|
|
created, vpid.port_name, vpid.node_name);
|
|
fcoe_wwn_to_str(vpid.node_name, wwnn_str, sizeof(wwnn_str));
|
|
fcoe_wwn_to_str(vpid.port_name, wwpn_str, sizeof(wwpn_str));
|
|
BNX2FC_HBA_DBG(lport, "Creating vport %s:%s.\n", wwnn_str,
|
|
wwpn_str);
|
|
if (fc_vport_create(lport->host, 0, &vpid))
|
|
created++;
|
|
else
|
|
BNX2FC_HBA_DBG(lport, "Failed to create vport\n");
|
|
}
|
|
done:
|
|
return created;
|
|
}
|
|
|
|
static int __bnx2fc_enable(struct fcoe_ctlr *ctlr)
|
|
{
|
|
struct bnx2fc_interface *interface = fcoe_ctlr_priv(ctlr);
|
|
struct bnx2fc_hba *hba;
|
|
struct cnic_fc_npiv_tbl *npiv_tbl;
|
|
struct fc_lport *lport;
|
|
|
|
if (interface->enabled == false) {
|
|
if (!ctlr->lp) {
|
|
pr_err(PFX "__bnx2fc_enable: lport not found\n");
|
|
return -ENODEV;
|
|
} else if (!bnx2fc_link_ok(ctlr->lp)) {
|
|
fcoe_ctlr_link_up(ctlr);
|
|
interface->enabled = true;
|
|
}
|
|
}
|
|
|
|
/* Create static NPIV ports if any are contained in NVRAM */
|
|
hba = interface->hba;
|
|
lport = ctlr->lp;
|
|
|
|
if (!hba)
|
|
goto done;
|
|
|
|
if (!hba->cnic)
|
|
goto done;
|
|
|
|
if (!lport)
|
|
goto done;
|
|
|
|
if (!lport->host)
|
|
goto done;
|
|
|
|
if (!hba->cnic->get_fc_npiv_tbl)
|
|
goto done;
|
|
|
|
npiv_tbl = kzalloc(sizeof(struct cnic_fc_npiv_tbl), GFP_KERNEL);
|
|
if (!npiv_tbl)
|
|
goto done;
|
|
|
|
if (hba->cnic->get_fc_npiv_tbl(hba->cnic, npiv_tbl))
|
|
goto done_free;
|
|
|
|
bnx2fc_npiv_create_vports(lport, npiv_tbl);
|
|
done_free:
|
|
kfree(npiv_tbl);
|
|
done:
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Deprecated: Use bnx2fc_enabled()
|
|
*/
|
|
static int bnx2fc_enable(struct net_device *netdev)
|
|
{
|
|
struct bnx2fc_interface *interface;
|
|
struct fcoe_ctlr *ctlr;
|
|
int rc = 0;
|
|
|
|
rtnl_lock();
|
|
mutex_lock(&bnx2fc_dev_lock);
|
|
|
|
interface = bnx2fc_interface_lookup(netdev);
|
|
ctlr = bnx2fc_to_ctlr(interface);
|
|
if (!interface) {
|
|
rc = -ENODEV;
|
|
pr_err(PFX "bnx2fc_enable: interface not found\n");
|
|
} else {
|
|
rc = __bnx2fc_enable(ctlr);
|
|
}
|
|
|
|
mutex_unlock(&bnx2fc_dev_lock);
|
|
rtnl_unlock();
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_ctlr_enabled() - Enable or disable an FCoE Controller
|
|
* @cdev: The FCoE Controller that is being enabled or disabled
|
|
*
|
|
* fcoe_sysfs will ensure that the state of 'enabled' has
|
|
* changed, so no checking is necessary here. This routine simply
|
|
* calls fcoe_enable or fcoe_disable, both of which are deprecated.
|
|
* When those routines are removed the functionality can be merged
|
|
* here.
|
|
*/
|
|
static int bnx2fc_ctlr_enabled(struct fcoe_ctlr_device *cdev)
|
|
{
|
|
struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(cdev);
|
|
|
|
switch (cdev->enabled) {
|
|
case FCOE_CTLR_ENABLED:
|
|
return __bnx2fc_enable(ctlr);
|
|
case FCOE_CTLR_DISABLED:
|
|
return __bnx2fc_disable(ctlr);
|
|
case FCOE_CTLR_UNUSED:
|
|
default:
|
|
return -ENOTSUPP;
|
|
};
|
|
}
|
|
|
|
enum bnx2fc_create_link_state {
|
|
BNX2FC_CREATE_LINK_DOWN,
|
|
BNX2FC_CREATE_LINK_UP,
|
|
};
|
|
|
|
/**
|
|
* _bnx2fc_create() - Create bnx2fc FCoE interface
|
|
* @netdev : The net_device object the Ethernet interface to create on
|
|
* @fip_mode: The FIP mode for this creation
|
|
* @link_state: The ctlr link state on creation
|
|
*
|
|
* Called from either the libfcoe 'create' module parameter
|
|
* via fcoe_create or from fcoe_syfs's ctlr_create file.
|
|
*
|
|
* libfcoe's 'create' module parameter is deprecated so some
|
|
* consolidation of code can be done when that interface is
|
|
* removed.
|
|
*
|
|
* Returns: 0 for success
|
|
*/
|
|
static int _bnx2fc_create(struct net_device *netdev,
|
|
enum fip_mode fip_mode,
|
|
enum bnx2fc_create_link_state link_state)
|
|
{
|
|
struct fcoe_ctlr_device *cdev;
|
|
struct fcoe_ctlr *ctlr;
|
|
struct bnx2fc_interface *interface;
|
|
struct bnx2fc_hba *hba;
|
|
struct net_device *phys_dev = netdev;
|
|
struct fc_lport *lport;
|
|
struct ethtool_drvinfo drvinfo;
|
|
int rc = 0;
|
|
int vlan_id = 0;
|
|
|
|
BNX2FC_MISC_DBG("Entered bnx2fc_create\n");
|
|
if (fip_mode != FIP_MODE_FABRIC) {
|
|
printk(KERN_ERR "fip mode not FABRIC\n");
|
|
return -EIO;
|
|
}
|
|
|
|
rtnl_lock();
|
|
|
|
mutex_lock(&bnx2fc_dev_lock);
|
|
|
|
if (!try_module_get(THIS_MODULE)) {
|
|
rc = -EINVAL;
|
|
goto mod_err;
|
|
}
|
|
|
|
/* obtain physical netdev */
|
|
if (is_vlan_dev(netdev))
|
|
phys_dev = vlan_dev_real_dev(netdev);
|
|
|
|
/* verify if the physical device is a netxtreme2 device */
|
|
if (phys_dev->ethtool_ops && phys_dev->ethtool_ops->get_drvinfo) {
|
|
memset(&drvinfo, 0, sizeof(drvinfo));
|
|
phys_dev->ethtool_ops->get_drvinfo(phys_dev, &drvinfo);
|
|
if (strncmp(drvinfo.driver, "bnx2x", strlen("bnx2x"))) {
|
|
printk(KERN_ERR PFX "Not a netxtreme2 device\n");
|
|
rc = -EINVAL;
|
|
goto netdev_err;
|
|
}
|
|
} else {
|
|
printk(KERN_ERR PFX "unable to obtain drv_info\n");
|
|
rc = -EINVAL;
|
|
goto netdev_err;
|
|
}
|
|
|
|
/* obtain interface and initialize rest of the structure */
|
|
hba = bnx2fc_hba_lookup(phys_dev);
|
|
if (!hba) {
|
|
rc = -ENODEV;
|
|
printk(KERN_ERR PFX "bnx2fc_create: hba not found\n");
|
|
goto netdev_err;
|
|
}
|
|
|
|
if (bnx2fc_interface_lookup(netdev)) {
|
|
rc = -EEXIST;
|
|
goto netdev_err;
|
|
}
|
|
|
|
interface = bnx2fc_interface_create(hba, netdev, fip_mode);
|
|
if (!interface) {
|
|
printk(KERN_ERR PFX "bnx2fc_interface_create failed\n");
|
|
rc = -ENOMEM;
|
|
goto netdev_err;
|
|
}
|
|
|
|
if (is_vlan_dev(netdev)) {
|
|
vlan_id = vlan_dev_vlan_id(netdev);
|
|
interface->vlan_enabled = 1;
|
|
}
|
|
|
|
ctlr = bnx2fc_to_ctlr(interface);
|
|
cdev = fcoe_ctlr_to_ctlr_dev(ctlr);
|
|
interface->vlan_id = vlan_id;
|
|
interface->tm_timeout = BNX2FC_TM_TIMEOUT;
|
|
|
|
interface->timer_work_queue =
|
|
create_singlethread_workqueue("bnx2fc_timer_wq");
|
|
if (!interface->timer_work_queue) {
|
|
printk(KERN_ERR PFX "ulp_init could not create timer_wq\n");
|
|
rc = -EINVAL;
|
|
goto ifput_err;
|
|
}
|
|
|
|
lport = bnx2fc_if_create(interface, &cdev->dev, 0);
|
|
if (!lport) {
|
|
printk(KERN_ERR PFX "Failed to create interface (%s)\n",
|
|
netdev->name);
|
|
rc = -EINVAL;
|
|
goto if_create_err;
|
|
}
|
|
|
|
/* Add interface to if_list */
|
|
list_add_tail(&interface->list, &if_list);
|
|
|
|
lport->boot_time = jiffies;
|
|
|
|
/* Make this master N_port */
|
|
ctlr->lp = lport;
|
|
|
|
if (link_state == BNX2FC_CREATE_LINK_UP)
|
|
cdev->enabled = FCOE_CTLR_ENABLED;
|
|
else
|
|
cdev->enabled = FCOE_CTLR_DISABLED;
|
|
|
|
if (link_state == BNX2FC_CREATE_LINK_UP &&
|
|
!bnx2fc_link_ok(lport)) {
|
|
fcoe_ctlr_link_up(ctlr);
|
|
fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT;
|
|
set_bit(ADAPTER_STATE_READY, &interface->hba->adapter_state);
|
|
}
|
|
|
|
BNX2FC_HBA_DBG(lport, "create: START DISC\n");
|
|
bnx2fc_start_disc(interface);
|
|
|
|
if (link_state == BNX2FC_CREATE_LINK_UP)
|
|
interface->enabled = true;
|
|
|
|
/*
|
|
* Release from kref_init in bnx2fc_interface_setup, on success
|
|
* lport should be holding a reference taken in bnx2fc_if_create
|
|
*/
|
|
bnx2fc_interface_put(interface);
|
|
/* put netdev that was held while calling dev_get_by_name */
|
|
mutex_unlock(&bnx2fc_dev_lock);
|
|
rtnl_unlock();
|
|
return 0;
|
|
|
|
if_create_err:
|
|
destroy_workqueue(interface->timer_work_queue);
|
|
ifput_err:
|
|
bnx2fc_net_cleanup(interface);
|
|
bnx2fc_interface_put(interface);
|
|
goto mod_err;
|
|
netdev_err:
|
|
module_put(THIS_MODULE);
|
|
mod_err:
|
|
mutex_unlock(&bnx2fc_dev_lock);
|
|
rtnl_unlock();
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_create() - Create a bnx2fc interface
|
|
* @netdev : The net_device object the Ethernet interface to create on
|
|
* @fip_mode: The FIP mode for this creation
|
|
*
|
|
* Called from fcoe transport
|
|
*
|
|
* Returns: 0 for success
|
|
*/
|
|
static int bnx2fc_create(struct net_device *netdev, enum fip_mode fip_mode)
|
|
{
|
|
return _bnx2fc_create(netdev, fip_mode, BNX2FC_CREATE_LINK_UP);
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_ctlr_alloc() - Allocate a bnx2fc interface from fcoe_sysfs
|
|
* @netdev: The net_device to be used by the allocated FCoE Controller
|
|
*
|
|
* This routine is called from fcoe_sysfs. It will start the fcoe_ctlr
|
|
* in a link_down state. The allows the user an opportunity to configure
|
|
* the FCoE Controller from sysfs before enabling the FCoE Controller.
|
|
*
|
|
* Creating in with this routine starts the FCoE Controller in Fabric
|
|
* mode. The user can change to VN2VN or another mode before enabling.
|
|
*/
|
|
static int bnx2fc_ctlr_alloc(struct net_device *netdev)
|
|
{
|
|
return _bnx2fc_create(netdev, FIP_MODE_FABRIC,
|
|
BNX2FC_CREATE_LINK_DOWN);
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_find_hba_for_cnic - maps cnic instance to bnx2fc hba instance
|
|
*
|
|
* @cnic: Pointer to cnic device instance
|
|
*
|
|
**/
|
|
static struct bnx2fc_hba *bnx2fc_find_hba_for_cnic(struct cnic_dev *cnic)
|
|
{
|
|
struct bnx2fc_hba *hba;
|
|
|
|
/* Called with bnx2fc_dev_lock held */
|
|
list_for_each_entry(hba, &adapter_list, list) {
|
|
if (hba->cnic == cnic)
|
|
return hba;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct bnx2fc_interface *bnx2fc_interface_lookup(struct net_device
|
|
*netdev)
|
|
{
|
|
struct bnx2fc_interface *interface;
|
|
|
|
/* Called with bnx2fc_dev_lock held */
|
|
list_for_each_entry(interface, &if_list, list) {
|
|
if (interface->netdev == netdev)
|
|
return interface;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct bnx2fc_hba *bnx2fc_hba_lookup(struct net_device
|
|
*phys_dev)
|
|
{
|
|
struct bnx2fc_hba *hba;
|
|
|
|
/* Called with bnx2fc_dev_lock held */
|
|
list_for_each_entry(hba, &adapter_list, list) {
|
|
if (hba->phys_dev == phys_dev)
|
|
return hba;
|
|
}
|
|
printk(KERN_ERR PFX "adapter_lookup: hba NULL\n");
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_ulp_exit - shuts down adapter instance and frees all resources
|
|
*
|
|
* @dev cnic device handle
|
|
*/
|
|
static void bnx2fc_ulp_exit(struct cnic_dev *dev)
|
|
{
|
|
struct bnx2fc_hba *hba;
|
|
struct bnx2fc_interface *interface, *tmp;
|
|
|
|
BNX2FC_MISC_DBG("Entered bnx2fc_ulp_exit\n");
|
|
|
|
if (!test_bit(CNIC_F_BNX2X_CLASS, &dev->flags)) {
|
|
printk(KERN_ERR PFX "bnx2fc port check: %s, flags: %lx\n",
|
|
dev->netdev->name, dev->flags);
|
|
return;
|
|
}
|
|
|
|
mutex_lock(&bnx2fc_dev_lock);
|
|
hba = bnx2fc_find_hba_for_cnic(dev);
|
|
if (!hba) {
|
|
printk(KERN_ERR PFX "bnx2fc_ulp_exit: hba not found, dev 0%p\n",
|
|
dev);
|
|
mutex_unlock(&bnx2fc_dev_lock);
|
|
return;
|
|
}
|
|
|
|
list_del_init(&hba->list);
|
|
adapter_count--;
|
|
|
|
list_for_each_entry_safe(interface, tmp, &if_list, list)
|
|
/* destroy not called yet, move to quiesced list */
|
|
if (interface->hba == hba)
|
|
__bnx2fc_destroy(interface);
|
|
mutex_unlock(&bnx2fc_dev_lock);
|
|
|
|
/* Ensure ALL destroy work has been completed before return */
|
|
flush_workqueue(bnx2fc_wq);
|
|
|
|
bnx2fc_ulp_stop(hba);
|
|
/* unregister cnic device */
|
|
if (test_and_clear_bit(BNX2FC_CNIC_REGISTERED, &hba->reg_with_cnic))
|
|
hba->cnic->unregister_device(hba->cnic, CNIC_ULP_FCOE);
|
|
bnx2fc_hba_destroy(hba);
|
|
}
|
|
|
|
static void bnx2fc_rport_terminate_io(struct fc_rport *rport)
|
|
{
|
|
/* This is a no-op */
|
|
}
|
|
|
|
/**
|
|
* bnx2fc_fcoe_reset - Resets the fcoe
|
|
*
|
|
* @shost: shost the reset is from
|
|
*
|
|
* Returns: always 0
|
|
*/
|
|
static int bnx2fc_fcoe_reset(struct Scsi_Host *shost)
|
|
{
|
|
struct fc_lport *lport = shost_priv(shost);
|
|
fc_lport_reset(lport);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static bool bnx2fc_match(struct net_device *netdev)
|
|
{
|
|
struct net_device *phys_dev = netdev;
|
|
|
|
mutex_lock(&bnx2fc_dev_lock);
|
|
if (is_vlan_dev(netdev))
|
|
phys_dev = vlan_dev_real_dev(netdev);
|
|
|
|
if (bnx2fc_hba_lookup(phys_dev)) {
|
|
mutex_unlock(&bnx2fc_dev_lock);
|
|
return true;
|
|
}
|
|
|
|
mutex_unlock(&bnx2fc_dev_lock);
|
|
return false;
|
|
}
|
|
|
|
|
|
static struct fcoe_transport bnx2fc_transport = {
|
|
.name = {"bnx2fc"},
|
|
.attached = false,
|
|
.list = LIST_HEAD_INIT(bnx2fc_transport.list),
|
|
.alloc = bnx2fc_ctlr_alloc,
|
|
.match = bnx2fc_match,
|
|
.create = bnx2fc_create,
|
|
.destroy = bnx2fc_destroy,
|
|
.enable = bnx2fc_enable,
|
|
.disable = bnx2fc_disable,
|
|
};
|
|
|
|
/**
|
|
* bnx2fc_cpu_online - Create a receive thread for an online CPU
|
|
*
|
|
* @cpu: cpu index for the online cpu
|
|
*/
|
|
static int bnx2fc_cpu_online(unsigned int cpu)
|
|
{
|
|
struct bnx2fc_percpu_s *p;
|
|
struct task_struct *thread;
|
|
|
|
p = &per_cpu(bnx2fc_percpu, cpu);
|
|
|
|
thread = kthread_create_on_node(bnx2fc_percpu_io_thread,
|
|
(void *)p, cpu_to_node(cpu),
|
|
"bnx2fc_thread/%d", cpu);
|
|
if (IS_ERR(thread))
|
|
return PTR_ERR(thread);
|
|
|
|
/* bind thread to the cpu */
|
|
kthread_bind(thread, cpu);
|
|
p->iothread = thread;
|
|
wake_up_process(thread);
|
|
return 0;
|
|
}
|
|
|
|
static int bnx2fc_cpu_offline(unsigned int cpu)
|
|
{
|
|
struct bnx2fc_percpu_s *p;
|
|
struct task_struct *thread;
|
|
struct bnx2fc_work *work, *tmp;
|
|
|
|
BNX2FC_MISC_DBG("destroying io thread for CPU %d\n", cpu);
|
|
|
|
/* Prevent any new work from being queued for this CPU */
|
|
p = &per_cpu(bnx2fc_percpu, cpu);
|
|
spin_lock_bh(&p->fp_work_lock);
|
|
thread = p->iothread;
|
|
p->iothread = NULL;
|
|
|
|
/* Free all work in the list */
|
|
list_for_each_entry_safe(work, tmp, &p->work_list, list) {
|
|
list_del_init(&work->list);
|
|
bnx2fc_process_cq_compl(work->tgt, work->wqe);
|
|
kfree(work);
|
|
}
|
|
|
|
spin_unlock_bh(&p->fp_work_lock);
|
|
|
|
if (thread)
|
|
kthread_stop(thread);
|
|
return 0;
|
|
}
|
|
|
|
static int bnx2fc_slave_configure(struct scsi_device *sdev)
|
|
{
|
|
if (!bnx2fc_queue_depth)
|
|
return 0;
|
|
|
|
scsi_change_queue_depth(sdev, bnx2fc_queue_depth);
|
|
return 0;
|
|
}
|
|
|
|
static enum cpuhp_state bnx2fc_online_state;
|
|
|
|
/**
|
|
* bnx2fc_mod_init - module init entry point
|
|
*
|
|
* Initialize driver wide global data structures, and register
|
|
* with cnic module
|
|
**/
|
|
static int __init bnx2fc_mod_init(void)
|
|
{
|
|
struct fcoe_percpu_s *bg;
|
|
struct task_struct *l2_thread;
|
|
int rc = 0;
|
|
unsigned int cpu = 0;
|
|
struct bnx2fc_percpu_s *p;
|
|
|
|
printk(KERN_INFO PFX "%s", version);
|
|
|
|
/* register as a fcoe transport */
|
|
rc = fcoe_transport_attach(&bnx2fc_transport);
|
|
if (rc) {
|
|
printk(KERN_ERR "failed to register an fcoe transport, check "
|
|
"if libfcoe is loaded\n");
|
|
goto out;
|
|
}
|
|
|
|
INIT_LIST_HEAD(&adapter_list);
|
|
INIT_LIST_HEAD(&if_list);
|
|
mutex_init(&bnx2fc_dev_lock);
|
|
adapter_count = 0;
|
|
|
|
/* Attach FC transport template */
|
|
rc = bnx2fc_attach_transport();
|
|
if (rc)
|
|
goto detach_ft;
|
|
|
|
bnx2fc_wq = alloc_workqueue("bnx2fc", 0, 0);
|
|
if (!bnx2fc_wq) {
|
|
rc = -ENOMEM;
|
|
goto release_bt;
|
|
}
|
|
|
|
bg = &bnx2fc_global;
|
|
skb_queue_head_init(&bg->fcoe_rx_list);
|
|
l2_thread = kthread_create(bnx2fc_l2_rcv_thread,
|
|
(void *)bg,
|
|
"bnx2fc_l2_thread");
|
|
if (IS_ERR(l2_thread)) {
|
|
rc = PTR_ERR(l2_thread);
|
|
goto free_wq;
|
|
}
|
|
wake_up_process(l2_thread);
|
|
spin_lock_bh(&bg->fcoe_rx_list.lock);
|
|
bg->kthread = l2_thread;
|
|
spin_unlock_bh(&bg->fcoe_rx_list.lock);
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
p = &per_cpu(bnx2fc_percpu, cpu);
|
|
INIT_LIST_HEAD(&p->work_list);
|
|
spin_lock_init(&p->fp_work_lock);
|
|
}
|
|
|
|
rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "scsi/bnx2fc:online",
|
|
bnx2fc_cpu_online, bnx2fc_cpu_offline);
|
|
if (rc < 0)
|
|
goto stop_thread;
|
|
bnx2fc_online_state = rc;
|
|
|
|
cnic_register_driver(CNIC_ULP_FCOE, &bnx2fc_cnic_cb);
|
|
return 0;
|
|
|
|
stop_thread:
|
|
kthread_stop(l2_thread);
|
|
free_wq:
|
|
destroy_workqueue(bnx2fc_wq);
|
|
release_bt:
|
|
bnx2fc_release_transport();
|
|
detach_ft:
|
|
fcoe_transport_detach(&bnx2fc_transport);
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
static void __exit bnx2fc_mod_exit(void)
|
|
{
|
|
LIST_HEAD(to_be_deleted);
|
|
struct bnx2fc_hba *hba, *next;
|
|
struct fcoe_percpu_s *bg;
|
|
struct task_struct *l2_thread;
|
|
struct sk_buff *skb;
|
|
|
|
/*
|
|
* NOTE: Since cnic calls register_driver routine rtnl_lock,
|
|
* it will have higher precedence than bnx2fc_dev_lock.
|
|
* unregister_device() cannot be called with bnx2fc_dev_lock
|
|
* held.
|
|
*/
|
|
mutex_lock(&bnx2fc_dev_lock);
|
|
list_splice_init(&adapter_list, &to_be_deleted);
|
|
adapter_count = 0;
|
|
mutex_unlock(&bnx2fc_dev_lock);
|
|
|
|
/* Unregister with cnic */
|
|
list_for_each_entry_safe(hba, next, &to_be_deleted, list) {
|
|
list_del_init(&hba->list);
|
|
printk(KERN_ERR PFX "MOD_EXIT:destroy hba = 0x%p\n",
|
|
hba);
|
|
bnx2fc_ulp_stop(hba);
|
|
/* unregister cnic device */
|
|
if (test_and_clear_bit(BNX2FC_CNIC_REGISTERED,
|
|
&hba->reg_with_cnic))
|
|
hba->cnic->unregister_device(hba->cnic,
|
|
CNIC_ULP_FCOE);
|
|
bnx2fc_hba_destroy(hba);
|
|
}
|
|
cnic_unregister_driver(CNIC_ULP_FCOE);
|
|
|
|
/* Destroy global thread */
|
|
bg = &bnx2fc_global;
|
|
spin_lock_bh(&bg->fcoe_rx_list.lock);
|
|
l2_thread = bg->kthread;
|
|
bg->kthread = NULL;
|
|
while ((skb = __skb_dequeue(&bg->fcoe_rx_list)) != NULL)
|
|
kfree_skb(skb);
|
|
|
|
spin_unlock_bh(&bg->fcoe_rx_list.lock);
|
|
|
|
if (l2_thread)
|
|
kthread_stop(l2_thread);
|
|
|
|
cpuhp_remove_state(bnx2fc_online_state);
|
|
|
|
destroy_workqueue(bnx2fc_wq);
|
|
/*
|
|
* detach from scsi transport
|
|
* must happen after all destroys are done
|
|
*/
|
|
bnx2fc_release_transport();
|
|
|
|
/* detach from fcoe transport */
|
|
fcoe_transport_detach(&bnx2fc_transport);
|
|
}
|
|
|
|
module_init(bnx2fc_mod_init);
|
|
module_exit(bnx2fc_mod_exit);
|
|
|
|
static struct fcoe_sysfs_function_template bnx2fc_fcoe_sysfs_templ = {
|
|
.set_fcoe_ctlr_enabled = bnx2fc_ctlr_enabled,
|
|
.get_fcoe_ctlr_link_fail = fcoe_ctlr_get_lesb,
|
|
.get_fcoe_ctlr_vlink_fail = fcoe_ctlr_get_lesb,
|
|
.get_fcoe_ctlr_miss_fka = fcoe_ctlr_get_lesb,
|
|
.get_fcoe_ctlr_symb_err = fcoe_ctlr_get_lesb,
|
|
.get_fcoe_ctlr_err_block = fcoe_ctlr_get_lesb,
|
|
.get_fcoe_ctlr_fcs_error = fcoe_ctlr_get_lesb,
|
|
|
|
.get_fcoe_fcf_selected = fcoe_fcf_get_selected,
|
|
.get_fcoe_fcf_vlan_id = bnx2fc_fcf_get_vlan_id,
|
|
};
|
|
|
|
static struct fc_function_template bnx2fc_transport_function = {
|
|
.show_host_node_name = 1,
|
|
.show_host_port_name = 1,
|
|
.show_host_supported_classes = 1,
|
|
.show_host_supported_fc4s = 1,
|
|
.show_host_active_fc4s = 1,
|
|
.show_host_maxframe_size = 1,
|
|
|
|
.show_host_port_id = 1,
|
|
.show_host_supported_speeds = 1,
|
|
.get_host_speed = fc_get_host_speed,
|
|
.show_host_speed = 1,
|
|
.show_host_port_type = 1,
|
|
.get_host_port_state = fc_get_host_port_state,
|
|
.show_host_port_state = 1,
|
|
.show_host_symbolic_name = 1,
|
|
|
|
.dd_fcrport_size = (sizeof(struct fc_rport_libfc_priv) +
|
|
sizeof(struct bnx2fc_rport)),
|
|
.show_rport_maxframe_size = 1,
|
|
.show_rport_supported_classes = 1,
|
|
|
|
.show_host_fabric_name = 1,
|
|
.show_starget_node_name = 1,
|
|
.show_starget_port_name = 1,
|
|
.show_starget_port_id = 1,
|
|
.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
|
|
.show_rport_dev_loss_tmo = 1,
|
|
.get_fc_host_stats = bnx2fc_get_host_stats,
|
|
|
|
.issue_fc_host_lip = bnx2fc_fcoe_reset,
|
|
|
|
.terminate_rport_io = bnx2fc_rport_terminate_io,
|
|
|
|
.vport_create = bnx2fc_vport_create,
|
|
.vport_delete = bnx2fc_vport_destroy,
|
|
.vport_disable = bnx2fc_vport_disable,
|
|
.bsg_request = fc_lport_bsg_request,
|
|
};
|
|
|
|
static struct fc_function_template bnx2fc_vport_xport_function = {
|
|
.show_host_node_name = 1,
|
|
.show_host_port_name = 1,
|
|
.show_host_supported_classes = 1,
|
|
.show_host_supported_fc4s = 1,
|
|
.show_host_active_fc4s = 1,
|
|
.show_host_maxframe_size = 1,
|
|
|
|
.show_host_port_id = 1,
|
|
.show_host_supported_speeds = 1,
|
|
.get_host_speed = fc_get_host_speed,
|
|
.show_host_speed = 1,
|
|
.show_host_port_type = 1,
|
|
.get_host_port_state = fc_get_host_port_state,
|
|
.show_host_port_state = 1,
|
|
.show_host_symbolic_name = 1,
|
|
|
|
.dd_fcrport_size = (sizeof(struct fc_rport_libfc_priv) +
|
|
sizeof(struct bnx2fc_rport)),
|
|
.show_rport_maxframe_size = 1,
|
|
.show_rport_supported_classes = 1,
|
|
|
|
.show_host_fabric_name = 1,
|
|
.show_starget_node_name = 1,
|
|
.show_starget_port_name = 1,
|
|
.show_starget_port_id = 1,
|
|
.set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
|
|
.show_rport_dev_loss_tmo = 1,
|
|
.get_fc_host_stats = fc_get_host_stats,
|
|
.issue_fc_host_lip = bnx2fc_fcoe_reset,
|
|
.terminate_rport_io = fc_rport_terminate_io,
|
|
.bsg_request = fc_lport_bsg_request,
|
|
};
|
|
|
|
/*
|
|
* Additional scsi_host attributes.
|
|
*/
|
|
static ssize_t
|
|
bnx2fc_tm_timeout_show(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct Scsi_Host *shost = class_to_shost(dev);
|
|
struct fc_lport *lport = shost_priv(shost);
|
|
struct fcoe_port *port = lport_priv(lport);
|
|
struct bnx2fc_interface *interface = port->priv;
|
|
|
|
sprintf(buf, "%u\n", interface->tm_timeout);
|
|
return strlen(buf);
|
|
}
|
|
|
|
static ssize_t
|
|
bnx2fc_tm_timeout_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t count)
|
|
{
|
|
struct Scsi_Host *shost = class_to_shost(dev);
|
|
struct fc_lport *lport = shost_priv(shost);
|
|
struct fcoe_port *port = lport_priv(lport);
|
|
struct bnx2fc_interface *interface = port->priv;
|
|
int rval, val;
|
|
|
|
rval = kstrtouint(buf, 10, &val);
|
|
if (rval)
|
|
return rval;
|
|
if (val > 255)
|
|
return -ERANGE;
|
|
|
|
interface->tm_timeout = (u8)val;
|
|
return strlen(buf);
|
|
}
|
|
|
|
static DEVICE_ATTR(tm_timeout, S_IRUGO|S_IWUSR, bnx2fc_tm_timeout_show,
|
|
bnx2fc_tm_timeout_store);
|
|
|
|
static struct device_attribute *bnx2fc_host_attrs[] = {
|
|
&dev_attr_tm_timeout,
|
|
NULL,
|
|
};
|
|
|
|
/**
|
|
* scsi_host_template structure used while registering with SCSI-ml
|
|
*/
|
|
static struct scsi_host_template bnx2fc_shost_template = {
|
|
.module = THIS_MODULE,
|
|
.name = "QLogic Offload FCoE Initiator",
|
|
.queuecommand = bnx2fc_queuecommand,
|
|
.eh_timed_out = fc_eh_timed_out,
|
|
.eh_abort_handler = bnx2fc_eh_abort, /* abts */
|
|
.eh_device_reset_handler = bnx2fc_eh_device_reset, /* lun reset */
|
|
.eh_target_reset_handler = bnx2fc_eh_target_reset, /* tgt reset */
|
|
.eh_host_reset_handler = fc_eh_host_reset,
|
|
.slave_alloc = fc_slave_alloc,
|
|
.change_queue_depth = scsi_change_queue_depth,
|
|
.this_id = -1,
|
|
.cmd_per_lun = 3,
|
|
.sg_tablesize = BNX2FC_MAX_BDS_PER_CMD,
|
|
.dma_boundary = 0x7fff,
|
|
.max_sectors = 0x3fbf,
|
|
.track_queue_depth = 1,
|
|
.slave_configure = bnx2fc_slave_configure,
|
|
.shost_attrs = bnx2fc_host_attrs,
|
|
};
|
|
|
|
static struct libfc_function_template bnx2fc_libfc_fcn_templ = {
|
|
.frame_send = bnx2fc_xmit,
|
|
.elsct_send = bnx2fc_elsct_send,
|
|
.fcp_abort_io = bnx2fc_abort_io,
|
|
.fcp_cleanup = bnx2fc_cleanup,
|
|
.get_lesb = fcoe_get_lesb,
|
|
.rport_event_callback = bnx2fc_rport_event_handler,
|
|
};
|
|
|
|
/**
|
|
* bnx2fc_cnic_cb - global template of bnx2fc - cnic driver interface
|
|
* structure carrying callback function pointers
|
|
*/
|
|
static struct cnic_ulp_ops bnx2fc_cnic_cb = {
|
|
.owner = THIS_MODULE,
|
|
.cnic_init = bnx2fc_ulp_init,
|
|
.cnic_exit = bnx2fc_ulp_exit,
|
|
.cnic_start = bnx2fc_ulp_start,
|
|
.cnic_stop = bnx2fc_ulp_stop,
|
|
.indicate_kcqes = bnx2fc_indicate_kcqe,
|
|
.indicate_netevent = bnx2fc_indicate_netevent,
|
|
.cnic_get_stats = bnx2fc_ulp_get_stats,
|
|
};
|