diff options
author | Ramkrishna Vepa <ram.vepa@neterion.com> | 2009-04-01 18:15:13 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-04-02 00:33:44 -0700 |
commit | 703da5a1a231d8e3da8c7f88a505a4024941193b (patch) | |
tree | 6bf815de99cdb9bbfacad1a8e8ea8d43acc4fa4f /drivers/net/vxge | |
parent | 113241321dcd19f36d53f2af46a4734855ca0cc0 (diff) |
Neterion: New driver: Main entry points
This patch implements all the driver entry point functions.
- Definition of all module loadable paramters.
- Implementation of all driver entry point functions.
- Changes in this submission -
- Fixed compilation error when enabling debug statements
- Fixed few warnings when CONFIG_PCI_MSI is not defined
- Removed unnecessary volatile variables
- Removed compare_ether_addr as it causes unaligned memory access on
the sparc64 platform.
- Changes in previous submissions -
- As per Stephen Hemminger's comments removed the following loadable
parameters - gro, rx_& tx max_indicate_pkts, exec_mode, rx & tx
pause_enable, tx_steering_type and intr_type.
- Added Device ID definition in vxge-main.h instead of
include/linux/pci_ids.h file - Reported by David Miller
- Incorporated following review comments from Ben Hutchings
- NAPI is always enabled (no option to turn it OFF).
- Loadable parameters
rx_steering_type: This loadable option is removed.
ring_blocks: This loadable option is removed.
The driver default settings work well in most if not all cases.
Another patch to configure these parameters with ethtool will be
released in the future.
- LRO has been deprecated in favour of GRO - Bill Fink & Dave Miller's comment
- Fixed sparse warnings - Reported by Andi Kleen
- Removed unused variables
Signed-off-by: Sivakumar Subramani <sivakumar.subramani@neterion.com>
Signed-off-by: Rastapur Santosh <santosh.rastapur@neterion.com>
Signed-off-by: Ramkrishna Vepa <ram.vepa@neterion.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/vxge')
-rw-r--r-- | drivers/net/vxge/vxge-main.c | 4502 | ||||
-rw-r--r-- | drivers/net/vxge/vxge-main.h | 557 |
2 files changed, 5059 insertions, 0 deletions
diff --git a/drivers/net/vxge/vxge-main.c b/drivers/net/vxge/vxge-main.c new file mode 100644 index 00000000000..61ef1611815 --- /dev/null +++ b/drivers/net/vxge/vxge-main.c @@ -0,0 +1,4502 @@ +/****************************************************************************** +* This software may be used and distributed according to the terms of +* the GNU General Public License (GPL), incorporated herein by reference. +* Drivers based on or derived from this code fall under the GPL and must +* retain the authorship, copyright and license notice. This file is not +* a complete program and may only be used when the entire operating +* system is licensed under the GPL. +* See the file COPYING in this distribution for more information. +* +* vxge-main.c: Driver for Neterion Inc's X3100 Series 10GbE PCIe I/O +* Virtualized Server Adapter. +* Copyright(c) 2002-2009 Neterion Inc. +* +* The module loadable parameters that are supported by the driver and a brief +* explanation of all the variables: +* vlan_tag_strip: +* Strip VLAN Tag enable/disable. Instructs the device to remove +* the VLAN tag from all received tagged frames that are not +* replicated at the internal L2 switch. +* 0 - Do not strip the VLAN tag. +* 1 - Strip the VLAN tag. +* +* addr_learn_en: +* Enable learning the mac address of the guest OS interface in +* a virtualization environment. +* 0 - DISABLE +* 1 - ENABLE +* +* max_config_port: +* Maximum number of port to be supported. +* MIN -1 and MAX - 2 +* +* max_config_vpath: +* This configures the maximum no of VPATH configures for each +* device function. +* MIN - 1 and MAX - 17 +* +* max_config_dev: +* This configures maximum no of Device function to be enabled. +* MIN - 1 and MAX - 17 +* +******************************************************************************/ + +#include <linux/if_vlan.h> +#include <linux/pci.h> +#include <net/ip.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include "vxge-main.h" +#include "vxge-reg.h" + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("Neterion's X3100 Series 10GbE PCIe I/O" + "Virtualized Server Adapter"); + +static struct pci_device_id vxge_id_table[] __devinitdata = { + {PCI_VENDOR_ID_S2IO, PCI_DEVICE_ID_TITAN_WIN, PCI_ANY_ID, + PCI_ANY_ID}, + {PCI_VENDOR_ID_S2IO, PCI_DEVICE_ID_TITAN_UNI, PCI_ANY_ID, + PCI_ANY_ID}, + {0} +}; + +MODULE_DEVICE_TABLE(pci, vxge_id_table); + +VXGE_MODULE_PARAM_INT(vlan_tag_strip, VXGE_HW_VPATH_RPA_STRIP_VLAN_TAG_ENABLE); +VXGE_MODULE_PARAM_INT(addr_learn_en, VXGE_HW_MAC_ADDR_LEARN_DEFAULT); +VXGE_MODULE_PARAM_INT(max_config_port, VXGE_MAX_CONFIG_PORT); +VXGE_MODULE_PARAM_INT(max_config_vpath, VXGE_USE_DEFAULT); +VXGE_MODULE_PARAM_INT(max_mac_vpath, VXGE_MAX_MAC_ADDR_COUNT); +VXGE_MODULE_PARAM_INT(max_config_dev, VXGE_MAX_CONFIG_DEV); + +static u16 vpath_selector[VXGE_HW_MAX_VIRTUAL_PATHS] = + {0, 1, 3, 3, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 15, 31}; +static unsigned int bw_percentage[VXGE_HW_MAX_VIRTUAL_PATHS] = + {[0 ...(VXGE_HW_MAX_VIRTUAL_PATHS - 1)] = 0xFF}; +module_param_array(bw_percentage, uint, NULL, 0); + +static struct vxge_drv_config *driver_config; + +static inline int is_vxge_card_up(struct vxgedev *vdev) +{ + return test_bit(__VXGE_STATE_CARD_UP, &vdev->state); +} + +static inline void VXGE_COMPLETE_VPATH_TX(struct vxge_fifo *fifo) +{ + unsigned long flags = 0; + struct sk_buff *skb_ptr = NULL; + struct sk_buff **temp, *head, *skb; + + if (spin_trylock_irqsave(&fifo->tx_lock, flags)) { + vxge_hw_vpath_poll_tx(fifo->handle, (void **)&skb_ptr); + spin_unlock_irqrestore(&fifo->tx_lock, flags); + } + /* free SKBs */ + head = skb_ptr; + while (head) { + skb = head; + temp = (struct sk_buff **)&skb->cb; + head = *temp; + *temp = NULL; + dev_kfree_skb_irq(skb); + } +} + +static inline void VXGE_COMPLETE_ALL_TX(struct vxgedev *vdev) +{ + int i; + + /* Complete all transmits */ + for (i = 0; i < vdev->no_of_vpath; i++) + VXGE_COMPLETE_VPATH_TX(&vdev->vpaths[i].fifo); +} + +static inline void VXGE_COMPLETE_ALL_RX(struct vxgedev *vdev) +{ + int i; + struct vxge_ring *ring; + + /* Complete all receives*/ + for (i = 0; i < vdev->no_of_vpath; i++) { + ring = &vdev->vpaths[i].ring; + vxge_hw_vpath_poll_rx(ring->handle); + } +} + +/* + * MultiQ manipulation helper functions + */ +void vxge_stop_all_tx_queue(struct vxgedev *vdev) +{ + int i; + struct net_device *dev = vdev->ndev; + + if (vdev->config.tx_steering_type != TX_MULTIQ_STEERING) { + for (i = 0; i < vdev->no_of_vpath; i++) + vdev->vpaths[i].fifo.queue_state = VPATH_QUEUE_STOP; + } + netif_tx_stop_all_queues(dev); +} + +void vxge_stop_tx_queue(struct vxge_fifo *fifo) +{ + struct net_device *dev = fifo->ndev; + + struct netdev_queue *txq = NULL; + if (fifo->tx_steering_type == TX_MULTIQ_STEERING) + txq = netdev_get_tx_queue(dev, fifo->driver_id); + else { + txq = netdev_get_tx_queue(dev, 0); + fifo->queue_state = VPATH_QUEUE_STOP; + } + + netif_tx_stop_queue(txq); +} + +void vxge_start_all_tx_queue(struct vxgedev *vdev) +{ + int i; + struct net_device *dev = vdev->ndev; + + if (vdev->config.tx_steering_type != TX_MULTIQ_STEERING) { + for (i = 0; i < vdev->no_of_vpath; i++) + vdev->vpaths[i].fifo.queue_state = VPATH_QUEUE_START; + } + netif_tx_start_all_queues(dev); +} + +static void vxge_wake_all_tx_queue(struct vxgedev *vdev) +{ + int i; + struct net_device *dev = vdev->ndev; + + if (vdev->config.tx_steering_type != TX_MULTIQ_STEERING) { + for (i = 0; i < vdev->no_of_vpath; i++) + vdev->vpaths[i].fifo.queue_state = VPATH_QUEUE_START; + } + netif_tx_wake_all_queues(dev); +} + +void vxge_wake_tx_queue(struct vxge_fifo *fifo, struct sk_buff *skb) +{ + struct net_device *dev = fifo->ndev; + + int vpath_no = fifo->driver_id; + struct netdev_queue *txq = NULL; + if (fifo->tx_steering_type == TX_MULTIQ_STEERING) { + txq = netdev_get_tx_queue(dev, vpath_no); + if (netif_tx_queue_stopped(txq)) + netif_tx_wake_queue(txq); + } else { + txq = netdev_get_tx_queue(dev, 0); + if (fifo->queue_state == VPATH_QUEUE_STOP) + if (netif_tx_queue_stopped(txq)) { + fifo->queue_state = VPATH_QUEUE_START; + netif_tx_wake_queue(txq); + } + } +} + +/* + * vxge_callback_link_up + * + * This function is called during interrupt context to notify link up state + * change. + */ +void +vxge_callback_link_up(struct __vxge_hw_device *hldev) +{ + struct net_device *dev = hldev->ndev; + struct vxgedev *vdev = (struct vxgedev *)netdev_priv(dev); + + vxge_debug_entryexit(VXGE_TRACE, "%s: %s:%d", + vdev->ndev->name, __func__, __LINE__); + printk(KERN_NOTICE "%s: Link Up\n", vdev->ndev->name); + vdev->stats.link_up++; + + netif_carrier_on(vdev->ndev); + vxge_wake_all_tx_queue(vdev); + + vxge_debug_entryexit(VXGE_TRACE, + "%s: %s:%d Exiting...", vdev->ndev->name, __func__, __LINE__); +} + +/* + * vxge_callback_link_down + * + * This function is called during interrupt context to notify link down state + * change. + */ +void +vxge_callback_link_down(struct __vxge_hw_device *hldev) +{ + struct net_device *dev = hldev->ndev; + struct vxgedev *vdev = (struct vxgedev *)netdev_priv(dev); + + vxge_debug_entryexit(VXGE_TRACE, + "%s: %s:%d", vdev->ndev->name, __func__, __LINE__); + printk(KERN_NOTICE "%s: Link Down\n", vdev->ndev->name); + + vdev->stats.link_down++; + netif_carrier_off(vdev->ndev); + vxge_stop_all_tx_queue(vdev); + + vxge_debug_entryexit(VXGE_TRACE, + "%s: %s:%d Exiting...", vdev->ndev->name, __func__, __LINE__); +} + +/* + * vxge_rx_alloc + * + * Allocate SKB. + */ +static struct sk_buff* +vxge_rx_alloc(void *dtrh, struct vxge_ring *ring, const int skb_size) +{ + struct net_device *dev; + struct sk_buff *skb; + struct vxge_rx_priv *rx_priv; + + dev = ring->ndev; + vxge_debug_entryexit(VXGE_TRACE, "%s: %s:%d", + ring->ndev->name, __func__, __LINE__); + + rx_priv = vxge_hw_ring_rxd_private_get(dtrh); + + /* try to allocate skb first. this one may fail */ + skb = netdev_alloc_skb(dev, skb_size + + VXGE_HW_HEADER_ETHERNET_II_802_3_ALIGN); + if (skb == NULL) { + vxge_debug_mem(VXGE_ERR, + "%s: out of memory to allocate SKB", dev->name); + ring->stats.skb_alloc_fail++; + return NULL; + } + + vxge_debug_mem(VXGE_TRACE, + "%s: %s:%d Skb : 0x%p", ring->ndev->name, + __func__, __LINE__, skb); + + skb_reserve(skb, VXGE_HW_HEADER_ETHERNET_II_802_3_ALIGN); + + rx_priv->skb = skb; + rx_priv->data_size = skb_size; + vxge_debug_entryexit(VXGE_TRACE, + "%s: %s:%d Exiting...", ring->ndev->name, __func__, __LINE__); + + return skb; +} + +/* + * vxge_rx_map + */ +static int vxge_rx_map(void *dtrh, struct vxge_ring *ring) +{ + struct vxge_rx_priv *rx_priv; + dma_addr_t dma_addr; + + vxge_debug_entryexit(VXGE_TRACE, "%s: %s:%d", + ring->ndev->name, __func__, __LINE__); + rx_priv = vxge_hw_ring_rxd_private_get(dtrh); + + dma_addr = pci_map_single(ring->pdev, rx_priv->skb->data, + rx_priv->data_size, PCI_DMA_FROMDEVICE); + + if (dma_addr == 0) { + ring->stats.pci_map_fail++; + return -EIO; + } + vxge_debug_mem(VXGE_TRACE, + "%s: %s:%d 1 buffer mode dma_addr = 0x%llx", + ring->ndev->name, __func__, __LINE__, + (unsigned long long)dma_addr); + vxge_hw_ring_rxd_1b_set(dtrh, dma_addr, rx_priv->data_size); + + rx_priv->data_dma = dma_addr; + vxge_debug_entryexit(VXGE_TRACE, + "%s: %s:%d Exiting...", ring->ndev->name, __func__, __LINE__); + + return 0; +} + +/* + * vxge_rx_initial_replenish + * Allocation of RxD as an initial replenish procedure. + */ +static enum vxge_hw_status +vxge_rx_initial_replenish(void *dtrh, void *userdata) +{ + struct vxge_ring *ring = (struct vxge_ring *)userdata; + struct vxge_rx_priv *rx_priv; + + vxge_debug_entryexit(VXGE_TRACE, "%s: %s:%d", + ring->ndev->name, __func__, __LINE__); + if (vxge_rx_alloc(dtrh, ring, + VXGE_LL_MAX_FRAME_SIZE(ring->ndev)) == NULL) + return VXGE_HW_FAIL; + + if (vxge_rx_map(dtrh, ring)) { + rx_priv = vxge_hw_ring_rxd_private_get(dtrh); + dev_kfree_skb(rx_priv->skb); + + return VXGE_HW_FAIL; + } + vxge_debug_entryexit(VXGE_TRACE, + "%s: %s:%d Exiting...", ring->ndev->name, __func__, __LINE__); + + return VXGE_HW_OK; +} + +static inline void +vxge_rx_complete(struct vxge_ring *ring, struct sk_buff *skb, u16 vlan, + int pkt_length, struct vxge_hw_ring_rxd_info *ext_info) +{ + + vxge_debug_entryexit(VXGE_TRACE, "%s: %s:%d", + ring->ndev->name, __func__, __LINE__); + skb_record_rx_queue(skb, ring->driver_id); + skb->protocol = eth_type_trans(skb, ring->ndev); + + ring->stats.rx_frms++; + ring->stats.rx_bytes += pkt_length; + + if (skb->pkt_type == PACKET_MULTICAST) + ring->stats.rx_mcast++; + + vxge_debug_rx(VXGE_TRACE, + "%s: %s:%d skb protocol = %d", + ring->ndev->name, __func__, __LINE__, skb->protocol); + + if (ring->gro_enable) { + if (ring->vlgrp && ext_info->vlan && + (ring->vlan_tag_strip == + VXGE_HW_VPATH_RPA_STRIP_VLAN_TAG_ENABLE)) + vlan_gro_receive(&ring->napi, ring->vlgrp, + ext_info->vlan, skb); + else + napi_gro_receive(&ring->napi, skb); + } else { + if (ring->vlgrp && vlan && + (ring->vlan_tag_strip == + VXGE_HW_VPATH_RPA_STRIP_VLAN_TAG_ENABLE)) + vlan_hwaccel_receive_skb(skb, ring->vlgrp, vlan); + else + netif_receive_skb(skb); + } + vxge_debug_entryexit(VXGE_TRACE, + "%s: %s:%d Exiting...", ring->ndev->name, __func__, __LINE__); +} + +static inline void vxge_re_pre_post(void *dtr, struct vxge_ring *ring, + struct vxge_rx_priv *rx_priv) +{ + pci_dma_sync_single_for_device(ring->pdev, + rx_priv->data_dma, rx_priv->data_size, PCI_DMA_FROMDEVICE); + + vxge_hw_ring_rxd_1b_set(dtr, rx_priv->data_dma, rx_priv->data_size); + vxge_hw_ring_rxd_pre_post(ring->handle, dtr); +} + +static inline void vxge_post(int *dtr_cnt, void **first_dtr, + void *post_dtr, struct __vxge_hw_ring *ringh) +{ + int dtr_count = *dtr_cnt; + if ((*dtr_cnt % VXGE_HW_RXSYNC_FREQ_CNT) == 0) { + if (*first_dtr) + vxge_hw_ring_rxd_post_post_wmb(ringh, *first_dtr); + *first_dtr = post_dtr; + } else + vxge_hw_ring_rxd_post_post(ringh, post_dtr); + dtr_count++; + *dtr_cnt = dtr_count; +} + +/* + * vxge_rx_1b_compl + * + * If the interrupt is because of a received frame or if the receive ring + * contains fresh as yet un-processed frames, this function is called. + */ +enum vxge_hw_status +vxge_rx_1b_compl(struct __vxge_hw_ring *ringh, void *dtr, + u8 t_code, void *userdata) +{ + struct vxge_ring *ring = (struct vxge_ring *)userdata; + struct net_device *dev = ring->ndev; + unsigned int dma_sizes; + void *first_dtr = NULL; + int dtr_cnt = 0; + int data_size; + dma_addr_t data_dma; + int pkt_length; + struct sk_buff *skb; + struct vxge_rx_priv *rx_priv; + struct vxge_hw_ring_rxd_info ext_info; + vxge_debug_entryexit(VXGE_TRACE, "%s: %s:%d", + ring->ndev->name, __func__, __LINE__); + ring->pkts_processed = 0; + + vxge_hw_ring_replenish(ringh, 0); + + do { + rx_priv = vxge_hw_ring_rxd_private_get(dtr); + skb = rx_priv->skb; + data_size = rx_priv->data_size; + data_dma = rx_priv->data_dma; + + vxge_debug_rx(VXGE_TRACE, + "%s: %s:%d skb = 0x%p", + ring->ndev->name, __func__, __LINE__, skb); + + vxge_hw_ring_rxd_1b_get(ringh, dtr, &dma_sizes); + pkt_length = dma_sizes; + + vxge_debug_rx(VXGE_TRACE, + "%s: %s:%d Packet Length = %d", + ring->ndev->name, __func__, __LINE__, pkt_length); + + vxge_hw_ring_rxd_1b_info_get(ringh, dtr, &ext_info); + + /* check skb validity */ + vxge_assert(skb); + + prefetch((char *)skb + L1_CACHE_BYTES); + if (unlikely(t_code)) { + + if (vxge_hw_ring_handle_tcode(ringh, dtr, t_code) != + VXGE_HW_OK) { + + ring->stats.rx_errors++; + vxge_debug_rx(VXGE_TRACE, + "%s: %s :%d Rx T_code is %d", + ring->ndev->name, __func__, + __LINE__, t_code); + + /* If the t_code is not supported and if the + * t_code is other than 0x5 (unparseable packet + * such as unknown UPV6 header), Drop it !!! + */ + vxge_re_pre_post(dtr, ring, rx_priv); + + vxge_post(&dtr_cnt, &first_dtr, dtr, ringh); + ring->stats.rx_dropped++; + continue; + } + } + + if (pkt_length > VXGE_LL_RX_COPY_THRESHOLD) { + + if (vxge_rx_alloc(dtr, ring, data_size) != NULL) { + + if (!vxge_rx_map(dtr, ring)) { + skb_put(skb, pkt_length); + + pci_unmap_single(ring->pdev, data_dma, + data_size, PCI_DMA_FROMDEVICE); + + vxge_hw_ring_rxd_pre_post(ringh, dtr); + vxge_post(&dtr_cnt, &first_dtr, dtr, + ringh); + } else { + dev_kfree_skb(rx_priv->skb); + rx_priv->skb = skb; + rx_priv->data_size = data_size; + vxge_re_pre_post(dtr, ring, rx_priv); + + vxge_post(&dtr_cnt, &first_dtr, dtr, + ringh); + ring->stats.rx_dropped++; + break; + } + } else { + vxge_re_pre_post(dtr, ring, rx_priv); + + vxge_post(&dtr_cnt, &first_dtr, dtr, ringh); + ring->stats.rx_dropped++; + break; + } + } else { + struct sk_buff *skb_up; + + skb_up = netdev_alloc_skb(dev, pkt_length + + VXGE_HW_HEADER_ETHERNET_II_802_3_ALIGN); + if (skb_up != NULL) { + skb_reserve(skb_up, + VXGE_HW_HEADER_ETHERNET_II_802_3_ALIGN); + + pci_dma_sync_single_for_cpu(ring->pdev, + data_dma, data_size, + PCI_DMA_FROMDEVICE); + + vxge_debug_mem(VXGE_TRACE, + "%s: %s:%d skb_up = %p", + ring->ndev->name, __func__, + __LINE__, skb); + memcpy(skb_up->data, skb->data, pkt_length); + + vxge_re_pre_post(dtr, ring, rx_priv); + + vxge_post(&dtr_cnt, &first_dtr, dtr, + ringh); + /* will netif_rx small SKB instead */ + skb = skb_up; + skb_put(skb, pkt_length); + } else { + vxge_re_pre_post(dtr, ring, rx_priv); + + vxge_post(&dtr_cnt, &first_dtr, dtr, ringh); + vxge_debug_rx(VXGE_ERR, + "%s: vxge_rx_1b_compl: out of " + "memory", dev->name); + ring->stats.skb_alloc_fail++; + break; + } + } + + if ((ext_info.proto & VXGE_HW_FRAME_PROTO_TCP_OR_UDP) && + !(ext_info.proto & VXGE_HW_FRAME_PROTO_IP_FRAG) && + ring->rx_csum && /* Offload Rx side CSUM */ + ext_info.l3_cksum == VXGE_HW_L3_CKSUM_OK && + ext_info.l4_cksum == VXGE_HW_L4_CKSUM_OK) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; + + vxge_rx_complete(ring, skb, ext_info.vlan, + pkt_length, &ext_info); + + ring->budget--; + ring->pkts_processed++; + if (!ring->budget) + break; + + } while (vxge_hw_ring_rxd_next_completed(ringh, &dtr, + &t_code) == VXGE_HW_OK); + + if (first_dtr) + vxge_hw_ring_rxd_post_post_wmb(ringh, first_dtr); + + dev->last_rx = jiffies; + + vxge_debug_entryexit(VXGE_TRACE, + "%s:%d Exiting...", + __func__, __LINE__); + return VXGE_HW_OK; +} + +/* + * vxge_xmit_compl + * + * If an interrupt was raised to indicate DMA complete of the Tx packet, + * this function is called. It identifies the last TxD whose buffer was + * freed and frees all skbs whose data have already DMA'ed into the NICs + * internal memory. + */ +enum vxge_hw_status +vxge_xmit_compl(struct __vxge_hw_fifo *fifo_hw, void *dtr, + enum vxge_hw_fifo_tcode t_code, void *userdata, + void **skb_ptr) +{ + struct vxge_fifo *fifo = (struct vxge_fifo *)userdata; + struct sk_buff *skb, *head = NULL; + struct sk_buff **temp; + int pkt_cnt = 0; + + vxge_debug_entryexit(VXGE_TRACE, + "%s:%d Entered....", __func__, __LINE__); + + do { + int frg_cnt; + skb_frag_t *frag; + int i = 0, j; + struct vxge_tx_priv *txd_priv = + vxge_hw_fifo_txdl_private_get(dtr); + + skb = txd_priv->skb; + frg_cnt = skb_shinfo(skb)->nr_frags; + frag = &skb_shinfo(skb)->frags[0]; + + vxge_debug_tx(VXGE_TRACE, + "%s: %s:%d fifo_hw = %p dtr = %p " + "tcode = 0x%x", fifo->ndev->name, __func__, + __LINE__, fifo_hw, dtr, t_code); + /* check skb validity */ + vxge_assert(skb); + vxge_debug_tx(VXGE_TRACE, + "%s: %s:%d skb = %p itxd_priv = %p frg_cnt = %d", + fifo->ndev->name, __func__, __LINE__, + skb, txd_priv, frg_cnt); + if (unlikely(t_code)) { + fifo->stats.tx_errors++; + vxge_debug_tx(VXGE_ERR, + "%s: tx: dtr %p completed due to " + "error t_code %01x", fifo->ndev->name, + dtr, t_code); + vxge_hw_fifo_handle_tcode(fifo_hw, dtr, t_code); + } + + /* for unfragmented skb */ + pci_unmap_single(fifo->pdev, txd_priv->dma_buffers[i++], + skb_headlen(skb), PCI_DMA_TODEVICE); + + for (j = 0; j < frg_cnt; j++) { + pci_unmap_page(fifo->pdev, + txd_priv->dma_buffers[i++], + frag->size, PCI_DMA_TODEVICE); + frag += 1; + } + + vxge_hw_fifo_txdl_free(fifo_hw, dtr); + + /* Updating the statistics block */ + fifo->stats.tx_frms++; + fifo->stats.tx_bytes += skb->len; + + temp = (struct sk_buff **)&skb->cb; + *temp = head; + head = skb; + + pkt_cnt++; + if (pkt_cnt > fifo->indicate_max_pkts) + break; + + } while (vxge_hw_fifo_txdl_next_completed(fifo_hw, + &dtr, &t_code) == VXGE_HW_OK); + + vxge_wake_tx_queue(fifo, skb); + + if (skb_ptr) + *skb_ptr = (void *) head; + + vxge_debug_entryexit(VXGE_TRACE, + "%s: %s:%d Exiting...", + fifo->ndev->name, __func__, __LINE__); + return VXGE_HW_OK; +} + +/* select a vpath to trasmit the packet */ +static u32 vxge_get_vpath_no(struct vxgedev *vdev, struct sk_buff *skb, + int *do_lock) +{ + u16 queue_len, counter = 0; + if (skb->protocol == htons(ETH_P_IP)) { + struct iphdr *ip; + struct tcphdr *th; + + ip = ip_hdr(skb); + + if ((ip->frag_off & htons(IP_OFFSET|IP_MF)) == 0) { + th = (struct tcphdr *)(((unsigned char *)ip) + + ip->ihl*4); + + queue_len = vdev->no_of_vpath; + counter = (ntohs(th->source) + + ntohs(th->dest)) & + vdev->vpath_selector[queue_len - 1]; + if (counter >= queue_len) + counter = queue_len - 1; + + if (ip->protocol == IPPROTO_UDP) { +#ifdef NETIF_F_LLTX + *do_lock = 0; +#endif + } + } + } + return counter; +} + +static enum vxge_hw_status vxge_search_mac_addr_in_list( + struct vxge_vpath *vpath, u64 del_mac) +{ + struct list_head *entry, *next; + list_for_each_safe(entry, next, &vpath->mac_addr_list) { + if (((struct vxge_mac_addrs *)entry)->macaddr == del_mac) + return TRUE; + } + return FALSE; +} + +static int vxge_learn_mac(struct vxgedev *vdev, u8 *mac_header) +{ + struct macInfo mac_info; + u8 *mac_address = NULL; + u64 mac_addr = 0, vpath_vector = 0; + int vpath_idx = 0; + enum vxge_hw_status status = VXGE_HW_OK; + struct vxge_vpath *vpath = NULL; + struct __vxge_hw_device *hldev; + + hldev = (struct __vxge_hw_device *) pci_get_drvdata(vdev->pdev); + + mac_address = (u8 *)&mac_addr; + memcpy(mac_address, mac_header, ETH_ALEN); + + /* Is this mac address already in the list? */ + for (vpath_idx = 0; vpath_idx < vdev->no_of_vpath; vpath_idx++) { + vpath = &vdev->vpaths[vpath_idx]; + if (vxge_search_mac_addr_in_list(vpath, mac_addr)) + return vpath_idx; + } + + memset(&mac_info, 0, sizeof(struct macInfo)); + memcpy(mac_info.macaddr, mac_header, ETH_ALEN); + + /* Any vpath has room to add mac address to its da table? */ + for (vpath_idx = 0; vpath_idx < vdev->no_of_vpath; vpath_idx++) { + vpath = &vdev->vpaths[vpath_idx]; + if (vpath->mac_addr_cnt < vpath->max_mac_addr_cnt) { + /* Add this mac address to this vpath */ + mac_info.vpath_no = vpath_idx; + mac_info.state = VXGE_LL_MAC_ADDR_IN_DA_TABLE; + status = vxge_add_mac_addr(vdev, &mac_info); + if (status != VXGE_HW_OK) + return -EPERM; + return vpath_idx; + } + } + + mac_info.state = VXGE_LL_MAC_ADDR_IN_LIST; + vpath_idx = 0; + mac_info.vpath_no = vpath_idx; + /* Is the first vpath already selected as catch-basin ? */ + vpath = &vdev->vpaths[vpath_idx]; + if (vpath->mac_addr_cnt > vpath->max_mac_addr_cnt) { + /* Add this mac address to this vpath */ + if (FALSE == vxge_mac_list_add(vpath, &mac_info)) + return -EPERM; + return vpath_idx; + } + + /* Select first vpath as catch-basin */ + vpath_vector = vxge_mBIT(vpath->device_id); + status = vxge_hw_mgmt_reg_write(vpath->vdev->devh, + vxge_hw_mgmt_reg_type_mrpcim, + 0, + (ulong)offsetof( + struct vxge_hw_mrpcim_reg, + rts_mgr_cbasin_cfg), + vpath_vector); + if (status != VXGE_HW_OK) { + vxge_debug_tx(VXGE_ERR, + "%s: Unable to set the vpath-%d in catch-basin mode", + VXGE_DRIVER_NAME, vpath->device_id); + return -EPERM; + } + + if (FALSE == vxge_mac_list_add(vpath, &mac_info)) + return -EPERM; + + return vpath_idx; +} + +/** + * vxge_xmit + * @skb : the socket buffer containing the Tx data. + * @dev : device pointer. + * + * This function is the Tx entry point of the driver. Neterion NIC supports + * certain protocol assist features on Tx side, namely CSO, S/G, LSO. + * NOTE: when device cant queue the pkt, just the trans_start variable will + * not be upadted. +*/ +static int +vxge_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct vxge_fifo *fifo = NULL; + void *dtr_priv; + void *dtr = NULL; + struct vxgedev *vdev = NULL; + enum vxge_hw_status status; + int frg_cnt, first_frg_len; + skb_frag_t *frag; + int i = 0, j = 0, avail; + u64 dma_pointer; + struct vxge_tx_priv *txdl_priv = NULL; + struct __vxge_hw_fifo *fifo_hw; + u32 max_mss = 0x0; + int offload_type; + unsigned long flags = 0; + int vpath_no = 0; + int do_spin_tx_lock = 1; + + vxge_debug_entryexit(VXGE_TRACE, "%s: %s:%d", + dev->name, __func__, __LINE__); + + /* A buffer with no data will be dropped */ + if (unlikely(skb->len <= 0)) { + vxge_debug_tx(VXGE_ERR, + "%s: Buffer has no data..", dev->name); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + vdev = (struct vxgedev *)netdev_priv(dev); + + if (unlikely(!is_vxge_card_up(vdev))) { + vxge_debug_tx(VXGE_ERR, + "%s: vdev not initialized", dev->name); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + if (vdev->config.addr_learn_en) { + vpath_no = vxge_learn_mac(vdev, skb->data + ETH_ALEN); + if (vpath_no == -EPERM) { + vxge_debug_tx(VXGE_ERR, + "%s: Failed to store the mac address", + dev->name); + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + } + + if (vdev->config.tx_steering_type == TX_MULTIQ_STEERING) + vpath_no = skb_get_queue_mapping(skb); + else if (vdev->config.tx_steering_type == TX_PORT_STEERING) + vpath_no = vxge_get_vpath_no(vdev, skb, &do_spin_tx_lock); + + vxge_debug_tx(VXGE_TRACE, "%s: vpath_no= %d", dev->name, vpath_no); + + if (vpath_no >= vdev->no_of_vpath) + vpath_no = 0; + + fifo = &vdev->vpaths[vpath_no].fifo; + fifo_hw = fifo->handle; + + if (do_spin_tx_lock) + spin_lock_irqsave(&fifo->tx_lock, flags); + else { + if (unlikely(!spin_trylock_irqsave(&fifo->tx_lock, flags))) + return NETDEV_TX_LOCKED; + } + + if (vdev->config.tx_steering_type == TX_MULTIQ_STEERING) { + if (netif_subqueue_stopped(dev, skb)) { + spin_unlock_irqrestore(&fifo->tx_lock, flags); + return NETDEV_TX_BUSY; + } + } else if (unlikely(fifo->queue_state == VPATH_QUEUE_STOP)) { + if (netif_queue_stopped(dev)) { + spin_unlock_irqrestore(&fifo->tx_lock, flags); + return NETDEV_TX_BUSY; + } + } + avail = vxge_hw_fifo_free_txdl_count_get(fifo_hw); + if (avail == 0) { + vxge_debug_tx(VXGE_ERR, + "%s: No free TXDs available", dev->name); + fifo->stats.txd_not_free++; + vxge_stop_tx_queue(fifo); + goto _exit2; + } + + status = vxge_hw_fifo_txdl_reserve(fifo_hw, &dtr, &dtr_priv); + if (unlikely(status != VXGE_HW_OK)) { + vxge_debug_tx(VXGE_ERR, + "%s: Out of descriptors .", dev->name); + fifo->stats.txd_out_of_desc++; + vxge_stop_tx_queue(fifo); + goto _exit2; + } + + vxge_debug_tx(VXGE_TRACE, + "%s: %s:%d fifo_hw = %p dtr = %p dtr_priv = %p", + dev->name, __func__, __LINE__, + fifo_hw, dtr, dtr_priv); + + if (vdev->vlgrp && vlan_tx_tag_present(skb)) { + u16 vlan_tag = vlan_tx_tag_get(skb); + vxge_hw_fifo_txdl_vlan_set(dtr, vlan_tag); + } + + first_frg_len = skb_headlen(skb); + + dma_pointer = pci_map_single(fifo->pdev, skb->data, first_frg_len, + PCI_DMA_TODEVICE); + + if (unlikely(pci_dma_mapping_error(fifo->pdev, dma_pointer))) { + vxge_hw_fifo_txdl_free(fifo_hw, dtr); + vxge_stop_tx_queue(fifo); + fifo->stats.pci_map_fail++; + goto _exit2; + } + + txdl_priv = vxge_hw_fifo_txdl_private_get(dtr); + txdl_priv->skb = skb; + txdl_priv->dma_buffers[j] = dma_pointer; + + frg_cnt = skb_shinfo(skb)->nr_frags; + vxge_debug_tx(VXGE_TRACE, + "%s: %s:%d skb = %p txdl_priv = %p " + "frag_cnt = %d dma_pointer = 0x%llx", dev->name, + __func__, __LINE__, skb, txdl_priv, + frg_cnt, (unsigned long long)dma_pointer); + + vxge_hw_fifo_txdl_buffer_set(fifo_hw, dtr, j++, dma_pointer, + first_frg_len); + + frag = &skb_shinfo(skb)->frags[0]; + for (i = 0; i < frg_cnt; i++) { + /* ignore 0 length fragment */ + if (!frag->size) + continue; + + dma_pointer = + (u64)pci_map_page(fifo->pdev, frag->page, + frag->page_offset, frag->size, + PCI_DMA_TODEVICE); + + if (unlikely(pci_dma_mapping_error(fifo->pdev, dma_pointer))) + goto _exit0; + vxge_debug_tx(VXGE_TRACE, + "%s: %s:%d frag = %d dma_pointer = 0x%llx", + dev->name, __func__, __LINE__, i, + (unsigned long long)dma_pointer); + + txdl_priv->dma_buffers[j] = dma_pointer; + vxge_hw_fifo_txdl_buffer_set(fifo_hw, dtr, j++, dma_pointer, + frag->size); + frag += 1; + } + + offload_type = vxge_offload_type(skb); + + if (offload_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) { + + int mss = vxge_tcp_mss(skb); + if (mss) { + max_mss = dev->mtu + ETH_HLEN - + VXGE_HW_TCPIP_HEADER_MAX_SIZE; + if (mss > max_mss) + mss = max_mss; + vxge_debug_tx(VXGE_TRACE, + "%s: %s:%d mss = %d", + dev->name, __func__, __LINE__, mss); + vxge_hw_fifo_txdl_mss_set(dtr, mss); + } else { + vxge_assert(skb->len <= + dev->mtu + VXGE_HW_MAC_HEADER_MAX_SIZE); + vxge_assert(0); + goto _exit1; + } + } + + if (skb->ip_summed == CHECKSUM_PARTIAL) + vxge_hw_fifo_txdl_cksum_set_bits(dtr, + VXGE_HW_FIFO_TXD_TX_CKO_IPV4_EN | + VXGE_HW_FIFO_TXD_TX_CKO_TCP_EN | + VXGE_HW_FIFO_TXD_TX_CKO_UDP_EN); + + vxge_hw_fifo_txdl_post(fifo_hw, dtr); + dev->trans_start = jiffies; + spin_unlock_irqrestore(&fifo->tx_lock, flags); + + VXGE_COMPLETE_VPATH_TX(fifo); + vxge_debug_entryexit(VXGE_TRACE, "%s: %s:%d Exiting...", + dev->name, __func__, __LINE__); + return 0; + +_exit0: + vxge_debug_tx(VXGE_TRACE, "%s: pci_map_page failed", dev->name); + +_exit1: + j = 0; + frag = &skb_shinfo(skb)->frags[0]; + + pci_unmap_single(fifo->pdev, txdl_priv->dma_buffers[j++], + skb_headlen(skb), PCI_DMA_TODEVICE); + + for (; j < i; j++) { + pci_unmap_page(fifo->pdev, txdl_priv->dma_buffers[j], + frag->size, PCI_DMA_TODEVICE); + frag += 1; + } + + vxge_hw_fifo_txdl_free(fifo_hw, dtr); +_exit2: + dev_kfree_skb(skb); + spin_unlock_irqrestore(&fifo->tx_lock, flags); + VXGE_COMPLETE_VPATH_TX(fifo); + + return 0; +} + +/* + * vxge_rx_term + * + * Function will be called by hw function to abort all outstanding receive + * descriptors. + */ +static void +vxge_rx_term(void *dtrh, enum vxge_hw_rxd_state state, void *userdata) +{ + struct vxge_ring *ring = (struct vxge_ring *)userdata; + struct vxge_rx_priv *rx_priv = + vxge_hw_ring_rxd_private_get(dtrh); + + vxge_debug_entryexit(VXGE_TRACE, "%s: %s:%d", + ring->ndev->name, __func__, __LINE__); + if (state != VXGE_HW_RXD_STATE_POSTED) + return; + + pci_unmap_single(ring->pdev, rx_priv->data_dma, + rx_priv->data_size, PCI_DMA_FROMDEVICE); + + dev_kfree_skb(rx_priv->skb); + + vxge_debug_entryexit(VXGE_TRACE, + "%s: %s:%d Exiting...", + ring->ndev->name, __func__, __LINE__); +} + +/* + * vxge_tx_term + * + * Function will be called to abort all outstanding tx descriptors + */ +static void +vxge_tx_term(void *dtrh, enum vxge_hw_txdl_state state, void *userdata) +{ + struct vxge_fifo *fifo = (struct vxge_fifo *)userdata; + skb_frag_t *frag; + int i = 0, j, frg_cnt; + struct vxge_tx_priv *txd_priv = vxge_hw_fifo_txdl_private_get(dtrh); + struct sk_buff *skb = txd_priv->skb; + + vxge_debug_entryexit(VXGE_TRACE, "%s:%d", __func__, __LINE__); + + if (state != VXGE_HW_TXDL_STATE_POSTED) + return; + + /* check skb validity */ + vxge_assert(skb); + frg_cnt = skb_shinfo(skb)->nr_frags; + frag = &skb_shinfo(skb)->frags[0]; + + /* for unfragmented skb */ + pci_unmap_single(fifo->pdev, txd_priv->dma_buffers[i++], + skb_headlen(skb), PCI_DMA_TODEVICE); + + for (j = 0; j < frg_cnt; j++) { + pci_unmap_page(fifo->pdev, txd_priv->dma_buffers[i++], + frag->size, PCI_DMA_TODEVICE); + frag += 1; + } + + dev_kfree_skb(skb); + + vxge_debug_entryexit(VXGE_TRACE, + "%s:%d Exiting...", __func__, __LINE__); +} + +/** + * vxge_set_multicast + * @dev: pointer to the device structure + * + * Entry point for multicast address enable/disable + * This function is a driver entry point which gets called by the kernel + * whenever multicast addresses must be enabled/disabled. This also gets + * called to set/reset promiscuous mode. Depending on the deivce flag, we + * determine, if multicast address must be enabled or if promiscuous mode + * is to be disabled etc. + */ +static void vxge_set_multicast(struct net_device *dev) +{ + struct dev_mc_list *mclist; + struct vxgedev *vdev; + int i, mcast_cnt = 0; + struct __vxge_hw_device *hldev; + enum vxge_hw_status status = VXGE_HW_OK; + struct macInfo mac_info; + int v |