/* 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)
*/
/* 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; static DEFINE_SPINLOCK(bnx2fc_global_lock);
static 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.");
static 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.");
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;
}
staticvoid 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.
*/
}
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);
}
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;
}
/* send down to lld */
fr_dev(fp) = lport; if (port->fcoe_pending_queue.qlen)
fcoe_check_wait_queue(lport, skb); elseif (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
*/ staticint 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;
if (unlikely(lport == NULL)) {
printk(KERN_ERR PFX "bnx2fc_rcv: lport is NULL\n"); goto err;
}
skb = skb_share_check(skb, GFP_ATOMIC); if (!skb) return -1;
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;
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;
}
fr_crc = le32_to_cpu(fr_crc(fp));
if (unlikely(fr_crc != ~crc32(~0, skb->data, fr_len))) {
crc_err = this_cpu_inc_return(lport->stats->InvalidCRCCount); if (crc_err < 5)
printk(KERN_WARNING PFX "dropping frame with " "CRC error\n");
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
*/ staticint 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);
/** * 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);
}
/* 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;
/** * 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.
*/ staticvoid bnx2fc_indicate_netevent(void *context, unsignedlong 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); return;
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);
}
} elseif (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;
this_cpu_inc(lport->stats->LinkFailureCount);
fcoe_clean_pending_queue(lport);
wait_for_upload = 1;
}
}
}
mutex_unlock(&bnx2fc_dev_lock);
/* 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;
}
/** * 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
*/ staticint 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. * * @lport: The local port * @addr: Location of data to copy * * Remove any previously-set unicast MAC filter. * Add secondary FCoE MAC address filter for our OUI.
*/ staticvoid 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_hba_create - create a new bnx2fc hba * * @cnic: pointer to cnic device * * Creates a new FCoE hba on the given device. *
*/ staticstruct 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);
/** * 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
*/ staticstruct 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;
}
/** * bnx2fc_destroy - Destroy a bnx2fc FCoE interface * * @netdev: The net device that the FCoE interface is on * * Called from sysfs. * * Returns: 0 for success
*/ staticint 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;
}
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:
strscpy(hba->chip_num, "BCM57710", sizeof(hba->chip_num)); break; case PCI_DEVICE_ID_NX2_57711:
strscpy(hba->chip_num, "BCM57711", sizeof(hba->chip_num)); break; case PCI_DEVICE_ID_NX2_57712: case PCI_DEVICE_ID_NX2_57712_MF: case PCI_DEVICE_ID_NX2_57712_VF:
strscpy(hba->chip_num, "BCM57712", sizeof(hba->chip_num)); break; case PCI_DEVICE_ID_NX2_57800: case PCI_DEVICE_ID_NX2_57800_MF: case PCI_DEVICE_ID_NX2_57800_VF:
strscpy(hba->chip_num, "BCM57800", sizeof(hba->chip_num)); break; case PCI_DEVICE_ID_NX2_57810: case PCI_DEVICE_ID_NX2_57810_MF: case PCI_DEVICE_ID_NX2_57810_VF:
strscpy(hba->chip_num, "BCM57810", sizeof(hba->chip_num)); 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:
strscpy(hba->chip_num, "BCM57840", sizeof(hba->chip_num)); break; default:
pr_err(PFX "Unknown device id 0x%x\n", pdev->device); break;
}
pci_dev_get(hba->pcidev); 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.
*/ staticvoid 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);
/* * 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;
}
/* 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.
*/ staticvoid 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;
}
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);
/* Assumes rtnl_lock and the bnx2fc_dev_lock are already taken */ staticint __bnx2fc_disable(struct fcoe_ctlr *ctlr)
{ struct bnx2fc_interface *interface = fcoe_ctlr_priv(ctlr);
if (interface->enabled) { 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;
}
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;
}
/** * 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.
*/ staticint 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;
}
}
/** * _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
*/ staticint _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;
}
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;
/** * 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
*/ staticint 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.
*/ staticint bnx2fc_ctlr_alloc(struct net_device *netdev)
{ return _bnx2fc_create(netdev, FIP_MODE_FABRIC,
BNX2FC_CREATE_LINK_DOWN);
}
/* Called with bnx2fc_dev_lock held */
list_for_each_entry(interface, &if_list, list) { if (interface->netdev == netdev) return interface;
} return NULL;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.