* Re: [BNX2]: Fix suspend/resume problem.
From: David Miller @ 2007-08-04 3:57 UTC (permalink / raw)
To: mchan; +Cc: netdev
In-Reply-To: <1186180354.18322.79.camel@dell>
From: "Michael Chan" <mchan@broadcom.com>
Date: Fri, 03 Aug 2007 15:32:34 -0700
> [BNX2]: Fix suspend/resume problem.
>
> The device would not resume properly if it was shutdown before the system
> was suspended. In such scenario where the netif_running state is 0,
> bnx2_suspend() would not save the PCI state and so the memory enable bit
> and bus master enable bit would be lost.
>
> We fix this by always saving and restoring the PCI state in
> bnx2_suspend() and bnx2_resume() regardless of netif_running() state.
>
> Update version to 1.6.4.
>
> Signed-off-by: Michael Chan <mchan@broadcom.com>
Also applied, thanks Michael.
^ permalink raw reply
* [PATCH] ixgbe: New driver for Pci-Express 10GbE 82598 support
From: Auke Kok @ 2007-08-04 4:04 UTC (permalink / raw)
To: jeff, netdev
Cc: ayyappan.veeraiyan, akpm, arjan, hch, shemminger, nhorman, inaky,
mb, john.ronciak
This patch adds support for the Intel 82598 PCI-Express 10GbE
chipset. Devices will be available on the market soon.
This version of the driver is largely the same as the last release:
* Driver uses a single RX and single TX queue, each using 1 MSI-X
irq vector.
* Driver runs in NAPI mode only
* Driver is largely multiqueue-ready (TM)
Changes since last version:
* Removed ixgbe_param.c completely:
- removed rx parameter (unused)
- flowcontrol uses hardcoded defaults
- flowcontrol can be enabled as usual per ethtool
- LLI code was retired for now (it was disabled by default)
- buffermode setting uses the optimal case (PS for jumbo,
single buffers for 1500MTU's and below)
- irq moderation settings were implemented using the ethtool
coalesce settings:
ethtool -c ethX - displays rx and tx irq delay moderation
ethtool -C ethX rx-usecs N tx-usecs M - set irq delay moderation
times for rx, tx.
(in msi or legacy irq mode it will only use rx-usecs for both
rx and tx)
* removed no longer existing ethtool perm_addr ops
* Namespace cleanup
* Sparse address space annotations
* Removed a bunch of dead/unused code
* DEBUG_SHIRQ panic fix when going ifdn
Signed-off-by: Auke Kok <auke-jan.h.kok@intel.com>
Signed-off-by: Ayyappan Veeraiyan <ayyappan.veeraiyan@intel.com>
---
Documentation/networking/ixgbe.txt | 72 +
MAINTAINERS | 10 +-
drivers/net/Kconfig | 22 +
drivers/net/Makefile | 1 +
drivers/net/ixgbe/Makefile | 36 +
drivers/net/ixgbe/ixgbe.h | 243 ++++
drivers/net/ixgbe/ixgbe_82598.c | 598 ++++++++
drivers/net/ixgbe/ixgbe_api.c | 553 +++++++
drivers/net/ixgbe/ixgbe_api.h | 80 ++
drivers/net/ixgbe/ixgbe_common.c | 1679 ++++++++++++++++++++++
drivers/net/ixgbe/ixgbe_common.h | 43 +
drivers/net/ixgbe/ixgbe_ethtool.c | 931 ++++++++++++
drivers/net/ixgbe/ixgbe_main.c | 2765 ++++++++++++++++++++++++++++++++++++
drivers/net/ixgbe/ixgbe_osdep.h | 69 +
drivers/net/ixgbe/ixgbe_phy.c | 601 ++++++++
drivers/net/ixgbe/ixgbe_phy.h | 40 +
drivers/net/ixgbe/ixgbe_type.h | 1390 ++++++++++++++++++
17 files changed, 9127 insertions(+), 6 deletions(-)
create mode 100644 Documentation/networking/ixgbe.txt
create mode 100644 drivers/net/ixgbe/Makefile
create mode 100644 drivers/net/ixgbe/ixgbe.h
create mode 100644 drivers/net/ixgbe/ixgbe_82598.c
create mode 100644 drivers/net/ixgbe/ixgbe_api.c
create mode 100644 drivers/net/ixgbe/ixgbe_api.h
create mode 100644 drivers/net/ixgbe/ixgbe_common.c
create mode 100644 drivers/net/ixgbe/ixgbe_common.h
create mode 100644 drivers/net/ixgbe/ixgbe_ethtool.c
create mode 100644 drivers/net/ixgbe/ixgbe_main.c
create mode 100644 drivers/net/ixgbe/ixgbe_osdep.h
create mode 100644 drivers/net/ixgbe/ixgbe_phy.c
create mode 100644 drivers/net/ixgbe/ixgbe_phy.h
create mode 100644 drivers/net/ixgbe/ixgbe_type.h
diff --git a/Documentation/networking/ixgbe.txt b/Documentation/networking/ixgbe.txt
new file mode 100644
index 0000000..823d69c
--- /dev/null
+++ b/Documentation/networking/ixgbe.txt
@@ -0,0 +1,72 @@
+Linux* Base Driver for the 10 Gigabit Family of Adapters
+================================================================
+
+July 09, 2007
+
+
+Contents
+========
+
+- In This Release
+- Identifying Your Adapter
+- Command Line Parameters
+- Support
+
+In This Release
+===============
+
+This file describes the Linux* Base Driver for the 10 Gigabit PCI Express
+Family of Adapters. This driver supports the 2.6.x kernel. This driver
+includes support for Itanium(R)2-based systems.
+
+The following features are now available in supported kernels:
+ - Native VLANs
+ - Channel Bonding (teaming)
+ - SNMP
+
+Channel Bonding documentation can be found in the Linux kernel source:
+/Documentation/networking/bonding.txt
+
+Instructions on updating ethtool can be found in the section "Additional
+Configurations" later in this document.
+
+
+Identifying Your Adapter
+========================
+
+The following Intel network adapters are compatible with the drivers in this
+release:
+
+Controller Adapter Name Physical Layer
+---------- ------------ --------------
+82598 Intel(R) 10GbE-LR/LRM/SR
+ Server Adapters 10G Base -SR (850 nm optical fiber)
+ 10G Base -LRM (850 nm optical fiber)
+ 10G Base -LR (1310 nm optical fiber)
+
+For more information on how to identify your adapter, go to the Adapter &
+Driver ID Guide at:
+
+ http://support.intel.com/support/network/sb/CS-012904.htm
+
+For the latest Intel network drivers for Linux, refer to the following
+website. In the search field, enter your adapter name or type, or use the
+networking link on the left to search for your adapter:
+
+ http://downloadfinder.intel.com/scripts-df/support_intel.asp
+
+
+Support
+=======
+
+For general information, go to the Intel support website at:
+
+ http://support.intel.com
+
+or the Intel Wired Networking project hosted by Sourceforge at:
+
+ http://sourceforge.net/projects/e1000
+
+If an issue is identified with the released source code on the supported
+kernel with a supported adapter, email the specific information related
+to the issue to e1000-devel@lists.sf.net
diff --git a/MAINTAINERS b/MAINTAINERS
index e65e96a..66ebc66 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1998,16 +1998,14 @@ W: http://sourceforge.net/projects/e1000/
S: Supported
INTEL PRO/10GbE SUPPORT
-P: Jeff Kirsher
-M: jeffrey.t.kirsher@intel.com
P: Ayyappan Veeraiyan
M: ayyappan.veeraiyan@intel.com
-P: John Ronciak
-M: john.ronciak@intel.com
-P: Jesse Brandeburg
-M: jesse.brandeburg@intel.com
P: Auke Kok
M: auke-jan.h.kok@intel.com
+P: Jesse Brandeburg
+M: jesse.brandeburg@intel.com
+P: John Ronciak
+M: john.ronciak@intel.com
L: e1000-devel@lists.sourceforge.net
W: http://sourceforge.net/projects/e1000/
S: Supported
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 81ef81c..bbd25c7 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2405,6 +2405,28 @@ config EHEA
To compile the driver as a module, choose M here. The module
will be called ehea.
+config IXGBE
+ tristate "Intel(R) PRO/10GbE PCI Express adapters support"
+ depends on PCI
+ ---help---
+ This driver supports Intel(R) PRO/10GbE PCI Express family of
+ adapters. For more information on how to identify your adapter, go
+ to the Adapter & Driver ID Guide at:
+
+ <http://support.intel.com/support/network/adapter/pro100/21397.htm>
+
+ For general information and support, go to the Intel support
+ website at:
+
+ <http://support.intel.com>
+
+ More specific information on configuring the driver is in
+ <file:Documentation/networking/ixgbe.txt>.
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/networking/net-modules.txt>. The module
+ will be called ixgbe.
+
config IXGB
tristate "Intel(R) PRO/10GbE support"
depends on PCI
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index e684212..7529c8f 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_E1000) += e1000/
obj-$(CONFIG_IBM_EMAC) += ibm_emac/
+obj-$(CONFIG_IXGBE) += ixgbe/
obj-$(CONFIG_IXGB) += ixgb/
obj-$(CONFIG_CHELSIO_T1) += chelsio/
obj-$(CONFIG_CHELSIO_T3) += cxgb3/
diff --git a/drivers/net/ixgbe/Makefile b/drivers/net/ixgbe/Makefile
new file mode 100644
index 0000000..61526f1
--- /dev/null
+++ b/drivers/net/ixgbe/Makefile
@@ -0,0 +1,36 @@
+################################################################################
+#
+# Intel PRO/10GbE PCI Express Linux driver
+# Copyright(c) 1999 - 2007 Intel Corporation.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU General Public License,
+# version 2, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# The full GNU General Public License is included in this distribution in
+# the file called "COPYING".
+#
+# Contact Information:
+# Linux NICS <linux.nics@intel.com>
+# e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+# Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+#
+################################################################################
+
+#
+# Makefile for the Intel(R) PRO/10GbE PCI Express ethernet driver
+#
+
+obj-$(CONFIG_IXGBE) += ixgbe.o
+
+ixgbe-objs := ixgbe_main.o ixgbe_common.o ixgbe_api.o ixgbe_ethtool.o \
+ ixgbe_82598.o ixgbe_phy.o
diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ixgbe/ixgbe.h
new file mode 100644
index 0000000..74ddd6a
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe.h
@@ -0,0 +1,243 @@
+/*******************************************************************************
+
+ Intel PRO/10GbE PCI Express Linux driver
+ Copyright(c) 1999 - 2007 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#ifndef _IXGBE_H_
+#define _IXGBE_H_
+
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/vmalloc.h>
+
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+
+#include "ixgbe_api.h"
+
+#define IXGBE_ERR(args...) printk(KERN_ERR "ixgbe: " args)
+
+#define PFX "ixgbe: "
+#define DPRINTK(nlevel, klevel, fmt, args...) \
+ (void)((NETIF_MSG_##nlevel & adapter->msg_enable) && \
+ printk(KERN_##klevel PFX "%s: %s: " fmt, adapter->netdev->name, \
+ __FUNCTION__ , ## args))
+
+/* TX/RX descriptor defines */
+#define IXGBE_DEFAULT_TXD 1024
+#define IXGBE_MAX_TXD 4096
+#define IXGBE_MIN_TXD 64
+
+#define IXGBE_DEFAULT_RXD 1024
+#define IXGBE_MAX_RXD 4096
+#define IXGBE_MIN_RXD 64
+
+#define IXGBE_DEFAULT_RXQ 1
+#define IXGBE_MAX_RXQ 1
+#define IXGBE_MIN_RXQ 1
+
+/* flow control */
+#define DEFAULT_FCRTL 0x10000
+#define MIN_FCRTL 0
+#define MAX_FCRTL 0x7FF80
+#define DEFAULT_FCRTH 0x20000
+#define MIN_FCRTH 0
+#define MAX_FCRTH 0x7FFF0
+#define DEFAULT_FCPAUSE 0x6800 /* may be too long */
+#define MIN_FCPAUSE 0
+#define MAX_FCPAUSE 0xFFFF
+
+/* interrupt throttling */
+#define IXGBE_ITR_DEFAULT_USECS 125 /* 8000 irqs per second */
+#define IXGBE_ITR_MIN_USECS 2 /* 500k */
+#define IXGBE_ITR_MAX_USECS 10000 /* 100 */
+
+
+#define MAXIMUM_ETHERNET_VLAN_SIZE (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN)
+
+/* Used for packet split */
+#define IXGBE_RXBUFFER_64 64
+#define IXGBE_RXBUFFER_128 128
+#define IXGBE_RXBUFFER_256 256
+/* Supported Rx Buffer Sizes */
+#define IXGBE_RXBUFFER_2048 2048
+
+#define IXGBE_RX_HDR_SIZE IXGBE_RXBUFFER_256
+
+/* How many Tx Descriptors do we need to call netif_wake_queue? */
+#define IXGBE_TX_QUEUE_WAKE 16
+
+/* How many Rx Buffers do we bundle into one write to the hardware ? */
+#define IXGBE_RX_BUFFER_WRITE 16 /* Must be power of 2 */
+#define IXGBE_TX_FLAGS_CSUM (u32)(1)
+#define IXGBE_TX_FLAGS_VLAN (u32)(1 << 1)
+#define IXGBE_TX_FLAGS_TSO (u32)(1 << 2)
+#define IXGBE_TX_FLAGS_IPV4 (u32)(1 << 3)
+#define IXGBE_TX_FLAGS_VLAN_MASK 0xffff0000
+#define IXGBE_TX_FLAGS_VLAN_SHIFT 16
+
+/* wrapper around a pointer to a socket buffer,
+ * so a DMA handle can be stored along with the buffer */
+struct ixgbe_tx_buffer {
+ struct sk_buff *skb;
+ dma_addr_t dma;
+ unsigned long time_stamp;
+ u16 length;
+ u16 next_to_watch;
+};
+
+struct ixgbe_rx_buffer {
+ struct sk_buff *skb;
+ dma_addr_t dma;
+ struct page *page;
+ dma_addr_t page_dma;
+};
+
+struct ixgbe_queue_stats {
+ u64 packets;
+ u64 bytes;
+};
+
+struct ixgbe_ring {
+ struct ixgbe_adapter *adapter; /* backlink */
+ void *desc; /* descriptor ring memory */
+ dma_addr_t dma; /* phys. address of descriptor ring */
+ unsigned int size; /* length in bytes */
+ unsigned int count; /* amount of descriptors */
+ unsigned int next_to_use;
+ unsigned int next_to_clean;
+
+ union {
+ struct ixgbe_tx_buffer *tx_buffer_info;
+ struct ixgbe_rx_buffer *rx_buffer_info;
+ };
+
+ u16 head;
+ u16 tail;
+
+ /* To protect race between sender and clean_tx_irq */
+ spinlock_t tx_lock;
+
+ struct ixgbe_queue_stats stats;
+
+ u32 eims_value;
+ u32 itr_val;
+ u16 itr_range;
+ u16 itr_register;
+
+ char name[IFNAMSIZ + 5];
+};
+
+#define IXGBE_DESC_UNUSED(R) \
+ ((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \
+ (R)->next_to_clean - (R)->next_to_use - 1)
+
+#define IXGBE_RX_DESC_ADV(R, i) \
+ (&(((union ixgbe_adv_rx_desc *)((R).desc))[i]))
+#define IXGBE_TX_DESC_ADV(R, i) \
+ (&(((union ixgbe_adv_tx_desc *)((R).desc))[i]))
+#define IXGBE_TX_CTXTDESC_ADV(R, i) \
+ (&(((struct ixgbe_adv_tx_context_desc *)((R).desc))[i]))
+
+#define IXGBE_MAX_JUMBO_FRAME_SIZE 16128
+
+/* board specific private data structure */
+struct ixgbe_adapter {
+ struct timer_list watchdog_timer;
+ struct vlan_group *vlgrp;
+ u16 bd_number;
+ u16 rx_buf_len;
+ atomic_t irq_sem;
+ struct work_struct reset_task;
+
+ /* TX */
+ struct ixgbe_ring *tx_ring; /* One per active queue */
+ u64 restart_queue;
+ u64 lsc_int;
+ u64 hw_tso_ctxt;
+ u64 hw_tso6_ctxt;
+ u32 tx_timeout_count;
+ bool detect_tx_hung;
+
+ /* RX */
+ struct ixgbe_ring *rx_ring; /* One per active queue */
+ u64 hw_csum_tx_good;
+ u64 hw_csum_rx_error;
+ u64 hw_csum_rx_good;
+ u64 non_eop_descs;
+ int num_tx_queues;
+ int num_rx_queues;
+ struct msix_entry *msix_entries;
+
+ u64 rx_hdr_split;
+ u32 alloc_rx_page_failed;
+ u32 alloc_rx_buff_failed;
+ u32 flags;
+#define IXGBE_FLAG_RX_CSUM_ENABLED (u32)(1)
+#define IXGBE_FLAG_MSI_ENABLED (u32)(1 << 1)
+#define IXGBE_FLAG_MSIX_ENABLED (u32)(1 << 2)
+#define IXGBE_FLAG_RX_PS_ENABLED (u32)(1 << 3)
+#define IXGBE_FLAG_IN_NETPOLL (u32)(1 << 4)
+
+ /* Interrupt Throttle Rate */
+ u32 rx_eitr;
+ u32 tx_eitr;
+
+ /* OS defined structs */
+ struct net_device *netdev;
+ struct pci_dev *pdev;
+ struct net_device_stats net_stats;
+
+ /* structs defined in ixgbe_hw.h */
+ struct ixgbe_hw hw;
+ u16 msg_enable;
+ struct ixgbe_hw_stats stats;
+ char lsc_name[IFNAMSIZ + 5];
+
+ u64 tx_busy;
+};
+
+extern char ixgbe_driver_name[];
+extern char ixgbe_driver_version[];
+
+extern int ixgbe_up(struct ixgbe_adapter *adapter);
+extern void ixgbe_down(struct ixgbe_adapter *adapter);
+extern void ixgbe_reset(struct ixgbe_adapter *adapter);
+extern int ixgbe_setup_rx_resources(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *rxdr);
+extern int ixgbe_setup_tx_resources(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *txdr);
+extern void ixgbe_free_rx_resources(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *rx_ring);
+extern void ixgbe_free_tx_resources(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *tx_ring);
+extern void ixgbe_update_stats(struct ixgbe_adapter *adapter);
+
+extern void ixgbe_set_ethtool_ops(struct net_device *netdev);
+extern s32 ixgbe_mac_addr_valid(u8 *mac_addr);
+extern void ixgbe_mta_set(struct ixgbe_hw *hw, u8 *mc_addr);
+extern void ixgbe_mc_addr_add(struct ixgbe_hw *hw, u8 *mc_addr);
+#endif /* _IXGBE_H_ */
diff --git a/drivers/net/ixgbe/ixgbe_82598.c b/drivers/net/ixgbe/ixgbe_82598.c
new file mode 100644
index 0000000..917e6b7
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_82598.c
@@ -0,0 +1,598 @@
+/*******************************************************************************
+
+ Intel PRO/10GbE PCI Express Linux driver
+ Copyright(c) 1999 - 2007 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include "ixgbe_type.h"
+#include "ixgbe_api.h"
+#include "ixgbe_common.h"
+#include "ixgbe_phy.h"
+
+#define IXGBE_82598_MAX_TX_QUEUES 32
+#define IXGBE_82598_MAX_RX_QUEUES 64
+#define IXGBE_82598_RAR_ENTRIES 16
+
+s32 ixgbe_init_shared_code_82598(struct ixgbe_hw *hw);
+
+static s32 ixgbe_assign_func_pointers_82598(struct ixgbe_hw *hw);
+static s32 ixgbe_get_link_settings_82598(struct ixgbe_hw *hw, u32 *speed,
+ bool *autoneg);
+static s32 ixgbe_get_copper_link_settings_82598(struct ixgbe_hw *hw,
+ u32 *speed, bool *autoneg);
+static enum ixgbe_media_type ixgbe_get_media_type_82598(struct ixgbe_hw *hw);
+static u32 ixgbe_get_num_of_tx_queues_82598(struct ixgbe_hw *hw);
+static u32 ixgbe_get_num_of_rx_queues_82598(struct ixgbe_hw *hw);
+static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw);
+static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw, u32 *speed,
+ bool *link_up);
+static s32 ixgbe_setup_mac_link_speed_82598(struct ixgbe_hw *hw, u32 speed,
+ bool autoneg,
+ bool autoneg_wait_to_complete);
+static s32 ixgbe_setup_copper_link_82598(struct ixgbe_hw *hw);
+static s32 ixgbe_check_copper_link_82598(struct ixgbe_hw *hw, u32 *speed,
+ bool *link_up);
+static s32 ixgbe_setup_copper_link_speed_82598(struct ixgbe_hw *hw, u32 speed,
+ bool autoneg,
+ bool autoneg_wait_to_complete);
+static s32 ixgbe_reset_hw_82598(struct ixgbe_hw *hw);
+static u32 ixgbe_get_num_rx_addrs_82598(struct ixgbe_hw *hw);
+
+
+/**
+ * ixgbe_init_shared_code_82598 - Inits func ptrs and MAC type
+ * @hw: pointer to hardware structure
+ *
+ * Initialize the shared code for 82598. This will assign function pointers
+ * and assign the MAC type. Does not touch the hardware.
+ **/
+s32 ixgbe_init_shared_code_82598(struct ixgbe_hw *hw)
+{
+ /* Set MAC type */
+ hw->mac.type = ixgbe_mac_82598EB;
+
+ /* Assign function pointers */
+ ixgbe_assign_func_pointers_82598(hw);
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_assign_func_pointers_82598 - Assigns 82598-specific funtion pointers
+ * @hw: pointer to hardware structure
+ *
+ * Note - Generic function pointers have already been assigned, so the
+ * function pointers set here are only for 82598-specific functions.
+ **/
+static s32 ixgbe_assign_func_pointers_82598(struct ixgbe_hw *hw)
+{
+
+ hw->func.ixgbe_func_get_media_type =
+ &ixgbe_get_media_type_82598;
+ hw->func.ixgbe_func_get_num_of_tx_queues =
+ &ixgbe_get_num_of_tx_queues_82598;
+ hw->func.ixgbe_func_get_num_of_rx_queues =
+ &ixgbe_get_num_of_rx_queues_82598;
+ hw->func.ixgbe_func_reset_hw = &ixgbe_reset_hw_82598;
+
+ hw->func.ixgbe_func_get_num_rx_addrs =
+ &ixgbe_get_num_rx_addrs_82598;
+
+ /* Link */
+ if (ixgbe_get_media_type(hw) == ixgbe_media_type_copper) {
+ hw->func.ixgbe_func_setup_link =
+ &ixgbe_setup_copper_link_82598;
+ hw->func.ixgbe_func_check_link =
+ &ixgbe_check_copper_link_82598;
+ hw->func.ixgbe_func_setup_link_speed =
+ &ixgbe_setup_copper_link_speed_82598;
+ hw->func.ixgbe_func_get_link_settings =
+ &ixgbe_get_copper_link_settings_82598;
+ } else {
+ hw->func.ixgbe_func_setup_link =
+ &ixgbe_setup_mac_link_82598;
+ hw->func.ixgbe_func_check_link =
+ &ixgbe_check_mac_link_82598;
+ hw->func.ixgbe_func_setup_link_speed =
+ &ixgbe_setup_mac_link_speed_82598;
+ hw->func.ixgbe_func_get_link_settings =
+ &ixgbe_get_link_settings_82598;
+ }
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_get_link_settings_82598 - Determines default link settings
+ * @hw: pointer to hardware structure
+ * @speed: pointer to link speed
+ * @autoneg: boolean auto-negotiation value
+ *
+ * Determines the default link settings by reading the AUTOC register.
+ **/
+static s32 ixgbe_get_link_settings_82598(struct ixgbe_hw *hw, u32 *speed,
+ bool *autoneg)
+{
+ s32 status = IXGBE_SUCCESS;
+ s32 autoc_reg;
+
+ autoc_reg = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+
+ if (hw->mac.link_settings_loaded) {
+ autoc_reg &= ~IXGBE_AUTOC_LMS_ATTACH_TYPE;
+ autoc_reg &= ~IXGBE_AUTOC_LMS_MASK;
+ autoc_reg |= hw->mac.link_attach_type;
+ autoc_reg |= hw->mac.link_mode_select;
+ }
+
+ switch (autoc_reg & IXGBE_AUTOC_LMS_MASK) {
+ case IXGBE_AUTOC_LMS_1G_LINK_NO_AN:
+ *speed = IXGBE_LINK_SPEED_1GB_FULL;
+ *autoneg = false;
+ break;
+
+ case IXGBE_AUTOC_LMS_10G_LINK_NO_AN:
+ *speed = IXGBE_LINK_SPEED_10GB_FULL;
+ *autoneg = false;
+ break;
+
+ case IXGBE_AUTOC_LMS_1G_AN:
+ *speed = IXGBE_LINK_SPEED_1GB_FULL;
+ *autoneg = true;
+ break;
+
+ case IXGBE_AUTOC_LMS_KX4_AN:
+ case IXGBE_AUTOC_LMS_KX4_AN_1G_AN:
+ *speed = IXGBE_LINK_SPEED_UNKNOWN;
+ if (autoc_reg & IXGBE_AUTOC_KX4_SUPP) {
+ *speed |= IXGBE_LINK_SPEED_10GB_FULL;
+ }
+ if (autoc_reg & IXGBE_AUTOC_KX_SUPP) {
+ *speed |= IXGBE_LINK_SPEED_1GB_FULL;
+ }
+ *autoneg = true;
+ break;
+
+ default:
+ status = IXGBE_ERR_LINK_SETUP;
+ break;
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_get_copper_link_settings_82598 - Determines default link settings
+ * @hw: pointer to hardware structure
+ * @speed: pointer to link speed
+ * @autoneg: boolean auto-negotiation value
+ *
+ * Determines the default link settings by reading the AUTOC register.
+ **/
+static s32 ixgbe_get_copper_link_settings_82598(struct ixgbe_hw *hw,
+ u32 *speed, bool *autoneg)
+{
+ s32 status = IXGBE_ERR_LINK_SETUP;
+ u16 speed_ability;
+
+ *speed = 0;
+ *autoneg = true;
+
+ status = ixgbe_read_phy_reg(hw, IXGBE_MDIO_PHY_SPEED_ABILITY,
+ IXGBE_MDIO_PMA_PMD_DEV_TYPE,
+ &speed_ability);
+
+ if (status == IXGBE_SUCCESS) {
+ if (speed_ability & IXGBE_MDIO_PHY_SPEED_10G)
+ *speed |= IXGBE_LINK_SPEED_10GB_FULL;
+ if (speed_ability & IXGBE_MDIO_PHY_SPEED_1G)
+ *speed |= IXGBE_LINK_SPEED_1GB_FULL;
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_get_media_type_82598 - Determines media type
+ * @hw: pointer to hardware structure
+ *
+ * Returns the media type (fiber, copper, backplane)
+ **/
+static enum ixgbe_media_type ixgbe_get_media_type_82598(struct ixgbe_hw *hw)
+{
+ enum ixgbe_media_type media_type;
+
+ /* Media type for I82598 is based on device ID */
+ switch (hw->device_id) {
+ case IXGBE_DEV_ID_82598:
+ /* Default device ID is mezzanine card KX/KX4 */
+ media_type = ixgbe_media_type_backplane;
+ break;
+ case IXGBE_DEV_ID_82598AF_DUAL_PORT:
+ case IXGBE_DEV_ID_82598AF_SINGLE_PORT:
+ media_type = ixgbe_media_type_fiber;
+ break;
+ default:
+ media_type = ixgbe_media_type_unknown;
+ break;
+ }
+
+ return media_type;
+}
+
+/**
+ * ixgbe_get_num_of_tx_queues_82598 - Get number of TX queues
+ * @hw: pointer to hardware structure
+ *
+ * Returns the number of transmit queues for the given adapter.
+ **/
+static u32 ixgbe_get_num_of_tx_queues_82598(struct ixgbe_hw *hw)
+{
+ return IXGBE_82598_MAX_TX_QUEUES;
+}
+
+/**
+ * ixgbe_get_num_of_rx_queues_82598 - Get number of RX queues
+ * @hw: pointer to hardware structure
+ *
+ * Returns the number of receive queues for the given adapter.
+ **/
+static u32 ixgbe_get_num_of_rx_queues_82598(struct ixgbe_hw *hw)
+{
+ return IXGBE_82598_MAX_RX_QUEUES;
+}
+
+/**
+ * ixgbe_setup_mac_link_82598 - Configures MAC link settings
+ * @hw: pointer to hardware structure
+ *
+ * Configures link settings based on values in the ixgbe_hw struct.
+ * Restarts the link. Performs autonegotiation if needed.
+ **/
+static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw)
+{
+ u32 autoc_reg;
+ u32 links_reg;
+ u32 i;
+ s32 status = IXGBE_SUCCESS;
+
+ autoc_reg = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+
+ if (hw->mac.link_settings_loaded) {
+ autoc_reg &= ~IXGBE_AUTOC_LMS_ATTACH_TYPE;
+ autoc_reg &= ~IXGBE_AUTOC_LMS_MASK;
+ autoc_reg |= hw->mac.link_attach_type;
+ autoc_reg |= hw->mac.link_mode_select;
+
+ IXGBE_WRITE_REG(hw, IXGBE_AUTOC, autoc_reg);
+ msleep(50);
+ }
+
+ /* Restart link */
+ autoc_reg |= IXGBE_AUTOC_AN_RESTART;
+ IXGBE_WRITE_REG(hw, IXGBE_AUTOC, autoc_reg);
+
+ /* Only poll for autoneg to complete if specified to do so */
+ if (hw->phy.autoneg_wait_to_complete) {
+ if (hw->mac.link_mode_select == IXGBE_AUTOC_LMS_KX4_AN ||
+ hw->mac.link_mode_select == IXGBE_AUTOC_LMS_KX4_AN_1G_AN) {
+ links_reg = 0; /* Just in case Autoneg time = 0 */
+ for (i = 0; i < IXGBE_AUTO_NEG_TIME; i++) {
+ links_reg = IXGBE_READ_REG(hw, IXGBE_LINKS);
+ if (links_reg & IXGBE_LINKS_KX_AN_COMP)
+ break;
+ msleep(100);
+ }
+ if (!(links_reg & IXGBE_LINKS_KX_AN_COMP)) {
+ status = IXGBE_ERR_AUTONEG_NOT_COMPLETE;
+ DEBUGOUT("Autonegotiation did not complete.\n");
+ }
+ }
+ }
+
+ /* Add delay to filter out noises during initial link setup */
+ msleep(50);
+
+ return status;
+}
+
+/**
+ * ixgbe_check_mac_link_82598 - Get link/speed status
+ * @hw: pointer to hardware structure
+ * @speed: pointer to link speed
+ * @link_up: true is link is up, false otherwise
+ *
+ * Reads the links register to determine if link is up and the current speed
+ **/
+static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw, u32 *speed,
+ bool *link_up)
+{
+ u32 links_reg;
+
+ links_reg = IXGBE_READ_REG(hw, IXGBE_LINKS);
+
+ if (links_reg & IXGBE_LINKS_UP)
+ *link_up = true;
+ else
+ *link_up = false;
+
+ if (links_reg & IXGBE_LINKS_SPEED)
+ *speed = IXGBE_LINK_SPEED_10GB_FULL;
+ else
+ *speed = IXGBE_LINK_SPEED_1GB_FULL;
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_setup_mac_link_speed_82598 - Set MAC link speed
+ * @hw: pointer to hardware structure
+ * @speed: new link speed
+ * @autoneg: true if auto-negotiation enabled
+ * @autoneg_wait_to_complete: true if waiting is needed to complete
+ *
+ * Set the link speed in the AUTOC register and restarts link.
+ **/
+static s32 ixgbe_setup_mac_link_speed_82598(struct ixgbe_hw *hw,
+ u32 speed, bool autoneg,
+ bool autoneg_wait_to_complete)
+{
+ s32 status = IXGBE_SUCCESS;
+
+ /* If speed is 10G, then check for CX4 or XAUI. */
+ if ((speed == IXGBE_LINK_SPEED_10GB_FULL) &&
+ (!(hw->mac.link_attach_type & IXGBE_AUTOC_10G_KX4)))
+ hw->mac.link_mode_select = IXGBE_AUTOC_LMS_10G_LINK_NO_AN;
+ else if ((speed == IXGBE_LINK_SPEED_1GB_FULL) && (!autoneg))
+ hw->mac.link_mode_select = IXGBE_AUTOC_LMS_1G_LINK_NO_AN;
+ else if (autoneg) {
+ /* BX mode - Autonegotiate 1G */
+ if (!(hw->mac.link_attach_type & IXGBE_AUTOC_1G_PMA_PMD))
+ hw->mac.link_mode_select = IXGBE_AUTOC_LMS_1G_AN;
+ else /* KX/KX4 mode */
+ hw->mac.link_mode_select = IXGBE_AUTOC_LMS_KX4_AN_1G_AN;
+ } else {
+ status = IXGBE_ERR_LINK_SETUP;
+ }
+
+ if (status == IXGBE_SUCCESS) {
+ hw->phy.autoneg_wait_to_complete = autoneg_wait_to_complete;
+
+ hw->mac.link_settings_loaded = true;
+ /*
+ * Setup and restart the link based on the new values in
+ * ixgbe_hw This will write the AUTOC register based on the new
+ * stored values
+ */
+ ixgbe_setup_mac_link_82598(hw);
+ }
+
+ return status;
+}
+
+
+/**
+ * ixgbe_setup_copper_link_82598 - Setup copper link settings
+ * @hw: pointer to hardware structure
+ *
+ * Configures link settings based on values in the ixgbe_hw struct.
+ * Restarts the link. Performs autonegotiation if needed. Restart
+ * phy and wait for autonegotiate to finish. Then synchronize the
+ * MAC and PHY.
+ **/
+static s32 ixgbe_setup_copper_link_82598(struct ixgbe_hw *hw)
+{
+ s32 status;
+ u32 speed = 0;
+ bool link_up = false;
+
+ /* Set up MAC */
+ ixgbe_setup_mac_link_82598(hw);
+
+ /* Restart autonegotiation on PHY */
+ status = ixgbe_setup_phy_link(hw);
+
+ /* Synchronize MAC to PHY speed */
+ if (status == IXGBE_SUCCESS)
+ status = ixgbe_check_link(hw, &speed, &link_up);
+
+ return status;
+}
+
+/**
+ * ixgbe_check_copper_link_82598 - Syncs MAC & PHY link settings
+ * @hw: pointer to hardware structure
+ * @speed: pointer to link speed
+ * @link_up: true if link is up, false otherwise
+ *
+ * Reads the mac link, phy link, and synchronizes the MAC to PHY.
+ **/
+static s32 ixgbe_check_copper_link_82598(struct ixgbe_hw *hw, u32 *speed,
+ bool *link_up)
+{
+ s32 status;
+ u32 phy_speed = 0;
+ bool phy_link = false;
+
+ /* This is the speed and link the MAC is set at */
+ ixgbe_check_mac_link_82598(hw, speed, link_up);
+
+ /*
+ * Check current speed and link status of the PHY register.
+ * This is a vendor specific register and may have to
+ * be changed for other copper PHYs.
+ */
+ status = ixgbe_check_phy_link(hw, &phy_speed, &phy_link);
+
+ if ((status == IXGBE_SUCCESS) && (phy_link)) {
+ /*
+ * Check current link status of the MACs link's register
+ * matches that of the speed in the PHY register
+ */
+ if (*speed != phy_speed) {
+ /*
+ * The copper PHY requires 82598 attach type to be XAUI
+ * for 10G and BX for 1G
+ */
+ hw->mac.link_attach_type =
+ (IXGBE_AUTOC_10G_XAUI | IXGBE_AUTOC_1G_BX);
+
+ /* Synchronize the MAC speed to the PHY speed */
+ status = ixgbe_setup_mac_link_speed_82598(hw, phy_speed,
+ false, false);
+ if (status == IXGBE_SUCCESS)
+ ixgbe_check_mac_link_82598(hw, speed, link_up);
+ else
+ status = IXGBE_ERR_LINK_SETUP;
+ }
+ } else {
+ *link_up = phy_link;
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_setup_copper_link_speed_82598 - Set the PHY autoneg advertised field
+ * @hw: pointer to hardware structure
+ * @speed: new link speed
+ * @autoneg: true if autonegotiation enabled
+ * @autoneg_wait_to_complete: true if waiting is needed to complete
+ *
+ * Sets the link speed in the AUTOC register in the MAC and restarts link.
+ **/
+static s32 ixgbe_setup_copper_link_speed_82598(struct ixgbe_hw *hw, u32 speed,
+ bool autoneg,
+ bool autoneg_wait_to_complete)
+{
+ s32 status;
+ bool link_up = 0;
+
+ /* Setup the PHY according to input speed */
+ status = ixgbe_setup_phy_link_speed(hw, speed, autoneg,
+ autoneg_wait_to_complete);
+
+ /* Synchronize MAC to PHY speed */
+ if (status == IXGBE_SUCCESS)
+ status = ixgbe_check_link(hw, &speed, &link_up);
+
+ return status;
+}
+
+/**
+ * ixgbe_reset_hw_82598 - Performs hardware reset
+ * @hw: pointer to hardware structure
+ *
+ * Resets the hardware by reseting the transmit and receive units, masks and
+ * clears all interrupts, performing a PHY reset, and performing a link (MAC)
+ * reset.
+ **/
+static s32 ixgbe_reset_hw_82598(struct ixgbe_hw *hw)
+{
+ s32 status = IXGBE_SUCCESS;
+ u32 ctrl;
+ u32 gheccr;
+ u32 i;
+ u32 autoc;
+
+ /* Call adapter stop to disable tx/rx and clear interrupts */
+ ixgbe_stop_adapter(hw);
+
+ /* Reset PHY */
+ ixgbe_reset_phy(hw);
+
+ /*
+ * Prevent the PCI-E bus from from hanging by disabling PCI-E master
+ * access and verify no pending requests before reset
+ */
+ if (ixgbe_disable_pcie_master(hw) != IXGBE_SUCCESS) {
+ status = IXGBE_ERR_MASTER_REQUESTS_PENDING;
+ DEBUGOUT("PCI-E Master disable polling has failed.\n");
+ }
+
+ /*
+ * Issue global reset to the MAC. This needs to be a SW reset.
+ * If link reset is used, it might reset the MAC when mng is using it
+ */
+ ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
+ IXGBE_WRITE_REG(hw, IXGBE_CTRL, (ctrl | IXGBE_CTRL_RST));
+ IXGBE_WRITE_FLUSH(hw);
+
+ /* Poll for reset bit to self-clear indicating reset is complete */
+ for (i = 0; i < 10; i++) {
+ udelay(1);
+ ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
+ if (!(ctrl & IXGBE_CTRL_RST))
+ break;
+ }
+ if (ctrl & IXGBE_CTRL_RST) {
+ status = IXGBE_ERR_RESET_FAILED;
+ DEBUGOUT("Reset polling failed to complete.\n");
+ }
+
+ msleep(50);
+
+ gheccr = IXGBE_READ_REG(hw, IXGBE_GHECCR);
+ gheccr &= ~((1 << 21) | (1 << 18) | (1 << 9) | (1 << 6));
+ IXGBE_WRITE_REG(hw, IXGBE_GHECCR, gheccr);
+
+ /*
+ * AUTOC register which stores link settings gets cleared
+ * and reloaded from EEPROM after reset. We need to restore
+ * our stored value from init in case SW changed the attach
+ * type or speed. If this is the first time and link settings
+ * have not been stored, store default settings from AUTOC.
+ */
+ autoc = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+ if (hw->mac.link_settings_loaded) {
+ autoc &= ~(IXGBE_AUTOC_LMS_ATTACH_TYPE);
+ autoc &= ~(IXGBE_AUTOC_LMS_MASK);
+ autoc |= hw->mac.link_attach_type;
+ autoc |= hw->mac.link_mode_select;
+ IXGBE_WRITE_REG(hw, IXGBE_AUTOC, autoc);
+ } else {
+ hw->mac.link_attach_type =
+ (autoc & IXGBE_AUTOC_LMS_ATTACH_TYPE);
+ hw->mac.link_mode_select = (autoc & IXGBE_AUTOC_LMS_MASK);
+ hw->mac.link_settings_loaded = true;
+ }
+
+ /* Store the permanent mac address */
+ ixgbe_get_mac_addr(hw, hw->mac.perm_addr);
+
+ return status;
+}
+
+/**
+ * ixgbe_get_num_rx_addrs_82598 - Get RX address registers
+ * @hw: pointer to hardware structure
+ *
+ * Returns the of RAR entries for the given adapter.
+ **/
+static u32 ixgbe_get_num_rx_addrs_82598(struct ixgbe_hw *hw)
+{
+ return IXGBE_82598_RAR_ENTRIES;
+}
+
diff --git a/drivers/net/ixgbe/ixgbe_api.c b/drivers/net/ixgbe/ixgbe_api.c
new file mode 100644
index 0000000..0694fdf
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_api.c
@@ -0,0 +1,553 @@
+/*******************************************************************************
+
+ Intel PRO/10GbE PCI Express Linux driver
+ Copyright(c) 1999 - 2007 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include "ixgbe_api.h"
+#include "ixgbe_common.h"
+
+extern s32 ixgbe_init_shared_code_82598(struct ixgbe_hw *hw);
+extern s32 ixgbe_init_shared_code_phy(struct ixgbe_hw *hw);
+
+/**
+ * ixgbe_init_shared_code - Initialize the shared code
+ * @hw: pointer to hardware structure
+ *
+ * This will assign function pointers and assign the MAC type and PHY code.
+ * Does not touch the hardware. This function must be called prior to any
+ * other function in the shared code. The ixgbe_hw structure should be
+ * memset to 0 prior to calling this function. The following fields in
+ * hw structure should be filled in prior to calling this function:
+ * hw_addr, back, device_id, vendor_id, subsystem_device_id,
+ * subsystem_vendor_id, and revision_id
+ **/
+s32 ixgbe_init_shared_code(struct ixgbe_hw *hw)
+{
+ s32 status = IXGBE_ERR_DEVICE_NOT_SUPPORTED;
+
+ /*
+ * Assign generic function pointers before entering adapter-specific
+ * init
+ */
+ ixgbe_assign_func_pointers_generic(hw);
+
+ if (hw->vendor_id == IXGBE_INTEL_VENDOR_ID) {
+ switch (hw->device_id) {
+ case IXGBE_DEV_ID_82598:
+ case IXGBE_DEV_ID_82598AF_SINGLE_PORT:
+ case IXGBE_DEV_ID_82598AF_DUAL_PORT:
+ status = ixgbe_init_shared_code_82598(hw);
+ status = ixgbe_init_shared_code_phy(hw);
+ break;
+ default:
+ status = IXGBE_ERR_DEVICE_NOT_SUPPORTED;
+ break;
+ }
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_init_hw - Initialize the hardware
+ * @hw: pointer to hardware structure
+ *
+ * Initialize the hardware by resetting and then starting the hardware
+ **/
+s32 ixgbe_init_hw(struct ixgbe_hw *hw)
+{
+ return ixgbe_call_func(hw, ixgbe_func_init_hw, (hw),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_reset_hw - Performs a hardware reset
+ * @hw: pointer to hardware structure
+ *
+ * Resets the hardware by resetting the transmit and receive units, masks and
+ * clears all interrupts, performs a PHY reset, and performs a MAC reset
+ **/
+s32 ixgbe_reset_hw(struct ixgbe_hw *hw)
+{
+ return ixgbe_call_func(hw, ixgbe_func_reset_hw, (hw),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_start_hw - Prepares hardware for TX/TX
+ * @hw: pointer to hardware structure
+ *
+ * Starts the hardware by filling the bus info structure and media type,
+ * clears all on chip counters, initializes receive address registers,
+ * multicast table, VLAN filter table, calls routine to setup link and
+ * flow control settings, and leaves transmit and receive units disabled
+ * and uninitialized.
+ **/
+s32 ixgbe_start_hw(struct ixgbe_hw *hw)
+{
+ return ixgbe_call_func(hw, ixgbe_func_start_hw, (hw),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_clear_hw_cntrs - Clear hardware counters
+ * @hw: pointer to hardware structure
+ *
+ * Clears all hardware statistics counters by reading them from the hardware
+ * Statistics counters are clear on read.
+ **/
+s32 ixgbe_clear_hw_cntrs(struct ixgbe_hw *hw)
+{
+ return ixgbe_call_func(hw, ixgbe_func_clear_hw_cntrs, (hw),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_get_media_type - Get media type
+ * @hw: pointer to hardware structure
+ *
+ * Returns the media type (fiber, copper, backplane)
+ **/
+enum ixgbe_media_type ixgbe_get_media_type(struct ixgbe_hw *hw)
+{
+ return ixgbe_call_func(hw, ixgbe_func_get_media_type, (hw),
+ ixgbe_media_type_unknown);
+}
+
+/**
+ * ixgbe_get_mac_addr - Get MAC address
+ * @hw: pointer to hardware structure
+ * @mac_addr: Adapter MAC address
+ *
+ * Reads the adapter's MAC address from the first Receive Address Register
+ * (RAR0) A reset of the adapter must have been performed prior to calling this
+ * function in order for the MAC address to have been loaded from the EEPROM
+ * into RAR0
+ **/
+s32 ixgbe_get_mac_addr(struct ixgbe_hw *hw, u8 *mac_addr)
+{
+ return ixgbe_call_func(hw, ixgbe_func_get_mac_addr,
+ (hw, mac_addr), IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_get_bus_info - Set PCI bus info
+ * @hw: pointer to hardware structure
+ *
+ * Sets the PCI bus info (speed, width, type) within the ixgbe_hw structure
+ **/
+s32 ixgbe_get_bus_info(struct ixgbe_hw *hw)
+{
+ return ixgbe_call_func(hw, ixgbe_func_get_bus_info, (hw),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_get_num_of_tx_queues - Get TX queues
+ * @hw: pointer to hardware structure
+ *
+ * Returns the number of transmit queues for the given adapter.
+ **/
+u32 ixgbe_get_num_of_tx_queues(struct ixgbe_hw *hw)
+{
+ return ixgbe_call_func(hw, ixgbe_func_get_num_of_tx_queues,
+ (hw), 0);
+}
+
+/**
+ * ixgbe_stop_adapter - Disable TX/TX units
+ * @hw: pointer to hardware structure
+ *
+ * Sets the adapter_stopped flag within ixgbe_hw struct. Clears interrupts,
+ * disables transmit and receive units. The adapter_stopped flag is used by
+ * the shared code and drivers to determine if the adapter is in a stopped
+ * state and should not touch the hardware.
+ **/
+s32 ixgbe_stop_adapter(struct ixgbe_hw *hw)
+{
+ return ixgbe_call_func(hw, ixgbe_func_stop_adapter, (hw),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_identify_phy - Get PHY type
+ * @hw: pointer to hardware structure
+ *
+ * Determines the physical layer module found on the current adapter.
+ **/
+s32 ixgbe_identify_phy(struct ixgbe_hw *hw)
+{
+ s32 status = IXGBE_SUCCESS;
+
+ if (hw->phy.type == ixgbe_phy_unknown) {
+ status = ixgbe_call_func(hw,
+ ixgbe_func_identify_phy,
+ (hw),
+ IXGBE_NOT_IMPLEMENTED);
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_reset_phy - Perform a PHY reset
+ * @hw: pointer to hardware structure
+ **/
+s32 ixgbe_reset_phy(struct ixgbe_hw *hw)
+{
+ s32 status = IXGBE_SUCCESS;
+
+ if (hw->phy.type == ixgbe_phy_unknown) {
+ if (ixgbe_identify_phy(hw) != IXGBE_SUCCESS) {
+ status = IXGBE_ERR_PHY;
+ }
+ }
+
+ if (status == IXGBE_SUCCESS) {
+ status = ixgbe_call_func(hw,
+ ixgbe_func_reset_phy,
+ (hw),
+ IXGBE_NOT_IMPLEMENTED);
+ }
+ return status;
+}
+
+/**
+ * ixgbe_read_phy_reg - Read PHY register
+ * @hw: pointer to hardware structure
+ * @reg_addr: 32 bit address of PHY register to read
+ * @phy_data: Pointer to read data from PHY register
+ *
+ * Reads a value from a specified PHY register
+ **/
+s32 ixgbe_read_phy_reg(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type,
+ u16 *phy_data)
+{
+ s32 status = IXGBE_SUCCESS;
+
+ if (hw->phy.type == ixgbe_phy_unknown) {
+ if (ixgbe_identify_phy(hw) != IXGBE_SUCCESS) {
+ status = IXGBE_ERR_PHY;
+ }
+ }
+
+ if (status == IXGBE_SUCCESS) {
+ status = ixgbe_call_func(hw,
+ ixgbe_func_read_phy_reg,
+ (hw, reg_addr, device_type, phy_data),
+ IXGBE_NOT_IMPLEMENTED);
+ }
+ return status;
+}
+
+/**
+ * ixgbe_write_phy_reg - Write PHY register
+ * @hw: pointer to hardware structure
+ * @reg_addr: 32 bit PHY register to write
+ * @phy_data: Data to write to the PHY register
+ *
+ * Writes a value to specified PHY register
+ **/
+s32 ixgbe_write_phy_reg(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type,
+ u16 phy_data)
+{
+ s32 status = IXGBE_SUCCESS;
+
+ if (hw->phy.type == ixgbe_phy_unknown) {
+ if (ixgbe_identify_phy(hw) != IXGBE_SUCCESS) {
+ status = IXGBE_ERR_PHY;
+ }
+ }
+
+ if (status == IXGBE_SUCCESS) {
+ status = ixgbe_call_func(hw,
+ ixgbe_func_write_phy_reg,
+ (hw, reg_addr, device_type, phy_data),
+ IXGBE_NOT_IMPLEMENTED);
+ }
+ return status;
+}
+
+/**
+ * ixgbe_setup_link - Configure link settings
+ * @hw: pointer to hardware structure
+ *
+ * Configures link settings based on values in the ixgbe_hw struct.
+ * Restarts the link. Performs autonegotiation if needed.
+ **/
+s32 ixgbe_setup_link(struct ixgbe_hw *hw)
+{
+ return ixgbe_call_func(hw, ixgbe_func_setup_link, (hw),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_check_link - Get link and speed status
+ * @hw: pointer to hardware structure
+ *
+ * Reads the links register to determine if link is up and the current speed
+ **/
+s32 ixgbe_check_link(struct ixgbe_hw *hw, u32 *speed,
+ bool *link_up)
+{
+ return ixgbe_call_func(hw, ixgbe_func_check_link, (hw, speed, link_up),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_setup_link_speed - Set link speed
+ * @hw: pointer to hardware structure
+ * @speed: new link speed
+ * @autoneg: true if autonegotiation enabled
+ *
+ * Set the link speed and restarts the link.
+ **/
+s32 ixgbe_setup_link_speed(struct ixgbe_hw *hw, u32 speed,
+ bool autoneg,
+ bool autoneg_wait_to_complete)
+{
+ return ixgbe_call_func(hw, ixgbe_func_setup_link_speed, (hw, speed,
+ autoneg, autoneg_wait_to_complete),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_led_on - Turn on LED's
+ * @hw: pointer to hardware structure
+ * @index: led number to turn on
+ *
+ * Turns on the software controllable LEDs.
+ **/
+s32 ixgbe_led_on(struct ixgbe_hw *hw, u32 index)
+{
+ return ixgbe_call_func(hw, ixgbe_func_led_on, (hw, index),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_led_off - Turn off LED's
+ * @hw: pointer to hardware structure
+ * @index: led number to turn off
+ *
+ * Turns off the software controllable LEDs.
+ **/
+s32 ixgbe_led_off(struct ixgbe_hw *hw, u32 index)
+{
+ return ixgbe_call_func(hw, ixgbe_func_led_off, (hw, index),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_init_eeprom_params - Initialiaze EEPROM parameters
+ * @hw: pointer to hardware structure
+ *
+ * Initializes the EEPROM parameters ixgbe_eeprom_info within the
+ * ixgbe_hw struct in order to set up EEPROM access.
+ **/
+s32 ixgbe_init_eeprom_params(struct ixgbe_hw *hw)
+{
+ return ixgbe_call_func(hw, ixgbe_func_init_eeprom_params, (hw),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+
+/**
+ * ixgbe_write_eeprom - Write word to EEPROM
+ * @hw: pointer to hardware structure
+ * @offset: offset within the EEPROM to be written to
+ * @data: 16 bit word to be written to the EEPROM
+ *
+ * Writes 16 bit value to EEPROM. If ixgbe_eeprom_update_checksum is not
+ * called after this function, the EEPROM will most likely contain an
+ * invalid checksum.
+ **/
+s32 ixgbe_write_eeprom(struct ixgbe_hw *hw, u16 offset, u16 data)
+{
+ s32 status;
+
+ /*
+ * Initialize EEPROM parameters. This will not do anything if the
+ * EEPROM structure has already been initialized
+ */
+ ixgbe_init_eeprom_params(hw);
+
+ /* Check for invalid offset */
+ if (offset >= hw->eeprom.word_size) {
+ status = IXGBE_ERR_EEPROM;
+ } else {
+ status = ixgbe_call_func(hw,
+ ixgbe_func_write_eeprom,
+ (hw, offset, data),
+ IXGBE_NOT_IMPLEMENTED);
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_read_eeprom - Read word from EEPROM
+ * @hw: pointer to hardware structure
+ * @offset: offset within the EEPROM to be read
+ * @data: read 16 bit value from EEPROM
+ *
+ * Reads 16 bit value from EEPROM
+ **/
+s32 ixgbe_read_eeprom(struct ixgbe_hw *hw, u16 offset, u16 *data)
+{
+ s32 status;
+
+ /*
+ * Initialize EEPROM parameters. This will not do anything if the
+ * EEPROM structure has already been initialized
+ */
+ ixgbe_init_eeprom_params(hw);
+
+ /* Check for invalid offset */
+ if (offset >= hw->eeprom.word_size) {
+ status = IXGBE_ERR_EEPROM;
+ } else {
+ status = ixgbe_call_func(hw,
+ ixgbe_func_read_eeprom,
+ (hw, offset, data),
+ IXGBE_NOT_IMPLEMENTED);
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_validate_eeprom_checksum - Validate EEPROM checksum
+ * @hw: pointer to hardware structure
+ * @checksum_val: calculated checksum
+ *
+ * Performs checksum calculation and validates the EEPROM checksum
+ **/
+s32 ixgbe_validate_eeprom_checksum(struct ixgbe_hw *hw, u16 *checksum_val)
+{
+ return ixgbe_call_func(hw, ixgbe_func_validate_eeprom_checksum,
+ (hw, checksum_val), IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_set_rar - Set RX address register
+ * @hw: pointer to hardware structure
+ * @addr: Address to put into receive address register
+ * @index: Receive address register to write
+ * @vind: Vind to set RAR to
+ * @enable_addr: set flag that address is active
+ *
+ * Puts an ethernet address into a receive address register.
+ **/
+s32 ixgbe_set_rar(struct ixgbe_hw *hw, u32 index, u8 *addr, u32 vind,
+ u32 enable_addr)
+{
+ return ixgbe_call_func(hw, ixgbe_func_set_rar, (hw, index, addr, vind,
+ enable_addr), IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_init_rx_addrs - Initializes receive address filters.
+ * @hw: pointer to hardware structure
+ *
+ * Places the MAC address in receive address register 0 and clears the rest
+ * of the receive addresss registers. Clears the multicast table. Assumes
+ * the receiver is in reset when the routine is called.
+ **/
+s32 ixgbe_init_rx_addrs(struct ixgbe_hw *hw)
+{
+ return ixgbe_call_func(hw, ixgbe_func_init_rx_addrs, (hw),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_get_num_rx_addrs - Returns the number of RAR entries.
+ * @hw: pointer to hardware structure
+ **/
+u32 ixgbe_get_num_rx_addrs(struct ixgbe_hw *hw)
+{
+ return ixgbe_call_func(hw, ixgbe_func_get_num_rx_addrs, (hw), 0);
+}
+
+/**
+ * ixgbe_update_mc_addr_list - Updates the MAC's list of multicast addresses
+ * @hw: pointer to hardware structure
+ * @mc_addr_list: the list of new multicast addresses
+ * @mc_addr_count: number of addresses
+ * @pad: number of bytes between addresses in the list
+ *
+ * The given list replaces any existing list. Clears the MC addrs from receive
+ * address registers and the multicast table. Uses unsed receive address
+ * registers for the first multicast addresses, and hashes the rest into the
+ * multicast table.
+ **/
+s32 ixgbe_update_mc_addr_list(struct ixgbe_hw *hw, u8 *mc_addr_list,
+ u32 mc_addr_count, u32 pad)
+{
+ return ixgbe_call_func(hw, ixgbe_func_update_mc_addr_list, (hw,
+ mc_addr_list, mc_addr_count, pad),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_clear_vfta - Clear VLAN filter table
+ * @hw: pointer to hardware structure
+ *
+ * Clears the VLAN filer table, and the VMDq index associated with the filter
+ **/
+s32 ixgbe_clear_vfta(struct ixgbe_hw *hw)
+{
+ return ixgbe_call_func(hw, ixgbe_func_clear_vfta, (hw),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_set_vfta - Set VLAN filter table
+ * @hw: pointer to hardware structure
+ * @vlan: VLAN id to write to VLAN filter
+ * @vind: VMDq output index that maps queue to VLAN id in VFTA
+ * @vlan_on: boolean flag to turn on/off VLAN in VFTA
+ *
+ * Turn on/off specified VLAN in the VLAN filter table.
+ **/
+s32 ixgbe_set_vfta(struct ixgbe_hw *hw, u32 vlan, u32 vind, bool vlan_on)
+{
+ return ixgbe_call_func(hw, ixgbe_func_set_vfta, (hw, vlan, vind,
+ vlan_on), IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_setup_fc - Set flow control
+ * @hw: pointer to hardware structure
+ * @packetbuf_num: packet buffer number (0-7)
+ *
+ * Configures the flow control settings based on SW configuration.
+ **/
+s32 ixgbe_setup_fc(struct ixgbe_hw *hw, s32 packetbuf_num)
+{
+ return ixgbe_call_func(hw, ixgbe_func_setup_fc, (hw, packetbuf_num),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
diff --git a/drivers/net/ixgbe/ixgbe_api.h b/drivers/net/ixgbe/ixgbe_api.h
new file mode 100644
index 0000000..ddd9b67
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_api.h
@@ -0,0 +1,80 @@
+/*******************************************************************************
+
+ Intel PRO/10GbE PCI Express Linux driver
+ Copyright(c) 1999 - 2007 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#ifndef _IXGBE_API_H_
+#define _IXGBE_API_H_
+
+#include "ixgbe_type.h"
+
+s32 ixgbe_init_shared_code(struct ixgbe_hw *hw);
+
+s32 ixgbe_init_hw(struct ixgbe_hw *hw);
+s32 ixgbe_reset_hw(struct ixgbe_hw *hw);
+s32 ixgbe_start_hw(struct ixgbe_hw *hw);
+s32 ixgbe_clear_hw_cntrs(struct ixgbe_hw *hw);
+enum ixgbe_media_type ixgbe_get_media_type(struct ixgbe_hw *hw);
+s32 ixgbe_get_mac_addr(struct ixgbe_hw *hw, u8 *mac_addr);
+s32 ixgbe_get_bus_info(struct ixgbe_hw *hw);
+u32 ixgbe_get_num_of_tx_queues(struct ixgbe_hw *hw);
+s32 ixgbe_stop_adapter(struct ixgbe_hw *hw);
+
+s32 ixgbe_identify_phy(struct ixgbe_hw *hw);
+s32 ixgbe_reset_phy(struct ixgbe_hw *hw);
+s32 ixgbe_read_phy_reg(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type,
+ u16 *phy_data);
+s32 ixgbe_write_phy_reg(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type,
+ u16 phy_data);
+
+s32 ixgbe_setup_link(struct ixgbe_hw *hw);
+s32 ixgbe_setup_link_speed(struct ixgbe_hw *hw, u32 speed,
+ bool autoneg, bool autoneg_wait_to_complete);
+s32 ixgbe_check_link(struct ixgbe_hw *hw, u32 *speed,
+ bool *link_up);
+s32 ixgbe_led_on(struct ixgbe_hw *hw, u32 index);
+s32 ixgbe_led_off(struct ixgbe_hw *hw, u32 index);
+
+s32 ixgbe_init_eeprom_params(struct ixgbe_hw *hw);
+s32 ixgbe_write_eeprom(struct ixgbe_hw *hw, u16 offset, u16 data);
+s32 ixgbe_read_eeprom(struct ixgbe_hw *hw, u16 offset, u16 *data);
+s32 ixgbe_validate_eeprom_checksum(struct ixgbe_hw *hw, u16 *checksum_val);
+
+s32 ixgbe_set_rar(struct ixgbe_hw *hw, u32 index, u8 *addr,
+ u32 vind, u32 enable_addr);
+s32 ixgbe_init_rx_addrs(struct ixgbe_hw *hw);
+u32 ixgbe_get_num_rx_addrs(struct ixgbe_hw *hw);
+s32 ixgbe_update_mc_addr_list(struct ixgbe_hw *hw, u8 *mc_addr_list,
+ u32 mc_addr_count, u32 pad);
+s32 ixgbe_clear_vfta(struct ixgbe_hw *hw);
+s32 ixgbe_set_vfta(struct ixgbe_hw *hw, u32 vlan,
+ u32 vind, bool vlan_on);
+
+s32 ixgbe_setup_fc(struct ixgbe_hw *hw, s32 packetbuf_num);
+
+s32 ixgbe_validate_mac_addr(u8 *mac_addr);
+
+#endif /* _IXGBE_API_H_ */
diff --git a/drivers/net/ixgbe/ixgbe_common.c b/drivers/net/ixgbe/ixgbe_common.c
new file mode 100644
index 0000000..d882b4a
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_common.c
@@ -0,0 +1,1679 @@
+/*******************************************************************************
+
+ Intel PRO/10GbE PCI Express Linux driver
+ Copyright(c) 1999 - 2007 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include "ixgbe_common.h"
+#include "ixgbe_api.h"
+
+static s32 ixgbe_acquire_eeprom(struct ixgbe_hw *hw);
+static s32 ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw);
+static void ixgbe_release_eeprom_semaphore(struct ixgbe_hw *hw);
+static s32 ixgbe_ready_eeprom(struct ixgbe_hw *hw);
+static void ixgbe_standby_eeprom(struct ixgbe_hw *hw);
+static void ixgbe_shift_out_eeprom_bits(struct ixgbe_hw *hw, u16 data,
+ u16 count);
+static u16 ixgbe_shift_in_eeprom_bits(struct ixgbe_hw *hw, u16 count);
+static void ixgbe_raise_eeprom_clk(struct ixgbe_hw *hw, u32 *eec);
+static void ixgbe_lower_eeprom_clk(struct ixgbe_hw *hw, u32 *eec);
+static void ixgbe_release_eeprom(struct ixgbe_hw *hw);
+static u16 ixgbe_calc_eeprom_checksum(struct ixgbe_hw *hw);
+
+static void ixgbe_enable_rar(struct ixgbe_hw *hw, u32 index);
+static void ixgbe_disable_rar(struct ixgbe_hw *hw, u32 index);
+static s32 ixgbe_mta_vector(struct ixgbe_hw *hw, u8 *mc_addr);
+static void ixgbe_add_mc_addr(struct ixgbe_hw *hw, u8 *mc_addr);
+
+static s32 ixgbe_init_hw_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_get_mac_addr_generic(struct ixgbe_hw *hw, u8 *mac_addr);
+static s32 ixgbe_get_bus_info_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_stop_adapter_generic(struct ixgbe_hw *hw);
+
+static s32 ixgbe_led_on_generic(struct ixgbe_hw *hw, u32 index);
+static s32 ixgbe_led_off_generic(struct ixgbe_hw *hw, u32 index);
+
+static s32 ixgbe_init_eeprom_params_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_write_eeprom_generic(struct ixgbe_hw *hw, u16 offset,
+ u16 data);
+static s32 ixgbe_read_eeprom_bit_bang_generic(struct ixgbe_hw *hw, u16 offset,
+ u16 *data);
+static s32 ixgbe_validate_eeprom_checksum_generic(struct ixgbe_hw *hw,
+ u16 *checksum_val);
+static s32 ixgbe_update_eeprom_checksum_generic(struct ixgbe_hw *hw);
+
+static s32 ixgbe_set_rar_generic(struct ixgbe_hw *hw, u32 index, u8 *addr,
+ u32 vind, u32 enable_addr);
+static s32 ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw,
+ u8 *mc_addr_list,
+ u32 mc_addr_count, u32 pad);
+static s32 ixgbe_enable_mc_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_disable_mc_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_clear_vfta_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan,
+ u32 vind, bool vlan_on);
+
+static s32 ixgbe_setup_fc_generic(struct ixgbe_hw *hw, s32 packtetbuf_num);
+static void ixgbe_set_mta(struct ixgbe_hw *hw, u8 *mc_addr);
+/**
+ * ixgbe_assign_func_pointers_generic - Set generic func ptrs
+ * @hw: pointer to hardware structure
+ *
+ * Assigns generic function pointers. Adapter-specific functions can
+ * override the assignment of generic function pointers by assigning
+ * their own adapter-specific function pointers.
+ **/
+s32 ixgbe_assign_func_pointers_generic(struct ixgbe_hw *hw)
+{
+ struct ixgbe_functions *f = &hw->func;
+
+ f->ixgbe_func_init_hw = &ixgbe_init_hw_generic;
+ f->ixgbe_func_start_hw = &ixgbe_start_hw_generic;
+ f->ixgbe_func_clear_hw_cntrs = &ixgbe_clear_hw_cntrs_generic;
+ f->ixgbe_func_get_mac_addr = &ixgbe_get_mac_addr_generic;
+ f->ixgbe_func_stop_adapter = &ixgbe_stop_adapter_generic;
+ f->ixgbe_func_get_bus_info = &ixgbe_get_bus_info_generic;
+ /* LED */
+ f->ixgbe_func_led_on = &ixgbe_led_on_generic;
+ f->ixgbe_func_led_off = &ixgbe_led_off_generic;
+ /* EEPROM */
+ f->ixgbe_func_init_eeprom_params = &ixgbe_init_eeprom_params_generic;
+ f->ixgbe_func_read_eeprom = &ixgbe_read_eeprom_bit_bang_generic;
+ f->ixgbe_func_write_eeprom = &ixgbe_write_eeprom_generic;
+ f->ixgbe_func_validate_eeprom_checksum =
+ &ixgbe_validate_eeprom_checksum_generic;
+ f->ixgbe_func_update_eeprom_checksum =
+ &ixgbe_update_eeprom_checksum_generic;
+ /* RAR, Multicast, VLAN */
+ f->ixgbe_func_set_rar = &ixgbe_set_rar_generic;
+ f->ixgbe_func_init_rx_addrs = &ixgbe_init_rx_addrs_generic;
+ f->ixgbe_func_update_mc_addr_list = &ixgbe_update_mc_addr_list_generic;
+ f->ixgbe_func_enable_mc = &ixgbe_enable_mc_generic;
+ f->ixgbe_func_disable_mc = &ixgbe_disable_mc_generic;
+ f->ixgbe_func_clear_vfta = &ixgbe_clear_vfta_generic;
+ f->ixgbe_func_set_vfta = &ixgbe_set_vfta_generic;
+ f->ixgbe_func_setup_fc = &ixgbe_setup_fc_generic;
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_start_hw_generic - Prepare hardware for TX/RX
+ * @hw: pointer to hardware structure
+ *
+ * Starts the hardware by filling the bus info structure and media type, clears
+ * all on chip counters, initializes receive address registers, multicast
+ * table, VLAN filter table, calls routine to set up link and flow control
+ * settings, and leaves transmit and receive units disabled and uninitialized
+ **/
+static s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw)
+{
+ u32 ctrl_ext;
+
+ /* Set the media type */
+ hw->phy.media_type = ixgbe_get_media_type(hw);
+
+ /* Set bus info */
+ ixgbe_get_bus_info(hw);
+
+ /* Identify the PHY */
+ ixgbe_identify_phy(hw);
+
+ /*
+ * Store MAC address from RAR0, clear receive address registers, and
+ * clear the multicast table
+ */
+ ixgbe_init_rx_addrs(hw);
+
+ /* Clear the VLAN filter table */
+ ixgbe_clear_vfta(hw);
+
+ /* Set up link */
+ ixgbe_setup_link(hw);
+
+ /* Clear statistics registers */
+ ixgbe_clear_hw_cntrs(hw);
+
+ /* Set up flow control */
+ ixgbe_setup_fc(hw, 0);
+
+ /* Set No Snoop Disable */
+ ctrl_ext = IXGBE_READ_REG(hw, IXGBE_CTRL_EXT);
+ ctrl_ext |= IXGBE_CTRL_EXT_NS_DIS;
+ IXGBE_WRITE_REG(hw, IXGBE_CTRL_EXT, ctrl_ext);
+
+ /* Clear adapter stopped flag */
+ hw->adapter_stopped = false;
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_init_hw_generic - Generic hardware initialization
+ * @hw: pointer to hardware structure
+ *
+ * Initialize the hardware by reseting the hardware, filling the bus info
+ * structure and media type, clears all on chip counters, initializes receive
+ * address registers, multicast table, VLAN filter table, calls routine to set
+ * up link and flow control settings, and leaves transmit and receive units
+ * disabled and uninitialized
+ **/
+static s32 ixgbe_init_hw_generic(struct ixgbe_hw *hw)
+{
+ /* Reset the hardware */
+ ixgbe_reset_hw(hw);
+
+ /* Start the HW */
+ ixgbe_start_hw(hw);
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_clear_hw_cntrs_generic - Generic clear hardware counters
+ * @hw: pointer to hardware structure
+ *
+ * Clears all hardware statistics counters by reading them from the hardware
+ * Statistics counters are clear on read.
+ **/
+static s32 ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw)
+{
+ u16 i = 0;
+
+ IXGBE_READ_REG(hw, IXGBE_CRCERRS);
+ IXGBE_READ_REG(hw, IXGBE_ILLERRC);
+ IXGBE_READ_REG(hw, IXGBE_ERRBC);
+ IXGBE_READ_REG(hw, IXGBE_MSPDC);
+ for (i = 0; i < 8; i++)
+ IXGBE_READ_REG(hw, IXGBE_MPC(i));
+
+ IXGBE_READ_REG(hw, IXGBE_MLFC);
+ IXGBE_READ_REG(hw, IXGBE_MRFC);
+ IXGBE_READ_REG(hw, IXGBE_RLEC);
+ IXGBE_READ_REG(hw, IXGBE_LXONTXC);
+ IXGBE_READ_REG(hw, IXGBE_LXONRXC);
+ IXGBE_READ_REG(hw, IXGBE_LXOFFTXC);
+ IXGBE_READ_REG(hw, IXGBE_LXOFFRXC);
+
+ for (i = 0; i < 8; i++) {
+ IXGBE_READ_REG(hw, IXGBE_PXONTXC(i));
+ IXGBE_READ_REG(hw, IXGBE_PXONRXC(i));
+ IXGBE_READ_REG(hw, IXGBE_PXOFFTXC(i));
+ IXGBE_READ_REG(hw, IXGBE_PXOFFRXC(i));
+ }
+
+ IXGBE_READ_REG(hw, IXGBE_PRC64);
+ IXGBE_READ_REG(hw, IXGBE_PRC127);
+ IXGBE_READ_REG(hw, IXGBE_PRC255);
+ IXGBE_READ_REG(hw, IXGBE_PRC511);
+ IXGBE_READ_REG(hw, IXGBE_PRC1023);
+ IXGBE_READ_REG(hw, IXGBE_PRC1522);
+ IXGBE_READ_REG(hw, IXGBE_GPRC);
+ IXGBE_READ_REG(hw, IXGBE_BPRC);
+ IXGBE_READ_REG(hw, IXGBE_MPRC);
+ IXGBE_READ_REG(hw, IXGBE_GPTC);
+ IXGBE_READ_REG(hw, IXGBE_GORCL);
+ IXGBE_READ_REG(hw, IXGBE_GORCH);
+ IXGBE_READ_REG(hw, IXGBE_GOTCL);
+ IXGBE_READ_REG(hw, IXGBE_GOTCH);
+ for (i = 0; i < 8; i++)
+ IXGBE_READ_REG(hw, IXGBE_RNBC(i));
+ IXGBE_READ_REG(hw, IXGBE_RUC);
+ IXGBE_READ_REG(hw, IXGBE_RFC);
+ IXGBE_READ_REG(hw, IXGBE_ROC);
+ IXGBE_READ_REG(hw, IXGBE_RJC);
+ IXGBE_READ_REG(hw, IXGBE_MNGPRC);
+ IXGBE_READ_REG(hw, IXGBE_MNGPDC);
+ IXGBE_READ_REG(hw, IXGBE_MNGPTC);
+ IXGBE_READ_REG(hw, IXGBE_TORL);
+ IXGBE_READ_REG(hw, IXGBE_TORH);
+ IXGBE_READ_REG(hw, IXGBE_TPR);
+ IXGBE_READ_REG(hw, IXGBE_TPT);
+ IXGBE_READ_REG(hw, IXGBE_PTC64);
+ IXGBE_READ_REG(hw, IXGBE_PTC127);
+ IXGBE_READ_REG(hw, IXGBE_PTC255);
+ IXGBE_READ_REG(hw, IXGBE_PTC511);
+ IXGBE_READ_REG(hw, IXGBE_PTC1023);
+ IXGBE_READ_REG(hw, IXGBE_PTC1522);
+ IXGBE_READ_REG(hw, IXGBE_MPTC);
+ IXGBE_READ_REG(hw, IXGBE_BPTC);
+ for (i = 0; i < 16; i++) {
+ IXGBE_READ_REG(hw, IXGBE_QPRC(i));
+ IXGBE_READ_REG(hw, IXGBE_QBRC(i));
+ IXGBE_READ_REG(hw, IXGBE_QPTC(i));
+ IXGBE_READ_REG(hw, IXGBE_QBTC(i));
+ }
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_get_mac_addr_generic - Generic get MAC address
+ * @hw: pointer to hardware structure
+ * @mac_addr: Adapter MAC address
+ *
+ * Reads the adapter's MAC address from first Receive Address Register (RAR0)
+ * A reset of the adapter must be performed prior to calling this function
+ * in order for the MAC address to have been loaded from the EEPROM into RAR0
+ **/
+static s32 ixgbe_get_mac_addr_generic(struct ixgbe_hw *hw, u8 *mac_addr)
+{
+ u32 rar_high;
+ u32 rar_low;
+ u16 i;
+
+ rar_high = IXGBE_READ_REG(hw, IXGBE_RAH(0));
+ rar_low = IXGBE_READ_REG(hw, IXGBE_RAL(0));
+
+ for (i = 0; i < 4; i++)
+ mac_addr[i] = (u8)(rar_low >> (i*8));
+
+ for (i = 0; i < 2; i++)
+ mac_addr[i+4] = (u8)(rar_high >> (i*8));
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_get_bus_info_generic - Generic set PCI bus info
+ * @hw: pointer to hardware structure
+ *
+ * Sets the PCI bus info (speed, width, type) within the ixgbe_hw structure
+ **/
+static s32 ixgbe_get_bus_info_generic(struct ixgbe_hw *hw)
+{
+ u16 link_status;
+
+ hw->bus.type = ixgbe_bus_type_pci_express;
+
+ /* Get the negotiated link width and speed from PCI config space */
+ link_status = IXGBE_READ_PCIE_WORD(hw, IXGBE_PCI_LINK_STATUS);
+
+ switch (link_status & IXGBE_PCI_LINK_WIDTH) {
+ case IXGBE_PCI_LINK_WIDTH_1:
+ hw->bus.width = ixgbe_bus_width_pcie_x1;
+ break;
+ case IXGBE_PCI_LINK_WIDTH_2:
+ hw->bus.width = ixgbe_bus_width_pcie_x2;
+ break;
+ case IXGBE_PCI_LINK_WIDTH_4:
+ hw->bus.width = ixgbe_bus_width_pcie_x4;
+ break;
+ case IXGBE_PCI_LINK_WIDTH_8:
+ hw->bus.width = ixgbe_bus_width_pcie_x8;
+ break;
+ default:
+ hw->bus.width = ixgbe_bus_width_unknown;
+ break;
+ }
+
+ switch (link_status & IXGBE_PCI_LINK_SPEED) {
+ case IXGBE_PCI_LINK_SPEED_2500:
+ hw->bus.speed = ixgbe_bus_speed_2500;
+ break;
+ case IXGBE_PCI_LINK_SPEED_5000:
+ hw->bus.speed = ixgbe_bus_speed_5000;
+ break;
+ default:
+ hw->bus.speed = ixgbe_bus_speed_unknown;
+ break;
+ }
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_stop_adapter_generic - Generic stop TX/RX units
+ * @hw: pointer to hardware structure
+ *
+ * Sets the adapter_stopped flag within ixgbe_hw struct. Clears interrupts,
+ * disables transmit and receive units. The adapter_stopped flag is used by
+ * the shared code and drivers to determine if the adapter is in a stopped
+ * state and should not touch the hardware.
+ **/
+static s32 ixgbe_stop_adapter_generic(struct ixgbe_hw *hw)
+{
+ u32 number_of_queues;
+ u32 reg_val;
+ u16 i;
+
+ /*
+ * Set the adapter_stopped flag so other driver functions stop touching
+ * the hardware
+ */
+ hw->adapter_stopped = true;
+
+ /* Disable the receive unit */
+ reg_val = IXGBE_READ_REG(hw, IXGBE_RXCTRL);
+ reg_val &= ~(IXGBE_RXCTRL_RXEN);
+ IXGBE_WRITE_REG(hw, IXGBE_RXCTRL, reg_val);
+ msleep(2);
+
+ /* Clear interrupt mask to stop from interrupts being generated */
+ IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_IRQ_CLEAR_MASK);
+
+ /* Clear any pending interrupts */
+ IXGBE_READ_REG(hw, IXGBE_EICR);
+
+ /* Disable the transmit unit. Each queue must be disabled. */
+ number_of_queues = ixgbe_get_num_of_tx_queues(hw);
+ for (i = 0; i < number_of_queues; i++) {
+ reg_val = IXGBE_READ_REG(hw, IXGBE_TXDCTL(i));
+ if (reg_val & IXGBE_TXDCTL_ENABLE) {
+ reg_val &= ~IXGBE_TXDCTL_ENABLE;
+ IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(i), reg_val);
+ }
+ }
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_led_on_generic - Turns on the software controllable LEDs.
+ * @hw: pointer to hardware structure
+ * @index: led number to turn on
+ **/
+static s32 ixgbe_led_on_generic(struct ixgbe_hw *hw, u32 index)
+{
+ u32 led_reg = IXGBE_READ_REG(hw, IXGBE_LEDCTL);
+
+ /* To turn on the LED, set mode to ON. */
+ led_reg &= ~IXGBE_LED_MODE_MASK(index);
+ led_reg |= IXGBE_LED_ON << IXGBE_LED_MODE_SHIFT(index);
+ IXGBE_WRITE_REG(hw, IXGBE_LEDCTL, led_reg);
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_led_off_generic - Turns off the software controllable LEDs.
+ * @hw: pointer to hardware structure
+ * @index: led number to turn off
+ **/
+static s32 ixgbe_led_off_generic(struct ixgbe_hw *hw, u32 index)
+{
+ u32 led_reg = IXGBE_READ_REG(hw, IXGBE_LEDCTL);
+
+ /* To turn off the LED, set mode to OFF. */
+ led_reg &= ~IXGBE_LED_MODE_MASK(index);
+ led_reg |= IXGBE_LED_OFF << IXGBE_LED_MODE_SHIFT(index);
+ IXGBE_WRITE_REG(hw, IXGBE_LEDCTL, led_reg);
+
+ return IXGBE_SUCCESS;
+}
+
+
+/**
+ * ixgbe_init_eeprom_params_generic - Initialize EEPROM params
+ * @hw: pointer to hardware structure
+ *
+ * Initializes the EEPROM parameters ixgbe_eeprom_info within the
+ * ixgbe_hw struct in order to set up EEPROM access.
+ **/
+static s32 ixgbe_init_eeprom_params_generic(struct ixgbe_hw *hw)
+{
+ struct ixgbe_eeprom_info *eeprom = &hw->eeprom;
+ u32 eec;
+ u16 eeprom_size;
+
+ if (eeprom->type == ixgbe_eeprom_uninitialized) {
+ eeprom->type = ixgbe_eeprom_none;
+
+ /*
+ * Check for EEPROM present first.
+ * If not present leave as none
+ */
+ eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+ if (eec & IXGBE_EEC_PRES) {
+ eeprom->type = ixgbe_eeprom_spi;
+
+ /*
+ * SPI EEPROM is assumed here. This code would need to
+ * change if a future EEPROM is not SPI.
+ */
+ eeprom_size = (u16)((eec & IXGBE_EEC_SIZE) >>
+ IXGBE_EEC_SIZE_SHIFT);
+ eeprom->word_size = 1 << (eeprom_size +
+ IXGBE_EEPROM_WORD_SIZE_SHIFT);
+ }
+
+ if (eec & IXGBE_EEC_ADDR_SIZE)
+ eeprom->address_bits = 16;
+ else
+ eeprom->address_bits = 8;
+ DEBUGOUT3("Eeprom params: type = %d, size = %d, address bits: "
+ "%d\n", eeprom->type, eeprom->word_size,
+ eeprom->address_bits);
+ }
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_write_eeprom_generic - Writes 16 bit value to EEPROM
+ * @hw: pointer to hardware structure
+ * @offset: offset within the EEPROM to be written to
+ * @data: 16 bit word to be written to the EEPROM
+ *
+ * If ixgbe_eeprom_update_checksum is not called after this function, the
+ * EEPROM will most likely contain an invalid checksum.
+ **/
+static s32 ixgbe_write_eeprom_generic(struct ixgbe_hw *hw, u16 offset,
+ u16 data)
+{
+ s32 status;
+ u8 write_opcode = IXGBE_EEPROM_WRITE_OPCODE_SPI;
+
+ /* Prepare the EEPROM for writing */
+ status = ixgbe_acquire_eeprom(hw);
+
+ if (status == IXGBE_SUCCESS) {
+ if (ixgbe_ready_eeprom(hw) != IXGBE_SUCCESS) {
+ ixgbe_release_eeprom(hw);
+ status = IXGBE_ERR_EEPROM;
+ }
+ }
+
+ if (status == IXGBE_SUCCESS) {
+ ixgbe_standby_eeprom(hw);
+
+ /* Send the WRITE ENABLE command (8 bit opcode ) */
+ ixgbe_shift_out_eeprom_bits(hw, IXGBE_EEPROM_WREN_OPCODE_SPI,
+ IXGBE_EEPROM_OPCODE_BITS);
+
+ ixgbe_standby_eeprom(hw);
+
+ /*
+ * Some SPI eeproms use the 8th address bit embedded in the
+ * opcode
+ */
+ if ((hw->eeprom.address_bits == 8) && (offset >= 128))
+ write_opcode |= IXGBE_EEPROM_A8_OPCODE_SPI;
+
+ /* Send the Write command (8-bit opcode + addr) */
+ ixgbe_shift_out_eeprom_bits(hw, write_opcode,
+ IXGBE_EEPROM_OPCODE_BITS);
+ ixgbe_shift_out_eeprom_bits(hw, (u16)(offset*2),
+ hw->eeprom.address_bits);
+
+ /* Send the data */
+ data = (data >> 8) | (data << 8);
+ ixgbe_shift_out_eeprom_bits(hw, data, 16);
+ ixgbe_standby_eeprom(hw);
+
+ msleep(10);
+
+ /* Done with writing - release the EEPROM */
+ ixgbe_release_eeprom(hw);
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_read_eeprom_bit_bang_generic - Read EEPROM word using bit-bang
+ * @hw: pointer to hardware structure
+ * @offset: offset within the EEPROM to be read
+ * @data: read 16 bit value from EEPROM
+ *
+ * Reads 16 bit value from EEPROM through bit-bang method
+ **/
+static s32 ixgbe_read_eeprom_bit_bang_generic(struct ixgbe_hw *hw, u16 offset,
+ u16 *data)
+{
+ s32 status;
+ u16 word_in;
+ u8 read_opcode = IXGBE_EEPROM_READ_OPCODE_SPI;
+
+ /* Prepare the EEPROM for reading */
+ status = ixgbe_acquire_eeprom(hw);
+
+ if (status == IXGBE_SUCCESS) {
+ if (ixgbe_ready_eeprom(hw) != IXGBE_SUCCESS) {
+ ixgbe_release_eeprom(hw);
+ status = IXGBE_ERR_EEPROM;
+ }
+ }
+
+ if (status == IXGBE_SUCCESS) {
+ ixgbe_standby_eeprom(hw);
+
+ /*
+ * Some SPI eeproms use the 8th address bit embedded in the
+ * opcode
+ */
+ if ((hw->eeprom.address_bits == 8) && (offset >= 128))
+ read_opcode |= IXGBE_EEPROM_A8_OPCODE_SPI;
+
+ /* Send the READ command (opcode + addr) */
+ ixgbe_shift_out_eeprom_bits(hw, read_opcode,
+ IXGBE_EEPROM_OPCODE_BITS);
+ ixgbe_shift_out_eeprom_bits(hw, (u16)(offset*2),
+ hw->eeprom.address_bits);
+
+ /* Read the data. */
+ word_in = ixgbe_shift_in_eeprom_bits(hw, 16);
+ *data = (word_in >> 8) | (word_in << 8);
+
+ /* End this read operation */
+ ixgbe_release_eeprom(hw);
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_acquire_eeprom - Acquire EEPROM using bit-bang
+ * @hw: pointer to hardware structure
+ *
+ * Prepares EEPROM for access using bit-bang method. This function should
+ * be called before issuing a command to the EEPROM.
+ **/
+static s32 ixgbe_acquire_eeprom(struct ixgbe_hw *hw)
+{
+ s32 status = IXGBE_SUCCESS;
+ u32 eec;
+ u32 i;
+
+ if (ixgbe_acquire_swfw_sync(hw, IXGBE_GSSR_EEP_SM) != IXGBE_SUCCESS)
+ status = IXGBE_ERR_SWFW_SYNC;
+
+ if (status == IXGBE_SUCCESS) {
+ eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+
+ /* Request EEPROM Access */
+ eec |= IXGBE_EEC_REQ;
+ IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+
+ for (i = 0; i < IXGBE_EEPROM_GRANT_ATTEMPTS; i++) {
+ eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+ if (eec & IXGBE_EEC_GNT)
+ break;
+ udelay(5);
+ }
+
+ /* Release if grant not aquired */
+ if (!(eec & IXGBE_EEC_GNT)) {
+ eec &= ~IXGBE_EEC_REQ;
+ IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+ DEBUGOUT("Could not acquire EEPROM grant\n");
+
+ ixgbe_release_swfw_sync(hw, IXGBE_GSSR_EEP_SM);
+ status = IXGBE_ERR_EEPROM;
+ }
+ }
+
+ /* Setup EEPROM for Read/Write */
+ if (status == IXGBE_SUCCESS) {
+ /* Clear CS and SK */
+ eec &= ~(IXGBE_EEC_CS | IXGBE_EEC_SK);
+ IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+ IXGBE_WRITE_FLUSH(hw);
+ udelay(1);
+ }
+ return status;
+}
+
+/**
+ * ixgbe_get_eeprom_semaphore - Get hardware semaphore
+ * @hw: pointer to hardware structure
+ *
+ * Sets the hardware semaphores so EEPROM access can occur for bit-bang method
+ **/
+static s32 ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw)
+{
+ s32 status = IXGBE_ERR_EEPROM;
+ u32 timeout;
+ u32 i;
+ u32 swsm;
+
+ /* Set timeout value based on size of EEPROM */
+ timeout = hw->eeprom.word_size + 1;
+
+ /* Get SMBI software semaphore between device drivers first */
+ for (i = 0; i < timeout; i++) {
+ /*
+ * If the SMBI bit is 0 when we read it, then the bit will be
+ * set and we have the semaphore
+ */
+ swsm = IXGBE_READ_REG(hw, IXGBE_SWSM);
+ if (!(swsm & IXGBE_SWSM_SMBI)) {
+ status = IXGBE_SUCCESS;
+ break;
+ }
+ msleep(1);
+ }
+
+ /* Now get the semaphore between SW/FW through the SWESMBI bit */
+ if (status == IXGBE_SUCCESS) {
+ for (i = 0; i < timeout; i++) {
+ swsm = IXGBE_READ_REG(hw, IXGBE_SWSM);
+
+ /* Set the SW EEPROM semaphore bit to request access */
+ swsm |= IXGBE_SWSM_SWESMBI;
+ IXGBE_WRITE_REG(hw, IXGBE_SWSM, swsm);
+
+ /*
+ * If we set the bit successfully then we got the
+ * semaphore.
+ */
+ swsm = IXGBE_READ_REG(hw, IXGBE_SWSM);
+ if (swsm & IXGBE_SWSM_SWESMBI)
+ break;
+
+ udelay(50);
+ }
+
+ /*
+ * Release semaphores and return error if SW EEPROM semaphore
+ * was not granted because we don't have access to the EEPROM
+ */
+ if (i >= timeout) {
+ DEBUGOUT("Driver can't access the Eeprom - Semaphore "
+ "not granted.\n");
+ ixgbe_release_eeprom_semaphore(hw);
+ status = IXGBE_ERR_EEPROM;
+ }
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_release_eeprom_semaphore - Release hardware semaphore
+ * @hw: pointer to hardware structure
+ *
+ * This function clears hardware semaphore bits.
+ **/
+static void ixgbe_release_eeprom_semaphore(struct ixgbe_hw *hw)
+{
+ u32 swsm;
+
+ swsm = IXGBE_READ_REG(hw, IXGBE_SWSM);
+
+ /* Release both semaphores by writing 0 to the bits SWESMBI and SMBI */
+ swsm &= ~(IXGBE_SWSM_SWESMBI | IXGBE_SWSM_SMBI);
+ IXGBE_WRITE_REG(hw, IXGBE_SWSM, swsm);
+}
+
+/**
+ * ixgbe_ready_eeprom - Polls for EEPROM ready
+ * @hw: pointer to hardware structure
+ **/
+static s32 ixgbe_ready_eeprom(struct ixgbe_hw *hw)
+{
+ s32 status = IXGBE_SUCCESS;
+ u16 i;
+ u8 spi_stat_reg;
+
+ /*
+ * Read "Status Register" repeatedly until the LSB is cleared. The
+ * EEPROM will signal that the command has been completed by clearing
+ * bit 0 of the internal status register. If it's not cleared within
+ * 5 milliseconds, then error out.
+ */
+ for (i = 0; i < IXGBE_EEPROM_MAX_RETRY_SPI; i += 5) {
+ ixgbe_shift_out_eeprom_bits(hw, IXGBE_EEPROM_RDSR_OPCODE_SPI,
+ IXGBE_EEPROM_OPCODE_BITS);
+ spi_stat_reg = (u8)ixgbe_shift_in_eeprom_bits(hw, 8);
+ if (!(spi_stat_reg & IXGBE_EEPROM_STATUS_RDY_SPI))
+ break;
+
+ udelay(5);
+ ixgbe_standby_eeprom(hw);
+ };
+
+ /*
+ * On some parts, SPI write time could vary from 0-20mSec on 3.3V
+ * devices (and only 0-5mSec on 5V devices)
+ */
+ if (i >= IXGBE_EEPROM_MAX_RETRY_SPI) {
+ DEBUGOUT("SPI EEPROM Status error\n");
+ status = IXGBE_ERR_EEPROM;
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_standby_eeprom - Returns EEPROM to a "standby" state
+ * @hw: pointer to hardware structure
+ **/
+static void ixgbe_standby_eeprom(struct ixgbe_hw *hw)
+{
+ u32 eec;
+
+ eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+
+ /* Toggle CS to flush commands */
+ eec |= IXGBE_EEC_CS;
+ IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+ IXGBE_WRITE_FLUSH(hw);
+ udelay(1);
+ eec &= ~IXGBE_EEC_CS;
+ IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+ IXGBE_WRITE_FLUSH(hw);
+ udelay(1);
+}
+
+/**
+ * ixgbe_shift_out_eeprom_bits - Shift data bits out to the EEPROM.
+ * @hw: pointer to hardware structure
+ * @data: data to send to the EEPROM
+ * @count: number of bits to shift out
+ **/
+static void ixgbe_shift_out_eeprom_bits(struct ixgbe_hw *hw, u16 data,
+ u16 count)
+{
+ u32 eec;
+ u32 mask;
+ u32 i;
+
+ eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+
+ /*
+ * Mask is used to shift "count" bits of "data" out to the EEPROM
+ * one bit at a time. Determine the starting bit based on count
+ */
+ mask = 0x01 << (count - 1);
+
+ for (i = 0; i < count; i++) {
+ /*
+ * A "1" is shifted out to the EEPROM by setting bit "DI" to a
+ * "1", and then raising and then lowering the clock (the SK
+ * bit controls the clock input to the EEPROM). A "0" is
+ * shifted out to the EEPROM by setting "DI" to "0" and then
+ * raising and then lowering the clock.
+ */
+ if (data & mask)
+ eec |= IXGBE_EEC_DI;
+ else
+ eec &= ~IXGBE_EEC_DI;
+
+ IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+ IXGBE_WRITE_FLUSH(hw);
+
+ udelay(1);
+
+ ixgbe_raise_eeprom_clk(hw, &eec);
+ ixgbe_lower_eeprom_clk(hw, &eec);
+
+ /*
+ * Shift mask to signify next bit of data to shift in to the
+ * EEPROM
+ */
+ mask = mask >> 1;
+ };
+
+ /* We leave the "DI" bit set to "0" when we leave this routine. */
+ eec &= ~IXGBE_EEC_DI;
+ IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+ IXGBE_WRITE_FLUSH(hw);
+}
+
+/**
+ * ixgbe_shift_in_eeprom_bits - Shift data bits in from the EEPROM
+ * @hw: pointer to hardware structure
+ **/
+static u16 ixgbe_shift_in_eeprom_bits(struct ixgbe_hw *hw, u16 count)
+{
+ u32 eec;
+ u32 i;
+ u16 data = 0;
+
+ /*
+ * In order to read a register from the EEPROM, we need to shift
+ * 'count' bits in from the EEPROM. Bits are "shifted in" by raising
+ * the clock input to the EEPROM (setting the SK bit), and then reading
+ * the value of the "DO" bit. During this "shifting in" process the
+ * "DI" bit should always be clear.
+ */
+ eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+
+ eec &= ~(IXGBE_EEC_DO | IXGBE_EEC_DI);
+
+ for (i = 0; i < count; i++) {
+ data = data << 1;
+ ixgbe_raise_eeprom_clk(hw, &eec);
+
+ eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+
+ eec &= ~(IXGBE_EEC_DI);
+ if (eec & IXGBE_EEC_DO)
+ data |= 1;
+
+ ixgbe_lower_eeprom_clk(hw, &eec);
+ }
+
+ return data;
+}
+
+/**
+ * ixgbe_raise_eeprom_clk - Raises the EEPROM's clock input.
+ * @hw: pointer to hardware structure
+ * @eec: EEC register's current value
+ **/
+static void ixgbe_raise_eeprom_clk(struct ixgbe_hw *hw, u32 *eec)
+{
+ /*
+ * Raise the clock input to the EEPROM
+ * (setting the SK bit), then delay
+ */
+ *eec = *eec | IXGBE_EEC_SK;
+ IXGBE_WRITE_REG(hw, IXGBE_EEC, *eec);
+ IXGBE_WRITE_FLUSH(hw);
+ udelay(1);
+}
+
+/**
+ * ixgbe_lower_eeprom_clk - Lowers the EEPROM's clock input.
+ * @hw: pointer to hardware structure
+ * @eecd: EECD's current value
+ **/
+static void ixgbe_lower_eeprom_clk(struct ixgbe_hw *hw, u32 *eec)
+{
+ /*
+ * Lower the clock input to the EEPROM (clearing the SK bit), then
+ * delay
+ */
+ *eec = *eec & ~IXGBE_EEC_SK;
+ IXGBE_WRITE_REG(hw, IXGBE_EEC, *eec);
+ IXGBE_WRITE_FLUSH(hw);
+ udelay(1);
+}
+
+/**
+ * ixgbe_release_eeprom - Release EEPROM, release semaphores
+ * @hw: pointer to hardware structure
+ **/
+static void ixgbe_release_eeprom(struct ixgbe_hw *hw)
+{
+ u32 eec;
+
+ eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+
+ eec |= IXGBE_EEC_CS; /* Pull CS high */
+ eec &= ~IXGBE_EEC_SK; /* Lower SCK */
+
+ IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+ IXGBE_WRITE_FLUSH(hw);
+
+ udelay(1);
+
+ /* Stop requesting EEPROM access */
+ eec &= ~IXGBE_EEC_REQ;
+ IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+
+ ixgbe_release_swfw_sync(hw, IXGBE_GSSR_EEP_SM);
+}
+
+/**
+ * ixgbe_calc_eeprom_checksum - Calculates and returns the checksum
+ * @hw: pointer to hardware structure
+ **/
+static u16 ixgbe_calc_eeprom_checksum(struct ixgbe_hw *hw)
+{
+ u16 i;
+ u16 j;
+ u16 checksum = 0;
+ u16 length = 0;
+ u16 pointer = 0;
+ u16 word = 0;
+
+ /* Include 0x0-0x3F in the checksum */
+ for (i = 0; i < IXGBE_EEPROM_CHECKSUM; i++) {
+ if (ixgbe_read_eeprom(hw, i, &word) != IXGBE_SUCCESS) {
+ DEBUGOUT("EEPROM read failed\n");
+ break;
+ }
+ checksum += word;
+ }
+
+ /* Include all data from pointers except for the fw pointer */
+ for (i = IXGBE_PCIE_ANALOG_PTR; i < IXGBE_FW_PTR; i++) {
+ ixgbe_read_eeprom(hw, i, &pointer);
+
+ /* Make sure the pointer seems valid */
+ if (pointer != 0xFFFF && pointer != 0) {
+ ixgbe_read_eeprom(hw, pointer, &length);
+
+ if (length != 0xFFFF && length != 0) {
+ for (j = pointer+1; j <= pointer+length; j++) {
+ ixgbe_read_eeprom(hw, j, &word);
+ checksum += word;
+ }
+ }
+ }
+ }
+
+ checksum = (u16)IXGBE_EEPROM_SUM - checksum;
+
+ return checksum;
+}
+
+/**
+ * ixgbe_validate_eeprom_checksum_generic - Validate EEPROM checksum
+ * @hw: pointer to hardware structure
+ * @checksum_val: calculated checksum
+ *
+ * Performs checksum calculation and validates the EEPROM checksum. If the
+ * caller does not need checksum_val, the value can be NULL.
+ **/
+static s32 ixgbe_validate_eeprom_checksum_generic(struct ixgbe_hw *hw,
+ u16 *checksum_val)
+{
+ s32 status;
+ u16 checksum;
+ u16 read_checksum = 0;
+
+ /*
+ * Read the first word from the EEPROM. If this times out or fails, do
+ * not continue or we could be in for a very long wait while every
+ * EEPROM read fails
+ */
+ status = ixgbe_read_eeprom(hw, 0, &checksum);
+
+ if (status == IXGBE_SUCCESS) {
+ checksum = ixgbe_calc_eeprom_checksum(hw);
+
+ ixgbe_read_eeprom(hw, IXGBE_EEPROM_CHECKSUM, &read_checksum);
+
+ /*
+ * Verify read checksum from EEPROM is the same as
+ * calculated checksum
+ */
+ if (read_checksum != checksum) {
+ status = IXGBE_ERR_EEPROM_CHECKSUM;
+ }
+
+ /* If the user cares, return the calculated checksum */
+ if (checksum_val) {
+ *checksum_val = checksum;
+ }
+ } else {
+ DEBUGOUT("EEPROM read failed\n");
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_update_eeprom_checksum_generic - Updates the EEPROM checksm
+ * @hw: pointer to hardware structure
+ **/
+static s32 ixgbe_update_eeprom_checksum_generic(struct ixgbe_hw *hw)
+{
+ s32 status;
+ u16 checksum;
+
+ /*
+ * Read the first word from the EEPROM. If this times out or fails, do
+ * not continue or we could be in for a very long wait while every
+ * EEPROM read fails
+ */
+ status = ixgbe_read_eeprom(hw, 0, &checksum);
+
+ if (status == IXGBE_SUCCESS) {
+ checksum = ixgbe_calc_eeprom_checksum(hw);
+ status = ixgbe_write_eeprom(hw, IXGBE_EEPROM_CHECKSUM,
+ checksum);
+ } else {
+ DEBUGOUT("EEPROM read failed\n");
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_validate_mac_addr - Validate MAC address
+ * @mac_addr: pointer to MAC address.
+ *
+ * Tests a MAC address to ensure it is a valid Individual Address
+ **/
+s32 ixgbe_validate_mac_addr(u8 *mac_addr)
+{
+ s32 status = IXGBE_SUCCESS;
+
+ /* Make sure it is not a multicast address */
+ if (IXGBE_IS_MULTICAST(mac_addr)) {
+ DEBUGOUT("MAC address is multicast\n");
+ status = IXGBE_ERR_INVALID_MAC_ADDR;
+ /* Not a broadcast address */
+ } else if (IXGBE_IS_BROADCAST(mac_addr)) {
+ DEBUGOUT("MAC address is broadcast\n");
+ status = IXGBE_ERR_INVALID_MAC_ADDR;
+ /* Reject the zero address */
+ } else if (mac_addr[0] == 0 && mac_addr[1] == 0 && mac_addr[2] == 0 &&
+ mac_addr[3] == 0 && mac_addr[4] == 0 && mac_addr[5] == 0) {
+ DEBUGOUT("MAC address is all zeros\n");
+ status = IXGBE_ERR_INVALID_MAC_ADDR;
+ }
+ return status;
+}
+
+/**
+ * ixgbe_set_rar_generic - Set RX address register
+ * @hw: pointer to hardware structure
+ * @addr: Address to put into receive address register
+ * @index: Receive address register to write
+ * @vind: Vind to set RAR to
+ * @enable_addr: set flag that address is active
+ *
+ * Puts an ethernet address into a receive address register.
+ **/
+static s32 ixgbe_set_rar_generic(struct ixgbe_hw *hw, u32 index, u8 *addr,
+ u32 vind, u32 enable_addr)
+{
+ u32 rar_low, rar_high;
+
+ /*
+ * HW expects these in little endian so we reverse the byte order from
+ * network order (big endian) to little endian
+ */
+ rar_low = ((u32)addr[0] |
+ ((u32)addr[1] << 8) |
+ ((u32)addr[2] << 16) |
+ ((u32)addr[3] << 24));
+
+ rar_high = ((u32)addr[4] |
+ ((u32)addr[5] << 8) |
+ ((vind << IXGBE_RAH_VIND_SHIFT) & IXGBE_RAH_VIND_MASK));
+
+ if (enable_addr != 0)
+ rar_high |= IXGBE_RAH_AV;
+
+ IXGBE_WRITE_REG(hw, IXGBE_RAL(index), rar_low);
+ IXGBE_WRITE_REG(hw, IXGBE_RAH(index), rar_high);
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_enable_rar - Enable RX address register
+ * @hw: pointer to hardware structure
+ * @index: index into the RAR table
+ *
+ * Enables the select receive address register.
+ **/
+static void ixgbe_enable_rar(struct ixgbe_hw *hw, u32 index)
+{
+ u32 rar_high;
+
+ rar_high = IXGBE_READ_REG(hw, IXGBE_RAH(index));
+ rar_high |= IXGBE_RAH_AV;
+ IXGBE_WRITE_REG(hw, IXGBE_RAH(index), rar_high);
+}
+
+/**
+ * ixgbe_disable_rar - Disable RX address register
+ * @hw: pointer to hardware structure
+ * @index: index into the RAR table
+ *
+ * Disables the select receive address register.
+ **/
+static void ixgbe_disable_rar(struct ixgbe_hw *hw, u32 index)
+{
+ u32 rar_high;
+
+ rar_high = IXGBE_READ_REG(hw, IXGBE_RAH(index));
+ rar_high &= (~IXGBE_RAH_AV);
+ IXGBE_WRITE_REG(hw, IXGBE_RAH(index), rar_high);
+}
+
+/**
+ * ixgbe_init_rx_addrs_generic - Initializes receive address filters.
+ * @hw: pointer to hardware structure
+ *
+ * Places the MAC address in receive address register 0 and clears the rest
+ * of the receive addresss registers. Clears the multicast table. Assumes
+ * the receiver is in reset when the routine is called.
+ **/
+static s32 ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw)
+{
+ u32 i;
+ u32 rar_entries = ixgbe_get_num_rx_addrs(hw);
+
+ /*
+ * If the current mac address is valid, assume it is a software override
+ * to the permanent address.
+ * Otherwise, use the permanent address from the eeprom.
+ */
+ if (ixgbe_validate_mac_addr(hw->mac.addr) ==
+ IXGBE_ERR_INVALID_MAC_ADDR) {
+ /* Get the MAC address from the RAR0 for later reference */
+ ixgbe_get_mac_addr(hw, hw->mac.addr);
+
+ DEBUGOUT3(" Keeping Current RAR0 Addr =%.2X %.2X %.2X ",
+ hw->mac.addr[0], hw->mac.addr[1],
+ hw->mac.addr[2]);
+ DEBUGOUT3("%.2X %.2X %.2X\n", hw->mac.addr[3],
+ hw->mac.addr[4], hw->mac.addr[5]);
+ } else {
+ /* Setup the receive address. */
+ DEBUGOUT("Overriding MAC Address in RAR[0]\n");
+ DEBUGOUT3(" New MAC Addr =%.2X %.2X %.2X ",
+ hw->mac.addr[0], hw->mac.addr[1],
+ hw->mac.addr[2]);
+ DEBUGOUT3("%.2X %.2X %.2X\n", hw->mac.addr[3],
+ hw->mac.addr[4], hw->mac.addr[5]);
+
+ ixgbe_set_rar(hw, 0, hw->mac.addr, 0, IXGBE_RAH_AV);
+ }
+
+ hw->addr_ctrl.rar_used_count = 1;
+
+ /* Zero out the other receive addresses. */
+ DEBUGOUT("Clearing RAR[1-15]\n");
+ for (i = 1; i < rar_entries; i++) {
+ IXGBE_WRITE_REG(hw, IXGBE_RAL(i), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_RAH(i), 0);
+ }
+
+ /* Clear the MTA */
+ hw->addr_ctrl.mc_addr_in_rar_count = 0;
+ hw->addr_ctrl.mta_in_use = 0;
+ IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL, hw->mac.mc_filter_type);
+
+ DEBUGOUT(" Clearing MTA\n");
+ for (i = 0; i < IXGBE_MC_TBL_SIZE; i++)
+ IXGBE_WRITE_REG(hw, IXGBE_MTA(i), 0);
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_mta_vector - Determines bit-vector in multicast table to set
+ * @hw: pointer to hardware structure
+ * @mc_addr: the multicast address
+ *
+ * Extracts the 12 bits, from a multicast address, to determine which
+ * bit-vector to set in the multicast table. The hardware uses 12 bits, from
+ * incoming rx multicast addresses, to determine the bit-vector to check in
+ * the MTA. Which of the 4 combination, of 12-bits, the hardware uses is set
+ * by the MO field of the MCSTCTRL. The MO field is set during initalization
+ * to mc_filter_type.
+ **/
+static s32 ixgbe_mta_vector(struct ixgbe_hw *hw, u8 *mc_addr)
+{
+ u32 vector = 0;
+
+ switch (hw->mac.mc_filter_type) {
+ case 0: /* use bits [47:36] of the address */
+ vector = ((mc_addr[4] >> 4) | (((u16)mc_addr[5]) << 4));
+ break;
+ case 1: /* use bits [46:35] of the address */
+ vector = ((mc_addr[4] >> 3) | (((u16)mc_addr[5]) << 5));
+ break;
+ case 2: /* use bits [45:34] of the address */
+ vector = ((mc_addr[4] >> 2) | (((u16)mc_addr[5]) << 6));
+ break;
+ case 3: /* use bits [43:32] of the address */
+ vector = ((mc_addr[4]) | (((u16)mc_addr[5]) << 8));
+ break;
+ default: /* Invalid mc_filter_type */
+ DEBUGOUT("MC filter type param set incorrectly\n");
+ break;
+ }
+
+ /* vector can only be 12-bits or boundary will be exceeded */
+ vector &= 0xFFF;
+ return vector;
+}
+
+/**
+ * ixgbe_set_mta - Set bit-vector in multicast table
+ * @hw: pointer to hardware structure
+ * @hash_value: Multicast address hash value
+ *
+ * Sets the bit-vector in the multicast table.
+ **/
+static void ixgbe_set_mta(struct ixgbe_hw *hw, u8 *mc_addr)
+{
+ u32 vector;
+ u32 vector_bit;
+ u32 vector_reg;
+ u32 mta_reg;
+
+ hw->addr_ctrl.mta_in_use++;
+
+ vector = ixgbe_mta_vector(hw, mc_addr);
+ DEBUGOUT1(" bit-vector = 0x%03X\n", vector);
+
+ /*
+ * The MTA is a register array of 128 32-bit registers. It is treated
+ * like an array of 4096 bits. We want to set bit
+ * BitArray[vector_value]. So we figure out what register the bit is
+ * in, read it, OR in the new bit, then write back the new value. The
+ * register is determined by the upper 7 bits of the vector value and
+ * the bit within that register are determined by the lower 5 bits of
+ * the value.
+ */
+ vector_reg = (vector >> 5) & 0x7F;
+ vector_bit = vector & 0x1F;
+ mta_reg = IXGBE_READ_REG(hw, IXGBE_MTA(vector_reg));
+ mta_reg |= (1 << vector_bit);
+ IXGBE_WRITE_REG(hw, IXGBE_MTA(vector_reg), mta_reg);
+}
+
+/**
+ * ixgbe_add_mc_addr - Adds a multicast address.
+ * @hw: pointer to hardware structure
+ * @mc_addr: new multicast address
+ *
+ * Adds it to unused receive address register or to the multicast table.
+ **/
+static void ixgbe_add_mc_addr(struct ixgbe_hw *hw, u8 *mc_addr)
+{
+ u32 rar_entries = ixgbe_get_num_rx_addrs(hw);
+
+ DEBUGOUT6(" MC Addr =%.2X %.2X %.2X %.2X %.2X %.2X\n",
+ mc_addr[0], mc_addr[1], mc_addr[2],
+ mc_addr[3], mc_addr[4], mc_addr[5]);
+
+ /*
+ * Place this multicast address in the RAR if there is room,
+ * else put it in the MTA
+ */
+ if (hw->addr_ctrl.rar_used_count < rar_entries) {
+ ixgbe_set_rar(hw, hw->addr_ctrl.rar_used_count,
+ mc_addr, 0, IXGBE_RAH_AV);
+ DEBUGOUT1("Added a multicast address to RAR[%d]\n",
+ hw->addr_ctrl.rar_used_count);
+ hw->addr_ctrl.rar_used_count++;
+ hw->addr_ctrl.mc_addr_in_rar_count++;
+ } else {
+ ixgbe_set_mta(hw, mc_addr);
+ }
+
+ DEBUGOUT("ixgbe_add_mc_addr Complete\n");
+}
+
+/**
+ * ixgbe_update_mc_addr_list_generic - Updates MAC list of multicast addresses
+ * @hw: pointer to hardware structure
+ * @mc_addr_list: the list of new multicast addresses
+ * @mc_addr_count: number of addresses
+ * @pad: number of bytes between addresses in the list
+ *
+ * The given list replaces any existing list. Clears the MC addrs from receive
+ * address registers and the multicast table. Uses unsed receive address
+ * registers for the first multicast addresses, and hashes the rest into the
+ * multicast table.
+ **/
+static s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw,
+ u8 *mc_addr_list,
+ u32 mc_addr_count, u32 pad)
+{
+ u32 i;
+ u32 rar_entries = ixgbe_get_num_rx_addrs(hw);
+
+ /*
+ * Set the new number of MC addresses that we are being requested to
+ * use.
+ */
+ hw->addr_ctrl.num_mc_addrs = mc_addr_count;
+ hw->addr_ctrl.rar_used_count -= hw->addr_ctrl.mc_addr_in_rar_count;
+ hw->addr_ctrl.mc_addr_in_rar_count = 0;
+ hw->addr_ctrl.mta_in_use = 0;
+
+ /* Zero out the other receive addresses. */
+ DEBUGOUT("Clearing RAR[1-15]\n");
+ for (i = hw->addr_ctrl.rar_used_count; i < rar_entries; i++) {
+ IXGBE_WRITE_REG(hw, IXGBE_RAL(i), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_RAH(i), 0);
+ }
+
+ /* Clear the MTA */
+ DEBUGOUT(" Clearing MTA\n");
+ for (i = 0; i < IXGBE_MC_TBL_SIZE; i++)
+ IXGBE_WRITE_REG(hw, IXGBE_MTA(i), 0);
+
+ /* Add the new addresses */
+ for (i = 0; i < mc_addr_count; i++) {
+ DEBUGOUT(" Adding the multicast addresses:\n");
+ ixgbe_add_mc_addr(hw, mc_addr_list +
+ (i * (IXGBE_ETH_LENGTH_OF_ADDRESS + pad)));
+ }
+
+ /* Enable mta */
+ if (hw->addr_ctrl.mta_in_use > 0)
+ IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL,
+ IXGBE_MCSTCTRL_MFE | hw->mac.mc_filter_type);
+
+ DEBUGOUT("ixgbe_update_mc_addr_list_generic Complete\n");
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_enable_mc_generic - Enable multicast address in RAR
+ * @hw: pointer to hardware structure
+ *
+ * Enables multicast address in RAR and the use of the multicast hash table.
+ **/
+static s32 ixgbe_enable_mc_generic(struct ixgbe_hw *hw)
+{
+ u32 i;
+ struct ixgbe_addr_filter_info *a = &hw->addr_ctrl;
+
+ if (a->mc_addr_in_rar_count > 0)
+ for (i = (a->rar_used_count - a->mc_addr_in_rar_count);
+ i < a->rar_used_count; i++)
+ ixgbe_enable_rar(hw, i);
+
+ if (a->mta_in_use > 0)
+ IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL, IXGBE_MCSTCTRL_MFE |
+ hw->mac.mc_filter_type);
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_disable_mc_generic - Disable mutlicast address in RAR
+ * @hw: pointer to hardware structure
+ *
+ * Disables multicast address in RAR and the use of the multicast hash table.
+ **/
+static s32 ixgbe_disable_mc_generic(struct ixgbe_hw *hw)
+{
+ u32 i;
+ struct ixgbe_addr_filter_info *a = &hw->addr_ctrl;
+
+ if (a->mc_addr_in_rar_count > 0)
+ for (i = (a->rar_used_count - a->mc_addr_in_rar_count);
+ i < a->rar_used_count; i++)
+ ixgbe_disable_rar(hw, i);
+
+ if (a->mta_in_use > 0)
+ IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL, hw->mac.mc_filter_type);
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_clear_vfta_generic - Clear VLAN filter table
+ * @hw: pointer to hardware structure
+ *
+ * Clears the VLAN filer table, and the VMDq index associated with the filter
+ **/
+static s32 ixgbe_clear_vfta_generic(struct ixgbe_hw *hw)
+{
+ u32 offset;
+ u32 vlanbyte;
+
+ for (offset = 0; offset < IXGBE_VLAN_FILTER_TBL_SIZE; offset++)
+ IXGBE_WRITE_REG(hw, IXGBE_VFTA(offset), 0);
+
+ for (vlanbyte = 0; vlanbyte < 4; vlanbyte++)
+ for (offset = 0; offset < IXGBE_VLAN_FILTER_TBL_SIZE; offset++)
+ IXGBE_WRITE_REG(hw, IXGBE_VFTAVIND(vlanbyte, offset),
+ 0);
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_set_vfta_generic - Set VLAN filter table
+ * @hw: pointer to hardware structure
+ * @vlan: VLAN id to write to VLAN filter
+ * @vind: VMDq output index that maps queue to VLAN id in VFTA
+ * @vlan_on: boolean flag to turn on/off VLAN in VFTA
+ *
+ * Turn on/off specified VLAN in the VLAN filter table.
+ **/
+static s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind,
+ bool vlan_on)
+{
+ u32 VftaIndex;
+ u32 BitOffset;
+ u32 VftaReg;
+ u32 VftaByte;
+
+ /* Determine 32-bit word position in array */
+ VftaIndex = (vlan >> 5) & 0x7F; /* upper seven bits */
+
+ /* Determine the location of the (VMD) queue index */
+ VftaByte = ((vlan >> 3) & 0x03); /* bits (4:3) indicating byte array */
+ BitOffset = (vlan & 0x7) << 2; /* lower 3 bits indicate nibble */
+
+ /* Set the nibble for VMD queue index */
+ VftaReg = IXGBE_READ_REG(hw, IXGBE_VFTAVIND(VftaByte, VftaIndex));
+ VftaReg &= (~(0x0F << BitOffset));
+ VftaReg |= (vind << BitOffset);
+ IXGBE_WRITE_REG(hw, IXGBE_VFTAVIND(VftaByte, VftaIndex), VftaReg);
+
+ /* Determine the location of the bit for this VLAN id */
+ BitOffset = vlan & 0x1F; /* lower five bits */
+
+ VftaReg = IXGBE_READ_REG(hw, IXGBE_VFTA(VftaIndex));
+ if (vlan_on)
+ /* Turn on this VLAN id */
+ VftaReg |= (1 << BitOffset);
+ else
+ /* Turn off this VLAN id */
+ VftaReg &= ~(1 << BitOffset);
+ IXGBE_WRITE_REG(hw, IXGBE_VFTA(VftaIndex), VftaReg);
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_setup_fc_generic - Configure flow control settings
+ * @hw: pointer to hardware structure
+ * @packetbuf_num: packet buffer number (0-7)
+ *
+ * Configures the flow control settings based on SW configuration.
+ * This function is used for 802.3x flow control configuration only.
+ **/
+static s32 ixgbe_setup_fc_generic(struct ixgbe_hw *hw, s32 packetbuf_num)
+{
+ u32 frctl_reg;
+ u32 rmcs_reg;
+
+ if (packetbuf_num < 0 || packetbuf_num > 7) {
+ DEBUGOUT1("Invalid packet buffer number [%d], expected range is"
+ " 0-7\n", packetbuf_num);
+ }
+
+ frctl_reg = IXGBE_READ_REG(hw, IXGBE_FCTRL);
+ frctl_reg &= ~(IXGBE_FCTRL_RFCE | IXGBE_FCTRL_RPFCE);
+
+ rmcs_reg = IXGBE_READ_REG(hw, IXGBE_RMCS);
+ rmcs_reg &= ~(IXGBE_RMCS_TFCE_PRIORITY | IXGBE_RMCS_TFCE_802_3X);
+
+ /*
+ * We want to save off the original Flow Control configuration just in
+ * case we get disconnected and then reconnected into a different hub
+ * or switch with different Flow Control capabilities.
+ */
+ hw->fc.type = hw->fc.original_type;
+
+ /*
+ * The possible values of the "flow_control" parameter are:
+ * 0: Flow control is completely disabled
+ * 1: Rx flow control is enabled (we can receive pause frames but not
+ * send pause frames).
+ * 2: Tx flow control is enabled (we can send pause frames but we do not
+ * support receiving pause frames)
+ * 3: Both Rx and TX flow control (symmetric) are enabled.
+ * other: Invalid.
+ */
+ switch (hw->fc.type) {
+ case ixgbe_fc_none:
+ break;
+ case ixgbe_fc_rx_pause:
+ /*
+ * RX Flow control is enabled,
+ * and TX Flow control is disabled.
+ */
+ frctl_reg |= IXGBE_FCTRL_RFCE;
+ break;
+ case ixgbe_fc_tx_pause:
+ /*
+ * TX Flow control is enabled, and RX Flow control is disabled,
+ * by a software over-ride.
+ */
+ rmcs_reg |= IXGBE_RMCS_TFCE_802_3X;
+ break;
+ case ixgbe_fc_full:
+ /*
+ * Flow control (both RX and TX) is enabled by a software
+ * over-ride.
+ */
+ frctl_reg |= IXGBE_FCTRL_RFCE;
+ rmcs_reg |= IXGBE_RMCS_TFCE_802_3X;
+ break;
+ default:
+ /* We should never get here. The value should be 0-3. */
+ DEBUGOUT("Flow control param set incorrectly\n");
+ break;
+ }
+
+ /* Enable 802.3x based flow control settings. */
+ IXGBE_WRITE_REG(hw, IXGBE_FCTRL, frctl_reg);
+ IXGBE_WRITE_REG(hw, IXGBE_RMCS, rmcs_reg);
+
+ /*
+ * We need to set up the Receive Threshold high and low water
+ * marks as well as (optionally) enabling the transmission of
+ * XON frames.
+ */
+ if (hw->fc.type & ixgbe_fc_tx_pause) {
+ if (hw->fc.send_xon) {
+ IXGBE_WRITE_REG(hw, IXGBE_FCRTL(packetbuf_num),
+ (hw->fc.low_water | IXGBE_FCRTL_XONE));
+ } else {
+ IXGBE_WRITE_REG(hw, IXGBE_FCRTL(packetbuf_num),
+ hw->fc.low_water);
+ }
+ IXGBE_WRITE_REG(hw, IXGBE_FCRTH(packetbuf_num),
+ (hw->fc.high_water)|IXGBE_FCRTH_FCEN);
+ }
+
+ IXGBE_WRITE_REG(hw, IXGBE_FCTTV(0), hw->fc.pause_time);
+ IXGBE_WRITE_REG(hw, IXGBE_FCRTV, (hw->fc.pause_time >> 1));
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_disable_pcie_master - Disable PCI-express master access
+ * @hw: pointer to hardware structure
+ *
+ * Disables PCI-Express master access and verifies there are no pending
+ * requests. IXGBE_ERR_MASTER_REQUESTS_PENDING is returned if master disable
+ * bit hasn't caused the master requests to be disabled, else IXGBE_SUCCESS
+ * is returned signifying master requests disabled.
+ **/
+s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw)
+{
+ u32 ctrl;
+ s32 i;
+ s32 status = IXGBE_ERR_MASTER_REQUESTS_PENDING;
+
+ ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
+ ctrl |= IXGBE_CTRL_GIO_DIS;
+ IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl);
+
+ for (i = 0; i < IXGBE_PCI_MASTER_DISABLE_TIMEOUT; i++) {
+ if (!(IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_GIO)) {
+ status = IXGBE_SUCCESS;
+ break;
+ }
+ udelay(100);
+ }
+
+ return status;
+}
+
+
+/**
+ * ixgbe_acquire_swfw_sync - Aquire SWFW semaphore
+ * @hw: pointer to hardware structure
+ * @mask: Mask to specify wich semaphore to acquire
+ *
+ * Aquires the SWFW semaphore throught the GSSR register for the specified
+ * function (CSR, PHY0, PHY1, EEPROM, Flash)
+ **/
+s32 ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u16 mask)
+{
+ u32 gssr;
+ u32 swmask = mask;
+ u32 fwmask = mask << 5;
+ s32 timeout = 200;
+
+ while (timeout) {
+ if (ixgbe_get_eeprom_semaphore(hw))
+ return -IXGBE_ERR_SWFW_SYNC;
+
+ gssr = IXGBE_READ_REG(hw, IXGBE_GSSR);
+ if (!(gssr & (fwmask | swmask)))
+ break;
+
+ /*
+ * Firmware currently using resource (fwmask) or other software
+ * thread currently using resource (swmask)
+ */
+ ixgbe_release_eeprom_semaphore(hw);
+ msleep(5);
+ timeout--;
+ }
+
+ if (!timeout) {
+ DEBUGOUT("Driver can't access resource, GSSR timeout.\n");
+ return -IXGBE_ERR_SWFW_SYNC;
+ }
+
+ gssr |= swmask;
+ IXGBE_WRITE_REG(hw, IXGBE_GSSR, gssr);
+
+ ixgbe_release_eeprom_semaphore(hw);
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_release_swfw_sync - Release SWFW semaphore
+ * @hw: pointer to hardware structure
+ * @mask: Mask to specify wich semaphore to release
+ *
+ * Releases the SWFW semaphore throught the GSSR register for the specified
+ * function (CSR, PHY0, PHY1, EEPROM, Flash)
+ **/
+void ixgbe_release_swfw_sync(struct ixgbe_hw *hw, u16 mask)
+{
+ u32 gssr;
+ u32 swmask = mask;
+
+ ixgbe_get_eeprom_semaphore(hw);
+
+ gssr = IXGBE_READ_REG(hw, IXGBE_GSSR);
+ gssr &= ~swmask;
+ IXGBE_WRITE_REG(hw, IXGBE_GSSR, gssr);
+
+ ixgbe_release_eeprom_semaphore(hw);
+}
+
diff --git a/drivers/net/ixgbe/ixgbe_common.h b/drivers/net/ixgbe/ixgbe_common.h
new file mode 100644
index 0000000..d3de048
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_common.h
@@ -0,0 +1,43 @@
+/*******************************************************************************
+
+ Intel PRO/10GbE PCI Express Linux driver
+ Copyright(c) 1999 - 2007 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#ifndef _IXGBE_COMMON_H_
+#define _IXGBE_COMMON_H_
+
+#include "ixgbe_type.h"
+
+s32 ixgbe_assign_func_pointers_generic(struct ixgbe_hw *hw);
+
+s32 ixgbe_blink_led_start_generic(struct ixgbe_hw *hw, u32 index);
+s32 ixgbe_blink_led_stop_generic(struct ixgbe_hw *hw, u32 index);
+
+s32 ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u16 mask);
+void ixgbe_release_swfw_sync(struct ixgbe_hw *hw, u16 mask);
+s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw);
+
+#endif /* IXGBE_COMMON */
diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c
new file mode 100644
index 0000000..3f632f5
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_ethtool.c
@@ -0,0 +1,931 @@
+/*******************************************************************************
+
+ Intel PRO/10GbE PCI Express Linux driver
+ Copyright(c) 1999 - 2007 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+/* ethtool support for ixgbe */
+
+#include "ixgbe.h"
+
+#include <linux/uaccess.h>
+
+#define IXGBE_ALL_RAR_ENTRIES 16
+
+struct ixgbe_stats {
+ char stat_string[ETH_GSTRING_LEN];
+ int sizeof_stat;
+ int stat_offset;
+};
+
+#define IXGBE_STAT(m) sizeof(((struct ixgbe_adapter *)0)->m), \
+ offsetof(struct ixgbe_adapter, m)
+static struct ixgbe_stats ixgbe_gstrings_stats[] = {
+ {"rx_packets", IXGBE_STAT(net_stats.rx_packets)},
+ {"tx_packets", IXGBE_STAT(net_stats.tx_packets)},
+ {"rx_bytes", IXGBE_STAT(net_stats.rx_bytes)},
+ {"tx_bytes", IXGBE_STAT(net_stats.tx_bytes)},
+ {"lsc_int", IXGBE_STAT(lsc_int)},
+ {"tx_busy", IXGBE_STAT(tx_busy)},
+ {"non_eop_descs", IXGBE_STAT(non_eop_descs)},
+ {"rx_errors", IXGBE_STAT(net_stats.rx_errors)},
+ {"tx_errors", IXGBE_STAT(net_stats.tx_errors)},
+ {"rx_dropped", IXGBE_STAT(net_stats.rx_dropped)},
+ {"tx_dropped", IXGBE_STAT(net_stats.tx_dropped)},
+ {"multicast", IXGBE_STAT(net_stats.multicast)},
+ {"broadcast", IXGBE_STAT(stats.bprc)},
+ {"rx_no_buffer_count", IXGBE_STAT(stats.rnbc[0]) },
+ {"collisions", IXGBE_STAT(net_stats.collisions)},
+ {"rx_over_errors", IXGBE_STAT(net_stats.rx_over_errors)},
+ {"rx_crc_errors", IXGBE_STAT(net_stats.rx_crc_errors)},
+ {"rx_frame_errors", IXGBE_STAT(net_stats.rx_frame_errors)},
+ {"rx_fifo_errors", IXGBE_STAT(net_stats.rx_fifo_errors)},
+ {"rx_missed_errors", IXGBE_STAT(net_stats.rx_missed_errors)},
+ {"tx_aborted_errors", IXGBE_STAT(net_stats.tx_aborted_errors)},
+ {"tx_carrier_errors", IXGBE_STAT(net_stats.tx_carrier_errors)},
+ {"tx_fifo_errors", IXGBE_STAT(net_stats.tx_fifo_errors)},
+ {"tx_heartbeat_errors", IXGBE_STAT(net_stats.tx_heartbeat_errors)},
+ {"tx_timeout_count", IXGBE_STAT(tx_timeout_count)},
+ {"tx_restart_queue", IXGBE_STAT(restart_queue)},
+ {"rx_long_length_errors", IXGBE_STAT(stats.roc)},
+ {"rx_short_length_errors", IXGBE_STAT(stats.ruc)},
+ {"tx_tcp4_seg_ctxt", IXGBE_STAT(hw_tso_ctxt)},
+ {"tx_tcp6_seg_ctxt", IXGBE_STAT(hw_tso6_ctxt)},
+ {"tx_flow_control_xon", IXGBE_STAT(stats.lxontxc)},
+ {"rx_flow_control_xon", IXGBE_STAT(stats.lxonrxc)},
+ {"tx_flow_control_xoff", IXGBE_STAT(stats.lxofftxc)},
+ {"rx_flow_control_xoff", IXGBE_STAT(stats.lxoffrxc)},
+ {"rx_csum_offload_good", IXGBE_STAT(hw_csum_rx_good)},
+ {"rx_csum_offload_errors", IXGBE_STAT(hw_csum_rx_error)},
+ {"tx_csum_offload_ctxt", IXGBE_STAT(hw_csum_tx_good)},
+ {"rx_header_split", IXGBE_STAT(rx_hdr_split)},
+ {"alloc_rx_page_failed", IXGBE_STAT(alloc_rx_page_failed)},
+ {"alloc_rx_buff_failed", IXGBE_STAT(alloc_rx_buff_failed)},
+};
+
+#define IXGBE_QUEUE_STATS_LEN ( \
+ (((struct ixgbe_adapter *)netdev->priv)->num_tx_queues + \
+ ((struct ixgbe_adapter *)netdev->priv)->num_rx_queues) * \
+ (sizeof(struct ixgbe_queue_stats) / sizeof(u64)) \
+ )
+#define IXGBE_GLOBAL_STATS_LEN \
+ sizeof(ixgbe_gstrings_stats) / sizeof(struct ixgbe_stats)
+#define IXGBE_STATS_LEN (IXGBE_GLOBAL_STATS_LEN + IXGBE_QUEUE_STATS_LEN)
+
+static int ixgbe_get_settings(struct net_device *netdev,
+ struct ethtool_cmd *ecmd)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ ecmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE);
+ ecmd->advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE);
+ ecmd->port = PORT_FIBRE;
+ ecmd->transceiver = XCVR_EXTERNAL;
+
+ if (netif_carrier_ok(adapter->netdev)) {
+ ecmd->speed = SPEED_10000;
+ ecmd->duplex = DUPLEX_FULL;
+ } else {
+ ecmd->speed = -1;
+ ecmd->duplex = -1;
+ }
+
+ ecmd->autoneg = AUTONEG_DISABLE;
+ return 0;
+}
+
+static int ixgbe_set_settings(struct net_device *netdev,
+ struct ethtool_cmd *ecmd)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ if (ecmd->autoneg == AUTONEG_ENABLE ||
+ ecmd->speed + ecmd->duplex != SPEED_10000 + DUPLEX_FULL)
+ return -EINVAL;
+
+ if (netif_running(adapter->netdev)) {
+ ixgbe_down(adapter);
+ ixgbe_reset(adapter);
+ ixgbe_up(adapter);
+ } else {
+ ixgbe_reset(adapter);
+ }
+
+ return 0;
+}
+
+static void ixgbe_get_pauseparam(struct net_device *netdev,
+ struct ethtool_pauseparam *pause)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ struct ixgbe_hw *hw = &adapter->hw;
+
+ pause->autoneg = AUTONEG_DISABLE;
+
+ if (hw->fc.type == ixgbe_fc_rx_pause) {
+ pause->rx_pause = 1;
+ } else if (hw->fc.type == ixgbe_fc_tx_pause) {
+ pause->tx_pause = 1;
+ } else if (hw->fc.type == ixgbe_fc_full) {
+ pause->rx_pause = 1;
+ pause->tx_pause = 1;
+ }
+}
+
+static int ixgbe_set_pauseparam(struct net_device *netdev,
+ struct ethtool_pauseparam *pause)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ struct ixgbe_hw *hw = &adapter->hw;
+
+ if (pause->autoneg == AUTONEG_ENABLE)
+ return -EINVAL;
+
+ if (pause->rx_pause && pause->tx_pause)
+ hw->fc.type = ixgbe_fc_full;
+ else if (pause->rx_pause && !pause->tx_pause)
+ hw->fc.type = ixgbe_fc_rx_pause;
+ else if (!pause->rx_pause && pause->tx_pause)
+ hw->fc.type = ixgbe_fc_tx_pause;
+ else if (!pause->rx_pause && !pause->tx_pause)
+ hw->fc.type = ixgbe_fc_none;
+
+ hw->fc.original_type = hw->fc.type;
+
+ if (netif_running(adapter->netdev)) {
+ ixgbe_down(adapter);
+ ixgbe_up(adapter);
+ } else {
+ ixgbe_reset(adapter);
+ }
+
+ return 0;
+}
+
+static u32 ixgbe_get_rx_csum(struct net_device *netdev)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ return (adapter->flags & IXGBE_FLAG_RX_CSUM_ENABLED);
+}
+
+static int ixgbe_set_rx_csum(struct net_device *netdev, u32 data)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ if (data)
+ adapter->flags |= IXGBE_FLAG_RX_CSUM_ENABLED;
+ else
+ adapter->flags &= ~IXGBE_FLAG_RX_CSUM_ENABLED;
+
+ if (netif_running(netdev)) {
+ ixgbe_down(adapter);
+ ixgbe_up(adapter);
+ } else {
+ ixgbe_reset(adapter);
+ }
+
+ return 0;
+}
+
+static u32 ixgbe_get_tx_csum(struct net_device *netdev)
+{
+ return (netdev->features & NETIF_F_HW_CSUM) != 0;
+}
+
+static int ixgbe_set_tx_csum(struct net_device *netdev, u32 data)
+{
+ if (data)
+ netdev->features |= NETIF_F_HW_CSUM;
+ else
+ netdev->features &= ~NETIF_F_HW_CSUM;
+
+ return 0;
+}
+
+static int ixgbe_set_tso(struct net_device *netdev, u32 data)
+{
+
+ if (data) {
+ netdev->features |= NETIF_F_TSO;
+ netdev->features |= NETIF_F_TSO6;
+ } else {
+ netdev->features &= ~NETIF_F_TSO;
+ netdev->features &= ~NETIF_F_TSO6;
+ }
+ return 0;
+}
+
+static int ixgbe_get_coalesce(struct net_device *netdev,
+ struct ethtool_coalesce *ecmd)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ if (adapter->rx_eitr == 0)
+ ecmd->rx_coalesce_usecs = 0;
+ else
+ ecmd->rx_coalesce_usecs = 1000000 / adapter->rx_eitr;
+
+ if (adapter->tx_eitr == 0)
+ ecmd->tx_coalesce_usecs = 0;
+ else
+ ecmd->tx_coalesce_usecs = 1000000 / adapter->tx_eitr;
+
+ return 0;
+}
+
+static int ixgbe_set_coalesce(struct net_device *netdev,
+ struct ethtool_coalesce *ecmd)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ if ((ecmd->rx_coalesce_usecs > IXGBE_ITR_MAX_USECS) ||
+ ((ecmd->rx_coalesce_usecs > 0) &&
+ (ecmd->rx_coalesce_usecs < IXGBE_ITR_MIN_USECS)))
+ return -EINVAL;
+ if ((ecmd->tx_coalesce_usecs > IXGBE_ITR_MAX_USECS) ||
+ ((ecmd->tx_coalesce_usecs > 0) &&
+ (ecmd->tx_coalesce_usecs < IXGBE_ITR_MIN_USECS)))
+ return -EINVAL;
+
+ /* convert to rate of irq's per second */
+ if (ecmd->rx_coalesce_usecs == 0)
+ adapter->rx_eitr = 0;
+ else
+ adapter->rx_eitr = (1000000 / ecmd->rx_coalesce_usecs);
+
+ if (ecmd->tx_coalesce_usecs == 0)
+ adapter->tx_eitr = 0;
+ else
+ adapter->tx_eitr = (1000000 / ecmd->tx_coalesce_usecs);
+
+ if (netif_running(netdev)) {
+ ixgbe_down(adapter);
+ ixgbe_up(adapter);
+ }
+
+ return 0;
+}
+
+static u32 ixgbe_get_msglevel(struct net_device *netdev)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ return adapter->msg_enable;
+}
+
+static void ixgbe_set_msglevel(struct net_device *netdev, u32 data)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ adapter->msg_enable = data;
+}
+
+static int ixgbe_get_regs_len(struct net_device *netdev)
+{
+#define IXGBE_REGS_LEN 1127*sizeof(u32)
+ return IXGBE_REGS_LEN;
+}
+
+#define IXGBE_GET_STAT(_A_, _R_) _A_->stats._R_
+
+static void ixgbe_get_regs(struct net_device *netdev,
+ struct ethtool_regs *regs, void *p)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 *regs_buff = p;
+ u8 i;
+
+ memset(p, 0, IXGBE_REGS_LEN * sizeof(u32));
+
+ regs->version = (1 << 24) | hw->revision_id << 16 | hw->device_id;
+
+ /* General Registers */
+ regs_buff[0] = IXGBE_READ_REG(hw, IXGBE_CTRL);
+ regs_buff[1] = IXGBE_READ_REG(hw, IXGBE_STATUS);
+ regs_buff[2] = IXGBE_READ_REG(hw, IXGBE_CTRL_EXT);
+ regs_buff[3] = IXGBE_READ_REG(hw, IXGBE_ESDP);
+ regs_buff[4] = IXGBE_READ_REG(hw, IXGBE_EODSDP);
+ regs_buff[5] = IXGBE_READ_REG(hw, IXGBE_LEDCTL);
+ regs_buff[6] = IXGBE_READ_REG(hw, IXGBE_FRTIMER);
+ regs_buff[7] = IXGBE_READ_REG(hw, IXGBE_TCPTIMER);
+
+ /* NVM Register */
+ regs_buff[8] = IXGBE_READ_REG(hw, IXGBE_EEC);
+ regs_buff[9] = IXGBE_READ_REG(hw, IXGBE_EERD);
+ regs_buff[10] = IXGBE_READ_REG(hw, IXGBE_FLA);
+ regs_buff[11] = IXGBE_READ_REG(hw, IXGBE_EEMNGCTL);
+ regs_buff[12] = IXGBE_READ_REG(hw, IXGBE_EEMNGDATA);
+ regs_buff[13] = IXGBE_READ_REG(hw, IXGBE_FLMNGCTL);
+ regs_buff[14] = IXGBE_READ_REG(hw, IXGBE_FLMNGDATA);
+ regs_buff[15] = IXGBE_READ_REG(hw, IXGBE_FLMNGCNT);
+ regs_buff[16] = IXGBE_READ_REG(hw, IXGBE_FLOP);
+ regs_buff[17] = IXGBE_READ_REG(hw, IXGBE_GRC);
+
+ /* Interrupt */
+ regs_buff[18] = IXGBE_READ_REG(hw, IXGBE_EICR);
+ regs_buff[19] = IXGBE_READ_REG(hw, IXGBE_EICS);
+ regs_buff[20] = IXGBE_READ_REG(hw, IXGBE_EIMS);
+ regs_buff[21] = IXGBE_READ_REG(hw, IXGBE_EIMC);
+ regs_buff[22] = IXGBE_READ_REG(hw, IXGBE_EIAC);
+ regs_buff[23] = IXGBE_READ_REG(hw, IXGBE_EIAM);
+ regs_buff[24] = IXGBE_READ_REG(hw, IXGBE_EITR(0));
+ regs_buff[25] = IXGBE_READ_REG(hw, IXGBE_IVAR(0));
+ regs_buff[26] = IXGBE_READ_REG(hw, IXGBE_MSIXT);
+ regs_buff[27] = IXGBE_READ_REG(hw, IXGBE_MSIXPBA);
+ regs_buff[28] = IXGBE_READ_REG(hw, IXGBE_PBACL);
+ regs_buff[29] = IXGBE_READ_REG(hw, IXGBE_GPIE);
+
+ /* Flow Control */
+ regs_buff[30] = IXGBE_READ_REG(hw, IXGBE_PFCTOP);
+ regs_buff[31] = IXGBE_READ_REG(hw, IXGBE_FCTTV(0));
+ regs_buff[32] = IXGBE_READ_REG(hw, IXGBE_FCTTV(1));
+ regs_buff[33] = IXGBE_READ_REG(hw, IXGBE_FCTTV(2));
+ regs_buff[34] = IXGBE_READ_REG(hw, IXGBE_FCTTV(3));
+ for (i = 0; i < 8; i++)
+ regs_buff[35 + i] = IXGBE_READ_REG(hw, IXGBE_FCRTL(i));
+ for (i = 0; i < 8; i++)
+ regs_buff[43 + i] = IXGBE_READ_REG(hw, IXGBE_FCRTH(i));
+ regs_buff[51] = IXGBE_READ_REG(hw, IXGBE_FCRTV);
+ regs_buff[52] = IXGBE_READ_REG(hw, IXGBE_TFCS);
+
+ /* Receive DMA */
+ for (i = 0; i < 64; i++)
+ regs_buff[53 + i] = IXGBE_READ_REG(hw, IXGBE_RDBAL(i));
+ for (i = 0; i < 64; i++)
+ regs_buff[117 + i] = IXGBE_READ_REG(hw, IXGBE_RDBAH(i));
+ for (i = 0; i < 64; i++)
+ regs_buff[181 + i] = IXGBE_READ_REG(hw, IXGBE_RDLEN(i));
+ for (i = 0; i < 64; i++)
+ regs_buff[245 + i] = IXGBE_READ_REG(hw, IXGBE_RDH(i));
+ for (i = 0; i < 64; i++)
+ regs_buff[309 + i] = IXGBE_READ_REG(hw, IXGBE_RDT(i));
+ for (i = 0; i < 64; i++)
+ regs_buff[373 + i] = IXGBE_READ_REG(hw, IXGBE_RXDCTL(i));
+ for (i = 0; i < 16; i++)
+ regs_buff[437 + i] = IXGBE_READ_REG(hw, IXGBE_SRRCTL(i));
+ for (i = 0; i < 16; i++)
+ regs_buff[453 + i] = IXGBE_READ_REG(hw, IXGBE_DCA_RXCTRL(i));
+ regs_buff[469] = IXGBE_READ_REG(hw, IXGBE_RDRXCTL);
+ for (i = 0; i < 8; i++ )
+ regs_buff[470 + i] = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i));
+ regs_buff[478] = IXGBE_READ_REG(hw, IXGBE_RXCTRL);
+ regs_buff[479] = IXGBE_READ_REG(hw, IXGBE_DROPEN);
+
+ /* Receive */
+ regs_buff[480] = IXGBE_READ_REG(hw, IXGBE_RXCSUM);
+ regs_buff[481] = IXGBE_READ_REG(hw, IXGBE_RFCTL);
+ for (i = 0; i < 16; i++)
+ regs_buff[482 + i] = IXGBE_READ_REG(hw, IXGBE_RAL(i));
+ for (i = 0; i < 16; i++)
+ regs_buff[498 + i] = IXGBE_READ_REG(hw, IXGBE_RAH(i));
+ regs_buff[514] = IXGBE_READ_REG(hw, IXGBE_PSRTYPE);
+ regs_buff[515] = IXGBE_READ_REG(hw, IXGBE_FCTRL);
+ regs_buff[516] = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
+ regs_buff[517] = IXGBE_READ_REG(hw, IXGBE_MCSTCTRL);
+ regs_buff[518] = IXGBE_READ_REG(hw, IXGBE_MRQC);
+ regs_buff[519] = IXGBE_READ_REG(hw, IXGBE_VMD_CTL);
+ for (i = 0; i < 8; i++)
+ regs_buff[520 + i] = IXGBE_READ_REG(hw, IXGBE_IMIR(i));
+ for (i = 0; i < 8; i++)
+ regs_buff[528 + i] = IXGBE_READ_REG(hw, IXGBE_IMIREXT(i));
+ regs_buff[536] = IXGBE_READ_REG(hw, IXGBE_IMIRVP);
+
+ /* Transmit */
+ for (i = 0; i < 32; i++)
+ regs_buff[537 + i] = IXGBE_READ_REG(hw, IXGBE_TDBAL(i));
+ for (i = 0; i < 32; i++)
+ regs_buff[569 + i] = IXGBE_READ_REG(hw, IXGBE_TDBAH(i));
+ for (i = 0; i < 32; i++)
+ regs_buff[601 + i] = IXGBE_READ_REG(hw, IXGBE_TDLEN(i));
+ for (i = 0; i < 32; i++)
+ regs_buff[633 + i] = IXGBE_READ_REG(hw, IXGBE_TDH(i));
+ for (i = 0; i < 32; i++)
+ regs_buff[665 + i] = IXGBE_READ_REG(hw, IXGBE_TDT(i));
+ for (i = 0; i < 32; i++)
+ regs_buff[697 + i] = IXGBE_READ_REG(hw, IXGBE_TXDCTL(i));
+ for (i = 0; i < 32; i++)
+ regs_buff[729 + i] = IXGBE_READ_REG(hw, IXGBE_TDWBAL(i));
+ for (i = 0; i < 32; i++)
+ regs_buff[761 + i] = IXGBE_READ_REG(hw, IXGBE_TDWBAH(i));
+ regs_buff[793] = IXGBE_READ_REG(hw, IXGBE_DTXCTL);
+ for (i = 0; i < 16; i++)
+ regs_buff[794 + i] = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL(i));
+ regs_buff[810] = IXGBE_READ_REG(hw, IXGBE_TIPG);
+ for (i = 0; i < 8; i++)
+ regs_buff[811 + i] = IXGBE_READ_REG(hw, IXGBE_TXPBSIZE(i));
+ regs_buff[819] = IXGBE_READ_REG(hw, IXGBE_MNGTXMAP);
+
+ /* Wake Up */
+ regs_buff[820] = IXGBE_READ_REG(hw, IXGBE_WUC);
+ regs_buff[821] = IXGBE_READ_REG(hw, IXGBE_WUFC);
+ regs_buff[822] = IXGBE_READ_REG(hw, IXGBE_WUS);
+ regs_buff[823] = IXGBE_READ_REG(hw, IXGBE_IPAV);
+ regs_buff[824] = IXGBE_READ_REG(hw, IXGBE_IP4AT);
+ regs_buff[825] = IXGBE_READ_REG(hw, IXGBE_IP6AT);
+ regs_buff[826] = IXGBE_READ_REG(hw, IXGBE_WUPL);
+ regs_buff[827] = IXGBE_READ_REG(hw, IXGBE_WUPM);
+ regs_buff[828] = IXGBE_READ_REG(hw, IXGBE_FHFT);
+
+ /* DCE */
+ regs_buff[829] = IXGBE_READ_REG(hw, IXGBE_RMCS);
+ regs_buff[830] = IXGBE_READ_REG(hw, IXGBE_DPMCS);
+ regs_buff[831] = IXGBE_READ_REG(hw, IXGBE_PDPMCS);
+ regs_buff[832] = IXGBE_READ_REG(hw, IXGBE_RUPPBMR);
+ for (i = 0; i < 8; i++)
+ regs_buff[833 + i] = IXGBE_READ_REG(hw, IXGBE_RT2CR(i));
+ for (i = 0; i < 8; i++)
+ regs_buff[841 + i] = IXGBE_READ_REG(hw, IXGBE_RT2SR(i));
+ for (i = 0; i < 8; i++)
+ regs_buff[849 + i] = IXGBE_READ_REG(hw, IXGBE_TDTQ2TCCR(i));
+ for (i = 0; i < 8; i++)
+ regs_buff[857 + i] = IXGBE_READ_REG(hw, IXGBE_TDTQ2TCSR(i));
+ for (i = 0; i < 8; i++)
+ regs_buff[865 + i] = IXGBE_READ_REG(hw, IXGBE_TDPT2TCCR(i));
+ for (i = 0; i < 8; i++)
+ regs_buff[873 + i] = IXGBE_READ_REG(hw, IXGBE_TDPT2TCSR(i));
+
+ /* Statistics */
+ regs_buff[881] = IXGBE_GET_STAT(adapter, crcerrs);
+ regs_buff[882] = IXGBE_GET_STAT(adapter, illerrc);
+ regs_buff[883] = IXGBE_GET_STAT(adapter, errbc);
+ regs_buff[884] = IXGBE_GET_STAT(adapter, mspdc);
+ for (i = 0; i < 8; i++)
+ regs_buff[885 + i] = IXGBE_GET_STAT(adapter, mpc[i]);
+ regs_buff[893] = IXGBE_GET_STAT(adapter, mlfc);
+ regs_buff[894] = IXGBE_GET_STAT(adapter, mrfc);
+ regs_buff[895] = IXGBE_GET_STAT(adapter, rlec);
+ regs_buff[896] = IXGBE_GET_STAT(adapter, lxontxc);
+ regs_buff[897] = IXGBE_GET_STAT(adapter, lxonrxc);
+ regs_buff[898] = IXGBE_GET_STAT(adapter, lxofftxc);
+ regs_buff[899] = IXGBE_GET_STAT(adapter, lxoffrxc);
+ for (i = 0; i < 8; i++)
+ regs_buff[900 + i] = IXGBE_GET_STAT(adapter, pxontxc[i]);
+ for (i = 0; i < 8; i++)
+ regs_buff[908 + i] = IXGBE_GET_STAT(adapter, pxonrxc[i]);
+ for (i = 0; i < 8; i++)
+ regs_buff[916 + i] = IXGBE_GET_STAT(adapter, pxofftxc[i]);
+ for (i = 0; i < 8; i++)
+ regs_buff[924 + i] = IXGBE_GET_STAT(adapter, pxoffrxc[i]);
+ regs_buff[932] = IXGBE_GET_STAT(adapter, prc64);
+ regs_buff[933] = IXGBE_GET_STAT(adapter, prc127);
+ regs_buff[934] = IXGBE_GET_STAT(adapter, prc255);
+ regs_buff[935] = IXGBE_GET_STAT(adapter, prc511);
+ regs_buff[936] = IXGBE_GET_STAT(adapter, prc1023);
+ regs_buff[937] = IXGBE_GET_STAT(adapter, prc1522);
+ regs_buff[938] = IXGBE_GET_STAT(adapter, gprc);
+ regs_buff[939] = IXGBE_GET_STAT(adapter, bprc);
+ regs_buff[940] = IXGBE_GET_STAT(adapter, mprc);
+ regs_buff[941] = IXGBE_GET_STAT(adapter, gptc);
+ regs_buff[942] = IXGBE_GET_STAT(adapter, gorc);
+ regs_buff[944] = IXGBE_GET_STAT(adapter, gotc);
+ for (i = 0; i < 8; i++)
+ regs_buff[946 + i] = IXGBE_GET_STAT(adapter, rnbc[i]);
+ regs_buff[954] = IXGBE_GET_STAT(adapter, ruc);
+ regs_buff[955] = IXGBE_GET_STAT(adapter, rfc);
+ regs_buff[956] = IXGBE_GET_STAT(adapter, roc);
+ regs_buff[957] = IXGBE_GET_STAT(adapter, rjc);
+ regs_buff[958] = IXGBE_GET_STAT(adapter, mngprc);
+ regs_buff[959] = IXGBE_GET_STAT(adapter, mngpdc);
+ regs_buff[960] = IXGBE_GET_STAT(adapter, mngptc);
+ regs_buff[961] = IXGBE_GET_STAT(adapter, tor);
+ regs_buff[963] = IXGBE_GET_STAT(adapter, tpr);
+ regs_buff[964] = IXGBE_GET_STAT(adapter, tpt);
+ regs_buff[965] = IXGBE_GET_STAT(adapter, ptc64);
+ regs_buff[966] = IXGBE_GET_STAT(adapter, ptc127);
+ regs_buff[967] = IXGBE_GET_STAT(adapter, ptc255);
+ regs_buff[968] = IXGBE_GET_STAT(adapter, ptc511);
+ regs_buff[969] = IXGBE_GET_STAT(adapter, ptc1023);
+ regs_buff[970] = IXGBE_GET_STAT(adapter, ptc1522);
+ regs_buff[971] = IXGBE_GET_STAT(adapter, mptc);
+ regs_buff[972] = IXGBE_GET_STAT(adapter, bptc);
+ regs_buff[973] = IXGBE_GET_STAT(adapter, xec);
+ for (i = 0; i < 16; i++)
+ regs_buff[974 + i] = IXGBE_GET_STAT(adapter, qprc[i]);
+ for (i = 0; i < 16; i++)
+ regs_buff[990 + i] = IXGBE_GET_STAT(adapter, qptc[i]);
+ for (i = 0; i < 16; i++)
+ regs_buff[1006 + i] = IXGBE_GET_STAT(adapter, qbrc[i]);
+ for (i = 0; i < 16; i++)
+ regs_buff[1022 + i] = IXGBE_GET_STAT(adapter, qbtc[i]);
+
+ /* MAC */
+ regs_buff[1038] = IXGBE_READ_REG(hw, IXGBE_PCS1GCFIG);
+ regs_buff[1039] = IXGBE_READ_REG(hw, IXGBE_PCS1GLCTL);
+ regs_buff[1040] = IXGBE_READ_REG(hw, IXGBE_PCS1GLSTA);
+ regs_buff[1041] = IXGBE_READ_REG(hw, IXGBE_PCS1GDBG0);
+ regs_buff[1042] = IXGBE_READ_REG(hw, IXGBE_PCS1GDBG1);
+ regs_buff[1043] = IXGBE_READ_REG(hw, IXGBE_PCS1GANA);
+ regs_buff[1044] = IXGBE_READ_REG(hw, IXGBE_PCS1GANLP);
+ regs_buff[1045] = IXGBE_READ_REG(hw, IXGBE_PCS1GANNP);
+ regs_buff[1046] = IXGBE_READ_REG(hw, IXGBE_PCS1GANLPNP);
+ regs_buff[1047] = IXGBE_READ_REG(hw, IXGBE_HLREG0);
+ regs_buff[1048] = IXGBE_READ_REG(hw, IXGBE_HLREG1);
+ regs_buff[1049] = IXGBE_READ_REG(hw, IXGBE_PAP);
+ regs_buff[1050] = IXGBE_READ_REG(hw, IXGBE_MACA);
+ regs_buff[1051] = IXGBE_READ_REG(hw, IXGBE_APAE);
+ regs_buff[1052] = IXGBE_READ_REG(hw, IXGBE_ARD);
+ regs_buff[1053] = IXGBE_READ_REG(hw, IXGBE_AIS);
+ regs_buff[1054] = IXGBE_READ_REG(hw, IXGBE_MSCA);
+ regs_buff[1055] = IXGBE_READ_REG(hw, IXGBE_MSRWD);
+ regs_buff[1056] = IXGBE_READ_REG(hw, IXGBE_MLADD);
+ regs_buff[1057] = IXGBE_READ_REG(hw, IXGBE_MHADD);
+ regs_buff[1058] = IXGBE_READ_REG(hw, IXGBE_TREG);
+ regs_buff[1059] = IXGBE_READ_REG(hw, IXGBE_PCSS1);
+ regs_buff[1060] = IXGBE_READ_REG(hw, IXGBE_PCSS2);
+ regs_buff[1061] = IXGBE_READ_REG(hw, IXGBE_XPCSS);
+ regs_buff[1062] = IXGBE_READ_REG(hw, IXGBE_SERDESC);
+ regs_buff[1063] = IXGBE_READ_REG(hw, IXGBE_MACS);
+ regs_buff[1064] = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+ regs_buff[1065] = IXGBE_READ_REG(hw, IXGBE_LINKS);
+ regs_buff[1066] = IXGBE_READ_REG(hw, IXGBE_AUTOC2);
+ regs_buff[1067] = IXGBE_READ_REG(hw, IXGBE_AUTOC3);
+ regs_buff[1068] = IXGBE_READ_REG(hw, IXGBE_ANLP1);
+ regs_buff[1069] = IXGBE_READ_REG(hw, IXGBE_ANLP2);
+ regs_buff[1070] = IXGBE_READ_REG(hw, IXGBE_ATLASCTL);
+
+ /* Diagnostic */
+ regs_buff[1071] = IXGBE_READ_REG(hw, IXGBE_RDSTATCTL);
+ for (i = 0; i < 8; i++)
+ regs_buff[1072] = IXGBE_READ_REG(hw, IXGBE_RDSTAT(i));
+ regs_buff[1080] = IXGBE_READ_REG(hw, IXGBE_RDHMPN);
+ regs_buff[1081] = IXGBE_READ_REG(hw, IXGBE_RIC_DW0);
+ regs_buff[1082] = IXGBE_READ_REG(hw, IXGBE_RIC_DW1);
+ regs_buff[1083] = IXGBE_READ_REG(hw, IXGBE_RIC_DW2);
+ regs_buff[1084] = IXGBE_READ_REG(hw, IXGBE_RIC_DW3);
+ regs_buff[1085] = IXGBE_READ_REG(hw, IXGBE_RDPROBE);
+ regs_buff[1086] = IXGBE_READ_REG(hw, IXGBE_TDSTATCTL);
+ for (i = 0; i < 8; i++)
+ regs_buff[1087] = IXGBE_READ_REG(hw, IXGBE_TDSTAT(i));
+ regs_buff[1095] = IXGBE_READ_REG(hw, IXGBE_TDHMPN);
+ regs_buff[1096] = IXGBE_READ_REG(hw, IXGBE_TIC_DW0);
+ regs_buff[1097] = IXGBE_READ_REG(hw, IXGBE_TIC_DW1);
+ regs_buff[1098] = IXGBE_READ_REG(hw, IXGBE_TIC_DW2);
+ regs_buff[1099] = IXGBE_READ_REG(hw, IXGBE_TIC_DW3);
+ regs_buff[1100] = IXGBE_READ_REG(hw, IXGBE_TDPROBE);
+ regs_buff[1101] = IXGBE_READ_REG(hw, IXGBE_TXBUFCTRL);
+ regs_buff[1102] = IXGBE_READ_REG(hw, IXGBE_TXBUFDATA0);
+ regs_buff[1103] = IXGBE_READ_REG(hw, IXGBE_TXBUFDATA1);
+ regs_buff[1104] = IXGBE_READ_REG(hw, IXGBE_TXBUFDATA2);
+ regs_buff[1105] = IXGBE_READ_REG(hw, IXGBE_TXBUFDATA3);
+ regs_buff[1106] = IXGBE_READ_REG(hw, IXGBE_RXBUFCTRL);
+ regs_buff[1107] = IXGBE_READ_REG(hw, IXGBE_RXBUFDATA0);
+ regs_buff[1108] = IXGBE_READ_REG(hw, IXGBE_RXBUFDATA1);
+ regs_buff[1109] = IXGBE_READ_REG(hw, IXGBE_RXBUFDATA2);
+ regs_buff[1110] = IXGBE_READ_REG(hw, IXGBE_RXBUFDATA3);
+ for (i = 0; i < 8; i++)
+ regs_buff[1111] = IXGBE_READ_REG(hw, IXGBE_PCIE_DIAG(i));
+ regs_buff[1119] = IXGBE_READ_REG(hw, IXGBE_RFVAL);
+ regs_buff[1120] = IXGBE_READ_REG(hw, IXGBE_MDFTC1);
+ regs_buff[1121] = IXGBE_READ_REG(hw, IXGBE_MDFTC2);
+ regs_buff[1122] = IXGBE_READ_REG(hw, IXGBE_MDFTFIFO1);
+ regs_buff[1123] = IXGBE_READ_REG(hw, IXGBE_MDFTFIFO2);
+ regs_buff[1124] = IXGBE_READ_REG(hw, IXGBE_MDFTS);
+ regs_buff[1125] = IXGBE_READ_REG(hw, IXGBE_PCIEECCCTL);
+ regs_buff[1126] = IXGBE_READ_REG(hw, IXGBE_PBTXECC);
+ regs_buff[1127] = IXGBE_READ_REG(hw, IXGBE_PBRXECC);
+}
+
+static int ixgbe_get_eeprom_len(struct net_device *netdev)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ return adapter->hw.eeprom.word_size * 2;
+}
+
+static int ixgbe_get_eeprom(struct net_device *netdev,
+ struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ struct ixgbe_hw *hw = &adapter->hw;
+ u16 *eeprom_buff;
+ int first_word, last_word, eeprom_len;
+ int ret_val = 0;
+ u16 i;
+
+ if (eeprom->len == 0)
+ return -EINVAL;
+
+ eeprom->magic = hw->vendor_id | (hw->device_id << 16);
+
+ first_word = eeprom->offset >> 1;
+ last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+ eeprom_len = last_word - first_word + 1;
+
+ eeprom_buff = kmalloc(sizeof(u16) * eeprom_len, GFP_KERNEL);
+ if (!eeprom_buff)
+ return -ENOMEM;
+
+ for (i = 0; i < eeprom_len; i++) {
+ if ((ret_val = ixgbe_read_eeprom(hw, first_word + i,
+ &eeprom_buff[i])))
+ break;
+ }
+
+ /* Device's eeprom is always little-endian, word addressable */
+ for (i = 0; i < eeprom_len; i++)
+ le16_to_cpus(&eeprom_buff[i]);
+
+ memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
+ kfree(eeprom_buff);
+
+ return ret_val;
+}
+
+static void ixgbe_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ strncpy(drvinfo->driver, ixgbe_driver_name, 32);
+ strncpy(drvinfo->version, ixgbe_driver_version, 32);
+ strncpy(drvinfo->fw_version, "N/A", 32);
+ strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);
+ drvinfo->n_stats = IXGBE_STATS_LEN;
+ drvinfo->regdump_len = ixgbe_get_regs_len(netdev);
+}
+
+static void ixgbe_get_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ struct ixgbe_ring *tx_ring = adapter->tx_ring;
+ struct ixgbe_ring *rx_ring = adapter->rx_ring;
+
+ ring->rx_max_pending = IXGBE_MAX_RXD;
+ ring->tx_max_pending = IXGBE_MAX_TXD;
+ ring->rx_mini_max_pending = 0;
+ ring->rx_jumbo_max_pending = 0;
+ ring->rx_pending = rx_ring->count;
+ ring->tx_pending = tx_ring->count;
+ ring->rx_mini_pending = 0;
+ ring->rx_jumbo_pending = 0;
+}
+
+static int ixgbe_set_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ struct ixgbe_tx_buffer *old_buf;
+ struct ixgbe_rx_buffer *old_rx_buf;
+ void *old_desc;
+ int i, err;
+ u32 new_rx_count, new_tx_count, old_size;
+ dma_addr_t old_dma;
+
+ if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
+ return -EINVAL;
+
+ new_rx_count = max(ring->rx_pending, (u32)IXGBE_MIN_RXD);
+ new_rx_count = min(new_rx_count, (u32)IXGBE_MAX_RXD);
+ new_rx_count = ALIGN(new_rx_count, IXGBE_REQ_RX_DESCRIPTOR_MULTIPLE);
+
+ new_tx_count = max(ring->tx_pending, (u32)IXGBE_MIN_TXD);
+ new_tx_count = min(new_tx_count, (u32)IXGBE_MAX_TXD);
+ new_tx_count = ALIGN(new_tx_count, IXGBE_REQ_TX_DESCRIPTOR_MULTIPLE);
+
+ if ((new_tx_count == adapter->tx_ring->count) &&
+ (new_rx_count == adapter->rx_ring->count)) {
+ /* nothing to do */
+ return 0;
+ }
+
+ if (netif_running(adapter->netdev))
+ ixgbe_down(adapter);
+
+ /*
+ * We can't just free everything and then setup again,
+ * because the ISRs in MSI-X mode get passed pointers
+ * to the tx and rx ring structs.
+ */
+ if (new_tx_count != adapter->tx_ring->count) {
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ /* Save existing descriptor ring */
+ old_buf = adapter->tx_ring[i].tx_buffer_info;
+ old_desc = adapter->tx_ring[i].desc;
+ old_size = adapter->tx_ring[i].size;
+ old_dma = adapter->tx_ring[i].dma;
+ /* Try to allocate a new one */
+ adapter->tx_ring[i].tx_buffer_info = NULL;
+ adapter->tx_ring[i].desc = NULL;
+ adapter->tx_ring[i].count = new_tx_count;
+ err = ixgbe_setup_tx_resources(adapter,
+ &adapter->tx_ring[i]);
+ if (err) {
+ /* Restore the old one so at least
+ the adapter still works, even if
+ we failed the request */
+ adapter->tx_ring[i].tx_buffer_info = old_buf;
+ adapter->tx_ring[i].desc = old_desc;
+ adapter->tx_ring[i].size = old_size;
+ adapter->tx_ring[i].dma = old_dma;
+ goto err_setup;
+ }
+ /* Free the old buffer manually */
+ vfree(old_buf);
+ pci_free_consistent(adapter->pdev, old_size,
+ old_desc, old_dma);
+ }
+ }
+
+ if (new_rx_count != adapter->rx_ring->count) {
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+
+ old_rx_buf = adapter->rx_ring[i].rx_buffer_info;
+ old_desc = adapter->rx_ring[i].desc;
+ old_size = adapter->rx_ring[i].size;
+ old_dma = adapter->rx_ring[i].dma;
+
+ adapter->rx_ring[i].rx_buffer_info = NULL;
+ adapter->rx_ring[i].desc = NULL;
+ adapter->rx_ring[i].dma = 0;
+ adapter->rx_ring[i].count = new_rx_count;
+ err = ixgbe_setup_rx_resources(adapter,
+ &adapter->rx_ring[i]);
+ if (err) {
+ adapter->rx_ring[i].rx_buffer_info = old_rx_buf;
+ adapter->rx_ring[i].desc = old_desc;
+ adapter->rx_ring[i].size = old_size;
+ adapter->rx_ring[i].dma = old_dma;
+ goto err_setup;
+ }
+
+ vfree(old_rx_buf);
+ pci_free_consistent(adapter->pdev, old_size, old_desc,
+ old_dma);
+ }
+ }
+
+ err = 0;
+err_setup:
+ if (netif_running(adapter->netdev))
+ ixgbe_up(adapter);
+
+ return err;
+}
+
+static int ixgbe_get_stats_count(struct net_device *netdev)
+{
+ return IXGBE_STATS_LEN;
+}
+
+static void ixgbe_get_ethtool_stats(struct net_device *netdev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ u64 *queue_stat;
+ int stat_count = sizeof(struct ixgbe_queue_stats) / sizeof(u64);
+ int j, k;
+ int i;
+
+ ixgbe_update_stats(adapter);
+ for (i = 0; i < IXGBE_GLOBAL_STATS_LEN; i++) {
+ char *p = (char *)adapter + ixgbe_gstrings_stats[i].stat_offset;
+ data[i] = (ixgbe_gstrings_stats[i].sizeof_stat ==
+ sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+ }
+ for (j = 0; j < adapter->num_tx_queues; j++) {
+ queue_stat = (u64 *)&adapter->tx_ring[j].stats;
+ for (k = 0; k < stat_count; k++)
+ data[i + k] = queue_stat[k];
+ i += k;
+ }
+ for (j = 0; j < adapter->num_rx_queues; j++) {
+ queue_stat = (u64 *)&adapter->rx_ring[j].stats;
+ for (k = 0; k < stat_count; k++)
+ data[i + k] = queue_stat[k];
+ i += k;
+ }
+}
+
+static void ixgbe_get_strings(struct net_device *netdev, u32 stringset,
+ u8 *data)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ u8 *p = data;
+ int i;
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ for (i = 0; i < IXGBE_GLOBAL_STATS_LEN; i++) {
+ memcpy(p, ixgbe_gstrings_stats[i].stat_string,
+ ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ sprintf(p, "tx_queue_%u_packets", i);
+ p += ETH_GSTRING_LEN;
+ sprintf(p, "tx_queue_%u_bytes", i);
+ p += ETH_GSTRING_LEN;
+ }
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ sprintf(p, "rx_queue_%u_packets", i);
+ p += ETH_GSTRING_LEN;
+ sprintf(p, "rx_queue_%u_bytes", i);
+ p += ETH_GSTRING_LEN;
+ }
+/* BUG_ON(p - data != IXGBE_STATS_LEN * ETH_GSTRING_LEN); */
+ break;
+ }
+}
+
+
+static void ixgbe_get_wol(struct net_device *netdev,
+ struct ethtool_wolinfo *wol)
+{
+ wol->supported = 0;
+ wol->wolopts = 0;
+
+ return;
+}
+
+static int ixgbe_nway_reset(struct net_device *netdev)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ if (netif_running(netdev)) {
+ ixgbe_down(adapter);
+ ixgbe_reset(adapter);
+ ixgbe_up(adapter);
+ }
+
+ return 0;
+}
+
+static int ixgbe_phys_id(struct net_device *netdev, u32 data)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ u32 led_reg = IXGBE_READ_REG(&adapter->hw, IXGBE_LEDCTL);
+ u32 i;
+
+ if (!data || data > 300)
+ data = 300;
+
+ for (i = 0; i < (data * 1000); i += 400) {
+ ixgbe_led_on(&adapter->hw, IXGBE_LED_ON);
+ msleep_interruptible(200);
+ ixgbe_led_off(&adapter->hw, IXGBE_LED_ON);
+ msleep_interruptible(200);
+ }
+
+ /* Restore LED settings */
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_LEDCTL, led_reg);
+
+ return IXGBE_SUCCESS;
+}
+
+static struct ethtool_ops ixgbe_ethtool_ops = {
+ .get_settings = ixgbe_get_settings,
+ .set_settings = ixgbe_set_settings,
+ .get_drvinfo = ixgbe_get_drvinfo,
+ .get_regs_len = ixgbe_get_regs_len,
+ .get_regs = ixgbe_get_regs,
+ .get_wol = ixgbe_get_wol,
+ .nway_reset = ixgbe_nway_reset,
+ .get_link = ethtool_op_get_link,
+ .get_eeprom_len = ixgbe_get_eeprom_len,
+ .get_eeprom = ixgbe_get_eeprom,
+ .get_ringparam = ixgbe_get_ringparam,
+ .set_ringparam = ixgbe_set_ringparam,
+ .get_pauseparam = ixgbe_get_pauseparam,
+ .set_pauseparam = ixgbe_set_pauseparam,
+ .get_rx_csum = ixgbe_get_rx_csum,
+ .set_rx_csum = ixgbe_set_rx_csum,
+ .get_tx_csum = ixgbe_get_tx_csum,
+ .set_tx_csum = ixgbe_set_tx_csum,
+ .get_sg = ethtool_op_get_sg,
+ .set_sg = ethtool_op_set_sg,
+ .get_msglevel = ixgbe_get_msglevel,
+ .set_msglevel = ixgbe_set_msglevel,
+ .get_tso = ethtool_op_get_tso,
+ .set_tso = ixgbe_set_tso,
+ .get_coalesce = ixgbe_get_coalesce,
+ .set_coalesce = ixgbe_set_coalesce,
+ .get_strings = ixgbe_get_strings,
+ .phys_id = ixgbe_phys_id,
+ .get_stats_count = ixgbe_get_stats_count,
+ .get_ethtool_stats = ixgbe_get_ethtool_stats,
+};
+
+void ixgbe_set_ethtool_ops(struct net_device *netdev)
+{
+ SET_ETHTOOL_OPS(netdev, &ixgbe_ethtool_ops);
+}
diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c
new file mode 100644
index 0000000..7d5d0be
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_main.c
@@ -0,0 +1,2765 @@
+/*******************************************************************************
+
+ Intel PRO/10GbE PCI Express Linux driver
+ Copyright(c) 1999 - 2007 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/ipv6.h>
+#include <net/checksum.h>
+#include <net/ip6_checksum.h>
+
+#include "ixgbe.h"
+
+char ixgbe_driver_name[] = "ixgbe";
+static char ixgbe_driver_string[] =
+ "Intel(R) PRO/10GbE PCI Express Network Driver";
+
+#define DRIVERNAPI "-NAPI"
+#define DRV_VERSION "1.1.9"DRIVERNAPI
+char ixgbe_driver_version[] = DRV_VERSION;
+static char ixgbe_copyright[] = "Copyright (c) 1999-2007 Intel Corporation.";
+
+/* ixgbe_pci_tbl - PCI Device ID Table
+ *
+ * Wildcard entries (PCI_ANY_ID) should come last
+ * Last entry must be all 0s
+ *
+ * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
+ * Class, Class Mask, private data (not used) }
+ */
+static struct pci_device_id ixgbe_pci_tbl[] = {
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IXGBE_DEV_ID_82598)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IXGBE_DEV_ID_82598AF_DUAL_PORT)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IXGBE_DEV_ID_82598AF_SINGLE_PORT)},
+
+ /* required last entry */
+ {0, }
+};
+MODULE_DEVICE_TABLE(pci, ixgbe_pci_tbl);
+
+MODULE_AUTHOR("Intel Corporation, <linux.nics@intel.com>");
+MODULE_DESCRIPTION("Intel(R) PRO/10GbE PCI Express Network Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+#define DEFAULT_DEBUG_LEVEL_SHIFT 3
+
+static void ixgbe_set_ivar(struct ixgbe_adapter *adapter, u16 int_alloc_entry,
+ u8 msix_vector)
+{
+ u32 ivar, index;
+
+ msix_vector |= IXGBE_IVAR_ALLOC_VAL;
+ index = (int_alloc_entry >> 2) & 0x1F;
+ ivar = IXGBE_READ_REG_ARRAY(&adapter->hw, IXGBE_IVAR(0), index);
+ ivar |= (msix_vector << (8 * (int_alloc_entry & 0x3)));
+ IXGBE_WRITE_REG_ARRAY(&adapter->hw, IXGBE_IVAR(0), index, ivar);
+}
+
+static void ixgbe_unmap_and_free_tx_resource(struct ixgbe_adapter *adapter,
+ struct ixgbe_tx_buffer
+ *tx_buffer_info)
+{
+ if (tx_buffer_info->dma) {
+ pci_unmap_page(adapter->pdev,
+ tx_buffer_info->dma,
+ tx_buffer_info->length, PCI_DMA_TODEVICE);
+ tx_buffer_info->dma = 0;
+ }
+ if (tx_buffer_info->skb) {
+ dev_kfree_skb_any(tx_buffer_info->skb);
+ tx_buffer_info->skb = NULL;
+ }
+ /* tx_buffer_info must be completely set up in the transmit path */
+}
+
+static inline bool ixgbe_check_tx_hang(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *tx_ring,
+ unsigned int eop,
+ union ixgbe_adv_tx_desc *eop_desc)
+{
+ /* Detect a transmit hang in hardware, this serializes the
+ * check with the clearing of time_stamp and movement of i */
+ adapter->detect_tx_hung = false;
+ if (tx_ring->tx_buffer_info[eop].dma &&
+ time_after(jiffies,
+ tx_ring->tx_buffer_info[eop].time_stamp
+ + HZ)
+ && !(IXGBE_READ_REG(&adapter->hw, IXGBE_TFCS) &
+ IXGBE_TFCS_TXOFF)) {
+
+ /* detected Tx unit hang */
+ DPRINTK(DRV, ERR, "Detected Tx Unit Hang\n"
+ " TDH <%x>\n"
+ " TDT <%x>\n"
+ " next_to_use <%x>\n"
+ " next_to_clean <%x>\n"
+ "tx_buffer_info[next_to_clean]\n"
+ " time_stamp <%lx>\n"
+ " next_to_watch <%x>\n"
+ " jiffies <%lx>\n"
+ " next_to_watch.status <%x>\n",
+ readl(adapter->hw.hw_addr + tx_ring->head),
+ readl(adapter->hw.hw_addr + tx_ring->tail),
+ tx_ring->next_to_use,
+ tx_ring->next_to_clean,
+ tx_ring->tx_buffer_info[eop].time_stamp,
+ eop, jiffies, eop_desc->wb.status);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * ixgbe_clean_tx_irq - Reclaim resources after transmit completes
+ * @adapter: board private structure
+ **/
+static bool ixgbe_clean_tx_irq(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *tx_ring)
+{
+ struct net_device *netdev = adapter->netdev;
+ union ixgbe_adv_tx_desc *tx_desc, *eop_desc;
+ struct ixgbe_tx_buffer *tx_buffer_info;
+ unsigned int i, eop;
+ bool cleaned = false;
+
+ i = tx_ring->next_to_clean;
+ eop = tx_ring->tx_buffer_info[i].next_to_watch;
+ eop_desc = IXGBE_TX_DESC_ADV(*tx_ring, eop);
+ while (eop_desc->wb.status & cpu_to_le32(IXGBE_TXD_STAT_DD)) {
+ for (cleaned = false; !cleaned; ) {
+ tx_desc = IXGBE_TX_DESC_ADV(*tx_ring, i);
+ tx_buffer_info = &tx_ring->tx_buffer_info[i];
+ cleaned = (i == eop);
+
+ tx_ring->stats.bytes += tx_buffer_info->length;
+ ixgbe_unmap_and_free_tx_resource(adapter,
+ tx_buffer_info);
+ tx_desc->wb.status = 0;
+
+ i++;
+ if (i == tx_ring->count)
+ i = 0;
+ }
+
+ tx_ring->stats.packets++;
+
+ eop = tx_ring->tx_buffer_info[i].next_to_watch;
+ eop_desc = IXGBE_TX_DESC_ADV(*tx_ring, eop);
+ }
+
+ tx_ring->next_to_clean = i;
+
+#define TX_WAKE_THRESHOLD 32
+ spin_lock(&tx_ring->tx_lock);
+
+ if (cleaned && netif_carrier_ok(netdev) &&
+ IXGBE_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD)
+ netif_wake_queue(netdev);
+
+ spin_unlock(&tx_ring->tx_lock);
+
+ if (adapter->detect_tx_hung)
+ if (ixgbe_check_tx_hang(adapter, tx_ring, eop, eop_desc))
+ netif_stop_queue(netdev);
+
+ return cleaned;
+}
+
+/**
+ * ixgbe_receive_skb - Send a completed packet up the stack
+ * @adapter: board private structure
+ * @skb: packet to send up
+ * @is_vlan: packet has a VLAN tag
+ * @tag: VLAN tag from descriptor
+ **/
+static void ixgbe_receive_skb(struct ixgbe_adapter *adapter,
+ struct sk_buff *skb, bool is_vlan,
+ u16 tag)
+{
+ if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL)) {
+ if (adapter->vlgrp && is_vlan)
+ vlan_hwaccel_receive_skb(skb, adapter->vlgrp, tag);
+ else
+ netif_receive_skb(skb);
+ } else {
+
+ if (adapter->vlgrp && is_vlan)
+ vlan_hwaccel_rx(skb, adapter->vlgrp, tag);
+ else
+ netif_rx(skb);
+ }
+}
+
+static inline void ixgbe_rx_checksum(struct ixgbe_adapter *adapter,
+ u32 status_err,
+ struct sk_buff *skb)
+{
+ skb->ip_summed = CHECKSUM_NONE;
+
+ /* Ignore Checksum bit is set */
+ if ((status_err & IXGBE_RXD_STAT_IXSM) ||
+ !(adapter->flags & IXGBE_FLAG_RX_CSUM_ENABLED))
+ return;
+ /* TCP/UDP checksum error bit is set */
+ if (status_err & (IXGBE_RXDADV_ERR_TCPE | IXGBE_RXDADV_ERR_IPE)) {
+ /* let the stack verify checksum errors */
+ adapter->hw_csum_rx_error++;
+ return;
+ }
+ /* It must be a TCP or UDP packet with a valid checksum */
+ if (status_err & (IXGBE_RXD_STAT_L4CS | IXGBE_RXD_STAT_UDPCS))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ adapter->hw_csum_rx_good++;
+}
+
+/**
+ * ixgbe_alloc_rx_buffers - Replace used receive buffers; packet split
+ * @adapter: address of board private structure
+ **/
+static void ixgbe_alloc_rx_buffers(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *rx_ring,
+ int cleaned_count)
+{
+ struct net_device *netdev = adapter->netdev;
+ struct pci_dev *pdev = adapter->pdev;
+ union ixgbe_adv_rx_desc *rx_desc;
+ struct ixgbe_rx_buffer *rx_buffer_info;
+ struct sk_buff *skb;
+ unsigned int i;
+ unsigned int bufsz = adapter->rx_buf_len + NET_IP_ALIGN;
+
+ i = rx_ring->next_to_use;
+ rx_buffer_info = &rx_ring->rx_buffer_info[i];
+
+ while (cleaned_count--) {
+ rx_desc = IXGBE_RX_DESC_ADV(*rx_ring, i);
+
+ if (!rx_buffer_info->page &&
+ (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED)) {
+ rx_buffer_info->page = alloc_page(GFP_ATOMIC);
+ if (!rx_buffer_info->page) {
+ adapter->alloc_rx_page_failed++;
+ goto no_buffers;
+ }
+ rx_buffer_info->page_dma =
+ pci_map_page(pdev, rx_buffer_info->page,
+ 0, PAGE_SIZE, PCI_DMA_FROMDEVICE);
+ }
+
+ if (!rx_buffer_info->skb) {
+ skb = netdev_alloc_skb(netdev, bufsz);
+
+ if (!skb) {
+ adapter->alloc_rx_buff_failed++;
+ goto no_buffers;
+ }
+
+ /*
+ * Make buffer alignment 2 beyond a 16 byte boundary
+ * this will result in a 16 byte aligned IP header after
+ * the 14 byte MAC header is removed
+ */
+ skb_reserve(skb, NET_IP_ALIGN);
+
+ rx_buffer_info->skb = skb;
+ rx_buffer_info->dma = pci_map_single(pdev, skb->data,
+ bufsz,
+ PCI_DMA_FROMDEVICE);
+ }
+ /* Refresh the desc even if buffer_addrs didn't change because
+ * each write-back erases this info. */
+ if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) {
+ rx_desc->read.pkt_addr =
+ cpu_to_le64(rx_buffer_info->page_dma);
+ rx_desc->read.hdr_addr =
+ cpu_to_le64(rx_buffer_info->dma);
+ } else {
+ rx_desc->read.pkt_addr =
+ cpu_to_le64(rx_buffer_info->dma);
+ }
+
+ i++;
+ if (i == rx_ring->count)
+ i = 0;
+ rx_buffer_info = &rx_ring->rx_buffer_info[i];
+ }
+no_buffers:
+ if (rx_ring->next_to_use != i) {
+ rx_ring->next_to_use = i;
+ if (i-- == 0)
+ i = (rx_ring->count - 1);
+
+ /*
+ * Force memory writes to complete before letting h/w
+ * know there are new descriptors to fetch. (Only
+ * applicable for weak-ordered memory model archs,
+ * such as IA-64).
+ */
+ wmb();
+ writel(i, adapter->hw.hw_addr + rx_ring->tail);
+ }
+}
+
+static bool ixgbe_clean_rx_irq(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *rx_ring,
+ int *work_done, int work_to_do)
+{
+ struct net_device *netdev = adapter->netdev;
+ struct pci_dev *pdev = adapter->pdev;
+ union ixgbe_adv_rx_desc *rx_desc, *next_rxd;
+ struct ixgbe_rx_buffer *rx_buffer_info, *next_buffer;
+ struct sk_buff *skb;
+ unsigned int i;
+ u32 upper_len, len, staterr;
+ u16 hdr_info;
+ bool cleaned = false;
+ int cleaned_count = 0;
+
+ i = rx_ring->next_to_clean;
+ upper_len = 0;
+ rx_desc = IXGBE_RX_DESC_ADV(*rx_ring, i);
+ staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
+ rx_buffer_info = &rx_ring->rx_buffer_info[i];
+
+ while (staterr & IXGBE_RXD_STAT_DD) {
+ if (*work_done >= work_to_do)
+ break;
+ (*work_done)++;
+
+ if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) {
+ hdr_info =
+ le16_to_cpu(rx_desc->wb.lower.lo_dword.hdr_info);
+ len =
+ ((hdr_info & IXGBE_RXDADV_HDRBUFLEN_MASK) >>
+ IXGBE_RXDADV_HDRBUFLEN_SHIFT);
+ if (hdr_info & IXGBE_RXDADV_SPH)
+ adapter->rx_hdr_split++;
+ if (len > IXGBE_RX_HDR_SIZE)
+ len = IXGBE_RX_HDR_SIZE;
+ upper_len = le16_to_cpu(rx_desc->wb.upper.length);
+ } else
+ len = le16_to_cpu(rx_desc->wb.upper.length);
+
+ cleaned = true;
+ skb = rx_buffer_info->skb;
+ prefetch(skb->data - NET_IP_ALIGN);
+ rx_buffer_info->skb = NULL;
+
+ if (len && !skb_shinfo(skb)->nr_frags) {
+ pci_unmap_single(pdev, rx_buffer_info->dma,
+ adapter->rx_buf_len + NET_IP_ALIGN,
+ PCI_DMA_FROMDEVICE);
+ skb_put(skb, len);
+ }
+
+ if (upper_len) {
+ pci_unmap_page(pdev, rx_buffer_info->page_dma,
+ PAGE_SIZE, PCI_DMA_FROMDEVICE);
+ rx_buffer_info->page_dma = 0;
+ skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags,
+ rx_buffer_info->page, 0, upper_len);
+ rx_buffer_info->page = NULL;
+
+ skb->len += upper_len;
+ skb->data_len += upper_len;
+ skb->truesize += upper_len;
+ }
+
+ i++;
+ if (i == rx_ring->count)
+ i = 0;
+ next_buffer = &rx_ring->rx_buffer_info[i];
+
+ next_rxd = IXGBE_RX_DESC_ADV(*rx_ring, i);
+ prefetch(next_rxd);
+
+ cleaned_count++;
+ if (staterr & IXGBE_RXD_STAT_EOP) {
+ rx_ring->stats.packets++;
+ rx_ring->stats.bytes += skb->len;
+ } else {
+ rx_buffer_info->skb = next_buffer->skb;
+ rx_buffer_info->dma = next_buffer->dma;
+ next_buffer->skb = skb;
+ adapter->non_eop_descs++;
+ goto next_desc;
+ }
+
+ if (staterr & IXGBE_RXDADV_ERR_FRAME_ERR_MASK) {
+ dev_kfree_skb_irq(skb);
+ goto next_desc;
+ }
+
+ ixgbe_rx_checksum(adapter, staterr, skb);
+ skb->protocol = eth_type_trans(skb, netdev);
+ ixgbe_receive_skb(adapter, skb, (staterr & IXGBE_RXD_STAT_VP),
+ le16_to_cpu(rx_desc->wb.upper.vlan));
+ netdev->last_rx = jiffies;
+
+next_desc:
+ rx_desc->wb.upper.status_error = 0;
+
+ /* return some buffers to hardware, one at a time is too slow */
+ if (cleaned_count >= IXGBE_RX_BUFFER_WRITE) {
+ ixgbe_alloc_rx_buffers(adapter, rx_ring,
+ cleaned_count);
+ cleaned_count = 0;
+ }
+
+ /* use prefetched values */
+ rx_desc = next_rxd;
+ rx_buffer_info = next_buffer;
+
+ staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
+ }
+
+ rx_ring->next_to_clean = i;
+ cleaned_count = IXGBE_DESC_UNUSED(rx_ring);
+
+ if (cleaned_count)
+ ixgbe_alloc_rx_buffers(adapter, rx_ring, cleaned_count);
+
+ return cleaned;
+}
+
+#define IXGBE_MAX_INTR 10
+/**
+ * ixgbe_configure_msix - Configure MSI-X hardware
+ * @adapter: board private structure
+ *
+ * ixgbe_configure_msix sets up the hardware to properly generate MSI-X
+ * interrupts.
+ **/
+static void ixgbe_configure_msix(struct ixgbe_adapter *adapter)
+{
+ int i, vector = 0;
+
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ ixgbe_set_ivar(adapter, IXGBE_IVAR_TX_QUEUE(i),
+ IXGBE_MSIX_VECTOR(vector));
+ if (adapter->tx_eitr != 0)
+ writel(1000000000 / (adapter->tx_eitr * 256),
+ adapter->hw.hw_addr +
+ adapter->tx_ring[i].itr_register);
+ else
+ writel(0, adapter->hw.hw_addr +
+ adapter->tx_ring[i].itr_register);
+ vector++;
+ }
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ ixgbe_set_ivar(adapter, IXGBE_IVAR_RX_QUEUE(i),
+ IXGBE_MSIX_VECTOR(vector));
+ if (adapter->rx_eitr != 0)
+ writel(1000000000 / (adapter->rx_eitr * 256),
+ adapter->hw.hw_addr +
+ adapter->rx_ring[i].itr_register);
+ else
+ writel(0, adapter->hw.hw_addr +
+ adapter->rx_ring[i].itr_register);
+ vector++;
+ }
+
+ vector = adapter->num_tx_queues + adapter->num_rx_queues;
+ ixgbe_set_ivar(adapter, IXGBE_IVAR_OTHER_CAUSES_INDEX,
+ IXGBE_MSIX_VECTOR(vector));
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_EITR(vector), 1950);
+}
+
+static irqreturn_t ixgbe_msix_lsc(int irq, void *data)
+{
+ struct net_device *netdev = data;
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 eicr = IXGBE_READ_REG(hw, IXGBE_EICR);
+
+ if (eicr & IXGBE_EICR_LSC) {
+ adapter->lsc_int++;
+ mod_timer(&adapter->watchdog_timer, jiffies);
+ }
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, IXGBE_EIMS_OTHER);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ixgbe_msix_clean_tx(int irq, void *data)
+{
+ struct ixgbe_ring *txr = data;
+ struct ixgbe_adapter *adapter = txr->adapter;
+ int i;
+
+ for (i = 0; i < IXGBE_MAX_INTR; i++) {
+ if (!ixgbe_clean_tx_irq(adapter, txr))
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ixgbe_msix_clean_rx(int irq, void *data)
+{
+ struct ixgbe_ring *rxr = data;
+ struct ixgbe_adapter *adapter = rxr->adapter;
+
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, rxr->eims_value);
+ netif_rx_schedule(adapter->netdev);
+ return IRQ_HANDLED;
+}
+
+static int ixgbe_clean_rxonly(struct net_device *netdev, int *budget)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ int work_to_do = min(*budget, netdev->quota);
+ int work_done = 0;
+ struct ixgbe_ring *rxr = adapter->rx_ring;
+
+ /* Keep link state information with original netdev */
+ if (!netif_carrier_ok(netdev))
+ goto quit_polling;
+
+ ixgbe_clean_rx_irq(adapter, rxr, &work_done, work_to_do);
+
+ *budget -= work_done;
+ netdev->quota -= work_done;
+
+ /* If no Tx and not enough Rx work done, exit the polling mode */
+ if ((work_done == 0) || !netif_running(netdev)) {
+quit_polling:
+ netif_rx_complete(netdev);
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, rxr->eims_value);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * ixgbe_setup_msix - Initialize MSI-X interrupts
+ *
+ * ixgbe_setup_msix allocates MSI-X vectors and requests
+ * interrutps from the kernel.
+ **/
+static int ixgbe_setup_msix(struct ixgbe_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ int i, int_vector = 0, err = 0;
+ int max_msix_count;
+
+ /* +1 for the LSC interrupt */
+ max_msix_count = adapter->num_rx_queues + adapter->num_tx_queues + 1;
+ adapter->msix_entries = kcalloc(max_msix_count,
+ sizeof(struct msix_entry), GFP_KERNEL);
+ if (!adapter->msix_entries)
+ return -ENOMEM;
+
+ for (i = 0; i < max_msix_count; i++)
+ adapter->msix_entries[i].entry = i;
+
+ err = pci_enable_msix(adapter->pdev, adapter->msix_entries,
+ max_msix_count);
+ if (err)
+ goto out;
+
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ sprintf(adapter->tx_ring[i].name, "%s-tx%d", netdev->name, i);
+ err = request_irq(adapter->msix_entries[int_vector].vector,
+ &ixgbe_msix_clean_tx,
+ 0,
+ adapter->tx_ring[i].name,
+ &(adapter->tx_ring[i]));
+ if (err) {
+ DPRINTK(PROBE, ERR,
+ "request_irq failed for MSIX interrupt "
+ "Error: %d\n", err);
+ goto release_irqs;
+ }
+ adapter->tx_ring[i].eims_value =
+ (1 << IXGBE_MSIX_VECTOR(int_vector));
+ adapter->tx_ring[i].itr_register = IXGBE_EITR(int_vector);
+ adapter->tx_ring[i].itr_val = adapter->tx_eitr;
+ int_vector++;
+ }
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ if (strlen(netdev->name) < (IFNAMSIZ - 5))
+ sprintf(adapter->rx_ring[i].name,
+ "%s-rx%d", netdev->name, i);
+ else
+ memcpy(adapter->rx_ring[i].name,
+ netdev->name, IFNAMSIZ);
+ err = request_irq(adapter->msix_entries[int_vector].vector,
+ &ixgbe_msix_clean_rx, 0,
+ adapter->rx_ring[i].name,
+ &(adapter->rx_ring[i]));
+ if (err) {
+ DPRINTK(PROBE, ERR,
+ "request_irq failed for MSIX interrupt "
+ "Error: %d\n", err);
+ goto release_irqs;
+ }
+
+ adapter->rx_ring[i].eims_value =
+ (1 << IXGBE_MSIX_VECTOR(int_vector));
+ adapter->rx_ring[i].itr_register = IXGBE_EITR(int_vector);
+ adapter->rx_ring[i].itr_val = adapter->rx_eitr;
+ int_vector++;
+ }
+
+ sprintf(adapter->lsc_name, "%s-lsc", netdev->name);
+ err = request_irq(adapter->msix_entries[int_vector].vector,
+ &ixgbe_msix_lsc, 0, adapter->lsc_name, netdev);
+ if (err) {
+ DPRINTK(PROBE, ERR,
+ "request_irq for msix_lsc failed: %d\n", err);
+ goto release_irqs;
+ }
+
+ adapter->netdev->poll = ixgbe_clean_rxonly;
+ adapter->flags |= IXGBE_FLAG_MSIX_ENABLED;
+ return 0;
+
+release_irqs:
+ int_vector--;
+ for (; int_vector >= adapter->num_tx_queues; int_vector--)
+ free_irq(adapter->msix_entries[int_vector].vector,
+ &(adapter->rx_ring[int_vector -
+ adapter->num_tx_queues]));
+
+ for (; int_vector >= 0; int_vector--)
+ free_irq(adapter->msix_entries[int_vector].vector,
+ &(adapter->tx_ring[int_vector]));
+out:
+ kfree(adapter->msix_entries);
+ adapter->msix_entries = NULL;
+ adapter->flags &= ~IXGBE_FLAG_MSIX_ENABLED;
+ return err;
+}
+
+/**
+ * ixgbe_intr - Interrupt Handler
+ * @irq: interrupt number
+ * @data: pointer to a network interface device structure
+ * @pt_regs: CPU registers structure
+ **/
+static irqreturn_t ixgbe_intr(int irq, void *data)
+{
+ struct net_device *netdev = data;
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 eicr;
+
+ eicr = IXGBE_READ_REG(hw, IXGBE_EICR);
+
+ if (!eicr)
+ return IRQ_NONE; /* Not our interrupt */
+
+ if (eicr & IXGBE_EICR_LSC) {
+ adapter->lsc_int++;
+ mod_timer(&adapter->watchdog_timer, jiffies);
+ }
+ if (netif_rx_schedule_prep(netdev)) {
+ /* Disable interrupts and register for poll. The flush of the
+ * posted write is intentionally left out. */
+ atomic_inc(&adapter->irq_sem);
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, ~0);
+ __netif_rx_schedule(netdev);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ixgbe_request_irq - initialize interrupts
+ * @adapter: board private structure
+ *
+ * Attempts to configure interrupts using the best available
+ * capabilities of the hardware and kernel.
+ **/
+static int ixgbe_request_irq(struct ixgbe_adapter *adapter, u32 *num_rx_queues)
+{
+ struct net_device *netdev = adapter->netdev;
+ int flags, err;
+ irqreturn_t(*handler) (int, void *) = &ixgbe_intr;
+
+ flags = IRQF_SHARED;
+
+ err = ixgbe_setup_msix(adapter);
+ if (!err)
+ goto request_done;
+
+ /*
+ * if we can't do MSI-X, fall through and try MSI
+ * No need to reallocate memory since we're decreasing the number of
+ * queues. We just won't use the other ones, also it is freed correctly
+ * on ixgbe_remove.
+ */
+ *num_rx_queues = 1;
+
+ /* do MSI */
+ err = pci_enable_msi(adapter->pdev);
+ if (!err) {
+ adapter->flags |= IXGBE_FLAG_MSI_ENABLED;
+ flags &= ~IRQF_SHARED;
+ handler = &ixgbe_intr;
+ }
+
+ err = request_irq(adapter->pdev->irq, handler, flags,
+ netdev->name, netdev);
+ if (err)
+ DPRINTK(PROBE, ERR, "request_irq failed, Error %d\n", err);
+
+request_done:
+ return err;
+}
+
+static void ixgbe_free_irq(struct ixgbe_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+
+ if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) {
+ int i;
+
+ for (i = 0; i < adapter->num_tx_queues; i++)
+ free_irq(adapter->msix_entries[i].vector,
+ &(adapter->tx_ring[i]));
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ free_irq(adapter->msix_entries[i +
+ adapter->num_tx_queues].vector,
+ &(adapter->rx_ring[i]));
+ i = adapter->num_rx_queues + adapter->num_tx_queues;
+ free_irq(adapter->msix_entries[i].vector, netdev);
+ pci_disable_msix(adapter->pdev);
+ kfree(adapter->msix_entries);
+ adapter->msix_entries = NULL;
+ adapter->flags &= ~IXGBE_FLAG_MSIX_ENABLED;
+ return;
+ }
+
+ free_irq(adapter->pdev->irq, netdev);
+ if (adapter->flags & IXGBE_FLAG_MSI_ENABLED) {
+ pci_disable_msi(adapter->pdev);
+ adapter->flags &= ~IXGBE_FLAG_MSI_ENABLED;
+ }
+}
+
+/**
+ * ixgbe_irq_disable - Mask off interrupt generation on the NIC
+ * @adapter: board private structure
+ **/
+static inline void ixgbe_irq_disable(struct ixgbe_adapter *adapter)
+{
+ atomic_inc(&adapter->irq_sem);
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, ~0);
+ IXGBE_WRITE_FLUSH(&adapter->hw);
+ synchronize_irq(adapter->pdev->irq);
+}
+
+/**
+ * ixgbe_irq_enable - Enable default interrupt generation settings
+ * @adapter: board private structure
+ **/
+static inline void ixgbe_irq_enable(struct ixgbe_adapter *adapter)
+{
+ if (atomic_dec_and_test(&adapter->irq_sem)) {
+ if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) {
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIAC,
+ (IXGBE_EIMS_ENABLE_MASK &
+ ~(IXGBE_EIMS_OTHER | IXGBE_EIMS_LSC)));
+ }
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS,
+ IXGBE_EIMS_ENABLE_MASK);
+ IXGBE_WRITE_FLUSH(&adapter->hw);
+ }
+}
+
+/**
+ * ixgbe_configure_msi_and_legacy - Initialize PIN (INTA...) and MSI interrupts
+ *
+ **/
+static void ixgbe_configure_msi_and_legacy(struct ixgbe_adapter *adapter)
+{
+ int i;
+ struct ixgbe_hw *hw = &adapter->hw;
+
+ if (adapter->rx_eitr)
+ IXGBE_WRITE_REG(hw, IXGBE_EITR(0),
+ 1000000000 / (adapter->rx_eitr * 256));
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ ixgbe_set_ivar(adapter, IXGBE_IVAR_RX_QUEUE(i),
+ IXGBE_MSIX_VECTOR(i));
+ }
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ ixgbe_set_ivar(adapter, IXGBE_IVAR_TX_QUEUE(i),
+ IXGBE_MSIX_VECTOR(i));
+ }
+}
+
+/**
+ * ixgbe_configure_tx - Configure 8254x Transmit Unit after Reset
+ * @adapter: board private structure
+ *
+ * Configure the Tx unit of the MAC after a reset.
+ **/
+static void ixgbe_configure_tx(struct ixgbe_adapter *adapter)
+{
+ u64 tdba;
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 i, tdlen;
+
+ /* Setup the HW Tx Head and Tail descriptor pointers */
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ tdba = adapter->tx_ring[i].dma;
+ tdlen = adapter->tx_ring[i].count *
+ sizeof(union ixgbe_adv_tx_desc);
+ IXGBE_WRITE_REG(hw, IXGBE_TDBAL(i), (tdba & DMA_32BIT_MASK));
+ IXGBE_WRITE_REG(hw, IXGBE_TDBAH(i), (tdba >> 32));
+ IXGBE_WRITE_REG(hw, IXGBE_TDLEN(i), tdlen);
+ IXGBE_WRITE_REG(hw, IXGBE_TDH(i), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_TDT(i), 0);
+ adapter->tx_ring[i].head = IXGBE_TDH(i);
+ adapter->tx_ring[i].tail = IXGBE_TDT(i);
+ }
+
+ IXGBE_WRITE_REG(hw, IXGBE_TIPG, IXGBE_TIPG_FIBER_DEFAULT);
+}
+
+#define PAGE_USE_COUNT(S) (((S) >> PAGE_SHIFT) + \
+ (((S) & (PAGE_SIZE - 1)) ? 1 : 0))
+
+#define IXGBE_SRRCTL_BSIZEHDRSIZE_SHIFT 2
+/**
+ * ixgbe_configure_rx - Configure 8254x Receive Unit after Reset
+ * @adapter: board private structure
+ *
+ * Configure the Rx unit of the MAC after a reset.
+ **/
+static void ixgbe_configure_rx(struct ixgbe_adapter *adapter)
+{
+ u64 rdba;
+ struct ixgbe_hw *hw = &adapter->hw;
+ struct net_device *netdev = adapter->netdev;
+ int max_frame = netdev->mtu + ETH_HLEN + ETH_FCS_LEN;
+ u32 rdlen, rxctrl, rxcsum;
+ u32 random[10];
+ u32 reta, mrqc;
+ int i;
+ u32 fctrl, hlreg0;
+ u32 srrctl;
+ u32 pages;
+
+ /* Decide whether to use packet split mode or not */
+ if (netdev->mtu > ETH_DATA_LEN)
+ adapter->flags |= IXGBE_FLAG_RX_PS_ENABLED;
+ else
+ adapter->flags &= ~IXGBE_FLAG_RX_PS_ENABLED;
+
+ /* Set the RX buffer length according to the mode */
+ if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) {
+ adapter->rx_buf_len = IXGBE_RX_HDR_SIZE;
+ } else {
+ if (netdev->mtu <= ETH_DATA_LEN)
+ adapter->rx_buf_len = MAXIMUM_ETHERNET_VLAN_SIZE;
+ else
+ adapter->rx_buf_len = ALIGN(max_frame, 1024);
+ }
+
+ fctrl = IXGBE_READ_REG(&adapter->hw, IXGBE_FCTRL);
+ fctrl |= IXGBE_FCTRL_BAM;
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCTRL, fctrl);
+
+ hlreg0 = IXGBE_READ_REG(hw, IXGBE_HLREG0);
+ if (adapter->netdev->mtu <= ETH_DATA_LEN)
+ hlreg0 &= ~IXGBE_HLREG0_JUMBOEN;
+ else
+ hlreg0 |= IXGBE_HLREG0_JUMBOEN;
+ IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hlreg0);
+
+ pages = PAGE_USE_COUNT(adapter->netdev->mtu);
+
+ srrctl = IXGBE_READ_REG(&adapter->hw, IXGBE_SRRCTL(0));
+ srrctl &= ~IXGBE_SRRCTL_BSIZEHDR_MASK;
+ srrctl &= ~IXGBE_SRRCTL_BSIZEPKT_MASK;
+
+ if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) {
+ srrctl |= PAGE_SIZE >> IXGBE_SRRCTL_BSIZEPKT_SHIFT;
+ srrctl |= IXGBE_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS;
+ srrctl |= ((IXGBE_RX_HDR_SIZE <<
+ IXGBE_SRRCTL_BSIZEHDRSIZE_SHIFT) &
+ IXGBE_SRRCTL_BSIZEHDR_MASK);
+ } else {
+ srrctl |= IXGBE_SRRCTL_DESCTYPE_ADV_ONEBUF;
+
+ if (adapter->rx_buf_len == MAXIMUM_ETHERNET_VLAN_SIZE)
+ srrctl |=
+ IXGBE_RXBUFFER_2048 >> IXGBE_SRRCTL_BSIZEPKT_SHIFT;
+ else
+ srrctl |=
+ adapter->rx_buf_len >> IXGBE_SRRCTL_BSIZEPKT_SHIFT;
+ }
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_SRRCTL(0), srrctl);
+
+ rdlen = adapter->rx_ring[0].count * sizeof(union ixgbe_adv_rx_desc);
+ /* disable receives while setting up the descriptors */
+ rxctrl = IXGBE_READ_REG(hw, IXGBE_RXCTRL);
+ IXGBE_WRITE_REG(hw, IXGBE_RXCTRL, rxctrl & ~IXGBE_RXCTRL_RXEN);
+ rxctrl |= (IXGBE_RXCTRL_DMBYPS | IXGBE_RXCTRL_RXEN);
+
+ /* Setup the HW Rx Head and Tail Descriptor Pointers and
+ * the Base and Length of the Rx Descriptor Ring */
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ rdba = adapter->rx_ring[i].dma;
+ IXGBE_WRITE_REG(hw, IXGBE_RDBAL(i), (rdba & DMA_32BIT_MASK));
+ IXGBE_WRITE_REG(hw, IXGBE_RDBAH(i), (rdba >> 32));
+ IXGBE_WRITE_REG(hw, IXGBE_RDLEN(i), rdlen);
+ IXGBE_WRITE_REG(hw, IXGBE_RDH(i), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_RDT(i), 0);
+ adapter->rx_ring[i].head = IXGBE_RDH(i);
+ adapter->rx_ring[i].tail = IXGBE_RDT(i);
+ }
+
+ if (adapter->num_rx_queues > 1) {
+ /* Random 40bytes used as random key in RSS hash function */
+ get_random_bytes(&random[0], 40);
+
+ switch (adapter->num_rx_queues) {
+ case 8:
+ case 4:
+ /* Bits [3:0] in each byte refers the Rx queue no */
+ reta = 0x00010203;
+ break;
+ case 2:
+ reta = 0x00010001;
+ break;
+ default:
+ reta = 0x00000000;
+ break;
+ }
+
+ /* Fill out redirection table */
+ for (i = 0; i < 32; i++) {
+ IXGBE_WRITE_REG_ARRAY(hw, IXGBE_RETA(0), i, reta);
+ if (adapter->num_rx_queues > 4) {
+ i++;
+ IXGBE_WRITE_REG_ARRAY(hw, IXGBE_RETA(0), i,
+ 0x04050607);
+ }
+ }
+
+ /* Fill out hash function seeds */
+ for (i = 0; i < 10; i++)
+ IXGBE_WRITE_REG_ARRAY(hw, IXGBE_RSSRK(0), i, random[i]);
+
+ mrqc = IXGBE_MRQC_RSSEN
+ /* Perform hash on these packet types */
+ | IXGBE_MRQC_RSS_FIELD_IPV4
+ | IXGBE_MRQC_RSS_FIELD_IPV4_TCP
+ | IXGBE_MRQC_RSS_FIELD_IPV4_UDP
+ | IXGBE_MRQC_RSS_FIELD_IPV6_EX_TCP
+ | IXGBE_MRQC_RSS_FIELD_IPV6_EX
+ | IXGBE_MRQC_RSS_FIELD_IPV6
+ | IXGBE_MRQC_RSS_FIELD_IPV6_TCP
+ | IXGBE_MRQC_RSS_FIELD_IPV6_UDP
+ | IXGBE_MRQC_RSS_FIELD_IPV6_EX_UDP;
+ IXGBE_WRITE_REG(hw, IXGBE_MRQC, mrqc);
+
+ /* Multiqueue and packet checksumming are mutually exclusive. */
+ rxcsum = IXGBE_READ_REG(hw, IXGBE_RXCSUM);
+ rxcsum |= IXGBE_RXCSUM_PCSD;
+ IXGBE_WRITE_REG(hw, IXGBE_RXCSUM, rxcsum);
+ } else {
+ /* Enable Receive Checksum Offload for TCP and UDP */
+ rxcsum = IXGBE_READ_REG(hw, IXGBE_RXCSUM);
+ if (adapter->flags & IXGBE_FLAG_RX_CSUM_ENABLED) {
+ /* Enable IPv4 payload checksum for UDP fragments
+ * Must be used in conjunction with packet-split. */
+ rxcsum |= IXGBE_RXCSUM_IPPCSE;
+ } else {
+ /* don't need to clear IPPCSE as it defaults to 0 */
+ }
+ IXGBE_WRITE_REG(hw, IXGBE_RXCSUM, rxcsum);
+ }
+ /* Enable Receives */
+ IXGBE_WRITE_REG(hw, IXGBE_RXCTRL, rxctrl);
+ rxctrl = IXGBE_READ_REG(hw, IXGBE_RXCTRL);
+}
+
+static void ixgbe_vlan_rx_register(struct net_device *netdev,
+ struct vlan_group *grp)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ u32 ctrl;
+
+ ixgbe_irq_disable(adapter);
+ adapter->vlgrp = grp;
+
+ if (grp) {
+ /* enable VLAN tag insert/strip */
+ ctrl = IXGBE_READ_REG(&adapter->hw, IXGBE_VLNCTRL);
+ ctrl |= IXGBE_VLNCTRL_VME | IXGBE_VLNCTRL_VFE;
+ ctrl &= ~IXGBE_VLNCTRL_CFIEN;
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_VLNCTRL, ctrl);
+ }
+
+ ixgbe_irq_enable(adapter);
+}
+
+static void ixgbe_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ /* add VID to filter table */
+ ixgbe_set_vfta(&adapter->hw, vid, 0, true);
+}
+
+static void ixgbe_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ ixgbe_irq_disable(adapter);
+ vlan_group_set_device(adapter->vlgrp, vid, NULL);
+ ixgbe_irq_enable(adapter);
+
+ /* remove VID from filter table */
+ ixgbe_set_vfta(&adapter->hw, vid, 0, false);
+}
+
+static void ixgbe_restore_vlan(struct ixgbe_adapter *adapter)
+{
+ ixgbe_vlan_rx_register(adapter->netdev, adapter->vlgrp);
+
+ if (adapter->vlgrp) {
+ u16 vid;
+ for (vid = 0; vid < VLAN_GROUP_ARRAY_LEN; vid++) {
+ if (!vlan_group_get_device(adapter->vlgrp, vid))
+ continue;
+ ixgbe_vlan_rx_add_vid(adapter->netdev, vid);
+ }
+ }
+}
+
+/**
+ * ixgbe_set_multi - Multicast and Promiscuous mode set
+ * @netdev: network interface device structure
+ *
+ * The set_multi entry point is called whenever the multicast address
+ * list or the network interface flags are updated. This routine is
+ * responsible for configuring the hardware for proper multicast,
+ * promiscuous mode, and all-multi behavior.
+ **/
+static void ixgbe_set_multi(struct net_device *netdev)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ struct ixgbe_hw *hw = &adapter->hw;
+ struct dev_mc_list *mc_ptr;
+ u8 *mta_list;
+ u32 fctrl;
+ int i;
+
+ /* Check for Promiscuous and All Multicast modes */
+
+ fctrl = IXGBE_READ_REG(hw, IXGBE_FCTRL);
+
+ if (netdev->flags & IFF_PROMISC) {
+ fctrl |= (IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE);
+ } else if (netdev->flags & IFF_ALLMULTI) {
+ fctrl |= IXGBE_FCTRL_MPE;
+ fctrl &= ~IXGBE_FCTRL_UPE;
+ } else {
+ fctrl &= ~(IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE);
+ }
+
+ IXGBE_WRITE_REG(hw, IXGBE_FCTRL, fctrl);
+
+ if (netdev->mc_count) {
+ mta_list = kcalloc(netdev->mc_count, ETH_ALEN, GFP_ATOMIC);
+ if (!mta_list)
+ return;
+
+ /* Shared function expects packed array of only addresses. */
+ mc_ptr = netdev->mc_list;
+
+ for (i = 0; i < netdev->mc_count; i++) {
+ if (!mc_ptr)
+ break;
+ memcpy(mta_list + (i * ETH_ALEN), mc_ptr->dmi_addr,
+ ETH_ALEN);
+ mc_ptr = mc_ptr->next;
+ }
+
+ ixgbe_update_mc_addr_list(hw, mta_list, i, 0);
+ kfree(mta_list);
+ } else {
+ ixgbe_update_mc_addr_list(hw, NULL, 0, 0);
+ }
+
+}
+
+int ixgbe_up(struct ixgbe_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+ int i;
+ u32 gpie;
+ struct ixgbe_hw *hw = &adapter->hw;
+ u32 txdctl, rxdctl, mhadd;
+ int max_frame = netdev->mtu + ETH_HLEN + ETH_FCS_LEN;
+
+ /* hardware has been reset, we need to reload some things */
+ ixgbe_set_multi(netdev);
+
+ ixgbe_restore_vlan(adapter);
+
+ ixgbe_configure_tx(adapter);
+ ixgbe_configure_rx(adapter);
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ ixgbe_alloc_rx_buffers(adapter, &adapter->rx_ring[i],
+ (adapter->rx_ring[i].count - 1));
+
+ if ((adapter->flags & IXGBE_FLAG_MSIX_ENABLED) ||
+ (adapter->flags & IXGBE_FLAG_MSI_ENABLED)) {
+ gpie = IXGBE_READ_REG(&adapter->hw, IXGBE_GPIE);
+ if (!(adapter->flags & IXGBE_FLAG_MSI_ENABLED))
+ gpie |= IXGBE_GPIE_MSIX_MODE;
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_GPIE,
+ (gpie | IXGBE_GPIE_EIAME |
+ IXGBE_GPIE_PBA_SUPPORT | IXGBE_GPIE_OCD));
+ gpie = IXGBE_READ_REG(&adapter->hw, IXGBE_GPIE);
+
+ }
+
+ mhadd = IXGBE_READ_REG(hw, IXGBE_MHADD);
+
+ if (max_frame != (mhadd >> IXGBE_MHADD_MFS_SHIFT)) {
+ mhadd &= ~IXGBE_MHADD_MFS_MASK;
+ mhadd |= max_frame << IXGBE_MHADD_MFS_SHIFT;
+
+ IXGBE_WRITE_REG(hw, IXGBE_MHADD, mhadd);
+ }
+
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ txdctl = IXGBE_READ_REG(&adapter->hw, IXGBE_TXDCTL(i));
+ txdctl |= IXGBE_TXDCTL_ENABLE;
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_TXDCTL(i), txdctl);
+ }
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ rxdctl = IXGBE_READ_REG(&adapter->hw, IXGBE_RXDCTL(i));
+ rxdctl |= IXGBE_RXDCTL_ENABLE;
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_RXDCTL(i), rxdctl);
+ }
+
+ mod_timer(&adapter->watchdog_timer, jiffies);
+
+ netif_poll_enable(netdev);
+ if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED)
+ ixgbe_configure_msix(adapter);
+ else
+ ixgbe_configure_msi_and_legacy(adapter);
+ ixgbe_irq_enable(adapter);
+
+ return 0;
+}
+
+void ixgbe_reset(struct ixgbe_adapter *adapter)
+{
+ /* Allow time for pending master requests to run */
+ ixgbe_reset_hw(&adapter->hw);
+
+ if (ixgbe_init_hw(&adapter->hw))
+ DPRINTK(PROBE, ERR, "Hardware Error\n");
+
+ /* reprogram the RAR[0] in case user changed it. */
+ ixgbe_set_rar(&adapter->hw, 0, adapter->hw.mac.addr, 0, IXGBE_RAH_AV);
+
+}
+
+#ifdef CONFIG_PM
+static int ixgbe_resume(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ u32 err, num_rx_queues = adapter->num_rx_queues;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ err = pci_enable_device(pdev);
+ if (err) {
+ printk(KERN_ERR "ixgbe: Cannot enable PCI device from " \
+ "suspend\n");
+ return err;
+ }
+ pci_set_master(pdev);
+
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ pci_enable_wake(pdev, PCI_D3cold, 0);
+
+ if (netif_running(netdev)) {
+ err = ixgbe_request_irq(adapter, &num_rx_queues);
+ if (err)
+ return err;
+ }
+
+ ixgbe_reset(adapter);
+
+ if (netif_running(netdev))
+ ixgbe_up(adapter);
+
+ netif_device_attach(netdev);
+
+ return 0;
+}
+#endif
+
+/**
+ * ixgbe_clean_rx_ring - Free Rx Buffers per Queue
+ * @adapter: board private structure
+ * @rx_ring: ring to free buffers from
+ **/
+static void ixgbe_clean_rx_ring(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *rx_ring)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ unsigned long size;
+ unsigned int i;
+
+ /* Free all the Rx ring sk_buffs */
+
+ for (i = 0; i < rx_ring->count; i++) {
+ struct ixgbe_rx_buffer *rx_buffer_info;
+
+ rx_buffer_info = &rx_ring->rx_buffer_info[i];
+ if (rx_buffer_info->dma) {
+ pci_unmap_single(pdev, rx_buffer_info->dma,
+ adapter->rx_buf_len,
+ PCI_DMA_FROMDEVICE);
+ rx_buffer_info->dma = 0;
+ }
+ if (rx_buffer_info->skb) {
+ dev_kfree_skb(rx_buffer_info->skb);
+ rx_buffer_info->skb = NULL;
+ }
+ if (!rx_buffer_info->page)
+ continue;
+ pci_unmap_page(pdev, rx_buffer_info->page_dma, PAGE_SIZE,
+ PCI_DMA_FROMDEVICE);
+ rx_buffer_info->page_dma = 0;
+
+ put_page(rx_buffer_info->page);
+ rx_buffer_info->page = NULL;
+ }
+
+ size = sizeof(struct ixgbe_rx_buffer) * rx_ring->count;
+ memset(rx_ring->rx_buffer_info, 0, size);
+
+ /* Zero out the descriptor ring */
+ memset(rx_ring->desc, 0, rx_ring->size);
+
+ rx_ring->next_to_clean = 0;
+ rx_ring->next_to_use = 0;
+
+ writel(0, adapter->hw.hw_addr + rx_ring->head);
+ writel(0, adapter->hw.hw_addr + rx_ring->tail);
+}
+
+/**
+ * ixgbe_clean_tx_ring - Free Tx Buffers
+ * @adapter: board private structure
+ * @tx_ring: ring to be cleaned
+ **/
+static void ixgbe_clean_tx_ring(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *tx_ring)
+{
+ struct ixgbe_tx_buffer *tx_buffer_info;
+ unsigned long size;
+ unsigned int i;
+
+ /* Free all the Tx ring sk_buffs */
+
+ for (i = 0; i < tx_ring->count; i++) {
+ tx_buffer_info = &tx_ring->tx_buffer_info[i];
+ ixgbe_unmap_and_free_tx_resource(adapter, tx_buffer_info);
+ }
+
+ size = sizeof(struct ixgbe_tx_buffer) * tx_ring->count;
+ memset(tx_ring->tx_buffer_info, 0, size);
+
+ /* Zero out the descriptor ring */
+ memset(tx_ring->desc, 0, tx_ring->size);
+
+ tx_ring->next_to_use = 0;
+ tx_ring->next_to_clean = 0;
+
+ writel(0, adapter->hw.hw_addr + tx_ring->head);
+ writel(0, adapter->hw.hw_addr + tx_ring->tail);
+}
+
+/**
+ * ixgbe_clean_all_tx_rings - Free Tx Buffers for all queues
+ * @adapter: board private structure
+ **/
+static void ixgbe_clean_all_tx_rings(struct ixgbe_adapter *adapter)
+{
+ int i;
+
+ for (i = 0; i < adapter->num_tx_queues; i++)
+ ixgbe_clean_tx_ring(adapter, &adapter->tx_ring[i]);
+}
+
+/**
+ * ixgbe_clean_all_rx_rings - Free Rx Buffers for all queues
+ * @adapter: board private structure
+ **/
+static void ixgbe_clean_all_rx_rings(struct ixgbe_adapter *adapter)
+{
+ int i;
+
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ ixgbe_clean_rx_ring(adapter, &adapter->rx_ring[i]);
+}
+
+void ixgbe_down(struct ixgbe_adapter *adapter)
+{
+ struct net_device *netdev = adapter->netdev;
+
+ netif_poll_disable(netdev);
+
+ ixgbe_irq_disable(adapter);
+
+ del_timer_sync(&adapter->watchdog_timer);
+
+ netif_carrier_off(netdev);
+ netif_stop_queue(netdev);
+
+ ixgbe_reset(adapter);
+ ixgbe_clean_all_tx_rings(adapter);
+ ixgbe_clean_all_rx_rings(adapter);
+
+}
+
+static int ixgbe_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+#ifdef CONFIG_PM
+ int retval = 0;
+#endif
+
+ netif_device_detach(netdev);
+
+ if (netif_running(netdev)) {
+ ixgbe_down(adapter);
+ ixgbe_free_irq(adapter);
+ }
+
+#ifdef CONFIG_PM
+ retval = pci_save_state(pdev);
+ if (retval)
+ return retval;
+#endif
+
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ pci_enable_wake(pdev, PCI_D3cold, 0);
+
+ pci_disable_device(pdev);
+
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ return 0;
+}
+
+static void ixgbe_shutdown(struct pci_dev *pdev)
+{
+ ixgbe_suspend(pdev, PMSG_SUSPEND);
+}
+
+/**
+ * ixgbe_clean - NAPI Rx polling callback
+ * @adapter: board private structure
+ **/
+static int ixgbe_clean(struct net_device *netdev, int *budget)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ int work_to_do = min(*budget, netdev->quota);
+ int tx_cleaned, work_done = 0;
+
+ /* Keep link state information with original netdev */
+ if (!netif_carrier_ok(adapter->netdev))
+ goto quit_polling;
+
+ /* In non-MSIX case, there is no multi-Tx/Rx queue */
+ tx_cleaned = ixgbe_clean_tx_irq(adapter, adapter->tx_ring);
+ ixgbe_clean_rx_irq(adapter, adapter->rx_ring, &work_done, work_to_do);
+
+ *budget -= work_done;
+ netdev->quota -= work_done;
+
+ /* If no Tx and not enough Rx work done, exit the polling mode */
+ if ((!tx_cleaned && (work_done == 0)) ||
+ !netif_running(adapter->netdev)) {
+quit_polling:
+ netif_rx_complete(netdev);
+ ixgbe_irq_enable(adapter);
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * ixgbe_tx_timeout - Respond to a Tx Hang
+ * @netdev: network interface device structure
+ **/
+static void ixgbe_tx_timeout(struct net_device *netdev)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ /* Do the reset outside of interrupt context */
+ schedule_work(&adapter->reset_task);
+}
+
+static void ixgbe_reset_task(struct work_struct *work)
+{
+ struct ixgbe_adapter *adapter;
+ adapter = container_of(work, struct ixgbe_adapter, reset_task);
+
+ adapter->tx_timeout_count++;
+
+ ixgbe_down(adapter);
+ ixgbe_up(adapter);
+}
+
+/**
+ * ixgbe_alloc_queues - Allocate memory for all rings
+ * @adapter: board private structure to initialize
+ *
+ * We allocate one ring per queue at run-time since we don't know the
+ * number of queues at compile-time. The polling_netdev array is
+ * intended for Multiqueue, but should work fine with a single queue.
+ **/
+static int __devinit ixgbe_alloc_queues(struct ixgbe_adapter *adapter)
+{
+ int i;
+
+ adapter->tx_ring = kcalloc(adapter->num_tx_queues,
+ sizeof(struct ixgbe_ring), GFP_KERNEL);
+ if (!adapter->tx_ring)
+ return -ENOMEM;
+
+ for (i = 0; i < adapter->num_tx_queues; i++)
+ adapter->tx_ring[i].count = IXGBE_DEFAULT_TXD;
+
+ adapter->rx_ring = kcalloc(adapter->num_rx_queues,
+ sizeof(struct ixgbe_ring), GFP_KERNEL);
+ if (!adapter->rx_ring) {
+ kfree(adapter->tx_ring);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ adapter->rx_ring[i].adapter = adapter;
+ adapter->rx_ring[i].itr_register = IXGBE_EITR(i);
+ adapter->rx_ring[i].count = IXGBE_DEFAULT_RXD;
+ }
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_sw_init - Initialize general software structures (struct ixgbe_adapter)
+ * @adapter: board private structure to initialize
+ *
+ * ixgbe_sw_init initializes the Adapter private data structure.
+ * Fields are initialized based on PCI device information and
+ * OS network device settings (MTU size).
+ **/
+static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ struct pci_dev *pdev = adapter->pdev;
+
+ /* PCI config space info */
+
+ hw->vendor_id = pdev->vendor;
+ hw->device_id = pdev->device;
+ hw->subsystem_vendor_id = pdev->subsystem_vendor;
+ hw->subsystem_device_id = pdev->subsystem_device;
+
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &hw->revision_id);
+
+ if (ixgbe_init_shared_code(hw)) {
+ DPRINTK(PROBE, ERR, "Init_shared_code failed\n");
+ return -EIO;
+ }
+
+ hw->mac.link_mode_select = IXGBE_AUTOC_LMS_10G_LINK_NO_AN;
+ if (ixgbe_init_hw(hw)) {
+ DPRINTK(PROBE, ERR, "HW Init failed\n");
+ return -EIO;
+ }
+ if (ixgbe_setup_link_speed(hw, IXGBE_LINK_SPEED_10GB_FULL, true,
+ false)) {
+ DPRINTK(PROBE, ERR, "link_setup_speed FAILED\n");
+ return -EIO;
+ }
+
+ /* initialize eeprom parameters */
+ if (ixgbe_init_eeprom_params(hw)) {
+ IXGBE_ERR("EEPROM initialization failed\n");
+ return -EIO;
+ }
+
+ /* Set the default values */
+ adapter->num_rx_queues = IXGBE_DEFAULT_RXQ;
+ adapter->num_tx_queues = 1;
+ adapter->flags |= IXGBE_FLAG_RX_CSUM_ENABLED;
+
+ if (ixgbe_alloc_queues(adapter)) {
+ DPRINTK(PROBE, ERR, "Unable to allocate memory for queues\n");
+ return -ENOMEM;
+ }
+
+ atomic_set(&adapter->irq_sem, 1);
+
+ return 0;
+}
+
+/**
+ * ixgbe_setup_tx_resources - allocate Tx resources (Descriptors)
+ * @adapter: board private structure
+ * @txdr: tx descriptor ring (for a specific queue) to setup
+ *
+ * Return 0 on success, negative on failure
+ **/
+int ixgbe_setup_tx_resources(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *txdr)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ int size;
+
+ size = sizeof(struct ixgbe_tx_buffer) * txdr->count;
+ txdr->tx_buffer_info = vmalloc(size);
+ if (!txdr->tx_buffer_info) {
+ DPRINTK(PROBE, ERR,
+ "Unable to allocate memory for the transmit descriptor ring\n");
+ return -ENOMEM;
+ }
+ memset(txdr->tx_buffer_info, 0, size);
+
+ /* round up to nearest 4K */
+ txdr->size = txdr->count * sizeof(union ixgbe_adv_tx_desc);
+ txdr->size = ALIGN(txdr->size, 4096);
+
+ txdr->desc = pci_alloc_consistent(pdev, txdr->size, &txdr->dma);
+ if (!txdr->desc) {
+ vfree(txdr->tx_buffer_info);
+ DPRINTK(PROBE, ERR,
+ "Memory allocation failed for the tx desc ring\n");
+ return -ENOMEM;
+ }
+
+ txdr->adapter = adapter;
+ txdr->next_to_use = 0;
+ txdr->next_to_clean = 0;
+ spin_lock_init(&txdr->tx_lock);
+
+ return 0;
+}
+
+/**
+ * ixgbe_setup_rx_resources - allocate Rx resources (Descriptors)
+ * @adapter: board private structure
+ * @rxdr: rx descriptor ring (for a specific queue) to setup
+ *
+ * Returns 0 on success, negative on failure
+ **/
+int ixgbe_setup_rx_resources(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *rxdr)
+{
+ struct pci_dev *pdev = adapter->pdev;
+ int size, desc_len;
+
+ size = sizeof(struct ixgbe_rx_buffer) * rxdr->count;
+ rxdr->rx_buffer_info = vmalloc(size);
+ if (!rxdr->rx_buffer_info) {
+ DPRINTK(PROBE, ERR,
+ "vmalloc allocation failed for the rx desc ring\n");
+ return -ENOMEM;
+ }
+ memset(rxdr->rx_buffer_info, 0, size);
+
+ desc_len = sizeof(union ixgbe_adv_rx_desc);
+
+ /* Round up to nearest 4K */
+ rxdr->size = rxdr->count * desc_len;
+ rxdr->size = ALIGN(rxdr->size, 4096);
+
+ rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma);
+
+ if (!rxdr->desc) {
+ DPRINTK(PROBE, ERR,
+ "Memory allocation failed for the rx desc ring\n");
+ vfree(rxdr->rx_buffer_info);
+ return -ENOMEM;
+ }
+
+ rxdr->next_to_clean = 0;
+ rxdr->next_to_use = 0;
+ rxdr->adapter = adapter;
+
+ return 0;
+}
+
+/**
+ * ixgbe_free_tx_resources - Free Tx Resources per Queue
+ * @adapter: board private structure
+ * @tx_ring: Tx descriptor ring for a specific queue
+ *
+ * Free all transmit software resources
+ **/
+void ixgbe_free_tx_resources(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *tx_ring)
+{
+ struct pci_dev *pdev = adapter->pdev;
+
+ ixgbe_clean_tx_ring(adapter, tx_ring);
+
+ vfree(tx_ring->tx_buffer_info);
+ tx_ring->tx_buffer_info = NULL;
+
+ pci_free_consistent(pdev, tx_ring->size, tx_ring->desc, tx_ring->dma);
+
+ tx_ring->desc = NULL;
+}
+
+/**
+ * ixgbe_free_all_tx_resources - Free Tx Resources for All Queues
+ * @adapter: board private structure
+ *
+ * Free all transmit software resources
+ **/
+static void ixgbe_free_all_tx_resources(struct ixgbe_adapter *adapter)
+{
+ int i;
+
+ for (i = 0; i < adapter->num_tx_queues; i++)
+ ixgbe_free_tx_resources(adapter, &adapter->tx_ring[i]);
+}
+
+/**
+ * ixgbe_free_rx_resources - Free Rx Resources
+ * @adapter: board private structure
+ * @rx_ring: ring to clean the resources from
+ *
+ * Free all receive software resources
+ **/
+void ixgbe_free_rx_resources(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *rx_ring)
+{
+ struct pci_dev *pdev = adapter->pdev;
+
+
+ ixgbe_clean_rx_ring(adapter, rx_ring);
+
+ vfree(rx_ring->rx_buffer_info);
+ rx_ring->rx_buffer_info = NULL;
+
+ pci_free_consistent(pdev, rx_ring->size, rx_ring->desc, rx_ring->dma);
+
+ rx_ring->desc = NULL;
+}
+
+/**
+ * ixgbe_free_all_rx_resources - Free Rx Resources for All Queues
+ * @adapter: board private structure
+ *
+ * Free all receive software resources
+ **/
+static void ixgbe_free_all_rx_resources(struct ixgbe_adapter *adapter)
+{
+ int i;
+
+ for (i = 0; i < adapter->num_rx_queues; i++)
+ ixgbe_free_rx_resources(adapter, &adapter->rx_ring[i]);
+}
+
+/**
+ * ixgbe_setup_all_tx_resources - wrapper to allocate Tx resources
+ * (Descriptors) for all queues
+ * @adapter: board private structure
+ *
+ * If this function returns with an error, then it's possible one or
+ * more of the rings is populated (while the rest are not). It is the
+ * callers duty to clean those orphaned rings.
+ *
+ * Return 0 on success, negative on failure
+ **/
+static int ixgbe_setup_all_tx_resources(struct ixgbe_adapter *adapter)
+{
+ int i, err = 0;
+
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ err = ixgbe_setup_tx_resources(adapter, &adapter->tx_ring[i]);
+ if (err) {
+ DPRINTK(PROBE, ERR,
+ "Allocation for Tx Queue %u failed\n", i);
+ break;
+ }
+ }
+
+ return err;
+}
+
+/**
+ * ixgbe_setup_all_rx_resources - wrapper to allocate Rx resources
+ * (Descriptors) for all queues
+ * @adapter: board private structure
+ *
+ * If this function returns with an error, then it's possible one or
+ * more of the rings is populated (while the rest are not). It is the
+ * callers duty to clean those orphaned rings.
+ *
+ * Return 0 on success, negative on failure
+ **/
+
+static int ixgbe_setup_all_rx_resources(struct ixgbe_adapter *adapter)
+{
+ int i, err = 0;
+
+ for (i = 0; i < adapter->num_rx_queues; i++) {
+ err = ixgbe_setup_rx_resources(adapter, &adapter->rx_ring[i]);
+ if (err) {
+ DPRINTK(PROBE, ERR,
+ "Allocation for Rx Queue %u failed\n", i);
+ break;
+ }
+ }
+
+ return err;
+}
+
+/**
+ * ixgbe_change_mtu - Change the Maximum Transfer Unit
+ * @netdev: network interface device structure
+ * @new_mtu: new value for maximum frame size
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int ixgbe_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
+
+ if ((max_frame < (ETH_ZLEN + ETH_FCS_LEN)) ||
+ (max_frame > IXGBE_MAX_JUMBO_FRAME_SIZE))
+ return -EINVAL;
+
+ netdev->mtu = new_mtu;
+
+ if (netif_running(netdev)) {
+ ixgbe_down(adapter);
+ ixgbe_up(adapter);
+ }
+
+ return 0;
+}
+
+/**
+ * ixgbe_open - Called when a network interface is made active
+ * @netdev: network interface device structure
+ *
+ * Returns 0 on success, negative value on failure
+ *
+ * The open entry point is called when a network interface is made
+ * active by the system (IFF_UP). At this point all resources needed
+ * for transmit and receive operations are allocated, the interrupt
+ * handler is registered with the OS, the watchdog timer is started,
+ * and the stack is notified that the interface is ready.
+ **/
+static int ixgbe_open(struct net_device *netdev)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ int err, i;
+ u32 ctrl_ext, num_rx_queues = adapter->num_rx_queues;
+
+ /* Let firmware know the driver has taken over */
+ ctrl_ext = IXGBE_READ_REG(&adapter->hw, IXGBE_CTRL_EXT);
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_CTRL_EXT,
+ ctrl_ext | IXGBE_CTRL_EXT_DRV_LOAD);
+
+ /* allocate transmit descriptors */
+ err = ixgbe_setup_all_tx_resources(adapter);
+ if (err)
+ goto err_setup_tx;
+
+ /* allocate receive descriptors */
+ err = ixgbe_setup_all_rx_resources(adapter);
+ if (err)
+ goto err_setup_rx;
+
+ err = ixgbe_request_irq(adapter, &num_rx_queues);
+ if (err)
+ goto err_req_irq;
+
+ /* ixgbe_request might have reduced num_rx_queues */
+ if (num_rx_queues < adapter->num_rx_queues) {
+ /* released the remaining rx queue resource */
+ for (i = num_rx_queues; i < adapter->num_rx_queues; i++)
+ ixgbe_free_rx_resources(adapter, &adapter->rx_ring[i]);
+ adapter->num_rx_queues = num_rx_queues;
+ }
+
+ err = ixgbe_up(adapter);
+ if (err)
+ goto err_up;
+
+ return IXGBE_SUCCESS;
+
+err_up:
+ ixgbe_free_irq(adapter);
+err_req_irq:
+ ixgbe_free_all_rx_resources(adapter);
+err_setup_rx:
+ ixgbe_free_all_tx_resources(adapter);
+err_setup_tx:
+ ixgbe_reset(adapter);
+
+ return err;
+}
+
+/**
+ * ixgbe_close - Disables a network interface
+ * @netdev: network interface device structure
+ *
+ * Returns 0, this is not allowed to fail
+ *
+ * The close entry point is called when an interface is de-activated
+ * by the OS. The hardware is still under the drivers control, but
+ * needs to be disabled. A global MAC reset is issued to stop the
+ * hardware, and all transmit and receive resources are freed.
+ **/
+static int ixgbe_close(struct net_device *netdev)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ u32 ctrl_ext;
+
+ ixgbe_down(adapter);
+ ixgbe_free_irq(adapter);
+
+ ixgbe_free_all_tx_resources(adapter);
+ ixgbe_free_all_rx_resources(adapter);
+
+ ctrl_ext = IXGBE_READ_REG(&adapter->hw, IXGBE_CTRL_EXT);
+ IXGBE_WRITE_REG(&adapter->hw, IXGBE_CTRL_EXT,
+ ctrl_ext & ~IXGBE_CTRL_EXT_DRV_LOAD);
+
+ return 0;
+}
+
+/**
+ * ixgbe_update_stats - Update the board statistics counters.
+ * @adapter: board private structure
+ **/
+void ixgbe_update_stats(struct ixgbe_adapter *adapter)
+{
+ struct ixgbe_hw *hw = &adapter->hw;
+ u64 good_rx, missed_rx, bprc;
+
+ adapter->stats.crcerrs += IXGBE_READ_REG(hw, IXGBE_CRCERRS);
+ good_rx = IXGBE_READ_REG(hw, IXGBE_GPRC);
+ missed_rx = IXGBE_READ_REG(hw, IXGBE_MPC(0));
+ missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(1));
+ missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(2));
+ missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(3));
+ missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(4));
+ missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(5));
+ missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(6));
+ missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(7));
+ adapter->stats.gprc += (good_rx - missed_rx);
+
+ adapter->stats.mpc[0] += missed_rx;
+ adapter->stats.gorc += IXGBE_READ_REG(hw, IXGBE_GORCH);
+ bprc = IXGBE_READ_REG(hw, IXGBE_BPRC);
+ adapter->stats.bprc += bprc;
+ adapter->stats.mprc += IXGBE_READ_REG(hw, IXGBE_MPRC);
+ adapter->stats.mprc -= bprc;
+ adapter->stats.roc += IXGBE_READ_REG(hw, IXGBE_ROC);
+ adapter->stats.prc64 += IXGBE_READ_REG(hw, IXGBE_PRC64);
+ adapter->stats.prc127 += IXGBE_READ_REG(hw, IXGBE_PRC127);
+ adapter->stats.prc255 += IXGBE_READ_REG(hw, IXGBE_PRC255);
+ adapter->stats.prc511 += IXGBE_READ_REG(hw, IXGBE_PRC511);
+ adapter->stats.prc1023 += IXGBE_READ_REG(hw, IXGBE_PRC1023);
+ adapter->stats.prc1522 += IXGBE_READ_REG(hw, IXGBE_PRC1522);
+
+ adapter->stats.rlec += IXGBE_READ_REG(hw, IXGBE_RLEC);
+ adapter->stats.lxonrxc += IXGBE_READ_REG(hw, IXGBE_LXONRXC);
+ adapter->stats.lxontxc += IXGBE_READ_REG(hw, IXGBE_LXONTXC);
+ adapter->stats.lxoffrxc += IXGBE_READ_REG(hw, IXGBE_LXOFFRXC);
+ adapter->stats.lxofftxc += IXGBE_READ_REG(hw, IXGBE_LXOFFTXC);
+ adapter->stats.ruc += IXGBE_READ_REG(hw, IXGBE_RUC);
+ adapter->stats.gptc += IXGBE_READ_REG(hw, IXGBE_GPTC);
+ adapter->stats.gotc += IXGBE_READ_REG(hw, IXGBE_GOTCH);
+ adapter->stats.rnbc[0] += IXGBE_READ_REG(hw, IXGBE_RNBC(0));
+ adapter->stats.ruc += IXGBE_READ_REG(hw, IXGBE_RUC);
+ adapter->stats.rfc += IXGBE_READ_REG(hw, IXGBE_RFC);
+ adapter->stats.rjc += IXGBE_READ_REG(hw, IXGBE_RJC);
+ adapter->stats.tor += IXGBE_READ_REG(hw, IXGBE_TORH);
+ adapter->stats.tpr += IXGBE_READ_REG(hw, IXGBE_TPR);
+ adapter->stats.ptc64 += IXGBE_READ_REG(hw, IXGBE_PTC64);
+ adapter->stats.ptc127 += IXGBE_READ_REG(hw, IXGBE_PTC127);
+ adapter->stats.ptc255 += IXGBE_READ_REG(hw, IXGBE_PTC255);
+ adapter->stats.ptc511 += IXGBE_READ_REG(hw, IXGBE_PTC511);
+ adapter->stats.ptc1023 += IXGBE_READ_REG(hw, IXGBE_PTC1023);
+ adapter->stats.ptc1522 += IXGBE_READ_REG(hw, IXGBE_PTC1522);
+ adapter->stats.mptc += IXGBE_READ_REG(hw, IXGBE_MPTC);
+ adapter->stats.bptc += IXGBE_READ_REG(hw, IXGBE_BPTC);
+
+ /* Fill out the OS statistics structure */
+ adapter->net_stats.rx_packets = adapter->stats.gprc;
+ adapter->net_stats.tx_packets = adapter->stats.gptc;
+ adapter->net_stats.rx_bytes = adapter->stats.gorc;
+ adapter->net_stats.tx_bytes = adapter->stats.gotc;
+ adapter->net_stats.multicast = adapter->stats.mprc;
+
+ /* Rx Errors */
+ adapter->net_stats.rx_errors = adapter->stats.crcerrs +
+ adapter->stats.rlec;
+ adapter->net_stats.rx_dropped = 0;
+ adapter->net_stats.rx_length_errors = adapter->stats.rlec;
+ adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs;
+ adapter->net_stats.rx_missed_errors = adapter->stats.mpc[0];
+
+}
+
+/**
+ * ixgbe_watchdog - Timer Call-back
+ * @data: pointer to adapter cast into an unsigned long
+ **/
+static void ixgbe_watchdog(unsigned long data)
+{
+ struct ixgbe_adapter *adapter = (struct ixgbe_adapter *)data;
+ struct net_device *netdev = adapter->netdev;
+ bool link_up;
+ u32 link_speed = 0;
+
+ ixgbe_check_link(&adapter->hw, &(link_speed), &link_up);
+
+ if (link_up) {
+ if (!netif_carrier_ok(netdev)) {
+ DPRINTK(LINK, INFO,
+ "NIC Link is Up %d Gbps Full Duplex\n",
+ ((link_speed ==
+ IXGBE_LINK_SPEED_10GB_FULL) ? 10 : 1));
+
+ netif_carrier_on(netdev);
+ netif_wake_queue(netdev);
+ } else {
+ /* Force detection of hung controller */
+ adapter->detect_tx_hung = true;
+ }
+ } else {
+ if (netif_carrier_ok(netdev)) {
+ DPRINTK(LINK, INFO, "NIC Link is Down\n");
+ netif_carrier_off(netdev);
+ netif_stop_queue(netdev);
+ }
+ }
+
+ ixgbe_update_stats(adapter);
+
+ /* Reset the timer */
+ mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + 2 * HZ));
+}
+
+#define IXGBE_MAX_TXD_PWR 14
+#define IXGBE_MAX_DATA_PER_TXD (1 << IXGBE_MAX_TXD_PWR)
+
+/* Tx Descriptors needed, worst case */
+#define TXD_USE_COUNT(S) (((S) >> IXGBE_MAX_TXD_PWR) + \
+ (((S) & (IXGBE_MAX_DATA_PER_TXD - 1)) ? 1 : 0))
+#define DESC_NEEDED TXD_USE_COUNT(IXGBE_MAX_DATA_PER_TXD) /* skb->data */ + \
+ MAX_SKB_FRAGS * TXD_USE_COUNT(PAGE_SIZE) + 1 /* for context */
+
+static int ixgbe_tso(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *tx_ring, struct sk_buff *skb,
+ u32 tx_flags, u8 *hdr_len)
+{
+ struct ixgbe_adv_tx_context_desc *context_desc;
+ unsigned int i;
+ int err;
+ struct ixgbe_tx_buffer *tx_buffer_info;
+ u32 vlan_macip_lens = 0, type_tucmd_mlhl = 0;
+ u32 mss_l4len_idx = 0, l4len;
+ *hdr_len = 0;
+
+ if (skb_is_gso(skb)) {
+ if (skb_header_cloned(skb)) {
+ err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+ if (err)
+ return err;
+ }
+ l4len = tcp_hdrlen(skb);
+ *hdr_len += l4len;
+
+ if (skb->protocol == ntohs(ETH_P_IP)) {
+ struct iphdr *iph = ip_hdr(skb);
+ iph->tot_len = 0;
+ iph->check = 0;
+ tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
+ iph->daddr, 0,
+ IPPROTO_TCP,
+ 0);
+ adapter->hw_tso_ctxt++;
+ } else if (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6) {
+ ipv6_hdr(skb)->payload_len = 0;
+ tcp_hdr(skb)->check =
+ ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+ &ipv6_hdr(skb)->daddr,
+ 0, IPPROTO_TCP, 0);
+ adapter->hw_tso6_ctxt++;
+ }
+
+ i = tx_ring->next_to_use;
+
+ tx_buffer_info = &tx_ring->tx_buffer_info[i];
+ context_desc = IXGBE_TX_CTXTDESC_ADV(*tx_ring, i);
+
+ /* VLAN MACLEN IPLEN */
+ if (tx_flags & IXGBE_TX_FLAGS_VLAN)
+ vlan_macip_lens |=
+ (tx_flags & IXGBE_TX_FLAGS_VLAN_MASK);
+ vlan_macip_lens |= ((skb_network_offset(skb)) <<
+ IXGBE_ADVTXD_MACLEN_SHIFT);
+ *hdr_len += skb_network_offset(skb);
+ vlan_macip_lens |=
+ (skb_transport_header(skb) - skb_network_header(skb));
+ *hdr_len +=
+ (skb_transport_header(skb) - skb_network_header(skb));
+ context_desc->vlan_macip_lens = cpu_to_le32(vlan_macip_lens);
+ context_desc->seqnum_seed = 0;
+
+ /* ADV DTYP TUCMD MKRLOC/ISCSIHEDLEN */
+ type_tucmd_mlhl |= (IXGBE_TXD_CMD_DEXT |
+ IXGBE_ADVTXD_DTYP_CTXT);
+
+ if (skb->protocol == ntohs(ETH_P_IP))
+ type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_IPV4;
+ type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_TCP;
+ context_desc->type_tucmd_mlhl = cpu_to_le32(type_tucmd_mlhl);
+
+ /* MSS L4LEN IDX */
+ mss_l4len_idx |=
+ (skb_shinfo(skb)->gso_size << IXGBE_ADVTXD_MSS_SHIFT);
+ mss_l4len_idx |= (l4len << IXGBE_ADVTXD_L4LEN_SHIFT);
+ context_desc->mss_l4len_idx = cpu_to_le32(mss_l4len_idx);
+
+ tx_buffer_info->time_stamp = jiffies;
+ tx_buffer_info->next_to_watch = i;
+
+ i++;
+ if (i == tx_ring->count)
+ i = 0;
+ tx_ring->next_to_use = i;
+
+ return true;
+ }
+ return false;
+}
+
+static bool ixgbe_tx_csum(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *tx_ring,
+ struct sk_buff *skb, u32 tx_flags)
+{
+ struct ixgbe_adv_tx_context_desc *context_desc;
+ unsigned int i;
+ struct ixgbe_tx_buffer *tx_buffer_info;
+ u32 vlan_macip_lens = 0, type_tucmd_mlhl = 0;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL ||
+ (tx_flags & IXGBE_TX_FLAGS_VLAN)) {
+ i = tx_ring->next_to_use;
+ tx_buffer_info = &tx_ring->tx_buffer_info[i];
+ context_desc = IXGBE_TX_CTXTDESC_ADV(*tx_ring, i);
+
+ if (tx_flags & IXGBE_TX_FLAGS_VLAN)
+ vlan_macip_lens |=
+ (tx_flags & IXGBE_TX_FLAGS_VLAN_MASK);
+ vlan_macip_lens |= (skb_network_offset(skb) <<
+ IXGBE_ADVTXD_MACLEN_SHIFT);
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ vlan_macip_lens |= (skb_transport_header(skb) -
+ skb_network_header(skb));
+
+ context_desc->vlan_macip_lens = cpu_to_le32(vlan_macip_lens);
+ context_desc->seqnum_seed = 0;
+
+ type_tucmd_mlhl |= (IXGBE_TXD_CMD_DEXT |
+ IXGBE_ADVTXD_DTYP_CTXT);
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ if (skb->protocol == ntohs(ETH_P_IP))
+ type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_IPV4;
+
+ if (skb->sk->sk_protocol == IPPROTO_TCP)
+ type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_TCP;
+ }
+
+ context_desc->type_tucmd_mlhl = cpu_to_le32(type_tucmd_mlhl);
+ context_desc->mss_l4len_idx = 0;
+
+ tx_buffer_info->time_stamp = jiffies;
+ tx_buffer_info->next_to_watch = i;
+ adapter->hw_csum_tx_good++;
+ i++;
+ if (i == tx_ring->count)
+ i = 0;
+ tx_ring->next_to_use = i;
+
+ return true;
+ }
+ return false;
+}
+
+static int ixgbe_tx_map(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *tx_ring,
+ struct sk_buff *skb, unsigned int first)
+{
+ struct ixgbe_tx_buffer *tx_buffer_info;
+ unsigned int len = skb->len;
+ unsigned int offset = 0, size, count = 0, i;
+ unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
+ unsigned int f;
+
+ len -= skb->data_len;
+
+ i = tx_ring->next_to_use;
+
+ while (len) {
+ tx_buffer_info = &tx_ring->tx_buffer_info[i];
+ size = min(len, (uint)IXGBE_MAX_DATA_PER_TXD);
+
+ tx_buffer_info->length = size;
+ tx_buffer_info->dma = pci_map_single(adapter->pdev,
+ skb->data + offset,
+ size, PCI_DMA_TODEVICE);
+ tx_buffer_info->time_stamp = jiffies;
+ tx_buffer_info->next_to_watch = i;
+
+ len -= size;
+ offset += size;
+ count++;
+ i++;
+ if (i == tx_ring->count)
+ i = 0;
+ }
+
+ for (f = 0; f < nr_frags; f++) {
+ struct skb_frag_struct *frag;
+
+ frag = &skb_shinfo(skb)->frags[f];
+ len = frag->size;
+ offset = frag->page_offset;
+
+ while (len) {
+ tx_buffer_info = &tx_ring->tx_buffer_info[i];
+ size = min(len, (uint)IXGBE_MAX_DATA_PER_TXD);
+
+ tx_buffer_info->length = size;
+ tx_buffer_info->dma = pci_map_page(adapter->pdev,
+ frag->page,
+ offset,
+ size, PCI_DMA_TODEVICE);
+ tx_buffer_info->time_stamp = jiffies;
+ tx_buffer_info->next_to_watch = i;
+
+ len -= size;
+ offset += size;
+ count++;
+ i++;
+ if (i == tx_ring->count)
+ i = 0;
+ }
+ }
+ if (i == 0)
+ i = tx_ring->count - 1;
+ else
+ i = i - 1;
+ tx_ring->tx_buffer_info[i].skb = skb;
+ tx_ring->tx_buffer_info[first].next_to_watch = i;
+
+ return count;
+}
+
+static void ixgbe_tx_queue(struct ixgbe_adapter *adapter,
+ struct ixgbe_ring *tx_ring,
+ int tx_flags, int count, u32 paylen, u8 hdr_len)
+{
+ union ixgbe_adv_tx_desc *tx_desc = NULL;
+ struct ixgbe_tx_buffer *tx_buffer_info;
+ u32 olinfo_status = 0, cmd_type_len = 0;
+ unsigned int i;
+ u32 txd_cmd = IXGBE_TXD_CMD_EOP | IXGBE_TXD_CMD_RS | IXGBE_TXD_CMD_IFCS;
+
+ cmd_type_len |= IXGBE_ADVTXD_DTYP_DATA;
+
+ cmd_type_len |= IXGBE_ADVTXD_DCMD_IFCS | IXGBE_ADVTXD_DCMD_DEXT;
+
+ if (tx_flags & IXGBE_TX_FLAGS_VLAN)
+ cmd_type_len |= IXGBE_ADVTXD_DCMD_VLE;
+
+ if (tx_flags & IXGBE_TX_FLAGS_TSO) {
+ cmd_type_len |= IXGBE_ADVTXD_DCMD_TSE;
+
+ olinfo_status |= IXGBE_TXD_POPTS_TXSM <<
+ IXGBE_ADVTXD_POPTS_SHIFT;
+
+ if (tx_flags & IXGBE_TX_FLAGS_IPV4)
+ olinfo_status |= IXGBE_TXD_POPTS_IXSM <<
+ IXGBE_ADVTXD_POPTS_SHIFT;
+
+ } else if (tx_flags & IXGBE_TX_FLAGS_CSUM)
+ olinfo_status |= IXGBE_TXD_POPTS_TXSM <<
+ IXGBE_ADVTXD_POPTS_SHIFT;
+
+ olinfo_status |= ((paylen - hdr_len) << IXGBE_ADVTXD_PAYLEN_SHIFT);
+
+ i = tx_ring->next_to_use;
+ while (count--) {
+ tx_buffer_info = &tx_ring->tx_buffer_info[i];
+ tx_desc = IXGBE_TX_DESC_ADV(*tx_ring, i);
+ tx_desc->read.buffer_addr = cpu_to_le64(tx_buffer_info->dma);
+ tx_desc->read.cmd_type_len =
+ cpu_to_le32(cmd_type_len | tx_buffer_info->length);
+ tx_desc->read.olinfo_status = cpu_to_le32(olinfo_status);
+
+ i++;
+ if (i == tx_ring->count)
+ i = 0;
+ }
+
+ tx_desc->read.cmd_type_len |= cpu_to_le32(txd_cmd);
+
+ /*
+ * Force memory writes to complete before letting h/w
+ * know there are new descriptors to fetch. (Only
+ * applicable for weak-ordered memory model archs,
+ * such as IA-64).
+ */
+ wmb();
+
+ tx_ring->next_to_use = i;
+ writel(i, adapter->hw.hw_addr + tx_ring->tail);
+}
+
+static int ixgbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ struct ixgbe_ring *tx_ring;
+ unsigned int len = skb->len;
+ unsigned int first;
+ unsigned int tx_flags = 0;
+ unsigned long flags = 0;
+ u8 hdr_len;
+ int tso;
+ unsigned int mss = 0;
+ int count = 0;
+ unsigned int f;
+ unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
+ len -= skb->data_len;
+
+ tx_ring = adapter->tx_ring;
+
+ if (skb->len <= 0) {
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+ mss = skb_shinfo(skb)->gso_size;
+
+ if (mss)
+ count++;
+ else if (skb->ip_summed == CHECKSUM_PARTIAL)
+ count++;
+
+ count += TXD_USE_COUNT(len);
+ for (f = 0; f < nr_frags; f++)
+ count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size);
+
+ spin_lock_irqsave(&tx_ring->tx_lock, flags);
+ if (IXGBE_DESC_UNUSED(tx_ring) < (count + 2)) {
+ adapter->tx_busy++;
+ netif_stop_queue(netdev);
+ spin_unlock_irqrestore(&tx_ring->tx_lock, flags);
+ return NETDEV_TX_BUSY;
+ }
+ spin_unlock_irqrestore(&tx_ring->tx_lock, flags);
+ if (adapter->vlgrp && vlan_tx_tag_present(skb)) {
+ tx_flags |= IXGBE_TX_FLAGS_VLAN;
+ tx_flags |= (vlan_tx_tag_get(skb) << IXGBE_TX_FLAGS_VLAN_SHIFT);
+ }
+
+ if (skb->protocol == ntohs(ETH_P_IP))
+ tx_flags |= IXGBE_TX_FLAGS_IPV4;
+ first = tx_ring->next_to_use;
+ tso = ixgbe_tso(adapter, tx_ring, skb, tx_flags, &hdr_len);
+ if (tso < 0) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ if (tso)
+ tx_flags |= IXGBE_TX_FLAGS_TSO;
+ else if (ixgbe_tx_csum(adapter, tx_ring, skb, tx_flags) &&
+ (skb->ip_summed == CHECKSUM_PARTIAL))
+ tx_flags |= IXGBE_TX_FLAGS_CSUM;
+
+ ixgbe_tx_queue(adapter, tx_ring, tx_flags,
+ ixgbe_tx_map(adapter, tx_ring, skb, first),
+ skb->len, hdr_len);
+
+ netdev->trans_start = jiffies;
+
+ spin_lock_irqsave(&tx_ring->tx_lock, flags);
+ /* Make sure there is space in the ring for the next send. */
+ if (IXGBE_DESC_UNUSED(tx_ring) < DESC_NEEDED)
+ netif_stop_queue(netdev);
+ spin_unlock_irqrestore(&tx_ring->tx_lock, flags);
+
+ return NETDEV_TX_OK;
+}
+
+/**
+ * ixgbe_get_stats - Get System Network Statistics
+ * @netdev: network interface device structure
+ *
+ * Returns the address of the device statistics structure.
+ * The statistics are actually updated from the timer callback.
+ **/
+static struct net_device_stats *ixgbe_get_stats(struct net_device *netdev)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ /* only return the current stats */
+ return &adapter->net_stats;
+}
+
+/**
+ * ixgbe_set_mac - Change the Ethernet Address of the NIC
+ * @netdev: network interface device structure
+ * @p: pointer to an address structure
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int ixgbe_set_mac(struct net_device *netdev, void *p)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+ struct sockaddr *addr = p;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+ memcpy(adapter->hw.mac.addr, addr->sa_data, netdev->addr_len);
+
+ ixgbe_set_rar(&adapter->hw, 0, adapter->hw.mac.addr, 0, IXGBE_RAH_AV);
+
+ return 0;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling 'interrupt' - used by things like netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+static void ixgbe_netpoll(struct net_device *netdev)
+{
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ disable_irq(adapter->pdev->irq);
+ adapter->flags |= IXGBE_FLAG_IN_NETPOLL;
+ ixgbe_intr(adapter->pdev->irq, netdev);
+ adapter->flags &= ~IXGBE_FLAG_IN_NETPOLL;
+ enable_irq(adapter->pdev->irq);
+}
+#endif
+
+/**
+ * ixgbe_probe - Device Initialization Routine
+ * @pdev: PCI device information struct
+ * @ent: entry in ixgbe_pci_tbl
+ *
+ * Returns 0 on success, negative on failure
+ *
+ * ixgbe_probe initializes an adapter identified by a pci_dev structure.
+ * The OS initialization, configuring of the adapter private structure,
+ * and a hardware reset occur.
+ **/
+static int __devinit ixgbe_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *netdev;
+ struct ixgbe_adapter *adapter = NULL;
+ unsigned long mmio_start, mmio_len;
+ static int cards_found;
+ int i, err, pci_using_dac;
+ struct ixgbe_hw *hw;
+
+ err = pci_enable_device(pdev);
+ if (err)
+ return err;
+
+ if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK) &&
+ !pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK)) {
+ pci_using_dac = 1;
+ } else {
+ err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (err) {
+ err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+ if (err) {
+ IXGBE_ERR("No usable DMA configuration,"
+ "aborting\n");
+ goto err_dma;
+ }
+ }
+ pci_using_dac = 0;
+ }
+
+ err = pci_request_regions(pdev, ixgbe_driver_name);
+ if (err) {
+ IXGBE_ERR("pci_request_regions failed 0x%x\n", err);
+ goto err_pci_reg;
+ }
+
+ pci_set_master(pdev);
+
+ netdev = alloc_etherdev(sizeof(struct ixgbe_adapter));
+ if (!netdev) {
+ err = -ENOMEM;
+ goto err_alloc_etherdev;
+ }
+
+ SET_MODULE_OWNER(netdev);
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+
+ pci_set_drvdata(pdev, netdev);
+ adapter = netdev_priv(netdev);
+
+ adapter->netdev = netdev;
+ adapter->pdev = pdev;
+ hw = &adapter->hw;
+ hw->back = adapter;
+ adapter->msg_enable = (1 << DEFAULT_DEBUG_LEVEL_SHIFT) - 1;
+
+ mmio_start = pci_resource_start(pdev, 0);
+ mmio_len = pci_resource_len(pdev, 0);
+
+ hw->hw_addr = ioremap(mmio_start, mmio_len);
+ if (!hw->hw_addr) {
+ err = -EIO;
+ goto err_ioremap;
+ }
+
+ for (i = 1; i <= 5; i++) {
+ if (pci_resource_len(pdev, i) == 0)
+ continue;
+ }
+
+ netdev->open = &ixgbe_open;
+ netdev->stop = &ixgbe_close;
+ netdev->hard_start_xmit = &ixgbe_xmit_frame;
+ netdev->get_stats = &ixgbe_get_stats;
+ netdev->set_multicast_list = &ixgbe_set_multi;
+ netdev->set_mac_address = &ixgbe_set_mac;
+ netdev->change_mtu = &ixgbe_change_mtu;
+ ixgbe_set_ethtool_ops(netdev);
+ netdev->tx_timeout = &ixgbe_tx_timeout;
+ netdev->watchdog_timeo = 5 * HZ;
+ netdev->poll = &ixgbe_clean;
+ netdev->weight = 64;
+ netdev->vlan_rx_register = ixgbe_vlan_rx_register;
+ netdev->vlan_rx_add_vid = ixgbe_vlan_rx_add_vid;
+ netdev->vlan_rx_kill_vid = ixgbe_vlan_rx_kill_vid;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ netdev->poll_controller = ixgbe_netpoll;
+#endif
+ strcpy(netdev->name, pci_name(pdev));
+
+ netdev->mem_start = mmio_start;
+ netdev->mem_end = mmio_start + mmio_len;
+
+ adapter->bd_number = cards_found;
+
+ /* setup the private structure */
+ err = ixgbe_sw_init(adapter);
+ if (err)
+ goto err_sw_init;
+
+ netdev->features = NETIF_F_SG |
+ NETIF_F_HW_CSUM |
+ NETIF_F_HW_VLAN_TX |
+ NETIF_F_HW_VLAN_RX |
+ NETIF_F_HW_VLAN_FILTER;
+
+ netdev->features |= NETIF_F_TSO;
+
+ netdev->features |= NETIF_F_TSO6;
+ if (pci_using_dac)
+ netdev->features |= NETIF_F_HIGHDMA;
+
+
+ /* make sure the EEPROM is good */
+ if (ixgbe_validate_eeprom_checksum(hw, NULL) < 0) {
+ DPRINTK(PROBE, ERR, "The EEPROM Checksum Is Not Valid\n");
+ err = -EIO;
+ goto err_eeprom;
+ }
+
+ memcpy(netdev->dev_addr, hw->mac.perm_addr, netdev->addr_len);
+ memcpy(netdev->perm_addr, hw->mac.perm_addr, netdev->addr_len);
+
+ if (ixgbe_validate_mac_addr(netdev->perm_addr)) {
+ err = -EIO;
+ goto err_eeprom;
+ }
+
+ ixgbe_get_bus_info(hw);
+
+ init_timer(&adapter->watchdog_timer);
+ adapter->watchdog_timer.function = &ixgbe_watchdog;
+ adapter->watchdog_timer.data = (unsigned long)adapter;
+
+ INIT_WORK(&adapter->reset_task, ixgbe_reset_task);
+
+ /* initialize default flow control settings */
+ hw->fc.original_type = ixgbe_fc_full;
+ hw->fc.type = ixgbe_fc_full;
+ hw->fc.high_water = DEFAULT_FCRTH;
+ hw->fc.low_water = DEFAULT_FCRTL;
+ hw->fc.pause_time = DEFAULT_FCPAUSE;
+
+ /* interrupt throttling */
+ adapter->rx_eitr = (1000000 / IXGBE_ITR_DEFAULT_USECS);
+ adapter->tx_eitr = (1000000 / IXGBE_ITR_DEFAULT_USECS);
+
+ /* print bus type/speed/width info */
+ DPRINTK(PROBE, INFO, "(PCI Express:%s:%s) \n",
+ ((hw->bus.speed == ixgbe_bus_speed_2500) ? "2.5Gb/s":"Unknown"),
+ (hw->bus.width == ixgbe_bus_width_pcie_x8) ? "Width x8" :
+ (hw->bus.width == ixgbe_bus_width_pcie_x4) ? "Width x4" :
+ (hw->bus.width == ixgbe_bus_width_pcie_x1) ? "Width x1" :
+ ("Unknown"));
+
+ /* reset the hardware with the new settings */
+ ixgbe_reset(adapter);
+
+ netif_carrier_off(netdev);
+ netif_stop_queue(netdev);
+ netif_poll_disable(netdev);
+
+ strcpy(netdev->name, "eth%d");
+ err = register_netdev(netdev);
+ if (err)
+ goto err_register;
+
+
+ cards_found++;
+ return 0;
+
+err_register:
+err_sw_init:
+err_eeprom:
+ iounmap(hw->hw_addr);
+err_ioremap:
+ free_netdev(netdev);
+err_alloc_etherdev:
+ pci_release_regions(pdev);
+err_pci_reg:
+err_dma:
+ pci_disable_device(pdev);
+ return err;
+}
+
+/**
+ * ixgbe_remove - Device Removal Routine
+ * @pdev: PCI device information struct
+ *
+ * ixgbe_remove is called by the PCI subsystem to alert the driver
+ * that it should release a PCI device. The could be caused by a
+ * Hot-Plug event, or because the driver is going to be removed from
+ * memory.
+ **/
+static void __devexit ixgbe_remove(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+ unregister_netdev(netdev);
+
+ kfree(adapter->tx_ring);
+ kfree(adapter->rx_ring);
+
+ iounmap(adapter->hw.hw_addr);
+ pci_release_regions(pdev);
+
+ free_netdev(netdev);
+}
+
+u16 ixgbe_read_pci_cfg_word(struct ixgbe_hw *hw, u32 reg)
+{
+ u16 value;
+ struct ixgbe_adapter *adapter = hw->back;
+
+ pci_read_config_word(adapter->pdev, reg, &value);
+ return value;
+}
+
+/**
+ * ixgbe_io_error_detected - called when PCI error is detected
+ * @pdev: Pointer to PCI device
+ * @state: The current pci connection state
+ *
+ * This function is called after a PCI bus error affecting
+ * this device has been detected.
+ */
+static pci_ers_result_t ixgbe_io_error_detected(struct pci_dev *pdev,
+ pci_channel_state_t state)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct ixgbe_adapter *adapter = netdev->priv;
+
+ netif_device_detach(netdev);
+
+ if (netif_running(netdev))
+ ixgbe_down(adapter);
+ pci_disable_device(pdev);
+
+ /* Request a slot slot reset. */
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/**
+ * ixgbe_io_slot_reset - called after the pci bus has been reset.
+ * @pdev: Pointer to PCI device
+ *
+ * Restart the card from scratch, as if from a cold-boot.
+ */
+static pci_ers_result_t ixgbe_io_slot_reset(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct ixgbe_adapter *adapter = netdev->priv;
+
+ if (pci_enable_device(pdev)) {
+ DPRINTK(PROBE, ERR,
+ "Cannot re-enable PCI device after reset.\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+ pci_set_master(pdev);
+
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ pci_enable_wake(pdev, PCI_D3cold, 0);
+
+ ixgbe_reset(adapter);
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+/**
+ * ixgbe_io_resume - called when traffic can start flowing again.
+ * @pdev: Pointer to PCI device
+ *
+ * This callback is called when the error recovery driver tells us that
+ * its OK to resume normal operation.
+ */
+static void ixgbe_io_resume(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+ struct ixgbe_adapter *adapter = netdev->priv;
+
+ if (netif_running(netdev)) {
+ if (ixgbe_up(adapter)) {
+ DPRINTK(PROBE, INFO, "ixgbe_up failed after reset\n");
+ return;
+ }
+ }
+
+ netif_device_attach(netdev);
+
+}
+
+static struct pci_error_handlers ixgbe_err_handler = {
+ .error_detected = ixgbe_io_error_detected,
+ .slot_reset = ixgbe_io_slot_reset,
+ .resume = ixgbe_io_resume,
+};
+
+static struct pci_driver ixgbe_driver = {
+ .name = ixgbe_driver_name,
+ .id_table = ixgbe_pci_tbl,
+ .probe = ixgbe_probe,
+ .remove = __devexit_p(ixgbe_remove),
+#ifdef CONFIG_PM
+ .suspend = ixgbe_suspend,
+ .resume = ixgbe_resume,
+#endif
+ .shutdown = ixgbe_shutdown,
+ .err_handler = &ixgbe_err_handler
+};
+
+/**
+ * ixgbe_init_module - Driver Registration Routine
+ *
+ * ixgbe_init_module is the first routine called when the driver is
+ * loaded. All it does is register with the PCI subsystem.
+ **/
+static int __init ixgbe_init_module(void)
+{
+ int ret;
+ printk(KERN_INFO "%s - version %s\n", ixgbe_driver_string,
+ ixgbe_driver_version);
+
+ printk(KERN_INFO "%s\n", ixgbe_copyright);
+
+ ret = pci_register_driver(&ixgbe_driver);
+ return ret;
+}
+module_init(ixgbe_init_module);
+
+/**
+ * ixgbe_exit_module - Driver Exit Cleanup Routine
+ *
+ * ixgbe_exit_module is called just before the driver is removed
+ * from memory.
+ **/
+static void __exit ixgbe_exit_module(void)
+{
+ pci_unregister_driver(&ixgbe_driver);
+}
+module_exit(ixgbe_exit_module);
+
+/* ixgbe_main.c */
diff --git a/drivers/net/ixgbe/ixgbe_osdep.h b/drivers/net/ixgbe/ixgbe_osdep.h
new file mode 100644
index 0000000..1f81c93
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_osdep.h
@@ -0,0 +1,69 @@
+/*******************************************************************************
+
+ Intel PRO/10GbE PCI Express Linux driver
+ Copyright(c) 1999 - 2007 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+
+/* glue for the OS independent part of ixgbe
+ * includes register access macros
+ */
+
+#ifndef _IXGBE_OSDEP_H_
+#define _IXGBE_OSDEP_H_
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+
+#include <linux/sched.h>
+
+#define DEBUGOUT(S) do {} while (0)
+#define DEBUGOUT1(S, A...) do {} while (0)
+
+#define DEBUGFUNC(F) DEBUGOUT(F)
+#define DEBUGOUT2 DEBUGOUT1
+#define DEBUGOUT3 DEBUGOUT2
+#define DEBUGOUT6 DEBUGOUT3
+#define DEBUGOUT7 DEBUGOUT6
+
+#define IXGBE_WRITE_REG(a, reg, value) writel((value), ((a)->hw_addr + (reg)))
+
+#define IXGBE_READ_REG(a, reg) readl((a)->hw_addr + (reg))
+
+#define IXGBE_WRITE_REG_ARRAY(a, reg, offset, value) ( \
+ writel((value), ((a)->hw_addr + (reg) + ((offset) << 2))))
+
+#define IXGBE_READ_REG_ARRAY(a, reg, offset) ( \
+ readl((a)->hw_addr + (reg) + ((offset) << 2)))
+
+#define IXGBE_WRITE_FLUSH(a) IXGBE_READ_REG(a, IXGBE_STATUS)
+struct ixgbe_hw;
+extern u16 ixgbe_read_pci_cfg_word(struct ixgbe_hw *hw, u32 reg);
+#define IXGBE_READ_PCIE_WORD ixgbe_read_pci_cfg_word
+#define IXGBE_EEPROM_GRANT_ATTEMPS 100
+
+#endif /* _IXGBE_OSDEP_H_ */
diff --git a/drivers/net/ixgbe/ixgbe_phy.c b/drivers/net/ixgbe/ixgbe_phy.c
new file mode 100644
index 0000000..b06235b
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_phy.c
@@ -0,0 +1,601 @@
+/*******************************************************************************
+
+ Intel PRO/10GbE PCI Express Linux driver
+ Copyright(c) 1999 - 2007 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include "ixgbe_api.h"
+#include "ixgbe_common.h"
+#include "ixgbe_phy.h"
+
+static s32 ixgbe_get_phy_id(struct ixgbe_hw *hw);
+static s32 ixgbe_assign_func_pointers_phy(struct ixgbe_hw *hw);
+static bool ixgbe_validate_phy_addr(struct ixgbe_hw *hw, u32 phy_addr);
+static enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id);
+static s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
+ u32 device_type, u16 *phy_data);
+static s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
+ u32 device_type, u16 phy_data);
+
+/* PHY specific */
+static s32 ixgbe_setup_tnx_phy_link(struct ixgbe_hw *hw);
+static s32 ixgbe_check_tnx_phy_link(struct ixgbe_hw *hw, u32 *speed,
+ bool *link_up);
+static s32 ixgbe_setup_tnx_phy_link_speed(struct ixgbe_hw *hw, u32 speed,
+ bool autoneg,
+ bool autoneg_wait_to_complete);
+/**
+ * ixgbe_init_shared_code_phy - Initialize PHY shared code
+ * @hw: pointer to hardware structure
+ **/
+s32 ixgbe_init_shared_code_phy(struct ixgbe_hw *hw)
+{
+ /* Assign function pointers */
+ ixgbe_assign_func_pointers_phy(hw);
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_assign_func_pointers_phy - Assigns PHY-specific function pointers
+ * @hw: pointer to hardware structure
+ *
+ * Note, generic function pointers have already been assigned, so the
+ * function pointers set here are only for PHY-specific functions.
+ **/
+static s32 ixgbe_assign_func_pointers_phy(struct ixgbe_hw *hw)
+{
+ hw->func.ixgbe_func_reset_phy =
+ &ixgbe_reset_phy_generic;
+ hw->func.ixgbe_func_read_phy_reg =
+ &ixgbe_read_phy_reg_generic;
+ hw->func.ixgbe_func_write_phy_reg =
+ &ixgbe_write_phy_reg_generic;
+ hw->func.ixgbe_func_identify_phy =
+ &ixgbe_identify_phy_generic;
+
+ if (ixgbe_get_media_type(hw) == ixgbe_media_type_copper) {
+ /* Call PHY identify routine to get the phy type */
+ ixgbe_identify_phy(hw);
+
+ switch (hw->phy.type) {
+ case ixgbe_phy_tn:
+ hw->func.ixgbe_func_setup_phy_link =
+ &ixgbe_setup_tnx_phy_link;
+ hw->func.ixgbe_func_check_phy_link =
+ &ixgbe_check_tnx_phy_link;
+ hw->func.ixgbe_func_setup_phy_link_speed =
+ &ixgbe_setup_tnx_phy_link_speed;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_identify_phy_generic - Get physical layer module
+ * @hw: pointer to hardware structure
+ *
+ * Determines the physical layer module found on the current adapter.
+ **/
+static s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw)
+{
+ s32 status = IXGBE_ERR_PHY_ADDR_INVALID;
+ u32 phy_addr;
+
+ for (phy_addr = 0; phy_addr < IXGBE_MAX_PHY_ADDR; phy_addr++) {
+ if (ixgbe_validate_phy_addr(hw, phy_addr)) {
+ hw->phy.addr = phy_addr;
+ ixgbe_get_phy_id(hw);
+ hw->phy.type = ixgbe_get_phy_type_from_id(hw->phy.id);
+ status = IXGBE_SUCCESS;
+ break;
+ }
+ }
+ return status;
+}
+
+/**
+ * ixgbe_validate_phy_addr - Determines phy address is valid
+ * @hw: pointer to hardware structure
+ *
+ **/
+static bool ixgbe_validate_phy_addr(struct ixgbe_hw *hw, u32 phy_addr)
+{
+ u16 phy_id = 0;
+ bool valid = false;
+
+ hw->phy.addr = phy_addr;
+ ixgbe_read_phy_reg_generic(hw,
+ IXGBE_MDIO_PHY_ID_HIGH,
+ IXGBE_MDIO_PMA_PMD_DEV_TYPE,
+ &phy_id);
+
+ if (phy_id != 0xFFFF && phy_id != 0x0)
+ valid = true;
+
+ return valid;
+}
+
+/**
+ * ixgbe_get_phy_id - Get the phy type
+ * @hw: pointer to hardware structure
+ *
+ **/
+static s32 ixgbe_get_phy_id(struct ixgbe_hw *hw)
+{
+ u32 status;
+ u16 phy_id_high = 0;
+ u16 phy_id_low = 0;
+
+ status = ixgbe_read_phy_reg_generic(hw,
+ IXGBE_MDIO_PHY_ID_HIGH,
+ IXGBE_MDIO_PMA_PMD_DEV_TYPE,
+ &phy_id_high);
+
+ if (status == IXGBE_SUCCESS) {
+ hw->phy.id = (u32)(phy_id_high << 16);
+ status = ixgbe_read_phy_reg_generic(hw,
+ IXGBE_MDIO_PHY_ID_LOW,
+ IXGBE_MDIO_PMA_PMD_DEV_TYPE,
+ &phy_id_low);
+ hw->phy.id |= (u32)(phy_id_low & IXGBE_PHY_REVISION_MASK);
+ hw->phy.revision = (u32)(phy_id_low & ~IXGBE_PHY_REVISION_MASK);
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_get_phy_type_from_id - Get the phy type
+ * @hw: pointer to hardware structure
+ *
+ **/
+static enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id)
+{
+ enum ixgbe_phy_type phy_type;
+
+ switch (phy_id) {
+ case TN1010_PHY_ID:
+ phy_type = ixgbe_phy_tn;
+ break;
+
+ case QT2022_PHY_ID:
+ phy_type = ixgbe_phy_qt;
+ break;
+
+ default:
+ phy_type = ixgbe_phy_unknown;
+ break;
+ }
+
+ return phy_type;
+}
+
+/**
+ * ixgbe_reset_phy_generic - Performs a PHY reset
+ * @hw: pointer to hardware structure
+ **/
+static s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw)
+{
+ /*
+ * Perform soft PHY reset to the PHY_XS.
+ * This will cause a soft reset to the PHY
+ */
+ return ixgbe_write_phy_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL,
+ IXGBE_MDIO_PHY_XS_DEV_TYPE,
+ IXGBE_MDIO_PHY_XS_RESET);
+}
+
+/**
+ * ixgbe_read_phy_reg_generic - Reads a value from a specified PHY register
+ * @hw: pointer to hardware structure
+ * @reg_addr: 32 bit address of PHY register to read
+ * @phy_data: Pointer to read data from PHY register
+ **/
+static s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
+ u32 device_type, u16 *phy_data)
+{
+ u32 command;
+ u32 i;
+ u32 timeout = 10;
+ u32 data;
+ s32 status = IXGBE_SUCCESS;
+ u16 gssr;
+
+ if (IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_LAN_ID_1)
+ gssr = IXGBE_GSSR_PHY1_SM;
+ else
+ gssr = IXGBE_GSSR_PHY0_SM;
+
+ if (ixgbe_acquire_swfw_sync(hw, gssr) != IXGBE_SUCCESS)
+ status = IXGBE_ERR_SWFW_SYNC;
+
+ if (status == IXGBE_SUCCESS) {
+ /* Setup and write the address cycle command */
+ command = ((reg_addr << IXGBE_MSCA_NP_ADDR_SHIFT) |
+ (device_type << IXGBE_MSCA_DEV_TYPE_SHIFT) |
+ (hw->phy.addr << IXGBE_MSCA_PHY_ADDR_SHIFT) |
+ (IXGBE_MSCA_ADDR_CYCLE | IXGBE_MSCA_MDI_COMMAND));
+
+ IXGBE_WRITE_REG(hw, IXGBE_MSCA, command);
+
+ /*
+ * Check every 10 usec to see if the address cycle completed.
+ * The MDI Command bit will clear when the operation is
+ * complete
+ */
+ for (i = 0; i < timeout; i++) {
+ udelay(10);
+
+ command = IXGBE_READ_REG(hw, IXGBE_MSCA);
+
+ if ((command & IXGBE_MSCA_MDI_COMMAND) == 0) {
+ break;
+ }
+ }
+
+ if ((command & IXGBE_MSCA_MDI_COMMAND) != 0) {
+ DEBUGFUNC("PHY address command did not complete.\n");
+ status = IXGBE_ERR_PHY;
+ }
+
+ if (status == IXGBE_SUCCESS) {
+ /*
+ * Address cycle complete, setup and write the read
+ * command
+ */
+ command = ((reg_addr << IXGBE_MSCA_NP_ADDR_SHIFT) |
+ (device_type << IXGBE_MSCA_DEV_TYPE_SHIFT) |
+ (hw->phy.addr << IXGBE_MSCA_PHY_ADDR_SHIFT) |
+ (IXGBE_MSCA_READ | IXGBE_MSCA_MDI_COMMAND));
+
+ IXGBE_WRITE_REG(hw, IXGBE_MSCA, command);
+
+ /*
+ * Check every 10 usec to see if the address cycle
+ * completed. The MDI Command bit will clear when the
+ * operation is complete
+ */
+ for (i = 0; i < timeout; i++) {
+ udelay(10);
+
+ command = IXGBE_READ_REG(hw, IXGBE_MSCA);
+
+ if ((command & IXGBE_MSCA_MDI_COMMAND) == 0)
+ break;
+ }
+
+ if ((command & IXGBE_MSCA_MDI_COMMAND) != 0) {
+ DEBUGFUNC("PHY read command didn't complete\n");
+ status = IXGBE_ERR_PHY;
+ } else {
+ /*
+ * Read operation is complete. Get the data
+ * from MSRWD
+ */
+ data = IXGBE_READ_REG(hw, IXGBE_MSRWD);
+ data >>= IXGBE_MSRWD_READ_DATA_SHIFT;
+ *phy_data = (u16)(data);
+ }
+ }
+
+ ixgbe_release_swfw_sync(hw, gssr);
+ }
+ return status;
+}
+
+/**
+ * ixgbe_write_phy_reg_generic - Writes a value to specified PHY register
+ * @hw: pointer to hardware structure
+ * @reg_addr: 32 bit PHY register to write
+ * @device_type: 5 bit device type
+ * @phy_data: Data to write to the PHY register
+ **/
+static s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
+ u32 device_type, u16 phy_data)
+{
+ u32 command;
+ u32 i;
+ u32 timeout = 10;
+ s32 status = IXGBE_SUCCESS;
+ u16 gssr;
+
+ if (IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_LAN_ID_1)
+ gssr = IXGBE_GSSR_PHY1_SM;
+ else
+ gssr = IXGBE_GSSR_PHY0_SM;
+
+ if (ixgbe_acquire_swfw_sync(hw, gssr) != IXGBE_SUCCESS)
+ status = IXGBE_ERR_SWFW_SYNC;
+
+ if (status == IXGBE_SUCCESS) {
+ /* Put the data in the MDI single read and write data register*/
+ IXGBE_WRITE_REG(hw, IXGBE_MSRWD, (u32)phy_data);
+
+ /* Setup and write the address cycle command */
+ command = ((reg_addr << IXGBE_MSCA_NP_ADDR_SHIFT) |
+ (device_type << IXGBE_MSCA_DEV_TYPE_SHIFT) |
+ (hw->phy.addr << IXGBE_MSCA_PHY_ADDR_SHIFT) |
+ (IXGBE_MSCA_ADDR_CYCLE | IXGBE_MSCA_MDI_COMMAND));
+
+ IXGBE_WRITE_REG(hw, IXGBE_MSCA, command);
+
+ /*
+ * Check every 10 usec to see if the address cycle completed.
+ * The MDI Command bit will clear when the operation is
+ * complete
+ */
+ for (i = 0; i < timeout; i++) {
+ udelay(10);
+
+ command = IXGBE_READ_REG(hw, IXGBE_MSCA);
+
+ if ((command & IXGBE_MSCA_MDI_COMMAND) == 0) {
+ DEBUGFUNC("PHY address cmd didn't complete\n");
+ break;
+ }
+ }
+
+ if ((command & IXGBE_MSCA_MDI_COMMAND) != 0)
+ status = IXGBE_ERR_PHY;
+
+ if (status == IXGBE_SUCCESS) {
+ /*
+ * Address cycle complete, setup and write the write
+ * command
+ */
+ command = ((reg_addr << IXGBE_MSCA_NP_ADDR_SHIFT) |
+ (device_type << IXGBE_MSCA_DEV_TYPE_SHIFT) |
+ (hw->phy.addr << IXGBE_MSCA_PHY_ADDR_SHIFT) |
+ (IXGBE_MSCA_WRITE | IXGBE_MSCA_MDI_COMMAND));
+
+ IXGBE_WRITE_REG(hw, IXGBE_MSCA, command);
+
+ /*
+ * Check every 10 usec to see if the address cycle
+ * completed. The MDI Command bit will clear when the
+ * operation is complete
+ */
+ for (i = 0; i < timeout; i++) {
+ udelay(10);
+
+ command = IXGBE_READ_REG(hw, IXGBE_MSCA);
+
+ if ((command & IXGBE_MSCA_MDI_COMMAND) == 0) {
+ DEBUGFUNC("PHY write command did not "
+ "complete.\n");
+ break;
+ }
+ }
+
+ if ((command & IXGBE_MSCA_MDI_COMMAND) != 0)
+ status = IXGBE_ERR_PHY;
+ }
+
+ ixgbe_release_swfw_sync(hw, gssr);
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_setup_phy_link - Restart PHY autoneg
+ * @hw: pointer to hardware structure
+ *
+ * Restart autonegotiation and PHY and waits for completion.
+ **/
+s32 ixgbe_setup_phy_link(struct ixgbe_hw *hw)
+{
+ return ixgbe_call_func(hw, ixgbe_func_setup_phy_link, (hw),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_check_phy_link - Determine link and speed status
+ * @hw: pointer to hardware structure
+ *
+ * Reads a PHY register to determine if link is up and the current speed for
+ * the PHY.
+ **/
+s32 ixgbe_check_phy_link(struct ixgbe_hw *hw, u32 *speed,
+ bool *link_up)
+{
+ return ixgbe_call_func(hw, ixgbe_func_check_phy_link, (hw, speed,
+ link_up), IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_setup_phy_link_speed - Set auto advertise
+ * @hw: pointer to hardware structure
+ * @speed: new link speed
+ * @autoneg: true if autonegotiation enabled
+ *
+ * Sets the auto advertised capabilities
+ **/
+s32 ixgbe_setup_phy_link_speed(struct ixgbe_hw *hw, u32 speed,
+ bool autoneg,
+ bool autoneg_wait_to_complete)
+{
+ return ixgbe_call_func(hw, ixgbe_func_setup_phy_link_speed, (hw, speed,
+ autoneg, autoneg_wait_to_complete),
+ IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ * ixgbe_setup_tnx_phy_link - Set and restart autoneg
+ * @hw: pointer to hardware structure
+ *
+ * Restart autonegotiation and PHY and waits for completion.
+ **/
+static s32 ixgbe_setup_tnx_phy_link(struct ixgbe_hw *hw)
+{
+ s32 status = IXGBE_NOT_IMPLEMENTED;
+ u32 time_out;
+ u32 max_time_out = 10;
+ u16 autoneg_speed_selection_register = 0x10;
+ u16 autoneg_restart_mask = 0x0200;
+ u16 autoneg_complete_mask = 0x0020;
+ u16 autoneg_reg = 0;
+
+ /*
+ * Set advertisement settings in PHY based on autoneg_advertised
+ * settings. If autoneg_advertised = 0, then advertise default values
+ * txn devices cannot be "forced" to a autoneg 10G and fail. But can
+ * for a 1G.
+ */
+ ixgbe_read_phy_reg(hw,
+ autoneg_speed_selection_register,
+ IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+ &autoneg_reg);
+
+ if (hw->phy.autoneg_advertised == IXGBE_LINK_SPEED_1GB_FULL)
+ autoneg_reg &= 0xEFFF; /* 0 in bit 12 is 1G operation */
+ else
+ autoneg_reg |= 0x1000; /* 1 in bit 12 is 10G/1G operation */
+
+ ixgbe_write_phy_reg(hw,
+ autoneg_speed_selection_register,
+ IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+ autoneg_reg);
+
+
+ /* Restart PHY autonegotiation and wait for completion */
+ ixgbe_read_phy_reg(hw,
+ IXGBE_MDIO_AUTO_NEG_CONTROL,
+ IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+ &autoneg_reg);
+
+ autoneg_reg |= autoneg_restart_mask;
+
+ ixgbe_write_phy_reg(hw,
+ IXGBE_MDIO_AUTO_NEG_CONTROL,
+ IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+ autoneg_reg);
+
+ /* Wait for autonegotiation to finish */
+ for (time_out = 0; time_out < max_time_out; time_out++) {
+ udelay(10);
+ /* Restart PHY autonegotiation and wait for completion */
+ status = ixgbe_read_phy_reg(hw,
+ IXGBE_MDIO_AUTO_NEG_STATUS,
+ IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+ &autoneg_reg);
+
+ autoneg_reg &= autoneg_complete_mask;
+ if (autoneg_reg == autoneg_complete_mask) {
+ status = IXGBE_SUCCESS;
+ break;
+ }
+ }
+
+ if (time_out == max_time_out)
+ status = IXGBE_ERR_LINK_SETUP;
+
+ return status;
+}
+
+/**
+ * ixgbe_check_tnx_phy_link - Determine link and speed status
+ * @hw: pointer to hardware structure
+ *
+ * Reads the VS1 register to determine if link is up and the current speed for
+ * the PHY.
+ **/
+static s32 ixgbe_check_tnx_phy_link(struct ixgbe_hw *hw, u32 *speed,
+ bool *link_up)
+{
+ s32 status = IXGBE_SUCCESS;
+ u32 time_out;
+ u32 max_time_out = 10;
+ u16 phy_link = 0;
+ u16 phy_speed = 0;
+ u16 phy_data = 0;
+
+ /* Initialize speed and link to default case */
+ *link_up = false;
+ *speed = IXGBE_LINK_SPEED_10GB_FULL;
+
+ /*
+ * Check current speed and link status of the PHY register.
+ * This is a vendor specific register and may have to
+ * be changed for other copper PHYs.
+ */
+ for (time_out = 0; time_out < max_time_out; time_out++) {
+ udelay(10);
+ if (phy_link == IXGBE_MDIO_VENDOR_SPECIFIC_1_LINK_STATUS) {
+ *link_up = true;
+ if (phy_speed ==
+ IXGBE_MDIO_VENDOR_SPECIFIC_1_SPEED_STATUS)
+ *speed = IXGBE_LINK_SPEED_1GB_FULL;
+ break;
+ } else {
+ status = ixgbe_read_phy_reg(hw,
+ IXGBE_MDIO_VENDOR_SPECIFIC_1_STATUS,
+ IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+ &phy_data);
+ phy_link = phy_data &
+ IXGBE_MDIO_VENDOR_SPECIFIC_1_LINK_STATUS;
+ phy_speed = phy_data &
+ IXGBE_MDIO_VENDOR_SPECIFIC_1_SPEED_STATUS;
+ }
+ }
+
+ return status;
+}
+
+/**
+ * ixgbe_setup_tnx_phy_link_speed - Sets the auto advertised capabilities
+ * @hw: pointer to hardware structure
+ * @speed: new link speed
+ * @autoneg: true if autonegotiation enabled
+ **/
+static s32 ixgbe_setup_tnx_phy_link_speed(struct ixgbe_hw *hw, u32 speed,
+ bool autoneg,
+ bool autoneg_wait_to_complete)
+{
+ /*
+ * Clear autoneg_advertised and set new values based on input link
+ * speed.
+ */
+ hw->phy.autoneg_advertised = 0;
+
+ if (speed & IXGBE_LINK_SPEED_10GB_FULL) {
+ hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10GB_FULL;
+ }
+ if (speed & IXGBE_LINK_SPEED_1GB_FULL) {
+ hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL;
+ }
+
+ /* Setup link based on the new speed settings */
+ ixgbe_setup_tnx_phy_link(hw);
+
+ return IXGBE_SUCCESS;
+}
diff --git a/drivers/net/ixgbe/ixgbe_phy.h b/drivers/net/ixgbe/ixgbe_phy.h
new file mode 100644
index 0000000..ada510a
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_phy.h
@@ -0,0 +1,40 @@
+/*******************************************************************************
+
+ Intel PRO/10GbE PCI Express Linux driver
+ Copyright(c) 1999 - 2007 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#ifndef _IXGBE_PHY_H_
+#define _IXGBE_PHY_H_
+
+#include "ixgbe_type.h"
+
+s32 ixgbe_init_shared_code_phy(struct ixgbe_hw *hw);
+s32 ixgbe_setup_phy_link(struct ixgbe_hw *hw);
+s32 ixgbe_check_phy_link(struct ixgbe_hw *hw, u32 *speed, bool *link_up);
+s32 ixgbe_setup_phy_link_speed(struct ixgbe_hw *hw, u32 speed, bool autoneg,
+ bool autoneg_wait_to_complete);
+
+#endif /* _IXGBE_PHY_H_ */
diff --git a/drivers/net/ixgbe/ixgbe_type.h b/drivers/net/ixgbe/ixgbe_type.h
new file mode 100644
index 0000000..0d7db6c
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_type.h
@@ -0,0 +1,1390 @@
+/*******************************************************************************
+
+ Intel PRO/10GbE PCI Express Linux driver
+ Copyright(c) 1999 - 2007 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#ifndef _IXGBE_TYPE_H_
+#define _IXGBE_TYPE_H_
+
+#include "ixgbe_osdep.h"
+
+/* Vendor ID */
+#define IXGBE_INTEL_VENDOR_ID 0x8086
+
+/* Device IDs */
+#define IXGBE_DEV_ID_82598 0x10B6
+#define IXGBE_DEV_ID_82598AF_DUAL_PORT 0x10C6
+#define IXGBE_DEV_ID_82598AF_SINGLE_PORT 0x10C7
+
+/* General Registers */
+#define IXGBE_CTRL 0x00000
+#define IXGBE_STATUS 0x00008
+#define IXGBE_CTRL_EXT 0x00018
+#define IXGBE_ESDP 0x00020
+#define IXGBE_EODSDP 0x00028
+#define IXGBE_LEDCTL 0x00200
+#define IXGBE_FRTIMER 0x00048
+#define IXGBE_TCPTIMER 0x0004C
+
+/* NVM Registers */
+#define IXGBE_EEC 0x10010
+#define IXGBE_EERD 0x10014
+#define IXGBE_FLA 0x1001C
+#define IXGBE_EEMNGCTL 0x10110
+#define IXGBE_EEMNGDATA 0x10114
+#define IXGBE_FLMNGCTL 0x10118
+#define IXGBE_FLMNGDATA 0x1011C
+#define IXGBE_FLMNGCNT 0x10120
+#define IXGBE_FLOP 0x1013C
+#define IXGBE_GRC 0x10200
+
+/* Interrupt Registers */
+#define IXGBE_EICR 0x00800
+#define IXGBE_EICS 0x00808
+#define IXGBE_EIMS 0x00880
+#define IXGBE_EIMC 0x00888
+#define IXGBE_EIAC 0x00810
+#define IXGBE_EIAM 0x00890
+#define IXGBE_EITR(_i) (0x00820 + ((_i) * 4)) /* 0x820-0x86c */
+#define IXGBE_IVAR(_i) (0x00900 + ((_i) * 4)) /* 24 at 0x900-0x960 */
+#define IXGBE_MSIXT 0x00000 /* MSI-X Table. 0x0000 - 0x01C */
+#define IXGBE_MSIXPBA 0x02000 /* MSI-X Pending bit array */
+#define IXGBE_PBACL 0x11068
+#define IXGBE_GPIE 0x00898
+
+/* Flow Control Registers */
+#define IXGBE_PFCTOP 0x03008
+#define IXGBE_FCTTV(_i) (0x03200 + ((_i) * 4)) /* 4 of these (0-3) */
+#define IXGBE_FCRTL(_i) (0x03220 + ((_i) * 8)) /* 8 of these (0-7) */
+#define IXGBE_FCRTH(_i) (0x03260 + ((_i) * 8)) /* 8 of these (0-7) */
+#define IXGBE_FCRTV 0x032A0
+#define IXGBE_TFCS 0x0CE00
+
+/* Receive DMA Registers */
+#define IXGBE_RDBAL(_i) (0x01000 + ((_i) * 0x40)) /* 64 of each (0-63)*/
+#define IXGBE_RDBAH(_i) (0x01004 + ((_i) * 0x40))
+#define IXGBE_RDLEN(_i) (0x01008 + ((_i) * 0x40))
+#define IXGBE_RDH(_i) (0x01010 + ((_i) * 0x40))
+#define IXGBE_RDT(_i) (0x01018 + ((_i) * 0x40))
+#define IXGBE_RXDCTL(_i) (0x01028 + ((_i) * 0x40))
+#define IXGBE_SRRCTL(_i) (0x02100 + ((_i) * 4))
+ /* array of 16 (0x02100-0x0213C) */
+#define IXGBE_DCA_RXCTRL(_i) (0x02200 + ((_i) * 4))
+ /* array of 16 (0x02200-0x0223C) */
+#define IXGBE_RDRXCTL 0x02F00
+#define IXGBE_RXPBSIZE(_i) (0x03C00 + ((_i) * 4))
+ /* 8 of these 0x03C00 - 0x03C1C */
+#define IXGBE_RXCTRL 0x03000
+#define IXGBE_DROPEN 0x03D04
+#define IXGBE_RXPBSIZE_SHIFT 10
+
+/* Receive Registers */
+#define IXGBE_RXCSUM 0x05000
+#define IXGBE_RFCTL 0x05008
+#define IXGBE_MTA(_i) (0x05200 + ((_i) * 4))
+ /* Multicast Table Array - 128 entries */
+#define IXGBE_RAL(_i) (0x05400 + ((_i) * 8)) /* 16 of these (0-15) */
+#define IXGBE_RAH(_i) (0x05404 + ((_i) * 8)) /* 16 of these (0-15) */
+#define IXGBE_PSRTYPE 0x05480
+ /* 0x5480-0x54BC Packet split receive type */
+#define IXGBE_VFTA(_i) (0x0A000 + ((_i) * 4))
+ /* array of 4096 1-bit vlan filters */
+#define IXGBE_VFTAVIND(_j, _i) (0x0A200 + ((_j) * 0x200) + ((_i) * 4))
+ /*array of 4096 4-bit vlan vmdq indicies */
+#define IXGBE_FCTRL 0x05080
+#define IXGBE_VLNCTRL 0x05088
+#define IXGBE_MCSTCTRL 0x05090
+#define IXGBE_MRQC 0x05818
+#define IXGBE_VMD_CTL 0x0581C
+#define IXGBE_IMIR(_i) (0x05A80 + ((_i) * 4)) /* 8 of these (0-7) */
+#define IXGBE_IMIREXT(_i) (0x05AA0 + ((_i) * 4)) /* 8 of these (0-7) */
+#define IXGBE_IMIRVP 0x05AC0
+#define IXGBE_RETA(_i) (0x05C00 + ((_i) * 4)) /* 32 of these (0-31) */
+#define IXGBE_RSSRK(_i) (0x05C80 + ((_i) * 4)) /* 10 of these (0-9) */
+
+/* Transmit DMA registers */
+#define IXGBE_TDBAL(_i) (0x06000 + ((_i) * 0x40))/* 32 of these (0-31)*/
+#define IXGBE_TDBAH(_i) (0x06004 + ((_i) * 0x40))
+#define IXGBE_TDLEN(_i) (0x06008 + ((_i) * 0x40))
+#define IXGBE_TDH(_i) (0x06010 + ((_i) * 0x40))
+#define IXGBE_TDT(_i) (0x06018 + ((_i) * 0x40))
+#define IXGBE_TXDCTL(_i) (0x06028 + ((_i) * 0x40))
+#define IXGBE_TDWBAL(_i) (0x06038 + ((_i) * 0x40))
+#define IXGBE_TDWBAH(_i) (0x0603C + ((_i) * 0x40))
+#define IXGBE_DTXCTL 0x07E00
+#define IXGBE_DCA_TXCTRL(_i) (0x07200 + ((_i) * 4))
+ /* there are 16 of these (0-15) */
+#define IXGBE_TIPG 0x0CB00
+#define IXGBE_TXPBSIZE(_i) (0x0CC00 + ((_i) *0x04))
+ /* there are 8 of these */
+#define IXGBE_MNGTXMAP 0x0CD10
+#define IXGBE_TIPG_FIBER_DEFAULT 3
+#define IXGBE_TXPBSIZE_SHIFT 10
+
+/* Wake up registers */
+#define IXGBE_WUC 0x05800
+#define IXGBE_WUFC 0x05808
+#define IXGBE_WUS 0x05810
+#define IXGBE_IPAV 0x05838
+#define IXGBE_IP4AT 0x05840 /* IPv4 table 0x5840-0x5858 */
+#define IXGBE_IP6AT 0x05880 /* IPv6 table 0x5880-0x588F */
+#define IXGBE_WUPL 0x05900
+#define IXGBE_WUPM 0x05A00 /* wake up pkt memory 0x5A00-0x5A7C */
+#define IXGBE_FHFT 0x09000 /* Flex host filter table 9000-93FC */
+
+/* Music registers */
+#define IXGBE_RMCS 0x03D00
+#define IXGBE_DPMCS 0x07F40
+#define IXGBE_PDPMCS 0x0CD00
+#define IXGBE_RUPPBMR 0x050A0
+#define IXGBE_RT2CR(_i) (0x03C20 + ((_i) * 4)) /* 8 of these (0-7) */
+#define IXGBE_RT2SR(_i) (0x03C40 + ((_i) * 4)) /* 8 of these (0-7) */
+#define IXGBE_TDTQ2TCCR(_i) (0x0602C + ((_i) * 0x40)) /* 8 of these (0-7) */
+#define IXGBE_TDTQ2TCSR(_i) (0x0622C + ((_i) * 0x40)) /* 8 of these (0-7) */
+#define IXGBE_TDPT2TCCR(_i) (0x0CD20 + ((_i) * 4)) /* 8 of these (0-7) */
+#define IXGBE_TDPT2TCSR(_i) (0x0CD40 + ((_i) * 4)) /* 8 of these (0-7) */
+
+/* Stats registers */
+#define IXGBE_CRCERRS 0x04000
+#define IXGBE_ILLERRC 0x04004
+#define IXGBE_ERRBC 0x04008
+#define IXGBE_MSPDC 0x04010
+#define IXGBE_MPC(_i) (0x03FA0 + ((_i) * 4)) /* 8 of these 3FA0-3FBC*/
+#define IXGBE_MLFC 0x04034
+#define IXGBE_MRFC 0x04038
+#define IXGBE_RLEC 0x04040
+#define IXGBE_LXONTXC 0x03F60
+#define IXGBE_LXONRXC 0x0CF60
+#define IXGBE_LXOFFTXC 0x03F68
+#define IXGBE_LXOFFRXC 0x0CF68
+#define IXGBE_PXONTXC(_i) (0x03F00 + ((_i) * 4)) /* 8 of these 3F00-3F1C*/
+#define IXGBE_PXONRXC(_i) (0x0CF00 + ((_i) * 4)) /* 8 of these CF00-CF1C*/
+#define IXGBE_PXOFFTXC(_i) (0x03F20 + ((_i) * 4)) /* 8 of these 3F20-3F3C*/
+#define IXGBE_PXOFFRXC(_i) (0x0CF20 + ((_i) * 4)) /* 8 of these CF20-CF3C*/
+#define IXGBE_PRC64 0x0405C
+#define IXGBE_PRC127 0x04060
+#define IXGBE_PRC255 0x04064
+#define IXGBE_PRC511 0x04068
+#define IXGBE_PRC1023 0x0406C
+#define IXGBE_PRC1522 0x04070
+#define IXGBE_GPRC 0x04074
+#define IXGBE_BPRC 0x04078
+#define IXGBE_MPRC 0x0407C
+#define IXGBE_GPTC 0x04080
+#define IXGBE_GORCL 0x04088
+#define IXGBE_GORCH 0x0408C
+#define IXGBE_GOTCL 0x04090
+#define IXGBE_GOTCH 0x04094
+#define IXGBE_RNBC(_i) (0x03FC0 + ((_i) * 4)) /* 8 of these 3FC0-3FDC*/
+#define IXGBE_RUC 0x040A4
+#define IXGBE_RFC 0x040A8
+#define IXGBE_ROC 0x040AC
+#define IXGBE_RJC 0x040B0
+#define IXGBE_MNGPRC 0x040B4
+#define IXGBE_MNGPDC 0x040B8
+#define IXGBE_MNGPTC 0x0CF90
+#define IXGBE_TORL 0x040C0
+#define IXGBE_TORH 0x040C4
+#define IXGBE_TPR 0x040D0
+#define IXGBE_TPT 0x040D4
+#define IXGBE_PTC64 0x040D8
+#define IXGBE_PTC127 0x040DC
+#define IXGBE_PTC255 0x040E0
+#define IXGBE_PTC511 0x040E4
+#define IXGBE_PTC1023 0x040E8
+#define IXGBE_PTC1522 0x040EC
+#define IXGBE_MPTC 0x040F0
+#define IXGBE_BPTC 0x040F4
+#define IXGBE_XEC 0x04120
+
+#define IXGBE_RQSMR(_i) (0x02300 + ((_i) * 4)) /* 16 of these */
+#define IXGBE_TQSMR(_i) (0x07300 + ((_i) * 4)) /* 8 of these */
+
+#define IXGBE_QPRC(_i) (0x01030 + ((_i) * 0x40)) /* 16 of these */
+#define IXGBE_QPTC(_i) (0x06030 + ((_i) * 0x40)) /* 16 of these */
+#define IXGBE_QBRC(_i) (0x01034 + ((_i) * 0x40)) /* 16 of these */
+#define IXGBE_QBTC(_i) (0x06034 + ((_i) * 0x40)) /* 16 of these */
+
+/* Management */
+#define IXGBE_MAVTV(_i) (0x05010 + ((_i) * 4)) /* 8 of these (0-7) */
+#define IXGBE_MFUTP(_i) (0x05030 + ((_i) * 4)) /* 8 of these (0-7) */
+#define IXGBE_MANC 0x05820
+#define IXGBE_MFVAL 0x05824
+#define IXGBE_MANC2H 0x05860
+#define IXGBE_MDEF(_i) (0x05890 + ((_i) * 4)) /* 8 of these (0-7) */
+#define IXGBE_MIPAF 0x058B0
+#define IXGBE_MMAL(_i) (0x05910 + ((_i) * 8)) /* 4 of these (0-3) */
+#define IXGBE_MMAH(_i) (0x05914 + ((_i) * 8)) /* 4 of these (0-3) */
+#define IXGBE_FTFT 0x09400 /* 0x9400-0x97FC */
+
+/* ARC Subsystem registers */
+#define IXGBE_HICR 0x15F00
+#define IXGBE_FWSTS 0x15F0C
+#define IXGBE_HSMC0R 0x15F04
+#define IXGBE_HSMC1R 0x15F08
+#define IXGBE_SWSR 0x15F10
+#define IXGBE_HFDR 0x15FE8
+#define IXGBE_FLEX_MNG 0x15800 /* 0x15800 - 0x15EFC */
+
+/* PCI-E registers */
+#define IXGBE_GCR 0x11000
+#define IXGBE_GTV 0x11004
+#define IXGBE_FUNCTAG 0x11008
+#define IXGBE_GLT 0x1100C
+#define IXGBE_GSCL_1 0x11010
+#define IXGBE_GSCL_2 0x11014
+#define IXGBE_GSCL_3 0x11018
+#define IXGBE_GSCL_4 0x1101C
+#define IXGBE_GSCN_0 0x11020
+#define IXGBE_GSCN_1 0x11024
+#define IXGBE_GSCN_2 0x11028
+#define IXGBE_GSCN_3 0x1102C
+#define IXGBE_FACTPS 0x10150
+#define IXGBE_PCIEANACTL 0x11040
+#define IXGBE_SWSM 0x10140
+#define IXGBE_FWSM 0x10148
+#define IXGBE_GSSR 0x10160
+#define IXGBE_MREVID 0x11064
+#define IXGBE_DCA_ID 0x11070
+#define IXGBE_DCA_CTRL 0x11074
+
+/* Diagnostic Registers */
+#define IXGBE_RDSTATCTL 0x02C20
+#define IXGBE_RDSTAT(_i) (0x02C00 + ((_i) * 4)) /* 0x02C00-0x02C1C */
+#define IXGBE_RDHMPN 0x02F08
+#define IXGBE_RIC_DW0 0x02F10
+#define IXGBE_RIC_DW1 0x02F14
+#define IXGBE_RIC_DW2 0x02F18
+#define IXGBE_RIC_DW3 0x02F1C
+#define IXGBE_RDPROBE 0x02F20
+#define IXGBE_TDSTATCTL 0x07C20
+#define IXGBE_TDSTAT(_i) (0x07C00 + ((_i) * 4)) /* 0x07C00 - 0x07C1C */
+#define IXGBE_TDHMPN 0x07F08
+#define IXGBE_TIC_DW0 0x07F10
+#define IXGBE_TIC_DW1 0x07F14
+#define IXGBE_TIC_DW2 0x07F18
+#define IXGBE_TIC_DW3 0x07F1C
+#define IXGBE_TDPROBE 0x07F20
+#define IXGBE_TXBUFCTRL 0x0C600
+#define IXGBE_TXBUFDATA0 0x0C610
+#define IXGBE_TXBUFDATA1 0x0C614
+#define IXGBE_TXBUFDATA2 0x0C618
+#define IXGBE_TXBUFDATA3 0x0C61C
+#define IXGBE_RXBUFCTRL 0x03600
+#define IXGBE_RXBUFDATA0 0x03610
+#define IXGBE_RXBUFDATA1 0x03614
+#define IXGBE_RXBUFDATA2 0x03618
+#define IXGBE_RXBUFDATA3 0x0361C
+#define IXGBE_PCIE_DIAG(_i) (0x11090 + ((_i) * 4)) /* 8 of these */
+#define IXGBE_RFVAL 0x050A4
+#define IXGBE_MDFTC1 0x042B8
+#define IXGBE_MDFTC2 0x042C0
+#define IXGBE_MDFTFIFO1 0x042C4
+#define IXGBE_MDFTFIFO2 0x042C8
+#define IXGBE_MDFTS 0x042CC
+#define IXGBE_RXDATAWRPTR(_i) (0x03700 + ((_i) * 4)) /* 8 of these 3700-370C*/
+#define IXGBE_RXDESCWRPTR(_i) (0x03710 + ((_i) * 4)) /* 8 of these 3710-371C*/
+#define IXGBE_RXDATARDPTR(_i) (0x03720 + ((_i) * 4)) /* 8 of these 3720-372C*/
+#define IXGBE_RXDESCRDPTR(_i) (0x03730 + ((_i) * 4)) /* 8 of these 3730-373C*/
+#define IXGBE_TXDATAWRPTR(_i) (0x0C700 + ((_i) * 4)) /* 8 of these C700-C70C*/
+#define IXGBE_TXDESCWRPTR(_i) (0x0C710 + ((_i) * 4)) /* 8 of these C710-C71C*/
+#define IXGBE_TXDATARDPTR(_i) (0x0C720 + ((_i) * 4)) /* 8 of these C720-C72C*/
+#define IXGBE_TXDESCRDPTR(_i) (0x0C730 + ((_i) * 4)) /* 8 of these C730-C73C*/
+#define IXGBE_PCIEECCCTL 0x1106C
+#define IXGBE_PBTXECC 0x0C300
+#define IXGBE_PBRXECC 0x03300
+#define IXGBE_GHECCR 0x110B0
+
+/* MAC Registers */
+#define IXGBE_PCS1GCFIG 0x04200
+#define IXGBE_PCS1GLCTL 0x04208
+#define IXGBE_PCS1GLSTA 0x0420C
+#define IXGBE_PCS1GDBG0 0x04210
+#define IXGBE_PCS1GDBG1 0x04214
+#define IXGBE_PCS1GANA 0x04218
+#define IXGBE_PCS1GANLP 0x0421C
+#define IXGBE_PCS1GANNP 0x04220
+#define IXGBE_PCS1GANLPNP 0x04224
+#define IXGBE_HLREG0 0x04240
+#define IXGBE_HLREG1 0x04244
+#define IXGBE_PAP 0x04248
+#define IXGBE_MACA 0x0424C
+#define IXGBE_APAE 0x04250
+#define IXGBE_ARD 0x04254
+#define IXGBE_AIS 0x04258
+#define IXGBE_MSCA 0x0425C
+#define IXGBE_MSRWD 0x04260
+#define IXGBE_MLADD 0x04264
+#define IXGBE_MHADD 0x04268
+#define IXGBE_TREG 0x0426C
+#define IXGBE_PCSS1 0x04288
+#define IXGBE_PCSS2 0x0428C
+#define IXGBE_XPCSS 0x04290
+#define IXGBE_SERDESC 0x04298
+#define IXGBE_MACS 0x0429C
+#define IXGBE_AUTOC 0x042A0
+#define IXGBE_LINKS 0x042A4
+#define IXGBE_AUTOC2 0x042A8
+#define IXGBE_AUTOC3 0x042AC
+#define IXGBE_ANLP1 0x042B0
+#define IXGBE_ANLP2 0x042B4
+#define IXGBE_ATLASCTL 0x04800
+
+/* CTRL Bit Masks */
+#define IXGBE_CTRL_GIO_DIS 0x00000004 /* Global IO Master Disable bit */
+#define IXGBE_CTRL_LNK_RST 0x00000008 /* Link Reset. Resets everything. */
+#define IXGBE_CTRL_RST 0x04000000 /* Reset (SW) */
+
+/* FACTPS */
+#define IXGBE_FACTPS_LFS 0x40000000 /* LAN Function Select */
+
+/* MHADD Bit Masks */
+#define IXGBE_MHADD_MFS_MASK 0xFFFF0000
+#define IXGBE_MHADD_MFS_SHIFT 16
+
+/* Extended Device Control */
+#define IXGBE_CTRL_EXT_NS_DIS 0x00010000 /* No Snoop disable */
+#define IXGBE_CTRL_EXT_RO_DIS 0x00020000 /* Relaxed Ordering disable */
+#define IXGBE_CTRL_EXT_DRV_LOAD 0x10000000 /* Driver loaded bit for FW */
+
+/* Direct Cache Access (DCA) definitions */
+#define IXGBE_DCA_CTRL_DCA_ENABLE 0x00000000 /* DCA Enable */
+#define IXGBE_DCA_CTRL_DCA_DISABLE 0x00000001 /* DCA Disable */
+
+#define IXGBE_DCA_CTRL_DCA_MODE_CB1 0x00 /* DCA Mode CB1 */
+#define IXGBE_DCA_CTRL_DCA_MODE_CB2 0x02 /* DCA Mode CB2 */
+
+#define IXGBE_DCA_RXCTRL_CPUID_MASK 0x0000001F /* Rx CPUID Mask */
+#define IXGBE_DCA_RXCTRL_DESC_DCA_EN (1 << 5) /* DCA Rx Desc enable */
+#define IXGBE_DCA_RXCTRL_HEAD_DCA_EN (1 << 6) /* DCA Rx Desc header enable */
+#define IXGBE_DCA_RXCTRL_DATA_DCA_EN (1 << 7) /* DCA Rx Desc payload enable */
+
+#define IXGBE_DCA_TXCTRL_CPUID_MASK 0x0000001F /* Tx CPUID Mask */
+#define IXGBE_DCA_TXCTRL_DESC_DCA_EN (1 << 5) /* DCA Tx Desc enable */
+
+/* MSCA Bit Masks */
+#define IXGBE_MSCA_NP_ADDR_MASK 0x0000FFFF /* MDI Address (new protocol) */
+#define IXGBE_MSCA_NP_ADDR_SHIFT 0
+#define IXGBE_MSCA_DEV_TYPE_MASK 0x001F0000 /* Device Type (new protocol) */
+#define IXGBE_MSCA_DEV_TYPE_SHIFT 16 /* Register Address (old protocol */
+#define IXGBE_MSCA_PHY_ADDR_MASK 0x03E00000 /* PHY Address mask */
+#define IXGBE_MSCA_PHY_ADDR_SHIFT 21 /* PHY Address shift*/
+#define IXGBE_MSCA_OP_CODE_MASK 0x0C000000 /* OP CODE mask */
+#define IXGBE_MSCA_OP_CODE_SHIFT 26 /* OP CODE shift */
+#define IXGBE_MSCA_ADDR_CYCLE 0x00000000 /* OP CODE 00 (addr cycle) */
+#define IXGBE_MSCA_WRITE 0x04000000 /* OP CODE 01 (write) */
+#define IXGBE_MSCA_READ 0x08000000 /* OP CODE 10 (read) */
+#define IXGBE_MSCA_READ_AUTOINC 0x0C000000 /* OP CODE 11 (read, auto inc)*/
+#define IXGBE_MSCA_ST_CODE_MASK 0x30000000 /* ST Code mask */
+#define IXGBE_MSCA_ST_CODE_SHIFT 28 /* ST Code shift */
+#define IXGBE_MSCA_NEW_PROTOCOL 0x00000000 /* ST CODE 00 (new protocol) */
+#define IXGBE_MSCA_OLD_PROTOCOL 0x10000000 /* ST CODE 01 (old protocol) */
+#define IXGBE_MSCA_MDI_COMMAND 0x40000000 /* Initiate MDI command */
+#define IXGBE_MSCA_MDI_IN_PROG_EN 0x80000000 /* MDI in progress enable */
+
+/* MSRWD bit masks */
+#define IXGBE_MSRWD_WRITE_DATA_MASK 0x0000FFFF
+#define IXGBE_MSRWD_WRITE_DATA_SHIFT 0
+#define IXGBE_MSRWD_READ_DATA_MASK 0xFFFF0000
+#define IXGBE_MSRWD_READ_DATA_SHIFT 16
+
+/* Device Type definitions for new protocol MDIO commands */
+#define IXGBE_MDIO_PMA_PMD_DEV_TYPE 0x1
+#define IXGBE_MDIO_PCS_DEV_TYPE 0x3
+#define IXGBE_MDIO_PHY_XS_DEV_TYPE 0x4
+#define IXGBE_MDIO_AUTO_NEG_DEV_TYPE 0x7
+#define IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE 0x1E /* Device 30 */
+
+#define IXGBE_MDIO_VENDOR_SPECIFIC_1_CONTROL 0x0 /* VS1 Control Reg */
+#define IXGBE_MDIO_VENDOR_SPECIFIC_1_STATUS 0x1 /* VS1 Status Reg */
+#define IXGBE_MDIO_VENDOR_SPECIFIC_1_LINK_STATUS 0x0008 /* 1 = Link Up */
+#define IXGBE_MDIO_VENDOR_SPECIFIC_1_SPEED_STATUS 0x0010 /* 0 - 10G, 1 - 1G */
+#define IXGBE_MDIO_VENDOR_SPECIFIC_1_10G_SPEED 0x0018
+#define IXGBE_MDIO_VENDOR_SPECIFIC_1_1G_SPEED 0x0010
+
+#define IXGBE_MDIO_AUTO_NEG_CONTROL 0x0 /* AUTO_NEG Control Reg */
+#define IXGBE_MDIO_AUTO_NEG_STATUS 0x1 /* AUTO_NEG Status Reg */
+#define IXGBE_MDIO_PHY_XS_CONTROL 0x0 /* PHY_XS Control Reg */
+#define IXGBE_MDIO_PHY_XS_RESET 0x8000 /* PHY_XS Reset */
+#define IXGBE_MDIO_PHY_ID_HIGH 0x2 /* PHY ID High Reg*/
+#define IXGBE_MDIO_PHY_ID_LOW 0x3 /* PHY ID Low Reg*/
+#define IXGBE_MDIO_PHY_SPEED_ABILITY 0x4 /* Speed Abilty Reg */
+#define IXGBE_MDIO_PHY_SPEED_10G 0x0001 /* 10G capable */
+#define IXGBE_MDIO_PHY_SPEED_1G 0x0010 /* 1G capable */
+
+#define IXGBE_PHY_REVISION_MASK 0xFFFFFFF0
+#define IXGBE_MAX_PHY_ADDR 32
+
+/* PHY IDs*/
+#define TN1010_PHY_ID 0x00A19410
+#define QT2022_PHY_ID 0x0043A400
+
+/* General purpose Interrupt Enable */
+#define IXGBE_GPIE_MSIX_MODE 0x00000010 /* MSI-X mode */
+#define IXGBE_GPIE_OCD 0x00000020 /* Other Clear Disable */
+#define IXGBE_GPIE_EIMEN 0x00000040 /* Immediate Interrupt Enable */
+#define IXGBE_GPIE_EIAME 0x40000000
+#define IXGBE_GPIE_PBA_SUPPORT 0x80000000
+
+/* Transmit Flow Control status */
+#define IXGBE_TFCS_TXOFF 0x00000001
+#define IXGBE_TFCS_TXOFF0 0x00000100
+#define IXGBE_TFCS_TXOFF1 0x00000200
+#define IXGBE_TFCS_TXOFF2 0x00000400
+#define IXGBE_TFCS_TXOFF3 0x00000800
+#define IXGBE_TFCS_TXOFF4 0x00001000
+#define IXGBE_TFCS_TXOFF5 0x00002000
+#define IXGBE_TFCS_TXOFF6 0x00004000
+#define IXGBE_TFCS_TXOFF7 0x00008000
+
+/* TCP Timer */
+#define IXGBE_TCPTIMER_KS 0x00000100
+#define IXGBE_TCPTIMER_COUNT_ENABLE 0x00000200
+#define IXGBE_TCPTIMER_COUNT_FINISH 0x00000400
+#define IXGBE_TCPTIMER_LOOP 0x00000800
+#define IXGBE_TCPTIMER_DURATION_MASK 0x000000FF
+
+/* HLREG0 Bit Masks */
+#define IXGBE_HLREG0_TXCRCEN 0x00000001 /* bit 0 */
+#define IXGBE_HLREG0_RXCRCSTRP 0x00000002 /* bit 1 */
+#define IXGBE_HLREG0_JUMBOEN 0x00000004 /* bit 2 */
+#define IXGBE_HLREG0_TXPADEN 0x00000400 /* bit 10 */
+#define IXGBE_HLREG0_TXPAUSEEN 0x00001000 /* bit 12 */
+#define IXGBE_HLREG0_RXPAUSEEN 0x00004000 /* bit 14 */
+#define IXGBE_HLREG0_LPBK 0x00008000 /* bit 15 */
+#define IXGBE_HLREG0_MDCSPD 0x00010000 /* bit 16 */
+#define IXGBE_HLREG0_CONTMDC 0x00020000 /* bit 17 */
+#define IXGBE_HLREG0_CTRLFLTR 0x00040000 /* bit 18 */
+#define IXGBE_HLREG0_PREPEND 0x00F00000 /* bits 20-23 */
+#define IXGBE_HLREG0_PRIPAUSEEN 0x01000000 /* bit 24 */
+#define IXGBE_HLREG0_RXPAUSERECDA 0x06000000 /* bits 25-26 */
+#define IXGBE_HLREG0_RXLNGTHERREN 0x08000000 /* bit 27 */
+#define IXGBE_HLREG0_RXPADSTRIPEN 0x10000000 /* bit 28 */
+
+/* VMD_CTL bitmasks */
+#define IXGBE_VMD_CTL_VMDQ_EN 0x00000001
+#define IXGBE_VMD_CTL_VMDQ_FILTER 0x00000002
+
+/* RDHMPN and TDHMPN bitmasks */
+#define IXGBE_RDHMPN_RDICADDR 0x007FF800
+#define IXGBE_RDHMPN_RDICRDREQ 0x00800000
+#define IXGBE_RDHMPN_RDICADDR_SHIFT 11
+#define IXGBE_TDHMPN_TDICADDR 0x003FF800
+#define IXGBE_TDHMPN_TDICRDREQ 0x00800000
+#define IXGBE_TDHMPN_TDICADDR_SHIFT 11
+
+/* Receive Checksum Control */
+#define IXGBE_RXCSUM_IPPCSE 0x00001000 /* IP payload checksum enable */
+#define IXGBE_RXCSUM_PCSD 0x00002000 /* packet checksum disabled */
+
+/* FCRTL Bit Masks */
+#define IXGBE_FCRTL_XONE 0x80000000 /* bit 31, XON enable */
+#define IXGBE_FCRTH_FCEN 0x80000000 /* Rx Flow control enable */
+
+/* PAP bit masks*/
+#define IXGBE_PAP_TXPAUSECNT_MASK 0x0000FFFF /* Pause counter mask */
+
+/* RMCS Bit Masks */
+#define IXGBE_RMCS_RRM 0x00000002 /* Receive Recylce Mode enable */
+/* Receive Arbitration Control: 0 Round Robin, 1 DFP */
+#define IXGBE_RMCS_RAC 0x00000004
+#define IXGBE_RMCS_DFP IXGBE_RMCS_RAC /* Deficit Fixed Priority ena */
+#define IXGBE_RMCS_TFCE_802_3X 0x00000008 /* Tx Priority flow control ena */
+#define IXGBE_RMCS_TFCE_PRIORITY 0x00000010 /* Tx Priority flow control ena */
+#define IXGBE_RMCS_ARBDIS 0x00000040 /* Arbitration disable bit */
+
+/* Interrupt register bitmasks */
+
+/* Extended Interrupt Cause Read */
+#define IXGBE_EICR_RTX_QUEUE 0x0000FFFF /* RTx Queue Interrupt */
+#define IXGBE_EICR_LSC 0x00100000 /* Link Status Change */
+#define IXGBE_EICR_MNG 0x00400000 /* Managability Event Interrupt */
+#define IXGBE_EICR_PBUR 0x10000000 /* Packet Buffer Handler Error */
+#define IXGBE_EICR_DHER 0x20000000 /* Descriptor Handler Error */
+#define IXGBE_EICR_TCP_TIMER 0x40000000 /* TCP Timer */
+#define IXGBE_EICR_OTHER 0x80000000 /* Interrupt Cause Active */
+
+/* Extended Interrupt Cause Set */
+#define IXGBE_EICS_RTX_QUEUE IXGBE_EICR_RTX_QUEUE /* RTx Queue Interrupt */
+#define IXGBE_EICS_LSC IXGBE_EICR_LSC /* Link Status Change */
+#define IXGBE_EICR_GPI_SDP0 0x01000000 /* Gen Purpose Interrupt on SDP0 */
+#define IXGBE_EICS_MNG IXGBE_EICR_MNG /* MNG Event Interrupt */
+#define IXGBE_EICS_PBUR IXGBE_EICR_PBUR /* Pkt Buf Handler Error */
+#define IXGBE_EICS_DHER IXGBE_EICR_DHER /* Desc Handler Error */
+#define IXGBE_EICS_TCP_TIMER IXGBE_EICR_TCP_TIMER /* TCP Timer */
+#define IXGBE_EICS_OTHER IXGBE_EICR_OTHER /* INT Cause Active */
+
+/* Extended Interrupt Mask Set */
+#define IXGBE_EIMS_RTX_QUEUE IXGBE_EICR_RTX_QUEUE /* RTx Queue Interrupt */
+#define IXGBE_EIMS_LSC IXGBE_EICR_LSC /* Link Status Change */
+#define IXGBE_EIMS_MNG IXGBE_EICR_MNG /* MNG Event Interrupt */
+#define IXGBE_EIMS_PBUR IXGBE_EICR_PBUR /* Pkt Buf Handler Error */
+#define IXGBE_EIMS_DHER IXGBE_EICR_DHER /* Descr Handler Error */
+#define IXGBE_EIMS_TCP_TIMER IXGBE_EICR_TCP_TIMER /* TCP Timer */
+#define IXGBE_EIMS_OTHER IXGBE_EICR_OTHER /* INT Cause Active */
+
+/* Extended Interrupt Mask Clear */
+#define IXGBE_EIMC_RTX_QUEUE IXGBE_EICR_RTX_QUEUE /* RTx Queue Interrupt */
+#define IXGBE_EIMC_LSC IXGBE_EICR_LSC /* Link Status Change */
+#define IXGBE_EIMC_MNG IXGBE_EICR_MNG /* MNG Event Interrupt */
+#define IXGBE_EIMC_PBUR IXGBE_EICR_PBUR /* Pkt Buf Handler Error */
+#define IXGBE_EIMC_DHER IXGBE_EICR_DHER /* Desc Handler Error */
+#define IXGBE_EIMC_TCP_TIMER IXGBE_EICR_TCP_TIMER /* TCP Timer */
+#define IXGBE_EIMC_OTHER IXGBE_EICR_OTHER /* INT Cause Active */
+
+#define IXGBE_EIMS_ENABLE_MASK ( \
+ IXGBE_EIMS_RTX_QUEUE | \
+ IXGBE_EIMS_LSC | \
+ IXGBE_EIMS_TCP_TIMER | \
+ IXGBE_EIMS_OTHER)
+
+/* Immediate Interrupt RX (A.K.A. Low Latency Interrupt) */
+#define IXGBE_IMIR_PORT_IM_EN 0x00010000 /* TCP port enable */
+#define IXGBE_IMIR_PORT_BP 0x00020000 /* TCP port check bypass */
+#define IXGBE_IMIREXT_SIZE_BP 0x00001000 /* Packet size bypass */
+#define IXGBE_IMIREXT_CTRL_URG 0x00002000 /* Check URG bit in header */
+#define IXGBE_IMIREXT_CTRL_ACK 0x00004000 /* Check ACK bit in header */
+#define IXGBE_IMIREXT_CTRL_PSH 0x00008000 /* Check PSH bit in header */
+#define IXGBE_IMIREXT_CTRL_RST 0x00010000 /* Check RST bit in header */
+#define IXGBE_IMIREXT_CTRL_SYN 0x00020000 /* Check SYN bit in header */
+#define IXGBE_IMIREXT_CTRL_FIN 0x00040000 /* Check FIN bit in header */
+#define IXGBE_IMIREXT_CTRL_BP 0x00080000 /* Bypass check of control bits */
+
+/* Interrupt clear mask */
+#define IXGBE_IRQ_CLEAR_MASK 0xFFFFFFFF
+
+/* Interrupt Vector Allocation Registers */
+#define IXGBE_IVAR_REG_NUM 25
+#define IXGBE_IVAR_TXRX_ENTRY 96
+#define IXGBE_IVAR_RX_ENTRY 64
+#define IXGBE_IVAR_RX_QUEUE(_i) (0 + (_i))
+#define IXGBE_IVAR_TX_QUEUE(_i) (64 + (_i))
+#define IXGBE_IVAR_TX_ENTRY 32
+
+#define IXGBE_IVAR_TCP_TIMER_INDEX 96 /* 0 based index */
+#define IXGBE_IVAR_OTHER_CAUSES_INDEX 97 /* 0 based index */
+
+#define IXGBE_MSIX_VECTOR(_i) (0 + (_i))
+
+#define IXGBE_IVAR_ALLOC_VAL 0x80 /* Interrupt Allocation valid */
+
+/* VLAN Control Bit Masks */
+#define IXGBE_VLNCTRL_VET 0x0000FFFF /* bits 0-15 */
+#define IXGBE_VLNCTRL_CFI 0x10000000 /* bit 28 */
+#define IXGBE_VLNCTRL_CFIEN 0x20000000 /* bit 29 */
+#define IXGBE_VLNCTRL_VFE 0x40000000 /* bit 30 */
+#define IXGBE_VLNCTRL_VME 0x80000000 /* bit 31 */
+
+#define IXGBE_ETHERNET_IEEE_VLAN_TYPE 0x8100 /* 802.1q protocol */
+
+/* STATUS Bit Masks */
+#define IXGBE_STATUS_LAN_ID 0x0000000C /* LAN ID */
+#define IXGBE_STATUS_GIO 0x00080000 /* GIO Master Enable Status */
+
+#define IXGBE_STATUS_LAN_ID_0 0x00000000 /* LAN ID 0 */
+#define IXGBE_STATUS_LAN_ID_1 0x00000004 /* LAN ID 1 */
+
+/* ESDP Bit Masks */
+#define IXGBE_ESDP_SDP4 0x00000001 /* SDP4 Data Value */
+#define IXGBE_ESDP_SDP5 0x00000002 /* SDP5 Data Value */
+#define IXGBE_ESDP_SDP4_DIR 0x00000004 /* SDP4 IO direction */
+#define IXGBE_ESDP_SDP5_DIR 0x00000008 /* SDP5 IO direction */
+
+/* LEDCTL Bit Masks */
+#define IXGBE_LED_IVRT_BASE 0x00000040
+#define IXGBE_LED_BLINK_BASE 0x00000080
+#define IXGBE_LED_MODE_MASK_BASE 0x0000000F
+#define IXGBE_LED_OFFSET(_base, _i) (_base << (8 * (_i)))
+#define IXGBE_LED_MODE_SHIFT(_i) (8*(_i))
+#define IXGBE_LED_IVRT(_i) IXGBE_LED_OFFSET(IXGBE_LED_IVRT_BASE, _i)
+#define IXGBE_LED_BLINK(_i) IXGBE_LED_OFFSET(IXGBE_LED_BLINK_BASE, _i)
+#define IXGBE_LED_MODE_MASK(_i) IXGBE_LED_OFFSET(IXGBE_LED_MODE_MASK_BASE, _i)
+
+/* LED modes */
+#define IXGBE_LED_LINK_UP 0x0
+#define IXGBE_LED_LINK_10G 0x1
+#define IXGBE_LED_MAC 0x2
+#define IXGBE_LED_FILTER 0x3
+#define IXGBE_LED_LINK_ACTIVE 0x4
+#define IXGBE_LED_LINK_1G 0x5
+#define IXGBE_LED_ON 0xE
+#define IXGBE_LED_OFF 0xF
+
+/* AUTOC Bit Masks */
+#define IXGBE_AUTOC_KX4_SUPP 0x80000000
+#define IXGBE_AUTOC_KX_SUPP 0x40000000
+#define IXGBE_AUTOC_PAUSE 0x30000000
+#define IXGBE_AUTOC_RF 0x08000000
+#define IXGBE_AUTOC_PD_TMR 0x06000000
+#define IXGBE_AUTOC_AN_RX_LOOSE 0x01000000
+#define IXGBE_AUTOC_AN_RX_DRIFT 0x00800000
+#define IXGBE_AUTOC_AN_RX_ALIGN 0x007C0000
+#define IXGBE_AUTOC_AN_RESTART 0x00001000
+#define IXGBE_AUTOC_FLU 0x00000001
+#define IXGBE_AUTOC_LMS_SHIFT 13
+#define IXGBE_AUTOC_LMS_MASK (0x7 << IXGBE_AUTOC_LMS_SHIFT)
+#define IXGBE_AUTOC_LMS_1G_LINK_NO_AN (0x0 << IXGBE_AUTOC_LMS_SHIFT)
+#define IXGBE_AUTOC_LMS_10G_LINK_NO_AN (0x1 << IXGBE_AUTOC_LMS_SHIFT)
+#define IXGBE_AUTOC_LMS_1G_AN (0x2 << IXGBE_AUTOC_LMS_SHIFT)
+#define IXGBE_AUTOC_LMS_KX4_AN (0x4 << IXGBE_AUTOC_LMS_SHIFT)
+#define IXGBE_AUTOC_LMS_KX4_AN_1G_AN (0x6 << IXGBE_AUTOC_LMS_SHIFT)
+#define IXGBE_AUTOC_LMS_ATTACH_TYPE (0x7 << IXGBE_AUTOC_10G_PMA_PMD_SHIFT)
+
+#define IXGBE_AUTOC_1G_PMA_PMD 0x00000200
+#define IXGBE_AUTOC_10G_PMA_PMD 0x00000180
+#define IXGBE_AUTOC_10G_PMA_PMD_SHIFT 7
+#define IXGBE_AUTOC_1G_PMA_PMD_SHIFT 9
+#define IXGBE_AUTOC_10G_XAUI (0x0 << IXGBE_AUTOC_10G_PMA_PMD_SHIFT)
+#define IXGBE_AUTOC_10G_KX4 (0x1 << IXGBE_AUTOC_10G_PMA_PMD_SHIFT)
+#define IXGBE_AUTOC_10G_CX4 (0x2 << IXGBE_AUTOC_10G_PMA_PMD_SHIFT)
+#define IXGBE_AUTOC_1G_BX (0x0 << IXGBE_AUTOC_1G_PMA_PMD_SHIFT)
+#define IXGBE_AUTOC_1G_KX (0x1 << IXGBE_AUTOC_1G_PMA_PMD_SHIFT)
+
+/* LINKS Bit Masks */
+#define IXGBE_LINKS_KX_AN_COMP 0x80000000
+#define IXGBE_LINKS_UP 0x40000000
+#define IXGBE_LINKS_SPEED 0x20000000
+#define IXGBE_LINKS_MODE 0x18000000
+#define IXGBE_LINKS_RX_MODE 0x06000000
+#define IXGBE_LINKS_TX_MODE 0x01800000
+#define IXGBE_LINKS_XGXS_EN 0x00400000
+#define IXGBE_LINKS_PCS_1G_EN 0x00200000
+#define IXGBE_LINKS_1G_AN_EN 0x00100000
+#define IXGBE_LINKS_KX_AN_IDLE 0x00080000
+#define IXGBE_LINKS_1G_SYNC 0x00040000
+#define IXGBE_LINKS_10G_ALIGN 0x00020000
+#define IXGBE_LINKS_10G_LANE_SYNC 0x00017000
+#define IXGBE_LINKS_TL_FAULT 0x00001000
+#define IXGBE_LINKS_SIGNAL 0x00000F00
+
+#define IXGBE_AUTO_NEG_TIME 45 /* 4.5 Seconds */
+
+/* SW Semaphore Register bitmasks */
+#define IXGBE_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */
+#define IXGBE_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */
+#define IXGBE_SWSM_WMNG 0x00000004 /* Wake MNG Clock */
+
+/* GSSR definitions */
+#define IXGBE_GSSR_EEP_SM 0x0001
+#define IXGBE_GSSR_PHY0_SM 0x0002
+#define IXGBE_GSSR_PHY1_SM 0x0004
+#define IXGBE_GSSR_MAC_CSR_SM 0x0008
+#define IXGBE_GSSR_FLASH_SM 0x0010
+
+/* EEC Register */
+#define IXGBE_EEC_SK 0x00000001 /* EEPROM Clock */
+#define IXGBE_EEC_CS 0x00000002 /* EEPROM Chip Select */
+#define IXGBE_EEC_DI 0x00000004 /* EEPROM Data In */
+#define IXGBE_EEC_DO 0x00000008 /* EEPROM Data Out */
+#define IXGBE_EEC_FWE_MASK 0x00000030 /* FLASH Write Enable */
+#define IXGBE_EEC_FWE_DIS 0x00000010 /* Disable FLASH writes */
+#define IXGBE_EEC_FWE_EN 0x00000020 /* Enable FLASH writes */
+#define IXGBE_EEC_FWE_SHIFT 4
+#define IXGBE_EEC_REQ 0x00000040 /* EEPROM Access Request */
+#define IXGBE_EEC_GNT 0x00000080 /* EEPROM Access Grant */
+#define IXGBE_EEC_PRES 0x00000100 /* EEPROM Present */
+#define IXGBE_EEC_ARD 0x00000200 /* EEPROM Auto Read Done */
+/* EEPROM Addressing bits based on type (0-small, 1-large) */
+#define IXGBE_EEC_ADDR_SIZE 0x00000400
+#define IXGBE_EEC_SIZE 0x00007800 /* EEPROM Size */
+
+#define IXGBE_EEC_SIZE_SHIFT 11
+#define IXGBE_EEPROM_WORD_SIZE_SHIFT 6
+#define IXGBE_EEPROM_OPCODE_BITS 8
+
+/* Checksum and EEPROM pointers */
+#define IXGBE_EEPROM_CHECKSUM 0x3F
+#define IXGBE_EEPROM_SUM 0xBABA
+#define IXGBE_PCIE_ANALOG_PTR 0x03
+#define IXGBE_ATLAS0_CONFIG_PTR 0x04
+#define IXGBE_ATLAS1_CONFIG_PTR 0x05
+#define IXGBE_PCIE_GENERAL_PTR 0x06
+#define IXGBE_PCIE_CONFIG0_PTR 0x07
+#define IXGBE_PCIE_CONFIG1_PTR 0x08
+#define IXGBE_CORE0_PTR 0x09
+#define IXGBE_CORE1_PTR 0x0A
+#define IXGBE_MAC0_PTR 0x0B
+#define IXGBE_MAC1_PTR 0x0C
+#define IXGBE_CSR0_CONFIG_PTR 0x0D
+#define IXGBE_CSR1_CONFIG_PTR 0x0E
+#define IXGBE_FW_PTR 0x0F
+
+/* EEPROM Commands - SPI */
+#define IXGBE_EEPROM_MAX_RETRY_SPI 5000 /* Max wait 5ms for RDY signal */
+#define IXGBE_EEPROM_STATUS_RDY_SPI 0x01
+#define IXGBE_EEPROM_READ_OPCODE_SPI 0x03 /* EEPROM read opcode */
+#define IXGBE_EEPROM_WRITE_OPCODE_SPI 0x02 /* EEPROM write opcode */
+#define IXGBE_EEPROM_A8_OPCODE_SPI 0x08 /* opcode bit-3 = addr bit-8 */
+#define IXGBE_EEPROM_WREN_OPCODE_SPI 0x06 /* EEPROM set Write Ena latch */
+/* EEPROM reset Write Enbale latch */
+#define IXGBE_EEPROM_WRDI_OPCODE_SPI 0x04
+#define IXGBE_EEPROM_RDSR_OPCODE_SPI 0x05 /* EEPROM read Status reg */
+#define IXGBE_EEPROM_WRSR_OPCODE_SPI 0x01 /* EEPROM write Status reg */
+#define IXGBE_EEPROM_ERASE4K_OPCODE_SPI 0x20 /* EEPROM ERASE 4KB */
+#define IXGBE_EEPROM_ERASE64K_OPCODE_SPI 0xD8 /* EEPROM ERASE 64KB */
+#define IXGBE_EEPROM_ERASE256_OPCODE_SPI 0xDB /* EEPROM ERASE 256B */
+
+/* EEPROM Read Register */
+#define IXGBE_EEPROM_READ_REG_DATA 16 /* data offset in EEPROM read reg */
+#define IXGBE_EEPROM_READ_REG_DONE 2 /* Offset to READ done bit */
+#define IXGBE_EEPROM_READ_REG_START 1 /* First bit to start operation */
+#define IXGBE_EEPROM_READ_ADDR_SHIFT 2 /* Shift to the address bits */
+
+#define IXGBE_ETH_LENGTH_OF_ADDRESS 6
+
+#ifndef IXGBE_EEPROM_GRANT_ATTEMPTS
+#define IXGBE_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */
+#endif
+
+#ifndef IXGBE_EERD_ATTEMPTS
+/* Number of 5 microseconds we wait for EERD read to complete */
+#define IXGBE_EERD_ATTEMPTS 100000
+#endif
+
+/* PCI Bus Info */
+#define IXGBE_PCI_LINK_STATUS 0xB2
+#define IXGBE_PCI_LINK_WIDTH 0x3F0
+#define IXGBE_PCI_LINK_WIDTH_1 0x10
+#define IXGBE_PCI_LINK_WIDTH_2 0x20
+#define IXGBE_PCI_LINK_WIDTH_4 0x40
+#define IXGBE_PCI_LINK_WIDTH_8 0x80
+#define IXGBE_PCI_LINK_SPEED 0xF
+#define IXGBE_PCI_LINK_SPEED_2500 0x1
+#define IXGBE_PCI_LINK_SPEED_5000 0x2
+
+/* Number of 100 microseconds we wait for PCI Express master disable */
+#define IXGBE_PCI_MASTER_DISABLE_TIMEOUT 800
+
+/* PHY Types */
+#define IXGBE_M88E1145_E_PHY_ID 0x01410CD0
+
+/* Check whether address is multicast. This is little-endian specific check.*/
+#define IXGBE_IS_MULTICAST(Address) \
+ (bool)(((u8 *)(Address))[0] & ((u8)0x01))
+
+/* Check whether an address is broadcast. */
+#define IXGBE_IS_BROADCAST(Address) \
+ ((((u8 *)(Address))[0] == ((u8)0xff)) && \
+ (((u8 *)(Address))[1] == ((u8)0xff)))
+
+/* RAH */
+#define IXGBE_RAH_VIND_MASK 0x003C0000
+#define IXGBE_RAH_VIND_SHIFT 18
+#define IXGBE_RAH_AV 0x80000000
+
+/* Filters */
+#define IXGBE_MC_TBL_SIZE 128 /* Multicast Filter Table (4096 bits) */
+#define IXGBE_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */
+
+/* Header split receive */
+#define IXGBE_RFCTL_ISCSI_DIS 0x00000001
+#define IXGBE_RFCTL_ISCSI_DWC_MASK 0x0000003E
+#define IXGBE_RFCTL_ISCSI_DWC_SHIFT 1
+#define IXGBE_RFCTL_NFSW_DIS 0x00000040
+#define IXGBE_RFCTL_NFSR_DIS 0x00000080
+#define IXGBE_RFCTL_NFS_VER_MASK 0x00000300
+#define IXGBE_RFCTL_NFS_VER_SHIFT 8
+#define IXGBE_RFCTL_NFS_VER_2 0
+#define IXGBE_RFCTL_NFS_VER_3 1
+#define IXGBE_RFCTL_NFS_VER_4 2
+#define IXGBE_RFCTL_IPV6_DIS 0x00000400
+#define IXGBE_RFCTL_IPV6_XSUM_DIS 0x00000800
+#define IXGBE_RFCTL_IPFRSP_DIS 0x00004000
+#define IXGBE_RFCTL_IPV6_EX_DIS 0x00010000
+#define IXGBE_RFCTL_NEW_IPV6_EXT_DIS 0x00020000
+
+/* Transmit Config masks */
+#define IXGBE_TXDCTL_ENABLE 0x02000000 /* Enable specific Tx Queue */
+#define IXGBE_TXDCTL_SWFLSH 0x04000000 /* Tx Desc. write-back flushing */
+/* Enable short packet padding to 64 bytes */
+#define IXGBE_TX_PAD_ENABLE 0x00000400
+#define IXGBE_JUMBO_FRAME_ENABLE 0x00000004 /* Allow jumbo frames */
+/* This allows for 16K packets + 4k for vlan */
+#define IXGBE_MAX_FRAME_SZ 0x40040000
+
+#define IXGBE_TDWBAL_HEAD_WB_ENABLE 0x1 /* Tx head write-back enable */
+#define IXGBE_TDWBAL_SEQNUM_WB_ENABLE 0x2 /* Tx seq. # write-back enable */
+
+/* Receive Config masks */
+#define IXGBE_RXCTRL_RXEN 0x00000001 /* Enable Receiver */
+#define IXGBE_RXCTRL_DMBYPS 0x00000002 /* Descriptor Monitor Bypass */
+#define IXGBE_RXDCTL_ENABLE 0x02000000 /* Enable specific Rx Queue */
+
+#define IXGBE_FCTRL_SBP 0x00000002 /* Store Bad Packet */
+#define IXGBE_FCTRL_MPE 0x00000100 /* Multicast Promiscuous Ena*/
+#define IXGBE_FCTRL_UPE 0x00000200 /* Unicast Promiscuous Ena */
+#define IXGBE_FCTRL_BAM 0x00000400 /* Broadcast Accept Mode */
+#define IXGBE_FCTRL_PMCF 0x00001000 /* Pass MAC Control Frames */
+#define IXGBE_FCTRL_DPF 0x00002000 /* Discard Pause Frame */
+/* Receive Priority Flow Control Enbale */
+#define IXGBE_FCTRL_RPFCE 0x00004000
+#define IXGBE_FCTRL_RFCE 0x00008000 /* Receive Flow Control Ena */
+
+/* Multiple Receive Queue Control */
+#define IXGBE_MRQC_RSSEN 0x00000001 /* RSS Enable */
+#define IXGBE_MRQC_RSS_FIELD_MASK 0xFFFF0000
+#define IXGBE_MRQC_RSS_FIELD_IPV4_TCP 0x00010000
+#define IXGBE_MRQC_RSS_FIELD_IPV4 0x00020000
+#define IXGBE_MRQC_RSS_FIELD_IPV6_EX_TCP 0x00040000
+#define IXGBE_MRQC_RSS_FIELD_IPV6_EX 0x00080000
+#define IXGBE_MRQC_RSS_FIELD_IPV6 0x00100000
+#define IXGBE_MRQC_RSS_FIELD_IPV6_TCP 0x00200000
+#define IXGBE_MRQC_RSS_FIELD_IPV4_UDP 0x00400000
+#define IXGBE_MRQC_RSS_FIELD_IPV6_UDP 0x00800000
+#define IXGBE_MRQC_RSS_FIELD_IPV6_EX_UDP 0x01000000
+
+#define IXGBE_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */
+#define IXGBE_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */
+#define IXGBE_TXD_CMD_EOP 0x01000000 /* End of Packet */
+#define IXGBE_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */
+#define IXGBE_TXD_CMD_IC 0x04000000 /* Insert Checksum */
+#define IXGBE_TXD_CMD_RS 0x08000000 /* Report Status */
+#define IXGBE_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */
+#define IXGBE_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */
+#define IXGBE_TXD_STAT_DD 0x00000001 /* Descriptor Done */
+
+/* Receive Descriptor bit definitions */
+#define IXGBE_RXD_STAT_DD 0x01 /* Descriptor Done */
+#define IXGBE_RXD_STAT_EOP 0x02 /* End of Packet */
+#define IXGBE_RXD_STAT_IXSM 0x04 /* Ignore checksum */
+#define IXGBE_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */
+#define IXGBE_RXD_STAT_UDPCS 0x10 /* UDP xsum caculated */
+#define IXGBE_RXD_STAT_L4CS 0x20 /* L4 xsum calculated */
+#define IXGBE_RXD_STAT_IPCS 0x40 /* IP xsum calculated */
+#define IXGBE_RXD_STAT_PIF 0x80 /* passed in-exact filter */
+#define IXGBE_RXD_STAT_CRCV 0x100 /* Speculative CRC Valid */
+#define IXGBE_RXD_STAT_VEXT 0x200 /* 1st VLAN found */
+#define IXGBE_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */
+#define IXGBE_RXD_STAT_DYNINT 0x800 /* Pkt caused INT via DYNINT */
+#define IXGBE_RXD_STAT_ACK 0x8000 /* ACK Packet indication */
+#define IXGBE_RXD_ERR_CE 0x01 /* CRC Error */
+#define IXGBE_RXD_ERR_LE 0x02 /* Length Error */
+#define IXGBE_RXD_ERR_PE 0x08 /* Packet Error */
+#define IXGBE_RXD_ERR_OSE 0x10 /* Oversize Error */
+#define IXGBE_RXD_ERR_USE 0x20 /* Undersize Error */
+#define IXGBE_RXD_ERR_TCPE 0x40 /* TCP/UDP Checksum Error */
+#define IXGBE_RXD_ERR_IPE 0x80 /* IP Checksum Error */
+#define IXGBE_RXDADV_HBO 0x00800000
+#define IXGBE_RXDADV_ERR_CE 0x01000000 /* CRC Error */
+#define IXGBE_RXDADV_ERR_LE 0x02000000 /* Length Error */
+#define IXGBE_RXDADV_ERR_PE 0x08000000 /* Packet Error */
+#define IXGBE_RXDADV_ERR_OSE 0x10000000 /* Oversize Error */
+#define IXGBE_RXDADV_ERR_USE 0x20000000 /* Undersize Error */
+#define IXGBE_RXDADV_ERR_TCPE 0x40000000 /* TCP/UDP Checksum Error */
+#define IXGBE_RXDADV_ERR_IPE 0x80000000 /* IP Checksum Error */
+#define IXGBE_RXD_VLAN_ID_MASK 0x0FFF /* VLAN ID is in lower 12 bits */
+#define IXGBE_RXD_PRI_MASK 0xE000 /* Priority is in upper 3 bits */
+#define IXGBE_RXD_PRI_SHIFT 13
+#define IXGBE_RXD_CFI_MASK 0x1000 /* CFI is bit 12 */
+#define IXGBE_RXD_CFI_SHIFT 12
+
+/* SRRCTL bit definitions */
+#define IXGBE_SRRCTL_BSIZEPKT_SHIFT 10 /* so many KBs */
+#define IXGBE_SRRCTL_BSIZEPKT_MASK 0x0000007F
+#define IXGBE_SRRCTL_BSIZEHDR_MASK 0x00003F00
+#define IXGBE_SRRCTL_DESCTYPE_LEGACY 0x00000000
+#define IXGBE_SRRCTL_DESCTYPE_ADV_ONEBUF 0x02000000
+#define IXGBE_SRRCTL_DESCTYPE_HDR_SPLIT 0x04000000
+#define IXGBE_SRRCTL_DESCTYPE_HDR_REPLICATION_LARGE_PKT 0x08000000
+#define IXGBE_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS 0x0A000000
+
+#define IXGBE_RXDPS_HDRSTAT_HDRSP 0x00008000
+#define IXGBE_RXDPS_HDRSTAT_HDRLEN_MASK 0x000003FF
+
+#define IXGBE_RXDADV_RSSTYPE_MASK 0x0000000F
+#define IXGBE_RXDADV_PKTTYPE_MASK 0x0000FFF0
+#define IXGBE_RXDADV_HDRBUFLEN_MASK 0x00007FE0
+#define IXGBE_RXDADV_HDRBUFLEN_SHIFT 5
+#define IXGBE_RXDADV_SPLITHEADER_EN 0x00001000
+#define IXGBE_RXDADV_SPH 0x8000
+
+/* RSS Hash results */
+#define IXGBE_RXDADV_RSSTYPE_NONE 0x00000000
+#define IXGBE_RXDADV_RSSTYPE_IPV4_TCP 0x00000001
+#define IXGBE_RXDADV_RSSTYPE_IPV4 0x00000002
+#define IXGBE_RXDADV_RSSTYPE_IPV6_TCP 0x00000003
+#define IXGBE_RXDADV_RSSTYPE_IPV6_EX 0x00000004
+#define IXGBE_RXDADV_RSSTYPE_IPV6 0x00000005
+#define IXGBE_RXDADV_RSSTYPE_IPV6_TCP_EX 0x00000006
+#define IXGBE_RXDADV_RSSTYPE_IPV4_UDP 0x00000007
+#define IXGBE_RXDADV_RSSTYPE_IPV6_UDP 0x00000008
+#define IXGBE_RXDADV_RSSTYPE_IPV6_UDP_EX 0x00000009
+
+/* RSS Packet Types as indicated in the receive descriptor. */
+#define IXGBE_RXDADV_PKTTYPE_NONE 0x00000000
+#define IXGBE_RXDADV_PKTTYPE_IPV4 0x00000010 /* IPv4 hdr present */
+#define IXGBE_RXDADV_PKTTYPE_IPV4_EX 0x00000020 /* IPv4 hdr + extensions */
+#define IXGBE_RXDADV_PKTTYPE_IPV6 0x00000040 /* IPv6 hdr present */
+#define IXGBE_RXDADV_PKTTYPE_IPV6_EX 0x00000080 /* IPv6 hdr + extensions */
+#define IXGBE_RXDADV_PKTTYPE_TCP 0x00000100 /* TCP hdr present */
+#define IXGBE_RXDADV_PKTTYPE_UDP 0x00000200 /* UDP hdr present */
+#define IXGBE_RXDADV_PKTTYPE_SCTP 0x00000400 /* SCTP hdr present */
+#define IXGBE_RXDADV_PKTTYPE_NFS 0x00000800 /* NFS hdr present */
+
+/* Masks to determine if packets should be dropped due to frame errors */
+#define IXGBE_RXD_ERR_FRAME_ERR_MASK ( \
+ IXGBE_RXD_ERR_CE | \
+ IXGBE_RXD_ERR_LE | \
+ IXGBE_RXD_ERR_PE | \
+ IXGBE_RXD_ERR_OSE | \
+ IXGBE_RXD_ERR_USE)
+
+#define IXGBE_RXDADV_ERR_FRAME_ERR_MASK ( \
+ IXGBE_RXDADV_ERR_CE | \
+ IXGBE_RXDADV_ERR_LE | \
+ IXGBE_RXDADV_ERR_PE | \
+ IXGBE_RXDADV_ERR_OSE | \
+ IXGBE_RXDADV_ERR_USE)
+
+/* Multicast bit mask */
+#define IXGBE_MCSTCTRL_MFE 0x4
+
+/* Number of Transmit and Receive Descriptors must be a multiple of 8 */
+#define IXGBE_REQ_TX_DESCRIPTOR_MULTIPLE 8
+#define IXGBE_REQ_RX_DESCRIPTOR_MULTIPLE 8
+#define IXGBE_REQ_TX_BUFFER_GRANULARITY 1024
+
+/* Vlan-specific macros */
+#define IXGBE_RX_DESC_SPECIAL_VLAN_MASK 0x0FFF /* VLAN ID in lower 12 bits */
+#define IXGBE_RX_DESC_SPECIAL_PRI_MASK 0xE000 /* Priority in upper 3 bits */
+#define IXGBE_RX_DESC_SPECIAL_PRI_SHIFT 0x000D /* Priority in upper 3 of 16 */
+#define IXGBE_TX_DESC_SPECIAL_PRI_SHIFT IXGBE_RX_DESC_SPECIAL_PRI_SHIFT
+
+/* Transmit Descriptor - Legacy */
+struct ixgbe_legacy_tx_desc {
+ u64 buffer_addr; /* Address of the descriptor's data buffer */
+ union {
+ u32 data;
+ struct {
+ u16 length; /* Data buffer length */
+ u8 cso; /* Checksum offset */
+ u8 cmd; /* Descriptor control */
+ } flags;
+ } lower;
+ union {
+ u32 data;
+ struct {
+ u8 status; /* Descriptor status */
+ u8 css; /* Checksum start */
+ u16 vlan;
+ } fields;
+ } upper;
+};
+
+/* Transmit Descriptor - Advanced */
+union ixgbe_adv_tx_desc {
+ struct {
+ u64 buffer_addr; /* Address of descriptor's data buf */
+ u32 cmd_type_len;
+ u32 olinfo_status;
+ } read;
+ struct {
+ u64 rsvd; /* Reserved */
+ u32 nxtseq_seed;
+ u32 status;
+ } wb;
+};
+
+/* Receive Descriptor - Legacy */
+struct ixgbe_legacy_rx_desc {
+ u64 buffer_addr; /* Address of the descriptor's data buffer */
+ u16 length; /* Length of data DMAed into data buffer */
+ u16 csum; /* Packet checksum */
+ u8 status; /* Descriptor status */
+ u8 errors; /* Descriptor Errors */
+ u16 vlan;
+};
+
+/* Receive Descriptor - Advanced */
+union ixgbe_adv_rx_desc {
+ struct {
+ u64 pkt_addr; /* Packet buffer address */
+ u64 hdr_addr; /* Header buffer address */
+ } read;
+ struct {
+ struct {
+ struct {
+ u16 pkt_info; /* RSS type, Packet type */
+ u16 hdr_info; /* Split Header, header len */
+ } lo_dword;
+ union {
+ u32 rss; /* RSS Hash */
+ struct {
+ u16 ip_id; /* IP id */
+ u16 csum; /* Packet Checksum */
+ } csum_ip;
+ } hi_dword;
+ } lower;
+ struct {
+ u32 status_error; /* ext status/error */
+ u16 length; /* Packet length */
+ u16 vlan; /* VLAN tag */
+ } upper;
+ } wb; /* writeback */
+};
+
+/* Context descriptors */
+struct ixgbe_adv_tx_context_desc {
+ u32 vlan_macip_lens;
+ u32 seqnum_seed;
+ u32 type_tucmd_mlhl;
+ u32 mss_l4len_idx;
+};
+
+/* Adv Transmit Descriptor Config Masks */
+#define IXGBE_ADVTXD_DTALEN_MASK 0x0000FFFF /* Data buffer length(bytes) */
+#define IXGBE_ADVTXD_DTYP_MASK 0x00F00000 /* DTYP mask */
+#define IXGBE_ADVTXD_DTYP_CTXT 0x00200000 /* Advanced Context Desc */
+#define IXGBE_ADVTXD_DTYP_DATA 0x00300000 /* Advanced Data Descriptor */
+#define IXGBE_ADVTXD_DCMD_EOP IXGBE_TXD_CMD_EOP /* End of Packet */
+#define IXGBE_ADVTXD_DCMD_IFCS IXGBE_TXD_CMD_IFCS /* Insert FCS */
+#define IXGBE_ADVTXD_DCMD_RDMA 0x04000000 /* RDMA */
+#define IXGBE_ADVTXD_DCMD_RS IXGBE_TXD_CMD_RS /* Report Status */
+#define IXGBE_ADVTXD_DCMD_DDTYP_ISCSI 0x10000000 /* DDP hdr type or iSCSI */
+#define IXGBE_ADVTXD_DCMD_DEXT IXGBE_TXD_CMD_DEXT /* Desc ext (1=Adv) */
+#define IXGBE_ADVTXD_DCMD_VLE IXGBE_TXD_CMD_VLE /* VLAN pkt enable */
+#define IXGBE_ADVTXD_DCMD_TSE 0x80000000 /* TCP Seg enable */
+#define IXGBE_ADVTXD_STAT_DD IXGBE_TXD_STAT_DD /* Descriptor Done */
+#define IXGBE_ADVTXD_STAT_SN_CRC 0x00000002 /* NXTSEQ/SEED present in WB */
+#define IXGBE_ADVTXD_STAT_RSV 0x0000000C /* STA Reserved */
+#define IXGBE_ADVTXD_IDX_SHIFT 4 /* Adv desc Index shift */
+#define IXGBE_ADVTXD_POPTS_SHIFT 8 /* Adv desc POPTS shift */
+#define IXGBE_ADVTXD_POPTS_IXSM (IXGBE_TXD_POPTS_IXSM << \
+ IXGBE_ADVTXD_POPTS_SHIFT)
+#define IXGBE_ADVTXD_POPTS_TXSM (IXGBE_TXD_POPTS_TXSM << \
+ IXGBE_ADVTXD_POPTS_SHIFT)
+#define IXGBE_ADVTXD_POPTS_EOM 0x00000400 /* Enable L bit-RDMA DDP hdr */
+#define IXGBE_ADVTXD_POPTS_ISCO_1ST 0x00000000 /* 1st TSO of iSCSI PDU */
+#define IXGBE_ADVTXD_POPTS_ISCO_MDL 0x00000800 /* Middle TSO of iSCSI PDU */
+#define IXGBE_ADVTXD_POPTS_ISCO_LAST 0x00001000 /* Last TSO of iSCSI PDU */
+#define IXGBE_ADVTXD_POPTS_ISCO_FULL 0x00001800 /* 1st&Last TSO-full iSCSI PDU*/
+#define IXGBE_ADVTXD_POPTS_RSV 0x00002000 /* POPTS Reserved */
+#define IXGBE_ADVTXD_PAYLEN_SHIFT 14 /* Adv desc PAYLEN shift */
+#define IXGBE_ADVTXD_MACLEN_SHIFT 9 /* Adv ctxt desc mac len shift */
+#define IXGBE_ADVTXD_VLAN_SHIFT 16 /* Adv ctxt vlan tag shift */
+#define IXGBE_ADVTXD_TUCMD_IPV4 0x00000400 /* IP Packet Type: 1=IPv4 */
+#define IXGBE_ADVTXD_TUCMD_IPV6 0x00000000 /* IP Packet Type: 0=IPv6 */
+#define IXGBE_ADVTXD_TUCMD_L4T_UDP 0x00000000 /* L4 Packet TYPE of UDP */
+#define IXGBE_ADVTXD_TUCMD_L4T_TCP 0x00000800 /* L4 Packet TYPE of TCP */
+#define IXGBE_ADVTXD_TUCMD_MKRREQ 0x00002000 /* Req requires Markers and CRC */
+#define IXGBE_ADVTXD_L4LEN_SHIFT 8 /* Adv ctxt L4LEN shift */
+#define IXGBE_ADVTXD_MSS_SHIFT 16 /* Adv ctxt MSS shift */
+
+#define IXGBE_LINK_SPEED_UNKNOWN 0
+#define IXGBE_LINK_SPEED_1GB_FULL 0x0020
+#define IXGBE_LINK_SPEED_10GB_FULL 0x0080
+
+
+enum ixgbe_eeprom_type {
+ ixgbe_eeprom_uninitialized = 0,
+ ixgbe_eeprom_spi,
+ ixgbe_eeprom_none /* No NVM support */
+};
+
+enum ixgbe_mac_type {
+ ixgbe_mac_unknown = 0,
+ ixgbe_mac_82598EB,
+ ixgbe_num_macs
+};
+
+enum ixgbe_phy_type {
+ ixgbe_phy_unknown = 0,
+ ixgbe_phy_tn,
+ ixgbe_phy_qt,
+ ixgbe_phy_xaui
+};
+
+enum ixgbe_media_type {
+ ixgbe_media_type_unknown = 0,
+ ixgbe_media_type_fiber,
+ ixgbe_media_type_copper,
+ ixgbe_media_type_backplane
+};
+
+/* Flow Control Settings */
+enum ixgbe_fc_type {
+ ixgbe_fc_none = 0,
+ ixgbe_fc_rx_pause,
+ ixgbe_fc_tx_pause,
+ ixgbe_fc_full,
+ ixgbe_fc_default
+};
+
+/* PCI bus types */
+enum ixgbe_bus_type {
+ ixgbe_bus_type_unknown = 0,
+ ixgbe_bus_type_pci,
+ ixgbe_bus_type_pcix,
+ ixgbe_bus_type_pci_express,
+ ixgbe_bus_type_reserved
+};
+
+/* PCI bus speeds */
+enum ixgbe_bus_speed {
+ ixgbe_bus_speed_unknown = 0,
+ ixgbe_bus_speed_33,
+ ixgbe_bus_speed_66,
+ ixgbe_bus_speed_100,
+ ixgbe_bus_speed_120,
+ ixgbe_bus_speed_133,
+ ixgbe_bus_speed_2500,
+ ixgbe_bus_speed_5000,
+ ixgbe_bus_speed_reserved
+};
+
+/* PCI bus widths */
+enum ixgbe_bus_width {
+ ixgbe_bus_width_unknown = 0,
+ ixgbe_bus_width_pcie_x1,
+ ixgbe_bus_width_pcie_x2,
+ ixgbe_bus_width_pcie_x4,
+ ixgbe_bus_width_pcie_x8,
+ ixgbe_bus_width_32,
+ ixgbe_bus_width_64,
+ ixgbe_bus_width_reserved
+};
+
+struct ixgbe_eeprom_info {
+ enum ixgbe_eeprom_type type;
+ u16 word_size;
+ u16 address_bits;
+};
+
+struct ixgbe_addr_filter_info {
+ u32 num_mc_addrs;
+ u32 rar_used_count;
+ u32 mc_addr_in_rar_count;
+ u32 mta_in_use;
+};
+
+/* Bus parameters */
+struct ixgbe_bus_info {
+ enum ixgbe_bus_speed speed;
+ enum ixgbe_bus_width width;
+ enum ixgbe_bus_type type;
+};
+
+/* Flow control parameters */
+struct ixgbe_fc_info {
+ u32 high_water; /* Flow Control High-water */
+ u32 low_water; /* Flow Control Low-water */
+ u16 pause_time; /* Flow Control Pause timer */
+ bool send_xon; /* Flow control send XON */
+ bool strict_ieee; /* Strict IEEE mode */
+ enum ixgbe_fc_type type; /* Type of flow control */
+ enum ixgbe_fc_type original_type;
+};
+
+/* Statistics counters collected by the MAC */
+struct ixgbe_hw_stats {
+ u64 crcerrs;
+ u64 illerrc;
+ u64 errbc;
+ u64 mspdc;
+ u64 mpctotal;
+ u64 mpc[8];
+ u64 mlfc;
+ u64 mrfc;
+ u64 rlec;
+ u64 lxontxc;
+ u64 lxonrxc;
+ u64 lxofftxc;
+ u64 lxoffrxc;
+ u64 pxontxc[8];
+ u64 pxonrxc[8];
+ u64 pxofftxc[8];
+ u64 pxoffrxc[8];
+ u64 prc64;
+ u64 prc127;
+ u64 prc255;
+ u64 prc511;
+ u64 prc1023;
+ u64 prc1522;
+ u64 gprc;
+ u64 bprc;
+ u64 mprc;
+ u64 gptc;
+ u64 gorc;
+ u64 gotc;
+ u64 rnbc[8];
+ u64 ruc;
+ u64 rfc;
+ u64 roc;
+ u64 rjc;
+ u64 mngprc;
+ u64 mngpdc;
+ u64 mngptc;
+ u64 tor;
+ u64 tpr;
+ u64 tpt;
+ u64 ptc64;
+ u64 ptc127;
+ u64 ptc255;
+ u64 ptc511;
+ u64 ptc1023;
+ u64 ptc1522;
+ u64 mptc;
+ u64 bptc;
+ u64 xec;
+ u64 rqsmr[16];
+ u64 tqsmr[8];
+ u64 qprc[16];
+ u64 qptc[16];
+ u64 qbrc[16];
+ u64 qbtc[16];
+};
+
+/* forward declaration */
+struct ixgbe_hw;
+
+/* Function pointer table */
+struct ixgbe_functions {
+ s32 (*ixgbe_func_init_hw)(struct ixgbe_hw *);
+ s32 (*ixgbe_func_reset_hw)(struct ixgbe_hw *);
+ s32 (*ixgbe_func_start_hw)(struct ixgbe_hw *);
+ s32 (*ixgbe_func_clear_hw_cntrs)(struct ixgbe_hw *);
+ enum ixgbe_media_type (*ixgbe_func_get_media_type)(struct ixgbe_hw *);
+ s32 (*ixgbe_func_get_mac_addr)(struct ixgbe_hw *, u8 *);
+ u32 (*ixgbe_func_get_num_of_tx_queues)(struct ixgbe_hw *);
+ u32 (*ixgbe_func_get_num_of_rx_queues)(struct ixgbe_hw *);
+ s32 (*ixgbe_func_stop_adapter)(struct ixgbe_hw *);
+ s32 (*ixgbe_func_get_bus_info)(struct ixgbe_hw *);
+
+ /* PHY */
+ s32 (*ixgbe_func_identify_phy)(struct ixgbe_hw *);
+ s32 (*ixgbe_func_reset_phy)(struct ixgbe_hw *);
+ s32 (*ixgbe_func_read_phy_reg)(struct ixgbe_hw *, u32, u32, u16 *);
+ s32 (*ixgbe_func_write_phy_reg)(struct ixgbe_hw *, u32, u32, u16);
+ s32 (*ixgbe_func_setup_phy_link)(struct ixgbe_hw *);
+ s32 (*ixgbe_func_setup_phy_link_speed)(struct ixgbe_hw *,
+ u32, bool, bool);
+ s32 (*ixgbe_func_check_phy_link)(struct ixgbe_hw *, u32 *,
+ bool *);
+
+ /* Link */
+ s32 (*ixgbe_func_setup_link)(struct ixgbe_hw *);
+ s32 (*ixgbe_func_setup_link_speed)(struct ixgbe_hw *, u32,
+ bool, bool);
+ s32 (*ixgbe_func_check_link)(struct ixgbe_hw *, u32 *,
+ bool *);
+ s32 (*ixgbe_func_get_link_settings)(struct ixgbe_hw *,
+ u32 *,
+ bool *);
+
+ /* LED */
+ s32 (*ixgbe_func_led_on)(struct ixgbe_hw *, u32);
+ s32 (*ixgbe_func_led_off)(struct ixgbe_hw *, u32);
+ s32 (*ixgbe_func_blink_led_start)(struct ixgbe_hw *, u32);
+ s32 (*ixgbe_func_blink_led_stop)(struct ixgbe_hw *, u32);
+
+ /* EEPROM */
+ s32 (*ixgbe_func_init_eeprom_params)(struct ixgbe_hw *);
+ s32 (*ixgbe_func_read_eeprom)(struct ixgbe_hw *, u16, u16 *);
+ s32 (*ixgbe_func_write_eeprom)(struct ixgbe_hw *, u16, u16);
+ s32 (*ixgbe_func_validate_eeprom_checksum)(struct ixgbe_hw *, u16 *);
+ s32 (*ixgbe_func_update_eeprom_checksum)(struct ixgbe_hw *);
+
+ /* RAR, Multicast, VLAN */
+ s32 (*ixgbe_func_set_rar)(struct ixgbe_hw *, u32, u8 *, u32 , u32);
+ s32 (*ixgbe_func_init_rx_addrs)(struct ixgbe_hw *);
+ u32 (*ixgbe_func_get_num_rx_addrs)(struct ixgbe_hw *);
+ s32 (*ixgbe_func_update_mc_addr_list)(struct ixgbe_hw *, u8 *, u32,
+ u32);
+ s32 (*ixgbe_func_enable_mc)(struct ixgbe_hw *);
+ s32 (*ixgbe_func_disable_mc)(struct ixgbe_hw *);
+ s32 (*ixgbe_func_clear_vfta)(struct ixgbe_hw *);
+ s32 (*ixgbe_func_set_vfta)(struct ixgbe_hw *, u32, u32, bool);
+
+ /* Flow Control */
+ s32 (*ixgbe_func_setup_fc)(struct ixgbe_hw *, s32);
+};
+
+struct ixgbe_mac_info {
+ enum ixgbe_mac_type type;
+ u8 addr[IXGBE_ETH_LENGTH_OF_ADDRESS];
+ u8 perm_addr[IXGBE_ETH_LENGTH_OF_ADDRESS];
+ s32 mc_filter_type;
+ u32 link_attach_type;
+ u32 link_mode_select;
+ bool link_settings_loaded;
+};
+
+struct ixgbe_phy_info {
+ enum ixgbe_phy_type type;
+ u32 addr;
+ u32 id;
+ u32 revision;
+ enum ixgbe_media_type media_type;
+ u32 autoneg_advertised;
+ bool autoneg_wait_to_complete;
+};
+
+struct ixgbe_hw {
+ u8 __iomem *hw_addr;
+ void *back;
+ struct ixgbe_functions func;
+ struct ixgbe_mac_info mac;
+ struct ixgbe_addr_filter_info addr_ctrl;
+ struct ixgbe_fc_info fc;
+ struct ixgbe_phy_info phy;
+ struct ixgbe_eeprom_info eeprom;
+ struct ixgbe_bus_info bus;
+ u16 device_id;
+ u16 vendor_id;
+ u16 subsystem_device_id;
+ u16 subsystem_vendor_id;
+ u8 revision_id;
+ bool adapter_stopped;
+};
+
+#define ixgbe_func_from_hw_struct(hw, _func) hw->func._func
+
+#define ixgbe_call_func(hw, func, params, error) \
+ (ixgbe_func_from_hw_struct(hw, func) != NULL) ? \
+ ixgbe_func_from_hw_struct(hw, func) params: error
+
+/* Error Codes */
+#define IXGBE_SUCCESS 0
+#define IXGBE_ERR_EEPROM -1
+#define IXGBE_ERR_EEPROM_CHECKSUM -2
+#define IXGBE_ERR_PHY -3
+#define IXGBE_ERR_CONFIG -4
+#define IXGBE_ERR_PARAM -5
+#define IXGBE_ERR_MAC_TYPE -6
+#define IXGBE_ERR_UNKNOWN_PHY -7
+#define IXGBE_ERR_LINK_SETUP -8
+#define IXGBE_ERR_ADAPTER_STOPPED -9
+#define IXGBE_ERR_INVALID_MAC_ADDR -10
+#define IXGBE_ERR_DEVICE_NOT_SUPPORTED -11
+#define IXGBE_ERR_MASTER_REQUESTS_PENDING -12
+#define IXGBE_ERR_INVALID_LINK_SETTINGS -13
+#define IXGBE_ERR_AUTONEG_NOT_COMPLETE -14
+#define IXGBE_ERR_RESET_FAILED -15
+#define IXGBE_ERR_SWFW_SYNC -16
+#define IXGBE_ERR_PHY_ADDR_INVALID -17
+#define IXGBE_NOT_IMPLEMENTED 0x7FFFFFFF
+
+#endif /* _IXGBE_TYPE_H_ */
--
1.5.0.6
^ permalink raw reply related
* Re: [PATCH] ixgbe: New driver for Pci-Express 10GbE 82598 support
From: Kok, Auke @ 2007-08-04 4:35 UTC (permalink / raw)
To: jeff, netdev
Cc: Auke Kok, ayyappan.veeraiyan, akpm, arjan, hch, shemminger,
nhorman, inaky, mb, john.ronciak
In-Reply-To: <20070804040502.D9133F2C0F@doppio.foo-projects.org>
Auke Kok wrote:
> This patch adds support for the Intel 82598 PCI-Express 10GbE
> chipset. Devices will be available on the market soon.
Also available through http and git:
http://foo-projects.org/~sofar/ixgbe-20070803-submission.patch
http://foo-projects.org/~sofar/ixgbe-20070803-submission.patch.bz2
git://lost.foo-projects.org/~ahkok/linux-2.6#ixgbe-20070803-submission
Cheers,
Auke
^ permalink raw reply
* Re: [RFC 0/2][BNX2]: Add iSCSI support to BNX2 devices.
From: Jeff Garzik @ 2007-08-04 8:47 UTC (permalink / raw)
To: Michael Chan
Cc: davem, mchristi, netdev, open-iscsi, anilgv, talm, lusinsky, uri
In-Reply-To: <1186200229.18322.97.camel@dell>
Michael Chan wrote:
> [BNX2]: Add iSCSI support to BNX2 devices.
>
> Modify bnx2 and add a cnic driver to support some offload functions
> needed by iSCSI.
>
> Add a new open-iscsi driver to support iSCSI offload on bnx2 devices.
>
> Signed-off-by: Anil Veerabhadrappa <anilgv@broadcom.com>
> Signed-off-by: Michael Chan <mchan@broadcom.com>
>
> --
>
> The complete patch is in:
>
> ftp://Net_sys_anon@ftp1.broadcom.com/0001-BNX2-Add-iSCSI-support-to-BNX2-devices.patch
>
> I broke this into 2 patches and omitted the firmware blob in the next 2
> emails for review.
patch #2/2 did not make it (to me personally nor to
http://marc.info/?l=linux-netdev)
Jeff
^ permalink raw reply
* Re: [PATCH 00/23] per device dirty throttling -v8
From: Ray Lee @ 2007-08-04 16:01 UTC (permalink / raw)
To: david@lang.hm
Cc: Ingo Molnar, Linus Torvalds, Peter Zijlstra, linux-mm,
linux-kernel, miklos, akpm, neilb, dgc, tomoki.sekiyama.qu,
nikita, trond.myklebust, yingchao.zhou, richard, netdev
In-Reply-To: <Pine.LNX.4.64.0708040032570.6905@asgard.lang.hm>
(adding netdev cc:)
On 8/4/07, david@lang.hm <david@lang.hm> wrote:
> On Sat, 4 Aug 2007, Ingo Molnar wrote:
>
> > * Ingo Molnar <mingo@elte.hu> wrote:
> >
> >> There are positive reports in the never-ending "my system crawls like
> >> an XT when copying large files" bugzilla entry:
> >>
> >> http://bugzilla.kernel.org/show_bug.cgi?id=7372
> >
> > i forgot this entry:
> >
> > " We recently upgraded our office to gigabit Ethernet and got some big
> > AMD64 / 3ware boxes for file and vmware servers... only to find them
> > almost useless under any kind of real load. I've built some patched
> > 2.6.21.6 kernels (using the bdi throttling patch you mentioned) to
> > see if our various Debian Etch boxes run better. So far my testing
> > shows a *great* improvement over the stock Debian 2.6.18 kernel on
> > our configurations. "
> >
> > and bdi has been in -mm in the past i think, so we also know (to a
> > certain degree) that it does not hurt those workloads that are fine
> > either.
> >
> > [ my personal interest in this is the following regression: every time i
> > start a large kernel build with DEBUG_INFO on a quad-core 4GB RAM box,
> > i get up to 30 seconds complete pauses in Vim (and most other tasks),
> > during plain editing of the source code. (which happens when Vim tries
> > to write() to its swap/undo-file.) ]
>
> I have an issue that sounds like it's related.
>
> I've got a syslog server that's got two Opteron 246 cpu's, 16G ram, 2x140G
> 15k rpm drives (fusion MPT hardware mirroring), 16x500G 7200rpm SATA
> drives on 3ware 9500 cards (software raid6) running 2.6.20.3 with hz set
> at default and preempt turned off.
>
> I have syslog doing buffered writes to the SCSI drives and every 5 min a
> cron job copies the data to the raid array.
>
> I've found that if I do anything significant on the large raid array that
> the system looses a significant amount of the UDP syslog traffic, even
> though there should be pleanty of ram and cpu (and the spindles involved
> in the writes are not being touched), even a grep can cause up to 40%
> losses in the syslog traffic. I've experimented with nice levels (nicing
> down the grep and nicing up the syslogd) without a noticable effect on the
> losses.
>
> I've been planning to try a new kernel with hz=1000 to see if that would
> help, and after that experiment with the various preempt settings, but it
> sounds like the per-device queues may actually be more relavent to the
> problem.
>
> what would you suggest I test, and in what order and combination?
At least on a surface level, your report has some similarities to
http://lkml.org/lkml/2007/5/21/84 . In that message, John Miller
mentions several things he tried without effect:
< - I increased the max allowed receive buffer through
< proc/sys/net/core/rmem_max and the application calls the right
< syscall. "netstat -su" does not show any "packet receive errors".
<
< - After getting "kernel: swapper: page allocation failure.
< order:0, mode:0x20", I increased /proc/sys/vm/min_free_kbytes
<
< - ixgb.txt in kernel network documentation suggests to increase
< net.core.netdev_max_backlog to 300000. This did not help.
<
< - I also had to increase net.core.optmem_max, because the default
< value was too small for 700 multicast groups.
As they're all pretty simple to test, it may be worthwhile to give
them a shot just to rule things out.
Ray
^ permalink raw reply
* Re: strange tcp behavior
From: Evgeniy Polyakov @ 2007-08-04 16:03 UTC (permalink / raw)
To: Simon Arlott; +Cc: john, netdev, David Miller
In-Reply-To: <46B37426.20500@simon.arlott.org.uk>
On Fri, Aug 03, 2007 at 07:29:58PM +0100, Simon Arlott (simon@fire.lp0.eu) wrote:
> On 03/08/07 18:39, Evgeniy Polyakov wrote:
> > On Fri, Aug 03, 2007 at 05:51:42PM +0100, Simon Arlott (simon@fire.lp0.eu) wrote:
> >
> >> 17:38:03.533589 IP 192.168.7.4.50550 > 192.168.7.8.2500: R 82517592:82517592(0) win 1500 (raw)
> >> vs
> >> 17:37:38.383085 IP 192.168.7.8.2500 > 192.168.7.4.50550: R 4259643274:4259643274(0) ack 1171836829 win 14360
> >> What happened there ?
>
> Erm... you seem to have removed parts of my message in a way that doesn't
> make sense...
Sorry, I left line I tought were enough to understand your point.
> On Fri, Aug 03, 2007 at 05:51:42PM +0100, Simon Arlott wrote:
> > 17:38:04.536277 IP 192.168.7.8.2500 > 192.168.7.4.50550: R 1:1(0) ack 17 win 14360
> > vs
> > 17:37:38.383085 IP 192.168.7.8.2500 > 192.168.7.4.50550: R 4259643274:4259643274(0) ack 1171836829 win 14360
> > What happened there ?
>
> The first one is the RST sent when the connection is close()d without
> reading, and the second one is the same RST but after other connection
> has been made on the same ports using a different socket.
I understood it, and your question is about possibility for those
numbers to be roughly the same. Answer is 'no', it is not possible
(possible, but with extremely low probability).
If it is - this is a bug in ISN generation algo and must be fixed.
> > It is the same situation, which would happen if you will spam remote
> > side with RST packets with arbitrary sequence number in hope that it
> > will reset some connection.
>
> Isn't it still possible that the connection that got reset is left open
> (possibly for days) until another connection using the same ports is
> using roughly the same sequence numbers?
Of course it is possible, but it very unlikely. Practically it is
impossible in modern OSes - ISN generation algos are designed to prevent
this from happening.
> --
> Simon Arlott
--
Evgeniy Polyakov
^ permalink raw reply
* Re: Linksys Gigabit USB2.0 adapter (asix) regression
From: Erik Slagter @ 2007-08-04 16:24 UTC (permalink / raw)
To: David Hollis; +Cc: netdev
In-Reply-To: <1185820074.8086.4.camel@dhollis-lnx.sunera.com>
[-- Attachment #1.1: Type: text/plain, Size: 1103 bytes --]
David Hollis wrote:
>> They are either garbled are they are not passed on the wire. The
>> transmitted packets are shown by tshark, but a tshark run on "the other
>> end of the line" does not show them.
>>
>> Platform is indeed x86, to be precise: fedora 7, kernel 2.6.22-rc6, cpu
>> pentium M, dell laptop inspiron 9300, ICH6.
>>
>> If you want me to test something please yell, it's no trouble at all to
>> change a few lines in the driver's source and recompile the module.
>>
> Could you send me a complete dmesg dump when the driver is compiled with
> DEBUG enabled (at least from then usb logs that the device was inserted
> to the end). I'll need to see what it reports the values of the
> registers.
Please see attachment.
> Have you tried using the F7 2.6.22 kernel? I know that has worked fine
> for me on my system.
I tried vanilla 2.6.22 and now 2.6.23-rc1
>> Please note I cannot send mail to you: "(conversation with
>> dhollis.dyndns.org[71.251.104.159] timed out while sending MAIL FROM)"
>
> I've fixed that issue so my mail delivery isn't sporadic.
I still got it last week.
[-- Attachment #1.2: dmesg.txt --]
[-- Type: text/plain, Size: 13801 bytes --]
usb 1-8: new high speed USB device using ehci_hcd and address 11
usb 1-8: configuration #1 chosen from 1 choice
usb%d: asix_read_cmd() cmd=0x1e value=0x0000 index=0x0000 size=1
drivers/net/usb/asix.c: GPIO Status: 0x0002
usb%d: asix_write_cmd() cmd=0x0d value=0x0000 index=0x0000 size=0
usb%d: asix_read_cmd() cmd=0x0b value=0x0017 index=0x0000 size=2
usb%d: asix_write_cmd() cmd=0x0e value=0x0000 index=0x0000 size=0
drivers/net/usb/asix.c: EEPROM index 0x17 is 0x0580
drivers/net/usb/asix.c: GPIO0: 0, PhyMode: 0
usb%d: asix_write_gpio() - value = 0x008c
usb%d: asix_write_cmd() cmd=0x1f value=0x008c index=0x0000 size=0
usb%d: asix_write_gpio() - value = 0x003c
usb%d: asix_write_cmd() cmd=0x1f value=0x003c index=0x0000 size=0
usb%d: asix_write_gpio() - value = 0x001c
usb%d: asix_write_cmd() cmd=0x1f value=0x001c index=0x0000 size=0
usb%d: asix_write_gpio() - value = 0x003c
usb%d: asix_write_cmd() cmd=0x1f value=0x003c index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x20 value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x20 value=0x0048 index=0x0000 size=0
usb%d: asix_write_rx_ctl() - mode = 0x0000
usb%d: asix_write_cmd() cmd=0x10 value=0x0000 index=0x0000 size=0
usb%d: asix_read_cmd() cmd=0x13 value=0x0000 index=0x0000 size=6
usb%d: asix_get_phy_addr()
usb%d: asix_read_cmd() cmd=0x19 value=0x0000 index=0x0000 size=2
usb%d: asix_get_phy_addr() returning 0x18e0
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0002 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_mdio_read() phy_id=0x18, loc=0x02, returns=0x0141
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0003 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_mdio_read() phy_id=0x18, loc=0x03, returns=0x0cc2
drivers/net/usb/asix.c: PHYID=0x01410cc2
usb%d: marvell_phy_init()
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_read_cmd() cmd=0x07 value=0x0018 index=0x001b size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_mdio_read() phy_id=0x18, loc=0x1b, returns=0x848f
usb%d: MII_MARVELL_STATUS = 0x848f
usb%d: asix_mdio_write() phy_id=0x18, loc=0x14, val=0x0082
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x08 value=0x0018 index=0x0014 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0018 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_mdio_read() phy_id=0x18, loc=0x18, returns=0x4100
usb%d: MII_MARVELL_LED_CTRL (1) = 0x4100
usb%d: asix_mdio_write() phy_id=0x18, loc=0x18, val=0x4101
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x08 value=0x0018 index=0x0018 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0018 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_mdio_read() phy_id=0x18, loc=0x18, returns=0x4101
usb%d: MII_MARVELL_LED_CTRL (2) = 0x4101
usb%d: asix_mdio_write() phy_id=0x18, loc=0x00, val=0x9000
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x08 value=0x0018 index=0x0000 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_mdio_write() phy_id=0x18, loc=0x04, val=0x05e1
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x08 value=0x0018 index=0x0004 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_mdio_write() phy_id=0x18, loc=0x09, val=0x0200
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x08 value=0x0018 index=0x0009 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0000 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_mdio_read() phy_id=0x18, loc=0x00, returns=0x1000
usb%d: asix_mdio_write() phy_id=0x18, loc=0x00, val=0x1200
usb%d: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
usb%d: asix_write_cmd() cmd=0x08 value=0x0018 index=0x0000 size=2
usb%d: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
usb%d: asix_write_medium_mode() - mode = 0x0376
usb%d: asix_write_cmd() cmd=0x1b value=0x0376 index=0x0000 size=0
usb%d: asix_write_rx_ctl() - mode = 0x0088
usb%d: asix_write_cmd() cmd=0x10 value=0x0088 index=0x0000 size=0
eth2: register 'asix' at usb-0000:00:1d.7-8, ASIX AX88178 USB 2.0 Ethernet, 00:12:17:f2:1a:17
usbcore: registered new interface driver asix
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0001 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x01, returns=0x7949
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0001 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x01, returns=0x7949
eth2: ax88178_link_reset()
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0001 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x01, returns=0x7949
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0001 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x01, returns=0x7949
eth2: link down
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0004 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x04, returns=0x05e1
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0009 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x09, returns=0x0200
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0000 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x00, returns=0x1000
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0005 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x05, returns=0x0000
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0009 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x09, returns=0x0200
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x000a size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x0a, returns=0x0000
eth2: ax88178_link_reset() speed: 10 duplex: 0 setting mode to 0x0174
eth2: asix_write_medium_mode() - mode = 0x0174
eth2: asix_write_cmd() cmd=0x1b value=0x0174 index=0x0000 size=0
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0019 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x19, returns=0x0000
eth2: marvell_led_status() read 0x0000
eth2: marvell_led_status() writing 0x02f0
eth2: asix_mdio_write() phy_id=0x18, loc=0x19, val=0x02f0
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_write_cmd() cmd=0x08 value=0x0018 index=0x0019 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x10 value=0x0088 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
r8169: eth1: link down
eth2: Link Status is: 1
eth2: ax88178_link_reset()
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0001 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x01, returns=0x796d
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0001 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x01, returns=0x796d
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0004 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x04, returns=0x05e1
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0005 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x05, returns=0xc5e1
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x000a size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x0a, returns=0x3800
eth2: link up, 1000Mbps, full-duplex, lpa 0xC5E1
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0004 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x04, returns=0x05e1
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0009 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x09, returns=0x0200
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0000 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x00, returns=0x1000
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0005 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x05, returns=0xc5e1
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0009 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x09, returns=0x0200
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x000a size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x0a, returns=0x3800
eth2: ax88178_link_reset() speed: 1000 duplex: 1 setting mode to 0x037f
eth2: asix_write_medium_mode() - mode = 0x037f
eth2: asix_write_cmd() cmd=0x1b value=0x037f index=0x0000 size=0
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_read_cmd() cmd=0x07 value=0x0018 index=0x0019 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
eth2: asix_mdio_read() phy_id=0x18, loc=0x19, returns=0x02f0
eth2: marvell_led_status() read 0x02f0
eth2: marvell_led_status() writing 0x03e0
eth2: asix_mdio_write() phy_id=0x18, loc=0x19, val=0x03e0
eth2: asix_write_cmd() cmd=0x06 value=0x0000 index=0x0000 size=0
eth2: asix_write_cmd() cmd=0x08 value=0x0018 index=0x0019 size=2
eth2: asix_write_cmd() cmd=0x0a value=0x0000 index=0x0000 size=0
device eth2 entered promiscuous mode
eth2: asix_write_cmd_async() cmd=0x10 value=0x0089 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x10 value=0x0089 index=0x0000 size=0
device eth2 left promiscuous mode
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
device eth2 entered promiscuous mode
eth2: asix_write_cmd_async() cmd=0x10 value=0x0089 index=0x0000 size=0
device eth2 left promiscuous mode
eth2: asix_write_cmd_async() cmd=0x16 value=0x0000 index=0x0000 size=8
eth2: asix_write_cmd_async() cmd=0x10 value=0x0098 index=0x0000 size=0
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/x-pkcs7-signature, Size: 3315 bytes --]
^ permalink raw reply
* Re: Distributed storage.
From: Evgeniy Polyakov @ 2007-08-04 16:37 UTC (permalink / raw)
To: Daniel Phillips; +Cc: netdev, linux-kernel, linux-fsdevel, Peter Zijlstra
In-Reply-To: <200708031819.17039.phillips@phunq.net>
On Fri, Aug 03, 2007 at 06:19:16PM -0700, Daniel Phillips (phillips@phunq.net) wrote:
> It depends on the characteristics of the physical and virtual block
> devices involved. Slow block devices can produce surprising effects.
> Ddsnap still qualifies as "slow" under certain circumstances (big
> linear write immediately following a new snapshot). Before we added
> throttling we would see as many as 800,000 bios in flight. Nice to
Mmm, sounds tasty to work with such a system :)
> know the system can actually survive this... mostly. But memory
> deadlock is a clear and present danger under those conditions and we
> did hit it (not to mention that read latency sucked beyond belief).
>
> Anyway, we added a simple counting semaphore to throttle the bio traffic
> to a reasonable number and behavior became much nicer, but most
> importantly, this satisfies one of the primary requirements for
> avoiding block device memory deadlock: a strictly bounded amount of bio
> traffic in flight. In fact, we allow some bounded number of
> non-memalloc bios *plus* however much traffic the mm wants to throw at
> us in memalloc mode, on the assumption that the mm knows what it is
> doing and imposes its own bound of in flight bios per device. This
> needs auditing obviously, but the mm either does that or is buggy. In
> practice, with this throttling in place we never saw more than 2,000 in
> flight no matter how hard we hit it, which is about the number we were
> aiming at. Since we draw our reserve from the main memalloc pool, we
> can easily handle 2,000 bios in flight, even under extreme conditions.
>
> See:
> http://zumastor.googlecode.com/svn/trunk/ddsnap/kernel/dm-ddsnap.c
> down(&info->throttle_sem);
>
> To be sure, I am not very proud of this throttling mechanism for various
> reasons, but the thing is, _any_ throttling mechanism no matter how
> sucky solves the deadlock problem. Over time I want to move the
make_request_fn is always called in process context, we can wait in it
for memory in mempool. Although that means we already in trouble.
I agree, any kind of high-boundary leveling must be implemented in
device itself, since block layer does not know what device is at the end
and what it will need to process given block request.
--
Evgeniy Polyakov
^ permalink raw reply
* Re: Distributed storage.
From: Evgeniy Polyakov @ 2007-08-04 16:44 UTC (permalink / raw)
To: Daniel Phillips; +Cc: netdev, linux-kernel, linux-fsdevel
In-Reply-To: <200708031741.18404.phillips@phunq.net>
Hi Daniel.
> On Tuesday 31 July 2007 10:13, Evgeniy Polyakov wrote:
> > * storage can be formed on top of remote nodes and be exported
> > simultaneously (iSCSI is peer-to-peer only, NBD requires device
> > mapper and is synchronous)
>
> In fact, NBD has nothing to do with device mapper. I use it as a
> physical target underneath ddraid (a device mapper plugin) just like I
> would use your DST if it proves out.
I meant to create a storage on top of several nodes one needs to have
device mapper or something like that on top of NBD itself. To further
export resulted device one needs another userspace NDB application and
so on. DST simplifies that greatly.
DST original code worked as device mapper plugin too, but its two
additional allocations (io and clone) per block request ended up for me
as a show stopper.
--
Evgeniy Polyakov
^ permalink raw reply
* Re: strange tcp behavior
From: Evgeniy Polyakov @ 2007-08-04 16:49 UTC (permalink / raw)
To: David Miller; +Cc: simon, john, netdev
In-Reply-To: <20070803.130451.63127177.davem@davemloft.net>
On Fri, Aug 03, 2007 at 01:04:51PM -0700, David Miller (davem@davemloft.net) wrote:
> From: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
> Date: Fri, 3 Aug 2007 12:22:42 +0400
>
> > On Thu, Aug 02, 2007 at 07:21:34PM -0700, David Miller (davem@davemloft.net) wrote:
> > > What in the world are we doing allowing stream sockets to autobind?
> > > That is totally bogus. Even if we autobind, that won't make a connect
> > > happen.
> >
> > For accepted socket it is perfectly valid assumption - we could autobind
> > it during the first send. Or may bind it during accept. Its a matter of
> > taste I think. Autobinding during first sending can end up being a
> > protection against DoS in some obscure rare case...
>
> accept()ed socket is by definition fully bound and already in
> established state.
That what I meant - it binds during accept (well it can not be called
real binding), but could be autobound during first send to needed port.
Maybe that was one of intentions, don't know.
--
Evgeniy Polyakov
^ permalink raw reply
* Re: strange tcp behavior
From: Evgeniy Polyakov @ 2007-08-04 16:51 UTC (permalink / raw)
To: David Miller; +Cc: simon, john, netdev
In-Reply-To: <20070803.141717.36925406.davem@davemloft.net>
On Fri, Aug 03, 2007 at 02:17:17PM -0700, David Miller (davem@davemloft.net) wrote:
> From: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
> Date: Fri, 3 Aug 2007 12:22:42 +0400
>
> > Maybe recvmsg should be changed too for symmetry?
>
> I took a look at this, and it's not %100 trivial.
>
> Let's do this later, and only sendmsg for now in order to
> fix the bug in the stable branches.
I've tested your patch, besides there was an offset in one of hooks,
it works perfectly ok.
Feel free to add my ack, tested-by or whatever is needed for this :)
Your patch fixes the problem.
Actually inet_sendmsg() can be renamed to something less misleading,
since it is not used by TCP now.
--
Evgeniy Polyakov
^ permalink raw reply
* Re: [RFC 0/2][BNX2]: Add iSCSI support to BNX2 devices.
From: Michael Chan @ 2007-08-04 17:02 UTC (permalink / raw)
To: Jeff Garzik
Cc: davem, mchristi, netdev, open-iscsi, Anil Veerabhadrappa,
Tal Moyal, Robert Lusinsky, Uri Elzur
In-Reply-To: <46B43D2D.2090800@garzik.org>
Jeff Garzik wrote:
> Michael Chan wrote:
> > [BNX2]: Add iSCSI support to BNX2 devices.
> >
> > Modify bnx2 and add a cnic driver to support some offload functions
> > needed by iSCSI.
> >
> > Add a new open-iscsi driver to support iSCSI offload on
> bnx2 devices.
> >
> > Signed-off-by: Anil Veerabhadrappa <anilgv@broadcom.com>
> > Signed-off-by: Michael Chan <mchan@broadcom.com>
> >
> > --
> >
> > The complete patch is in:
> >
> >
> >
ftp://Net_sys_anon@ftp1.broadcom.com/0001-BNX2-Add-iSCSI-support-to-BNX2
-devices.patch
>
> > I broke this into 2 patches and omitted the firmware blob in the
next 2
> > emails for review.
> patch #2/2 did not make it (to me personally nor to
> http://marc.info/?l=linux-netdev)
Probably too big. The complete patch is available from FTP above.
I'll try to break it up some more and resend later.
^ permalink raw reply
* Re: Distributed storage.
From: Evgeniy Polyakov @ 2007-08-04 17:03 UTC (permalink / raw)
To: Manu Abraham; +Cc: netdev, linux-kernel, linux-fsdevel
In-Reply-To: <1a297b360708022204u4fc7603pb6baebe2bdf28618@mail.gmail.com>
On Fri, Aug 03, 2007 at 09:04:51AM +0400, Manu Abraham (abraham.manu@gmail.com) wrote:
> On 7/31/07, Evgeniy Polyakov <johnpol@2ka.mipt.ru> wrote:
>
> > TODO list currently includes following main items:
> > * redundancy algorithm (drop me a request of your own, but it is highly
> > unlikley that Reed-Solomon based will ever be used - it is too slow
> > for distributed RAID, I consider WEAVER codes)
>
>
> LDPC codes[1][2] have been replacing Turbo code[3] with regards to
> communication links and we have been seeing that transition. (maybe
> helpful, came to mind seeing the mention of Turbo code) Don't know how
> weaver compares to LDPC, though found some comparisons [4][5] But
> looking at fault tolerance figures, i guess Weaver is much better.
>
> [1] http://www.ldpc-codes.com/
> [2] http://portal.acm.org/citation.cfm?id=1240497
> [3] http://en.wikipedia.org/wiki/Turbo_code
> [4] http://domino.research.ibm.com/library/cyberdig.nsf/papers/BD559022A190D41C85257212006CEC11/$File/rj10391.pdf
> [5] http://hplabs.hp.com/personal/Jay_Wylie/publications/wylie_dsn2007.pdf
LDPC codes require to solve N order matrix over finite field - exactly
the reason I do not want to use Reed-Solomon codes even with optimized
non-Vandermonde matrix. I will investigate LDPC further though.
Turbo codes are like flow cipher compared to RS codes being block
ciphers. Transport media is reliable in data storages, otherwise they
would not even exist.
--
Evgeniy Polyakov
^ permalink raw reply
* Re: source interface ping bug ?
From: nano bug @ 2007-08-04 17:16 UTC (permalink / raw)
To: netdev
In-Reply-To: <d39c36500708021301i6754cb6bh4ba1a2a85092ce3c@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2165 bytes --]
Hello, any news about this ?
On 8/2/07, nano bug <linnewbye@gmail.com> wrote:
> ---------- Forwarded message ----------
> From: nano bug <linnewbye@gmail.com>
> Date: Aug 2, 2007 10:56 PM
> Subject: Re: source interface ping bug ?
> To: Ben Greear <greearb@candelatech.com>
>
>
> Hello,
>
> Sorry for the late reply, I have atached the strace output of eth0 and
> eth2 on kernel 2.6.20 and 2.6.22
>
> On 7/30/07, Ben Greear <greearb@candelatech.com> wrote:
> > nano bug wrote:
> > > Can someone have a look a this and tell if it's kernel related or if I
> > > posted this in the wrong place ? Thanks.
> > >
> > Last I checked, ping did not do an SO_BINDTODEVICE even if you did -i ethX.
> > I think it just looked up the IP for that port and treated it as -i a.b.c.d.
> >
> > That said, I'm not sure why the behaviour changes for you between kernel
> > releases.
> >
> > Maybe an 'strace' of your ping command on the different kernels would help
> > figure out what the problem is?
> >
> > Ben
> >
> > --
> > Ben Greear <greearb@candelatech.com>
> > Candela Technologies Inc http://www.candelatech.com
On 8/2/07, nano bug <linnewbye@gmail.com> wrote:
> ---------- Forwarded message ----------
> From: nano bug <linnewbye@gmail.com>
> Date: Aug 2, 2007 10:58 PM
> Subject: Re: source interface ping bug ?
> To: Patrick McHardy <kaber@trash.net>
>
>
> Hello,
>
> Yes I'm running NAT, I have atached the output of the iptables -t nat
> -vxnL command and the routing tables
>
> On 7/30/07, Patrick McHardy <kaber@trash.net> wrote:
> > nano bug wrote:
> > > [...]
> > > using source interface :
> > >
> > > root@darkstar:~/iputils# ./ping -I eth2 87.248.113.14
> > > PING 87.248.113.14 (87.248.113.14) from 86.106.19.75 eth2: 56(84) bytes of data.
> > >>From 86.106.19.75 icmp_seq=1 Destination Host Unreachable
> >
> > > root@darkstar:~# tcpdump -i eth2 -vvv -n host 87.248.113.14 and host
> > > 86.106.19.75
> > > tcpdump: listening on eth2, link-type EN10MB (Ethernet), capture size 96 bytes
> > > 01:19:24.292911 arp who-has 87.248.113.14 tell 86.106.19.75
> >
> >
> > Are you using (or running) NAT locally? What do your routing tables look
> > like?
> >
>
>
[-- Attachment #2: route_tables --]
[-- Type: application/octet-stream, Size: 2032 bytes --]
root@darkstar:~#
root@darkstar:~#
root@darkstar:~# ip route show
80.97.71.0/24 dev eth0 proto kernel scope link src 80.97.71.23
10.10.10.0/24 dev eth1 proto kernel scope link src 10.10.10.1
86.106.18.0/23 dev eth2 scope link
127.0.0.0/8 dev lo scope link
root@darkstar:~# ip rule show
0: from all lookup local
50: from all lookup main
100: from 80.97.71.0/24 lookup 201
101: from 86.106.18.0/23 lookup 202
102: from all lookup 222
32766: from all lookup main
32767: from all lookup default
root@darkstar:~# ip route show table 201
default via 80.97.71.1 dev eth0 proto static
prohibit default proto static metric 1
root@darkstar:~# ip route show table 202
default via 86.106.18.1 dev eth2 proto static
prohibit default proto static metric 1
root@darkstar:~# ip route show table 222
default proto static
nexthop via 80.97.71.1 dev eth0 weight 1
nexthop via 86.106.18.1 dev eth2 weight 99
prohibit default proto static metric 1
root@darkstar:~# iptables -V
iptables v1.3.8
root@darkstar:~# iptables -t nat -xvnL
Chain PREROUTING (policy ACCEPT 2120 packets, 298134 bytes)
pkts bytes target prot opt in out source destination
1 48 DNAT tcp -- eth2 * 0.0.0.0/0 0.0.0.0/0 tcp dpts:18856:18870 to:172.16.2.1
0 0 DNAT udp -- eth2 * 0.0.0.0/0 0.0.0.0/0 udp dpts:18856:18870 to:172.16.2.1
Chain POSTROUTING (policy ACCEPT 56 packets, 4568 bytes)
pkts bytes target prot opt in out source destination
2 138 MASQUERADE all -- * * 10.10.10.0/24 0.0.0.0/0
0 0 MASQUERADE all -- * * 172.16.1.0/24 0.0.0.0/0
0 0 MASQUERADE all -- * * 172.16.2.0/24 0.0.0.0/0
Chain OUTPUT (policy ACCEPT 55 packets, 4520 bytes)
pkts bytes target prot opt in out source destination
root@darkstar:~#
[-- Attachment #3: strace_eth0_2.6.20 --]
[-- Type: application/octet-stream, Size: 13375 bytes --]
execve("./ping", ["./ping", "-I", "eth0", "87.248.113.14", "-c", "10"], [/* 28 vars */]) = 0
brk(0) = 0x8063000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=34724, ...}) = 0
mmap2(NULL, 34724, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7f2f000
close(3) = 0
open("/lib/libresolv.so.2", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0`!\0\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=77439, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f2e000
mmap2(NULL, 75976, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7f1b000
mmap2(0xb7f2a000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf) = 0xb7f2a000
mmap2(0xb7f2c000, 6344, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7f2c000
close(3) = 0
open("/lib/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@_\1\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1528742, ...}) = 0
mmap2(NULL, 1316260, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7dd9000
mmap2(0xb7f15000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x13c) = 0xb7f15000
mmap2(0xb7f18000, 9636, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7f18000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7dd8000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7dd86c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb7f15000, 4096, PROT_READ) = 0
munmap(0xb7f2f000, 34724) = 0
socket(PF_INET, SOCK_RAW, IPPROTO_ICMP) = 3
getuid32() = 0
setuid32(0) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
setsockopt(4, SOL_SOCKET, SO_BINDTODEVICE, "eth0\0", 5) = 0
connect(4, {sa_family=AF_INET, sin_port=htons(1025), sin_addr=inet_addr("87.248.113.14")}, 16) = 0
getsockname(4, {sa_family=AF_INET, sin_port=htons(32778), sin_addr=inet_addr("80.97.71.23")}, [16]) = 0
close(4) = 0
ioctl(3, SIOCGIFINDEX, {ifr_name="eth0", ifr_index=1}) = 0
setsockopt(3, SOL_RAW, ICMP_FILTER, ~(ICMP_ECHOREPLY|ICMP_DEST_UNREACH|ICMP_SOURCE_QUENCH|ICMP_REDIRECT|ICMP_TIME_EXCEEDED|ICMP_PARAMETERPROB), 4) = 0
setsockopt(3, SOL_IP, IP_RECVERR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_SNDBUF, [324], 4) = 0
setsockopt(3, SOL_SOCKET, SO_RCVBUF, [65536], 4) = 0
getsockopt(3, SOL_SOCKET, SO_RCVBUF, [131072], [4]) = 0
brk(0) = 0x8063000
brk(0x8084000) = 0x8084000
fstat64(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f37000
setsockopt(3, SOL_SOCKET, SO_TIMESTAMP, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_SNDTIMEO, "\1\0\0\0\0\0\0\0", 8) = 0
setsockopt(3, SOL_SOCKET, SO_RCVTIMEO, "\1\0\0\0\0\0\0\0", 8) = 0
getpid() = 2170
rt_sigaction(SIGINT, {0x804b220, [], SA_INTERRUPT}, NULL, 8) = 0
rt_sigaction(SIGALRM, {0x804b220, [], SA_INTERRUPT}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {0x804b230, [], SA_INTERRUPT}, NULL, 8) = 0
gettimeofday({1186083994, 673594}, NULL) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbfe0bb88) = -1 EINVAL (Invalid argument)
gettimeofday({1186083994, 673892}, NULL) = 0
gettimeofday({1186083994, 674001}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0k/z\10\0\1\2324\262F\321H\n\0\10\t\n\v\f\r\16\17\20"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\341\"\0\0006\1C\10W\370q\16PaG\27\0\0s/z\10\0\1"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "PING 87.248.113.14 (87.248.113.1"..., 140PING 87.248.113.14 (87.248.113.14) from 80.97.71.23 eth0: 56(84) bytes of data.
64 bytes from 87.248.113.14: icmp_seq=1 ttl=54 time=63.1 ms
) = 140
gettimeofday({1186083994, 740380}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 934) = 0
gettimeofday({1186083995, 672555}, NULL) = 0
sched_yield() = 0
recvmsg(3, 0xbfe0bdb8, MSG_DONTWAIT) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083995, 675887}, NULL) = 0
gettimeofday({1186083995, 676770}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\231#z\10\0\2\2334\262F\242S\n\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\353\32\0\0006\0019\20W\370q\16PaG\27\0\0\241#z\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=2 ttl=54 time=67.1 ms
) = 60
gettimeofday({1186083995, 746675}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 930) = 0
gettimeofday({1186083996, 671678}, NULL) = 0
sched_yield() = 0
recvmsg(3, 0xbfe0bdb8, MSG_DONTWAIT) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083996, 675088}, NULL) = 0
gettimeofday({1186083996, 676068}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0V%z\10\0\3\2344\262F\344P\n\0\10\t\n\v\f\r\16\17\20"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\372\341\0\0006\1)IW\370q\16PaG\27\0\0^%z\10\0\3"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=3 ttl=54 time=70.6 ms
) = 60
gettimeofday({1186083996, 749536}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 926) = 0
gettimeofday({1186083997, 671550}, NULL) = 0
sched_yield() = 0
recvmsg(3, 0xbfe0bdb8, MSG_DONTWAIT) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083997, 674843}, NULL) = 0
gettimeofday({1186083997, 675844}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0005%z\10\0\4\2354\262F\4P\n\0\10\t\n\v\f\r\16\17\20"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\6\340\0\0006\1\35KW\370q\16PaG\27\0\0=%z\10\0\4"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=4 ttl=54 time=63.6 ms
) = 60
gettimeofday({1186083997, 742365}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 933) = 0
gettimeofday({1186083998, 680849}, NULL) = 0
gettimeofday({1186083998, 681944}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0`\fz\10\0\5\2364\262F\330g\n\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\20\366\0\0006\1\0235W\370q\16PaG\27\0\0h\fz\10\0"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=5 ttl=54 time=65.4 ms
) = 60
gettimeofday({1186083998, 750410}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 931) = 0
gettimeofday({1186083999, 690220}, NULL) = 0
gettimeofday({1186083999, 691307}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\313\346z\10\0\6\2374\262Fk\214\n\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\33\5\0\0006\1\t&W\370q\16PaG\27\0\0\323\346z\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=6 ttl=54 time=61.6 ms
) = 60
gettimeofday({1186083999, 755718}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 935) = 0
gettimeofday({1186084000, 689662}, NULL) = 0
gettimeofday({1186084000, 690769}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\344\347z\10\0\7\2404\262FQ\212\n\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T%0\0\0006\1\376\372W\370q\16PaG\27\0\0\354\347z\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=7 ttl=54 time=65.6 ms
) = 60
gettimeofday({1186084000, 759246}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 931) = 0
gettimeofday({1186084001, 699157}, NULL) = 0
gettimeofday({1186084001, 700258}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\322\301z\10\0\10\2414\262Fb\257\n\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T.\355\0\0006\1\365=W\370q\16PaG\27\0\0\332\301z\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=8 ttl=54 time=71.1 ms
) = 60
gettimeofday({1186084001, 774234}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 925) = 0
gettimeofday({1186084002, 698648}, NULL) = 0
gettimeofday({1186084002, 699750}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\315\302z\10\0\t\2424\262Ff\255\n\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T9\6\0\0006\1\353$W\370q\16PaG\27\0\0\325\302z\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=9 ttl=54 time=72.0 ms
) = 60
gettimeofday({1186084002, 774597}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 925) = 0
gettimeofday({1186084003, 698164}, NULL) = 0
gettimeofday({1186084003, 699284}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\236\303z\10\0\n\2434\262F\224\253\n\0\10\t\n\v\f"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
setitimer(ITIMER_REAL, {it_interval={0, 0}, it_value={1, 0}}, NULL) = 0
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0TB\362\0\0006\1\3418W\370q\16PaG\27\0\0\246\303z\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6164 bytes from 87.248.113.14: icmp_seq=10 ttl=54 time=74.0 ms
) = 61
write(1, "\n", 1
) = 1
write(1, "--- 87.248.113.14 ping statistic"..., 156--- 87.248.113.14 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9024ms
rtt min/avg/max/mdev = 61.605/67.447/74.038/4.053 ms
) = 156
exit_group(0) = ?
Process 2170 detached
[-- Attachment #4: strace_eth0_2.6.22 --]
[-- Type: application/octet-stream, Size: 13336 bytes --]
execve("./ping", ["./ping", "-I", "eth0", "87.248.113.14", "-c", "10"], [/* 29 vars */]) = 0
brk(0) = 0x8063000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=34724, ...}) = 0
mmap2(NULL, 34724, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7fd7000
close(3) = 0
open("/lib/libresolv.so.2", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0`!\0\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=77439, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fd6000
mmap2(NULL, 75976, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7fc3000
mmap2(0xb7fd2000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf) = 0xb7fd2000
mmap2(0xb7fd4000, 6344, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7fd4000
close(3) = 0
open("/lib/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@_\1\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1528742, ...}) = 0
mmap2(NULL, 1316260, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7e81000
mmap2(0xb7fbd000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x13c) = 0xb7fbd000
mmap2(0xb7fc0000, 9636, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7fc0000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7e80000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7e806c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb7fbd000, 4096, PROT_READ) = 0
munmap(0xb7fd7000, 34724) = 0
socket(PF_INET, SOCK_RAW, IPPROTO_ICMP) = 3
getuid32() = 0
setuid32(0) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
setsockopt(4, SOL_SOCKET, SO_BINDTODEVICE, "eth0\0", 5) = 0
connect(4, {sa_family=AF_INET, sin_port=htons(1025), sin_addr=inet_addr("87.248.113.14")}, 16) = 0
getsockname(4, {sa_family=AF_INET, sin_port=htons(32794), sin_addr=inet_addr("80.97.71.23")}, [16]) = 0
close(4) = 0
ioctl(3, SIOCGIFINDEX, {ifr_name="eth0", ifr_index=1}) = 0
setsockopt(3, SOL_RAW, ICMP_FILTER, ~(ICMP_ECHOREPLY|ICMP_DEST_UNREACH|ICMP_SOURCE_QUENCH|ICMP_REDIRECT|ICMP_TIME_EXCEEDED|ICMP_PARAMETERPROB), 4) = 0
setsockopt(3, SOL_IP, IP_RECVERR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_SNDBUF, [324], 4) = 0
setsockopt(3, SOL_SOCKET, SO_RCVBUF, [65536], 4) = 0
getsockopt(3, SOL_SOCKET, SO_RCVBUF, [131072], [4]) = 0
brk(0) = 0x8063000
brk(0x8084000) = 0x8084000
fstat64(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fdf000
setsockopt(3, SOL_SOCKET, SO_TIMESTAMP, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_SNDTIMEO, "\1\0\0\0\0\0\0\0", 8) = 0
setsockopt(3, SOL_SOCKET, SO_RCVTIMEO, "\1\0\0\0\0\0\0\0", 8) = 0
getpid() = 3133
rt_sigaction(SIGINT, {0x804b220, [], SA_INTERRUPT}, NULL, 8) = 0
rt_sigaction(SIGALRM, {0x804b220, [], SA_INTERRUPT}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {0x804b230, [], SA_INTERRUPT}, NULL, 8) = 0
gettimeofday({1186083746, 626252}, NULL) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbff7ecf8) = -1 EINVAL (Invalid argument)
gettimeofday({1186083746, 628890}, NULL) = 0
gettimeofday({1186083746, 630093}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0$\330=\f\0\1\2423\262FM\235\t\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\371~\0\0006\1*\254W\370q\16PaG\27\0\0,\330=\f\0"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "PING 87.248.113.14 (87.248.113.1"..., 140PING 87.248.113.14 (87.248.113.14) from 80.97.71.23 eth0: 56(84) bytes of data.
64 bytes from 87.248.113.14: icmp_seq=1 ttl=54 time=71.7 ms
) = 140
gettimeofday({1186083746, 705534}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR, revents=POLLIN}], 1, 924) = 1
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("80.97.71.1")}, msg_iov(1)=[{"E\0\0T\0\0@\0\377\1L\316PaG\1PaG\27\0\0\277s\324\v\0\220"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, MSG_DONTWAIT) = 84
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, "\10\0\0\0\300\372\4\10", 8) = 0
gettimeofday({1186083747, 576174}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 52) = 0
gettimeofday({1186083747, 633886}, NULL) = 0
gettimeofday({1186083747, 633994}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\346\307=\f\0\2\2433\262F\212\254\t\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\3J\0\0006\1 \341W\370q\16PaG\27\0\0\356\307=\f\0"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=2 ttl=54 time=79.0 ms
) = 60
gettimeofday({1186083747, 713584}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 921) = 0
gettimeofday({1186083748, 633914}, NULL) = 0
gettimeofday({1186083748, 634023}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\310\306=\f\0\3\2443\262F\247\254\t\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\ra\0\0006\1\26\312W\370q\16PaG\27\0\0\320\306=\f"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=3 ttl=54 time=65.7 ms
) = 60
gettimeofday({1186083748, 700301}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 934) = 0
gettimeofday({1186083749, 633921}, NULL) = 0
gettimeofday({1186083749, 634029}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\301\305=\f\0\4\2453\262F\255\254\t\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\27\320\0\0006\1\f[W\370q\16PaG\27\0\0\311\305=\f"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=4 ttl=54 time=68.8 ms
) = 60
gettimeofday({1186083749, 703430}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 931) = 0
gettimeofday({1186083750, 633923}, NULL) = 0
gettimeofday({1186083750, 634032}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\275\304=\f\0\5\2463\262F\260\254\t\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T\"\26\0\0006\1\2\25W\370q\16PaG\27\0\0\305\304=\f"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=5 ttl=54 time=69.3 ms
) = 60
gettimeofday({1186083750, 703966}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 930) = 0
gettimeofday({1186083751, 633940}, NULL) = 0
gettimeofday({1186083751, 634050}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\252\303=\f\0\6\2473\262F\302\254\t\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T,\272\0\0006\1\367pW\370q\16PaG\27\0\0\262\303=\f"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=6 ttl=54 time=64.0 ms
) = 60
gettimeofday({1186083751, 698661}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 936) = 0
gettimeofday({1186083752, 633964}, NULL) = 0
gettimeofday({1186083752, 634073}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\222\302=\f\0\7\2503\262F\331\254\t\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0T7>\0\0006\1\354\354W\370q\16PaG\27\0\0\232\302=\f"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=7 ttl=54 time=64.5 ms
) = 60
gettimeofday({1186083752, 699205}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 935) = 0
gettimeofday({1186083753, 633955}, NULL) = 0
gettimeofday({1186083753, 634064}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\232\301=\f\0\10\2513\262F\320\254\t\0\10\t\n\v\f"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0TA\236\0\0006\1\342\214W\370q\16PaG\27\0\0\242\301"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=8 ttl=54 time=62.7 ms
) = 60
gettimeofday({1186083753, 697399}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 937) = 0
gettimeofday({1186083754, 633961}, NULL) = 0
gettimeofday({1186083754, 634070}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\223\300=\f\0\t\2523\262F\326\254\t\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0TL-\0\0006\1\327\375W\370q\16PaG\27\0\0\233\300=\f"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=9 ttl=54 time=68.6 ms
) = 60
gettimeofday({1186083754, 703225}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 931) = 0
gettimeofday({1186083755, 633971}, NULL) = 0
gettimeofday({1186083755, 634079}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\211\277=\f\0\n\2533\262F\337\254\t\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
setitimer(ITIMER_REAL, {it_interval={0, 0}, it_value={1, 0}}, NULL) = 0
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\0\0TVE\0\0006\1\315\345W\370q\16PaG\27\0\0\221\277=\f"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6164 bytes from 87.248.113.14: icmp_seq=10 ttl=54 time=63.9 ms
) = 61
write(1, "\n", 1
) = 1
write(1, "--- 87.248.113.14 ping statistic"..., 156--- 87.248.113.14 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9007ms
rtt min/avg/max/mdev = 62.799/67.886/79.054/4.651 ms
) = 156
exit_group(0) = ?
Process 3133 detached
[-- Attachment #5: strace_eth2_2.6.20 --]
[-- Type: application/octet-stream, Size: 13335 bytes --]
execve("./ping", ["./ping", "-I", "eth2", "87.248.113.14", "-c", "10"], [/* 26 vars */]) = 0
brk(0) = 0x8063000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=34724, ...}) = 0
mmap2(NULL, 34724, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7fcb000
close(3) = 0
open("/lib/libresolv.so.2", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0`!\0\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=77439, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fca000
mmap2(NULL, 75976, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7fb7000
mmap2(0xb7fc6000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf) = 0xb7fc6000
mmap2(0xb7fc8000, 6344, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7fc8000
close(3) = 0
open("/lib/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@_\1\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1528742, ...}) = 0
mmap2(NULL, 1316260, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7e75000
mmap2(0xb7fb1000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x13c) = 0xb7fb1000
mmap2(0xb7fb4000, 9636, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7fb4000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7e74000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7e746c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb7fb1000, 4096, PROT_READ) = 0
munmap(0xb7fcb000, 34724) = 0
socket(PF_INET, SOCK_RAW, IPPROTO_ICMP) = 3
getuid32() = 0
setuid32(0) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
setsockopt(4, SOL_SOCKET, SO_BINDTODEVICE, "eth2\0", 5) = 0
connect(4, {sa_family=AF_INET, sin_port=htons(1025), sin_addr=inet_addr("87.248.113.14")}, 16) = 0
getsockname(4, {sa_family=AF_INET, sin_port=htons(32778), sin_addr=inet_addr("86.106.19.75")}, [16]) = 0
close(4) = 0
ioctl(3, SIOCGIFINDEX, {ifr_name="eth2", ifr_index=3}) = 0
setsockopt(3, SOL_RAW, ICMP_FILTER, ~(ICMP_ECHOREPLY|ICMP_DEST_UNREACH|ICMP_SOURCE_QUENCH|ICMP_REDIRECT|ICMP_TIME_EXCEEDED|ICMP_PARAMETERPROB), 4) = 0
setsockopt(3, SOL_IP, IP_RECVERR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_SNDBUF, [324], 4) = 0
setsockopt(3, SOL_SOCKET, SO_RCVBUF, [65536], 4) = 0
getsockopt(3, SOL_SOCKET, SO_RCVBUF, [131072], [4]) = 0
brk(0) = 0x8063000
brk(0x8084000) = 0x8084000
fstat64(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fd3000
setsockopt(3, SOL_SOCKET, SO_TIMESTAMP, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_SNDTIMEO, "\1\0\0\0\0\0\0\0", 8) = 0
setsockopt(3, SOL_SOCKET, SO_RCVTIMEO, "\1\0\0\0\0\0\0\0", 8) = 0
getpid() = 2113
rt_sigaction(SIGINT, {0x804b220, [], SA_INTERRUPT}, NULL, 8) = 0
rt_sigaction(SIGALRM, {0x804b220, [], SA_INTERRUPT}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {0x804b230, [], SA_INTERRUPT}, NULL, 8) = 0
gettimeofday({1186083952, 706356}, NULL) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbfb8ce78) = -1 EINVAL (Invalid argument)
gettimeofday({1186083952, 706639}, NULL) = 0
gettimeofday({1186083952, 706742}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\350\257A\10\0\1p4\262F\266\310\n\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0T6\371\0\0005\1\33mW\370q\16Vj\23K\0\0\360\257A"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "PING 87.248.113.14 (87.248.113.1"..., 141PING 87.248.113.14 (87.248.113.14) from 86.106.19.75 eth2: 56(84) bytes of data.
64 bytes from 87.248.113.14: icmp_seq=1 ttl=53 time=75.1 ms
) = 141
gettimeofday({1186083952, 785127}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 922) = 0
gettimeofday({1186083953, 713300}, NULL) = 0
gettimeofday({1186083953, 714212}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\271\221A\10\0\2q4\262F\344\345\n\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0TAL\0\0005\1\21\32W\370q\16Vj\23K\0\0\301\221A\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=2 ttl=53 time=70.6 ms
) = 60
gettimeofday({1186083953, 787144}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 927) = 0
gettimeofday({1186083954, 712881}, NULL) = 0
gettimeofday({1186083954, 713737}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\223\222A\10\0\3r4\262F\t\344\n\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0TK\223\0\0005\1\6\323W\370q\16Vj\23K\0\0\233\222"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=3 ttl=53 time=71.5 ms
) = 60
gettimeofday({1186083954, 787613}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 926) = 0
gettimeofday({1186083955, 712260}, NULL) = 0
gettimeofday({1186083955, 713116}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\377\223A\10\0\4s4\262F\234\341\n\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0TU\254\0\0005\1\374\271W\370q\16Vj\23K\0\0\7\224"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=4 ttl=53 time=67.4 ms
) = 60
gettimeofday({1186083955, 781925}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 931) = 0
gettimeofday({1186083956, 721775}, NULL) = 0
gettimeofday({1186083956, 722662}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\264mA\10\0\5t4\262F\346\6\v\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0T`\37\0\0005\1\362FW\370q\16Vj\23K\0\0\274mA\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=5 ttl=53 time=68.2 ms
) = 60
gettimeofday({1186083956, 793146}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 929) = 0
gettimeofday({1186083957, 721304}, NULL) = 0
gettimeofday({1186083957, 722165}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\244nA\10\0\6u4\262F\365\4\v\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0Tj\26\0\0005\1\350OW\370q\16Vj\23K\0\0\254nA\10"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=6 ttl=53 time=69.7 ms
) = 60
gettimeofday({1186083957, 792929}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 929) = 0
gettimeofday({1186083958, 720785}, NULL) = 0
gettimeofday({1186083958, 721642}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\256oA\10\0\7v4\262F\352\2\v\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0Tt/\0\0005\1\3366W\370q\16Vj\23K\0\0\266oA\10\0"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=7 ttl=53 time=71.7 ms
) = 60
gettimeofday({1186083958, 795674}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 926) = 0
gettimeofday({1186083959, 720275}, NULL) = 0
gettimeofday({1186083959, 721132}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\253pA\10\0\10w4\262F\354\0\v\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0T}\330\0\0005\1\324\215W\370q\16Vj\23K\0\0\263p"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=8 ttl=53 time=66.7 ms
) = 60
gettimeofday({1186083959, 789802}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 931) = 0
gettimeofday({1186083960, 729784}, NULL) = 0
gettimeofday({1186083960, 730641}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\205JA\10\0\tx4\262F\21&\v\0\10\t\n\v\f\r\16\17\20"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0T\207\350\0\0005\1\312}W\370q\16Vj\23K\0\0\215J"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6064 bytes from 87.248.113.14: icmp_seq=9 ttl=53 time=67.2 ms
) = 60
gettimeofday({1186083960, 800065}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR, revents=POLLIN}], 1, 930) = 1
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("80.97.71.1")}, msg_iov(1)=[{"E\0\0T\0\0@\0\377\1L\316PaG\1PaG\27\0\0gr\\\10\0\1y4\262"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, MSG_DONTWAIT) = 84
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, "\10\0\0\0\300\372\4\10", 8) = 0
gettimeofday({1186083961, 341678}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 388) = 0
gettimeofday({1186083961, 729316}, NULL) = 0
gettimeofday({1186083961, 730180}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0QKA\10\0\ny4\262FD$\v\0\10\t\n\v\f\r\16\17\20\21\22"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, MSG_CONFIRM) = 64
setitimer(ITIMER_REAL, {it_interval={0, 0}, it_value={1, 0}}, NULL) = 0
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"E\210\0T\222:\0\0005\1\300+W\370q\16Vj\23K\0\0YKA\10\0"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
write(1, "64 bytes from 87.248.113.14: icm"..., 6164 bytes from 87.248.113.14: icmp_seq=10 ttl=53 time=70.3 ms
) = 61
write(1, "\n", 1
) = 1
write(1, "--- 87.248.113.14 ping statistic"..., 156--- 87.248.113.14 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9022ms
rtt min/avg/max/mdev = 66.726/69.874/75.180/2.477 ms
) = 156
exit_group(0) = ?
Process 2113 detached
[-- Attachment #6: strace_eth2_2.6.22 --]
[-- Type: application/octet-stream, Size: 15575 bytes --]
execve("./ping", ["./ping", "-I", "eth2", "87.248.113.14", "-c", "10"], [/* 29 vars */]) = 0
brk(0) = 0x8063000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=34724, ...}) = 0
mmap2(NULL, 34724, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7eff000
close(3) = 0
open("/lib/libresolv.so.2", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0`!\0\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=77439, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7efe000
mmap2(NULL, 75976, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7eeb000
mmap2(0xb7efa000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf) = 0xb7efa000
mmap2(0xb7efc000, 6344, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7efc000
close(3) = 0
open("/lib/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@_\1\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1528742, ...}) = 0
mmap2(NULL, 1316260, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7da9000
mmap2(0xb7ee5000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x13c) = 0xb7ee5000
mmap2(0xb7ee8000, 9636, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7ee8000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7da8000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7da86c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb7ee5000, 4096, PROT_READ) = 0
munmap(0xb7eff000, 34724) = 0
socket(PF_INET, SOCK_RAW, IPPROTO_ICMP) = 3
getuid32() = 0
setuid32(0) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
setsockopt(4, SOL_SOCKET, SO_BINDTODEVICE, "eth2\0", 5) = 0
connect(4, {sa_family=AF_INET, sin_port=htons(1025), sin_addr=inet_addr("87.248.113.14")}, 16) = 0
getsockname(4, {sa_family=AF_INET, sin_port=htons(32794), sin_addr=inet_addr("86.106.19.75")}, [16]) = 0
close(4) = 0
ioctl(3, SIOCGIFINDEX, {ifr_name="eth2", ifr_index=3}) = 0
setsockopt(3, SOL_RAW, ICMP_FILTER, ~(ICMP_ECHOREPLY|ICMP_DEST_UNREACH|ICMP_SOURCE_QUENCH|ICMP_REDIRECT|ICMP_TIME_EXCEEDED|ICMP_PARAMETERPROB), 4) = 0
setsockopt(3, SOL_IP, IP_RECVERR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_SNDBUF, [324], 4) = 0
setsockopt(3, SOL_SOCKET, SO_RCVBUF, [65536], 4) = 0
getsockopt(3, SOL_SOCKET, SO_RCVBUF, [131072], [4]) = 0
brk(0) = 0x8063000
brk(0x8084000) = 0x8084000
fstat64(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f07000
setsockopt(3, SOL_SOCKET, SO_TIMESTAMP, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_SNDTIMEO, "\1\0\0\0\0\0\0\0", 8) = 0
setsockopt(3, SOL_SOCKET, SO_RCVTIMEO, "\1\0\0\0\0\0\0\0", 8) = 0
getpid() = 3093
rt_sigaction(SIGINT, {0x804b220, [], SA_INTERRUPT}, NULL, 8) = 0
rt_sigaction(SIGALRM, {0x804b220, [], SA_INTERRUPT}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {0x804b230, [], SA_INTERRUPT}, NULL, 8) = 0
gettimeofday({1186083700, 834172}, NULL) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbff2ac98) = -1 EINVAL (Invalid argument)
gettimeofday({1186083700, 836688}, NULL) = 0
gettimeofday({1186083700, 837896}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\274\254\25\f\0\1t3\262F\10\311\f\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("80.97.71.1")}, msg_iov(1)=[{"E\0\0T\0\0@\0\377\1L\316PaG\1PaG\27\0\0a\243\324\v\0bu"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 84
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, "\10\0\0\0\300\372\4\10", 8) = 0
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083701, 583220}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 253) = 0
gettimeofday({1186083701, 843650}, NULL) = 0
gettimeofday({1186083701, 843759}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\324\224\25\f\0\2u3\262F\357\337\f\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083702, 843650}, NULL) = 0
gettimeofday({1186083702, 843766}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\314\223\25\f\0\3v3\262F\366\337\f\0\10\t\n\v\f\r"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("86.106.19.75")}, msg_iov(1)=[{"E\300\0pEx\0\0@\1`\353Vj\23KVj\23K\3\1\374\376\0\0\0\0"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 112
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\274\254\25\f\0\1", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
setsockopt(3, SOL_RAW, ICMP_FILTER, ~(ICMP_ECHOREPLY|ICMP_SOURCE_QUENCH|ICMP_REDIRECT), 4) = 0
write(1, "PING 87.248.113.14 (87.248.113.1"..., 139PING 87.248.113.14 (87.248.113.14) from 86.106.19.75 eth2: 56(84) bytes of data.
From 86.106.19.75 icmp_seq=1 Destination Host Unreachable
) = 139
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\324\224\25\f\0\2", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
write(1, "From 86.106.19.75 icmp_seq=2 Des"..., 58From 86.106.19.75 icmp_seq=2 Destination Host Unreachable
) = 58
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\314\223\25\f\0\3", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
write(1, "From 86.106.19.75 icmp_seq=3 Des"..., 58From 86.106.19.75 icmp_seq=3 Destination Host Unreachable
) = 58
gettimeofday({1186083703, 846127}, NULL) = 0
gettimeofday({1186083703, 846230}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0+\211\25\f\0\4w3\262F\226\351\f\0\10\t\n\v\f\r\16"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("86.106.19.75")}, msg_iov(1)=[{"E\300\0pEy\0\0@\1`\352Vj\23KVj\23K\3\1\374\376\0\0\0\0"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, 0) = 112
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("86.106.19.75")}, msg_iov(1)=[{"E\300\0pEz\0\0@\1`\351Vj\23KVj\23K\3\1\374\376\0\0\0\0"..., 192}], msg_controllen=20, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=0}, MSG_DONTWAIT) = 112
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083703, 847416}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 999) = 0
gettimeofday({1186083704, 843581}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 10) = 0
gettimeofday({1186083704, 853609}, NULL) = 0
gettimeofday({1186083704, 853719}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\351j\25\f\0\5x3\262F\327\6\r\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083705, 853658}, NULL) = 0
gettimeofday({1186083705, 853772}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\263i\25\f\0\6y3\262F\f\7\r\0\10\t\n\v\f\r\16\17\20"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083706, 853912}, NULL) = 0
gettimeofday({1186083706, 854026}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\264g\25\f\0\7z3\262F\n\10\r\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, 0xbff2aec8, 0) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0+\211\25\f\0\4", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
write(1, "From 86.106.19.75 icmp_seq=4 Des"..., 58From 86.106.19.75 icmp_seq=4 Destination Host Unreachable
) = 58
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\351j\25\f\0\5", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
write(1, "From 86.106.19.75 icmp_seq=5 Des"..., 58From 86.106.19.75 icmp_seq=5 Destination Host Unreachable
) = 58
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\263i\25\f\0\6", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
write(1, "From 86.106.19.75 icmp_seq=6 Des"..., 58From 86.106.19.75 icmp_seq=6 Destination Host Unreachable
) = 58
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083706, 856631}, NULL) = 0
poll([{fd=3, events=POLLIN|POLLERR}], 1, 998) = 0
gettimeofday({1186083707, 853868}, NULL) = 0
gettimeofday({1186083707, 853975}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\346f\25\f\0\10{3\262F\327\7\r\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083708, 853620}, NULL) = 0
gettimeofday({1186083708, 853735}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\325f\25\f\0\t|3\262F\347\6\r\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1186083709, 853616}, NULL) = 0
gettimeofday({1186083709, 853729}, NULL) = 0
sendmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\332e\25\f\0\n}3\262F\341\6\r\0\10\t\n\v\f\r\16\17"..., 64}], msg_controllen=24, {cmsg_len=24, cmsg_level=SOL_IP, cmsg_type=, ...}, msg_flags=0}, 0) = 64
setitimer(ITIMER_REAL, {it_interval={0, 0}, it_value={10, 0}}, NULL) = 0
recvmsg(3, 0xbff2aec8, 0) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\346f\25\f\0\10", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
write(1, "From 86.106.19.75 icmp_seq=8 Des"..., 58From 86.106.19.75 icmp_seq=8 Destination Host Unreachable
) = 58
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\325f\25\f\0\t", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
write(1, "From 86.106.19.75 icmp_seq=9 Des"..., 58From 86.106.19.75 icmp_seq=9 Destination Host Unreachable
) = 58
recvmsg(3, 0xbff2aec8, MSG_DONTWAIT) = -1 EHOSTUNREACH (No route to host)
recvmsg(3, {msg_name(16)={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("87.248.113.14")}, msg_iov(1)=[{"\10\0\332e\25\f\0\n", 8}], msg_controllen=64, {cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=0x1d /* SCM_??? */, ...}, msg_flags=MSG_TRUNC|MSG_ERRQUEUE}, MSG_ERRQUEUE|MSG_DONTWAIT) = 8
write(1, "From 86.106.19.75 icmp_seq=10 De"..., 59From 86.106.19.75 icmp_seq=10 Destination Host Unreachable
) = 59
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
recvmsg(3, 0xbff2aec8, 0) = -1 EAGAIN (Resource temporarily unavailable)
--- SIGALRM (Alarm clock) @ 0 (0) ---
sigreturn() = ? (mask now [])
write(1, "\n", 1
) = 1
write(1, "--- 87.248.113.14 ping statistic"..., 124--- 87.248.113.14 ping statistics ---
10 packets transmitted, 0 received, +9 errors, 100% packet loss, time 9019ms
, pipe 4
) = 124
exit_group(1) = ?
Process 3093 detached
^ permalink raw reply
* Re: [PATCH 00/23] per device dirty throttling -v8
From: david @ 2007-08-04 17:15 UTC (permalink / raw)
To: Ray Lee
Cc: Ingo Molnar, Linus Torvalds, Peter Zijlstra, linux-mm,
linux-kernel, miklos, akpm, neilb, dgc, tomoki.sekiyama.qu,
nikita, trond.myklebust, yingchao.zhou, richard, netdev
In-Reply-To: <2c0942db0708040901x7ada0fe2mf71f37ecba51005b@mail.gmail.com>
On Sat, 4 Aug 2007, Ray Lee wrote:
> (adding netdev cc:)
>
> On 8/4/07, david@lang.hm <david@lang.hm> wrote:
>> On Sat, 4 Aug 2007, Ingo Molnar wrote:
>>
>>> * Ingo Molnar <mingo@elte.hu> wrote:
>>>
>>>> There are positive reports in the never-ending "my system crawls like
>>>> an XT when copying large files" bugzilla entry:
>>>>
>>>> http://bugzilla.kernel.org/show_bug.cgi?id=7372
>>>
>>> i forgot this entry:
>>>
>>> " We recently upgraded our office to gigabit Ethernet and got some big
>>> AMD64 / 3ware boxes for file and vmware servers... only to find them
>>> almost useless under any kind of real load. I've built some patched
>>> 2.6.21.6 kernels (using the bdi throttling patch you mentioned) to
>>> see if our various Debian Etch boxes run better. So far my testing
>>> shows a *great* improvement over the stock Debian 2.6.18 kernel on
>>> our configurations. "
>>>
>>> and bdi has been in -mm in the past i think, so we also know (to a
>>> certain degree) that it does not hurt those workloads that are fine
>>> either.
>>>
>>> [ my personal interest in this is the following regression: every time i
>>> start a large kernel build with DEBUG_INFO on a quad-core 4GB RAM box,
>>> i get up to 30 seconds complete pauses in Vim (and most other tasks),
>>> during plain editing of the source code. (which happens when Vim tries
>>> to write() to its swap/undo-file.) ]
>>
>> I have an issue that sounds like it's related.
>>
>> I've got a syslog server that's got two Opteron 246 cpu's, 16G ram, 2x140G
>> 15k rpm drives (fusion MPT hardware mirroring), 16x500G 7200rpm SATA
>> drives on 3ware 9500 cards (software raid6) running 2.6.20.3 with hz set
>> at default and preempt turned off.
>>
>> I have syslog doing buffered writes to the SCSI drives and every 5 min a
>> cron job copies the data to the raid array.
>>
>> I've found that if I do anything significant on the large raid array that
>> the system looses a significant amount of the UDP syslog traffic, even
>> though there should be pleanty of ram and cpu (and the spindles involved
>> in the writes are not being touched), even a grep can cause up to 40%
>> losses in the syslog traffic. I've experimented with nice levels (nicing
>> down the grep and nicing up the syslogd) without a noticable effect on the
>> losses.
>>
>> I've been planning to try a new kernel with hz=1000 to see if that would
>> help, and after that experiment with the various preempt settings, but it
>> sounds like the per-device queues may actually be more relavent to the
>> problem.
>>
>> what would you suggest I test, and in what order and combination?
>
> At least on a surface level, your report has some similarities to
> http://lkml.org/lkml/2007/5/21/84 . In that message, John Miller
> mentions several things he tried without effect:
>
> < - I increased the max allowed receive buffer through
> < proc/sys/net/core/rmem_max and the application calls the right
> < syscall. "netstat -su" does not show any "packet receive errors".
> <
> < - After getting "kernel: swapper: page allocation failure.
> < order:0, mode:0x20", I increased /proc/sys/vm/min_free_kbytes
> <
> < - ixgb.txt in kernel network documentation suggests to increase
> < net.core.netdev_max_backlog to 300000. This did not help.
> <
> < - I also had to increase net.core.optmem_max, because the default
> < value was too small for 700 multicast groups.
>
> As they're all pretty simple to test, it may be worthwhile to give
> them a shot just to rule things out.
I will try them later today.
I forgot to mention that the filesystems are ext2 for the mirrored high
speed disks and xfs for the 8TB array.
David Lang
^ permalink raw reply
* [PATCH][RESEND] Avoid possible NULL pointer deref in 3c359 driver
From: Jesper Juhl @ 2007-08-04 18:31 UTC (permalink / raw)
To: Andrew Morton
Cc: Linux Kernel Mailing List, Mike Phillips, netdev, linux-tr,
Jesper Juhl, davem
(Resending old patch originally submitted at 1/7-2007 02:19)
In xl_freemem(), if dev_if is NULL, the line
struct xl_private *xl_priv =(struct xl_private *)dev->priv;
will cause a NULL pointer dereference. However, if we move
that assignment below the 'if' statement that tests for a NULL
'dev', then that NULL deref can never happen.
It never hurts to be safe :-)
Signed-off-by: Jesper Juhl <jesper.juhl@gmail.com>
---
diff --git a/drivers/net/tokenring/3c359.c b/drivers/net/tokenring/3c359.c
index e22a3f5..671f4da 100644
--- a/drivers/net/tokenring/3c359.c
+++ b/drivers/net/tokenring/3c359.c
@@ -1044,15 +1044,17 @@ static void xl_freemem(struct net_device *dev)
static irqreturn_t xl_interrupt(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *)dev_id;
- struct xl_private *xl_priv =(struct xl_private *)dev->priv;
- u8 __iomem * xl_mmio = xl_priv->xl_mmio ;
- u16 intstatus, macstatus ;
+ struct xl_private *xl_priv;
+ u8 __iomem * xl_mmio;
+ u16 intstatus, macstatus;
if (!dev) {
- printk(KERN_WARNING "Device structure dead, aaahhhh !\n") ;
+ printk(KERN_WARNING "3c359: Device structure dead, aaahhhh!\n");
return IRQ_NONE;
}
+ xl_priv = (struct xl_private *)dev->priv;
+ xl_mmio = xl_priv->xl_mmio;
intstatus = readw(xl_mmio + MMIO_INTSTATUS) ;
if (!(intstatus & 1)) /* We didn't generate the interrupt */
^ permalink raw reply related
* Re: [PATCH] ixgbe: New driver for Pci-Express 10GbE 82598 support
From: Bill Fink @ 2007-08-04 19:04 UTC (permalink / raw)
To: Auke Kok
Cc: jeff, netdev, ayyappan.veeraiyan, akpm, arjan, hch, shemminger,
nhorman, inaky, mb, john.ronciak
In-Reply-To: <20070804040502.D9133F2C0F@doppio.foo-projects.org>
On Fri, 3 Aug 2007, Auke Kok wrote:
> This patch adds support for the Intel 82598 PCI-Express 10GbE
> chipset. Devices will be available on the market soon.
>
> This version of the driver is largely the same as the last release:
>
> * Driver uses a single RX and single TX queue, each using 1 MSI-X
> irq vector.
> * Driver runs in NAPI mode only
> * Driver is largely multiqueue-ready (TM)
...
> diff --git a/Documentation/networking/ixgbe.txt b/Documentation/networking/ixgbe.txt
> new file mode 100644
> index 0000000..823d69c
> --- /dev/null
> +++ b/Documentation/networking/ixgbe.txt
> @@ -0,0 +1,72 @@
> +Linux* Base Driver for the 10 Gigabit Family of Adapters
> +================================================================
> +
> +July 09, 2007
> +
> +
> +Contents
> +========
> +
> +- In This Release
> +- Identifying Your Adapter
> +- Command Line Parameters
There is no section "Command Line Parameters" in the document.
-Bill
> +- Support
> +
> +In This Release
> +===============
> +
> +This file describes the Linux* Base Driver for the 10 Gigabit PCI Express
> +Family of Adapters. This driver supports the 2.6.x kernel. This driver
> +includes support for Itanium(R)2-based systems.
> +
> +The following features are now available in supported kernels:
> + - Native VLANs
> + - Channel Bonding (teaming)
> + - SNMP
> +
> +Channel Bonding documentation can be found in the Linux kernel source:
> +/Documentation/networking/bonding.txt
> +
> +Instructions on updating ethtool can be found in the section "Additional
> +Configurations" later in this document.
> +
> +
> +Identifying Your Adapter
> +========================
> +
> +The following Intel network adapters are compatible with the drivers in this
> +release:
> +
> +Controller Adapter Name Physical Layer
> +---------- ------------ --------------
> +82598 Intel(R) 10GbE-LR/LRM/SR
> + Server Adapters 10G Base -SR (850 nm optical fiber)
> + 10G Base -LRM (850 nm optical fiber)
> + 10G Base -LR (1310 nm optical fiber)
> +
> +For more information on how to identify your adapter, go to the Adapter &
> +Driver ID Guide at:
> +
> + http://support.intel.com/support/network/sb/CS-012904.htm
> +
> +For the latest Intel network drivers for Linux, refer to the following
> +website. In the search field, enter your adapter name or type, or use the
> +networking link on the left to search for your adapter:
> +
> + http://downloadfinder.intel.com/scripts-df/support_intel.asp
> +
> +
> +Support
> +=======
> +
> +For general information, go to the Intel support website at:
> +
> + http://support.intel.com
> +
> +or the Intel Wired Networking project hosted by Sourceforge at:
> +
> + http://sourceforge.net/projects/e1000
> +
> +If an issue is identified with the released source code on the supported
> +kernel with a supported adapter, email the specific information related
> +to the issue to e1000-devel@lists.sf.net
^ permalink raw reply
* Re: [PATCH] ixgbe: New driver for Pci-Express 10GbE 82598 support
From: Kok, Auke @ 2007-08-04 23:04 UTC (permalink / raw)
To: Bill Fink
Cc: jeff, netdev, ayyappan.veeraiyan, akpm, arjan, hch, shemminger,
nhorman, inaky, mb, john.ronciak
In-Reply-To: <20070804150401.afb6b709.billfink@mindspring.com>
Bill Fink wrote:
> On Fri, 3 Aug 2007, Auke Kok wrote:
>> +
>> +Contents
>> +========
>> +
>> +- In This Release
>> +- Identifying Your Adapter
>> +- Command Line Parameters
>
> There is no section "Command Line Parameters" in the document.
>
> -Bill
hmm yes, I removed all of them :)
Obviously I'll update the index. Thanks
Auke
^ permalink raw reply
* [RFC Resend 3/3][BNX2]: Add iSCSI support to BNX2 devices.
From: Michael Chan @ 2007-08-05 0:19 UTC (permalink / raw)
To: Jeff Garzik
Cc: David Miller, mchristi, netdev, open-iscsi, anilgv, talm,
lusinsky, uri
In-Reply-To: <46B43D2D.2090800@garzik.org>
diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
new file mode 100644
index 0000000..0576e1b
--- /dev/null
+++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c
@@ -0,0 +1,3718 @@
+/* bnx2i_iscsi.c: Broadcom NetXtreme II iSCSI driver.
+
+ *
+ * Copyright (c) 2004, 2005, 2006 Broadcom Corporation
+ *
+ * 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: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+
+#include "bnx2i.h"
+
+struct scsi_host_template bnx2i_host_template;
+struct iscsi_transport bnx2i_iscsi_transport;
+
+/*
+ * Global endpoint resource info
+ */
+void *bnx2i_ep_pages[MAX_PAGES_PER_CTRL_STRUCT_POOL];
+struct list_head bnx2i_free_ep_list;
+struct list_head bnx2i_unbound_ep;
+u32 bnx2i_num_free_ep;
+u32 bnx2i_max_free_ep;
+spinlock_t bnx2i_resc_lock;
+struct tcp_port_mngt bnx2i_tcp_port_tbl;
+static u16 bnx2i_local_tcp_port = 63000;
+
+
+static struct io_bdt *bnx2i_alloc_bd_table(struct bnx2i_sess *sess,
+ struct bnx2i_cmd *);
+static void bnx2i_free_tcp_port(u16 port);
+static u16 bnx2i_alloc_tcp_port(void);
+
+
+static int bnx2i_adapter_ready(struct bnx2i_hba *hba)
+{
+ int retval = 0;
+
+ if (!hba || !test_bit(ADAPTER_STATE_UP, &hba->adapter_state) ||
+ test_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state))
+ retval = -EPERM;
+ return retval;
+}
+
+/*
+ * identifies & marks various bd info for imm data, unsolicited data
+ * and the first solicited data seq.
+ */
+static void bnx2i_get_write_cmd_bd_idx(struct bnx2i_cmd *cmd, u32 buf_off,
+ u32 *start_bd_off, u32 *start_bd_idx)
+{
+ u32 cur_offset = 0;
+ u32 cur_bd_idx = 0;
+ struct iscsi_bd *bd_tbl = cmd->bd_tbl->bd_tbl;
+
+ if (buf_off) {
+ while (buf_off >= (cur_offset + bd_tbl->buffer_length)) {
+ cur_offset += bd_tbl->buffer_length;
+ cur_bd_idx++;
+ bd_tbl++;
+ }
+ }
+
+ *start_bd_off = buf_off - cur_offset;
+ *start_bd_idx = cur_bd_idx;
+}
+
+/*
+ * identifies & marks various bd info for immediate data,
+ * unsolicited data and first solicited data seq.
+ */
+static void bnx2i_setup_write_cmd_bd_info(struct bnx2i_cmd *cmd)
+{
+ struct bnx2i_conn *conn = NULL;
+ struct bnx2i_sess *sess = NULL;
+ u32 start_bd_offset = 0;
+ u32 start_bd_idx = 0;
+ u32 buffer_offset = 0;
+ u32 seq_len = 0;
+ u32 fbl = 0, mrdsl = 0;
+ u32 cmd_len = cmd->req.total_data_transfer_length;
+
+ if (cmd)
+ conn = cmd->conn;
+ if (conn->sess)
+ sess = conn->sess;
+
+ /* if ImmediateData is turned off & IntialR2T is turned on,
+ * there will be no immediate or unsolicited data, just return.
+ */
+ if (sess->initial_r2t && !sess->imm_data) {
+ return;
+ }
+ fbl = sess->first_burst_len;
+ mrdsl = conn->max_data_seg_len_xmit;
+
+ /* Immediate data */
+ if (sess->imm_data) {
+ seq_len = min(mrdsl, fbl);
+ seq_len = min(cmd_len, seq_len);
+ buffer_offset += seq_len;
+ }
+ if (seq_len == cmd_len) {
+ return;
+ }
+
+ if (!sess->initial_r2t) {
+ if (seq_len >= fbl)
+ goto r2t_data;
+ seq_len = min(fbl, cmd_len) - seq_len;
+ bnx2i_get_write_cmd_bd_idx(cmd, buffer_offset,
+ &start_bd_offset, &start_bd_idx);
+ cmd->req.ud_buffer_offset = start_bd_offset;
+ cmd->req.ud_start_bd_index = start_bd_idx;
+ buffer_offset += seq_len;
+ }
+r2t_data:
+ if (buffer_offset != cmd_len) {
+ bnx2i_get_write_cmd_bd_idx(cmd, buffer_offset,
+ &start_bd_offset, &start_bd_idx);
+ if ((start_bd_offset > fbl) ||
+ (start_bd_idx > cmd->scsi_cmd->use_sg)) {
+ int i = 0;
+
+ printk(KERN_ALERT "bnx2i- error, buf offset 0x%x "
+ "bd_valid %d use_sg %d\n",
+ buffer_offset, cmd->bd_tbl->bd_valid,
+ cmd->scsi_cmd->use_sg);
+ for (i = 0; i < cmd->bd_tbl->bd_valid; i++)
+ printk(KERN_ALERT "bnx2i err, bd[%d]: len %x\n",
+ i, cmd->bd_tbl->bd_tbl[i].\
+ buffer_length);
+ }
+ cmd->req.sd_buffer_offset = start_bd_offset;
+ cmd->req.sd_start_bd_index = start_bd_idx;
+ }
+}
+
+
+/*
+ */
+static int bnx2i_split_bd(struct bnx2i_cmd *cmd, u64 addr, int sg_len,
+ int bd_index)
+{
+ struct iscsi_bd *bd = cmd->bd_tbl->bd_tbl;
+ int frag_size = 0, sg_frags = 0;
+
+ while (sg_len) {
+ if (sg_len >= BD_SPLIT_SIZE)
+ frag_size = BD_SPLIT_SIZE;
+ else
+ frag_size = sg_len;
+ bd[bd_index + sg_frags].buffer_addr_lo = (u32) addr;
+ bd[bd_index + sg_frags].buffer_addr_hi = addr >> 32;
+ bd[bd_index + sg_frags].buffer_length = frag_size;
+ bd[bd_index + sg_frags].flags = 0;
+ if ((bd_index + sg_frags) == 0)
+ bd[0].flags = ISCSI_BD_FIRST_IN_BD_CHAIN;
+ addr += (u64) frag_size;
+ sg_frags++;
+ sg_len -= frag_size;
+ }
+ return sg_frags;
+}
+
+
+/*
+ * map single buffer
+ */
+static int bnx2i_map_single_buf(struct bnx2i_hba *hba,
+ struct bnx2i_cmd *cmd)
+{
+ struct scsi_cmnd *sc = cmd->scsi_cmd;
+ struct iscsi_bd *bd = cmd->bd_tbl->bd_tbl;
+ int byte_count = 0;
+ int bd_count = 0;
+ u64 addr;
+
+ byte_count = sc->request_bufflen;
+ sc->SCp.dma_handle =
+ pci_map_single(hba->pci_dev, sc->request_buffer,
+ sc->request_bufflen, sc->sc_data_direction);
+ addr = sc->SCp.dma_handle;
+
+ if (byte_count > MAX_BD_LENGTH) {
+ bd_count = bnx2i_split_bd(cmd, addr, byte_count, 0);
+ } else {
+ bd_count = 1;
+ bd[0].buffer_addr_lo = addr & 0xffffffff;
+ bd[0].buffer_addr_hi = addr >> 32;
+ bd[0].buffer_length = sc->request_bufflen;
+ bd[0].flags = ISCSI_BD_FIRST_IN_BD_CHAIN |
+ ISCSI_BD_LAST_IN_BD_CHAIN;
+ }
+ bd[bd_count - 1].flags |= ISCSI_BD_LAST_IN_BD_CHAIN;
+
+ return bd_count;
+}
+
+
+/*
+ * map SG list
+ */
+static int bnx2i_map_sg(struct bnx2i_hba *hba, struct bnx2i_cmd *cmd)
+{
+ struct scsi_cmnd *sc = cmd->scsi_cmd;
+ struct iscsi_bd *bd = cmd->bd_tbl->bd_tbl;
+ struct scatterlist *sg;
+ int byte_count = 0;
+ int sg_frags = 0;
+ int bd_count = 0;
+ int sg_count = 0;
+ int sg_len;
+ u64 addr;
+ int i;
+
+ sg = (struct scatterlist *) sc->request_buffer;
+
+ sg_count = pci_map_sg(hba->pci_dev, sg, sc->use_sg,
+ sc->sc_data_direction);
+
+ for (i = 0; i < sg_count; i++) {
+ sg_len = sg_dma_len(sg);
+ addr = sg_dma_address(sg);
+ if (sg_len > MAX_BD_LENGTH) {
+ sg_frags = bnx2i_split_bd(cmd, addr, sg_len,
+ bd_count);
+ } else {
+ sg_frags = 1;
+ bd[bd_count].buffer_addr_lo = addr & 0xffffffff;
+ bd[bd_count].buffer_addr_hi = addr >> 32;
+ bd[bd_count].buffer_length = sg_len;
+ bd[bd_count].flags = 0;
+ if (bd_count == 0)
+ bd[bd_count].flags =
+ ISCSI_BD_FIRST_IN_BD_CHAIN;
+ }
+ byte_count += sg_len;
+ sg++;
+ bd_count += sg_frags;
+ }
+ bd[bd_count - 1].flags |= ISCSI_BD_LAST_IN_BD_CHAIN;
+
+ BUG_ON(byte_count != sc->request_bufflen);
+ return bd_count;
+}
+
+/*
+ * creates BD list table for the command
+ */
+static int bnx2i_iscsi_map_sg_list(struct bnx2i_cmd *cmd)
+{
+ struct bnx2i_hba *hba = cmd->conn->sess->hba;
+ struct scsi_cmnd *sc = cmd->scsi_cmd;
+ int bd_count = 0;
+
+ if (sc->use_sg)
+ bd_count = bnx2i_map_sg(hba, cmd);
+ else if (sc->request_bufflen)
+ bd_count = bnx2i_map_single_buf(hba, cmd);
+ else {
+ struct iscsi_bd *bd = cmd->bd_tbl->bd_tbl;
+ bd_count = 0;
+ bd[0].buffer_addr_lo = bd[0].buffer_addr_hi = 0;
+ bd[0].buffer_length = bd[0].flags = 0;
+ }
+ cmd->bd_tbl->bd_valid = bd_count;
+ return 0;
+}
+
+
+/*
+ * create BD list table for the command
+ */
+int bnx2i_iscsi_unmap_sg_list(struct bnx2i_cmd *cmd)
+{
+ struct bnx2i_hba *hba = cmd->conn->sess->hba;
+ struct scsi_cmnd *sc = cmd->scsi_cmd;
+ struct pci_dev *pdev = hba->pci_dev;
+ struct scatterlist *sg;
+
+ if (cmd->bd_tbl->bd_valid && sc) {
+ if (sc->use_sg) {
+ sg = (struct scatterlist *) sc->request_buffer;
+ pci_unmap_sg(pdev, sg, sc->use_sg,
+ sc->sc_data_direction);
+ } else {
+ pci_unmap_single(pdev, sc->SCp.dma_handle,
+ sc->request_bufflen,
+ sc->sc_data_direction);
+ }
+ cmd->bd_tbl->bd_valid = 0;
+ }
+ return 0;
+}
+
+
+
+static void bnx2i_setup_cmd_wqe_template(struct bnx2i_cmd *cmd)
+{
+ memset(&cmd->req, 0x00, sizeof(cmd->req));
+ cmd->req.op_code = ISCSI_OPCODE_SCSI_CMD;
+ cmd->req.bd_list_addr_lo = (u32) cmd->bd_tbl->bd_tbl_dma;
+ cmd->req.bd_list_addr_hi =
+ (u32) ((u64) cmd->bd_tbl->bd_tbl_dma >> 32);
+
+}
+
+
+/*
+ * update iscsi cid table entry with connection pointer
+ */
+static void bnx2i_bind_conn_to_iscsi_cid(struct bnx2i_conn *conn,
+ u32 iscsi_cid)
+{
+ struct bnx2i_hba *hba = NULL;
+
+ if (!conn || !conn->sess)
+ return;
+
+ hba = conn->sess->hba;
+
+ if (hba->cid_que.conn_cid_tbl[iscsi_cid])
+ printk(KERN_ERR "bnx2i: conn bind - entry #%d not free\n",
+ iscsi_cid);
+ hba->cid_que.conn_cid_tbl[iscsi_cid] = conn;
+}
+
+
+/*
+ * maps an iscsi cid to corresponding conn ptr
+ */
+struct bnx2i_conn *bnx2i_get_conn_from_id(struct bnx2i_hba *hba,
+ u16 iscsi_cid)
+{
+ if (!hba->cid_que.conn_cid_tbl) {
+ printk(KERN_ERR "bnx2i: ERROR - missing conn<->cid table\n");
+ return NULL;
+
+ } else if (iscsi_cid >= hba->max_active_conns) {
+ printk(KERN_ERR "bnx2i: wrong cid #%d\n", iscsi_cid);
+ return NULL;
+ }
+ return(hba->cid_que.conn_cid_tbl[iscsi_cid]);
+}
+
+
+
+/*
+ * allocates a iscsi_cid from free pool
+ */
+static u32 bnx2i_alloc_iscsi_cid(struct bnx2i_hba *hba)
+{
+ int idx = 0;
+
+ if (!hba->cid_que.cid_free_cnt)
+ return (ISCSI_RESERVED_TAG);
+
+ idx = hba->cid_que.cid_q_cons_idx;
+ hba->cid_que.cid_q_cons_idx++;
+ if (hba->cid_que.cid_q_cons_idx == hba->cid_que.cid_q_max_idx) {
+ hba->cid_que.cid_q_cons_idx = 0;
+ }
+
+ hba->cid_que.cid_free_cnt--;
+ return hba->cid_que.cid_que[idx];
+}
+
+
+/*
+ * return iscsi_cid back to free pool
+ */
+static void bnx2i_free_iscsi_cid(struct bnx2i_hba *hba, u16 iscsi_cid)
+{
+ int idx = 0;
+
+ if (iscsi_cid == (u16)ISCSI_RESERVED_TAG)
+ return;
+
+ hba->cid_que.cid_free_cnt++;
+
+ idx = hba->cid_que.cid_q_prod_idx;
+ hba->cid_que.cid_que[idx] = iscsi_cid;
+ hba->cid_que.conn_cid_tbl[iscsi_cid] = NULL;
+ hba->cid_que.cid_q_prod_idx++;
+ if (hba->cid_que.cid_q_prod_idx == hba->cid_que.cid_q_max_idx) {
+ hba->cid_que.cid_q_prod_idx = 0;
+ }
+}
+
+
+
+/*
+ * setup iscsi_cid queue, 'iscsi_cid' value ranges from 0 to (MAX_CONNS -1)
+ */
+static int bnx2i_setup_free_cid_que(struct bnx2i_hba *hba)
+{
+ int mem_size;
+ int i = 0;
+
+ mem_size = hba->max_active_conns * sizeof(u16);
+ mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+
+ hba->cid_que.cid_que_base = kmalloc(mem_size, GFP_KERNEL);
+ if (!hba->cid_que.cid_que_base)
+ return -ENOMEM;
+
+ mem_size = hba->max_active_conns * sizeof(struct bnx2i_conn *);
+ mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ hba->cid_que.conn_cid_tbl =
+ (struct bnx2i_conn **)kmalloc(mem_size, GFP_KERNEL);
+ if (!hba->cid_que.conn_cid_tbl) {
+ kfree(hba->cid_que.cid_que_base);
+ hba->cid_que.cid_que_base = NULL;
+ }
+
+ hba->cid_que.cid_que = (u32 *)hba->cid_que.cid_que_base;
+ hba->cid_que.cid_q_prod_idx = 0;
+ hba->cid_que.cid_q_cons_idx = 0;
+ hba->cid_que.cid_q_max_idx = hba->max_active_conns;
+ hba->cid_que.cid_free_cnt = hba->max_active_conns;
+
+ for (i = 0; i < hba->max_active_conns; i++) {
+ hba->cid_que.cid_que[i] = i;
+ hba->cid_que.conn_cid_tbl[i] = NULL;
+ }
+ return 0;
+}
+
+
+/*
+ * Releases resources held by free 'iscsi_cid' queue
+ */
+static void bnx2i_release_free_cid_que(struct bnx2i_hba *hba)
+{
+ if (hba->cid_que.cid_que_base) {
+ kfree(hba->cid_que.cid_que_base);
+ hba->cid_que.cid_que_base = NULL;
+ }
+
+ if (hba->cid_que.conn_cid_tbl) {
+ kfree(hba->cid_que.conn_cid_tbl);
+ hba->cid_que.conn_cid_tbl = NULL;
+ }
+}
+
+
+/*
+ * routine allocates a free endpoint structure from the global pool
+ */
+struct bnx2i_endpoint *bnx2i_alloc_ep(void)
+{
+ struct bnx2i_endpoint *endpoint = NULL;
+ struct list_head *listp;
+ u16 tcp_port;
+
+ spin_lock_bh(&bnx2i_resc_lock);
+
+ tcp_port = bnx2i_alloc_tcp_port();
+ if (!tcp_port) {
+ spin_unlock_bh(&bnx2i_resc_lock);
+ return NULL;
+ }
+ if (list_empty(&bnx2i_free_ep_list)) {
+ spin_unlock_bh(&bnx2i_resc_lock);
+ printk(KERN_ERR "alloc_ep: unable to alloc ep struct\n");
+ return endpoint;
+ }
+ listp = (struct list_head *)bnx2i_free_ep_list.next;
+ list_del_init(listp);
+ bnx2i_num_free_ep--;
+
+ endpoint = (struct bnx2i_endpoint *)listp;
+ endpoint->in_use = 1;
+ endpoint->tcp_port = tcp_port;
+ init_waitqueue_head(&endpoint->ofld_wait);
+
+ spin_unlock_bh(&bnx2i_resc_lock);
+ return endpoint;
+}
+
+
+/*
+ * free endpoint structure to global free pool
+ */
+void bnx2i_free_ep(struct bnx2i_endpoint *endpoint)
+{
+ if (!endpoint)
+ return;
+
+ spin_lock_bh(&bnx2i_resc_lock);
+ endpoint->state = EP_STATE_IDLE;
+ endpoint->in_use = 0;
+ bnx2i_free_iscsi_cid(endpoint->hba, endpoint->ep_iscsi_cid);
+ if (endpoint->conn) {
+ endpoint->conn->ep = NULL;
+ endpoint->conn = NULL;
+ }
+ endpoint->sess = NULL;
+
+ if (endpoint->tcp_port) {
+ bnx2i_free_tcp_port(endpoint->tcp_port);
+ }
+ endpoint->hba = NULL;
+ list_add_tail(&endpoint->link, &bnx2i_free_ep_list);
+ bnx2i_num_free_ep++;
+ spin_unlock_bh(&bnx2i_resc_lock);
+}
+
+
+/*
+ * allocates free pool of endpoint structurres, endpoint structures
+ * are used to store QP related control and PT info
+ */
+int bnx2i_alloc_ep_pool(void)
+{
+ struct bnx2i_endpoint *endpoint = NULL;
+ int index = 0, count = 0;
+ int ret_val = 1;
+ int total_endpoints = 0;
+ int page_count = 0;
+ int num_endpoints_per_page = 0;
+ void *mem_ptr = NULL;
+
+ spin_lock_init(&bnx2i_resc_lock);
+ INIT_LIST_HEAD(&bnx2i_free_ep_list);
+ INIT_LIST_HEAD(&bnx2i_unbound_ep);
+
+ for (index = 0; index < MAX_PAGES_PER_CTRL_STRUCT_POOL; index++) {
+ bnx2i_ep_pages[index] = NULL;
+ }
+
+ num_endpoints_per_page =
+ PAGE_SIZE / sizeof(struct bnx2i_endpoint);
+
+ total_endpoints = ISCSI_MAX_CONNS_PER_HBA;
+ if (total_endpoints >
+ (num_endpoints_per_page * MAX_PAGES_PER_CTRL_STRUCT_POOL)) {
+ total_endpoints = (num_endpoints_per_page *
+ MAX_PAGES_PER_CTRL_STRUCT_POOL);
+ }
+
+ bnx2i_num_free_ep = 0;
+ for (index = 0; index < total_endpoints;) {
+ mem_ptr = (void *)kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (mem_ptr == NULL) {
+ printk(KERN_ERR "ep_pool: mem alloc failed\n");
+ break;
+ }
+ bnx2i_ep_pages[page_count++] = (void *)mem_ptr;
+
+ memset(mem_ptr, 0, PAGE_SIZE);
+
+ endpoint = (struct bnx2i_endpoint *)mem_ptr;
+ for (count = 0; count < num_endpoints_per_page; count++) {
+ endpoint->in_use = 0;
+ list_add_tail(&endpoint->link, &bnx2i_free_ep_list);
+ endpoint++;
+ }
+
+ bnx2i_num_free_ep += num_endpoints_per_page;
+ index += num_endpoints_per_page;
+ }
+ if (bnx2i_num_free_ep == 0)
+ ret_val = 0;
+ bnx2i_max_free_ep = bnx2i_num_free_ep;
+
+ return(ret_val);
+}
+
+
+/*
+ * Free memory resources held by global endpoint pool
+ */
+void bnx2i_release_ep_pool(void)
+{
+ int index = 0;
+ void *mem_ptr = NULL;
+
+ for (index = 0; index < MAX_PAGES_PER_CTRL_STRUCT_POOL; index++) {
+ mem_ptr = bnx2i_ep_pages[index];
+ if (mem_ptr) {
+ kfree((void *) mem_ptr);
+ break;
+ }
+ bnx2i_ep_pages[index] = NULL;
+ }
+ bnx2i_num_free_ep = 0;
+ return;
+}
+
+
+/*
+ * iSCSI Session ITT queue management code
+ */
+static u32 bnx2i_alloc_itt(struct bnx2i_sess *sess, struct bnx2i_cmd *cmd)
+{
+ u32 itt_val = ITT_INVALID_SIGNATURE;
+
+ if (sess->itt_q.itt_q_count) {
+ itt_val = sess->itt_q.itt_que[sess->itt_q.itt_q_cons_idx++];
+ sess->itt_q.itt_q_cons_idx %= sess->itt_q.itt_q_max_idx;
+ sess->itt_q.itt_cmd[itt_val] = cmd;
+ sess->itt_q.itt_q_count--;
+ }
+ return itt_val;
+}
+
+
+static void bnx2i_free_itt(struct bnx2i_sess *sess, struct bnx2i_cmd *cmd)
+{
+ if (cmd->req.itt == ITT_INVALID_SIGNATURE) {
+ printk(KERN_ALERT "free_itt: RSVD ITT - sess 0x%p\n", sess);
+ }
+ sess->itt_q.itt_que[sess->itt_q.itt_q_prod_idx++] = cmd->req.itt;
+ sess->itt_q.itt_q_prod_idx %= sess->itt_q.itt_q_max_idx;
+ sess->itt_q.itt_cmd[cmd->req.itt] = NULL;
+ sess->itt_q.itt_q_count++;
+ cmd->req.itt = ITT_INVALID_SIGNATURE;
+}
+
+
+/*
+ * setup ITT queue during iSCSI session creation. ITT queue is a
+ * circular array of ITTs [range 0 - (SQ SIZE - 1)] managed by
+ * producer and consumer index
+ */
+static int bnx2i_setup_free_itt_queue(struct bnx2i_sess *sess)
+{
+ u16 itt_q_size = (u16)sess->sq_size;
+ u32 itt_value = 0;
+ int unit_size = sizeof(u16);
+ int mem_size = PAGE_SIZE;
+
+ if ((itt_q_size * unit_size) > mem_size)
+ mem_size = (itt_q_size * unit_size);
+
+ mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ sess->itt_q.itt_que_base = kmalloc(mem_size, GFP_KERNEL);
+ if (!sess->itt_q.itt_que_base) {
+ return -ENOMEM;
+ }
+
+ mem_size = (itt_q_size * sizeof(struct bnx2i_cmd *));
+ mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
+ sess->itt_q.itt_cmd =
+ (struct bnx2i_cmd **) kmalloc(mem_size, GFP_KERNEL);
+ if (!sess->itt_q.itt_cmd) {
+ kfree(sess->itt_q.itt_que_base);
+ sess->itt_q.itt_que_base = NULL;
+ return -1;
+ }
+ memset(sess->itt_q.itt_cmd, 0x00, mem_size);
+
+ sess->itt_q.itt_que = (u32 *)sess->itt_q.itt_que_base;
+ sess->itt_q.itt_q_prod_idx = 0;
+ sess->itt_q.itt_q_cons_idx = 0;
+ sess->itt_q.itt_q_max_idx = itt_q_size;
+ sess->itt_q.itt_q_count = itt_q_size;
+
+ itt_value = 0;
+ while (itt_value < itt_q_size) {
+ sess->itt_q.itt_cmd[itt_value] = (struct bnx2i_cmd *)NULL;
+ sess->itt_q.itt_que[sess->itt_q.itt_q_prod_idx++] =
+ itt_value++;
+ if (sess->itt_q.itt_q_prod_idx >= sess->itt_q.itt_q_max_idx) {
+ sess->itt_q.itt_q_prod_idx = 0;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * free resources held by free ITT queue
+ */
+static void bnx2i_release_free_itt_queue(struct bnx2i_sess *sess)
+{
+ sess->itt_q.itt_q_count = 0;
+ if (sess->itt_q.itt_que_base) {
+ kfree (sess->itt_q.itt_que_base);
+ sess->itt_q.itt_que_base = NULL;
+ }
+
+ if (sess->itt_q.itt_cmd) {
+ kfree (sess->itt_q.itt_cmd);
+ sess->itt_q.itt_cmd = NULL;
+ }
+ return;
+}
+
+
+/*
+ * allocates a command structures from free poll
+ */
+struct bnx2i_cmd *bnx2i_alloc_cmd(struct bnx2i_sess *sess)
+{
+ struct bnx2i_cmd *cmd = NULL;
+ struct list_head *listp;
+
+ if (unlikely(!sess || (sess->num_free_cmds == 0))) {
+ return cmd;
+ }
+
+ listp = (struct list_head *) sess->free_cmds.next;
+ list_del_init(listp);
+ sess->num_free_cmds--;
+ cmd = (struct bnx2i_cmd *)listp;
+ cmd->in_use = 1;
+ cmd->scsi_status_rcvd = cmd->resi_len = 0;
+ cmd->scsi_uflow = cmd->scsi_oflow = 0;
+
+ bnx2i_setup_cmd_wqe_template(cmd);
+
+ cmd->req.itt = bnx2i_alloc_itt(sess, cmd);
+
+ return cmd;
+}
+
+
+/*
+ * return command structure and ITT back to free pool.
+ */
+void bnx2i_free_cmd(struct bnx2i_sess *sess, struct bnx2i_cmd *cmd)
+{
+ if (!sess || !cmd)
+ return;
+
+ cmd->in_use = 0;
+ bnx2i_free_itt(sess, cmd);
+ list_add_tail(&cmd->link, &sess->free_cmds);
+ sess->num_free_cmds++;
+}
+
+
+/*
+ * Allocate command structure pool for a given iSCSI session
+ */
+int bnx2i_alloc_cmd_pool(struct bnx2i_sess *sess)
+{
+ struct bnx2i_cmd *cmdp = NULL;
+ int index = 0, count = 0;
+ int ret_val = 0;
+ int total_cmds = 0;
+ int num_cmds = 0;
+ int page_count = 0;
+ int num_cmds_per_page = 0;
+ void *mem_ptr = NULL;
+
+ if (!sess)
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&sess->free_cmds);
+ for (index = 0; index < MAX_PAGES_PER_CTRL_STRUCT_POOL; index++) {
+ sess->cmd_pages[index] = NULL;
+ }
+
+ num_cmds_per_page = PAGE_SIZE / sizeof(struct bnx2i_cmd);
+ total_cmds = sess->hba->scsi_template->can_queue + 1;
+ if (total_cmds >
+ (num_cmds_per_page * MAX_PAGES_PER_CTRL_STRUCT_POOL)) {
+ total_cmds = num_cmds_per_page *
+ MAX_PAGES_PER_CTRL_STRUCT_POOL;
+ }
+
+ for (index = 0; index < total_cmds;) {
+ mem_ptr = (void *) kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (mem_ptr == NULL) {
+ break;
+ }
+ sess->cmd_pages[page_count++] = (void *)mem_ptr;
+
+ num_cmds = num_cmds_per_page;
+ if ((total_cmds - index) < num_cmds_per_page)
+ num_cmds = (total_cmds - index);
+
+ memset(mem_ptr, 0, PAGE_SIZE);
+ cmdp = (struct bnx2i_cmd *) mem_ptr;
+ for (count = 0; count < num_cmds; count++) {
+ cmdp->in_use = 0;
+ cmdp->req.itt = ITT_INVALID_SIGNATURE;
+
+ /* Allocate BD table */
+ cmdp->bd_tbl = bnx2i_alloc_bd_table(sess, cmdp);
+ if (!cmdp->bd_tbl) {
+ /* should never fail, as it's guaranteed to have
+ * (ISCSI_MAX_CMDS_PER_SESS + 1) BD tables
+ * allocated before calling this function.
+ */
+ printk(KERN_ERR "no BD table cmd %p\n", cmdp);
+ goto bd_table_failed;
+ }
+ list_add_tail(&cmdp->link, &sess->free_cmds);
+ cmdp++;
+ }
+
+ sess->num_free_cmds += num_cmds;
+ index += num_cmds;
+ }
+ sess->allocated_cmds = sess->num_free_cmds;
+
+ if (sess->num_free_cmds == 0)
+ ret_val = -ENOMEM;
+ return(ret_val);
+
+bd_table_failed:
+ return(-ENOMEM);
+}
+
+
+/*
+ * Release memory held by command struct pool.
+ */
+void bnx2i_free_cmd_pool(struct bnx2i_sess *sess)
+{
+ int index = 0;
+ void *mem_ptr = NULL;
+
+ if (unlikely(!sess))
+ return;
+
+ if (sess->num_free_cmds != sess->allocated_cmds) {
+ /*
+ * WARN: either there is some command struct leak or
+ * still some SCSI commands are pending.
+ * TODO: post mortem required...
+ */
+ }
+ for (index = 0; index < MAX_PAGES_PER_CTRL_STRUCT_POOL; index++) {
+ mem_ptr = sess->cmd_pages[index];
+ if (mem_ptr) {
+ kfree((void *) mem_ptr);
+ break;
+ }
+ sess->cmd_pages[index] = NULL;
+ }
+ sess->num_free_cmds = sess->allocated_cmds = 0;
+ return;
+}
+
+
+/*
+ * Allocate a BD table
+ */
+static struct io_bdt *bnx2i_alloc_bd_table(struct bnx2i_sess *sess,
+ struct bnx2i_cmd *cmd)
+{
+ struct io_bdt *bd_tbl = NULL;
+
+ if (list_empty(&sess->bd_tbl_list)) {
+ return NULL;
+ }
+ bd_tbl = (struct io_bdt *)sess->bd_tbl_list.next;
+ list_del(&bd_tbl->link);
+ list_add_tail(&bd_tbl->link, &sess->bd_tbl_active);
+ bd_tbl->bd_valid = 0;
+ if (!bd_tbl->cmdp) {
+ bd_tbl->cmdp = cmd;
+ }
+ return bd_tbl;
+}
+
+
+/*
+ * Free up memory pages allocated held by BD resources
+ */
+static void bnx2i_free_all_bdt_resc_pages(struct bnx2i_sess *sess)
+{
+ int i = 0;
+ struct bd_resc_page *resc_page = NULL;
+
+ spin_lock_bh(&sess->lock);
+ while (!list_empty(&sess->bd_resc_page)) {
+ resc_page = (struct bd_resc_page *)sess->bd_resc_page.prev;
+ list_del(sess->bd_resc_page.prev);
+ for(i = 0; i < resc_page->num_valid; i++) {
+ kfree(resc_page->page[i]);
+ }
+ kfree(resc_page);
+ }
+ spin_unlock_bh(&sess->lock);
+}
+
+
+
+/*
+ * allocated 4K page to track BD table memory
+ */
+struct bd_resc_page *bnx2i_alloc_bdt_resc_page(struct bnx2i_sess *sess)
+{
+ void *mem_ptr;
+ struct bd_resc_page *resc_page = NULL;
+
+ mem_ptr = (void *) kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!mem_ptr)
+ return NULL;
+
+ resc_page = (struct bd_resc_page *) mem_ptr;
+ list_add_tail(&resc_page->link, &sess->bd_resc_page);
+ resc_page->max_ptrs = (PAGE_SIZE -
+ (u32)&((struct bd_resc_page *) 0)->page[0]) / sizeof(void *);
+ resc_page->num_valid = 0;
+
+ return resc_page;
+}
+
+
+/*
+ * link newly allocated memory page to the list
+ */
+int bnx2i_add_bdt_resc_page(struct bnx2i_sess *sess, void *bd_page)
+{
+ struct bd_resc_page *resc_page = NULL;
+
+#define is_resc_page_full(_resc_pg) (_resc_pg->num_valid == _resc_pg->max_ptrs)
+#define active_resc_page(_resc_list) \
+ (list_empty(_resc_list) ? NULL : (_resc_list)->prev)
+ if (list_empty(&sess->bd_resc_page)) {
+ resc_page = bnx2i_alloc_bdt_resc_page(sess);
+ } else {
+ resc_page = (struct bd_resc_page *)
+ active_resc_page(&sess->bd_resc_page);
+ }
+
+ if (!resc_page)
+ return -ENOMEM;
+
+ resc_page->page[resc_page->num_valid++] = bd_page;
+ if (is_resc_page_full(resc_page)) {
+ resc_page = bnx2i_alloc_bdt_resc_page(sess);
+ }
+ return 0;
+}
+
+
+/*
+ * Allocate BD table pool, DMA'able memory for a given session.
+ */
+int bnx2i_alloc_bd_table_pool(struct bnx2i_sess *sess)
+{
+ int index = 0, count = 0;
+ int ret_val = 0;
+ int num_elem_per_page;
+ struct io_bdt *bdt_info;
+ char *mem_ptr = NULL;
+ u32 bd_tbl_size = 0;
+ u32 mem_size = 0;
+ int total_bd_tbl = 0;
+
+ INIT_LIST_HEAD(&sess->bd_resc_page);
+ INIT_LIST_HEAD(&sess->bd_tbl_list);
+ INIT_LIST_HEAD(&sess->bd_tbl_active);
+ total_bd_tbl = sess->hba->scsi_template->can_queue + 1;
+ mem_size = total_bd_tbl * sizeof(struct io_bdt);
+ num_elem_per_page = PAGE_SIZE / sizeof(struct io_bdt);
+ for (index = 0; index < total_bd_tbl; index += num_elem_per_page) {
+ if (((total_bd_tbl - index) * sizeof(struct io_bdt))
+ >= PAGE_SIZE) {
+ mem_size = PAGE_SIZE;
+ num_elem_per_page = PAGE_SIZE / sizeof(struct io_bdt);
+ } else {
+ mem_size =
+ (total_bd_tbl - index) * sizeof(struct io_bdt);
+ num_elem_per_page = (total_bd_tbl - index);
+ }
+ mem_ptr = (void *)kmalloc(mem_size, GFP_KERNEL);
+ if (mem_ptr == NULL) {
+ printk(KERN_ERR "alloc_bd_tbl: mem alloc failed\n");
+ ret_val = -ENOMEM;
+ goto resc_alloc_failed;
+ }
+ bnx2i_add_bdt_resc_page(sess, mem_ptr);
+
+ memset(mem_ptr, 0, mem_size);
+ bdt_info = (struct io_bdt *)mem_ptr;
+ for (count = 0; count < num_elem_per_page; count++) {
+ list_add_tail(&bdt_info->link, &sess->bd_tbl_list);
+ bdt_info++;
+ }
+ }
+
+ bd_tbl_size = ISCSI_MAX_BDS_PER_CMD * sizeof(struct iscsi_bd);
+ bdt_info = (struct io_bdt *)sess->bd_tbl_list.next;
+ while (bdt_info && (bdt_info != (struct io_bdt *)&sess->bd_tbl_list)) {
+ mem_ptr = (char *)pci_alloc_consistent(sess->hba->pci_dev,
+ bd_tbl_size,
+ &bdt_info->bd_tbl_dma);
+ if (!mem_ptr) {
+ printk(KERN_ERR "bd_tbl: DMA mem alloc failed\n");
+ ret_val = -ENOMEM;
+ goto dma_alloc_failed;
+ }
+ bdt_info->bd_tbl = (struct iscsi_bd *)mem_ptr;
+ bdt_info->max_bd_cnt = ISCSI_MAX_BDS_PER_CMD;
+ bdt_info->bd_valid = 0;
+ bdt_info->cmdp = NULL;
+
+ bdt_info = (struct io_bdt *)bdt_info->link.next;
+ }
+ return(ret_val);
+
+resc_alloc_failed:
+dma_alloc_failed:
+ return(ret_val);
+}
+
+
+/*
+ * releases BD table pool memory
+ */
+void bnx2i_free_bd_table_pool(struct bnx2i_sess *sess)
+{
+ struct list_head *list;
+ struct io_bdt *bdt_info;
+ u32 bd_tbl_size = 0;
+
+ bd_tbl_size = ISCSI_MAX_BDS_PER_CMD * sizeof(struct iscsi_bd);
+ list_for_each(list, &sess->bd_tbl_list) {
+ bdt_info = list_entry(list, struct io_bdt, link);
+ pci_free_consistent(sess->hba->pci_dev, bd_tbl_size,
+ (void *)bdt_info->bd_tbl,
+ bdt_info->bd_tbl_dma);
+ bdt_info->bd_tbl = NULL;
+ if (bdt_info->cmdp) {
+ bdt_info->cmdp->bd_tbl = NULL;
+ bdt_info->cmdp = NULL;
+ }
+ }
+
+ list_for_each(list, &sess->bd_tbl_active) {
+ bdt_info = list_entry(list, struct io_bdt, link);
+ pci_free_consistent(sess->hba->pci_dev, bd_tbl_size,
+ (void *)bdt_info->bd_tbl,
+ bdt_info->bd_tbl_dma);
+ bdt_info->bd_tbl = NULL;
+ if (bdt_info->cmdp) {
+ bdt_info->cmdp->bd_tbl = NULL;
+ bdt_info->cmdp = NULL;
+ }
+ }
+}
+
+
+/*
+ * allocate memory for dummy buffer and associated BD table
+ * to be used by middle path (MP) requests
+ */
+static int bnx2i_setup_mp_bdt(struct bnx2i_hba *hba)
+{
+ int rc = 0;
+ struct iscsi_bd *mp_bdt;
+ u64 addr;
+ hba->mp_bd_tbl = NULL;
+ if (hba->cnic_dev_type == CNIC_10GIG_GEN1)
+ return rc;
+
+ hba->mp_bd_tbl = pci_alloc_consistent(hba->pci_dev,
+ PAGE_SIZE, &hba->mp_bd_dma);
+ if (!hba->mp_bd_tbl) {
+ printk(KERN_ERR "unable to allocate Middle Path BDT\n");
+ rc = -1;
+ goto out;
+ }
+
+ hba->dummy_buffer =
+ pci_alloc_consistent(hba->pci_dev,
+ PAGE_SIZE, &hba->dummy_buf_dma);
+ if (!hba->dummy_buffer) {
+ printk(KERN_ERR "unable to alloc Middle Path Dummy Buffer\n");
+ pci_free_consistent(hba->pci_dev, PAGE_SIZE,
+ hba->mp_bd_tbl, hba->mp_bd_dma);
+ hba->mp_bd_tbl = NULL;
+ rc = -1;
+ goto out;
+ }
+
+ mp_bdt = (struct iscsi_bd *)hba->mp_bd_tbl;
+ addr = (unsigned long)hba->dummy_buf_dma;
+ mp_bdt->buffer_addr_lo = addr & 0xffffffff;
+ mp_bdt->buffer_addr_hi = addr >> 32;
+ mp_bdt->buffer_length = PAGE_SIZE;
+ mp_bdt->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
+ ISCSI_BD_FIRST_IN_BD_CHAIN;
+
+out:
+ return rc;
+}
+
+
+/*
+ * free MP dummy buffer and associated BD table
+ */
+static void bnx2i_free_mp_bdt(struct bnx2i_hba *hba)
+{
+
+ if (hba->mp_bd_tbl) {
+ pci_free_consistent(hba->pci_dev, PAGE_SIZE,
+ hba->mp_bd_tbl, hba->mp_bd_dma);
+ hba->mp_bd_tbl = NULL;
+ }
+ if (hba->dummy_buffer) {
+ pci_free_consistent(hba->pci_dev, PAGE_SIZE,
+ hba->dummy_buffer, hba->dummy_buf_dma);
+ hba->dummy_buffer = NULL;
+ }
+ return;
+}
+
+
+static u16 bnx2i_alloc_tcp_port()
+{
+ return bnx2i_local_tcp_port++;
+}
+
+
+/*
+ * Function : bnx2i_free_tcp_port
+ * Description:
+ */
+static void bnx2i_free_tcp_port(u16 port)
+{
+ if (!bnx2i_tcp_port_tbl.free_q)
+ return;
+
+ bnx2i_tcp_port_tbl.free_q[bnx2i_tcp_port_tbl.prod_idx] = port;
+ bnx2i_tcp_port_tbl.prod_idx++;
+ bnx2i_tcp_port_tbl.prod_idx %= bnx2i_tcp_port_tbl.max_idx;
+ bnx2i_tcp_port_tbl.num_free_ports++;
+}
+
+void bnx2i_tcp_port_new_entry(u16 tcp_port)
+{
+ u32 idx = bnx2i_tcp_port_tbl.prod_idx;
+
+ spin_lock(&bnx2i_resc_lock);
+ bnx2i_tcp_port_tbl.free_q[idx] = (u16)tcp_port;
+ bnx2i_tcp_port_tbl.prod_idx++;
+ bnx2i_tcp_port_tbl.prod_idx %= bnx2i_tcp_port_tbl.max_idx;
+ bnx2i_tcp_port_tbl.num_free_ports++;
+ bnx2i_tcp_port_tbl.num_required--;
+ spin_unlock(&bnx2i_resc_lock);
+}
+
+/*
+ * Function : bnx2i_init_tcp_port_mngr
+ * Description:
+ */
+void bnx2i_init_tcp_port_mngr(void)
+{
+ int mem_size = 0;
+
+ bnx2i_tcp_port_tbl.num_free_ports = 0;
+ bnx2i_tcp_port_tbl.prod_idx = 0;
+ bnx2i_tcp_port_tbl.cons_idx = 0;
+ bnx2i_tcp_port_tbl.max_idx = 0;
+ bnx2i_tcp_port_tbl.num_required = 0;
+
+#define BNX2I_MAX_TCP_PORTS 1024
+
+ bnx2i_tcp_port_tbl.port_tbl_size = BNX2I_MAX_TCP_PORTS;
+
+ mem_size = sizeof(u16) * bnx2i_tcp_port_tbl.port_tbl_size;
+ if (bnx2i_tcp_port_tbl.port_tbl_size) {
+ bnx2i_tcp_port_tbl.free_q =
+ (u16 *)kmalloc(mem_size, GFP_KERNEL);
+
+ if (bnx2i_tcp_port_tbl.free_q)
+ bnx2i_tcp_port_tbl.max_idx =
+ bnx2i_tcp_port_tbl.port_tbl_size;
+ }
+}
+
+
+/*
+ * Function : bnx2i_cleanup_tcp_port_mngr
+ * Description:
+ */
+void bnx2i_cleanup_tcp_port_mngr(void)
+{
+ if (bnx2i_tcp_port_tbl.free_q) {
+ kfree(bnx2i_tcp_port_tbl.free_q);
+ bnx2i_tcp_port_tbl.free_q = NULL;
+ }
+ bnx2i_tcp_port_tbl.num_free_ports = 0;
+}
+
+
+
+/*
+ * interface was brought down by the user, fail all iSCSI sessions
+ * on this adapter,
+ */
+void bnx2i_start_iscsi_hba_shutdown(struct bnx2i_hba *hba)
+{
+ struct list_head *list = NULL;
+ struct list_head *tmp = NULL;
+ struct bnx2i_sess *sess;
+
+ list_for_each_safe(list, tmp, &hba->active_sess) {
+ sess = (struct bnx2i_sess *)list;
+ bnx2i_do_iscsi_sess_recovery(sess, DID_NO_CONNECT);
+ }
+}
+
+
+/*
+ * IP address change indication, fail all iSCSI sessions on this adapter
+ */
+void bnx2i_iscsi_handle_ip_event(struct bnx2i_hba *hba)
+{
+ struct list_head *list = NULL;
+ struct list_head *tmp = NULL;
+ struct bnx2i_sess *sess;
+
+ spin_lock(&hba->lock);
+ list_for_each_safe(list, tmp, &hba->active_sess) {
+ sess = (struct bnx2i_sess *)list;
+ spin_unlock(&hba->lock);
+ bnx2i_do_iscsi_sess_recovery(sess, DID_RESET);
+ spin_lock(&hba->lock);
+ }
+ spin_unlock(&hba->lock);
+}
+
+
+
+static void
+conn_err_recovery_task(struct work_struct *work)
+{
+ struct bnx2i_hba *hba = container_of(work, struct bnx2i_hba,
+ err_rec_task);
+ struct bnx2i_sess *sess;
+ int cons_idx = hba->sess_recov_cons_idx;
+
+ while (hba->sess_recov_prod_idx != cons_idx) {
+ sess = hba->sess_recov_list[cons_idx];
+ bnx2i_do_iscsi_sess_recovery(sess, DID_RESET);
+ if (cons_idx == hba->sess_recov_max_idx)
+ cons_idx = 0;
+ else
+ cons_idx++;
+ }
+ hba->sess_recov_cons_idx = cons_idx;
+}
+
+
+
+
+/*
+ * allocate memory buffer to extract conn context
+ */
+static void bnx2i_init_ctx_dump_mem(struct bnx2i_hba *hba)
+{
+ if (hba->ctx_addr)
+ return;
+
+ hba->ictx_poll_mode = 0;
+ hba->ctx_size = 0;
+ hba->ctx_read_cnt = 0xffffffff;
+ hba->ctx_addr = pci_alloc_consistent(hba->pci_dev,
+ BNX2I_CONN_CTX_BUF_SIZE,
+ &hba->ctx_dma_hndl);
+ if (!hba->ctx_addr)
+ return;
+ hba->ctx_size = BNX2I_CONN_CTX_BUF_SIZE;
+}
+
+
+/*
+ * free context memory buffer
+ */
+static void bnx2i_free_ctx_dump_mem(struct bnx2i_hba *hba)
+{
+ if (!hba->ctx_addr || (hba->ctx_size == 0))
+ return;
+
+ pci_free_consistent(hba->pci_dev, hba->ctx_size,
+ hba->ctx_addr, hba->ctx_dma_hndl);
+ hba->ctx_dma_hndl = 0;
+ hba->ctx_addr = NULL;
+ hba->ctx_size = 0;
+}
+
+
+static int bnx2i_ep_destroy_list_add(struct bnx2i_hba *hba,
+ struct bnx2i_endpoint *ep)
+{
+ int cur_idx;
+
+ write_lock(&hba->ep_rdwr_lock);
+ cur_idx = hba->ep_destroy_prod_idx++;
+ hba->ep_destroy_list[cur_idx] = ep;
+ hba->ep_destroy_prod_idx %= hba->ep_destroy_max_idx;
+ write_unlock(&hba->ep_rdwr_lock);
+ return 0;
+}
+
+struct bnx2i_endpoint *bnx2i_ep_destroy_list_next(struct bnx2i_hba *hba)
+{
+ int cur_idx;
+
+ read_lock(&hba->ep_rdwr_lock);
+ if (hba->ep_destroy_prod_idx == hba->ep_destroy_cons_idx) {
+ read_unlock(&hba->ep_rdwr_lock);
+ return NULL;
+ }
+ cur_idx = hba->ep_destroy_cons_idx++;
+ hba->ep_destroy_cons_idx %= hba->ep_destroy_max_idx;
+ read_unlock(&hba->ep_rdwr_lock);
+
+ return (hba->ep_destroy_list[cur_idx]);
+}
+
+static int bnx2i_ep_ofld_list_add(struct bnx2i_hba *hba,
+ struct bnx2i_endpoint *ep)
+{
+ int cur_idx;
+
+ write_lock(&hba->ep_rdwr_lock);
+ cur_idx = hba->ep_ofld_prod_idx++;
+ hba->ep_ofld_list[cur_idx] = ep;
+ hba->ep_ofld_prod_idx %= hba->ep_ofld_max_idx;
+ write_unlock(&hba->ep_rdwr_lock);
+ return 0;
+}
+
+struct bnx2i_endpoint *bnx2i_ep_ofld_list_next(struct bnx2i_hba *hba)
+{
+ int cur_idx;
+
+ read_lock(&hba->ep_rdwr_lock);
+ if (hba->ep_ofld_prod_idx == hba->ep_ofld_cons_idx) {
+ read_unlock(&hba->ep_rdwr_lock);
+ return NULL;
+ }
+ cur_idx = hba->ep_ofld_cons_idx++;
+ hba->ep_ofld_cons_idx %= hba->ep_ofld_max_idx;
+ read_unlock(&hba->ep_rdwr_lock);
+
+ return (hba->ep_ofld_list[cur_idx]);
+}
+
+static int bnx2i_init_ep_ofld_destroy_que(struct bnx2i_hba *hba)
+{
+ rwlock_init(&hba->ep_rdwr_lock);
+ hba->ep_ofld_list = (struct bnx2i_endpoint **)
+ kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!hba->ep_ofld_list)
+ return -ENOMEM;
+
+ hba->ep_ofld_prod_idx = 0;
+ hba->ep_ofld_cons_idx = 0;
+ hba->ep_ofld_max_idx =
+ PAGE_SIZE / sizeof(struct bnx2i_endpoint *) - 1;
+
+ hba->ep_destroy_list = (struct bnx2i_endpoint **)
+ kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!hba->ep_destroy_list) {
+ kfree(hba->ep_ofld_list);
+ hba->ep_ofld_list = NULL;
+ return -ENOMEM;
+ }
+
+ hba->ep_destroy_prod_idx = 0;
+ hba->ep_destroy_cons_idx = 0;
+ hba->ep_destroy_max_idx =
+ PAGE_SIZE / sizeof(struct bnx2i_endpoint *) - 1;
+ return 0;
+}
+
+
+static void bnx2i_free_ep_ofld_destroy_que(struct bnx2i_hba *hba)
+{
+ if (hba->ep_ofld_list) {
+ kfree(hba->ep_ofld_list);
+ hba->ep_ofld_list = NULL;
+ }
+ if (hba->ep_destroy_list) {
+ kfree(hba->ep_destroy_list);
+ hba->ep_destroy_list = NULL;
+ }
+}
+
+/*
+ * allocate & initialize adapter structure and call other
+ * support routines to do per adapter initialization
+ */
+struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic)
+{
+ struct bnx2i_hba *hba = NULL;
+
+ hba = kmalloc(sizeof(struct bnx2i_hba), GFP_KERNEL);
+
+ if (hba == NULL)
+ return NULL;
+
+ memset((void *) hba, 0, sizeof(struct bnx2i_hba));
+
+ /* Get PCI related information and update hba struct members */
+ hba->pci_dev = cnic->pcidev;
+ if (hba->pci_dev) {
+ hba->pci_did = hba->pci_dev->device;
+ hba->pci_vid = hba->pci_dev->vendor;
+ hba->pci_sdid = hba->pci_dev->subsystem_device;
+ hba->pci_svid = hba->pci_dev->subsystem_vendor;
+ hba->pci_func = PCI_FUNC(hba->pci_dev->devfn);
+ hba->pci_devno = PCI_SLOT(hba->pci_dev->devfn);
+ hba->pci_intr_num = hba->pci_dev->irq;
+ }
+
+ INIT_LIST_HEAD(&hba->active_sess);
+ if (bnx2i_init_ep_ofld_destroy_que(hba))
+ goto ep_ofld_que_err;
+
+ hba->mtu_supported = BNX2I_MAX_MTU_SUPPORTED;
+
+ /* TODO: different values for Teton/Xinan/Everest */
+ hba->max_active_conns = ISCSI_MAX_CONNS_PER_HBA;
+
+ if (bnx2i_setup_free_cid_que(hba))
+ goto cid_que_err;
+
+ /* SQ/RQ/CQ size can be changed via sysfx interface */
+ hba->max_sqes = BNX2I_SQ_WQES_DEFAULT;
+ hba->max_rqes = BNX2I_RQ_WQES_DEFAULT;
+ hba->max_cqes = BNX2I_CQ_WQES_DEFAULT;
+ hba->num_ccell = BNX2I_CCELLS_DEFAULT;
+
+ if (bnx2i_setup_mp_bdt(hba)) {
+ goto mp_bdt_err;
+ }
+
+ spin_lock_init(&hba->lock);
+ /* initialize timer and wait queue used for resource cleanup when
+ * interface is brought down */
+ init_timer(&hba->hba_timer);
+ init_waitqueue_head(&hba->eh_wait);
+
+ INIT_WORK(&hba->err_rec_task, conn_err_recovery_task);
+ hba->sess_recov_prod_idx = 0;
+ hba->sess_recov_cons_idx = 0;
+ hba->sess_recov_max_idx = 0;
+ hba->sess_recov_list =
+ (struct bnx2i_sess **)kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!hba->sess_recov_list)
+ goto rec_que_err;
+ hba->sess_recov_max_idx = PAGE_SIZE / sizeof (struct bnx2i_sess *) - 1;
+
+ bnx2i_init_ctx_dump_mem(hba);
+
+ return hba;
+
+rec_que_err:
+ bnx2i_free_mp_bdt(hba);
+mp_bdt_err:
+ bnx2i_release_free_cid_que(hba);
+cid_que_err:
+ bnx2i_free_ep_ofld_destroy_que(hba);
+ep_ofld_que_err:
+ bnx2i_free_hba(hba);
+
+ return NULL;
+}
+
+
+/*
+ * free adapter structure and call various cleanup routines.
+ */
+void bnx2i_free_hba(struct bnx2i_hba *hba)
+{
+ if (hba == NULL)
+ return;
+
+ bnx2i_free_ctx_dump_mem(hba);
+
+ bnx2i_free_mp_bdt(hba);
+ bnx2i_release_free_cid_que(hba);
+ bnx2i_free_ep_ofld_destroy_que(hba);
+
+ INIT_LIST_HEAD(&hba->active_sess);
+ /* Free memory held by hba structure */
+ kfree((void *)hba);
+}
+
+
+
+
+/*
+ * return all commands in active queue which should already have been
+ * cleaned up by the cnic device.
+ */
+static void bnx2i_flush_active_cmd_queue(struct bnx2i_sess *sess, int err_code)
+{
+ struct list_head *list;
+ struct list_head *tmp;
+ struct bnx2i_cmd *cmd;
+ unsigned long flags;
+ if (!sess->num_active_cmds)
+ return;
+
+ spin_lock_irqsave(sess->host->host_lock, flags);
+ list_for_each_safe(list, tmp, &sess->active_cmds) {
+ cmd = (struct bnx2i_cmd *) list;
+ cmd->req.itt &= ISCSI_CMD_RESPONSE_INDEX;
+ bnx2i_iscsi_unmap_sg_list(cmd);
+ cmd->cmd_state = ISCSI_CMD_STATE_COMPLETED;
+ list_del_init(&cmd->link);
+ bnx2i_return_failed_command(sess, cmd, err_code);
+ bnx2i_free_cmd(sess, cmd);
+ }
+ spin_unlock_irqrestore(sess->host->host_lock, flags);
+}
+
+
+/*
+ * initiate cleanup of outstanding commands for sess recovery
+ */
+static int bnx2i_session_recovery_start(struct bnx2i_sess *sess, int err_code)
+{
+ if (unlikely(!sess)) {
+ printk(KERN_ALERT "sess_recov_start: sess not active\n");
+ return FAILED;
+ }
+
+ if (!is_sess_active(sess)) {
+ wait_event_interruptible_timeout(sess->er_wait,
+ (sess->state ==
+ BNX2I_SESS_IN_FFP), HZ);
+ if (signal_pending(current))
+ flush_signals(current);
+ if (!is_sess_active(sess)) {
+ printk(KERN_ALERT "sess_reco: sess still not active\n");
+ sess->lead_conn->state = CONN_STATE_XPORT_FREEZE;
+ return FAILED;
+ }
+ }
+
+ return SUCCESS;
+}
+
+
+/*
+ * SCSI host reset handler, which is translates to iSCSI session
+ * recovery
+ */
+int bnx2i_do_iscsi_sess_recovery(struct bnx2i_sess *sess, int err_code)
+{
+ struct bnx2i_hba *hba = NULL;
+ struct bnx2i_conn *conn = sess->lead_conn;
+
+ if (bnx2i_session_recovery_start(sess, err_code) != SUCCESS) {
+ printk(KERN_INFO "bnx2i: sess rec start returned error\n");
+ return FAILED;
+ }
+ hba = sess->hba;
+
+ sess->recovery_state = ISCSI_SESS_RECOVERY_OPEN_ISCSI;
+ iscsi_conn_error(conn->cls_conn, ISCSI_ERR_CONN_FAILED);
+
+ /* if session teardown is because of net interface down,
+ * no need to wait for complete recovery */
+ if (err_code == DID_NO_CONNECT) {
+ wait_event_interruptible_timeout(sess->er_wait,
+ !conn->ep,
+ msecs_to_jiffies(1000));
+ } else {
+ wait_event_interruptible(sess->er_wait,
+ ((sess->recovery_state &
+ ISCSI_SESS_RECOVERY_COMPLETE) ||
+ (sess->recovery_state &
+ ISCSI_SESS_RECOVERY_FAILED)));
+ }
+
+ if (signal_pending(current))
+ flush_signals(current);
+
+ if (err_code == DID_NO_CONNECT)
+ return SUCCESS;
+
+ if (sess->recovery_state & ISCSI_SESS_RECOVERY_COMPLETE) {
+ printk(KERN_INFO "bnx2i: host #%d reset succeeded\n",
+ sess->host->host_no);
+ sess->state = BNX2I_SESS_IN_FFP;
+ } else {
+ return FAILED;
+ }
+ sess->recovery_state = 0;
+ return SUCCESS;
+}
+
+
+/*
+ * free up resources held by this session
+ */
+int bnx2i_iscsi_sess_release(struct bnx2i_hba *hba, struct bnx2i_sess *sess)
+{
+ if (!sess)
+ return 0;
+
+ bnx2i_release_free_itt_queue(sess);
+ bnx2i_free_cmd_pool(sess);
+ bnx2i_free_bd_table_pool(sess);
+ bnx2i_free_all_bdt_resc_pages(sess);
+
+ list_del_init(&sess->link);
+ hba->num_active_sess--;
+
+ return 0;
+}
+
+
+/*
+ * initialize various per session statistic counters
+ */
+static void bnx2i_init_iscsi_sess_stats(struct bnx2i_sess *sess)
+{
+ if (!sess)
+ return;
+
+ sess->violation_notified = 0;
+
+ sess->total_data_octets_sent = 0;
+ sess->total_data_octets_rcvd = 0;
+ sess->conn_login_ok = 0;
+ sess->conn_login_failed = 0;
+ sess->num_login_req_pdus = 0;
+ sess->num_login_resp_pdus = 0;
+ sess->num_scsi_cmd_pdus = 0;
+ sess->num_scsi_resp_pdus = 0;
+ sess->num_nopout_pdus = 0;
+ sess->num_nopin_pdus = 0;
+ sess->num_reject_pdus = 0;
+ sess->num_async_pdus = 0;
+ sess->num_dataout_pdus = 0;
+ sess->num_r2t_pdus = 0;
+ sess->num_datain_pdus = 0;
+ sess->num_snack_pdus = 0;
+ sess->num_text_req_pdus = 0;
+ sess->num_text_resp_pdus = 0;
+ sess->num_tmf_req_pdus = 0;
+ sess->num_tmf_resp_pdus = 0;
+ sess->num_logout_req_pdus = 0;
+ sess->num_logout_resp_pdus = 0;
+}
+
+
+/*
+ * set iSCSI parameter values to defaults, as defined in rfc3720
+ */
+static void bnx2i_sess_set_param_defaults(struct bnx2i_sess *sess)
+{
+ sess->initial_r2t = ISCSI_DEFAULT_INITIAL_R2T;
+ sess->max_r2t = ISCSI_DEFAULT_MAX_OUTSTANDING_R2T;
+ sess->imm_data = ISCSI_DEFAULT_IMMEDIATE_DATA;
+ sess->first_burst_len = ISCSI_DEFAULT_FIRST_BURST_LENGTH;
+ sess->max_burst_len = ISCSI_DEFAULT_MAX_BURST_LENGTH;
+ sess->time2wait = 2;
+ sess->time2retain = 20;
+}
+
+
+/*
+ * initialize session structure elements and allocate per sess resources
+ */
+int bnx2i_iscsi_sess_new(struct bnx2i_hba *hba, struct bnx2i_sess *sess)
+{
+ int rc;
+
+ spin_lock(&hba->lock);
+ list_add_tail(&sess->link, &hba->active_sess);
+ hba->num_active_sess++;
+ spin_unlock(&hba->lock);
+
+ sess->sq_size = hba->max_sqes;
+ sess->tsih = 0;
+ sess->lead_conn = NULL;
+
+ spin_lock_init(&sess->lock);
+
+ /* initialize active connection list */
+ INIT_LIST_HEAD(&sess->conn_list);
+ INIT_LIST_HEAD(&sess->free_cmds);
+
+ INIT_LIST_HEAD(&sess->active_cmds);
+ sess->num_active_cmds = 0;
+
+ sess->num_active_conn = 0;
+ sess->max_conns = 1;
+ sess->conn_id = 0;
+ sess->target_name = NULL;
+
+ sess->state = BNX2I_SESS_INITIAL;
+ sess->recovery_state = 0;
+
+ if (bnx2i_alloc_bd_table_pool(sess) != 0) {
+ printk(KERN_ERR "sess_new: unable to alloc bd table pool\n");
+ rc = -ENOMEM;
+ goto err_bd_pool;
+ }
+
+ if (bnx2i_alloc_cmd_pool(sess) != 0) {
+ printk(KERN_ERR "sess_new: alloc cmd pool failed\n");
+ rc = -ENOMEM;
+ goto err_cmd_pool;
+ }
+
+ rc = bnx2i_setup_free_itt_queue(sess);
+ if (rc) {
+ rc = -ENOMEM;
+ goto err_itt_que;
+ }
+
+ init_timer(&sess->abort_timer);
+ init_waitqueue_head(&sess->er_wait);
+ init_timer(&sess->poll_timer);
+
+ bnx2i_init_iscsi_sess_stats(sess);
+ bnx2i_sess_set_param_defaults(sess);
+
+ return 0;
+
+err_itt_que:
+ bnx2i_free_cmd_pool(sess);
+err_cmd_pool:
+ bnx2i_free_bd_table_pool(sess);
+err_bd_pool:
+ return rc;
+}
+
+
+/*
+ * Login related resources is freed in this routine.
+ */
+void bnx2i_conn_free_login_resources(struct bnx2i_hba *hba,
+ struct bnx2i_conn *conn)
+{
+ if (conn->gen_pdu.resp_bd_tbl) {
+ pci_free_consistent(hba->pci_dev, PAGE_SIZE,
+ conn->gen_pdu.resp_bd_tbl,
+ conn->gen_pdu.resp_bd_dma);
+ conn->gen_pdu.resp_bd_tbl = NULL;
+ }
+
+ if (conn->gen_pdu.req_bd_tbl) {
+ pci_free_consistent(hba->pci_dev, PAGE_SIZE,
+ conn->gen_pdu.req_bd_tbl,
+ conn->gen_pdu.req_bd_dma);
+ conn->gen_pdu.req_bd_tbl = NULL;
+ }
+
+ if (conn->gen_pdu.resp_buf) {
+ pci_free_consistent(hba->pci_dev, ISCSI_CONN_LOGIN_BUF_SIZE,
+ conn->gen_pdu.resp_buf,
+ conn->gen_pdu.resp_dma_addr);
+ conn->gen_pdu.resp_buf = NULL;
+ }
+
+ if (conn->gen_pdu.req_buf) {
+ pci_free_consistent(hba->pci_dev, ISCSI_CONN_LOGIN_BUF_SIZE,
+ conn->gen_pdu.req_buf,
+ conn->gen_pdu.req_dma_addr);
+ conn->gen_pdu.req_buf = NULL;
+ }
+}
+
+
+/*
+ * Login & nop-in related resources is allocated in this routine.
+ */
+static int bnx2i_conn_alloc_login_resources(struct bnx2i_hba *hba,
+ struct bnx2i_conn *conn)
+{
+ /* Allocate memory for login request/response buffers */
+ conn->gen_pdu.req_buf =
+ (char *) pci_alloc_consistent(hba->pci_dev,
+ ISCSI_CONN_LOGIN_BUF_SIZE,
+ &conn->gen_pdu.req_dma_addr);
+ if (conn->gen_pdu.req_buf == NULL)
+ goto login_req_buf_failure;
+
+ conn->gen_pdu.req_buf_size = 0;
+ conn->gen_pdu.req_wr_ptr = conn->gen_pdu.req_buf;
+
+ conn->gen_pdu.resp_buf =
+ (char *) pci_alloc_consistent(hba->pci_dev,
+ ISCSI_CONN_LOGIN_BUF_SIZE,
+ &conn->gen_pdu.resp_dma_addr);
+ if (conn->gen_pdu.resp_buf == NULL)
+ goto login_resp_buf_failure;
+
+ conn->gen_pdu.resp_buf_size = ISCSI_CONN_LOGIN_BUF_SIZE;
+ conn->gen_pdu.resp_wr_ptr = conn->gen_pdu.resp_buf;
+
+ conn->gen_pdu.req_bd_tbl =
+ (char *) pci_alloc_consistent(hba->pci_dev, PAGE_SIZE,
+ &conn->gen_pdu.req_bd_dma);
+ if (conn->gen_pdu.req_bd_tbl == NULL)
+ goto login_req_bd_tbl_failure;
+
+ conn->gen_pdu.resp_bd_tbl =
+ (char *) pci_alloc_consistent(hba->pci_dev, PAGE_SIZE,
+ &conn->gen_pdu.resp_bd_dma);
+ if (conn->gen_pdu.resp_bd_tbl == NULL)
+ goto login_resp_bd_tbl_failure;
+
+ return 0;
+
+login_resp_bd_tbl_failure:
+ pci_free_consistent(hba->pci_dev, PAGE_SIZE, conn->gen_pdu.req_bd_tbl,
+ conn->gen_pdu.req_bd_dma);
+ conn->gen_pdu.req_bd_tbl = NULL;
+
+login_req_bd_tbl_failure:
+ pci_free_consistent(hba->pci_dev, ISCSI_CONN_LOGIN_BUF_SIZE,
+ conn->gen_pdu.resp_buf,
+ conn->gen_pdu.resp_dma_addr);
+ conn->gen_pdu.resp_buf = NULL;
+login_resp_buf_failure:
+ pci_free_consistent(hba->pci_dev, ISCSI_CONN_LOGIN_BUF_SIZE,
+ conn->gen_pdu.req_buf, conn->gen_pdu.req_dma_addr);
+ conn->gen_pdu.req_buf = NULL;
+login_req_buf_failure:
+ printk(KERN_ERR "bnx2i:a conn login resource alloc failed!!\n");
+ return -ENOMEM;
+
+}
+
+
+/*
+ * connection structure is initialized in this routine.
+ */
+int bnx2i_iscsi_conn_new(struct bnx2i_sess *sess, struct bnx2i_conn *conn)
+{
+ int ret_code = 0;
+ struct bnx2i_hba *hba = sess->hba;
+
+ if (!sess || !conn || !hba)
+ return -EINVAL;
+
+ conn->sess = sess;
+ conn->header_digest_en = 0;
+ conn->data_digest_en = 0;
+
+ spin_lock_init(&conn->lock);
+
+ init_timer(&conn->poll_timer);
+ conn->gen_pdu.cmd = NULL;
+
+ /* 'ep' ptr will be assigned in bind() call */
+ conn->ep = NULL;
+
+ ret_code = bnx2i_conn_alloc_login_resources(hba, conn);
+ if (ret_code != 0) {
+ printk(KERN_ALERT "conn_new: login resc alloc failed!!\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+
+/*
+ * extract & update SN counters from login response
+ */
+static int bnx2i_login_resp_update_cmdsn(struct bnx2i_conn *conn)
+{
+ u32 max_cmdsn;
+ u32 exp_cmdsn;
+ u32 stat_sn;
+ struct bnx2i_sess *sess = conn->sess;
+ struct iscsi_nopin *hdr = NULL;
+
+ hdr = (struct iscsi_nopin *) &conn->gen_pdu.resp_hdr;
+
+ max_cmdsn = ntohl(hdr->max_cmdsn);
+ exp_cmdsn = ntohl(hdr->exp_cmdsn);
+ stat_sn = ntohl(hdr->statsn);
+#define SN_DELTA_ISLAND 0xffff
+ if (max_cmdsn < exp_cmdsn -1 &&
+ max_cmdsn > exp_cmdsn - SN_DELTA_ISLAND)
+ return -EINVAL;
+
+ if (max_cmdsn > sess->max_cmdsn ||
+ max_cmdsn < sess->max_cmdsn - SN_DELTA_ISLAND)
+ sess->max_cmdsn = max_cmdsn;
+
+ if (exp_cmdsn > sess->exp_cmdsn ||
+ exp_cmdsn < sess->exp_cmdsn - SN_DELTA_ISLAND) {
+ sess->exp_cmdsn = exp_cmdsn;
+ }
+ if (stat_sn == conn->exp_statsn)
+ conn->exp_statsn++;
+
+ return 0;
+}
+
+
+/*
+ * update iSCSI SN counters for the given session
+ */
+void bnx2i_update_cmd_sequence(struct bnx2i_sess *sess,
+ u32 exp_sn, u32 max_sn)
+{
+ u32 exp_cmdsn = exp_sn;
+ u32 max_cmdsn = max_sn;
+
+ if (max_cmdsn < exp_cmdsn -1 &&
+ max_cmdsn > exp_cmdsn - SN_DELTA_ISLAND) {
+ printk(KERN_ALERT "cmd_sequence: error, exp 0x%x, max 0x%x\n",
+ exp_cmdsn, max_cmdsn);
+ BUG_ON(1);
+ }
+ if (max_cmdsn > sess->max_cmdsn ||
+ max_cmdsn < sess->max_cmdsn - SN_DELTA_ISLAND)
+ sess->max_cmdsn = max_cmdsn;
+ if (exp_cmdsn > sess->exp_cmdsn ||
+ exp_cmdsn < sess->exp_cmdsn - SN_DELTA_ISLAND) {
+ sess->exp_cmdsn = exp_cmdsn;
+ }
+
+ return;
+}
+
+
+/*
+ * This function propogates SCSI response to SCSI-ML by calling
+ * scsi_done() and also returns command struct back to free pool
+ */
+int bnx2i_process_scsi_resp(struct bnx2i_cmd *cmd)
+{
+ int ret = 0;
+ struct scsi_cmnd *sc = cmd->scsi_cmd;
+ struct Scsi_Host *host;
+ int res_count = 0;
+
+ if (!sc)
+ return 0;
+
+ host = cmd->conn->sess->host;
+ sc->result = (DID_OK << 16) | cmd->scsi_status;
+
+ if (cmd->iscsi_resp != ISCSI_STATUS_CMD_COMPLETED) {
+ sc->result = (DID_ERROR << 16);
+ goto call_scsi_done;
+ }
+
+ if (sc->sc_data_direction == DMA_TO_DEVICE) {
+ goto call_scsi_done;
+ }
+
+ if (cmd->scsi_uflow) {
+ res_count = cmd->resi_len;
+ if (res_count > 0 && res_count <= sc->request_bufflen)
+ sc->resid = res_count;
+ else
+ sc->result = (DID_BAD_TARGET << 16) |
+ cmd->scsi_status;
+ } else if (cmd->scsi_oflow) {
+ sc->resid = res_count;
+ }
+
+call_scsi_done:
+ if ((cmd->cmd_state == ISCSI_CMD_STATE_ABORT_PEND) ||
+ (cmd->cmd_state == ISCSI_CMD_STATE_CLEANUP_PEND)) {
+ printk(KERN_ALERT "scsi_resp: command is being aborted\n");
+ return -1;
+ }
+
+ spin_lock(host->host_lock);
+ cmd->scsi_cmd = NULL;
+ cmd->conn->sess->num_active_cmds--;
+ sc->scsi_done(sc);
+ bnx2i_free_cmd(cmd->conn->sess, cmd);
+ spin_unlock(host->host_lock);
+ return ret;
+}
+
+
+
+/*
+ * login response PDU is pushed to application daemon by
+ * calling iscsi_recv_pdu()
+ */
+int bnx2i_indicate_login_resp(struct bnx2i_conn *conn)
+{
+ int ret = 0;
+ int data_len = 0;
+ struct iscsi_login_rsp *login_resp =
+ (struct iscsi_login_rsp *) &conn->gen_pdu.resp_hdr;
+
+ /* check if this is the first login response for this connection.
+ * If yes, we need to copy initial StatSN to connection structure.
+ */
+ if (conn->exp_statsn == STATSN_UPDATE_SIGNATURE) {
+ conn->exp_statsn = ntohl(login_resp->statsn) + 1;
+ }
+
+ ret = bnx2i_login_resp_update_cmdsn(conn);
+ if (ret != 0) {
+ return -EINVAL;
+ }
+
+ data_len = conn->gen_pdu.resp_wr_ptr - conn->gen_pdu.resp_buf;
+ iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *) login_resp,
+ (char *) conn->gen_pdu.resp_buf, data_len);
+
+ return 0;
+}
+
+
+/*
+ * deliver logout response PDU to application daemon
+ */
+int bnx2i_indicate_logout_resp(struct bnx2i_conn *conn)
+{
+ struct iscsi_logout_rsp *logout_resp =
+ (struct iscsi_logout_rsp *) &conn->gen_pdu.resp_hdr;
+
+ iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *) logout_resp,
+ (char *) NULL, 0);
+
+ return 0;
+}
+
+
+/*
+ * deliver iSCSI async PDU to user daemon
+ */
+int bnx2i_indicate_async_mesg(struct bnx2i_conn *conn)
+{
+ struct iscsi_async *async_msg =
+ (struct iscsi_async *) &conn->gen_pdu.resp_hdr;
+
+ iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *) async_msg,
+ (char *) NULL, 0);
+
+ return 0;
+}
+
+
+
+/*
+ * Function : bnx2i_process_nopin
+ */
+int bnx2i_process_nopin(struct bnx2i_conn *conn, struct bnx2i_cmd *cmd,
+ char *data_buf, int data_len)
+{
+ struct iscsi_nopin *nopin_msg =
+ (struct iscsi_nopin *) &conn->gen_pdu.resp_hdr;
+
+ iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *) nopin_msg,
+ (char *) data_buf, data_len);
+
+ spin_lock(conn->sess->host->host_lock);
+ list_del_init(&cmd->link);
+ bnx2i_free_cmd(cmd->conn->sess, cmd);
+ spin_unlock(conn->sess->host->host_lock);
+
+ return 0;
+}
+
+
+
+/*
+ * Allocates buffers and BD tables before shipping requests to cnic
+ * for PDUs prepared by 'iscsid' daemon
+ */
+static void bnx2i_iscsi_prep_generic_pdu_bd(struct bnx2i_conn *conn)
+{
+ struct iscsi_bd *bd_tbl = NULL;
+
+ bd_tbl = (struct iscsi_bd *) conn->gen_pdu.req_bd_tbl;
+
+ bd_tbl->buffer_addr_hi =
+ (u32) ((u64) conn->gen_pdu.req_dma_addr >> 32);
+ bd_tbl->buffer_addr_lo = (u32) conn->gen_pdu.req_dma_addr;
+ bd_tbl->buffer_length = conn->gen_pdu.req_wr_ptr -
+ conn->gen_pdu.req_buf;
+ bd_tbl->reserved0 = 0;
+ bd_tbl->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
+ ISCSI_BD_FIRST_IN_BD_CHAIN;
+
+ bd_tbl = (struct iscsi_bd *) conn->gen_pdu.resp_bd_tbl;
+ bd_tbl->buffer_addr_hi = (u64) conn->gen_pdu.resp_dma_addr >> 32;
+ bd_tbl->buffer_addr_lo = (u32) conn->gen_pdu.resp_dma_addr;
+ bd_tbl->buffer_length = ISCSI_CONN_LOGIN_BUF_SIZE;
+ bd_tbl->reserved0 = 0;
+ bd_tbl->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
+ ISCSI_BD_FIRST_IN_BD_CHAIN;
+}
+
+
+
+/*
+ * called to transmit PDUs prepared by the 'iscsid' daemon. iSCSI login,
+ * Nop-out and Logout requests flow through this path.
+ */
+static int bnx2i_iscsi_send_generic_request(struct bnx2i_cmd *cmnd)
+{
+ int rc = 0;
+ char *buf = NULL;
+ int data_len = 0;
+ struct bnx2i_conn *conn = cmnd->conn;
+
+ bnx2i_iscsi_prep_generic_pdu_bd(conn);
+ switch (cmnd->iscsi_opcode & ISCSI_OPCODE_MASK) {
+ case ISCSI_OP_LOGIN:
+ bnx2i_send_iscsi_login(conn, cmnd);
+ break;
+
+ case ISCSI_OP_NOOP_OUT:
+ data_len = conn->gen_pdu.req_buf_size;
+ buf = conn->gen_pdu.req_buf;
+ if (data_len)
+ rc = bnx2i_send_iscsi_nopout(conn, cmnd,
+ ISCSI_RESERVED_TAG,
+ buf, data_len, 1);
+ else
+ rc = bnx2i_send_iscsi_nopout(conn, cmnd,
+ ISCSI_RESERVED_TAG,
+ NULL, 0, 1);
+ break;
+
+ case ISCSI_OP_LOGOUT:
+ rc = bnx2i_send_iscsi_logout(conn, cmnd);
+ break;
+
+ default:
+ printk(KERN_ALERT "send_gen: unsupported op 0x%x\n",
+ cmnd->iscsi_opcode);
+ }
+ return rc;
+}
+
+
+/**********************************************************************
+ * SCSI-ML Interface
+ **********************************************************************/
+
+static void bnx2i_cpy_scsi_cdb(struct scsi_cmnd *sc,
+ struct bnx2i_cmd *cmd)
+{
+ u32 dword;
+ int lpcnt = 0;
+ u8 *srcp = NULL;
+ u32 *dstp = NULL;
+ u32 scsi_lun[2];
+
+ int_to_scsilun(sc->device->lun, (struct scsi_lun *) scsi_lun);
+ cmd->req.lun[0] = ntohl(scsi_lun[0]);
+ cmd->req.lun[1] = ntohl(scsi_lun[1]);
+
+ lpcnt = cmd->scsi_cmd->cmd_len / sizeof(dword);
+ srcp = (u8 *) sc->cmnd;
+ dstp = (u32 *) cmd->req.cdb;
+ while (lpcnt--) {
+ memcpy(&dword, srcp, 4);
+ *dstp = cpu_to_be32(dword);
+ srcp += 4;
+ dstp++;
+ }
+ if (sc->cmd_len & 0x3) {
+ dword = (u32) srcp[0] | ((u32) srcp[1] << 8);
+ *dstp = cpu_to_be32(dword);
+ }
+}
+
+
+
+/*
+ * handles SCSI command queued by SCSI-ML, allocates a command structure,
+ * assigning CMDSN, mapping SG buffers and handing over request to CNIC.
+ */
+int bnx2i_queuecommand(struct scsi_cmnd *sc,
+ void (*done) (struct scsi_cmnd *))
+{
+ struct Scsi_Host *shost;
+ struct bnx2i_sess *sess = NULL;
+ struct bnx2i_conn *conn = NULL;
+ struct bnx2i_cmd *cmd = NULL;
+ struct bnx2i_hba *hba = NULL;
+ static int old_recovery_state = 0;
+
+ sc->scsi_done = done;
+ sc->result = 0;
+ shost = sc->device->host;
+ sess = iscsi_hostdata(shost->hostdata);
+ BUG_ON(shost != sess->host);
+
+ if (sess) {
+ hba = sess->hba;
+ } else {
+ printk(KERN_ALERT "bnx2i: quecmd: Error dev not found \n");
+ goto dev_not_found;
+ }
+
+#define iscsi_cmd_win_closed(_sess) \
+ ((int) (_sess->max_cmdsn - _sess->cmdsn) < 0)
+
+ if (iscsi_cmd_win_closed(sess)) {
+ goto iscsi_win_closed;
+ }
+
+ if ((sess->state & BNX2I_SESS_IN_SHUTDOWN) ||
+ (sess->state & BNX2I_SESS_IN_LOGOUT)) {
+ goto dev_not_found;
+ }
+
+ if (sess->recovery_state) {
+ if (old_recovery_state != sess->recovery_state) {
+ old_recovery_state = sess->recovery_state;
+ }
+
+ if (sess->recovery_state & ISCSI_SESS_RECOVERY_FAILED)
+ goto dev_not_found;
+ else if (!(sess->recovery_state & ISCSI_SESS_RECOVERY_COMPLETE))
+ goto iscsi_win_closed;
+ else
+ sess->recovery_state = 0;
+ }
+
+ cmd = bnx2i_alloc_cmd(sess);
+ if (cmd == NULL) {
+ /* This should never happen as cmd list size == SHT->can_queue
+ */
+ goto cmd_not_accepted;
+ }
+
+ cmd->conn = conn = sess->lead_conn;
+ cmd->scsi_cmd = sc;
+ cmd->req.total_data_transfer_length = sc->request_bufflen;
+ cmd->iscsi_opcode = ISCSI_OPCODE_SCSI_CMD;
+ cmd->req.cmd_sn = sess->cmdsn++;
+
+ bnx2i_iscsi_map_sg_list(cmd);
+ bnx2i_cpy_scsi_cdb(sc, cmd);
+
+ if (sc->sc_data_direction == DMA_TO_DEVICE) {
+ cmd->req.op_attr = ISCSI_CMD_REQUEST_WRITE;
+ cmd->req.itt |= (ISCSI_TASK_TYPE_WRITE <<
+ ISCSI_CMD_REQUEST_TYPE_SHIFT);
+ bnx2i_setup_write_cmd_bd_info(cmd);
+ } else {
+ cmd->req.op_attr = ISCSI_CMD_REQUEST_READ;
+ cmd->req.itt |= (ISCSI_TASK_TYPE_READ <<
+ ISCSI_CMD_REQUEST_TYPE_SHIFT);
+ }
+ cmd->req.num_bds = cmd->bd_tbl->bd_valid;
+ if (!cmd->bd_tbl->bd_valid) {
+ cmd->req.bd_list_addr_lo = (u32) hba->mp_bd_dma;
+ cmd->req.bd_list_addr_hi =
+ (u32) ((u64) hba->mp_bd_dma >> 32);
+ cmd->req.num_bds = 1;
+ }
+
+ cmd->cmd_state = ISCSI_CMD_STATE_INITIATED;
+ sc->SCp.ptr = (char *) cmd;
+
+ if (cmd->req.itt != ITT_INVALID_SIGNATURE) {
+ bnx2i_send_iscsi_scsicmd(conn, cmd);
+ list_add_tail(&cmd->link, &sess->active_cmds);
+ sess->num_active_cmds++;
+ }
+ return 0;
+
+iscsi_win_closed:
+cmd_not_accepted:
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+dev_not_found:
+ sc->sense_buffer[0] = 0x70;
+ sc->sense_buffer[2] = NOT_READY;
+ sc->sense_buffer[7] = 0x6;
+ sc->sense_buffer[12] = 0x08;
+ sc->sense_buffer[13] = 0x00;
+ sc->result = (DID_NO_CONNECT << 16);
+ sc->resid = sc->request_bufflen;
+ sc->scsi_done(sc);
+ return 0;
+}
+
+
+
+/*
+ * TMF request timeout handler
+ */
+static void bnx2i_iscsi_tmf_timer(unsigned long data)
+{
+ struct bnx2i_cmd *cmd = (struct bnx2i_cmd *) data;
+
+ printk(KERN_ALERT "TMF timer: abort failed, cmd 0x%p\n", cmd);
+ cmd->cmd_state = ISCSI_CMD_STATE_FAILED;
+ wake_up(&cmd->conn->sess->er_wait);
+}
+
+
+/*
+ * initiate command abort process by requesting CNIC to send
+ * an iSCSI TMF request to target
+ */
+static int bnx2i_initiate_abort_cmd(struct scsi_cmnd *sc)
+{
+ struct bnx2i_cmd *cmd = (struct bnx2i_cmd *) sc->SCp.ptr;
+ struct bnx2i_cmd *tmf_cmd = NULL;
+ struct Scsi_Host *shost = cmd->scsi_cmd->device->host;
+ struct bnx2i_conn *conn = cmd->conn;
+ struct bnx2i_sess *sess = NULL;
+ struct bnx2i_hba *hba = NULL;
+
+ shost = cmd->scsi_cmd->device->host;
+ sess = iscsi_hostdata(shost->hostdata);
+ BUG_ON(shost != sess->host);
+
+ if (sess && (is_sess_active(sess))) {
+ hba = sess->hba;
+ } else {
+ return FAILED;
+ }
+
+ bnx2i_setup_ictx_dump(hba, conn);
+
+ if (cmd->scsi_cmd != sc) {
+ /* command already completed to scsi mid-layer */
+ goto cmd_not_active;
+ }
+
+ tmf_cmd = bnx2i_alloc_cmd(sess);
+ if (cmd == NULL) {
+ goto lack_of_resc;
+ }
+
+ tmf_cmd->conn = conn = sess->lead_conn;
+ tmf_cmd->scsi_cmd = NULL;
+ tmf_cmd->iscsi_opcode = ISCSI_OPCODE_TMF_REQUEST;
+ tmf_cmd->req.cmd_sn = sess->cmdsn;
+ tmf_cmd->tmf_ref_itt = cmd->req.itt;
+ tmf_cmd->tmf_ref_cmd = cmd;
+ tmf_cmd->tmf_ref_sc = cmd->scsi_cmd;
+ cmd->cmd_state = ISCSI_CMD_STATE_ABORT_PEND;
+ tmf_cmd->cmd_state = ISCSI_CMD_STATE_INITIATED;
+
+ sess->abort_timer.expires = 10*HZ + jiffies;
+ sess->abort_timer.function = bnx2i_iscsi_tmf_timer;
+ sess->abort_timer.data = (unsigned long)tmf_cmd;
+ add_timer(&sess->abort_timer);
+
+ bnx2i_send_iscsi_tmf(conn, tmf_cmd);
+
+ /* update iSCSI context for this conn, wait for CNIC to complete */
+ wait_event_interruptible(sess->er_wait,
+ tmf_cmd->cmd_state != ISCSI_CMD_STATE_INITIATED);
+
+ if (signal_pending(current))
+ flush_signals(current);
+
+ del_timer_sync(&sess->abort_timer);
+
+ if (tmf_cmd->cmd_state == ISCSI_CMD_STATE_FAILED) {
+ printk(KERN_ALERT "abort: abort failed, cmd 0x%p\n", tmf_cmd);
+ /* TMF timed out, return error status and let SCSI-ML do
+ * session recovery.
+ */
+ list_del_init(&tmf_cmd->link);
+ bnx2i_free_cmd(sess, tmf_cmd);
+ return FAILED;
+ }
+
+ list_del_init(&tmf_cmd->link);
+ bnx2i_free_cmd(sess, tmf_cmd);
+
+ if ((cmd->scsi_cmd->result & 0xFF0000) == (DID_ABORT << 16)) {
+ cmd->cmd_state = ISCSI_CMD_STATE_CLEANUP_PEND;
+ bnx2i_send_cmd_cleanup_req(hba, cmd);
+ wait_event_interruptible_timeout(sess->er_wait,
+ (cmd->cmd_state ==
+ ISCSI_CMD_STATE_CLEANUP_CMPL),
+ msecs_to_jiffies(
+ ISCSI_CMD_CLEANUP_TIMEOUT));
+
+ if (signal_pending(current))
+ flush_signals(current);
+ } else {
+ cmd->scsi_cmd->result = (DID_ABORT << 16);
+ }
+ cmd->conn->sess->num_active_cmds--;
+ list_del_init(&cmd->link);
+ cmd->scsi_cmd = NULL;
+ bnx2i_free_cmd(cmd->conn->sess, cmd);
+
+cmd_not_active:
+ return SUCCESS;
+
+lack_of_resc:
+ return FAILED;
+}
+
+
+/*
+ * SCSI abort request handler.
+ */
+int bnx2i_abort(struct scsi_cmnd *sc)
+{
+ int reason;
+ struct bnx2i_cmd *cmd = (struct bnx2i_cmd *) sc->SCp.ptr;
+
+ if (unlikely(!cmd)) {
+ /* command already completed to scsi mid-layer */
+ printk(KERN_INFO "bnx2i_abort: sc 0x%p, not active\n", sc);
+ return SUCCESS;
+ }
+
+ reason = bnx2i_initiate_abort_cmd(sc);
+ return reason;
+}
+
+
+
+/*
+ * hardware reset
+ */
+int bnx2i_reset(struct scsi_cmnd *sc)
+{
+ return 0;
+}
+
+
+void bnx2i_return_failed_command(struct bnx2i_sess *sess,
+ struct bnx2i_cmd *cmd, int err_code)
+{
+ struct scsi_cmnd *sc = cmd->scsi_cmd;
+ sc->result = err_code << 16;
+ sc->resid = cmd->scsi_cmd->request_bufflen;
+ cmd->scsi_cmd = NULL;
+ sess->num_active_cmds--;
+ sc->scsi_done(sc);
+}
+
+
+
+/*
+ * SCSI host reset handler - iSCSI session recovery
+ */
+int bnx2i_host_reset(struct scsi_cmnd *sc)
+{
+ struct Scsi_Host *shost = sc->device->host;
+ struct bnx2i_sess *sess = NULL;
+ int rc = 0;
+
+ shost = sc->device->host;
+ sess = iscsi_hostdata(shost->hostdata);
+ printk(KERN_INFO "bnx2i: attempting to reset host, #%d\n",
+ sess->host->host_no);
+
+ BUG_ON(shost != sess->host);
+ rc = bnx2i_do_iscsi_sess_recovery(sess, DID_RESET);
+
+ return rc;
+}
+
+
+
+/**********************************************************************
+ * open-iscsi interface
+ **********************************************************************/
+
+
+#define get_bnx2_device(_hba, _devc) do { \
+ if ((_hba->pci_did == PCI_DEVICE_ID_NX2_5706) || \
+ (_hba->pci_did == PCI_DEVICE_ID_NX2_5706S)) { \
+ _devc = '6'; \
+ } else if ((_hba->pci_did == PCI_DEVICE_ID_NX2_5708) || \
+ (_hba->pci_did == PCI_DEVICE_ID_NX2_5708S)) { \
+ _devc = '8'; \
+ } else if ((_hba->pci_did == PCI_DEVICE_ID_NX2_5709) || \
+ (_hba->pci_did == PCI_DEVICE_ID_NX2_5709S)) { \
+ _devc = '9'; \
+ } \
+ } while (0)
+
+/* from open-iscsi project */
+/*
+ * iSCSI Session's hostdata organization:
+ *
+ * *------------------* <== hostdata_session(host->hostdata)
+ * | ptr to class sess|
+ * |------------------| <== iscsi_hostdata(host->hostdata)
+ * | iscsi_session |
+ * *------------------*
+ */
+
+#define hostdata_privsize(_sz) (sizeof(unsigned long) + _sz + \
+ _sz % sizeof(unsigned long))
+
+#define hostdata_session(_hostdata) (iscsi_ptr(*(unsigned long *)_hostdata))
+
+#define session_to_cls(_sess) hostdata_session(_sess->host->hostdata)
+
+
+
+
+/*
+ * Function: bnx2i_register_xport
+ * Description: this routine will allocate memory for SCSI host template,
+ * iSCSI template and registers one instance of NX2 device with
+ * iSCSI Transport Kernel module.
+ */
+int bnx2i_register_xport(struct bnx2i_hba *hba)
+{
+ void *mem_ptr = NULL;
+ char dev_id = '8';
+
+ if (!hba)
+ return -EINVAL;
+
+ get_bnx2_device(hba, dev_id);
+
+ mem_ptr = kmalloc(sizeof(struct scsi_host_template), GFP_KERNEL);
+ hba->scsi_template = (struct scsi_host_template *) mem_ptr;
+ if (hba->scsi_template == NULL) {
+ printk(KERN_ALERT "bnx2i: failed to alloc memory for sht\n");
+ return -ENOMEM;
+ }
+
+ mem_ptr = kmalloc(sizeof(struct iscsi_transport), GFP_KERNEL);
+ hba->iscsi_transport = (struct iscsi_transport *) mem_ptr;
+ if (hba->iscsi_transport == NULL) {
+ printk(KERN_ALERT "mem error for iscsi_transport template\n");
+ goto iscsi_xport_err;
+ }
+
+ mem_ptr = kmalloc(BRCM_ISCSI_XPORT_NAME_SIZE_MAX, GFP_KERNEL);
+ if (mem_ptr == NULL) {
+ printk(KERN_ALERT "failed to alloc memory for xport name\n");
+ goto scsi_name_mem_err;
+ }
+
+ memcpy((void *) hba->scsi_template,
+ (const void *) &bnx2i_host_template,
+ sizeof(struct scsi_host_template));
+ hba->scsi_template->name = mem_ptr;
+ memcpy((void *) hba->scsi_template->name,
+ (const void *) bnx2i_host_template.name,
+ strlen(bnx2i_host_template.name) + 1);
+
+ mem_ptr = kmalloc(BRCM_ISCSI_XPORT_NAME_SIZE_MAX, GFP_KERNEL);
+ if (mem_ptr == NULL) {
+ printk(KERN_ALERT "failed to alloc proc name mem\n");
+ goto scsi_proc_name_mem_err;
+ }
+ hba->scsi_template->proc_name = mem_ptr;
+
+ memcpy((void *) hba->iscsi_transport,
+ (const void *) &bnx2i_iscsi_transport,
+ sizeof(struct iscsi_transport));
+
+ hba->iscsi_transport->host_template = hba->scsi_template;
+
+ mem_ptr = kmalloc(BRCM_ISCSI_XPORT_NAME_SIZE_MAX, GFP_KERNEL);
+ if (mem_ptr == NULL) {
+ printk(KERN_ALERT "mem alloc error, iscsi xport name\n");
+ goto xport_name_mem_err;
+ }
+ hba->iscsi_transport->name = mem_ptr;
+ sprintf(mem_ptr, "%s%c-%.2x%.2x%.2x", BRCM_ISCSI_XPORT_NAME_PREFIX,
+ dev_id, (u8)hba->pci_dev->bus->number,
+ hba->pci_devno, (u8)hba->pci_func);
+
+ memcpy((void *)hba->scsi_template->proc_name,
+ (const void *)mem_ptr, strlen(mem_ptr) + 1);
+
+ hba->shost_template = iscsi_register_transport(hba->iscsi_transport);
+ if (!hba->shost_template) {
+ printk(KERN_ALERT "bnx2i: xport reg failed, hba 0x%p\n", hba);
+ goto failed_registration;
+ }
+ printk(KERN_ALERT "bnx2i: netif=%s, iscsi=%s\n",
+ hba->netdev->name, hba->scsi_template->proc_name);
+ return 0;
+
+failed_registration:
+ kfree(hba->iscsi_transport->name);
+xport_name_mem_err:
+ kfree(hba->scsi_template->proc_name);
+scsi_proc_name_mem_err:
+ kfree(hba->scsi_template->name);
+scsi_name_mem_err:
+ kfree(hba->iscsi_transport);
+iscsi_xport_err:
+ kfree(hba->scsi_template);
+ printk(KERN_ALERT "register iscsi xport failed, hba 0x%p\n", hba);
+ return -ENOMEM;
+}
+
+
+/*
+ * Function: bnx2i_deregister_xport
+ * Description: this routine will de-allocate memory for SCSI host template,
+ * iSCSI template and de-registers a NX2 device instance
+ */
+int bnx2i_deregister_xport(struct bnx2i_hba *hba)
+{
+ if (!hba)
+ return -EINVAL;
+
+ iscsi_unregister_transport(hba->iscsi_transport);
+ hba->shost_template = NULL;
+
+ if (hba->scsi_template->name) {
+ kfree(hba->scsi_template->name);
+ hba->scsi_template->name = NULL;
+ }
+ if (hba->scsi_template) {
+ kfree(hba->scsi_template);
+ hba->scsi_template = NULL;
+ }
+ if (hba->iscsi_transport->name) {
+ kfree(hba->iscsi_transport->name);
+ hba->iscsi_transport->name = NULL;
+ }
+ if (hba->iscsi_transport) {
+ kfree(hba->iscsi_transport);
+ hba->iscsi_transport = NULL;
+ }
+ return 0;
+}
+
+
+/*
+ * Function: bnx2i_session_create
+ * Description: Creates a new iSCSI session instance on given device.
+ */
+struct iscsi_cls_session *
+ bnx2i_session_create(struct iscsi_transport *it,
+ struct scsi_transport_template *scsit,
+ uint16_t cmds_max, uint16_t qdepth,
+ uint32_t initial_cmdsn, uint32_t *host_no)
+{
+ struct bnx2i_hba *hba = NULL;
+ struct bnx2i_sess *sess = NULL;
+ struct Scsi_Host *shost;
+ struct iscsi_cls_session *cls_session;
+ int ret_code = 0;
+
+ hba = bnx2i_get_hba_from_template(scsit);
+ if (bnx2i_adapter_ready(hba))
+ return NULL;
+
+ shost = scsi_host_alloc(hba->iscsi_transport->host_template,
+ hostdata_privsize(sizeof(struct bnx2i_sess)));
+ if (!shost)
+ return NULL;
+
+ shost->max_id = 1;
+ shost->max_channel = 1;
+ shost->max_lun = hba->iscsi_transport->max_lun;
+ shost->max_cmd_len = hba->iscsi_transport->max_cmd_len;
+ if (cmds_max)
+ shost->can_queue = cmds_max;
+ if (qdepth)
+ shost->cmd_per_lun = qdepth;
+ shost->transportt = scsit;
+ *host_no = shost->host_no;
+ sess = iscsi_hostdata(shost->hostdata);
+
+ if (!sess)
+ goto sess_resc_fail;
+
+ memset(sess, 0, sizeof(struct bnx2i_sess));
+ sess->hba = hba;
+ sess->host = shost;
+
+ /*
+ * For Open-iSCSI, only normal sessions go through bnx2i.
+ * Discovery session goes through host stack TCP/IP stack.
+ */
+ ret_code = bnx2i_iscsi_sess_new(hba, sess);
+ if (ret_code) {
+ /*
+ * failed to allocate memory
+ */
+ printk(KERN_ALERT "bnx2i_sess_create: unable to alloc sess\n");
+ goto sess_resc_fail;
+ }
+
+ /*
+ * Update CmdSN related parameters
+ */
+ sess->cmdsn = initial_cmdsn;
+ sess->exp_cmdsn = initial_cmdsn + 1;
+ sess->max_cmdsn = initial_cmdsn + 1;
+
+ if (scsi_add_host(shost, NULL))
+ goto add_sh_fail;
+
+ if (!try_module_get(it->owner))
+ goto cls_sess_falied;
+
+ cls_session = iscsi_create_session(shost, it, 0);
+ if (!cls_session)
+ goto module_put;
+ *(unsigned long *)shost->hostdata = (unsigned long)cls_session;
+
+ return hostdata_session(shost->hostdata);
+
+module_put:
+ module_put(it->owner);
+cls_sess_falied:
+ scsi_remove_host(shost);
+add_sh_fail:
+ bnx2i_iscsi_sess_release(hba, sess);
+sess_resc_fail:
+ scsi_host_put(shost);
+ return NULL;
+}
+
+
+/*
+ * Function: bnx2i_session_destroy
+ * Description: Destroys previously created iSCSI session instance.
+ */
+void bnx2i_session_destroy(struct iscsi_cls_session *cls_session)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct bnx2i_sess *sess = iscsi_hostdata(shost->hostdata);
+ struct module *owner = cls_session->transport->owner;
+
+ if (sess) {
+ bnx2i_iscsi_sess_release(sess->hba, sess);
+ }
+
+ if (sess->target_name) {
+ kfree(sess->target_name);
+ sess->target_name = NULL;
+ }
+
+ scsi_remove_host(shost);
+ iscsi_destroy_session(cls_session);
+ scsi_host_put(shost);
+ module_put(owner);
+}
+
+
+/*
+ * Function: bnx2i_sess_recovery_timeo
+ * Description: session recovery timeout handling routine
+ */
+void bnx2i_sess_recovery_timeo(struct iscsi_cls_session *cls_session)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct bnx2i_sess *sess = iscsi_hostdata(shost->hostdata);
+
+ sess->recovery_state |= ISCSI_SESS_RECOVERY_FAILED;
+ if (sess->state != BNX2I_SESS_IN_FFP) {
+ }
+ wake_up(&sess->er_wait);
+}
+
+
+/*
+ * Function: bnx2i_conn_create
+ * Description: Creates a new iSCSI connection instance for a given session
+ */
+struct iscsi_cls_conn *bnx2i_conn_create(struct iscsi_cls_session *cls_session,
+ uint32_t cid)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct bnx2i_sess *sess = iscsi_hostdata(shost->hostdata);
+ struct bnx2i_conn *conn;
+ struct iscsi_cls_conn *cls_conn;
+
+ cls_conn = iscsi_create_conn(cls_session, cid);
+ if (!cls_conn)
+ return NULL;
+
+ conn = cls_conn->dd_data;
+ memset(conn, 0, sizeof(struct bnx2i_conn));
+ conn->cls_conn = cls_conn;
+ conn->exp_statsn = STATSN_UPDATE_SIGNATURE;
+ conn->iscsi_conn_cid = conn->fw_cid = 0;
+ conn->header_digest_en = 0;
+ conn->data_digest_en = 0;
+ conn->persist_address = NULL;
+ conn->state = CONN_STATE_IDLE;
+ /*
+ * Initialize the connection structure
+ */
+ bnx2i_iscsi_conn_new(sess, conn);
+ conn->conn_cid = cid;
+ return cls_conn;
+}
+
+
+
+/*
+ * Function: bnx2i_conn_bind
+ * Description: Binds together iSCSI session instance, iSCSI connection
+ * instance and the TCP connection. If TCP connection does not belong
+ * on the device iSCSI sess/conn is bound, return failure to user.
+ */
+int bnx2i_conn_bind(struct iscsi_cls_session *cls_session,
+ struct iscsi_cls_conn *cls_conn,
+ uint64_t transport_fd, int is_leading)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct bnx2i_sess *sess = iscsi_hostdata(shost->hostdata);
+ struct bnx2i_conn *tmp = ERR_PTR(-EEXIST);
+ struct bnx2i_conn *conn = cls_conn->dd_data;
+ int ret_code = 0;
+ struct bnx2i_endpoint *ep;
+
+ ep = (struct bnx2i_endpoint *) (unsigned long) transport_fd;
+
+ if (ep->state == EP_STATE_PEER_DISCONN) {
+ /* Peer disconnect via' FIN or RST */
+ return -EINVAL;
+ }
+
+ if (ep->hba != sess->hba) {
+ /* Error - TCP connection does not belong to this device
+ */
+ printk(KERN_ALERT "bnx2i: conn bind, ep=0x%p (0x%p) does not",
+ ep, ep->hba);
+ printk(KERN_ALERT "belong to hba 0x%p\n", sess->hba);
+ return -EEXIST;
+ }
+ if (!conn->gen_pdu.cmd)
+ conn->gen_pdu.cmd = bnx2i_alloc_cmd(sess);
+
+ /* look-up for existing connection, MC/S is not currently supported */
+ spin_lock_bh(&sess->lock);
+ tmp = NULL;
+ if (!list_empty(&sess->conn_list)) {
+ list_for_each_entry(tmp, &sess->conn_list, link) {
+ if (tmp == conn) {
+ break;
+ }
+ }
+ }
+ if ((tmp != conn) && (conn->sess == sess)) {
+ /* bind iSCSI connection to this session */
+ list_add(&conn->link, &sess->conn_list);
+ if (is_leading) {
+ sess->lead_conn = conn;
+ }
+ }
+
+ conn->ep = (struct bnx2i_endpoint *) (unsigned long) transport_fd;
+ conn->ep->conn = conn;
+ conn->ep->sess = sess;
+ conn->state = CONN_STATE_XPORT_READY;
+ conn->iscsi_conn_cid = conn->ep->ep_iscsi_cid;
+ conn->fw_cid = conn->ep->ep_cid;
+
+ bnx2i_bind_conn_to_iscsi_cid(conn, ep->ep_iscsi_cid);
+
+ spin_unlock_bh(&sess->lock);
+ return ret_code;
+}
+
+
+/*
+ * Function: bnx2i_conn_destroy
+ * Description: Destroys a iSCSI connection instance.
+ */
+void bnx2i_conn_destroy(struct iscsi_cls_conn *cls_conn)
+{
+ struct bnx2i_conn *conn = cls_conn->dd_data;
+
+ bnx2i_conn_free_login_resources(conn->sess->hba, conn);
+
+ if (conn->persist_address) {
+ kfree(conn->persist_address);
+ conn->persist_address = NULL;
+ }
+ iscsi_destroy_conn(cls_conn);
+}
+
+
+/*
+ * Function: bnx2i_conn_set_param
+ * Description: During FFP migration, user daemon will issue this call to
+ * update negotiated iSCSI parameters to driver.
+ */
+int bnx2i_conn_set_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf, int buflen)
+{
+ struct bnx2i_conn *conn = cls_conn->dd_data;
+ struct bnx2i_sess *sess = conn->sess;
+
+ spin_lock_bh(&sess->lock);
+ if (conn->state != CONN_STATE_IN_LOGIN) {
+ printk(KERN_ERR "bnx2i: can't change param [%d]\n", param);
+ spin_unlock_bh(&sess->lock);
+ return 0;
+ }
+ spin_unlock_bh(&sess->lock);
+ switch (param) {
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ sscanf(buf, "%d", &conn->max_data_seg_len_recv);
+ break;
+ case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+ sscanf(buf, "%d", &conn->max_data_seg_len_xmit);
+ break;
+ case ISCSI_PARAM_HDRDGST_EN:
+ sscanf(buf, "%d", &conn->header_digest_en);
+ break;
+ case ISCSI_PARAM_DATADGST_EN:
+ sscanf(buf, "%d", &conn->data_digest_en);
+ break;
+ case ISCSI_PARAM_INITIAL_R2T_EN:
+ if (conn == sess->lead_conn) {
+ sscanf(buf, "%d", &sess->initial_r2t);
+ }
+ break;
+ case ISCSI_PARAM_MAX_R2T:
+ if (conn == sess->lead_conn) {
+ sscanf(buf, "%d", &sess->max_r2t);
+ }
+ break;
+ case ISCSI_PARAM_IMM_DATA_EN:
+ if (conn == sess->lead_conn) {
+ sscanf(buf, "%d", &sess->imm_data);
+ }
+ break;
+ case ISCSI_PARAM_FIRST_BURST:
+ if (conn == sess->lead_conn) {
+ sscanf(buf, "%d", &sess->first_burst_len);
+ }
+ break;
+ case ISCSI_PARAM_MAX_BURST:
+ if (conn == sess->lead_conn) {
+ sscanf(buf, "%d", &sess->max_burst_len);
+ }
+ break;
+ case ISCSI_PARAM_PDU_INORDER_EN:
+ if (conn == sess->lead_conn) {
+ sscanf(buf, "%d", &sess->pdu_inorder);
+ }
+ break;
+ case ISCSI_PARAM_DATASEQ_INORDER_EN:
+ if (conn == sess->lead_conn) {
+ sscanf(buf, "%d", &sess->dataseq_inorder);
+ }
+ break;
+ case ISCSI_PARAM_ERL:
+ if (conn == sess->lead_conn) {
+ sscanf(buf, "%d", &sess->erl);
+ }
+ break;
+ case ISCSI_PARAM_IFMARKER_EN:
+ sscanf(buf, "%d", &conn->ifmarker_enable);
+ BUG_ON(conn->ifmarker_enable);
+ break;
+ case ISCSI_PARAM_OFMARKER_EN:
+ sscanf(buf, "%d", &conn->ofmarker_enable);
+ BUG_ON(conn->ofmarker_enable);
+ break;
+ case ISCSI_PARAM_EXP_STATSN:
+ sscanf(buf, "%u", &conn->exp_statsn);
+ break;
+ case ISCSI_PARAM_TARGET_NAME:
+ if (sess->target_name)
+ break;
+ sess->target_name = kstrdup(buf, GFP_KERNEL);
+ if (!sess->target_name)
+ return -ENOMEM;
+ break;
+ case ISCSI_PARAM_TPGT:
+ sscanf(buf, "%d", &sess->tgt_prtl_grp);
+ break;
+ case ISCSI_PARAM_PERSISTENT_PORT:
+ {
+ sscanf(buf, "%d", &conn->persist_port);
+ }
+ break;
+ case ISCSI_PARAM_PERSISTENT_ADDRESS:
+ if (conn->persist_address)
+ break;
+ conn->persist_address = kstrdup(buf, GFP_KERNEL);
+ if (!conn->persist_address)
+ return -ENOMEM;
+ break;
+ default:
+ printk(KERN_ALERT "PARAM_UNKNOWN: 0x%x\n", param);
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Function: bnx2i_conn_get_param
+ * Description: Call to retrieve iSCSI connection parameters
+ */
+int bnx2i_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, char *buf)
+{
+ struct bnx2i_conn *conn;
+ int len = 0;
+
+ if (!cls_conn)
+ return -EINVAL;
+ conn = (struct bnx2i_conn *)cls_conn->dd_data;
+ if (!conn || !conn->ep ||
+ (conn->ep->state != EP_STATE_ULP_UPDATE_COMPL))
+ return -EINVAL;
+
+ switch (param) {
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ len = sprintf(buf, "%u\n", conn->max_data_seg_len_recv);
+ break;
+ case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+ len = sprintf(buf, "%u\n", conn->max_data_seg_len_xmit);
+ break;
+ case ISCSI_PARAM_HDRDGST_EN:
+ len = sprintf(buf, "%d\n", conn->header_digest_en);
+ break;
+ case ISCSI_PARAM_DATADGST_EN:
+ len = sprintf(buf, "%d\n", conn->data_digest_en);
+ break;
+ case ISCSI_PARAM_IFMARKER_EN:
+ len = sprintf(buf, "%u\n", conn->ifmarker_enable);
+ break;
+ case ISCSI_PARAM_OFMARKER_EN:
+ len = sprintf(buf, "%u\n", conn->ofmarker_enable);
+ break;
+ case ISCSI_PARAM_EXP_STATSN:
+ len = sprintf(buf, "%u\n", conn->exp_statsn);
+ break;
+ case ISCSI_PARAM_PERSISTENT_PORT:
+ len = sprintf(buf, "%d\n", conn->persist_port);
+ break;
+ case ISCSI_PARAM_PERSISTENT_ADDRESS:
+ if (conn->persist_address) {
+ len = sprintf(buf, "%s\n", conn->persist_address);
+ }
+ break;
+ case ISCSI_PARAM_CONN_PORT:
+ len = sprintf(buf, "%hu\n", conn->ep->cm_sk->dst_port);
+ break;
+ case ISCSI_PARAM_CONN_ADDRESS:
+ len = sprintf(buf, NIPQUAD_FMT "\n",
+ NIPQUAD(conn->ep->cm_sk->dst_ip));
+ break;
+ default:
+ printk(KERN_ALERT "get_param: conn 0x%p param %d not found\n",
+ conn, (u32)param);
+ return -ENOSYS;
+ }
+
+ return len;
+}
+
+
+/*
+ * Function: bnx2i_session_get_param
+ * Description: Call to obtain iSCSI session parameters
+ */
+int bnx2i_session_get_param(struct iscsi_cls_session *cls_session,
+ enum iscsi_param param, char *buf)
+{
+ struct Scsi_Host *shost = NULL;
+ struct bnx2i_sess *sess = NULL;
+ int len = 0;
+
+ if (!cls_session)
+ return -EINVAL;
+
+ shost = iscsi_session_to_shost(cls_session);
+ sess = iscsi_hostdata(shost->hostdata);
+ if (!sess || !sess->lead_conn)
+ return -EINVAL;
+
+ switch (param) {
+ case ISCSI_PARAM_INITIAL_R2T_EN:
+ len = sprintf(buf, "%d\n", sess->initial_r2t);
+ break;
+ case ISCSI_PARAM_MAX_R2T:
+ len = sprintf(buf, "%hu\n", sess->max_r2t);
+ break;
+ case ISCSI_PARAM_IMM_DATA_EN:
+ len = sprintf(buf, "%d\n", sess->imm_data);
+ break;
+ case ISCSI_PARAM_FIRST_BURST:
+ len = sprintf(buf, "%u\n", sess->first_burst_len);
+ break;
+ case ISCSI_PARAM_MAX_BURST:
+ len = sprintf(buf, "%u\n", sess->max_burst_len);
+ break;
+ case ISCSI_PARAM_PDU_INORDER_EN:
+ len = sprintf(buf, "%d\n", sess->pdu_inorder);
+ break;
+ case ISCSI_PARAM_DATASEQ_INORDER_EN:
+ len = sprintf(buf, "%d\n", sess->dataseq_inorder);
+ break;
+ case ISCSI_PARAM_ERL:
+ len = sprintf(buf, "%d\n", sess->erl);
+ break;
+ case ISCSI_PARAM_TARGET_NAME:
+ if (sess->target_name) {
+ len = sprintf(buf, "%s\n", sess->target_name);
+ }
+ break;
+ case ISCSI_PARAM_TPGT:
+ len = sprintf(buf, "%d\n", sess->tgt_prtl_grp);
+ break;
+ default:
+ printk(KERN_ALERT "sess_get_param: sess 0x%p", sess);
+ printk(KERN_ALERT "param (0x%x) not found\n", (u32) param);
+ return -ENOSYS;
+ }
+
+ return len;
+}
+
+
+/*
+ * Function: bnx2i_conn_start
+ * Description: last call in FFP migration to handover iscsi conn to the driver
+ */
+int bnx2i_conn_start(struct iscsi_cls_conn *cls_conn)
+{
+ struct bnx2i_conn *conn = (struct bnx2i_conn *) cls_conn->dd_data;
+ struct bnx2i_sess *sess = conn->sess;
+
+ if (conn->state != CONN_STATE_IN_LOGIN) {
+ printk(KERN_ALERT "conn_start: conn 0x%p state 0x%x err!!\n",
+ conn, conn->state);
+ return -EINVAL;
+ }
+
+ if (!sess->initial_r2t) {
+ if (sess->first_burst_len > sess->max_burst_len)
+ return -EINVAL;
+ } else if (conn->max_data_seg_len_xmit > sess->max_burst_len) {
+ if (sess->first_burst_len > sess->max_burst_len)
+ return -EINVAL;
+ /* don't bother if only immediate data is supported and
+ * FBL & MBL are greater than MRDSL. In that case initiator
+ * will always send MRDSL worth of immediate data
+ */
+ }
+
+ conn->state = CONN_STATE_FFP_STATE;
+ if (conn->sess->lead_conn == conn) {
+ conn->sess->state = BNX2I_SESS_IN_FFP;
+ }
+
+ conn->ep->state = EP_STATE_ULP_UPDATE_START;
+ bnx2i_update_iscsi_conn(conn);
+
+ conn->ep->ofld_timer.expires = 10*HZ + jiffies;
+ conn->ep->ofld_timer.function = bnx2i_ep_ofld_timer;
+ conn->ep->ofld_timer.data = (unsigned long)conn->ep;
+ add_timer(&conn->ep->ofld_timer);
+ /* update iSCSI context for this conn, wait for CNIC to complete */
+ wait_event_interruptible(conn->ep->ofld_wait,
+ conn->ep->state != EP_STATE_ULP_UPDATE_START);
+
+ if (signal_pending(current))
+ flush_signals(current);
+ del_timer_sync(&conn->ep->ofld_timer);
+ if (conn->ep->state != EP_STATE_ULP_UPDATE_COMPL) {
+ /* should never happen */
+ }
+ /* Free login ITT, not required anymore */
+ if (conn->gen_pdu.cmd) {
+ bnx2i_free_cmd(conn->sess, conn->gen_pdu.cmd);
+ conn->gen_pdu.cmd = NULL;
+ }
+
+ switch (conn->stop_state) {
+ case STOP_CONN_RECOVER:
+ sess->recovery_state = ISCSI_SESS_RECOVERY_COMPLETE;
+ conn->sess->state = BNX2I_SESS_IN_FFP;
+ iscsi_unblock_session(session_to_cls(sess));
+ wake_up(&sess->er_wait);
+ break;
+ case STOP_CONN_TERM:
+ break;
+ default:
+ ;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Function: bnx2i_conn_stop
+ * Description: call to take control of iscsi conn from the driver.
+ * Could be called when login failed, when recovery is to be
+ * attempted or during connection teardown
+ */
+void bnx2i_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
+{
+ struct bnx2i_conn *conn = (struct bnx2i_conn *)cls_conn->dd_data;
+
+ conn->stop_state = flag;
+ iscsi_block_session(session_to_cls(conn->sess));
+
+ switch (flag) {
+ case STOP_CONN_RECOVER:
+ conn->sess->state = BNX2I_SESS_IN_RECOVERY;
+ break;
+ case STOP_CONN_TERM:
+ if (conn->sess && (conn->sess->state & BNX2I_SESS_IN_FFP)) {
+ conn->sess->state = BNX2I_SESS_IN_SHUTDOWN;
+ }
+ break;
+ default:
+ printk(KERN_ERR "bnx2i: invalid conn stop req %d\n", flag);
+ }
+
+ return;
+}
+
+
+/*
+ * Function: bnx2i_conn_send_pdu
+ * Description: To send iSCSI PDUs prepared by user daemon, only login, logout,
+ * nop-out pdu's should flow this path.
+ */
+int bnx2i_conn_send_pdu(struct iscsi_cls_conn *cls_conn,
+ struct iscsi_hdr *hdr, char *data,
+ uint32_t data_size)
+{
+ struct bnx2i_conn *conn = NULL;
+ struct iscsi_hdr *iscsi_hdr = (struct iscsi_hdr *) hdr;
+ struct bnx2i_cmd *cmnd = NULL;
+ uint32_t payload_size = 0;
+ int rc;
+ unsigned long flags;
+
+ if (!cls_conn) {
+ printk(KERN_ALERT "bnx2i_conn_send_pdu: NULL conn ptr. \n");
+ return -EIO;
+ }
+ conn = (struct bnx2i_conn *)cls_conn->dd_data;
+ if (!conn->gen_pdu.req_buf) {
+ printk(KERN_ALERT "send_pdu: login buf not allocated\n");
+ /* ERR - buffer not allocated, should not happen */
+ return -EIO;
+ }
+
+ if (conn->gen_pdu.cmd) {
+ if ((conn->state != CONN_STATE_XPORT_READY) &&
+ (conn->state != CONN_STATE_IN_LOGIN)) {
+ printk(KERN_ALERT "send_pdu: %d != XPORT_READY\n",
+ conn->state);
+ return -EPERM;
+ }
+ cmnd = conn->gen_pdu.cmd;
+ } else { /* could be NOPOUT or the LOGOUT request */
+ spin_lock_irqsave(conn->sess->host->host_lock, flags);
+ cmnd = bnx2i_alloc_cmd(conn->sess);
+ spin_unlock_irqrestore(conn->sess->host->host_lock, flags);
+
+ if (!cmnd) {
+ printk(KERN_ALERT "bnx2i: Error - cmd not allocated\n");
+ return -EIO;
+ }
+ }
+ memset(conn->gen_pdu.req_buf, 0, ISCSI_CONN_LOGIN_BUF_SIZE);
+ /* Login request, copy hdr & data to buffer in conn struct */
+ memcpy((void *) &conn->gen_pdu.pdu_hdr, (const void *) hdr,
+ sizeof(struct iscsi_hdr));
+
+ cmnd->iscsi_opcode = iscsi_hdr->opcode;
+ switch (iscsi_hdr->opcode & ISCSI_OPCODE_MASK) {
+ case ISCSI_OP_LOGIN:
+ if (conn->state == CONN_STATE_XPORT_READY)
+ conn->state = CONN_STATE_IN_LOGIN;
+ break;
+ case ISCSI_OP_LOGOUT:
+ conn->state = CONN_STATE_IN_LOGOUT;
+ conn->sess->state = BNX2I_SESS_IN_LOGOUT;
+ break;
+ case ISCSI_OP_NOOP_OUT:
+ break;
+ default:
+ ;
+ }
+
+ conn->gen_pdu.req_buf_size = data_size;
+ payload_size = (hdr->dlength[0] << 16) | (hdr->dlength[1] << 8) |
+ hdr->dlength[2];
+
+ if (data_size) {
+ memcpy((void *)conn->gen_pdu.req_buf, (const void *)data,
+ data_size);
+ conn->gen_pdu.req_wr_ptr =
+ conn->gen_pdu.req_buf + payload_size;
+ }
+ cmnd->conn = conn;
+ cmnd->scsi_cmd = NULL;
+ rc = bnx2i_iscsi_send_generic_request(cmnd);
+ return rc;
+}
+
+
+/*
+ * Function : bnx2i_conn_get_stats
+ * Description: Returns iSCSI stats
+ */
+void bnx2i_conn_get_stats(struct iscsi_cls_conn *cls_conn,
+ struct iscsi_stats *stats)
+{
+ struct bnx2i_conn *conn = (struct bnx2i_conn *) cls_conn->dd_data;
+
+ stats->txdata_octets = conn->total_data_octets_sent;
+ stats->rxdata_octets = conn->total_data_octets_rcvd;
+
+ stats->noptx_pdus = conn->num_nopin_pdus;
+ stats->scsicmd_pdus = conn->num_scsi_cmd_pdus;
+ stats->tmfcmd_pdus = conn->num_tmf_req_pdus;
+ stats->login_pdus = conn->num_login_req_pdus;
+ stats->text_pdus = 0;
+ stats->dataout_pdus = conn->num_dataout_pdus;
+ stats->logout_pdus = conn->num_logout_req_pdus;
+ stats->snack_pdus = 0;
+
+ stats->noprx_pdus = conn->num_nopout_pdus;
+ stats->scsirsp_pdus = conn->num_scsi_resp_pdus;
+ stats->tmfrsp_pdus = conn->num_tmf_resp_pdus;
+ stats->textrsp_pdus = 0;
+ stats->datain_pdus = conn->num_datain_pdus;
+ stats->logoutrsp_pdus = conn->num_logout_resp_pdus;
+ stats->r2t_pdus = conn->num_r2t_pdus;
+ stats->async_pdus = conn->num_async_pdus;
+ stats->rjt_pdus = conn->num_reject_pdus;
+
+ stats->digest_err = 0;
+ stats->timeout_err = 0;
+ stats->custom_length = 0;
+}
+
+
+
+/*
+ * Function : bnx2i_check_nx2_dev_busy
+ * Description: this routine unregister devices if there are no active conns
+ */
+static void bnx2i_check_nx2_dev_busy(void)
+{
+ if (bnx2i_num_free_ep == bnx2i_max_free_ep) {
+ bnx2i_unreg_dev_all();
+ msleep(2);
+ }
+}
+
+
+/*
+ * Function : bnx2i_ep_connect
+ * Description: this routine initiates the TCP/IP connection by invoking
+ * Option-2 interface with l5_core and the CNIC
+ */
+int bnx2i_ep_connect(struct sockaddr *dst_addr, int non_blocking,
+ uint64_t *ep_handle)
+{
+ u32 iscsi_cid = BNX2I_CID_RESERVED;
+ struct sockaddr_in *desti = (struct sockaddr_in *) dst_addr;
+ struct bnx2i_endpoint *endpoint;
+ struct bnx2i_hba *hba = NULL;
+ struct cnic_dev *cnic = NULL;
+ struct cnic_dev *tmp_cnic = NULL;
+ struct bnx2i_hba *tmp_hba = NULL;
+ struct cnic_sockaddr saddr;
+ int rc = 0;
+ extern int bnx2i_reg_device;
+ extern struct bnx2i_hba *get_adapter_list_head(void);
+
+ /*
+ * check if the given destination can be reached through NX2 device
+ */
+
+ if ((!bnx2i_reg_device) &&
+ (bnx2i_num_free_ep == bnx2i_max_free_ep)) {
+ bnx2i_reg_dev_all();
+ }
+ tmp_hba = get_adapter_list_head();
+ if (tmp_hba && tmp_hba->cnic) {
+ tmp_cnic = tmp_hba->cnic;
+ cnic = tmp_cnic->cm_select_dev(desti, CNIC_ULP_ISCSI);
+ }
+ if (!cnic) {
+ printk(KERN_ALERT "bnx2i: ep_conn, can't connect using cnic\n");
+ rc = 0;
+ goto check_busy;
+ }
+ hba = bnx2i_find_hba_for_cnic(cnic);
+
+ if (bnx2i_adapter_ready(hba)) {
+ printk(KERN_ALERT "bnx2i: ep_conn, adapter not found\n");
+ rc = 0;
+ goto check_busy;
+ }
+ if (hba->netdev->mtu > hba->mtu_supported) {
+ printk(KERN_ALERT "bnx2i: %s network i/f mtu is set to %d\n",
+ hba->netdev->name, hba->netdev->mtu);
+ printk(KERN_ALERT "bnx2i: iSCSI HBA can support mtu of %d\n",
+ hba->mtu_supported);
+ rc = 0;
+ goto check_busy;
+ }
+ endpoint = bnx2i_alloc_ep();
+ if (!endpoint) {
+ printk(KERN_ALERT "bnx2i: ep_conn, unable to alloc ep\n");
+ *ep_handle = (uint64_t) 0;
+ rc = -ENOMEM;
+ goto check_busy;
+ }
+
+ endpoint->ep_iscsi_cid = (u16)ISCSI_RESERVED_TAG;
+ iscsi_cid = bnx2i_alloc_iscsi_cid(hba);
+ if (iscsi_cid == (u16) ISCSI_RESERVED_TAG) {
+ printk(KERN_ALERT "alloc_ep: unable to allocate iscsi cid\n");
+ rc = -ENOMEM;
+ goto iscsi_cid_err;
+ }
+ endpoint->hba = hba;
+ endpoint->hba_age = hba->age;
+
+ rc = bnx2i_alloc_qp_resc(hba, endpoint);
+ if (rc != 0) {
+ printk(KERN_ALERT "bnx2i: ep_conn, alloc QP resc error\n");
+ rc = -ENOMEM;
+ goto qp_resc_err;
+ }
+
+ endpoint->ep_iscsi_cid = iscsi_cid;
+ endpoint->state = EP_STATE_OFLD_START;
+ bnx2i_ep_ofld_list_add(hba, endpoint);
+
+ bnx2i_send_conn_ofld_req(hba, endpoint);
+
+ init_timer(&endpoint->ofld_timer);
+ endpoint->ofld_timer.expires = 2 * HZ + jiffies;
+ endpoint->ofld_timer.function = bnx2i_ep_ofld_timer;
+ endpoint->ofld_timer.data = (unsigned long) endpoint;
+ add_timer(&endpoint->ofld_timer);
+ /* Wait for CNIC hardware to setup conn context and return 'cid' */
+ wait_event_interruptible(endpoint->ofld_wait,
+ endpoint->state != EP_STATE_OFLD_START);
+
+ if (signal_pending(current))
+ flush_signals(current);
+ del_timer_sync(&endpoint->ofld_timer);
+ list_del_init(&endpoint->link);
+
+ if (endpoint->state != EP_STATE_OFLD_COMPL) {
+ rc = -ENOSPC;
+ goto conn_failed;
+ }
+
+ rc = -EINVAL;
+ if (hba->reg_with_cnic)
+ rc = cnic->cm_create(cnic, CNIC_ULP_ISCSI, endpoint->ep_cid,
+ iscsi_cid, &endpoint->cm_sk, endpoint);
+ if (rc)
+ goto conn_failed;
+
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.remote_addr.sin_addr.s_addr = desti->sin_addr.s_addr;
+ saddr.remote_addr.sin_port = desti->sin_port;
+ saddr.local_addr.sin_port = htons(endpoint->tcp_port);
+ endpoint->state = EP_STATE_CONNECT_START;
+ rc = -EINVAL;
+ if (hba->reg_with_cnic)
+ rc = cnic->cm_connect(endpoint->cm_sk, &saddr);
+ else
+ goto conn_failed;
+
+ if (rc)
+ goto release_ep;
+
+ bnx2i_map_ep_dbell_regs(endpoint);
+
+ *ep_handle = (uint64_t) (unsigned long) endpoint;
+ return 0;
+
+release_ep:
+ cnic->cm_destroy(endpoint->cm_sk);
+conn_failed:
+iscsi_cid_err:
+ bnx2i_free_qp_resc(hba, endpoint);
+qp_resc_err:
+ bnx2i_free_ep(endpoint);
+check_busy:
+ *ep_handle = (uint64_t) 0;
+ bnx2i_check_nx2_dev_busy();
+ return rc;
+}
+
+
+
+/*
+ * Function : bnx2i_ep_poll
+ * Description: polls for TCP connect request to complete
+ */
+int bnx2i_ep_poll(uint64_t ep_handle, int timeout_ms)
+{
+ struct bnx2i_endpoint *ep;
+ int rc = 0;
+ ep = (struct bnx2i_endpoint *) (unsigned long) ep_handle;
+
+ if (!ep) {
+ return -EINVAL;
+ }
+ if (ep->state == EP_STATE_IDLE) {
+ return -1;
+ }
+ if (ep->state == EP_STATE_CONNECT_COMPL) {
+ return 1;
+ }
+
+ rc = wait_event_interruptible_timeout(ep->ofld_wait,
+ (ep->state ==
+ EP_STATE_CONNECT_COMPL),
+ msecs_to_jiffies(timeout_ms));
+ if (!rc || (ep->state == EP_STATE_OFLD_FAILED)) {
+ rc = -1;
+ }
+
+ if (rc > 0) {
+ return 1;
+ } else if (!rc) {
+ return 0; /* timeout */
+ } else {
+ return rc;
+ }
+}
+
+
+/*
+ * Function : bnx2i_ep_disconnect
+ * Description: initiates TCP/IP connection teardown process
+ */
+void bnx2i_ep_disconnect(uint64_t ep_handle)
+{
+ struct bnx2i_endpoint *ep;
+ struct cnic_dev *cnic = NULL;
+ struct bnx2i_sess *sess = NULL;
+ int rc = 0;
+
+ ep = (struct bnx2i_endpoint *) (unsigned long) ep_handle;
+ if (!ep || (ep_handle == -1)) {
+ return;
+ }
+ if (ep->state == EP_STATE_IDLE) {
+ goto return_ep;
+ }
+ cnic = ep->hba->cnic;
+
+ if (ep->state == EP_STATE_PEER_DISCONN) {
+ ep->state = EP_STATE_DISCONN_COMPL;
+ goto peer_discon;
+ }
+
+ if (test_bit(ADAPTER_STATE_DOWN, &ep->hba->adapter_state)) {
+ goto free_resc;
+ }
+ if (ep->hba_age != ep->hba->age) {
+ goto dev_reset;
+ }
+
+ ep->state = EP_STATE_DISCONN_START;
+
+ init_timer(&ep->ofld_timer);
+ ep->ofld_timer.expires = 10*HZ + jiffies;
+ ep->ofld_timer.function = bnx2i_ep_ofld_timer;
+ ep->ofld_timer.data = (unsigned long) ep;
+ add_timer(&ep->ofld_timer);
+
+ if (ep->hba->reg_with_cnic)
+ cnic->cm_close(ep->cm_sk);
+ else
+ goto free_resc;
+
+ /* wait for option-2 conn teardown */
+ wait_event_interruptible(ep->ofld_wait,
+ ep->state != EP_STATE_DISCONN_START);
+
+ if (signal_pending(current))
+ flush_signals(current);
+ del_timer_sync(&ep->ofld_timer);
+
+peer_discon:
+ if (!ep->hba->reg_with_cnic)
+ goto free_resc;
+
+ rc = cnic->cm_destroy(ep->cm_sk);
+ ep->state = EP_STATE_CLEANUP_START;
+ init_timer(&ep->ofld_timer);
+ ep->ofld_timer.expires = 10*HZ + jiffies;
+ ep->ofld_timer.function = bnx2i_ep_ofld_timer;
+ ep->ofld_timer.data = (unsigned long) ep;
+ add_timer(&ep->ofld_timer);
+
+ bnx2i_ep_destroy_list_add(ep->hba, ep);
+
+ /* destroy iSCSI context, wait for it to complete */
+ bnx2i_send_conn_destroy(ep->hba, ep);
+ wait_event_interruptible(ep->ofld_wait,
+ (ep->state != EP_STATE_CLEANUP_START));
+
+ if (signal_pending(current))
+ flush_signals(current);
+ del_timer_sync(&ep->ofld_timer);
+ if (ep->state != EP_STATE_CLEANUP_CMPL) {
+ /* should never happen */
+ printk(KERN_ALERT "bnx2i - conn destroy failed\n");
+ }
+
+dev_reset:
+ bnx2i_flush_active_cmd_queue(ep->sess, DID_NO_CONNECT);
+
+free_resc:
+ bnx2i_free_qp_resc(ep->hba, ep);
+return_ep:
+ if (ep->conn && ep->conn->sess)
+ /* session recovery in progress */
+ sess = ep->conn->sess;
+ if (sess && (sess->state != BNX2I_SESS_IN_RECOVERY))
+ /* session logged out */
+ sess = NULL;
+
+ bnx2i_free_ep(ep);
+ if (sess) {
+ wake_up(&sess->er_wait);
+ }
+
+ bnx2i_check_nx2_dev_busy();
+ return;
+}
+
+
+
+
+/*
+ * 'Scsi_Host_Template' structure and 'iscsi_tranport' structure template
+ * used while registering with the iSCSI trnaport module.
+ */
+struct scsi_host_template bnx2i_host_template = {
+ .module = THIS_MODULE,
+ .name = "Broadcom Offload iSCSI Initiator",
+ .queuecommand = bnx2i_queuecommand,
+ .eh_abort_handler = bnx2i_abort,
+ .eh_host_reset_handler = bnx2i_host_reset,
+ .bios_param = NULL,
+ .can_queue = 128,
+ .max_sectors = 256,
+ .this_id = -1,
+ .cmd_per_lun = 64,
+ .use_clustering = ENABLE_CLUSTERING,
+ .sg_tablesize = ISCSI_MAX_BDS_PER_CMD,
+ .proc_name = NULL
+ };
+
+
+
+struct iscsi_transport bnx2i_iscsi_transport = {
+ .owner = THIS_MODULE,
+ .name = "bnx2i",
+ .caps = CAP_RECOVERY_L0 | CAP_HDRDGST | CAP_MULTI_R2T
+ | CAP_DATADGST,
+ .param_mask = ISCSI_MAX_RECV_DLENGTH |
+ ISCSI_MAX_XMIT_DLENGTH |
+ ISCSI_HDRDGST_EN |
+ ISCSI_DATADGST_EN |
+ ISCSI_INITIAL_R2T_EN |
+ ISCSI_MAX_R2T |
+ ISCSI_IMM_DATA_EN |
+ ISCSI_FIRST_BURST |
+ ISCSI_MAX_BURST |
+ ISCSI_PDU_INORDER_EN |
+ ISCSI_DATASEQ_INORDER_EN |
+ ISCSI_ERL |
+ ISCSI_CONN_PORT |
+ ISCSI_CONN_ADDRESS |
+ ISCSI_EXP_STATSN |
+ ISCSI_PERSISTENT_PORT |
+ ISCSI_PERSISTENT_ADDRESS |
+ ISCSI_TARGET_NAME |
+ ISCSI_TPGT,
+ .host_param_mask = 0,
+ .host_template = &bnx2i_host_template,
+ .sessiondata_size = sizeof(struct bnx2i_sess),
+ .conndata_size = sizeof(struct bnx2i_conn),
+ .max_conn = 1,
+ .max_cmd_len = 16,
+ .max_lun = 512,
+ .create_session = bnx2i_session_create,
+ .destroy_session = bnx2i_session_destroy,
+ .create_conn = bnx2i_conn_create,
+ .bind_conn = bnx2i_conn_bind,
+ .destroy_conn = bnx2i_conn_destroy,
+ .set_param = bnx2i_conn_set_param,
+ .get_conn_param = bnx2i_conn_get_param,
+ .get_session_param = bnx2i_session_get_param,
+ .start_conn = bnx2i_conn_start,
+ .stop_conn = bnx2i_conn_stop,
+ .send_pdu = bnx2i_conn_send_pdu,
+ .get_stats = bnx2i_conn_get_stats,
+ /* iscsi host params */
+ .get_host_param = NULL,
+ .set_host_param = NULL,
+ /* TCP connect - disconnect - option-2 interface calls */
+ .ep_connect = bnx2i_ep_connect,
+ .ep_poll = bnx2i_ep_poll,
+ .ep_disconnect = bnx2i_ep_disconnect,
+ /* Error recovery timeout call */
+ .session_recovery_timedout = bnx2i_sess_recovery_timeo
+};
diff --git a/drivers/scsi/bnx2i/bnx2i_sysfs.c b/drivers/scsi/bnx2i/bnx2i_sysfs.c
new file mode 100644
index 0000000..6bd6eba
--- /dev/null
+++ b/drivers/scsi/bnx2i/bnx2i_sysfs.c
@@ -0,0 +1,616 @@
+/* bnx2i_sysfs.c: Broadcom NetXtreme II iSCSI driver.
+ *
+ * Copyright (c) 2004, 2005, 2006 Broadcom Corporation
+ *
+ * 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: Anil Veerabhadrappa (anilgv@broadcom.com)
+ */
+
+#include "bnx2i.h"
+
+#define BNX2I_SYSFS_VERSION 0x2
+
+static ssize_t bnx2i_show_mips_status(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+ ssize_t len = 0;
+
+ bnx2i_read_mips_idle_counters(hba);
+
+ len = sprintf(buf, "%d\n%lu\n%d\n%llu\n%llu\n"
+ "%llu\n%llu\n%llu\n%llu\n%llu\n%llu\n",
+ BNX2I_SYSFS_VERSION, jiffies, HZ,
+ hba->mips_idle.cp_idle_count,
+ hba->mips_idle.txp_idle_count,
+ hba->mips_idle.txp_tdma_count,
+ hba->mips_idle.txp_ctx_count,
+ hba->mips_idle.txp_hdrq_count,
+ hba->mips_idle.tpat_idle_count,
+ hba->mips_idle.rxp_idle_count,
+ hba->mips_idle.com_idle_count);
+ return (len);
+}
+
+static ssize_t bnx2i_show_net_if_name(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ return sprintf(buf, "%s\n", hba->netdev->name);
+}
+
+static ssize_t bnx2i_show_pci_bar(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ return sprintf(buf, "0x%.8x\n",
+ (u32) pci_resource_start(hba->pci_dev, 0));
+}
+
+static ssize_t bnx2i_show_sq_info(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ return sprintf(buf, "0x%x\n", hba->max_sqes);
+}
+
+static ssize_t bnx2i_set_sq_info(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+ u32 val;
+
+ if (sscanf(buf, " 0x%x ", &val) > 0) {
+ if ((val >= BNX2I_SQ_WQES_MIN) &&
+ (val <= BNX2I_SQ_WQES_MAX)) {
+ hba->max_sqes = val;
+ }
+ }
+ return count;
+}
+
+static ssize_t bnx2i_show_cq_info(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ return sprintf(buf, "0x%x\n", hba->max_cqes);
+}
+
+static ssize_t bnx2i_set_cq_info(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ u32 val;
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ if (sscanf(buf, " 0x%x ", &val) > 0) {
+ if ((val >= BNX2I_CQ_WQES_MIN) &&
+ (val <= BNX2I_CQ_WQES_MAX)) {
+ hba->max_cqes = val;
+ }
+ }
+ return count;
+}
+
+static ssize_t bnx2i_show_rq_info(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ return sprintf(buf, "0x%x\n", hba->max_rqes);
+}
+
+static ssize_t bnx2i_set_rq_info(struct class_device *cdev, const char *buf,
+ size_t count)
+{
+ u32 val;
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ if (sscanf(buf, " 0x%x ", &val) > 0) {
+ if ((val >= BNX2I_RQ_WQES_MIN) &&
+ (val <= BNX2I_RQ_WQES_MAX)) {
+ hba->max_rqes = val;
+ }
+ }
+ return count;
+}
+
+
+static ssize_t bnx2i_show_ccell_info(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ return sprintf(buf, "0x%x\n", hba->num_ccell);
+}
+
+static ssize_t bnx2i_set_ccell_info(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ u32 val;
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ if (sscanf(buf, " 0x%x ", &val) > 0) {
+ if ((val >= BNX2I_CCELLS_MIN) &&
+ (val <= BNX2I_CCELLS_MAX)) {
+ hba->num_ccell = val;
+ }
+ }
+ return count;
+}
+
+
+static ssize_t bnx2i_read_pci_trigger_reg(struct class_device *cdev,
+ char *buf)
+{
+ u32 reg_val = 0;
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+#define PCI_EVENT_TRIGGER_REG 0xCAC /* DMA WCHAN STAT10 REG */
+ reg_val = readl(hba->cnic->regview + PCI_EVENT_TRIGGER_REG);
+ return sprintf(buf, "0x%x\n", reg_val);
+}
+
+
+static ssize_t bnx2i_get_iscsi_cntx_dump(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+ unsigned int *ptr = (unsigned int *) hba->ctx_addr;
+ unsigned int *dst_ptr = (unsigned int *) buf;
+ int unit_sz = sizeof(unsigned int);
+#define SYSFS_BUF_SIZE 4096
+#define NUM_SYSFS_BUFS_PER_CTX 4
+
+ if (hba->ctx_read_cnt == NUM_SYSFS_BUFS_PER_CTX)
+ return 0;
+
+ ptr += (((hba->ctx_read_cnt % NUM_SYSFS_BUFS_PER_CTX) *
+ SYSFS_BUF_SIZE) / unit_sz);
+ hba->ctx_read_cnt++;
+ memcpy(dst_ptr, ptr, SYSFS_BUF_SIZE);
+
+ return SYSFS_BUF_SIZE;
+}
+
+static ssize_t bnx2i_select_iscsi_cntx_dump(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ u32 iscsi_cid;
+ int ret = 0;
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ if (sscanf(buf, " 0x%x ", &iscsi_cid) > 0) {
+ ret = bnx2i_select_ctx_dump_cid(hba, iscsi_cid);
+ }
+ if (!ret)
+ ret = count;
+ return ret;
+}
+
+static ssize_t bnx2i_get_active_iscsi_cid_list(struct class_device *cdev,
+ char *buf)
+{
+ u32 active_iscsi_cid[32];
+ u32 active_cid[32];
+ int num_cid = 0;
+ ssize_t total_len = 0;
+ char *cur_ptr = buf;
+ int i = 0;
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+ u32 num_ccell = hba->ctx_ccell_tasks & 0xFFFF;
+ u32 num_tasks_per_conn = hba->ctx_ccell_tasks >> 16;
+
+ if (!hba->ictx_poll_mode) {
+ num_cid = bnx2i_list_iscsi_cid(hba, active_iscsi_cid,
+ active_cid);
+ }
+ total_len += sprintf(cur_ptr, "0x%x\n", BNX2I_SYSFS_VERSION);
+ cur_ptr = buf + total_len;
+ total_len += sprintf(cur_ptr, "0x%x\n", num_ccell);
+ cur_ptr = buf + total_len;
+ total_len += sprintf(cur_ptr, "0x%x\n", num_tasks_per_conn);
+ if (hba->ictx_poll_mode) {
+ if (hba->ictx_poll_cid) {
+ cur_ptr = buf + total_len;
+ total_len += sprintf(cur_ptr, "0x%x, 0x%x\n",
+ hba->ictx_poll_iscsi_cid,
+ hba->ictx_poll_cid);
+ hba->ictx_poll_cid = hba->ictx_poll_iscsi_cid = 0;
+ }
+ } else {
+ for (i = 0; i < num_cid; i++) {
+ cur_ptr = buf + total_len;
+ total_len += sprintf(cur_ptr, "0x%x, 0x%x\n",
+ active_iscsi_cid[i],
+ active_cid[i]);
+ }
+ }
+ return total_len;
+}
+
+
+static ssize_t bnx2i_set_iscsi_cid_err_poll_mode(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ u32 poll_mode;
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+
+ if (sscanf(buf, "0x%x", &poll_mode) > 0) {
+ if (poll_mode)
+ hba->ictx_poll_mode = 1;
+ else
+ hba->ictx_poll_mode = 0;
+ }
+ return count;
+}
+
+
+static ssize_t bnx2i_get_qp_shmem_dump(struct class_device *cdev, char *buf)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+ int resi_len = hba->sq_cq_size -
+ (hba->sq_cq_rdp - hba->sq_cq_dump);
+
+ if (!hba->sq_cq_dump || !hba->sq_cq_rdp) {
+ return -EINVAL;
+ } else if ((hba->sq_cq_dump + hba->sq_cq_size) ==
+ hba->sq_cq_rdp) {
+ kfree(hba->sq_cq_dump);
+ hba->sq_cq_dump = hba->sq_cq_rdp = NULL;
+ return 0;
+ }
+
+ if (resi_len > SYSFS_BUF_SIZE) {
+ resi_len = SYSFS_BUF_SIZE;
+ }
+ memcpy(buf, hba->sq_cq_rdp, resi_len);
+ hba->sq_cq_rdp += resi_len;
+
+ return resi_len;
+}
+
+
+
+
+static void bnx2i_dup_cq_mem(struct bnx2i_hba *hba,
+ struct bnx2i_conn *conn, int count)
+{
+ struct cqe *cqe_s;
+ struct cqe *cqe_d;
+ int total_cnt = count;
+
+ if (conn->ep->qp.cq_cons_qe == conn->ep->qp.cq_virt)
+ cqe_s = conn->ep->qp.cq_last_qe;
+ else
+ cqe_s = conn->ep->qp.cq_cons_qe - 1;
+ cqe_d = (struct cqe *)hba->sq_cq_rdp;
+ while (count--) {
+ memcpy(cqe_d, cqe_s, sizeof(struct cqe));
+ if (cqe_s == conn->ep->qp.cq_virt) {
+ cqe_s = conn->ep->qp.cq_last_qe;
+ } else {
+ cqe_s--;
+ }
+ cqe_d++;
+ if ((cqe_d - (struct cqe *)hba->sq_cq_rdp) > total_cnt) {
+ printk(KERN_ALERT "bnx2i - SQ Dump: mem overflow\n");
+ break;
+ }
+ }
+}
+
+
+static int bnx2i_init_cq_dump(struct bnx2i_hba *hba, u32 iscsi_cid,
+ u32 count)
+{
+ struct bnx2i_conn *conn = NULL;
+ int cq_size = 0;
+
+ conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
+
+ if (!conn) {
+ printk(KERN_ALERT "CQ dump: cid #%x not valid\n",
+ iscsi_cid);
+ return -EPERM;
+ }
+
+ if (hba->sq_cq_dump)
+ return -EPERM;
+
+ cq_size = (conn->ep->qp.cq_last_qe - conn->ep->qp.cq_virt) + 1;
+
+ if (!count || (count > cq_size))
+ count = cq_size;
+
+ hba->sq_cq_size = count * sizeof(struct cqe);
+
+ if (!hba->sq_cq_size)
+ return -EINVAL;
+
+ hba->sq_cq_dump = kmalloc(hba->sq_cq_size, GFP_KERNEL);
+ if (!hba->sq_cq_dump)
+ return -ENOMEM;
+ hba->sq_cq_rdp = hba->sq_cq_dump;
+
+ bnx2i_dup_cq_mem(hba, conn, count);
+ return 0;
+}
+
+
+
+static void bnx2i_dup_sq_mem(struct bnx2i_hba *hba,
+ struct bnx2i_conn *conn, int count)
+{
+ struct sqe *sqe_s;
+ struct sqe *sqe_d;
+ int total_cnt = count;
+
+ if (conn->ep->qp.sq_prod_qe == conn->ep->qp.sq_virt)
+ sqe_s = conn->ep->qp.sq_last_qe;
+ else
+ sqe_s = conn->ep->qp.sq_prod_qe - 1;
+ sqe_d = (struct sqe *)hba->sq_cq_rdp;
+ while (count--) {
+ memcpy(sqe_d, sqe_s, sizeof(struct sqe));
+ if (sqe_s == conn->ep->qp.sq_virt) {
+ sqe_s = conn->ep->qp.sq_last_qe;
+ } else {
+ sqe_s--;
+ }
+ sqe_d++;
+ if ((sqe_d - (struct sqe *) hba->sq_cq_rdp) >
+ total_cnt) {
+ printk(KERN_ALERT "bnx2i - SQ Dump: mem overflow\n");
+ break;
+ }
+ }
+}
+
+
+static int bnx2i_init_sq_dump(struct bnx2i_hba *hba,
+ u32 iscsi_cid, u32 count)
+{
+ struct bnx2i_conn *conn = NULL;
+ int sq_size = 0;
+
+ conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
+
+ if (!conn) {
+ printk(KERN_ALERT "SQ dump: cid #%x not valid\n",
+ iscsi_cid);
+ return -EINVAL;
+ }
+
+ if (hba->sq_cq_dump)
+ return -EINVAL;
+
+ sq_size = (conn->ep->qp.sq_last_qe - conn->ep->qp.sq_virt) + 1;
+
+ if (!count || (count > sq_size))
+ count = sq_size;
+
+ hba->sq_cq_size = count * sizeof(struct sqe);
+
+ if (!hba->sq_cq_size)
+ return -EINVAL;
+
+ hba->sq_cq_dump = kmalloc(hba->sq_cq_size, GFP_KERNEL);
+ if (!hba->sq_cq_dump)
+ return -ENOMEM;
+ hba->sq_cq_rdp = hba->sq_cq_dump;
+
+ bnx2i_dup_sq_mem(hba, conn, count);
+ return 0;
+}
+
+static ssize_t bnx2i_setup_qp_shmem_dump(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ struct bnx2i_hba *hba =
+ container_of(cdev, struct bnx2i_hba, class_dev);
+ u32 iscsi_cid;
+ char queue[32];
+ ssize_t ret = count;
+ u32 num_count;
+
+
+ if (sscanf(buf, "%c%c,%d,%d", &queue[0], &queue[1],
+ &iscsi_cid, &num_count) > 0) {
+ if (!strncmp(queue, "SQ", 2)) {
+ ret = bnx2i_init_sq_dump(hba, iscsi_cid, num_count);
+ } else if (!strncmp(queue, "CQ", 2)) {
+ ret = bnx2i_init_cq_dump(hba, iscsi_cid, num_count);
+ } else {
+ ret = -EINVAL;
+ }
+ }
+ return ret;
+}
+
+
+static ssize_t bnx2i_read_tcp_portd_options(struct class_device *cdev,
+ char *buf)
+{
+ extern struct tcp_port_mngt bnx2i_tcp_port_tbl;
+ return sprintf(buf, "0x%x\n", bnx2i_tcp_port_tbl.num_required);
+}
+
+static ssize_t bnx2i_write_tcp_portd_results(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ extern struct tcp_port_mngt bnx2i_tcp_port_tbl;
+ u32 tcp_port, bind_stat;
+
+ if (!bnx2i_tcp_port_tbl.free_q)
+ return count;
+
+ if (sscanf(buf, "%d,%d", &tcp_port, &bind_stat) > 0) {
+ if (bind_stat && tcp_port) {
+ bnx2i_tcp_port_new_entry(tcp_port);
+ }
+ }
+ return count;
+}
+
+
+static CLASS_DEVICE_ATTR (mips_info, S_IRUGO,
+ bnx2i_show_mips_status, NULL);
+static CLASS_DEVICE_ATTR (net_if_name, S_IRUGO,
+ bnx2i_show_net_if_name, NULL);
+static CLASS_DEVICE_ATTR (pci_bar, S_IRUGO,
+ bnx2i_show_pci_bar, NULL);
+static CLASS_DEVICE_ATTR (sq_size, S_IRUGO | S_IWUSR,
+ bnx2i_show_sq_info, bnx2i_set_sq_info);
+static CLASS_DEVICE_ATTR (cq_size, S_IRUGO | S_IWUSR,
+ bnx2i_show_cq_info, bnx2i_set_cq_info);
+static CLASS_DEVICE_ATTR (rq_size, S_IRUGO | S_IWUSR,
+ bnx2i_show_rq_info, bnx2i_set_rq_info);
+static CLASS_DEVICE_ATTR (num_ccell, S_IRUGO | S_IWUSR,
+ bnx2i_show_ccell_info, bnx2i_set_ccell_info);
+static CLASS_DEVICE_ATTR (pci_trigger, S_IRUGO,
+ bnx2i_read_pci_trigger_reg, NULL);
+static CLASS_DEVICE_ATTR (ctx_dump, S_IRUGO | S_IWUSR,
+ bnx2i_get_iscsi_cntx_dump,
+ bnx2i_select_iscsi_cntx_dump);
+static CLASS_DEVICE_ATTR (cid_list, S_IRUGO | S_IWUSR,
+ bnx2i_get_active_iscsi_cid_list,
+ bnx2i_set_iscsi_cid_err_poll_mode);
+static CLASS_DEVICE_ATTR (qp_shmem_dump, S_IRUGO | S_IWUSR,
+ bnx2i_get_qp_shmem_dump,
+ bnx2i_setup_qp_shmem_dump);
+static CLASS_DEVICE_ATTR (port_bind, S_IRUGO | S_IWUSR,
+ bnx2i_read_tcp_portd_options,
+ bnx2i_write_tcp_portd_results);
+
+
+static struct class_device_attribute *bnx2i_class_attributes[] = {
+ &class_device_attr_mips_info,
+ &class_device_attr_net_if_name,
+ &class_device_attr_pci_bar,
+ &class_device_attr_sq_size,
+ &class_device_attr_cq_size,
+ &class_device_attr_rq_size,
+ &class_device_attr_num_ccell,
+ &class_device_attr_pci_trigger,
+ &class_device_attr_ctx_dump,
+ &class_device_attr_cid_list,
+ &class_device_attr_qp_shmem_dump,
+};
+
+static struct class_device_attribute *tcp_port_class_attributes[] = {
+ &class_device_attr_port_bind
+};
+
+static void bnx2i_sysfs_release(struct class_device *class_dev)
+{
+}
+
+struct class_device port_class_dev;
+
+
+static struct class bnx2i_class = {
+ .name = "bnx2i",
+ .release = bnx2i_sysfs_release,
+};
+
+
+
+static int bnx2i_register_port_class_dev(struct class_device *class_dev)
+{
+ char dev_name[BUS_ID_SIZE];
+ int ret;
+ int i;
+
+ class_dev->class = &bnx2i_class;
+ class_dev->class_data = class_dev;
+ snprintf(dev_name, BUS_ID_SIZE, "%s", "tcp_portd");
+ strlcpy(class_dev->class_id, dev_name, BUS_ID_SIZE);
+
+ ret = class_device_register(class_dev);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < ARRAY_SIZE(tcp_port_class_attributes); ++i) {
+ ret = class_device_create_file(class_dev,
+ tcp_port_class_attributes[i]);
+ if (ret)
+ goto err_unregister;
+ }
+
+ return 0;
+
+err_unregister:
+ class_device_unregister(class_dev);
+err:
+ return ret;
+}
+
+
+int bnx2i_register_sysfs(struct bnx2i_hba *hba)
+{
+ struct class_device *class_dev = &hba->class_dev;
+ char dev_name[BUS_ID_SIZE];
+ int ret;
+ int i;
+
+ class_dev->class = &bnx2i_class;
+ class_dev->class_data = hba;
+ snprintf(dev_name, BUS_ID_SIZE, "%.2x:%.2x.%.1x",
+ hba->pci_dev->bus->number,
+ PCI_SLOT(hba->pci_dev->devfn),
+ PCI_FUNC(hba->pci_dev->devfn));
+ strlcpy(class_dev->class_id, dev_name, BUS_ID_SIZE);
+
+ ret = class_device_register(class_dev);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < ARRAY_SIZE(bnx2i_class_attributes); ++i) {
+ ret = class_device_create_file(class_dev,
+ bnx2i_class_attributes[i]);
+ if (ret)
+ goto err_unregister;
+ }
+
+ return 0;
+
+err_unregister:
+ class_device_unregister(class_dev);
+err:
+ return ret;
+}
+
+void bnx2i_unregister_sysfs(struct bnx2i_hba *hba)
+{
+ class_device_unregister(&hba->class_dev);
+}
+
+int bnx2i_sysfs_setup(void)
+{
+ int ret;
+ ret = class_register(&bnx2i_class);
+
+ bnx2i_register_port_class_dev(&port_class_dev);
+ return ret;
+}
+
+void bnx2i_sysfs_cleanup(void)
+{
+ class_device_unregister(&port_class_dev);
+ class_unregister(&bnx2i_class);
+}
^ permalink raw reply related
* Re: [RFC Resend 3/3][BNX2]: Add iSCSI support to BNX2 devices.
From: Arnaldo Carvalho de Melo @ 2007-08-05 2:28 UTC (permalink / raw)
To: Michael Chan
Cc: Jeff Garzik, David Miller, mchristi, netdev, open-iscsi, anilgv,
talm, lusinsky, uri
In-Reply-To: <1186273141.4806.4.camel@dell>
There is at least one bug, please check it and the other suggestions,
Thanks,
- Arnaldo
Em Sat, Aug 04, 2007 at 05:19:01PM -0700, Michael Chan escreveu:
> diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
> new file mode 100644
> index 0000000..0576e1b
> --- /dev/null
> +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c
> @@ -0,0 +1,3718 @@
> +/* bnx2i_iscsi.c: Broadcom NetXtreme II iSCSI driver.
> +
> + *
> + * Copyright (c) 2004, 2005, 2006 Broadcom Corporation
> + *
> + * 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: Anil Veerabhadrappa (anilgv@broadcom.com)
> + */
> +
> +#include "bnx2i.h"
> +
> +struct scsi_host_template bnx2i_host_template;
> +struct iscsi_transport bnx2i_iscsi_transport;
> +
> +/*
> + * Global endpoint resource info
> + */
> +void *bnx2i_ep_pages[MAX_PAGES_PER_CTRL_STRUCT_POOL];
> +struct list_head bnx2i_free_ep_list;
> +struct list_head bnx2i_unbound_ep;
> +u32 bnx2i_num_free_ep;
> +u32 bnx2i_max_free_ep;
> +spinlock_t bnx2i_resc_lock;
static DEFINE_SPINLOCK(bnx2i_resc_lock);
I guess the other variables can also be marked static
> +struct tcp_port_mngt bnx2i_tcp_port_tbl;
> +static u16 bnx2i_local_tcp_port = 63000;
> +
> +
> +static struct io_bdt *bnx2i_alloc_bd_table(struct bnx2i_sess *sess,
> + struct bnx2i_cmd *);
> +static void bnx2i_free_tcp_port(u16 port);
> +static u16 bnx2i_alloc_tcp_port(void);
> +
> +
> +static int bnx2i_adapter_ready(struct bnx2i_hba *hba)
> +{
> + int retval = 0;
> +
> + if (!hba || !test_bit(ADAPTER_STATE_UP, &hba->adapter_state) ||
> + test_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state))
> + retval = -EPERM;
> + return retval;
> +}
> +
> +/*
> + * identifies & marks various bd info for imm data, unsolicited data
> + * and the first solicited data seq.
> + */
> +static void bnx2i_get_write_cmd_bd_idx(struct bnx2i_cmd *cmd, u32 buf_off,
> + u32 *start_bd_off, u32 *start_bd_idx)
> +{
> + u32 cur_offset = 0;
> + u32 cur_bd_idx = 0;
> + struct iscsi_bd *bd_tbl = cmd->bd_tbl->bd_tbl;
> +
> + if (buf_off) {
> + while (buf_off >= (cur_offset + bd_tbl->buffer_length)) {
> + cur_offset += bd_tbl->buffer_length;
> + cur_bd_idx++;
> + bd_tbl++;
> + }
> + }
> +
> + *start_bd_off = buf_off - cur_offset;
> + *start_bd_idx = cur_bd_idx;
> +}
> +
> +/*
> + * identifies & marks various bd info for immediate data,
> + * unsolicited data and first solicited data seq.
> + */
> +static void bnx2i_setup_write_cmd_bd_info(struct bnx2i_cmd *cmd)
> +{
> + struct bnx2i_conn *conn = NULL;
> + struct bnx2i_sess *sess = NULL;
> + u32 start_bd_offset = 0;
> + u32 start_bd_idx = 0;
> + u32 buffer_offset = 0;
> + u32 seq_len = 0;
> + u32 fbl = 0, mrdsl = 0;
> + u32 cmd_len = cmd->req.total_data_transfer_length;
> +
> + if (cmd)
> + conn = cmd->conn;
> + if (conn->sess)
> + sess = conn->sess;
> +
> + /* if ImmediateData is turned off & IntialR2T is turned on,
> + * there will be no immediate or unsolicited data, just return.
> + */
> + if (sess->initial_r2t && !sess->imm_data) {
> + return;
> + }
> + fbl = sess->first_burst_len;
> + mrdsl = conn->max_data_seg_len_xmit;
> +
> + /* Immediate data */
> + if (sess->imm_data) {
> + seq_len = min(mrdsl, fbl);
> + seq_len = min(cmd_len, seq_len);
> + buffer_offset += seq_len;
> + }
> + if (seq_len == cmd_len) {
> + return;
> + }
> +
> + if (!sess->initial_r2t) {
> + if (seq_len >= fbl)
> + goto r2t_data;
> + seq_len = min(fbl, cmd_len) - seq_len;
> + bnx2i_get_write_cmd_bd_idx(cmd, buffer_offset,
> + &start_bd_offset, &start_bd_idx);
> + cmd->req.ud_buffer_offset = start_bd_offset;
> + cmd->req.ud_start_bd_index = start_bd_idx;
> + buffer_offset += seq_len;
> + }
> +r2t_data:
> + if (buffer_offset != cmd_len) {
> + bnx2i_get_write_cmd_bd_idx(cmd, buffer_offset,
> + &start_bd_offset, &start_bd_idx);
> + if ((start_bd_offset > fbl) ||
> + (start_bd_idx > cmd->scsi_cmd->use_sg)) {
> + int i = 0;
> +
> + printk(KERN_ALERT "bnx2i- error, buf offset 0x%x "
> + "bd_valid %d use_sg %d\n",
> + buffer_offset, cmd->bd_tbl->bd_valid,
> + cmd->scsi_cmd->use_sg);
> + for (i = 0; i < cmd->bd_tbl->bd_valid; i++)
> + printk(KERN_ALERT "bnx2i err, bd[%d]: len %x\n",
> + i, cmd->bd_tbl->bd_tbl[i].\
> + buffer_length);
> + }
> + cmd->req.sd_buffer_offset = start_bd_offset;
> + cmd->req.sd_start_bd_index = start_bd_idx;
> + }
> +}
> +
> +
> +/*
> + */
> +static int bnx2i_split_bd(struct bnx2i_cmd *cmd, u64 addr, int sg_len,
> + int bd_index)
> +{
> + struct iscsi_bd *bd = cmd->bd_tbl->bd_tbl;
> + int frag_size = 0, sg_frags = 0;
> +
> + while (sg_len) {
> + if (sg_len >= BD_SPLIT_SIZE)
> + frag_size = BD_SPLIT_SIZE;
> + else
> + frag_size = sg_len;
> + bd[bd_index + sg_frags].buffer_addr_lo = (u32) addr;
> + bd[bd_index + sg_frags].buffer_addr_hi = addr >> 32;
> + bd[bd_index + sg_frags].buffer_length = frag_size;
> + bd[bd_index + sg_frags].flags = 0;
> + if ((bd_index + sg_frags) == 0)
> + bd[0].flags = ISCSI_BD_FIRST_IN_BD_CHAIN;
> + addr += (u64) frag_size;
> + sg_frags++;
> + sg_len -= frag_size;
> + }
> + return sg_frags;
> +}
> +
> +
> +/*
> + * map single buffer
> + */
> +static int bnx2i_map_single_buf(struct bnx2i_hba *hba,
> + struct bnx2i_cmd *cmd)
> +{
> + struct scsi_cmnd *sc = cmd->scsi_cmd;
> + struct iscsi_bd *bd = cmd->bd_tbl->bd_tbl;
> + int byte_count = 0;
Why set byte_count to 0...
> + int bd_count = 0;
> + u64 addr;
> +
> + byte_count = sc->request_bufflen;
... when you imediately set it to something else?
> + sc->SCp.dma_handle =
> + pci_map_single(hba->pci_dev, sc->request_buffer,
> + sc->request_bufflen, sc->sc_data_direction);
> + addr = sc->SCp.dma_handle;
> +
> + if (byte_count > MAX_BD_LENGTH) {
> + bd_count = bnx2i_split_bd(cmd, addr, byte_count, 0);
> + } else {
> + bd_count = 1;
Ditto for bd_count
> + bd[0].buffer_addr_lo = addr & 0xffffffff;
> + bd[0].buffer_addr_hi = addr >> 32;
> + bd[0].buffer_length = sc->request_bufflen;
> + bd[0].flags = ISCSI_BD_FIRST_IN_BD_CHAIN |
> + ISCSI_BD_LAST_IN_BD_CHAIN;
> + }
> + bd[bd_count - 1].flags |= ISCSI_BD_LAST_IN_BD_CHAIN;
> +
> + return bd_count;
> +}
> +
> +
> +/*
> + * map SG list
> + */
> +static int bnx2i_map_sg(struct bnx2i_hba *hba, struct bnx2i_cmd *cmd)
> +{
> + struct scsi_cmnd *sc = cmd->scsi_cmd;
> + struct iscsi_bd *bd = cmd->bd_tbl->bd_tbl;
> + struct scatterlist *sg;
> + int byte_count = 0;
> + int sg_frags = 0;
> + int bd_count = 0;
> + int sg_count = 0;
> + int sg_len;
> + u64 addr;
> + int i;
> +
> + sg = (struct scatterlist *) sc->request_buffer;
Needless cast, scsi_cmnd->request_buffer is a void pointer.
> +
> + sg_count = pci_map_sg(hba->pci_dev, sg, sc->use_sg,
> + sc->sc_data_direction);
> +
> + for (i = 0; i < sg_count; i++) {
> + sg_len = sg_dma_len(sg);
> + addr = sg_dma_address(sg);
> + if (sg_len > MAX_BD_LENGTH) {
> + sg_frags = bnx2i_split_bd(cmd, addr, sg_len,
> + bd_count);
> + } else {
> + sg_frags = 1;
> + bd[bd_count].buffer_addr_lo = addr & 0xffffffff;
> + bd[bd_count].buffer_addr_hi = addr >> 32;
> + bd[bd_count].buffer_length = sg_len;
> + bd[bd_count].flags = 0;
> + if (bd_count == 0)
> + bd[bd_count].flags =
> + ISCSI_BD_FIRST_IN_BD_CHAIN;
> + }
> + byte_count += sg_len;
> + sg++;
> + bd_count += sg_frags;
> + }
> + bd[bd_count - 1].flags |= ISCSI_BD_LAST_IN_BD_CHAIN;
> +
> + BUG_ON(byte_count != sc->request_bufflen);
> + return bd_count;
> +}
> +
> +/*
> + * creates BD list table for the command
> + */
> +static int bnx2i_iscsi_map_sg_list(struct bnx2i_cmd *cmd)
> +{
> + struct bnx2i_hba *hba = cmd->conn->sess->hba;
> + struct scsi_cmnd *sc = cmd->scsi_cmd;
> + int bd_count = 0;
> +
> + if (sc->use_sg)
> + bd_count = bnx2i_map_sg(hba, cmd);
> + else if (sc->request_bufflen)
> + bd_count = bnx2i_map_single_buf(hba, cmd);
> + else {
> + struct iscsi_bd *bd = cmd->bd_tbl->bd_tbl;
> + bd_count = 0;
No need to set bd_count to zero, you already did it when declaring,
remove this one or the one at declaration time, above.
> + bd[0].buffer_addr_lo = bd[0].buffer_addr_hi = 0;
> + bd[0].buffer_length = bd[0].flags = 0;
> + }
> + cmd->bd_tbl->bd_valid = bd_count;
> + return 0;
> +}
> +
> +
> +/*
> + * create BD list table for the command
> + */
> +int bnx2i_iscsi_unmap_sg_list(struct bnx2i_cmd *cmd)
> +{
> + struct bnx2i_hba *hba = cmd->conn->sess->hba;
> + struct scsi_cmnd *sc = cmd->scsi_cmd;
> + struct pci_dev *pdev = hba->pci_dev;
Why do you declare pdev, and initialize it, here? It is only needed
if...
> + struct scatterlist *sg;
> +
> + if (cmd->bd_tbl->bd_valid && sc) {
... this condition is true, so it should be declared/initialized here
> + if (sc->use_sg) {
and the sg variable should be declared here, where it is needed.
> + sg = (struct scatterlist *) sc->request_buffer;
No need for the cast
> + pci_unmap_sg(pdev, sg, sc->use_sg,
> + sc->sc_data_direction);
> + } else {
> + pci_unmap_single(pdev, sc->SCp.dma_handle,
> + sc->request_bufflen,
> + sc->sc_data_direction);
> + }
> + cmd->bd_tbl->bd_valid = 0;
> + }
> + return 0;
> +}
> +
> +
> +
> +static void bnx2i_setup_cmd_wqe_template(struct bnx2i_cmd *cmd)
> +{
> + memset(&cmd->req, 0x00, sizeof(cmd->req));
> + cmd->req.op_code = ISCSI_OPCODE_SCSI_CMD;
> + cmd->req.bd_list_addr_lo = (u32) cmd->bd_tbl->bd_tbl_dma;
> + cmd->req.bd_list_addr_hi =
> + (u32) ((u64) cmd->bd_tbl->bd_tbl_dma >> 32);
> +
> +}
> +
> +
> +/*
> + * update iscsi cid table entry with connection pointer
> + */
> +static void bnx2i_bind_conn_to_iscsi_cid(struct bnx2i_conn *conn,
> + u32 iscsi_cid)
> +{
> + struct bnx2i_hba *hba = NULL;
needless initialization...
> +
> + if (!conn || !conn->sess)
> + return;
> +
> + hba = conn->sess->hba;
as it is initialized here
> +
> + if (hba->cid_que.conn_cid_tbl[iscsi_cid])
> + printk(KERN_ERR "bnx2i: conn bind - entry #%d not free\n",
> + iscsi_cid);
Strange, what to do with the old value? is it OK just to print this
_error_ message? Haven't checked, just feels suspicious to just use
printk, BUG_ON or WARN_ON case?
> + hba->cid_que.conn_cid_tbl[iscsi_cid] = conn;
> +}
> +
> +
> +/*
> + * maps an iscsi cid to corresponding conn ptr
> + */
> +struct bnx2i_conn *bnx2i_get_conn_from_id(struct bnx2i_hba *hba,
> + u16 iscsi_cid)
> +{
> + if (!hba->cid_que.conn_cid_tbl) {
> + printk(KERN_ERR "bnx2i: ERROR - missing conn<->cid table\n");
> + return NULL;
> +
> + } else if (iscsi_cid >= hba->max_active_conns) {
> + printk(KERN_ERR "bnx2i: wrong cid #%d\n", iscsi_cid);
> + return NULL;
> + }
> + return(hba->cid_que.conn_cid_tbl[iscsi_cid]);
> +}
> +
> +
> +
> +/*
> + * allocates a iscsi_cid from free pool
> + */
> +static u32 bnx2i_alloc_iscsi_cid(struct bnx2i_hba *hba)
> +{
> + int idx = 0;
Another needless initialization
> +
> + if (!hba->cid_que.cid_free_cnt)
> + return (ISCSI_RESERVED_TAG);
> +
> + idx = hba->cid_que.cid_q_cons_idx;
> + hba->cid_que.cid_q_cons_idx++;
> + if (hba->cid_que.cid_q_cons_idx == hba->cid_que.cid_q_max_idx) {
> + hba->cid_que.cid_q_cons_idx = 0;
> + }
No need for { }, there is just one line in this if
> +
> + hba->cid_que.cid_free_cnt--;
> + return hba->cid_que.cid_que[idx];
> +}
> +
> +
> +/*
> + * return iscsi_cid back to free pool
> + */
> +static void bnx2i_free_iscsi_cid(struct bnx2i_hba *hba, u16 iscsi_cid)
> +{
> + int idx = 0;
Needless initialization
> +
> + if (iscsi_cid == (u16)ISCSI_RESERVED_TAG)
> + return;
> +
> + hba->cid_que.cid_free_cnt++;
> +
> + idx = hba->cid_que.cid_q_prod_idx;
> + hba->cid_que.cid_que[idx] = iscsi_cid;
> + hba->cid_que.conn_cid_tbl[iscsi_cid] = NULL;
> + hba->cid_que.cid_q_prod_idx++;
> + if (hba->cid_que.cid_q_prod_idx == hba->cid_que.cid_q_max_idx) {
Ditto wrt {}
> + hba->cid_que.cid_q_prod_idx = 0;
> + }
> +}
> +
> +
> +
> +/*
> + * setup iscsi_cid queue, 'iscsi_cid' value ranges from 0 to (MAX_CONNS -1)
> + */
> +static int bnx2i_setup_free_cid_que(struct bnx2i_hba *hba)
> +{
> + int mem_size;
> + int i = 0;
> +
> + mem_size = hba->max_active_conns * sizeof(u16);
> + mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
> +
> + hba->cid_que.cid_que_base = kmalloc(mem_size, GFP_KERNEL);
good, the value returned by kmalloc is a void pointer, so no need to
cast it...
> + if (!hba->cid_que.cid_que_base)
> + return -ENOMEM;
> +
> + mem_size = hba->max_active_conns * sizeof(struct bnx2i_conn *);
> + mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
> + hba->cid_que.conn_cid_tbl =
> + (struct bnx2i_conn **)kmalloc(mem_size, GFP_KERNEL);
... so why do you use a cast here? :-)
> + if (!hba->cid_que.conn_cid_tbl) {
Hey, here you test if this was not allocated...
> + kfree(hba->cid_que.cid_que_base);
> + hba->cid_que.cid_que_base = NULL;
But don't return -ENOMEM here and...
> + }
> +
> + hba->cid_que.cid_que = (u32 *)hba->cid_que.cid_que_base;
> + hba->cid_que.cid_q_prod_idx = 0;
> + hba->cid_que.cid_q_cons_idx = 0;
> + hba->cid_que.cid_q_max_idx = hba->max_active_conns;
> + hba->cid_que.cid_free_cnt = hba->max_active_conns;
> +
> + for (i = 0; i < hba->max_active_conns; i++) {
> + hba->cid_que.cid_que[i] = i;
> + hba->cid_que.conn_cid_tbl[i] = NULL;
... potentially dereference NULL here? Bzzt :)
> + }
> + return 0;
> +}
> +
> +
> +/*
> + * Releases resources held by free 'iscsi_cid' queue
> + */
> +static void bnx2i_release_free_cid_que(struct bnx2i_hba *hba)
> +{
> + if (hba->cid_que.cid_que_base) {
kfree handles receiving a NULL pointer, so no need to duplicate the test
against NULL, just call kfree and set the pointer to NULL
> + kfree(hba->cid_que.cid_que_base);
> + hba->cid_que.cid_que_base = NULL;
> + }
> +
> + if (hba->cid_que.conn_cid_tbl) {
Ditto
> + kfree(hba->cid_que.conn_cid_tbl);
> + hba->cid_que.conn_cid_tbl = NULL;
> + }
> +}
> +
> +
> +/*
> + * routine allocates a free endpoint structure from the global pool
> + */
> +struct bnx2i_endpoint *bnx2i_alloc_ep(void)
> +{
> + struct bnx2i_endpoint *endpoint = NULL;
> + struct list_head *listp;
> + u16 tcp_port;
> +
> + spin_lock_bh(&bnx2i_resc_lock);
> +
> + tcp_port = bnx2i_alloc_tcp_port();
> + if (!tcp_port) {
> + spin_unlock_bh(&bnx2i_resc_lock);
> + return NULL;
> + }
> + if (list_empty(&bnx2i_free_ep_list)) {
> + spin_unlock_bh(&bnx2i_resc_lock);
> + printk(KERN_ERR "alloc_ep: unable to alloc ep struct\n");
> + return endpoint;
Why not return NULL here as you did in the previous test?
Well, since you initialized endpoint to NULL you could, in both cases
just do a goto out_unlock that would be defined...
> + }
> + listp = (struct list_head *)bnx2i_free_ep_list.next;
> + list_del_init(listp);
> + bnx2i_num_free_ep--;
> +
> + endpoint = (struct bnx2i_endpoint *)listp;
> + endpoint->in_use = 1;
> + endpoint->tcp_port = tcp_port;
> + init_waitqueue_head(&endpoint->ofld_wait);
... here:
out_unlock:
I'll stop reviewing here, please consider checking the rest of the patch
for the kinds of things I pointed out,
Best Regards,
- Arnaldo
> +
> + spin_unlock_bh(&bnx2i_resc_lock);
> + return endpoint;
> +}
> +
> +
> +/*
> + * free endpoint structure to global free pool
> + */
> +void bnx2i_free_ep(struct bnx2i_endpoint *endpoint)
> +{
> + if (!endpoint)
> + return;
> +
> + spin_lock_bh(&bnx2i_resc_lock);
> + endpoint->state = EP_STATE_IDLE;
> + endpoint->in_use = 0;
> + bnx2i_free_iscsi_cid(endpoint->hba, endpoint->ep_iscsi_cid);
> + if (endpoint->conn) {
> + endpoint->conn->ep = NULL;
> + endpoint->conn = NULL;
> + }
> + endpoint->sess = NULL;
> +
> + if (endpoint->tcp_port) {
> + bnx2i_free_tcp_port(endpoint->tcp_port);
> + }
> + endpoint->hba = NULL;
> + list_add_tail(&endpoint->link, &bnx2i_free_ep_list);
> + bnx2i_num_free_ep++;
> + spin_unlock_bh(&bnx2i_resc_lock);
> +}
> +
> +
> +/*
> + * allocates free pool of endpoint structurres, endpoint structures
> + * are used to store QP related control and PT info
> + */
> +int bnx2i_alloc_ep_pool(void)
> +{
> + struct bnx2i_endpoint *endpoint = NULL;
> + int index = 0, count = 0;
> + int ret_val = 1;
> + int total_endpoints = 0;
> + int page_count = 0;
> + int num_endpoints_per_page = 0;
> + void *mem_ptr = NULL;
> +
> + spin_lock_init(&bnx2i_resc_lock);
> + INIT_LIST_HEAD(&bnx2i_free_ep_list);
> + INIT_LIST_HEAD(&bnx2i_unbound_ep);
> +
> + for (index = 0; index < MAX_PAGES_PER_CTRL_STRUCT_POOL; index++) {
> + bnx2i_ep_pages[index] = NULL;
> + }
> +
> + num_endpoints_per_page =
> + PAGE_SIZE / sizeof(struct bnx2i_endpoint);
> +
> + total_endpoints = ISCSI_MAX_CONNS_PER_HBA;
> + if (total_endpoints >
> + (num_endpoints_per_page * MAX_PAGES_PER_CTRL_STRUCT_POOL)) {
> + total_endpoints = (num_endpoints_per_page *
> + MAX_PAGES_PER_CTRL_STRUCT_POOL);
> + }
> +
> + bnx2i_num_free_ep = 0;
> + for (index = 0; index < total_endpoints;) {
> + mem_ptr = (void *)kmalloc(PAGE_SIZE, GFP_KERNEL);
> + if (mem_ptr == NULL) {
> + printk(KERN_ERR "ep_pool: mem alloc failed\n");
> + break;
> + }
> + bnx2i_ep_pages[page_count++] = (void *)mem_ptr;
> +
> + memset(mem_ptr, 0, PAGE_SIZE);
> +
> + endpoint = (struct bnx2i_endpoint *)mem_ptr;
> + for (count = 0; count < num_endpoints_per_page; count++) {
> + endpoint->in_use = 0;
> + list_add_tail(&endpoint->link, &bnx2i_free_ep_list);
> + endpoint++;
> + }
> +
> + bnx2i_num_free_ep += num_endpoints_per_page;
> + index += num_endpoints_per_page;
> + }
> + if (bnx2i_num_free_ep == 0)
> + ret_val = 0;
> + bnx2i_max_free_ep = bnx2i_num_free_ep;
> +
> + return(ret_val);
> +}
> +
> +
> +/*
> + * Free memory resources held by global endpoint pool
> + */
> +void bnx2i_release_ep_pool(void)
> +{
> + int index = 0;
> + void *mem_ptr = NULL;
> +
> + for (index = 0; index < MAX_PAGES_PER_CTRL_STRUCT_POOL; index++) {
> + mem_ptr = bnx2i_ep_pages[index];
> + if (mem_ptr) {
> + kfree((void *) mem_ptr);
> + break;
> + }
> + bnx2i_ep_pages[index] = NULL;
> + }
> + bnx2i_num_free_ep = 0;
> + return;
> +}
> +
> +
> +/*
> + * iSCSI Session ITT queue management code
> + */
> +static u32 bnx2i_alloc_itt(struct bnx2i_sess *sess, struct bnx2i_cmd *cmd)
> +{
> + u32 itt_val = ITT_INVALID_SIGNATURE;
> +
> + if (sess->itt_q.itt_q_count) {
> + itt_val = sess->itt_q.itt_que[sess->itt_q.itt_q_cons_idx++];
> + sess->itt_q.itt_q_cons_idx %= sess->itt_q.itt_q_max_idx;
> + sess->itt_q.itt_cmd[itt_val] = cmd;
> + sess->itt_q.itt_q_count--;
> + }
> + return itt_val;
> +}
> +
> +
> +static void bnx2i_free_itt(struct bnx2i_sess *sess, struct bnx2i_cmd *cmd)
> +{
> + if (cmd->req.itt == ITT_INVALID_SIGNATURE) {
> + printk(KERN_ALERT "free_itt: RSVD ITT - sess 0x%p\n", sess);
> + }
> + sess->itt_q.itt_que[sess->itt_q.itt_q_prod_idx++] = cmd->req.itt;
> + sess->itt_q.itt_q_prod_idx %= sess->itt_q.itt_q_max_idx;
> + sess->itt_q.itt_cmd[cmd->req.itt] = NULL;
> + sess->itt_q.itt_q_count++;
> + cmd->req.itt = ITT_INVALID_SIGNATURE;
> +}
> +
> +
> +/*
> + * setup ITT queue during iSCSI session creation. ITT queue is a
> + * circular array of ITTs [range 0 - (SQ SIZE - 1)] managed by
> + * producer and consumer index
> + */
> +static int bnx2i_setup_free_itt_queue(struct bnx2i_sess *sess)
> +{
> + u16 itt_q_size = (u16)sess->sq_size;
> + u32 itt_value = 0;
> + int unit_size = sizeof(u16);
> + int mem_size = PAGE_SIZE;
> +
> + if ((itt_q_size * unit_size) > mem_size)
> + mem_size = (itt_q_size * unit_size);
> +
> + mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
> + sess->itt_q.itt_que_base = kmalloc(mem_size, GFP_KERNEL);
> + if (!sess->itt_q.itt_que_base) {
> + return -ENOMEM;
> + }
> +
> + mem_size = (itt_q_size * sizeof(struct bnx2i_cmd *));
> + mem_size = (mem_size + (PAGE_SIZE - 1)) & PAGE_MASK;
> + sess->itt_q.itt_cmd =
> + (struct bnx2i_cmd **) kmalloc(mem_size, GFP_KERNEL);
> + if (!sess->itt_q.itt_cmd) {
> + kfree(sess->itt_q.itt_que_base);
> + sess->itt_q.itt_que_base = NULL;
> + return -1;
> + }
> + memset(sess->itt_q.itt_cmd, 0x00, mem_size);
> +
> + sess->itt_q.itt_que = (u32 *)sess->itt_q.itt_que_base;
> + sess->itt_q.itt_q_prod_idx = 0;
> + sess->itt_q.itt_q_cons_idx = 0;
> + sess->itt_q.itt_q_max_idx = itt_q_size;
> + sess->itt_q.itt_q_count = itt_q_size;
> +
> + itt_value = 0;
> + while (itt_value < itt_q_size) {
> + sess->itt_q.itt_cmd[itt_value] = (struct bnx2i_cmd *)NULL;
> + sess->itt_q.itt_que[sess->itt_q.itt_q_prod_idx++] =
> + itt_value++;
> + if (sess->itt_q.itt_q_prod_idx >= sess->itt_q.itt_q_max_idx) {
> + sess->itt_q.itt_q_prod_idx = 0;
> + }
> + }
> +
> + return 0;
> +}
> +
> +
> +/*
> + * free resources held by free ITT queue
> + */
> +static void bnx2i_release_free_itt_queue(struct bnx2i_sess *sess)
> +{
> + sess->itt_q.itt_q_count = 0;
> + if (sess->itt_q.itt_que_base) {
> + kfree (sess->itt_q.itt_que_base);
> + sess->itt_q.itt_que_base = NULL;
> + }
> +
> + if (sess->itt_q.itt_cmd) {
> + kfree (sess->itt_q.itt_cmd);
> + sess->itt_q.itt_cmd = NULL;
> + }
> + return;
> +}
> +
> +
> +/*
> + * allocates a command structures from free poll
> + */
> +struct bnx2i_cmd *bnx2i_alloc_cmd(struct bnx2i_sess *sess)
> +{
> + struct bnx2i_cmd *cmd = NULL;
> + struct list_head *listp;
> +
> + if (unlikely(!sess || (sess->num_free_cmds == 0))) {
> + return cmd;
> + }
> +
> + listp = (struct list_head *) sess->free_cmds.next;
> + list_del_init(listp);
> + sess->num_free_cmds--;
> + cmd = (struct bnx2i_cmd *)listp;
> + cmd->in_use = 1;
> + cmd->scsi_status_rcvd = cmd->resi_len = 0;
> + cmd->scsi_uflow = cmd->scsi_oflow = 0;
> +
> + bnx2i_setup_cmd_wqe_template(cmd);
> +
> + cmd->req.itt = bnx2i_alloc_itt(sess, cmd);
> +
> + return cmd;
> +}
> +
> +
> +/*
> + * return command structure and ITT back to free pool.
> + */
> +void bnx2i_free_cmd(struct bnx2i_sess *sess, struct bnx2i_cmd *cmd)
> +{
> + if (!sess || !cmd)
> + return;
> +
> + cmd->in_use = 0;
> + bnx2i_free_itt(sess, cmd);
> + list_add_tail(&cmd->link, &sess->free_cmds);
> + sess->num_free_cmds++;
> +}
> +
> +
> +/*
> + * Allocate command structure pool for a given iSCSI session
> + */
> +int bnx2i_alloc_cmd_pool(struct bnx2i_sess *sess)
> +{
> + struct bnx2i_cmd *cmdp = NULL;
> + int index = 0, count = 0;
> + int ret_val = 0;
> + int total_cmds = 0;
> + int num_cmds = 0;
> + int page_count = 0;
> + int num_cmds_per_page = 0;
> + void *mem_ptr = NULL;
> +
> + if (!sess)
> + return -EINVAL;
> +
> + INIT_LIST_HEAD(&sess->free_cmds);
> + for (index = 0; index < MAX_PAGES_PER_CTRL_STRUCT_POOL; index++) {
> + sess->cmd_pages[index] = NULL;
> + }
> +
> + num_cmds_per_page = PAGE_SIZE / sizeof(struct bnx2i_cmd);
> + total_cmds = sess->hba->scsi_template->can_queue + 1;
> + if (total_cmds >
> + (num_cmds_per_page * MAX_PAGES_PER_CTRL_STRUCT_POOL)) {
> + total_cmds = num_cmds_per_page *
> + MAX_PAGES_PER_CTRL_STRUCT_POOL;
> + }
> +
> + for (index = 0; index < total_cmds;) {
> + mem_ptr = (void *) kmalloc(PAGE_SIZE, GFP_KERNEL);
> + if (mem_ptr == NULL) {
> + break;
> + }
> + sess->cmd_pages[page_count++] = (void *)mem_ptr;
> +
> + num_cmds = num_cmds_per_page;
> + if ((total_cmds - index) < num_cmds_per_page)
> + num_cmds = (total_cmds - index);
> +
> + memset(mem_ptr, 0, PAGE_SIZE);
> + cmdp = (struct bnx2i_cmd *) mem_ptr;
> + for (count = 0; count < num_cmds; count++) {
> + cmdp->in_use = 0;
> + cmdp->req.itt = ITT_INVALID_SIGNATURE;
> +
> + /* Allocate BD table */
> + cmdp->bd_tbl = bnx2i_alloc_bd_table(sess, cmdp);
> + if (!cmdp->bd_tbl) {
> + /* should never fail, as it's guaranteed to have
> + * (ISCSI_MAX_CMDS_PER_SESS + 1) BD tables
> + * allocated before calling this function.
> + */
> + printk(KERN_ERR "no BD table cmd %p\n", cmdp);
> + goto bd_table_failed;
> + }
> + list_add_tail(&cmdp->link, &sess->free_cmds);
> + cmdp++;
> + }
> +
> + sess->num_free_cmds += num_cmds;
> + index += num_cmds;
> + }
> + sess->allocated_cmds = sess->num_free_cmds;
> +
> + if (sess->num_free_cmds == 0)
> + ret_val = -ENOMEM;
> + return(ret_val);
> +
> +bd_table_failed:
> + return(-ENOMEM);
> +}
> +
> +
> +/*
> + * Release memory held by command struct pool.
> + */
> +void bnx2i_free_cmd_pool(struct bnx2i_sess *sess)
> +{
> + int index = 0;
> + void *mem_ptr = NULL;
> +
> + if (unlikely(!sess))
> + return;
> +
> + if (sess->num_free_cmds != sess->allocated_cmds) {
> + /*
> + * WARN: either there is some command struct leak or
> + * still some SCSI commands are pending.
> + * TODO: post mortem required...
> + */
> + }
> + for (index = 0; index < MAX_PAGES_PER_CTRL_STRUCT_POOL; index++) {
> + mem_ptr = sess->cmd_pages[index];
> + if (mem_ptr) {
> + kfree((void *) mem_ptr);
> + break;
> + }
> + sess->cmd_pages[index] = NULL;
> + }
> + sess->num_free_cmds = sess->allocated_cmds = 0;
> + return;
> +}
> +
> +
> +/*
> + * Allocate a BD table
> + */
> +static struct io_bdt *bnx2i_alloc_bd_table(struct bnx2i_sess *sess,
> + struct bnx2i_cmd *cmd)
> +{
> + struct io_bdt *bd_tbl = NULL;
> +
> + if (list_empty(&sess->bd_tbl_list)) {
> + return NULL;
> + }
> + bd_tbl = (struct io_bdt *)sess->bd_tbl_list.next;
> + list_del(&bd_tbl->link);
> + list_add_tail(&bd_tbl->link, &sess->bd_tbl_active);
> + bd_tbl->bd_valid = 0;
> + if (!bd_tbl->cmdp) {
> + bd_tbl->cmdp = cmd;
> + }
> + return bd_tbl;
> +}
> +
> +
> +/*
> + * Free up memory pages allocated held by BD resources
> + */
> +static void bnx2i_free_all_bdt_resc_pages(struct bnx2i_sess *sess)
> +{
> + int i = 0;
> + struct bd_resc_page *resc_page = NULL;
> +
> + spin_lock_bh(&sess->lock);
> + while (!list_empty(&sess->bd_resc_page)) {
> + resc_page = (struct bd_resc_page *)sess->bd_resc_page.prev;
> + list_del(sess->bd_resc_page.prev);
> + for(i = 0; i < resc_page->num_valid; i++) {
> + kfree(resc_page->page[i]);
> + }
> + kfree(resc_page);
> + }
> + spin_unlock_bh(&sess->lock);
> +}
> +
> +
> +
> +/*
> + * allocated 4K page to track BD table memory
> + */
> +struct bd_resc_page *bnx2i_alloc_bdt_resc_page(struct bnx2i_sess *sess)
> +{
> + void *mem_ptr;
> + struct bd_resc_page *resc_page = NULL;
> +
> + mem_ptr = (void *) kmalloc(PAGE_SIZE, GFP_KERNEL);
> + if (!mem_ptr)
> + return NULL;
> +
> + resc_page = (struct bd_resc_page *) mem_ptr;
> + list_add_tail(&resc_page->link, &sess->bd_resc_page);
> + resc_page->max_ptrs = (PAGE_SIZE -
> + (u32)&((struct bd_resc_page *) 0)->page[0]) / sizeof(void *);
> + resc_page->num_valid = 0;
> +
> + return resc_page;
> +}
> +
> +
> +/*
> + * link newly allocated memory page to the list
> + */
> +int bnx2i_add_bdt_resc_page(struct bnx2i_sess *sess, void *bd_page)
> +{
> + struct bd_resc_page *resc_page = NULL;
> +
> +#define is_resc_page_full(_resc_pg) (_resc_pg->num_valid == _resc_pg->max_ptrs)
> +#define active_resc_page(_resc_list) \
> + (list_empty(_resc_list) ? NULL : (_resc_list)->prev)
> + if (list_empty(&sess->bd_resc_page)) {
> + resc_page = bnx2i_alloc_bdt_resc_page(sess);
> + } else {
> + resc_page = (struct bd_resc_page *)
> + active_resc_page(&sess->bd_resc_page);
> + }
> +
> + if (!resc_page)
> + return -ENOMEM;
> +
> + resc_page->page[resc_page->num_valid++] = bd_page;
> + if (is_resc_page_full(resc_page)) {
> + resc_page = bnx2i_alloc_bdt_resc_page(sess);
> + }
> + return 0;
> +}
> +
> +
> +/*
> + * Allocate BD table pool, DMA'able memory for a given session.
> + */
> +int bnx2i_alloc_bd_table_pool(struct bnx2i_sess *sess)
> +{
> + int index = 0, count = 0;
> + int ret_val = 0;
> + int num_elem_per_page;
> + struct io_bdt *bdt_info;
> + char *mem_ptr = NULL;
> + u32 bd_tbl_size = 0;
> + u32 mem_size = 0;
> + int total_bd_tbl = 0;
> +
> + INIT_LIST_HEAD(&sess->bd_resc_page);
> + INIT_LIST_HEAD(&sess->bd_tbl_list);
> + INIT_LIST_HEAD(&sess->bd_tbl_active);
> + total_bd_tbl = sess->hba->scsi_template->can_queue + 1;
> + mem_size = total_bd_tbl * sizeof(struct io_bdt);
> + num_elem_per_page = PAGE_SIZE / sizeof(struct io_bdt);
> + for (index = 0; index < total_bd_tbl; index += num_elem_per_page) {
> + if (((total_bd_tbl - index) * sizeof(struct io_bdt))
> + >= PAGE_SIZE) {
> + mem_size = PAGE_SIZE;
> + num_elem_per_page = PAGE_SIZE / sizeof(struct io_bdt);
> + } else {
> + mem_size =
> + (total_bd_tbl - index) * sizeof(struct io_bdt);
> + num_elem_per_page = (total_bd_tbl - index);
> + }
> + mem_ptr = (void *)kmalloc(mem_size, GFP_KERNEL);
> + if (mem_ptr == NULL) {
> + printk(KERN_ERR "alloc_bd_tbl: mem alloc failed\n");
> + ret_val = -ENOMEM;
> + goto resc_alloc_failed;
> + }
> + bnx2i_add_bdt_resc_page(sess, mem_ptr);
> +
> + memset(mem_ptr, 0, mem_size);
> + bdt_info = (struct io_bdt *)mem_ptr;
> + for (count = 0; count < num_elem_per_page; count++) {
> + list_add_tail(&bdt_info->link, &sess->bd_tbl_list);
> + bdt_info++;
> + }
> + }
> +
> + bd_tbl_size = ISCSI_MAX_BDS_PER_CMD * sizeof(struct iscsi_bd);
> + bdt_info = (struct io_bdt *)sess->bd_tbl_list.next;
> + while (bdt_info && (bdt_info != (struct io_bdt *)&sess->bd_tbl_list)) {
> + mem_ptr = (char *)pci_alloc_consistent(sess->hba->pci_dev,
> + bd_tbl_size,
> + &bdt_info->bd_tbl_dma);
> + if (!mem_ptr) {
> + printk(KERN_ERR "bd_tbl: DMA mem alloc failed\n");
> + ret_val = -ENOMEM;
> + goto dma_alloc_failed;
> + }
> + bdt_info->bd_tbl = (struct iscsi_bd *)mem_ptr;
> + bdt_info->max_bd_cnt = ISCSI_MAX_BDS_PER_CMD;
> + bdt_info->bd_valid = 0;
> + bdt_info->cmdp = NULL;
> +
> + bdt_info = (struct io_bdt *)bdt_info->link.next;
> + }
> + return(ret_val);
> +
> +resc_alloc_failed:
> +dma_alloc_failed:
> + return(ret_val);
> +}
> +
> +
> +/*
> + * releases BD table pool memory
> + */
> +void bnx2i_free_bd_table_pool(struct bnx2i_sess *sess)
> +{
> + struct list_head *list;
> + struct io_bdt *bdt_info;
> + u32 bd_tbl_size = 0;
> +
> + bd_tbl_size = ISCSI_MAX_BDS_PER_CMD * sizeof(struct iscsi_bd);
> + list_for_each(list, &sess->bd_tbl_list) {
> + bdt_info = list_entry(list, struct io_bdt, link);
> + pci_free_consistent(sess->hba->pci_dev, bd_tbl_size,
> + (void *)bdt_info->bd_tbl,
> + bdt_info->bd_tbl_dma);
> + bdt_info->bd_tbl = NULL;
> + if (bdt_info->cmdp) {
> + bdt_info->cmdp->bd_tbl = NULL;
> + bdt_info->cmdp = NULL;
> + }
> + }
> +
> + list_for_each(list, &sess->bd_tbl_active) {
> + bdt_info = list_entry(list, struct io_bdt, link);
> + pci_free_consistent(sess->hba->pci_dev, bd_tbl_size,
> + (void *)bdt_info->bd_tbl,
> + bdt_info->bd_tbl_dma);
> + bdt_info->bd_tbl = NULL;
> + if (bdt_info->cmdp) {
> + bdt_info->cmdp->bd_tbl = NULL;
> + bdt_info->cmdp = NULL;
> + }
> + }
> +}
> +
> +
> +/*
> + * allocate memory for dummy buffer and associated BD table
> + * to be used by middle path (MP) requests
> + */
> +static int bnx2i_setup_mp_bdt(struct bnx2i_hba *hba)
> +{
> + int rc = 0;
> + struct iscsi_bd *mp_bdt;
> + u64 addr;
> + hba->mp_bd_tbl = NULL;
> + if (hba->cnic_dev_type == CNIC_10GIG_GEN1)
> + return rc;
> +
> + hba->mp_bd_tbl = pci_alloc_consistent(hba->pci_dev,
> + PAGE_SIZE, &hba->mp_bd_dma);
> + if (!hba->mp_bd_tbl) {
> + printk(KERN_ERR "unable to allocate Middle Path BDT\n");
> + rc = -1;
> + goto out;
> + }
> +
> + hba->dummy_buffer =
> + pci_alloc_consistent(hba->pci_dev,
> + PAGE_SIZE, &hba->dummy_buf_dma);
> + if (!hba->dummy_buffer) {
> + printk(KERN_ERR "unable to alloc Middle Path Dummy Buffer\n");
> + pci_free_consistent(hba->pci_dev, PAGE_SIZE,
> + hba->mp_bd_tbl, hba->mp_bd_dma);
> + hba->mp_bd_tbl = NULL;
> + rc = -1;
> + goto out;
> + }
> +
> + mp_bdt = (struct iscsi_bd *)hba->mp_bd_tbl;
> + addr = (unsigned long)hba->dummy_buf_dma;
> + mp_bdt->buffer_addr_lo = addr & 0xffffffff;
> + mp_bdt->buffer_addr_hi = addr >> 32;
> + mp_bdt->buffer_length = PAGE_SIZE;
> + mp_bdt->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
> + ISCSI_BD_FIRST_IN_BD_CHAIN;
> +
> +out:
> + return rc;
> +}
> +
> +
> +/*
> + * free MP dummy buffer and associated BD table
> + */
> +static void bnx2i_free_mp_bdt(struct bnx2i_hba *hba)
> +{
> +
> + if (hba->mp_bd_tbl) {
> + pci_free_consistent(hba->pci_dev, PAGE_SIZE,
> + hba->mp_bd_tbl, hba->mp_bd_dma);
> + hba->mp_bd_tbl = NULL;
> + }
> + if (hba->dummy_buffer) {
> + pci_free_consistent(hba->pci_dev, PAGE_SIZE,
> + hba->dummy_buffer, hba->dummy_buf_dma);
> + hba->dummy_buffer = NULL;
> + }
> + return;
> +}
> +
> +
> +static u16 bnx2i_alloc_tcp_port()
> +{
> + return bnx2i_local_tcp_port++;
> +}
> +
> +
> +/*
> + * Function : bnx2i_free_tcp_port
> + * Description:
> + */
> +static void bnx2i_free_tcp_port(u16 port)
> +{
> + if (!bnx2i_tcp_port_tbl.free_q)
> + return;
> +
> + bnx2i_tcp_port_tbl.free_q[bnx2i_tcp_port_tbl.prod_idx] = port;
> + bnx2i_tcp_port_tbl.prod_idx++;
> + bnx2i_tcp_port_tbl.prod_idx %= bnx2i_tcp_port_tbl.max_idx;
> + bnx2i_tcp_port_tbl.num_free_ports++;
> +}
> +
> +void bnx2i_tcp_port_new_entry(u16 tcp_port)
> +{
> + u32 idx = bnx2i_tcp_port_tbl.prod_idx;
> +
> + spin_lock(&bnx2i_resc_lock);
> + bnx2i_tcp_port_tbl.free_q[idx] = (u16)tcp_port;
> + bnx2i_tcp_port_tbl.prod_idx++;
> + bnx2i_tcp_port_tbl.prod_idx %= bnx2i_tcp_port_tbl.max_idx;
> + bnx2i_tcp_port_tbl.num_free_ports++;
> + bnx2i_tcp_port_tbl.num_required--;
> + spin_unlock(&bnx2i_resc_lock);
> +}
> +
> +/*
> + * Function : bnx2i_init_tcp_port_mngr
> + * Description:
> + */
> +void bnx2i_init_tcp_port_mngr(void)
> +{
> + int mem_size = 0;
> +
> + bnx2i_tcp_port_tbl.num_free_ports = 0;
> + bnx2i_tcp_port_tbl.prod_idx = 0;
> + bnx2i_tcp_port_tbl.cons_idx = 0;
> + bnx2i_tcp_port_tbl.max_idx = 0;
> + bnx2i_tcp_port_tbl.num_required = 0;
> +
> +#define BNX2I_MAX_TCP_PORTS 1024
> +
> + bnx2i_tcp_port_tbl.port_tbl_size = BNX2I_MAX_TCP_PORTS;
> +
> + mem_size = sizeof(u16) * bnx2i_tcp_port_tbl.port_tbl_size;
> + if (bnx2i_tcp_port_tbl.port_tbl_size) {
> + bnx2i_tcp_port_tbl.free_q =
> + (u16 *)kmalloc(mem_size, GFP_KERNEL);
> +
> + if (bnx2i_tcp_port_tbl.free_q)
> + bnx2i_tcp_port_tbl.max_idx =
> + bnx2i_tcp_port_tbl.port_tbl_size;
> + }
> +}
> +
> +
> +/*
> + * Function : bnx2i_cleanup_tcp_port_mngr
> + * Description:
> + */
> +void bnx2i_cleanup_tcp_port_mngr(void)
> +{
> + if (bnx2i_tcp_port_tbl.free_q) {
> + kfree(bnx2i_tcp_port_tbl.free_q);
> + bnx2i_tcp_port_tbl.free_q = NULL;
> + }
> + bnx2i_tcp_port_tbl.num_free_ports = 0;
> +}
> +
> +
> +
> +/*
> + * interface was brought down by the user, fail all iSCSI sessions
> + * on this adapter,
> + */
> +void bnx2i_start_iscsi_hba_shutdown(struct bnx2i_hba *hba)
> +{
> + struct list_head *list = NULL;
> + struct list_head *tmp = NULL;
> + struct bnx2i_sess *sess;
> +
> + list_for_each_safe(list, tmp, &hba->active_sess) {
> + sess = (struct bnx2i_sess *)list;
> + bnx2i_do_iscsi_sess_recovery(sess, DID_NO_CONNECT);
> + }
> +}
> +
> +
> +/*
> + * IP address change indication, fail all iSCSI sessions on this adapter
> + */
> +void bnx2i_iscsi_handle_ip_event(struct bnx2i_hba *hba)
> +{
> + struct list_head *list = NULL;
> + struct list_head *tmp = NULL;
> + struct bnx2i_sess *sess;
> +
> + spin_lock(&hba->lock);
> + list_for_each_safe(list, tmp, &hba->active_sess) {
> + sess = (struct bnx2i_sess *)list;
> + spin_unlock(&hba->lock);
> + bnx2i_do_iscsi_sess_recovery(sess, DID_RESET);
> + spin_lock(&hba->lock);
> + }
> + spin_unlock(&hba->lock);
> +}
> +
> +
> +
> +static void
> +conn_err_recovery_task(struct work_struct *work)
> +{
> + struct bnx2i_hba *hba = container_of(work, struct bnx2i_hba,
> + err_rec_task);
> + struct bnx2i_sess *sess;
> + int cons_idx = hba->sess_recov_cons_idx;
> +
> + while (hba->sess_recov_prod_idx != cons_idx) {
> + sess = hba->sess_recov_list[cons_idx];
> + bnx2i_do_iscsi_sess_recovery(sess, DID_RESET);
> + if (cons_idx == hba->sess_recov_max_idx)
> + cons_idx = 0;
> + else
> + cons_idx++;
> + }
> + hba->sess_recov_cons_idx = cons_idx;
> +}
> +
> +
> +
> +
> +/*
> + * allocate memory buffer to extract conn context
> + */
> +static void bnx2i_init_ctx_dump_mem(struct bnx2i_hba *hba)
> +{
> + if (hba->ctx_addr)
> + return;
> +
> + hba->ictx_poll_mode = 0;
> + hba->ctx_size = 0;
> + hba->ctx_read_cnt = 0xffffffff;
> + hba->ctx_addr = pci_alloc_consistent(hba->pci_dev,
> + BNX2I_CONN_CTX_BUF_SIZE,
> + &hba->ctx_dma_hndl);
> + if (!hba->ctx_addr)
> + return;
> + hba->ctx_size = BNX2I_CONN_CTX_BUF_SIZE;
> +}
> +
> +
> +/*
> + * free context memory buffer
> + */
> +static void bnx2i_free_ctx_dump_mem(struct bnx2i_hba *hba)
> +{
> + if (!hba->ctx_addr || (hba->ctx_size == 0))
> + return;
> +
> + pci_free_consistent(hba->pci_dev, hba->ctx_size,
> + hba->ctx_addr, hba->ctx_dma_hndl);
> + hba->ctx_dma_hndl = 0;
> + hba->ctx_addr = NULL;
> + hba->ctx_size = 0;
> +}
> +
> +
> +static int bnx2i_ep_destroy_list_add(struct bnx2i_hba *hba,
> + struct bnx2i_endpoint *ep)
> +{
> + int cur_idx;
> +
> + write_lock(&hba->ep_rdwr_lock);
> + cur_idx = hba->ep_destroy_prod_idx++;
> + hba->ep_destroy_list[cur_idx] = ep;
> + hba->ep_destroy_prod_idx %= hba->ep_destroy_max_idx;
> + write_unlock(&hba->ep_rdwr_lock);
> + return 0;
> +}
> +
> +struct bnx2i_endpoint *bnx2i_ep_destroy_list_next(struct bnx2i_hba *hba)
> +{
> + int cur_idx;
> +
> + read_lock(&hba->ep_rdwr_lock);
> + if (hba->ep_destroy_prod_idx == hba->ep_destroy_cons_idx) {
> + read_unlock(&hba->ep_rdwr_lock);
> + return NULL;
> + }
> + cur_idx = hba->ep_destroy_cons_idx++;
> + hba->ep_destroy_cons_idx %= hba->ep_destroy_max_idx;
> + read_unlock(&hba->ep_rdwr_lock);
> +
> + return (hba->ep_destroy_list[cur_idx]);
> +}
> +
> +static int bnx2i_ep_ofld_list_add(struct bnx2i_hba *hba,
> + struct bnx2i_endpoint *ep)
> +{
> + int cur_idx;
> +
> + write_lock(&hba->ep_rdwr_lock);
> + cur_idx = hba->ep_ofld_prod_idx++;
> + hba->ep_ofld_list[cur_idx] = ep;
> + hba->ep_ofld_prod_idx %= hba->ep_ofld_max_idx;
> + write_unlock(&hba->ep_rdwr_lock);
> + return 0;
> +}
> +
> +struct bnx2i_endpoint *bnx2i_ep_ofld_list_next(struct bnx2i_hba *hba)
> +{
> + int cur_idx;
> +
> + read_lock(&hba->ep_rdwr_lock);
> + if (hba->ep_ofld_prod_idx == hba->ep_ofld_cons_idx) {
> + read_unlock(&hba->ep_rdwr_lock);
> + return NULL;
> + }
> + cur_idx = hba->ep_ofld_cons_idx++;
> + hba->ep_ofld_cons_idx %= hba->ep_ofld_max_idx;
> + read_unlock(&hba->ep_rdwr_lock);
> +
> + return (hba->ep_ofld_list[cur_idx]);
> +}
> +
> +static int bnx2i_init_ep_ofld_destroy_que(struct bnx2i_hba *hba)
> +{
> + rwlock_init(&hba->ep_rdwr_lock);
> + hba->ep_ofld_list = (struct bnx2i_endpoint **)
> + kmalloc(PAGE_SIZE, GFP_KERNEL);
> + if (!hba->ep_ofld_list)
> + return -ENOMEM;
> +
> + hba->ep_ofld_prod_idx = 0;
> + hba->ep_ofld_cons_idx = 0;
> + hba->ep_ofld_max_idx =
> + PAGE_SIZE / sizeof(struct bnx2i_endpoint *) - 1;
> +
> + hba->ep_destroy_list = (struct bnx2i_endpoint **)
> + kmalloc(PAGE_SIZE, GFP_KERNEL);
> + if (!hba->ep_destroy_list) {
> + kfree(hba->ep_ofld_list);
> + hba->ep_ofld_list = NULL;
> + return -ENOMEM;
> + }
> +
> + hba->ep_destroy_prod_idx = 0;
> + hba->ep_destroy_cons_idx = 0;
> + hba->ep_destroy_max_idx =
> + PAGE_SIZE / sizeof(struct bnx2i_endpoint *) - 1;
> + return 0;
> +}
> +
> +
> +static void bnx2i_free_ep_ofld_destroy_que(struct bnx2i_hba *hba)
> +{
> + if (hba->ep_ofld_list) {
> + kfree(hba->ep_ofld_list);
> + hba->ep_ofld_list = NULL;
> + }
> + if (hba->ep_destroy_list) {
> + kfree(hba->ep_destroy_list);
> + hba->ep_destroy_list = NULL;
> + }
> +}
> +
> +/*
> + * allocate & initialize adapter structure and call other
> + * support routines to do per adapter initialization
> + */
> +struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic)
> +{
> + struct bnx2i_hba *hba = NULL;
> +
> + hba = kmalloc(sizeof(struct bnx2i_hba), GFP_KERNEL);
> +
> + if (hba == NULL)
> + return NULL;
> +
> + memset((void *) hba, 0, sizeof(struct bnx2i_hba));
> +
> + /* Get PCI related information and update hba struct members */
> + hba->pci_dev = cnic->pcidev;
> + if (hba->pci_dev) {
> + hba->pci_did = hba->pci_dev->device;
> + hba->pci_vid = hba->pci_dev->vendor;
> + hba->pci_sdid = hba->pci_dev->subsystem_device;
> + hba->pci_svid = hba->pci_dev->subsystem_vendor;
> + hba->pci_func = PCI_FUNC(hba->pci_dev->devfn);
> + hba->pci_devno = PCI_SLOT(hba->pci_dev->devfn);
> + hba->pci_intr_num = hba->pci_dev->irq;
> + }
> +
> + INIT_LIST_HEAD(&hba->active_sess);
> + if (bnx2i_init_ep_ofld_destroy_que(hba))
> + goto ep_ofld_que_err;
> +
> + hba->mtu_supported = BNX2I_MAX_MTU_SUPPORTED;
> +
> + /* TODO: different values for Teton/Xinan/Everest */
> + hba->max_active_conns = ISCSI_MAX_CONNS_PER_HBA;
> +
> + if (bnx2i_setup_free_cid_que(hba))
> + goto cid_que_err;
> +
> + /* SQ/RQ/CQ size can be changed via sysfx interface */
> + hba->max_sqes = BNX2I_SQ_WQES_DEFAULT;
> + hba->max_rqes = BNX2I_RQ_WQES_DEFAULT;
> + hba->max_cqes = BNX2I_CQ_WQES_DEFAULT;
> + hba->num_ccell = BNX2I_CCELLS_DEFAULT;
> +
> + if (bnx2i_setup_mp_bdt(hba)) {
> + goto mp_bdt_err;
> + }
> +
> + spin_lock_init(&hba->lock);
> + /* initialize timer and wait queue used for resource cleanup when
> + * interface is brought down */
> + init_timer(&hba->hba_timer);
> + init_waitqueue_head(&hba->eh_wait);
> +
> + INIT_WORK(&hba->err_rec_task, conn_err_recovery_task);
> + hba->sess_recov_prod_idx = 0;
> + hba->sess_recov_cons_idx = 0;
> + hba->sess_recov_max_idx = 0;
> + hba->sess_recov_list =
> + (struct bnx2i_sess **)kmalloc(PAGE_SIZE, GFP_KERNEL);
> + if (!hba->sess_recov_list)
> + goto rec_que_err;
> + hba->sess_recov_max_idx = PAGE_SIZE / sizeof (struct bnx2i_sess *) - 1;
> +
> + bnx2i_init_ctx_dump_mem(hba);
> +
> + return hba;
> +
> +rec_que_err:
> + bnx2i_free_mp_bdt(hba);
> +mp_bdt_err:
> + bnx2i_release_free_cid_que(hba);
> +cid_que_err:
> + bnx2i_free_ep_ofld_destroy_que(hba);
> +ep_ofld_que_err:
> + bnx2i_free_hba(hba);
> +
> + return NULL;
> +}
> +
> +
> +/*
> + * free adapter structure and call various cleanup routines.
> + */
> +void bnx2i_free_hba(struct bnx2i_hba *hba)
> +{
> + if (hba == NULL)
> + return;
> +
> + bnx2i_free_ctx_dump_mem(hba);
> +
> + bnx2i_free_mp_bdt(hba);
> + bnx2i_release_free_cid_que(hba);
> + bnx2i_free_ep_ofld_destroy_que(hba);
> +
> + INIT_LIST_HEAD(&hba->active_sess);
> + /* Free memory held by hba structure */
> + kfree((void *)hba);
> +}
> +
> +
> +
> +
> +/*
> + * return all commands in active queue which should already have been
> + * cleaned up by the cnic device.
> + */
> +static void bnx2i_flush_active_cmd_queue(struct bnx2i_sess *sess, int err_code)
> +{
> + struct list_head *list;
> + struct list_head *tmp;
> + struct bnx2i_cmd *cmd;
> + unsigned long flags;
> + if (!sess->num_active_cmds)
> + return;
> +
> + spin_lock_irqsave(sess->host->host_lock, flags);
> + list_for_each_safe(list, tmp, &sess->active_cmds) {
> + cmd = (struct bnx2i_cmd *) list;
> + cmd->req.itt &= ISCSI_CMD_RESPONSE_INDEX;
> + bnx2i_iscsi_unmap_sg_list(cmd);
> + cmd->cmd_state = ISCSI_CMD_STATE_COMPLETED;
> + list_del_init(&cmd->link);
> + bnx2i_return_failed_command(sess, cmd, err_code);
> + bnx2i_free_cmd(sess, cmd);
> + }
> + spin_unlock_irqrestore(sess->host->host_lock, flags);
> +}
> +
> +
> +/*
> + * initiate cleanup of outstanding commands for sess recovery
> + */
> +static int bnx2i_session_recovery_start(struct bnx2i_sess *sess, int err_code)
> +{
> + if (unlikely(!sess)) {
> + printk(KERN_ALERT "sess_recov_start: sess not active\n");
> + return FAILED;
> + }
> +
> + if (!is_sess_active(sess)) {
> + wait_event_interruptible_timeout(sess->er_wait,
> + (sess->state ==
> + BNX2I_SESS_IN_FFP), HZ);
> + if (signal_pending(current))
> + flush_signals(current);
> + if (!is_sess_active(sess)) {
> + printk(KERN_ALERT "sess_reco: sess still not active\n");
> + sess->lead_conn->state = CONN_STATE_XPORT_FREEZE;
> + return FAILED;
> + }
> + }
> +
> + return SUCCESS;
> +}
> +
> +
> +/*
> + * SCSI host reset handler, which is translates to iSCSI session
> + * recovery
> + */
> +int bnx2i_do_iscsi_sess_recovery(struct bnx2i_sess *sess, int err_code)
> +{
> + struct bnx2i_hba *hba = NULL;
> + struct bnx2i_conn *conn = sess->lead_conn;
> +
> + if (bnx2i_session_recovery_start(sess, err_code) != SUCCESS) {
> + printk(KERN_INFO "bnx2i: sess rec start returned error\n");
> + return FAILED;
> + }
> + hba = sess->hba;
> +
> + sess->recovery_state = ISCSI_SESS_RECOVERY_OPEN_ISCSI;
> + iscsi_conn_error(conn->cls_conn, ISCSI_ERR_CONN_FAILED);
> +
> + /* if session teardown is because of net interface down,
> + * no need to wait for complete recovery */
> + if (err_code == DID_NO_CONNECT) {
> + wait_event_interruptible_timeout(sess->er_wait,
> + !conn->ep,
> + msecs_to_jiffies(1000));
> + } else {
> + wait_event_interruptible(sess->er_wait,
> + ((sess->recovery_state &
> + ISCSI_SESS_RECOVERY_COMPLETE) ||
> + (sess->recovery_state &
> + ISCSI_SESS_RECOVERY_FAILED)));
> + }
> +
> + if (signal_pending(current))
> + flush_signals(current);
> +
> + if (err_code == DID_NO_CONNECT)
> + return SUCCESS;
> +
> + if (sess->recovery_state & ISCSI_SESS_RECOVERY_COMPLETE) {
> + printk(KERN_INFO "bnx2i: host #%d reset succeeded\n",
> + sess->host->host_no);
> + sess->state = BNX2I_SESS_IN_FFP;
> + } else {
> + return FAILED;
> + }
> + sess->recovery_state = 0;
> + return SUCCESS;
> +}
> +
> +
> +/*
> + * free up resources held by this session
> + */
> +int bnx2i_iscsi_sess_release(struct bnx2i_hba *hba, struct bnx2i_sess *sess)
> +{
> + if (!sess)
> + return 0;
> +
> + bnx2i_release_free_itt_queue(sess);
> + bnx2i_free_cmd_pool(sess);
> + bnx2i_free_bd_table_pool(sess);
> + bnx2i_free_all_bdt_resc_pages(sess);
> +
> + list_del_init(&sess->link);
> + hba->num_active_sess--;
> +
> + return 0;
> +}
> +
> +
> +/*
> + * initialize various per session statistic counters
> + */
> +static void bnx2i_init_iscsi_sess_stats(struct bnx2i_sess *sess)
> +{
> + if (!sess)
> + return;
> +
> + sess->violation_notified = 0;
> +
> + sess->total_data_octets_sent = 0;
> + sess->total_data_octets_rcvd = 0;
> + sess->conn_login_ok = 0;
> + sess->conn_login_failed = 0;
> + sess->num_login_req_pdus = 0;
> + sess->num_login_resp_pdus = 0;
> + sess->num_scsi_cmd_pdus = 0;
> + sess->num_scsi_resp_pdus = 0;
> + sess->num_nopout_pdus = 0;
> + sess->num_nopin_pdus = 0;
> + sess->num_reject_pdus = 0;
> + sess->num_async_pdus = 0;
> + sess->num_dataout_pdus = 0;
> + sess->num_r2t_pdus = 0;
> + sess->num_datain_pdus = 0;
> + sess->num_snack_pdus = 0;
> + sess->num_text_req_pdus = 0;
> + sess->num_text_resp_pdus = 0;
> + sess->num_tmf_req_pdus = 0;
> + sess->num_tmf_resp_pdus = 0;
> + sess->num_logout_req_pdus = 0;
> + sess->num_logout_resp_pdus = 0;
> +}
> +
> +
> +/*
> + * set iSCSI parameter values to defaults, as defined in rfc3720
> + */
> +static void bnx2i_sess_set_param_defaults(struct bnx2i_sess *sess)
> +{
> + sess->initial_r2t = ISCSI_DEFAULT_INITIAL_R2T;
> + sess->max_r2t = ISCSI_DEFAULT_MAX_OUTSTANDING_R2T;
> + sess->imm_data = ISCSI_DEFAULT_IMMEDIATE_DATA;
> + sess->first_burst_len = ISCSI_DEFAULT_FIRST_BURST_LENGTH;
> + sess->max_burst_len = ISCSI_DEFAULT_MAX_BURST_LENGTH;
> + sess->time2wait = 2;
> + sess->time2retain = 20;
> +}
> +
> +
> +/*
> + * initialize session structure elements and allocate per sess resources
> + */
> +int bnx2i_iscsi_sess_new(struct bnx2i_hba *hba, struct bnx2i_sess *sess)
> +{
> + int rc;
> +
> + spin_lock(&hba->lock);
> + list_add_tail(&sess->link, &hba->active_sess);
> + hba->num_active_sess++;
> + spin_unlock(&hba->lock);
> +
> + sess->sq_size = hba->max_sqes;
> + sess->tsih = 0;
> + sess->lead_conn = NULL;
> +
> + spin_lock_init(&sess->lock);
> +
> + /* initialize active connection list */
> + INIT_LIST_HEAD(&sess->conn_list);
> + INIT_LIST_HEAD(&sess->free_cmds);
> +
> + INIT_LIST_HEAD(&sess->active_cmds);
> + sess->num_active_cmds = 0;
> +
> + sess->num_active_conn = 0;
> + sess->max_conns = 1;
> + sess->conn_id = 0;
> + sess->target_name = NULL;
> +
> + sess->state = BNX2I_SESS_INITIAL;
> + sess->recovery_state = 0;
> +
> + if (bnx2i_alloc_bd_table_pool(sess) != 0) {
> + printk(KERN_ERR "sess_new: unable to alloc bd table pool\n");
> + rc = -ENOMEM;
> + goto err_bd_pool;
> + }
> +
> + if (bnx2i_alloc_cmd_pool(sess) != 0) {
> + printk(KERN_ERR "sess_new: alloc cmd pool failed\n");
> + rc = -ENOMEM;
> + goto err_cmd_pool;
> + }
> +
> + rc = bnx2i_setup_free_itt_queue(sess);
> + if (rc) {
> + rc = -ENOMEM;
> + goto err_itt_que;
> + }
> +
> + init_timer(&sess->abort_timer);
> + init_waitqueue_head(&sess->er_wait);
> + init_timer(&sess->poll_timer);
> +
> + bnx2i_init_iscsi_sess_stats(sess);
> + bnx2i_sess_set_param_defaults(sess);
> +
> + return 0;
> +
> +err_itt_que:
> + bnx2i_free_cmd_pool(sess);
> +err_cmd_pool:
> + bnx2i_free_bd_table_pool(sess);
> +err_bd_pool:
> + return rc;
> +}
> +
> +
> +/*
> + * Login related resources is freed in this routine.
> + */
> +void bnx2i_conn_free_login_resources(struct bnx2i_hba *hba,
> + struct bnx2i_conn *conn)
> +{
> + if (conn->gen_pdu.resp_bd_tbl) {
> + pci_free_consistent(hba->pci_dev, PAGE_SIZE,
> + conn->gen_pdu.resp_bd_tbl,
> + conn->gen_pdu.resp_bd_dma);
> + conn->gen_pdu.resp_bd_tbl = NULL;
> + }
> +
> + if (conn->gen_pdu.req_bd_tbl) {
> + pci_free_consistent(hba->pci_dev, PAGE_SIZE,
> + conn->gen_pdu.req_bd_tbl,
> + conn->gen_pdu.req_bd_dma);
> + conn->gen_pdu.req_bd_tbl = NULL;
> + }
> +
> + if (conn->gen_pdu.resp_buf) {
> + pci_free_consistent(hba->pci_dev, ISCSI_CONN_LOGIN_BUF_SIZE,
> + conn->gen_pdu.resp_buf,
> + conn->gen_pdu.resp_dma_addr);
> + conn->gen_pdu.resp_buf = NULL;
> + }
> +
> + if (conn->gen_pdu.req_buf) {
> + pci_free_consistent(hba->pci_dev, ISCSI_CONN_LOGIN_BUF_SIZE,
> + conn->gen_pdu.req_buf,
> + conn->gen_pdu.req_dma_addr);
> + conn->gen_pdu.req_buf = NULL;
> + }
> +}
> +
> +
> +/*
> + * Login & nop-in related resources is allocated in this routine.
> + */
> +static int bnx2i_conn_alloc_login_resources(struct bnx2i_hba *hba,
> + struct bnx2i_conn *conn)
> +{
> + /* Allocate memory for login request/response buffers */
> + conn->gen_pdu.req_buf =
> + (char *) pci_alloc_consistent(hba->pci_dev,
> + ISCSI_CONN_LOGIN_BUF_SIZE,
> + &conn->gen_pdu.req_dma_addr);
> + if (conn->gen_pdu.req_buf == NULL)
> + goto login_req_buf_failure;
> +
> + conn->gen_pdu.req_buf_size = 0;
> + conn->gen_pdu.req_wr_ptr = conn->gen_pdu.req_buf;
> +
> + conn->gen_pdu.resp_buf =
> + (char *) pci_alloc_consistent(hba->pci_dev,
> + ISCSI_CONN_LOGIN_BUF_SIZE,
> + &conn->gen_pdu.resp_dma_addr);
> + if (conn->gen_pdu.resp_buf == NULL)
> + goto login_resp_buf_failure;
> +
> + conn->gen_pdu.resp_buf_size = ISCSI_CONN_LOGIN_BUF_SIZE;
> + conn->gen_pdu.resp_wr_ptr = conn->gen_pdu.resp_buf;
> +
> + conn->gen_pdu.req_bd_tbl =
> + (char *) pci_alloc_consistent(hba->pci_dev, PAGE_SIZE,
> + &conn->gen_pdu.req_bd_dma);
> + if (conn->gen_pdu.req_bd_tbl == NULL)
> + goto login_req_bd_tbl_failure;
> +
> + conn->gen_pdu.resp_bd_tbl =
> + (char *) pci_alloc_consistent(hba->pci_dev, PAGE_SIZE,
> + &conn->gen_pdu.resp_bd_dma);
> + if (conn->gen_pdu.resp_bd_tbl == NULL)
> + goto login_resp_bd_tbl_failure;
> +
> + return 0;
> +
> +login_resp_bd_tbl_failure:
> + pci_free_consistent(hba->pci_dev, PAGE_SIZE, conn->gen_pdu.req_bd_tbl,
> + conn->gen_pdu.req_bd_dma);
> + conn->gen_pdu.req_bd_tbl = NULL;
> +
> +login_req_bd_tbl_failure:
> + pci_free_consistent(hba->pci_dev, ISCSI_CONN_LOGIN_BUF_SIZE,
> + conn->gen_pdu.resp_buf,
> + conn->gen_pdu.resp_dma_addr);
> + conn->gen_pdu.resp_buf = NULL;
> +login_resp_buf_failure:
> + pci_free_consistent(hba->pci_dev, ISCSI_CONN_LOGIN_BUF_SIZE,
> + conn->gen_pdu.req_buf, conn->gen_pdu.req_dma_addr);
> + conn->gen_pdu.req_buf = NULL;
> +login_req_buf_failure:
> + printk(KERN_ERR "bnx2i:a conn login resource alloc failed!!\n");
> + return -ENOMEM;
> +
> +}
> +
> +
> +/*
> + * connection structure is initialized in this routine.
> + */
> +int bnx2i_iscsi_conn_new(struct bnx2i_sess *sess, struct bnx2i_conn *conn)
> +{
> + int ret_code = 0;
> + struct bnx2i_hba *hba = sess->hba;
> +
> + if (!sess || !conn || !hba)
> + return -EINVAL;
> +
> + conn->sess = sess;
> + conn->header_digest_en = 0;
> + conn->data_digest_en = 0;
> +
> + spin_lock_init(&conn->lock);
> +
> + init_timer(&conn->poll_timer);
> + conn->gen_pdu.cmd = NULL;
> +
> + /* 'ep' ptr will be assigned in bind() call */
> + conn->ep = NULL;
> +
> + ret_code = bnx2i_conn_alloc_login_resources(hba, conn);
> + if (ret_code != 0) {
> + printk(KERN_ALERT "conn_new: login resc alloc failed!!\n");
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> +
> +
> +/*
> + * extract & update SN counters from login response
> + */
> +static int bnx2i_login_resp_update_cmdsn(struct bnx2i_conn *conn)
> +{
> + u32 max_cmdsn;
> + u32 exp_cmdsn;
> + u32 stat_sn;
> + struct bnx2i_sess *sess = conn->sess;
> + struct iscsi_nopin *hdr = NULL;
> +
> + hdr = (struct iscsi_nopin *) &conn->gen_pdu.resp_hdr;
> +
> + max_cmdsn = ntohl(hdr->max_cmdsn);
> + exp_cmdsn = ntohl(hdr->exp_cmdsn);
> + stat_sn = ntohl(hdr->statsn);
> +#define SN_DELTA_ISLAND 0xffff
> + if (max_cmdsn < exp_cmdsn -1 &&
> + max_cmdsn > exp_cmdsn - SN_DELTA_ISLAND)
> + return -EINVAL;
> +
> + if (max_cmdsn > sess->max_cmdsn ||
> + max_cmdsn < sess->max_cmdsn - SN_DELTA_ISLAND)
> + sess->max_cmdsn = max_cmdsn;
> +
> + if (exp_cmdsn > sess->exp_cmdsn ||
> + exp_cmdsn < sess->exp_cmdsn - SN_DELTA_ISLAND) {
> + sess->exp_cmdsn = exp_cmdsn;
> + }
> + if (stat_sn == conn->exp_statsn)
> + conn->exp_statsn++;
> +
> + return 0;
> +}
> +
> +
> +/*
> + * update iSCSI SN counters for the given session
> + */
> +void bnx2i_update_cmd_sequence(struct bnx2i_sess *sess,
> + u32 exp_sn, u32 max_sn)
> +{
> + u32 exp_cmdsn = exp_sn;
> + u32 max_cmdsn = max_sn;
> +
> + if (max_cmdsn < exp_cmdsn -1 &&
> + max_cmdsn > exp_cmdsn - SN_DELTA_ISLAND) {
> + printk(KERN_ALERT "cmd_sequence: error, exp 0x%x, max 0x%x\n",
> + exp_cmdsn, max_cmdsn);
> + BUG_ON(1);
> + }
> + if (max_cmdsn > sess->max_cmdsn ||
> + max_cmdsn < sess->max_cmdsn - SN_DELTA_ISLAND)
> + sess->max_cmdsn = max_cmdsn;
> + if (exp_cmdsn > sess->exp_cmdsn ||
> + exp_cmdsn < sess->exp_cmdsn - SN_DELTA_ISLAND) {
> + sess->exp_cmdsn = exp_cmdsn;
> + }
> +
> + return;
> +}
> +
> +
> +/*
> + * This function propogates SCSI response to SCSI-ML by calling
> + * scsi_done() and also returns command struct back to free pool
> + */
> +int bnx2i_process_scsi_resp(struct bnx2i_cmd *cmd)
> +{
> + int ret = 0;
> + struct scsi_cmnd *sc = cmd->scsi_cmd;
> + struct Scsi_Host *host;
> + int res_count = 0;
> +
> + if (!sc)
> + return 0;
> +
> + host = cmd->conn->sess->host;
> + sc->result = (DID_OK << 16) | cmd->scsi_status;
> +
> + if (cmd->iscsi_resp != ISCSI_STATUS_CMD_COMPLETED) {
> + sc->result = (DID_ERROR << 16);
> + goto call_scsi_done;
> + }
> +
> + if (sc->sc_data_direction == DMA_TO_DEVICE) {
> + goto call_scsi_done;
> + }
> +
> + if (cmd->scsi_uflow) {
> + res_count = cmd->resi_len;
> + if (res_count > 0 && res_count <= sc->request_bufflen)
> + sc->resid = res_count;
> + else
> + sc->result = (DID_BAD_TARGET << 16) |
> + cmd->scsi_status;
> + } else if (cmd->scsi_oflow) {
> + sc->resid = res_count;
> + }
> +
> +call_scsi_done:
> + if ((cmd->cmd_state == ISCSI_CMD_STATE_ABORT_PEND) ||
> + (cmd->cmd_state == ISCSI_CMD_STATE_CLEANUP_PEND)) {
> + printk(KERN_ALERT "scsi_resp: command is being aborted\n");
> + return -1;
> + }
> +
> + spin_lock(host->host_lock);
> + cmd->scsi_cmd = NULL;
> + cmd->conn->sess->num_active_cmds--;
> + sc->scsi_done(sc);
> + bnx2i_free_cmd(cmd->conn->sess, cmd);
> + spin_unlock(host->host_lock);
> + return ret;
> +}
> +
> +
> +
> +/*
> + * login response PDU is pushed to application daemon by
> + * calling iscsi_recv_pdu()
> + */
> +int bnx2i_indicate_login_resp(struct bnx2i_conn *conn)
> +{
> + int ret = 0;
> + int data_len = 0;
> + struct iscsi_login_rsp *login_resp =
> + (struct iscsi_login_rsp *) &conn->gen_pdu.resp_hdr;
> +
> + /* check if this is the first login response for this connection.
> + * If yes, we need to copy initial StatSN to connection structure.
> + */
> + if (conn->exp_statsn == STATSN_UPDATE_SIGNATURE) {
> + conn->exp_statsn = ntohl(login_resp->statsn) + 1;
> + }
> +
> + ret = bnx2i_login_resp_update_cmdsn(conn);
> + if (ret != 0) {
> + return -EINVAL;
> + }
> +
> + data_len = conn->gen_pdu.resp_wr_ptr - conn->gen_pdu.resp_buf;
> + iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *) login_resp,
> + (char *) conn->gen_pdu.resp_buf, data_len);
> +
> + return 0;
> +}
> +
> +
> +/*
> + * deliver logout response PDU to application daemon
> + */
> +int bnx2i_indicate_logout_resp(struct bnx2i_conn *conn)
> +{
> + struct iscsi_logout_rsp *logout_resp =
> + (struct iscsi_logout_rsp *) &conn->gen_pdu.resp_hdr;
> +
> + iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *) logout_resp,
> + (char *) NULL, 0);
> +
> + return 0;
> +}
> +
> +
> +/*
> + * deliver iSCSI async PDU to user daemon
> + */
> +int bnx2i_indicate_async_mesg(struct bnx2i_conn *conn)
> +{
> + struct iscsi_async *async_msg =
> + (struct iscsi_async *) &conn->gen_pdu.resp_hdr;
> +
> + iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *) async_msg,
> + (char *) NULL, 0);
> +
> + return 0;
> +}
> +
> +
> +
> +/*
> + * Function : bnx2i_process_nopin
> + */
> +int bnx2i_process_nopin(struct bnx2i_conn *conn, struct bnx2i_cmd *cmd,
> + char *data_buf, int data_len)
> +{
> + struct iscsi_nopin *nopin_msg =
> + (struct iscsi_nopin *) &conn->gen_pdu.resp_hdr;
> +
> + iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *) nopin_msg,
> + (char *) data_buf, data_len);
> +
> + spin_lock(conn->sess->host->host_lock);
> + list_del_init(&cmd->link);
> + bnx2i_free_cmd(cmd->conn->sess, cmd);
> + spin_unlock(conn->sess->host->host_lock);
> +
> + return 0;
> +}
> +
> +
> +
> +/*
> + * Allocates buffers and BD tables before shipping requests to cnic
> + * for PDUs prepared by 'iscsid' daemon
> + */
> +static void bnx2i_iscsi_prep_generic_pdu_bd(struct bnx2i_conn *conn)
> +{
> + struct iscsi_bd *bd_tbl = NULL;
> +
> + bd_tbl = (struct iscsi_bd *) conn->gen_pdu.req_bd_tbl;
> +
> + bd_tbl->buffer_addr_hi =
> + (u32) ((u64) conn->gen_pdu.req_dma_addr >> 32);
> + bd_tbl->buffer_addr_lo = (u32) conn->gen_pdu.req_dma_addr;
> + bd_tbl->buffer_length = conn->gen_pdu.req_wr_ptr -
> + conn->gen_pdu.req_buf;
> + bd_tbl->reserved0 = 0;
> + bd_tbl->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
> + ISCSI_BD_FIRST_IN_BD_CHAIN;
> +
> + bd_tbl = (struct iscsi_bd *) conn->gen_pdu.resp_bd_tbl;
> + bd_tbl->buffer_addr_hi = (u64) conn->gen_pdu.resp_dma_addr >> 32;
> + bd_tbl->buffer_addr_lo = (u32) conn->gen_pdu.resp_dma_addr;
> + bd_tbl->buffer_length = ISCSI_CONN_LOGIN_BUF_SIZE;
> + bd_tbl->reserved0 = 0;
> + bd_tbl->flags = ISCSI_BD_LAST_IN_BD_CHAIN |
> + ISCSI_BD_FIRST_IN_BD_CHAIN;
> +}
> +
> +
> +
> +/*
> + * called to transmit PDUs prepared by the 'iscsid' daemon. iSCSI login,
> + * Nop-out and Logout requests flow through this path.
> + */
> +static int bnx2i_iscsi_send_generic_request(struct bnx2i_cmd *cmnd)
> +{
> + int rc = 0;
> + char *buf = NULL;
> + int data_len = 0;
> + struct bnx2i_conn *conn = cmnd->conn;
> +
> + bnx2i_iscsi_prep_generic_pdu_bd(conn);
> + switch (cmnd->iscsi_opcode & ISCSI_OPCODE_MASK) {
> + case ISCSI_OP_LOGIN:
> + bnx2i_send_iscsi_login(conn, cmnd);
> + break;
> +
> + case ISCSI_OP_NOOP_OUT:
> + data_len = conn->gen_pdu.req_buf_size;
> + buf = conn->gen_pdu.req_buf;
> + if (data_len)
> + rc = bnx2i_send_iscsi_nopout(conn, cmnd,
> + ISCSI_RESERVED_TAG,
> + buf, data_len, 1);
> + else
> + rc = bnx2i_send_iscsi_nopout(conn, cmnd,
> + ISCSI_RESERVED_TAG,
> + NULL, 0, 1);
> + break;
> +
> + case ISCSI_OP_LOGOUT:
> + rc = bnx2i_send_iscsi_logout(conn, cmnd);
> + break;
> +
> + default:
> + printk(KERN_ALERT "send_gen: unsupported op 0x%x\n",
> + cmnd->iscsi_opcode);
> + }
> + return rc;
> +}
> +
> +
> +/**********************************************************************
> + * SCSI-ML Interface
> + **********************************************************************/
> +
> +static void bnx2i_cpy_scsi_cdb(struct scsi_cmnd *sc,
> + struct bnx2i_cmd *cmd)
> +{
> + u32 dword;
> + int lpcnt = 0;
> + u8 *srcp = NULL;
> + u32 *dstp = NULL;
> + u32 scsi_lun[2];
> +
> + int_to_scsilun(sc->device->lun, (struct scsi_lun *) scsi_lun);
> + cmd->req.lun[0] = ntohl(scsi_lun[0]);
> + cmd->req.lun[1] = ntohl(scsi_lun[1]);
> +
> + lpcnt = cmd->scsi_cmd->cmd_len / sizeof(dword);
> + srcp = (u8 *) sc->cmnd;
> + dstp = (u32 *) cmd->req.cdb;
> + while (lpcnt--) {
> + memcpy(&dword, srcp, 4);
> + *dstp = cpu_to_be32(dword);
> + srcp += 4;
> + dstp++;
> + }
> + if (sc->cmd_len & 0x3) {
> + dword = (u32) srcp[0] | ((u32) srcp[1] << 8);
> + *dstp = cpu_to_be32(dword);
> + }
> +}
> +
> +
> +
> +/*
> + * handles SCSI command queued by SCSI-ML, allocates a command structure,
> + * assigning CMDSN, mapping SG buffers and handing over request to CNIC.
> + */
> +int bnx2i_queuecommand(struct scsi_cmnd *sc,
> + void (*done) (struct scsi_cmnd *))
> +{
> + struct Scsi_Host *shost;
> + struct bnx2i_sess *sess = NULL;
> + struct bnx2i_conn *conn = NULL;
> + struct bnx2i_cmd *cmd = NULL;
> + struct bnx2i_hba *hba = NULL;
> + static int old_recovery_state = 0;
> +
> + sc->scsi_done = done;
> + sc->result = 0;
> + shost = sc->device->host;
> + sess = iscsi_hostdata(shost->hostdata);
> + BUG_ON(shost != sess->host);
> +
> + if (sess) {
> + hba = sess->hba;
> + } else {
> + printk(KERN_ALERT "bnx2i: quecmd: Error dev not found \n");
> + goto dev_not_found;
> + }
> +
> +#define iscsi_cmd_win_closed(_sess) \
> + ((int) (_sess->max_cmdsn - _sess->cmdsn) < 0)
> +
> + if (iscsi_cmd_win_closed(sess)) {
> + goto iscsi_win_closed;
> + }
> +
> + if ((sess->state & BNX2I_SESS_IN_SHUTDOWN) ||
> + (sess->state & BNX2I_SESS_IN_LOGOUT)) {
> + goto dev_not_found;
> + }
> +
> + if (sess->recovery_state) {
> + if (old_recovery_state != sess->recovery_state) {
> + old_recovery_state = sess->recovery_state;
> + }
> +
> + if (sess->recovery_state & ISCSI_SESS_RECOVERY_FAILED)
> + goto dev_not_found;
> + else if (!(sess->recovery_state & ISCSI_SESS_RECOVERY_COMPLETE))
> + goto iscsi_win_closed;
> + else
> + sess->recovery_state = 0;
> + }
> +
> + cmd = bnx2i_alloc_cmd(sess);
> + if (cmd == NULL) {
> + /* This should never happen as cmd list size == SHT->can_queue
> + */
> + goto cmd_not_accepted;
> + }
> +
> + cmd->conn = conn = sess->lead_conn;
> + cmd->scsi_cmd = sc;
> + cmd->req.total_data_transfer_length = sc->request_bufflen;
> + cmd->iscsi_opcode = ISCSI_OPCODE_SCSI_CMD;
> + cmd->req.cmd_sn = sess->cmdsn++;
> +
> + bnx2i_iscsi_map_sg_list(cmd);
> + bnx2i_cpy_scsi_cdb(sc, cmd);
> +
> + if (sc->sc_data_direction == DMA_TO_DEVICE) {
> + cmd->req.op_attr = ISCSI_CMD_REQUEST_WRITE;
> + cmd->req.itt |= (ISCSI_TASK_TYPE_WRITE <<
> + ISCSI_CMD_REQUEST_TYPE_SHIFT);
> + bnx2i_setup_write_cmd_bd_info(cmd);
> + } else {
> + cmd->req.op_attr = ISCSI_CMD_REQUEST_READ;
> + cmd->req.itt |= (ISCSI_TASK_TYPE_READ <<
> + ISCSI_CMD_REQUEST_TYPE_SHIFT);
> + }
> + cmd->req.num_bds = cmd->bd_tbl->bd_valid;
> + if (!cmd->bd_tbl->bd_valid) {
> + cmd->req.bd_list_addr_lo = (u32) hba->mp_bd_dma;
> + cmd->req.bd_list_addr_hi =
> + (u32) ((u64) hba->mp_bd_dma >> 32);
> + cmd->req.num_bds = 1;
> + }
> +
> + cmd->cmd_state = ISCSI_CMD_STATE_INITIATED;
> + sc->SCp.ptr = (char *) cmd;
> +
> + if (cmd->req.itt != ITT_INVALID_SIGNATURE) {
> + bnx2i_send_iscsi_scsicmd(conn, cmd);
> + list_add_tail(&cmd->link, &sess->active_cmds);
> + sess->num_active_cmds++;
> + }
> + return 0;
> +
> +iscsi_win_closed:
> +cmd_not_accepted:
> + return SCSI_MLQUEUE_HOST_BUSY;
> +
> +dev_not_found:
> + sc->sense_buffer[0] = 0x70;
> + sc->sense_buffer[2] = NOT_READY;
> + sc->sense_buffer[7] = 0x6;
> + sc->sense_buffer[12] = 0x08;
> + sc->sense_buffer[13] = 0x00;
> + sc->result = (DID_NO_CONNECT << 16);
> + sc->resid = sc->request_bufflen;
> + sc->scsi_done(sc);
> + return 0;
> +}
> +
> +
> +
> +/*
> + * TMF request timeout handler
> + */
> +static void bnx2i_iscsi_tmf_timer(unsigned long data)
> +{
> + struct bnx2i_cmd *cmd = (struct bnx2i_cmd *) data;
> +
> + printk(KERN_ALERT "TMF timer: abort failed, cmd 0x%p\n", cmd);
> + cmd->cmd_state = ISCSI_CMD_STATE_FAILED;
> + wake_up(&cmd->conn->sess->er_wait);
> +}
> +
> +
> +/*
> + * initiate command abort process by requesting CNIC to send
> + * an iSCSI TMF request to target
> + */
> +static int bnx2i_initiate_abort_cmd(struct scsi_cmnd *sc)
> +{
> + struct bnx2i_cmd *cmd = (struct bnx2i_cmd *) sc->SCp.ptr;
> + struct bnx2i_cmd *tmf_cmd = NULL;
> + struct Scsi_Host *shost = cmd->scsi_cmd->device->host;
> + struct bnx2i_conn *conn = cmd->conn;
> + struct bnx2i_sess *sess = NULL;
> + struct bnx2i_hba *hba = NULL;
> +
> + shost = cmd->scsi_cmd->device->host;
> + sess = iscsi_hostdata(shost->hostdata);
> + BUG_ON(shost != sess->host);
> +
> + if (sess && (is_sess_active(sess))) {
> + hba = sess->hba;
> + } else {
> + return FAILED;
> + }
> +
> + bnx2i_setup_ictx_dump(hba, conn);
> +
> + if (cmd->scsi_cmd != sc) {
> + /* command already completed to scsi mid-layer */
> + goto cmd_not_active;
> + }
> +
> + tmf_cmd = bnx2i_alloc_cmd(sess);
> + if (cmd == NULL) {
> + goto lack_of_resc;
> + }
> +
> + tmf_cmd->conn = conn = sess->lead_conn;
> + tmf_cmd->scsi_cmd = NULL;
> + tmf_cmd->iscsi_opcode = ISCSI_OPCODE_TMF_REQUEST;
> + tmf_cmd->req.cmd_sn = sess->cmdsn;
> + tmf_cmd->tmf_ref_itt = cmd->req.itt;
> + tmf_cmd->tmf_ref_cmd = cmd;
> + tmf_cmd->tmf_ref_sc = cmd->scsi_cmd;
> + cmd->cmd_state = ISCSI_CMD_STATE_ABORT_PEND;
> + tmf_cmd->cmd_state = ISCSI_CMD_STATE_INITIATED;
> +
> + sess->abort_timer.expires = 10*HZ + jiffies;
> + sess->abort_timer.function = bnx2i_iscsi_tmf_timer;
> + sess->abort_timer.data = (unsigned long)tmf_cmd;
> + add_timer(&sess->abort_timer);
> +
> + bnx2i_send_iscsi_tmf(conn, tmf_cmd);
> +
> + /* update iSCSI context for this conn, wait for CNIC to complete */
> + wait_event_interruptible(sess->er_wait,
> + tmf_cmd->cmd_state != ISCSI_CMD_STATE_INITIATED);
> +
> + if (signal_pending(current))
> + flush_signals(current);
> +
> + del_timer_sync(&sess->abort_timer);
> +
> + if (tmf_cmd->cmd_state == ISCSI_CMD_STATE_FAILED) {
> + printk(KERN_ALERT "abort: abort failed, cmd 0x%p\n", tmf_cmd);
> + /* TMF timed out, return error status and let SCSI-ML do
> + * session recovery.
> + */
> + list_del_init(&tmf_cmd->link);
> + bnx2i_free_cmd(sess, tmf_cmd);
> + return FAILED;
> + }
> +
> + list_del_init(&tmf_cmd->link);
> + bnx2i_free_cmd(sess, tmf_cmd);
> +
> + if ((cmd->scsi_cmd->result & 0xFF0000) == (DID_ABORT << 16)) {
> + cmd->cmd_state = ISCSI_CMD_STATE_CLEANUP_PEND;
> + bnx2i_send_cmd_cleanup_req(hba, cmd);
> + wait_event_interruptible_timeout(sess->er_wait,
> + (cmd->cmd_state ==
> + ISCSI_CMD_STATE_CLEANUP_CMPL),
> + msecs_to_jiffies(
> + ISCSI_CMD_CLEANUP_TIMEOUT));
> +
> + if (signal_pending(current))
> + flush_signals(current);
> + } else {
> + cmd->scsi_cmd->result = (DID_ABORT << 16);
> + }
> + cmd->conn->sess->num_active_cmds--;
> + list_del_init(&cmd->link);
> + cmd->scsi_cmd = NULL;
> + bnx2i_free_cmd(cmd->conn->sess, cmd);
> +
> +cmd_not_active:
> + return SUCCESS;
> +
> +lack_of_resc:
> + return FAILED;
> +}
> +
> +
> +/*
> + * SCSI abort request handler.
> + */
> +int bnx2i_abort(struct scsi_cmnd *sc)
> +{
> + int reason;
> + struct bnx2i_cmd *cmd = (struct bnx2i_cmd *) sc->SCp.ptr;
> +
> + if (unlikely(!cmd)) {
> + /* command already completed to scsi mid-layer */
> + printk(KERN_INFO "bnx2i_abort: sc 0x%p, not active\n", sc);
> + return SUCCESS;
> + }
> +
> + reason = bnx2i_initiate_abort_cmd(sc);
> + return reason;
> +}
> +
> +
> +
> +/*
> + * hardware reset
> + */
> +int bnx2i_reset(struct scsi_cmnd *sc)
> +{
> + return 0;
> +}
> +
> +
> +void bnx2i_return_failed_command(struct bnx2i_sess *sess,
> + struct bnx2i_cmd *cmd, int err_code)
> +{
> + struct scsi_cmnd *sc = cmd->scsi_cmd;
> + sc->result = err_code << 16;
> + sc->resid = cmd->scsi_cmd->request_bufflen;
> + cmd->scsi_cmd = NULL;
> + sess->num_active_cmds--;
> + sc->scsi_done(sc);
> +}
> +
> +
> +
> +/*
> + * SCSI host reset handler - iSCSI session recovery
> + */
> +int bnx2i_host_reset(struct scsi_cmnd *sc)
> +{
> + struct Scsi_Host *shost = sc->device->host;
> + struct bnx2i_sess *sess = NULL;
> + int rc = 0;
> +
> + shost = sc->device->host;
> + sess = iscsi_hostdata(shost->hostdata);
> + printk(KERN_INFO "bnx2i: attempting to reset host, #%d\n",
> + sess->host->host_no);
> +
> + BUG_ON(shost != sess->host);
> + rc = bnx2i_do_iscsi_sess_recovery(sess, DID_RESET);
> +
> + return rc;
> +}
> +
> +
> +
> +/**********************************************************************
> + * open-iscsi interface
> + **********************************************************************/
> +
> +
> +#define get_bnx2_device(_hba, _devc) do { \
> + if ((_hba->pci_did == PCI_DEVICE_ID_NX2_5706) || \
> + (_hba->pci_did == PCI_DEVICE_ID_NX2_5706S)) { \
> + _devc = '6'; \
> + } else if ((_hba->pci_did == PCI_DEVICE_ID_NX2_5708) || \
> + (_hba->pci_did == PCI_DEVICE_ID_NX2_5708S)) { \
> + _devc = '8'; \
> + } else if ((_hba->pci_did == PCI_DEVICE_ID_NX2_5709) || \
> + (_hba->pci_did == PCI_DEVICE_ID_NX2_5709S)) { \
> + _devc = '9'; \
> + } \
> + } while (0)
> +
> +/* from open-iscsi project */
> +/*
> + * iSCSI Session's hostdata organization:
> + *
> + * *------------------* <== hostdata_session(host->hostdata)
> + * | ptr to class sess|
> + * |------------------| <== iscsi_hostdata(host->hostdata)
> + * | iscsi_session |
> + * *------------------*
> + */
> +
> +#define hostdata_privsize(_sz) (sizeof(unsigned long) + _sz + \
> + _sz % sizeof(unsigned long))
> +
> +#define hostdata_session(_hostdata) (iscsi_ptr(*(unsigned long *)_hostdata))
> +
> +#define session_to_cls(_sess) hostdata_session(_sess->host->hostdata)
> +
> +
> +
> +
> +/*
> + * Function: bnx2i_register_xport
> + * Description: this routine will allocate memory for SCSI host template,
> + * iSCSI template and registers one instance of NX2 device with
> + * iSCSI Transport Kernel module.
> + */
> +int bnx2i_register_xport(struct bnx2i_hba *hba)
> +{
> + void *mem_ptr = NULL;
> + char dev_id = '8';
> +
> + if (!hba)
> + return -EINVAL;
> +
> + get_bnx2_device(hba, dev_id);
> +
> + mem_ptr = kmalloc(sizeof(struct scsi_host_template), GFP_KERNEL);
> + hba->scsi_template = (struct scsi_host_template *) mem_ptr;
> + if (hba->scsi_template == NULL) {
> + printk(KERN_ALERT "bnx2i: failed to alloc memory for sht\n");
> + return -ENOMEM;
> + }
> +
> + mem_ptr = kmalloc(sizeof(struct iscsi_transport), GFP_KERNEL);
> + hba->iscsi_transport = (struct iscsi_transport *) mem_ptr;
> + if (hba->iscsi_transport == NULL) {
> + printk(KERN_ALERT "mem error for iscsi_transport template\n");
> + goto iscsi_xport_err;
> + }
> +
> + mem_ptr = kmalloc(BRCM_ISCSI_XPORT_NAME_SIZE_MAX, GFP_KERNEL);
> + if (mem_ptr == NULL) {
> + printk(KERN_ALERT "failed to alloc memory for xport name\n");
> + goto scsi_name_mem_err;
> + }
> +
> + memcpy((void *) hba->scsi_template,
> + (const void *) &bnx2i_host_template,
> + sizeof(struct scsi_host_template));
> + hba->scsi_template->name = mem_ptr;
> + memcpy((void *) hba->scsi_template->name,
> + (const void *) bnx2i_host_template.name,
> + strlen(bnx2i_host_template.name) + 1);
> +
> + mem_ptr = kmalloc(BRCM_ISCSI_XPORT_NAME_SIZE_MAX, GFP_KERNEL);
> + if (mem_ptr == NULL) {
> + printk(KERN_ALERT "failed to alloc proc name mem\n");
> + goto scsi_proc_name_mem_err;
> + }
> + hba->scsi_template->proc_name = mem_ptr;
> +
> + memcpy((void *) hba->iscsi_transport,
> + (const void *) &bnx2i_iscsi_transport,
> + sizeof(struct iscsi_transport));
> +
> + hba->iscsi_transport->host_template = hba->scsi_template;
> +
> + mem_ptr = kmalloc(BRCM_ISCSI_XPORT_NAME_SIZE_MAX, GFP_KERNEL);
> + if (mem_ptr == NULL) {
> + printk(KERN_ALERT "mem alloc error, iscsi xport name\n");
> + goto xport_name_mem_err;
> + }
> + hba->iscsi_transport->name = mem_ptr;
> + sprintf(mem_ptr, "%s%c-%.2x%.2x%.2x", BRCM_ISCSI_XPORT_NAME_PREFIX,
> + dev_id, (u8)hba->pci_dev->bus->number,
> + hba->pci_devno, (u8)hba->pci_func);
> +
> + memcpy((void *)hba->scsi_template->proc_name,
> + (const void *)mem_ptr, strlen(mem_ptr) + 1);
> +
> + hba->shost_template = iscsi_register_transport(hba->iscsi_transport);
> + if (!hba->shost_template) {
> + printk(KERN_ALERT "bnx2i: xport reg failed, hba 0x%p\n", hba);
> + goto failed_registration;
> + }
> + printk(KERN_ALERT "bnx2i: netif=%s, iscsi=%s\n",
> + hba->netdev->name, hba->scsi_template->proc_name);
> + return 0;
> +
> +failed_registration:
> + kfree(hba->iscsi_transport->name);
> +xport_name_mem_err:
> + kfree(hba->scsi_template->proc_name);
> +scsi_proc_name_mem_err:
> + kfree(hba->scsi_template->name);
> +scsi_name_mem_err:
> + kfree(hba->iscsi_transport);
> +iscsi_xport_err:
> + kfree(hba->scsi_template);
> + printk(KERN_ALERT "register iscsi xport failed, hba 0x%p\n", hba);
> + return -ENOMEM;
> +}
> +
> +
> +/*
> + * Function: bnx2i_deregister_xport
> + * Description: this routine will de-allocate memory for SCSI host template,
> + * iSCSI template and de-registers a NX2 device instance
> + */
> +int bnx2i_deregister_xport(struct bnx2i_hba *hba)
> +{
> + if (!hba)
> + return -EINVAL;
> +
> + iscsi_unregister_transport(hba->iscsi_transport);
> + hba->shost_template = NULL;
> +
> + if (hba->scsi_template->name) {
> + kfree(hba->scsi_template->name);
> + hba->scsi_template->name = NULL;
> + }
> + if (hba->scsi_template) {
> + kfree(hba->scsi_template);
> + hba->scsi_template = NULL;
> + }
> + if (hba->iscsi_transport->name) {
> + kfree(hba->iscsi_transport->name);
> + hba->iscsi_transport->name = NULL;
> + }
> + if (hba->iscsi_transport) {
> + kfree(hba->iscsi_transport);
> + hba->iscsi_transport = NULL;
> + }
> + return 0;
> +}
> +
> +
> +/*
> + * Function: bnx2i_session_create
> + * Description: Creates a new iSCSI session instance on given device.
> + */
> +struct iscsi_cls_session *
> + bnx2i_session_create(struct iscsi_transport *it,
> + struct scsi_transport_template *scsit,
> + uint16_t cmds_max, uint16_t qdepth,
> + uint32_t initial_cmdsn, uint32_t *host_no)
> +{
> + struct bnx2i_hba *hba = NULL;
> + struct bnx2i_sess *sess = NULL;
> + struct Scsi_Host *shost;
> + struct iscsi_cls_session *cls_session;
> + int ret_code = 0;
> +
> + hba = bnx2i_get_hba_from_template(scsit);
> + if (bnx2i_adapter_ready(hba))
> + return NULL;
> +
> + shost = scsi_host_alloc(hba->iscsi_transport->host_template,
> + hostdata_privsize(sizeof(struct bnx2i_sess)));
> + if (!shost)
> + return NULL;
> +
> + shost->max_id = 1;
> + shost->max_channel = 1;
> + shost->max_lun = hba->iscsi_transport->max_lun;
> + shost->max_cmd_len = hba->iscsi_transport->max_cmd_len;
> + if (cmds_max)
> + shost->can_queue = cmds_max;
> + if (qdepth)
> + shost->cmd_per_lun = qdepth;
> + shost->transportt = scsit;
> + *host_no = shost->host_no;
> + sess = iscsi_hostdata(shost->hostdata);
> +
> + if (!sess)
> + goto sess_resc_fail;
> +
> + memset(sess, 0, sizeof(struct bnx2i_sess));
> + sess->hba = hba;
> + sess->host = shost;
> +
> + /*
> + * For Open-iSCSI, only normal sessions go through bnx2i.
> + * Discovery session goes through host stack TCP/IP stack.
> + */
> + ret_code = bnx2i_iscsi_sess_new(hba, sess);
> + if (ret_code) {
> + /*
> + * failed to allocate memory
> + */
> + printk(KERN_ALERT "bnx2i_sess_create: unable to alloc sess\n");
> + goto sess_resc_fail;
> + }
> +
> + /*
> + * Update CmdSN related parameters
> + */
> + sess->cmdsn = initial_cmdsn;
> + sess->exp_cmdsn = initial_cmdsn + 1;
> + sess->max_cmdsn = initial_cmdsn + 1;
> +
> + if (scsi_add_host(shost, NULL))
> + goto add_sh_fail;
> +
> + if (!try_module_get(it->owner))
> + goto cls_sess_falied;
> +
> + cls_session = iscsi_create_session(shost, it, 0);
> + if (!cls_session)
> + goto module_put;
> + *(unsigned long *)shost->hostdata = (unsigned long)cls_session;
> +
> + return hostdata_session(shost->hostdata);
> +
> +module_put:
> + module_put(it->owner);
> +cls_sess_falied:
> + scsi_remove_host(shost);
> +add_sh_fail:
> + bnx2i_iscsi_sess_release(hba, sess);
> +sess_resc_fail:
> + scsi_host_put(shost);
> + return NULL;
> +}
> +
> +
> +/*
> + * Function: bnx2i_session_destroy
> + * Description: Destroys previously created iSCSI session instance.
> + */
> +void bnx2i_session_destroy(struct iscsi_cls_session *cls_session)
> +{
> + struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
> + struct bnx2i_sess *sess = iscsi_hostdata(shost->hostdata);
> + struct module *owner = cls_session->transport->owner;
> +
> + if (sess) {
> + bnx2i_iscsi_sess_release(sess->hba, sess);
> + }
> +
> + if (sess->target_name) {
> + kfree(sess->target_name);
> + sess->target_name = NULL;
> + }
> +
> + scsi_remove_host(shost);
> + iscsi_destroy_session(cls_session);
> + scsi_host_put(shost);
> + module_put(owner);
> +}
> +
> +
> +/*
> + * Function: bnx2i_sess_recovery_timeo
> + * Description: session recovery timeout handling routine
> + */
> +void bnx2i_sess_recovery_timeo(struct iscsi_cls_session *cls_session)
> +{
> + struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
> + struct bnx2i_sess *sess = iscsi_hostdata(shost->hostdata);
> +
> + sess->recovery_state |= ISCSI_SESS_RECOVERY_FAILED;
> + if (sess->state != BNX2I_SESS_IN_FFP) {
> + }
> + wake_up(&sess->er_wait);
> +}
> +
> +
> +/*
> + * Function: bnx2i_conn_create
> + * Description: Creates a new iSCSI connection instance for a given session
> + */
> +struct iscsi_cls_conn *bnx2i_conn_create(struct iscsi_cls_session *cls_session,
> + uint32_t cid)
> +{
> + struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
> + struct bnx2i_sess *sess = iscsi_hostdata(shost->hostdata);
> + struct bnx2i_conn *conn;
> + struct iscsi_cls_conn *cls_conn;
> +
> + cls_conn = iscsi_create_conn(cls_session, cid);
> + if (!cls_conn)
> + return NULL;
> +
> + conn = cls_conn->dd_data;
> + memset(conn, 0, sizeof(struct bnx2i_conn));
> + conn->cls_conn = cls_conn;
> + conn->exp_statsn = STATSN_UPDATE_SIGNATURE;
> + conn->iscsi_conn_cid = conn->fw_cid = 0;
> + conn->header_digest_en = 0;
> + conn->data_digest_en = 0;
> + conn->persist_address = NULL;
> + conn->state = CONN_STATE_IDLE;
> + /*
> + * Initialize the connection structure
> + */
> + bnx2i_iscsi_conn_new(sess, conn);
> + conn->conn_cid = cid;
> + return cls_conn;
> +}
> +
> +
> +
> +/*
> + * Function: bnx2i_conn_bind
> + * Description: Binds together iSCSI session instance, iSCSI connection
> + * instance and the TCP connection. If TCP connection does not belong
> + * on the device iSCSI sess/conn is bound, return failure to user.
> + */
> +int bnx2i_conn_bind(struct iscsi_cls_session *cls_session,
> + struct iscsi_cls_conn *cls_conn,
> + uint64_t transport_fd, int is_leading)
> +{
> + struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
> + struct bnx2i_sess *sess = iscsi_hostdata(shost->hostdata);
> + struct bnx2i_conn *tmp = ERR_PTR(-EEXIST);
> + struct bnx2i_conn *conn = cls_conn->dd_data;
> + int ret_code = 0;
> + struct bnx2i_endpoint *ep;
> +
> + ep = (struct bnx2i_endpoint *) (unsigned long) transport_fd;
> +
> + if (ep->state == EP_STATE_PEER_DISCONN) {
> + /* Peer disconnect via' FIN or RST */
> + return -EINVAL;
> + }
> +
> + if (ep->hba != sess->hba) {
> + /* Error - TCP connection does not belong to this device
> + */
> + printk(KERN_ALERT "bnx2i: conn bind, ep=0x%p (0x%p) does not",
> + ep, ep->hba);
> + printk(KERN_ALERT "belong to hba 0x%p\n", sess->hba);
> + return -EEXIST;
> + }
> + if (!conn->gen_pdu.cmd)
> + conn->gen_pdu.cmd = bnx2i_alloc_cmd(sess);
> +
> + /* look-up for existing connection, MC/S is not currently supported */
> + spin_lock_bh(&sess->lock);
> + tmp = NULL;
> + if (!list_empty(&sess->conn_list)) {
> + list_for_each_entry(tmp, &sess->conn_list, link) {
> + if (tmp == conn) {
> + break;
> + }
> + }
> + }
> + if ((tmp != conn) && (conn->sess == sess)) {
> + /* bind iSCSI connection to this session */
> + list_add(&conn->link, &sess->conn_list);
> + if (is_leading) {
> + sess->lead_conn = conn;
> + }
> + }
> +
> + conn->ep = (struct bnx2i_endpoint *) (unsigned long) transport_fd;
> + conn->ep->conn = conn;
> + conn->ep->sess = sess;
> + conn->state = CONN_STATE_XPORT_READY;
> + conn->iscsi_conn_cid = conn->ep->ep_iscsi_cid;
> + conn->fw_cid = conn->ep->ep_cid;
> +
> + bnx2i_bind_conn_to_iscsi_cid(conn, ep->ep_iscsi_cid);
> +
> + spin_unlock_bh(&sess->lock);
> + return ret_code;
> +}
> +
> +
> +/*
> + * Function: bnx2i_conn_destroy
> + * Description: Destroys a iSCSI connection instance.
> + */
> +void bnx2i_conn_destroy(struct iscsi_cls_conn *cls_conn)
> +{
> + struct bnx2i_conn *conn = cls_conn->dd_data;
> +
> + bnx2i_conn_free_login_resources(conn->sess->hba, conn);
> +
> + if (conn->persist_address) {
> + kfree(conn->persist_address);
> + conn->persist_address = NULL;
> + }
> + iscsi_destroy_conn(cls_conn);
> +}
> +
> +
> +/*
> + * Function: bnx2i_conn_set_param
> + * Description: During FFP migration, user daemon will issue this call to
> + * update negotiated iSCSI parameters to driver.
> + */
> +int bnx2i_conn_set_param(struct iscsi_cls_conn *cls_conn,
> + enum iscsi_param param, char *buf, int buflen)
> +{
> + struct bnx2i_conn *conn = cls_conn->dd_data;
> + struct bnx2i_sess *sess = conn->sess;
> +
> + spin_lock_bh(&sess->lock);
> + if (conn->state != CONN_STATE_IN_LOGIN) {
> + printk(KERN_ERR "bnx2i: can't change param [%d]\n", param);
> + spin_unlock_bh(&sess->lock);
> + return 0;
> + }
> + spin_unlock_bh(&sess->lock);
> + switch (param) {
> + case ISCSI_PARAM_MAX_RECV_DLENGTH:
> + sscanf(buf, "%d", &conn->max_data_seg_len_recv);
> + break;
> + case ISCSI_PARAM_MAX_XMIT_DLENGTH:
> + sscanf(buf, "%d", &conn->max_data_seg_len_xmit);
> + break;
> + case ISCSI_PARAM_HDRDGST_EN:
> + sscanf(buf, "%d", &conn->header_digest_en);
> + break;
> + case ISCSI_PARAM_DATADGST_EN:
> + sscanf(buf, "%d", &conn->data_digest_en);
> + break;
> + case ISCSI_PARAM_INITIAL_R2T_EN:
> + if (conn == sess->lead_conn) {
> + sscanf(buf, "%d", &sess->initial_r2t);
> + }
> + break;
> + case ISCSI_PARAM_MAX_R2T:
> + if (conn == sess->lead_conn) {
> + sscanf(buf, "%d", &sess->max_r2t);
> + }
> + break;
> + case ISCSI_PARAM_IMM_DATA_EN:
> + if (conn == sess->lead_conn) {
> + sscanf(buf, "%d", &sess->imm_data);
> + }
> + break;
> + case ISCSI_PARAM_FIRST_BURST:
> + if (conn == sess->lead_conn) {
> + sscanf(buf, "%d", &sess->first_burst_len);
> + }
> + break;
> + case ISCSI_PARAM_MAX_BURST:
> + if (conn == sess->lead_conn) {
> + sscanf(buf, "%d", &sess->max_burst_len);
> + }
> + break;
> + case ISCSI_PARAM_PDU_INORDER_EN:
> + if (conn == sess->lead_conn) {
> + sscanf(buf, "%d", &sess->pdu_inorder);
> + }
> + break;
> + case ISCSI_PARAM_DATASEQ_INORDER_EN:
> + if (conn == sess->lead_conn) {
> + sscanf(buf, "%d", &sess->dataseq_inorder);
> + }
> + break;
> + case ISCSI_PARAM_ERL:
> + if (conn == sess->lead_conn) {
> + sscanf(buf, "%d", &sess->erl);
> + }
> + break;
> + case ISCSI_PARAM_IFMARKER_EN:
> + sscanf(buf, "%d", &conn->ifmarker_enable);
> + BUG_ON(conn->ifmarker_enable);
> + break;
> + case ISCSI_PARAM_OFMARKER_EN:
> + sscanf(buf, "%d", &conn->ofmarker_enable);
> + BUG_ON(conn->ofmarker_enable);
> + break;
> + case ISCSI_PARAM_EXP_STATSN:
> + sscanf(buf, "%u", &conn->exp_statsn);
> + break;
> + case ISCSI_PARAM_TARGET_NAME:
> + if (sess->target_name)
> + break;
> + sess->target_name = kstrdup(buf, GFP_KERNEL);
> + if (!sess->target_name)
> + return -ENOMEM;
> + break;
> + case ISCSI_PARAM_TPGT:
> + sscanf(buf, "%d", &sess->tgt_prtl_grp);
> + break;
> + case ISCSI_PARAM_PERSISTENT_PORT:
> + {
> + sscanf(buf, "%d", &conn->persist_port);
> + }
> + break;
> + case ISCSI_PARAM_PERSISTENT_ADDRESS:
> + if (conn->persist_address)
> + break;
> + conn->persist_address = kstrdup(buf, GFP_KERNEL);
> + if (!conn->persist_address)
> + return -ENOMEM;
> + break;
> + default:
> + printk(KERN_ALERT "PARAM_UNKNOWN: 0x%x\n", param);
> + break;
> + }
> +
> + return 0;
> +}
> +
> +
> +/*
> + * Function: bnx2i_conn_get_param
> + * Description: Call to retrieve iSCSI connection parameters
> + */
> +int bnx2i_conn_get_param(struct iscsi_cls_conn *cls_conn,
> + enum iscsi_param param, char *buf)
> +{
> + struct bnx2i_conn *conn;
> + int len = 0;
> +
> + if (!cls_conn)
> + return -EINVAL;
> + conn = (struct bnx2i_conn *)cls_conn->dd_data;
> + if (!conn || !conn->ep ||
> + (conn->ep->state != EP_STATE_ULP_UPDATE_COMPL))
> + return -EINVAL;
> +
> + switch (param) {
> + case ISCSI_PARAM_MAX_RECV_DLENGTH:
> + len = sprintf(buf, "%u\n", conn->max_data_seg_len_recv);
> + break;
> + case ISCSI_PARAM_MAX_XMIT_DLENGTH:
> + len = sprintf(buf, "%u\n", conn->max_data_seg_len_xmit);
> + break;
> + case ISCSI_PARAM_HDRDGST_EN:
> + len = sprintf(buf, "%d\n", conn->header_digest_en);
> + break;
> + case ISCSI_PARAM_DATADGST_EN:
> + len = sprintf(buf, "%d\n", conn->data_digest_en);
> + break;
> + case ISCSI_PARAM_IFMARKER_EN:
> + len = sprintf(buf, "%u\n", conn->ifmarker_enable);
> + break;
> + case ISCSI_PARAM_OFMARKER_EN:
> + len = sprintf(buf, "%u\n", conn->ofmarker_enable);
> + break;
> + case ISCSI_PARAM_EXP_STATSN:
> + len = sprintf(buf, "%u\n", conn->exp_statsn);
> + break;
> + case ISCSI_PARAM_PERSISTENT_PORT:
> + len = sprintf(buf, "%d\n", conn->persist_port);
> + break;
> + case ISCSI_PARAM_PERSISTENT_ADDRESS:
> + if (conn->persist_address) {
> + len = sprintf(buf, "%s\n", conn->persist_address);
> + }
> + break;
> + case ISCSI_PARAM_CONN_PORT:
> + len = sprintf(buf, "%hu\n", conn->ep->cm_sk->dst_port);
> + break;
> + case ISCSI_PARAM_CONN_ADDRESS:
> + len = sprintf(buf, NIPQUAD_FMT "\n",
> + NIPQUAD(conn->ep->cm_sk->dst_ip));
> + break;
> + default:
> + printk(KERN_ALERT "get_param: conn 0x%p param %d not found\n",
> + conn, (u32)param);
> + return -ENOSYS;
> + }
> +
> + return len;
> +}
> +
> +
> +/*
> + * Function: bnx2i_session_get_param
> + * Description: Call to obtain iSCSI session parameters
> + */
> +int bnx2i_session_get_param(struct iscsi_cls_session *cls_session,
> + enum iscsi_param param, char *buf)
> +{
> + struct Scsi_Host *shost = NULL;
> + struct bnx2i_sess *sess = NULL;
> + int len = 0;
> +
> + if (!cls_session)
> + return -EINVAL;
> +
> + shost = iscsi_session_to_shost(cls_session);
> + sess = iscsi_hostdata(shost->hostdata);
> + if (!sess || !sess->lead_conn)
> + return -EINVAL;
> +
> + switch (param) {
> + case ISCSI_PARAM_INITIAL_R2T_EN:
> + len = sprintf(buf, "%d\n", sess->initial_r2t);
> + break;
> + case ISCSI_PARAM_MAX_R2T:
> + len = sprintf(buf, "%hu\n", sess->max_r2t);
> + break;
> + case ISCSI_PARAM_IMM_DATA_EN:
> + len = sprintf(buf, "%d\n", sess->imm_data);
> + break;
> + case ISCSI_PARAM_FIRST_BURST:
> + len = sprintf(buf, "%u\n", sess->first_burst_len);
> + break;
> + case ISCSI_PARAM_MAX_BURST:
> + len = sprintf(buf, "%u\n", sess->max_burst_len);
> + break;
> + case ISCSI_PARAM_PDU_INORDER_EN:
> + len = sprintf(buf, "%d\n", sess->pdu_inorder);
> + break;
> + case ISCSI_PARAM_DATASEQ_INORDER_EN:
> + len = sprintf(buf, "%d\n", sess->dataseq_inorder);
> + break;
> + case ISCSI_PARAM_ERL:
> + len = sprintf(buf, "%d\n", sess->erl);
> + break;
> + case ISCSI_PARAM_TARGET_NAME:
> + if (sess->target_name) {
> + len = sprintf(buf, "%s\n", sess->target_name);
> + }
> + break;
> + case ISCSI_PARAM_TPGT:
> + len = sprintf(buf, "%d\n", sess->tgt_prtl_grp);
> + break;
> + default:
> + printk(KERN_ALERT "sess_get_param: sess 0x%p", sess);
> + printk(KERN_ALERT "param (0x%x) not found\n", (u32) param);
> + return -ENOSYS;
> + }
> +
> + return len;
> +}
> +
> +
> +/*
> + * Function: bnx2i_conn_start
> + * Description: last call in FFP migration to handover iscsi conn to the driver
> + */
> +int bnx2i_conn_start(struct iscsi_cls_conn *cls_conn)
> +{
> + struct bnx2i_conn *conn = (struct bnx2i_conn *) cls_conn->dd_data;
> + struct bnx2i_sess *sess = conn->sess;
> +
> + if (conn->state != CONN_STATE_IN_LOGIN) {
> + printk(KERN_ALERT "conn_start: conn 0x%p state 0x%x err!!\n",
> + conn, conn->state);
> + return -EINVAL;
> + }
> +
> + if (!sess->initial_r2t) {
> + if (sess->first_burst_len > sess->max_burst_len)
> + return -EINVAL;
> + } else if (conn->max_data_seg_len_xmit > sess->max_burst_len) {
> + if (sess->first_burst_len > sess->max_burst_len)
> + return -EINVAL;
> + /* don't bother if only immediate data is supported and
> + * FBL & MBL are greater than MRDSL. In that case initiator
> + * will always send MRDSL worth of immediate data
> + */
> + }
> +
> + conn->state = CONN_STATE_FFP_STATE;
> + if (conn->sess->lead_conn == conn) {
> + conn->sess->state = BNX2I_SESS_IN_FFP;
> + }
> +
> + conn->ep->state = EP_STATE_ULP_UPDATE_START;
> + bnx2i_update_iscsi_conn(conn);
> +
> + conn->ep->ofld_timer.expires = 10*HZ + jiffies;
> + conn->ep->ofld_timer.function = bnx2i_ep_ofld_timer;
> + conn->ep->ofld_timer.data = (unsigned long)conn->ep;
> + add_timer(&conn->ep->ofld_timer);
> + /* update iSCSI context for this conn, wait for CNIC to complete */
> + wait_event_interruptible(conn->ep->ofld_wait,
> + conn->ep->state != EP_STATE_ULP_UPDATE_START);
> +
> + if (signal_pending(current))
> + flush_signals(current);
> + del_timer_sync(&conn->ep->ofld_timer);
> + if (conn->ep->state != EP_STATE_ULP_UPDATE_COMPL) {
> + /* should never happen */
> + }
> + /* Free login ITT, not required anymore */
> + if (conn->gen_pdu.cmd) {
> + bnx2i_free_cmd(conn->sess, conn->gen_pdu.cmd);
> + conn->gen_pdu.cmd = NULL;
> + }
> +
> + switch (conn->stop_state) {
> + case STOP_CONN_RECOVER:
> + sess->recovery_state = ISCSI_SESS_RECOVERY_COMPLETE;
> + conn->sess->state = BNX2I_SESS_IN_FFP;
> + iscsi_unblock_session(session_to_cls(sess));
> + wake_up(&sess->er_wait);
> + break;
> + case STOP_CONN_TERM:
> + break;
> + default:
> + ;
> + }
> +
> + return 0;
> +}
> +
> +
> +/*
> + * Function: bnx2i_conn_stop
> + * Description: call to take control of iscsi conn from the driver.
> + * Could be called when login failed, when recovery is to be
> + * attempted or during connection teardown
> + */
> +void bnx2i_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
> +{
> + struct bnx2i_conn *conn = (struct bnx2i_conn *)cls_conn->dd_data;
> +
> + conn->stop_state = flag;
> + iscsi_block_session(session_to_cls(conn->sess));
> +
> + switch (flag) {
> + case STOP_CONN_RECOVER:
> + conn->sess->state = BNX2I_SESS_IN_RECOVERY;
> + break;
> + case STOP_CONN_TERM:
> + if (conn->sess && (conn->sess->state & BNX2I_SESS_IN_FFP)) {
> + conn->sess->state = BNX2I_SESS_IN_SHUTDOWN;
> + }
> + break;
> + default:
> + printk(KERN_ERR "bnx2i: invalid conn stop req %d\n", flag);
> + }
> +
> + return;
> +}
> +
> +
> +/*
> + * Function: bnx2i_conn_send_pdu
> + * Description: To send iSCSI PDUs prepared by user daemon, only login, logout,
> + * nop-out pdu's should flow this path.
> + */
> +int bnx2i_conn_send_pdu(struct iscsi_cls_conn *cls_conn,
> + struct iscsi_hdr *hdr, char *data,
> + uint32_t data_size)
> +{
> + struct bnx2i_conn *conn = NULL;
> + struct iscsi_hdr *iscsi_hdr = (struct iscsi_hdr *) hdr;
> + struct bnx2i_cmd *cmnd = NULL;
> + uint32_t payload_size = 0;
> + int rc;
> + unsigned long flags;
> +
> + if (!cls_conn) {
> + printk(KERN_ALERT "bnx2i_conn_send_pdu: NULL conn ptr. \n");
> + return -EIO;
> + }
> + conn = (struct bnx2i_conn *)cls_conn->dd_data;
> + if (!conn->gen_pdu.req_buf) {
> + printk(KERN_ALERT "send_pdu: login buf not allocated\n");
> + /* ERR - buffer not allocated, should not happen */
> + return -EIO;
> + }
> +
> + if (conn->gen_pdu.cmd) {
> + if ((conn->state != CONN_STATE_XPORT_READY) &&
> + (conn->state != CONN_STATE_IN_LOGIN)) {
> + printk(KERN_ALERT "send_pdu: %d != XPORT_READY\n",
> + conn->state);
> + return -EPERM;
> + }
> + cmnd = conn->gen_pdu.cmd;
> + } else { /* could be NOPOUT or the LOGOUT request */
> + spin_lock_irqsave(conn->sess->host->host_lock, flags);
> + cmnd = bnx2i_alloc_cmd(conn->sess);
> + spin_unlock_irqrestore(conn->sess->host->host_lock, flags);
> +
> + if (!cmnd) {
> + printk(KERN_ALERT "bnx2i: Error - cmd not allocated\n");
> + return -EIO;
> + }
> + }
> + memset(conn->gen_pdu.req_buf, 0, ISCSI_CONN_LOGIN_BUF_SIZE);
> + /* Login request, copy hdr & data to buffer in conn struct */
> + memcpy((void *) &conn->gen_pdu.pdu_hdr, (const void *) hdr,
> + sizeof(struct iscsi_hdr));
> +
> + cmnd->iscsi_opcode = iscsi_hdr->opcode;
> + switch (iscsi_hdr->opcode & ISCSI_OPCODE_MASK) {
> + case ISCSI_OP_LOGIN:
> + if (conn->state == CONN_STATE_XPORT_READY)
> + conn->state = CONN_STATE_IN_LOGIN;
> + break;
> + case ISCSI_OP_LOGOUT:
> + conn->state = CONN_STATE_IN_LOGOUT;
> + conn->sess->state = BNX2I_SESS_IN_LOGOUT;
> + break;
> + case ISCSI_OP_NOOP_OUT:
> + break;
> + default:
> + ;
> + }
> +
> + conn->gen_pdu.req_buf_size = data_size;
> + payload_size = (hdr->dlength[0] << 16) | (hdr->dlength[1] << 8) |
> + hdr->dlength[2];
> +
> + if (data_size) {
> + memcpy((void *)conn->gen_pdu.req_buf, (const void *)data,
> + data_size);
> + conn->gen_pdu.req_wr_ptr =
> + conn->gen_pdu.req_buf + payload_size;
> + }
> + cmnd->conn = conn;
> + cmnd->scsi_cmd = NULL;
> + rc = bnx2i_iscsi_send_generic_request(cmnd);
> + return rc;
> +}
> +
> +
> +/*
> + * Function : bnx2i_conn_get_stats
> + * Description: Returns iSCSI stats
> + */
> +void bnx2i_conn_get_stats(struct iscsi_cls_conn *cls_conn,
> + struct iscsi_stats *stats)
> +{
> + struct bnx2i_conn *conn = (struct bnx2i_conn *) cls_conn->dd_data;
> +
> + stats->txdata_octets = conn->total_data_octets_sent;
> + stats->rxdata_octets = conn->total_data_octets_rcvd;
> +
> + stats->noptx_pdus = conn->num_nopin_pdus;
> + stats->scsicmd_pdus = conn->num_scsi_cmd_pdus;
> + stats->tmfcmd_pdus = conn->num_tmf_req_pdus;
> + stats->login_pdus = conn->num_login_req_pdus;
> + stats->text_pdus = 0;
> + stats->dataout_pdus = conn->num_dataout_pdus;
> + stats->logout_pdus = conn->num_logout_req_pdus;
> + stats->snack_pdus = 0;
> +
> + stats->noprx_pdus = conn->num_nopout_pdus;
> + stats->scsirsp_pdus = conn->num_scsi_resp_pdus;
> + stats->tmfrsp_pdus = conn->num_tmf_resp_pdus;
> + stats->textrsp_pdus = 0;
> + stats->datain_pdus = conn->num_datain_pdus;
> + stats->logoutrsp_pdus = conn->num_logout_resp_pdus;
> + stats->r2t_pdus = conn->num_r2t_pdus;
> + stats->async_pdus = conn->num_async_pdus;
> + stats->rjt_pdus = conn->num_reject_pdus;
> +
> + stats->digest_err = 0;
> + stats->timeout_err = 0;
> + stats->custom_length = 0;
> +}
> +
> +
> +
> +/*
> + * Function : bnx2i_check_nx2_dev_busy
> + * Description: this routine unregister devices if there are no active conns
> + */
> +static void bnx2i_check_nx2_dev_busy(void)
> +{
> + if (bnx2i_num_free_ep == bnx2i_max_free_ep) {
> + bnx2i_unreg_dev_all();
> + msleep(2);
> + }
> +}
> +
> +
> +/*
> + * Function : bnx2i_ep_connect
> + * Description: this routine initiates the TCP/IP connection by invoking
> + * Option-2 interface with l5_core and the CNIC
> + */
> +int bnx2i_ep_connect(struct sockaddr *dst_addr, int non_blocking,
> + uint64_t *ep_handle)
> +{
> + u32 iscsi_cid = BNX2I_CID_RESERVED;
> + struct sockaddr_in *desti = (struct sockaddr_in *) dst_addr;
> + struct bnx2i_endpoint *endpoint;
> + struct bnx2i_hba *hba = NULL;
> + struct cnic_dev *cnic = NULL;
> + struct cnic_dev *tmp_cnic = NULL;
> + struct bnx2i_hba *tmp_hba = NULL;
> + struct cnic_sockaddr saddr;
> + int rc = 0;
> + extern int bnx2i_reg_device;
> + extern struct bnx2i_hba *get_adapter_list_head(void);
> +
> + /*
> + * check if the given destination can be reached through NX2 device
> + */
> +
> + if ((!bnx2i_reg_device) &&
> + (bnx2i_num_free_ep == bnx2i_max_free_ep)) {
> + bnx2i_reg_dev_all();
> + }
> + tmp_hba = get_adapter_list_head();
> + if (tmp_hba && tmp_hba->cnic) {
> + tmp_cnic = tmp_hba->cnic;
> + cnic = tmp_cnic->cm_select_dev(desti, CNIC_ULP_ISCSI);
> + }
> + if (!cnic) {
> + printk(KERN_ALERT "bnx2i: ep_conn, can't connect using cnic\n");
> + rc = 0;
> + goto check_busy;
> + }
> + hba = bnx2i_find_hba_for_cnic(cnic);
> +
> + if (bnx2i_adapter_ready(hba)) {
> + printk(KERN_ALERT "bnx2i: ep_conn, adapter not found\n");
> + rc = 0;
> + goto check_busy;
> + }
> + if (hba->netdev->mtu > hba->mtu_supported) {
> + printk(KERN_ALERT "bnx2i: %s network i/f mtu is set to %d\n",
> + hba->netdev->name, hba->netdev->mtu);
> + printk(KERN_ALERT "bnx2i: iSCSI HBA can support mtu of %d\n",
> + hba->mtu_supported);
> + rc = 0;
> + goto check_busy;
> + }
> + endpoint = bnx2i_alloc_ep();
> + if (!endpoint) {
> + printk(KERN_ALERT "bnx2i: ep_conn, unable to alloc ep\n");
> + *ep_handle = (uint64_t) 0;
> + rc = -ENOMEM;
> + goto check_busy;
> + }
> +
> + endpoint->ep_iscsi_cid = (u16)ISCSI_RESERVED_TAG;
> + iscsi_cid = bnx2i_alloc_iscsi_cid(hba);
> + if (iscsi_cid == (u16) ISCSI_RESERVED_TAG) {
> + printk(KERN_ALERT "alloc_ep: unable to allocate iscsi cid\n");
> + rc = -ENOMEM;
> + goto iscsi_cid_err;
> + }
> + endpoint->hba = hba;
> + endpoint->hba_age = hba->age;
> +
> + rc = bnx2i_alloc_qp_resc(hba, endpoint);
> + if (rc != 0) {
> + printk(KERN_ALERT "bnx2i: ep_conn, alloc QP resc error\n");
> + rc = -ENOMEM;
> + goto qp_resc_err;
> + }
> +
> + endpoint->ep_iscsi_cid = iscsi_cid;
> + endpoint->state = EP_STATE_OFLD_START;
> + bnx2i_ep_ofld_list_add(hba, endpoint);
> +
> + bnx2i_send_conn_ofld_req(hba, endpoint);
> +
> + init_timer(&endpoint->ofld_timer);
> + endpoint->ofld_timer.expires = 2 * HZ + jiffies;
> + endpoint->ofld_timer.function = bnx2i_ep_ofld_timer;
> + endpoint->ofld_timer.data = (unsigned long) endpoint;
> + add_timer(&endpoint->ofld_timer);
> + /* Wait for CNIC hardware to setup conn context and return 'cid' */
> + wait_event_interruptible(endpoint->ofld_wait,
> + endpoint->state != EP_STATE_OFLD_START);
> +
> + if (signal_pending(current))
> + flush_signals(current);
> + del_timer_sync(&endpoint->ofld_timer);
> + list_del_init(&endpoint->link);
> +
> + if (endpoint->state != EP_STATE_OFLD_COMPL) {
> + rc = -ENOSPC;
> + goto conn_failed;
> + }
> +
> + rc = -EINVAL;
> + if (hba->reg_with_cnic)
> + rc = cnic->cm_create(cnic, CNIC_ULP_ISCSI, endpoint->ep_cid,
> + iscsi_cid, &endpoint->cm_sk, endpoint);
> + if (rc)
> + goto conn_failed;
> +
> + memset(&saddr, 0, sizeof(saddr));
> + saddr.remote_addr.sin_addr.s_addr = desti->sin_addr.s_addr;
> + saddr.remote_addr.sin_port = desti->sin_port;
> + saddr.local_addr.sin_port = htons(endpoint->tcp_port);
> + endpoint->state = EP_STATE_CONNECT_START;
> + rc = -EINVAL;
> + if (hba->reg_with_cnic)
> + rc = cnic->cm_connect(endpoint->cm_sk, &saddr);
> + else
> + goto conn_failed;
> +
> + if (rc)
> + goto release_ep;
> +
> + bnx2i_map_ep_dbell_regs(endpoint);
> +
> + *ep_handle = (uint64_t) (unsigned long) endpoint;
> + return 0;
> +
> +release_ep:
> + cnic->cm_destroy(endpoint->cm_sk);
> +conn_failed:
> +iscsi_cid_err:
> + bnx2i_free_qp_resc(hba, endpoint);
> +qp_resc_err:
> + bnx2i_free_ep(endpoint);
> +check_busy:
> + *ep_handle = (uint64_t) 0;
> + bnx2i_check_nx2_dev_busy();
> + return rc;
> +}
> +
> +
> +
> +/*
> + * Function : bnx2i_ep_poll
> + * Description: polls for TCP connect request to complete
> + */
> +int bnx2i_ep_poll(uint64_t ep_handle, int timeout_ms)
> +{
> + struct bnx2i_endpoint *ep;
> + int rc = 0;
> + ep = (struct bnx2i_endpoint *) (unsigned long) ep_handle;
> +
> + if (!ep) {
> + return -EINVAL;
> + }
> + if (ep->state == EP_STATE_IDLE) {
> + return -1;
> + }
> + if (ep->state == EP_STATE_CONNECT_COMPL) {
> + return 1;
> + }
> +
> + rc = wait_event_interruptible_timeout(ep->ofld_wait,
> + (ep->state ==
> + EP_STATE_CONNECT_COMPL),
> + msecs_to_jiffies(timeout_ms));
> + if (!rc || (ep->state == EP_STATE_OFLD_FAILED)) {
> + rc = -1;
> + }
> +
> + if (rc > 0) {
> + return 1;
> + } else if (!rc) {
> + return 0; /* timeout */
> + } else {
> + return rc;
> + }
> +}
> +
> +
> +/*
> + * Function : bnx2i_ep_disconnect
> + * Description: initiates TCP/IP connection teardown process
> + */
> +void bnx2i_ep_disconnect(uint64_t ep_handle)
> +{
> + struct bnx2i_endpoint *ep;
> + struct cnic_dev *cnic = NULL;
> + struct bnx2i_sess *sess = NULL;
> + int rc = 0;
> +
> + ep = (struct bnx2i_endpoint *) (unsigned long) ep_handle;
> + if (!ep || (ep_handle == -1)) {
> + return;
> + }
> + if (ep->state == EP_STATE_IDLE) {
> + goto return_ep;
> + }
> + cnic = ep->hba->cnic;
> +
> + if (ep->state == EP_STATE_PEER_DISCONN) {
> + ep->state = EP_STATE_DISCONN_COMPL;
> + goto peer_discon;
> + }
> +
> + if (test_bit(ADAPTER_STATE_DOWN, &ep->hba->adapter_state)) {
> + goto free_resc;
> + }
> + if (ep->hba_age != ep->hba->age) {
> + goto dev_reset;
> + }
> +
> + ep->state = EP_STATE_DISCONN_START;
> +
> + init_timer(&ep->ofld_timer);
> + ep->ofld_timer.expires = 10*HZ + jiffies;
> + ep->ofld_timer.function = bnx2i_ep_ofld_timer;
> + ep->ofld_timer.data = (unsigned long) ep;
> + add_timer(&ep->ofld_timer);
> +
> + if (ep->hba->reg_with_cnic)
> + cnic->cm_close(ep->cm_sk);
> + else
> + goto free_resc;
> +
> + /* wait for option-2 conn teardown */
> + wait_event_interruptible(ep->ofld_wait,
> + ep->state != EP_STATE_DISCONN_START);
> +
> + if (signal_pending(current))
> + flush_signals(current);
> + del_timer_sync(&ep->ofld_timer);
> +
> +peer_discon:
> + if (!ep->hba->reg_with_cnic)
> + goto free_resc;
> +
> + rc = cnic->cm_destroy(ep->cm_sk);
> + ep->state = EP_STATE_CLEANUP_START;
> + init_timer(&ep->ofld_timer);
> + ep->ofld_timer.expires = 10*HZ + jiffies;
> + ep->ofld_timer.function = bnx2i_ep_ofld_timer;
> + ep->ofld_timer.data = (unsigned long) ep;
> + add_timer(&ep->ofld_timer);
> +
> + bnx2i_ep_destroy_list_add(ep->hba, ep);
> +
> + /* destroy iSCSI context, wait for it to complete */
> + bnx2i_send_conn_destroy(ep->hba, ep);
> + wait_event_interruptible(ep->ofld_wait,
> + (ep->state != EP_STATE_CLEANUP_START));
> +
> + if (signal_pending(current))
> + flush_signals(current);
> + del_timer_sync(&ep->ofld_timer);
> + if (ep->state != EP_STATE_CLEANUP_CMPL) {
> + /* should never happen */
> + printk(KERN_ALERT "bnx2i - conn destroy failed\n");
> + }
> +
> +dev_reset:
> + bnx2i_flush_active_cmd_queue(ep->sess, DID_NO_CONNECT);
> +
> +free_resc:
> + bnx2i_free_qp_resc(ep->hba, ep);
> +return_ep:
> + if (ep->conn && ep->conn->sess)
> + /* session recovery in progress */
> + sess = ep->conn->sess;
> + if (sess && (sess->state != BNX2I_SESS_IN_RECOVERY))
> + /* session logged out */
> + sess = NULL;
> +
> + bnx2i_free_ep(ep);
> + if (sess) {
> + wake_up(&sess->er_wait);
> + }
> +
> + bnx2i_check_nx2_dev_busy();
> + return;
> +}
> +
> +
> +
> +
> +/*
> + * 'Scsi_Host_Template' structure and 'iscsi_tranport' structure template
> + * used while registering with the iSCSI trnaport module.
> + */
> +struct scsi_host_template bnx2i_host_template = {
> + .module = THIS_MODULE,
> + .name = "Broadcom Offload iSCSI Initiator",
> + .queuecommand = bnx2i_queuecommand,
> + .eh_abort_handler = bnx2i_abort,
> + .eh_host_reset_handler = bnx2i_host_reset,
> + .bios_param = NULL,
> + .can_queue = 128,
> + .max_sectors = 256,
> + .this_id = -1,
> + .cmd_per_lun = 64,
> + .use_clustering = ENABLE_CLUSTERING,
> + .sg_tablesize = ISCSI_MAX_BDS_PER_CMD,
> + .proc_name = NULL
> + };
> +
> +
> +
> +struct iscsi_transport bnx2i_iscsi_transport = {
> + .owner = THIS_MODULE,
> + .name = "bnx2i",
> + .caps = CAP_RECOVERY_L0 | CAP_HDRDGST | CAP_MULTI_R2T
> + | CAP_DATADGST,
> + .param_mask = ISCSI_MAX_RECV_DLENGTH |
> + ISCSI_MAX_XMIT_DLENGTH |
> + ISCSI_HDRDGST_EN |
> + ISCSI_DATADGST_EN |
> + ISCSI_INITIAL_R2T_EN |
> + ISCSI_MAX_R2T |
> + ISCSI_IMM_DATA_EN |
> + ISCSI_FIRST_BURST |
> + ISCSI_MAX_BURST |
> + ISCSI_PDU_INORDER_EN |
> + ISCSI_DATASEQ_INORDER_EN |
> + ISCSI_ERL |
> + ISCSI_CONN_PORT |
> + ISCSI_CONN_ADDRESS |
> + ISCSI_EXP_STATSN |
> + ISCSI_PERSISTENT_PORT |
> + ISCSI_PERSISTENT_ADDRESS |
> + ISCSI_TARGET_NAME |
> + ISCSI_TPGT,
> + .host_param_mask = 0,
> + .host_template = &bnx2i_host_template,
> + .sessiondata_size = sizeof(struct bnx2i_sess),
> + .conndata_size = sizeof(struct bnx2i_conn),
> + .max_conn = 1,
> + .max_cmd_len = 16,
> + .max_lun = 512,
> + .create_session = bnx2i_session_create,
> + .destroy_session = bnx2i_session_destroy,
> + .create_conn = bnx2i_conn_create,
> + .bind_conn = bnx2i_conn_bind,
> + .destroy_conn = bnx2i_conn_destroy,
> + .set_param = bnx2i_conn_set_param,
> + .get_conn_param = bnx2i_conn_get_param,
> + .get_session_param = bnx2i_session_get_param,
> + .start_conn = bnx2i_conn_start,
> + .stop_conn = bnx2i_conn_stop,
> + .send_pdu = bnx2i_conn_send_pdu,
> + .get_stats = bnx2i_conn_get_stats,
> + /* iscsi host params */
> + .get_host_param = NULL,
> + .set_host_param = NULL,
> + /* TCP connect - disconnect - option-2 interface calls */
> + .ep_connect = bnx2i_ep_connect,
> + .ep_poll = bnx2i_ep_poll,
> + .ep_disconnect = bnx2i_ep_disconnect,
> + /* Error recovery timeout call */
> + .session_recovery_timedout = bnx2i_sess_recovery_timeo
> +};
> diff --git a/drivers/scsi/bnx2i/bnx2i_sysfs.c b/drivers/scsi/bnx2i/bnx2i_sysfs.c
> new file mode 100644
> index 0000000..6bd6eba
> --- /dev/null
> +++ b/drivers/scsi/bnx2i/bnx2i_sysfs.c
> @@ -0,0 +1,616 @@
> +/* bnx2i_sysfs.c: Broadcom NetXtreme II iSCSI driver.
> + *
> + * Copyright (c) 2004, 2005, 2006 Broadcom Corporation
> + *
> + * 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: Anil Veerabhadrappa (anilgv@broadcom.com)
> + */
> +
> +#include "bnx2i.h"
> +
> +#define BNX2I_SYSFS_VERSION 0x2
> +
> +static ssize_t bnx2i_show_mips_status(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> + ssize_t len = 0;
> +
> + bnx2i_read_mips_idle_counters(hba);
> +
> + len = sprintf(buf, "%d\n%lu\n%d\n%llu\n%llu\n"
> + "%llu\n%llu\n%llu\n%llu\n%llu\n%llu\n",
> + BNX2I_SYSFS_VERSION, jiffies, HZ,
> + hba->mips_idle.cp_idle_count,
> + hba->mips_idle.txp_idle_count,
> + hba->mips_idle.txp_tdma_count,
> + hba->mips_idle.txp_ctx_count,
> + hba->mips_idle.txp_hdrq_count,
> + hba->mips_idle.tpat_idle_count,
> + hba->mips_idle.rxp_idle_count,
> + hba->mips_idle.com_idle_count);
> + return (len);
> +}
> +
> +static ssize_t bnx2i_show_net_if_name(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + return sprintf(buf, "%s\n", hba->netdev->name);
> +}
> +
> +static ssize_t bnx2i_show_pci_bar(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + return sprintf(buf, "0x%.8x\n",
> + (u32) pci_resource_start(hba->pci_dev, 0));
> +}
> +
> +static ssize_t bnx2i_show_sq_info(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + return sprintf(buf, "0x%x\n", hba->max_sqes);
> +}
> +
> +static ssize_t bnx2i_set_sq_info(struct class_device *cdev,
> + const char *buf, size_t count)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> + u32 val;
> +
> + if (sscanf(buf, " 0x%x ", &val) > 0) {
> + if ((val >= BNX2I_SQ_WQES_MIN) &&
> + (val <= BNX2I_SQ_WQES_MAX)) {
> + hba->max_sqes = val;
> + }
> + }
> + return count;
> +}
> +
> +static ssize_t bnx2i_show_cq_info(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + return sprintf(buf, "0x%x\n", hba->max_cqes);
> +}
> +
> +static ssize_t bnx2i_set_cq_info(struct class_device *cdev,
> + const char *buf, size_t count)
> +{
> + u32 val;
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + if (sscanf(buf, " 0x%x ", &val) > 0) {
> + if ((val >= BNX2I_CQ_WQES_MIN) &&
> + (val <= BNX2I_CQ_WQES_MAX)) {
> + hba->max_cqes = val;
> + }
> + }
> + return count;
> +}
> +
> +static ssize_t bnx2i_show_rq_info(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + return sprintf(buf, "0x%x\n", hba->max_rqes);
> +}
> +
> +static ssize_t bnx2i_set_rq_info(struct class_device *cdev, const char *buf,
> + size_t count)
> +{
> + u32 val;
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + if (sscanf(buf, " 0x%x ", &val) > 0) {
> + if ((val >= BNX2I_RQ_WQES_MIN) &&
> + (val <= BNX2I_RQ_WQES_MAX)) {
> + hba->max_rqes = val;
> + }
> + }
> + return count;
> +}
> +
> +
> +static ssize_t bnx2i_show_ccell_info(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + return sprintf(buf, "0x%x\n", hba->num_ccell);
> +}
> +
> +static ssize_t bnx2i_set_ccell_info(struct class_device *cdev,
> + const char *buf, size_t count)
> +{
> + u32 val;
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + if (sscanf(buf, " 0x%x ", &val) > 0) {
> + if ((val >= BNX2I_CCELLS_MIN) &&
> + (val <= BNX2I_CCELLS_MAX)) {
> + hba->num_ccell = val;
> + }
> + }
> + return count;
> +}
> +
> +
> +static ssize_t bnx2i_read_pci_trigger_reg(struct class_device *cdev,
> + char *buf)
> +{
> + u32 reg_val = 0;
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +#define PCI_EVENT_TRIGGER_REG 0xCAC /* DMA WCHAN STAT10 REG */
> + reg_val = readl(hba->cnic->regview + PCI_EVENT_TRIGGER_REG);
> + return sprintf(buf, "0x%x\n", reg_val);
> +}
> +
> +
> +static ssize_t bnx2i_get_iscsi_cntx_dump(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> + unsigned int *ptr = (unsigned int *) hba->ctx_addr;
> + unsigned int *dst_ptr = (unsigned int *) buf;
> + int unit_sz = sizeof(unsigned int);
> +#define SYSFS_BUF_SIZE 4096
> +#define NUM_SYSFS_BUFS_PER_CTX 4
> +
> + if (hba->ctx_read_cnt == NUM_SYSFS_BUFS_PER_CTX)
> + return 0;
> +
> + ptr += (((hba->ctx_read_cnt % NUM_SYSFS_BUFS_PER_CTX) *
> + SYSFS_BUF_SIZE) / unit_sz);
> + hba->ctx_read_cnt++;
> + memcpy(dst_ptr, ptr, SYSFS_BUF_SIZE);
> +
> + return SYSFS_BUF_SIZE;
> +}
> +
> +static ssize_t bnx2i_select_iscsi_cntx_dump(struct class_device *cdev,
> + const char *buf, size_t count)
> +{
> + u32 iscsi_cid;
> + int ret = 0;
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + if (sscanf(buf, " 0x%x ", &iscsi_cid) > 0) {
> + ret = bnx2i_select_ctx_dump_cid(hba, iscsi_cid);
> + }
> + if (!ret)
> + ret = count;
> + return ret;
> +}
> +
> +static ssize_t bnx2i_get_active_iscsi_cid_list(struct class_device *cdev,
> + char *buf)
> +{
> + u32 active_iscsi_cid[32];
> + u32 active_cid[32];
> + int num_cid = 0;
> + ssize_t total_len = 0;
> + char *cur_ptr = buf;
> + int i = 0;
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> + u32 num_ccell = hba->ctx_ccell_tasks & 0xFFFF;
> + u32 num_tasks_per_conn = hba->ctx_ccell_tasks >> 16;
> +
> + if (!hba->ictx_poll_mode) {
> + num_cid = bnx2i_list_iscsi_cid(hba, active_iscsi_cid,
> + active_cid);
> + }
> + total_len += sprintf(cur_ptr, "0x%x\n", BNX2I_SYSFS_VERSION);
> + cur_ptr = buf + total_len;
> + total_len += sprintf(cur_ptr, "0x%x\n", num_ccell);
> + cur_ptr = buf + total_len;
> + total_len += sprintf(cur_ptr, "0x%x\n", num_tasks_per_conn);
> + if (hba->ictx_poll_mode) {
> + if (hba->ictx_poll_cid) {
> + cur_ptr = buf + total_len;
> + total_len += sprintf(cur_ptr, "0x%x, 0x%x\n",
> + hba->ictx_poll_iscsi_cid,
> + hba->ictx_poll_cid);
> + hba->ictx_poll_cid = hba->ictx_poll_iscsi_cid = 0;
> + }
> + } else {
> + for (i = 0; i < num_cid; i++) {
> + cur_ptr = buf + total_len;
> + total_len += sprintf(cur_ptr, "0x%x, 0x%x\n",
> + active_iscsi_cid[i],
> + active_cid[i]);
> + }
> + }
> + return total_len;
> +}
> +
> +
> +static ssize_t bnx2i_set_iscsi_cid_err_poll_mode(struct class_device *cdev,
> + const char *buf, size_t count)
> +{
> + u32 poll_mode;
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> +
> + if (sscanf(buf, "0x%x", &poll_mode) > 0) {
> + if (poll_mode)
> + hba->ictx_poll_mode = 1;
> + else
> + hba->ictx_poll_mode = 0;
> + }
> + return count;
> +}
> +
> +
> +static ssize_t bnx2i_get_qp_shmem_dump(struct class_device *cdev, char *buf)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> + int resi_len = hba->sq_cq_size -
> + (hba->sq_cq_rdp - hba->sq_cq_dump);
> +
> + if (!hba->sq_cq_dump || !hba->sq_cq_rdp) {
> + return -EINVAL;
> + } else if ((hba->sq_cq_dump + hba->sq_cq_size) ==
> + hba->sq_cq_rdp) {
> + kfree(hba->sq_cq_dump);
> + hba->sq_cq_dump = hba->sq_cq_rdp = NULL;
> + return 0;
> + }
> +
> + if (resi_len > SYSFS_BUF_SIZE) {
> + resi_len = SYSFS_BUF_SIZE;
> + }
> + memcpy(buf, hba->sq_cq_rdp, resi_len);
> + hba->sq_cq_rdp += resi_len;
> +
> + return resi_len;
> +}
> +
> +
> +
> +
> +static void bnx2i_dup_cq_mem(struct bnx2i_hba *hba,
> + struct bnx2i_conn *conn, int count)
> +{
> + struct cqe *cqe_s;
> + struct cqe *cqe_d;
> + int total_cnt = count;
> +
> + if (conn->ep->qp.cq_cons_qe == conn->ep->qp.cq_virt)
> + cqe_s = conn->ep->qp.cq_last_qe;
> + else
> + cqe_s = conn->ep->qp.cq_cons_qe - 1;
> + cqe_d = (struct cqe *)hba->sq_cq_rdp;
> + while (count--) {
> + memcpy(cqe_d, cqe_s, sizeof(struct cqe));
> + if (cqe_s == conn->ep->qp.cq_virt) {
> + cqe_s = conn->ep->qp.cq_last_qe;
> + } else {
> + cqe_s--;
> + }
> + cqe_d++;
> + if ((cqe_d - (struct cqe *)hba->sq_cq_rdp) > total_cnt) {
> + printk(KERN_ALERT "bnx2i - SQ Dump: mem overflow\n");
> + break;
> + }
> + }
> +}
> +
> +
> +static int bnx2i_init_cq_dump(struct bnx2i_hba *hba, u32 iscsi_cid,
> + u32 count)
> +{
> + struct bnx2i_conn *conn = NULL;
> + int cq_size = 0;
> +
> + conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
> +
> + if (!conn) {
> + printk(KERN_ALERT "CQ dump: cid #%x not valid\n",
> + iscsi_cid);
> + return -EPERM;
> + }
> +
> + if (hba->sq_cq_dump)
> + return -EPERM;
> +
> + cq_size = (conn->ep->qp.cq_last_qe - conn->ep->qp.cq_virt) + 1;
> +
> + if (!count || (count > cq_size))
> + count = cq_size;
> +
> + hba->sq_cq_size = count * sizeof(struct cqe);
> +
> + if (!hba->sq_cq_size)
> + return -EINVAL;
> +
> + hba->sq_cq_dump = kmalloc(hba->sq_cq_size, GFP_KERNEL);
> + if (!hba->sq_cq_dump)
> + return -ENOMEM;
> + hba->sq_cq_rdp = hba->sq_cq_dump;
> +
> + bnx2i_dup_cq_mem(hba, conn, count);
> + return 0;
> +}
> +
> +
> +
> +static void bnx2i_dup_sq_mem(struct bnx2i_hba *hba,
> + struct bnx2i_conn *conn, int count)
> +{
> + struct sqe *sqe_s;
> + struct sqe *sqe_d;
> + int total_cnt = count;
> +
> + if (conn->ep->qp.sq_prod_qe == conn->ep->qp.sq_virt)
> + sqe_s = conn->ep->qp.sq_last_qe;
> + else
> + sqe_s = conn->ep->qp.sq_prod_qe - 1;
> + sqe_d = (struct sqe *)hba->sq_cq_rdp;
> + while (count--) {
> + memcpy(sqe_d, sqe_s, sizeof(struct sqe));
> + if (sqe_s == conn->ep->qp.sq_virt) {
> + sqe_s = conn->ep->qp.sq_last_qe;
> + } else {
> + sqe_s--;
> + }
> + sqe_d++;
> + if ((sqe_d - (struct sqe *) hba->sq_cq_rdp) >
> + total_cnt) {
> + printk(KERN_ALERT "bnx2i - SQ Dump: mem overflow\n");
> + break;
> + }
> + }
> +}
> +
> +
> +static int bnx2i_init_sq_dump(struct bnx2i_hba *hba,
> + u32 iscsi_cid, u32 count)
> +{
> + struct bnx2i_conn *conn = NULL;
> + int sq_size = 0;
> +
> + conn = bnx2i_get_conn_from_id(hba, iscsi_cid);
> +
> + if (!conn) {
> + printk(KERN_ALERT "SQ dump: cid #%x not valid\n",
> + iscsi_cid);
> + return -EINVAL;
> + }
> +
> + if (hba->sq_cq_dump)
> + return -EINVAL;
> +
> + sq_size = (conn->ep->qp.sq_last_qe - conn->ep->qp.sq_virt) + 1;
> +
> + if (!count || (count > sq_size))
> + count = sq_size;
> +
> + hba->sq_cq_size = count * sizeof(struct sqe);
> +
> + if (!hba->sq_cq_size)
> + return -EINVAL;
> +
> + hba->sq_cq_dump = kmalloc(hba->sq_cq_size, GFP_KERNEL);
> + if (!hba->sq_cq_dump)
> + return -ENOMEM;
> + hba->sq_cq_rdp = hba->sq_cq_dump;
> +
> + bnx2i_dup_sq_mem(hba, conn, count);
> + return 0;
> +}
> +
> +static ssize_t bnx2i_setup_qp_shmem_dump(struct class_device *cdev,
> + const char *buf, size_t count)
> +{
> + struct bnx2i_hba *hba =
> + container_of(cdev, struct bnx2i_hba, class_dev);
> + u32 iscsi_cid;
> + char queue[32];
> + ssize_t ret = count;
> + u32 num_count;
> +
> +
> + if (sscanf(buf, "%c%c,%d,%d", &queue[0], &queue[1],
> + &iscsi_cid, &num_count) > 0) {
> + if (!strncmp(queue, "SQ", 2)) {
> + ret = bnx2i_init_sq_dump(hba, iscsi_cid, num_count);
> + } else if (!strncmp(queue, "CQ", 2)) {
> + ret = bnx2i_init_cq_dump(hba, iscsi_cid, num_count);
> + } else {
> + ret = -EINVAL;
> + }
> + }
> + return ret;
> +}
> +
> +
> +static ssize_t bnx2i_read_tcp_portd_options(struct class_device *cdev,
> + char *buf)
> +{
> + extern struct tcp_port_mngt bnx2i_tcp_port_tbl;
> + return sprintf(buf, "0x%x\n", bnx2i_tcp_port_tbl.num_required);
> +}
> +
> +static ssize_t bnx2i_write_tcp_portd_results(struct class_device *cdev,
> + const char *buf, size_t count)
> +{
> + extern struct tcp_port_mngt bnx2i_tcp_port_tbl;
> + u32 tcp_port, bind_stat;
> +
> + if (!bnx2i_tcp_port_tbl.free_q)
> + return count;
> +
> + if (sscanf(buf, "%d,%d", &tcp_port, &bind_stat) > 0) {
> + if (bind_stat && tcp_port) {
> + bnx2i_tcp_port_new_entry(tcp_port);
> + }
> + }
> + return count;
> +}
> +
> +
> +static CLASS_DEVICE_ATTR (mips_info, S_IRUGO,
> + bnx2i_show_mips_status, NULL);
> +static CLASS_DEVICE_ATTR (net_if_name, S_IRUGO,
> + bnx2i_show_net_if_name, NULL);
> +static CLASS_DEVICE_ATTR (pci_bar, S_IRUGO,
> + bnx2i_show_pci_bar, NULL);
> +static CLASS_DEVICE_ATTR (sq_size, S_IRUGO | S_IWUSR,
> + bnx2i_show_sq_info, bnx2i_set_sq_info);
> +static CLASS_DEVICE_ATTR (cq_size, S_IRUGO | S_IWUSR,
> + bnx2i_show_cq_info, bnx2i_set_cq_info);
> +static CLASS_DEVICE_ATTR (rq_size, S_IRUGO | S_IWUSR,
> + bnx2i_show_rq_info, bnx2i_set_rq_info);
> +static CLASS_DEVICE_ATTR (num_ccell, S_IRUGO | S_IWUSR,
> + bnx2i_show_ccell_info, bnx2i_set_ccell_info);
> +static CLASS_DEVICE_ATTR (pci_trigger, S_IRUGO,
> + bnx2i_read_pci_trigger_reg, NULL);
> +static CLASS_DEVICE_ATTR (ctx_dump, S_IRUGO | S_IWUSR,
> + bnx2i_get_iscsi_cntx_dump,
> + bnx2i_select_iscsi_cntx_dump);
> +static CLASS_DEVICE_ATTR (cid_list, S_IRUGO | S_IWUSR,
> + bnx2i_get_active_iscsi_cid_list,
> + bnx2i_set_iscsi_cid_err_poll_mode);
> +static CLASS_DEVICE_ATTR (qp_shmem_dump, S_IRUGO | S_IWUSR,
> + bnx2i_get_qp_shmem_dump,
> + bnx2i_setup_qp_shmem_dump);
> +static CLASS_DEVICE_ATTR (port_bind, S_IRUGO | S_IWUSR,
> + bnx2i_read_tcp_portd_options,
> + bnx2i_write_tcp_portd_results);
> +
> +
> +static struct class_device_attribute *bnx2i_class_attributes[] = {
> + &class_device_attr_mips_info,
> + &class_device_attr_net_if_name,
> + &class_device_attr_pci_bar,
> + &class_device_attr_sq_size,
> + &class_device_attr_cq_size,
> + &class_device_attr_rq_size,
> + &class_device_attr_num_ccell,
> + &class_device_attr_pci_trigger,
> + &class_device_attr_ctx_dump,
> + &class_device_attr_cid_list,
> + &class_device_attr_qp_shmem_dump,
> +};
> +
> +static struct class_device_attribute *tcp_port_class_attributes[] = {
> + &class_device_attr_port_bind
> +};
> +
> +static void bnx2i_sysfs_release(struct class_device *class_dev)
> +{
> +}
> +
> +struct class_device port_class_dev;
> +
> +
> +static struct class bnx2i_class = {
> + .name = "bnx2i",
> + .release = bnx2i_sysfs_release,
> +};
> +
> +
> +
> +static int bnx2i_register_port_class_dev(struct class_device *class_dev)
> +{
> + char dev_name[BUS_ID_SIZE];
> + int ret;
> + int i;
> +
> + class_dev->class = &bnx2i_class;
> + class_dev->class_data = class_dev;
> + snprintf(dev_name, BUS_ID_SIZE, "%s", "tcp_portd");
> + strlcpy(class_dev->class_id, dev_name, BUS_ID_SIZE);
> +
> + ret = class_device_register(class_dev);
> + if (ret)
> + goto err;
> +
> + for (i = 0; i < ARRAY_SIZE(tcp_port_class_attributes); ++i) {
> + ret = class_device_create_file(class_dev,
> + tcp_port_class_attributes[i]);
> + if (ret)
> + goto err_unregister;
> + }
> +
> + return 0;
> +
> +err_unregister:
> + class_device_unregister(class_dev);
> +err:
> + return ret;
> +}
> +
> +
> +int bnx2i_register_sysfs(struct bnx2i_hba *hba)
> +{
> + struct class_device *class_dev = &hba->class_dev;
> + char dev_name[BUS_ID_SIZE];
> + int ret;
> + int i;
> +
> + class_dev->class = &bnx2i_class;
> + class_dev->class_data = hba;
> + snprintf(dev_name, BUS_ID_SIZE, "%.2x:%.2x.%.1x",
> + hba->pci_dev->bus->number,
> + PCI_SLOT(hba->pci_dev->devfn),
> + PCI_FUNC(hba->pci_dev->devfn));
> + strlcpy(class_dev->class_id, dev_name, BUS_ID_SIZE);
> +
> + ret = class_device_register(class_dev);
> + if (ret)
> + goto err;
> +
> + for (i = 0; i < ARRAY_SIZE(bnx2i_class_attributes); ++i) {
> + ret = class_device_create_file(class_dev,
> + bnx2i_class_attributes[i]);
> + if (ret)
> + goto err_unregister;
> + }
> +
> + return 0;
> +
> +err_unregister:
> + class_device_unregister(class_dev);
> +err:
> + return ret;
> +}
> +
> +void bnx2i_unregister_sysfs(struct bnx2i_hba *hba)
> +{
> + class_device_unregister(&hba->class_dev);
> +}
> +
> +int bnx2i_sysfs_setup(void)
> +{
> + int ret;
> + ret = class_register(&bnx2i_class);
> +
> + bnx2i_register_port_class_dev(&port_class_dev);
> + return ret;
> +}
> +
> +void bnx2i_sysfs_cleanup(void)
> +{
> + class_device_unregister(&port_class_dev);
> + class_unregister(&bnx2i_class);
> +}
>
>
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: strange tcp behavior
From: David Miller @ 2007-08-05 3:21 UTC (permalink / raw)
To: johnpol; +Cc: simon, john, netdev
In-Reply-To: <20070804165151.GE14175@2ka.mipt.ru>
From: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
Date: Sat, 4 Aug 2007 20:51:51 +0400
> On Fri, Aug 03, 2007 at 02:17:17PM -0700, David Miller (davem@davemloft.net) wrote:
> > From: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
> > Date: Fri, 3 Aug 2007 12:22:42 +0400
> >
> > > Maybe recvmsg should be changed too for symmetry?
> >
> > I took a look at this, and it's not %100 trivial.
> >
> > Let's do this later, and only sendmsg for now in order to
> > fix the bug in the stable branches.
>
> I've tested your patch, besides there was an offset in one of hooks,
> it works perfectly ok.
>
> Feel free to add my ack, tested-by or whatever is needed for this :)
> Your patch fixes the problem.
It is already merged to Linus's tree long before you found a chance to
test it :-) So it would be difficult for me to do so.
^ permalink raw reply
* Re: [patch 2.6.23-rc1] add xt_statistic.h to the header list for usermode programs
From: David Miller @ 2007-08-05 4:18 UTC (permalink / raw)
To: cebbert; +Cc: netdev
In-Reply-To: <46B365A9.8080507@redhat.com>
From: Chuck Ebbert <cebbert@redhat.com>
Date: Fri, 03 Aug 2007 13:28:09 -0400
> Add xt_statistic.h to the list of headers to install.
>
> Apparently needed to build newer versions of iptables.
>
> Signed-off-by: Chuck Ebbert <cebbert@redhat.com>
Applied, thanks.
^ permalink raw reply
* Re: Distributed storage.
From: Daniel Phillips @ 2007-08-05 8:04 UTC (permalink / raw)
To: Evgeniy Polyakov; +Cc: netdev, linux-kernel, linux-fsdevel, Peter Zijlstra
In-Reply-To: <20070804163740.GB14175@2ka.mipt.ru>
On Saturday 04 August 2007 09:37, Evgeniy Polyakov wrote:
> On Fri, Aug 03, 2007 at 06:19:16PM -0700, I wrote:
> > To be sure, I am not very proud of this throttling mechanism for
> > various reasons, but the thing is, _any_ throttling mechanism no
> > matter how sucky solves the deadlock problem. Over time I want to
> > move the
>
> make_request_fn is always called in process context,
Yes, as is submit_bio which calls it. The decision re where it is best
to throttle, in submit_bio or in make_request_fn, has more to do with
system factoring, that is, is throttling something that _every_ block
device should have (yes I think) or is it a delicate, optional thing
that needs a tweakable algorithm per block device type (no I think).
The big worry I had was that by blocking on congestion in the
submit_bio/make_request_fn I might stuff up system-wide mm writeout.
But a while ago that part of the mm was tweaked (by Andrew if I recall
correctly) to use a pool of writeout threads and understand the concept
of one of them blocking on some block device, and not submit more
writeout to the same block device until the first thread finishes its
submission. Meanwhile, other mm writeout threads carry on with other
block devices.
> we can wait in it for memory in mempool. Although that means we
> already in trouble.
Not at all. This whole block writeout path needs to be written to run
efficiently even when normal system memory is completely gone. All it
means when we wait on a mempool is that the block device queue is as
full as we are ever going to let it become, and that means the block
device is working as hard as it can (subject to a small caveat: for
some loads a device can work more efficiently if it can queue up larger
numbers of requests down at the physical elevators).
By the way, ddsnap waits on a counting semaphore, not a mempool. That
is because we draw our reserve memory from the global memalloc reserve,
not from a mempool. And that is not only because it takes less code to
do so, but mainly because global pools as opposed to lots of little
special purpose pools seem like a good idea to me. Though I will admit
that with our current scheme we need to allow for the total of the
maximum reserve requirements for all memalloc users in the memalloc
pool, so it does not actually save any memory vs dedicated pools. We
could improve that if we wanted to, by having hard and soft reserve
requirements: the global reserve actually only needs to be as big as
the total of the hard requirements. With this idea, if by some unlucky
accident every single pool user got itself maxed out at the same time,
we would still not exceed our share of the global reserve.
Under "normal" low memory situations, a block device would typically be
free to grab reserve memory up to its soft limit, allowing it to
optimize over a wider range of queued transactions. My little idea
here is: allocating specific pages to a pool is kind of dumb, all we
really want to do is account precisely for the number of pages we are
allowed to draw from the global reserve.
OK, I kind of digressed, but this all counts as explaining the details
of what Peter and I have been up to for the last year (longer for me).
At this point, we don't need to do the reserve accounting in the most
absolutely perfect way possible, we just need to get something minimal
in place to fix the current deadlock problems, then we can iteratively
improve it.
> I agree, any kind of high-boundary leveling must be implemented in
> device itself, since block layer does not know what device is at the
> end and what it will need to process given block request.
I did not say the throttling has to be implemented in the device, only
that we did it there because it was easiest to code that up and try it
out (it worked). This throttling really wants to live at a higher
level, possibly submit_bio()...bio->endio(). Someone at OLS (James
Bottomley?) suggested it would be better done at the request queue
layer, but I do not immediately see why that should be. I guess this
is going to come down to somebody throwing out a patch for interested
folks to poke at. But this detail is a fine point. The big point is
to have _some_ throttling mechanism in place on the block IO path,
always.
Device mapper in particular does not have any throttling itself: calling
submit_bio on a device mapper device directly calls the device mapper
bio dispatcher. Default initialized block device queue do provide a
crude form of throttling based on limiting the number of requests.
This is insufficiently precise to do a good job in the long run, but it
works for now because the current gaggle of low level block drivers do
not have a lot of resource requirements and tend to behave fairly
predictably (except for some irritating issues re very slow devices
working in parallel with very fast devices, but... worry about that
later). Network block drivers - for example your driver - do have
nontrivial resource requirements and they do not, as far as I can see,
have any form of throttling on the upstream side. So unless you can
demonstrate I'm wrong (I would be more than happy about that) then we
are going to need to add some.
Anyway, I digressed again. _Every_ layer in a block IO stack needs to
have a reserve, if it consumes memory. So we can't escape the question
of how big to make those reserves by trying to push it all down to the
lowest level, hoping that the low level device knows more about how
many requests it will have in flight. For the time being, we will just
plug in some seat of the pants numbers in classic Linux fashion and
that will serve us until somebody gets around to inventing the one true
path discovery mechanism that can sniff around in the block IO stack
and figure out the optimal amount of system resources to reserve at
each level, which ought to be worth at least a master's thesis for
somebody.
Regards,
Daniel
^ permalink raw reply
* Re: Distributed storage.
From: Daniel Phillips @ 2007-08-05 8:06 UTC (permalink / raw)
To: Evgeniy Polyakov; +Cc: netdev, linux-kernel, linux-fsdevel
In-Reply-To: <20070804164438.GC14175@2ka.mipt.ru>
On Saturday 04 August 2007 09:44, Evgeniy Polyakov wrote:
> > On Tuesday 31 July 2007 10:13, Evgeniy Polyakov wrote:
> > > * storage can be formed on top of remote nodes and be
> > > exported simultaneously (iSCSI is peer-to-peer only, NBD requires
> > > device mapper and is synchronous)
> >
> > In fact, NBD has nothing to do with device mapper. I use it as a
> > physical target underneath ddraid (a device mapper plugin) just
> > like I would use your DST if it proves out.
>
> I meant to create a storage on top of several nodes one needs to have
> device mapper or something like that on top of NBD itself. To further
> export resulted device one needs another userspace NDB application
> and so on. DST simplifies that greatly.
>
> DST original code worked as device mapper plugin too, but its two
> additional allocations (io and clone) per block request ended up for
> me as a show stopper.
Ah, sorry, I misread. A show stopper in terms of efficiency, or in
terms of deadlock?
Regards,
Daniel
^ permalink raw reply
* e1000 doesn't resume properly from standby (2.6.23-rc2)
From: Simon Arlott @ 2007-08-05 8:52 UTC (permalink / raw)
To: linux-pm; +Cc: Linux Kernel Mailing List, netdev
00:0a.0 Ethernet controller: Intel Corp. 82546EB Gigabit Ethernet Controller (Copper) (rev 01)
Subsystem: Intel Corp.: Unknown device 1012
Flags: bus master, 66Mhz, medium devsel, latency 32, IRQ 5
Memory at e3020000 (64-bit, non-prefetchable) [size=128K]
I/O ports at b000 [size=64]
Capabilities: [dc] Power Management version 2
Capabilities: [e4] PCI-X non-bridge device.
Capabilities: [f0] Message Signalled Interrupts: 64bit+ Queue=0/0 Enable-
00:0a.1 Ethernet controller: Intel Corp. 82546EB Gigabit Ethernet Controller (Copper) (rev 01)
Subsystem: Intel Corp.: Unknown device 1012
Flags: bus master, 66Mhz, medium devsel, latency 32, IRQ 12
Memory at e3000000 (64-bit, non-prefetchable) [size=128K]
I/O ports at b400 [size=64]
Capabilities: [dc] Power Management version 2
Capabilities: [e4] PCI-X non-bridge device.
Capabilities: [f0] Message Signalled Interrupts: 64bit+ Queue=0/0 Enable-
[ 950.132046] Stopping tasks ... done.
[ 950.459794] Suspending console(s)
[ 951.776277] pnp: Device 00:0c disabled.
[ 951.776673] pnp: Device 00:0a disabled.
[ 951.776984] pnp: Device 00:09 disabled.
[ 951.777306] pnp: Device 00:08 disabled.
[ 951.777786] ACPI: PCI interrupt for device 0000:00:11.5 disabled
[ 951.995359] ACPI: PCI interrupt for device 0000:00:11.3 disabled
[ 952.006094] ACPI: PCI interrupt for device 0000:00:11.2 disabled
[ 952.022243] ACPI handle has no context!
[ 952.033068] ACPI: PCI interrupt for device 0000:00:0c.2 disabled
[ 952.044086] ACPI: PCI interrupt for device 0000:00:0c.1 disabled
[ 952.055083] ACPI: PCI interrupt for device 0000:00:0c.0 disabled
[ 952.282211] ACPI: PCI interrupt for device 0000:00:0a.1 disabled
[ 952.282221] ACPI handle has no context!
[ 952.537474] ACPI: PCI interrupt for device 0000:00:0a.0 disabled
[ 952.537495] ACPI handle has no context!
[ 956.857085] Back to C!
[ 957.295035] ACPI: Unable to turn cooling device [b18d0e00] 'off'
[ 957.521400] PCI: Setting latency timer of device 0000:00:01.0 to 64
[ 957.521478] ACPI: PCI Interrupt 0000:00:09.0[A] -> Link [LNKB] -> GSI 11 (level, low) -> IRQ 11
[ 957.532256] PM: Writing back config space on device 0000:00:0a.0 at offset f (was ff0100, writing ff0105)
[ 957.532277] PM: Writing back config space on device 0000:00:0a.0 at offset 8 (was 1, writing b001)
[ 957.532291] PM: Writing back config space on device 0000:00:0a.0 at offset 4 (was 4, writing e3020004)
[ 957.532299] PM: Writing back config space on device 0000:00:0a.0 at offset 3 (was 800000, writing 802008)
[ 957.532309] PM: Writing back config space on device 0000:00:0a.0 at offset 1 (was 2300000, writing 2300007)
[ 957.532339] ACPI: PCI Interrupt 0000:00:0a.0[A] -> Link [LNKC] -> GSI 5 (level, low) -> IRQ 5
[ 957.567251] PM: Writing back config space on device 0000:00:0a.1 at offset f (was ff0200, writing ff020c)
[ 957.567275] PM: Writing back config space on device 0000:00:0a.1 at offset 8 (was 1, writing b401)
[ 957.567290] PM: Writing back config space on device 0000:00:0a.1 at offset 4 (was 4, writing e3000004)
[ 957.567298] PM: Writing back config space on device 0000:00:0a.1 at offset 3 (was 800000, writing 802008)
[ 957.567308] PM: Writing back config space on device 0000:00:0a.1 at offset 1 (was 2300000, writing 2300007)
[ 957.567346] ACPI: PCI Interrupt 0000:00:0a.1[B] -> Link [LNKD] -> GSI 12 (level, low) -> IRQ 12
[ 957.589975] ACPI: PCI Interrupt 0000:00:0b.0[A] -> Link [LNKD] -> GSI 12 (level, low) -> IRQ 12
[ 957.600217] ACPI: PCI Interrupt 0000:00:0c.0[A] -> Link [LNKA] -> GSI 11 (level, low) -> IRQ 11
[ 957.611230] ACPI: PCI Interrupt 0000:00:0c.1[B] -> Link [LNKB] -> GSI 11 (level, low) -> IRQ 11
[ 957.838282] ACPI: PCI Interrupt 0000:00:0c.2[C] -> Link [LNKC] -> GSI 5 (level, low) -> IRQ 5
[ 957.902166] ohci1394: fw-host0: OHCI-1394 1.0 (PCI): IRQ=[11] MMIO=[e3046000-e30467ff] Max Packet=[512] IR/IT contexts=[4/8]
[ 957.911656] ACPI: PCI Interrupt 0000:00:11.1[A] -> Link [LNKA] -> GSI 11 (level, low) -> IRQ 11
[ 957.911666] PCI: VIA VLink IRQ fixup for 0000:00:11.1, from 255 to 11
[ 957.922034] ACPI: PCI Interrupt 0000:00:11.2[D] -> Link [LNKD] -> GSI 12 (level, low) -> IRQ 12
[ 957.933028] ACPI: PCI Interrupt 0000:00:11.3[D] -> Link [LNKD] -> GSI 12 (level, low) -> IRQ 12
[ 957.944076] ACPI: PCI Interrupt 0000:00:11.5[C] -> Link [LNKC] -> GSI 5 (level, low) -> IRQ 5
[ 957.944091] PCI: Setting latency timer of device 0000:00:11.5 to 64
[ 957.946061] ACPI: PCI Interrupt 0000:01:00.0[A] -> Link [LNKA] -> GSI 11 (level, low) -> IRQ 11
[ 957.947464] pnp: Device 00:08 activated.
[ 957.948724] pnp: Device 00:09 activated.
[ 957.950635] pnp: Device 00:0a activated.
[ 957.950664] pnp: Failed to activate device 00:0b.
[ 957.951942] pnp: Device 00:0c activated.
[ 959.883939] e1000: eth0: e1000_watchdog: NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX/TX
[ 961.866287] hde: selected mode 0x45
[ 965.310493] hdg: selected mode 0x45
[ 965.316152] hdh: selected mode 0x42
[ 969.135523] hda: selected mode 0x46
[ 973.454631] hdb: selected mode 0x45
[ 973.457179] hdc: selected mode 0x44
[ 973.459550] hdd: selected mode 0x42
[ 973.950474] Restarting tasks ... done.
[ 974.261357] agpgart: Found an AGP 2.0 compliant device at 0000:00:00.0.
[ 974.262099] agpgart: Putting AGP V2 device at 0000:00:00.0 into 4x mode
[ 974.262620] agpgart: Putting AGP V2 device at 0000:01:00.0 into 4x mode
[ 974.262894] [drm] Loading R200 Microcode
eth0 now doesn't receive anything - it's transmitting ok because
I can receive its packets on another host. It's also still getting
interrupts.
If I then ifconfig eth0 down and up, or change the MTU (since
that resets the link on e1000), it starts working again:
[ 993.926603] e1000: eth0: e1000_watchdog: NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX/TX
There's a link up at 959.883939 too...
--
Simon Arlott
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox