Neterion: New driver: Main entry points
Subsystems (2)
PATCH 8/10 Neterion: New driver: Main entry points
This patch takes care of implementing all driver entry point functions.
- Definition of all module loadable paramters.
- Implementation of all driver entry point functions.
Signed-off-by Sivakumar Subramani <[email protected]>
Signed-off-by Rastapur Santosh <[email protected]>
Signed-off-by Ramkrishna Vepa <[email protected]>
---
drivers/net/vxge/vxge-main.c +5204 -0
@@ -0,0 +1,5204 @@ +/****************************************************************************** +* 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 Receive Traffic Steering using MAC destination address. +* 0 - DISABLE +* 1 - ENABLE +* +* lro: +* Enable Large Receive Offload (LRO) +* 0 - DISABLE +* 1 - ENABLE +* +* ring_blocks: +* Numbers of RxD blocks in the ring +* MIN - 1 and MAX - 128 +* +* max_config_port: +* Maximum number of port to be supported. +* MIN -1 and MAX - 2 +* +* fifo_indicate_max_pkts: +* Sets the maximum number of transmit frames to be processed within +* single interrupt. +* MIN - 1 and MAX - 65536 +* +* ring_indicate_max_pkts: +* Sets the maximum number of received frames to be processed within +* single interrupt. +* MIN - 1 and MAX - 65536 +* +* 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 +* +* intr_type: +* This configures the type of interrupt. +* 0 - INTA +* 1 - Reserved +* 2 - MSIX +* 3 - EMULATED_INTA +* +* exec_mode: +* This is set make enable the debug mode by default. +* 0 - DISABLE +* 1 - ENABLE +* +* rx_steering_type: +* This parameter is for configuring the receive side steering. +* 0 - No steering +* 1 - Reserved +* 2 - RTH steering (default) +* +* tx_steering_type: +* This parameter is for configuring the transmit steering. +* 0 - No steering +* 1 - Priority steering +* 2 - Vlan steering +* 3 - Multiqueue steering +* 4 - Port steering (default) +* +* tx_pause_enable: +* This parameter enables pause frame generation. +* 0 - Disable +* 1 - Enable +* +* rx_pause_enable: +* This parameter enables response to received pause frames +* 0 - Disable +* 1 - Enable. +* +*/ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/if_vlan.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/etherdevice.h> + +#include <net/sock.h> +#include <net/ip.h> + +#include <linux/ip.h> +#include <linux/tcp.h> + +#include "vxge-main.h" +#include "vxge-reg.h" + +#define VXGE_DRIVER_VERSION_MAJOR 0 + +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 int vxge_probe (struct pci_dev *pdev,const struct pci_device_id *pre); +static void vxge_remove (struct pci_dev *pdev); + +static pci_ers_result_t vxge_io_error_detected (struct pci_dev *pdev, +pci_channel_state_t state); +static pci_ers_result_t vxge_io_slot_reset (struct pci_dev *pdev); +static void vxge_io_resume (struct pci_dev *pdev); + +#ifdef CONFIG_PM +static int vxge_pm_suspend (struct pci_dev *pdev,pm_message_t state); +static int vxge_pm_resume (struct pci_dev *pdev); +#endif + +#define VXGE_PCI_DEVICE_ID_TITAN_3 0x66f3 + +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_LICENSE ("Dual BSD/GPL" ); +MODULE_DESCRIPTION ("Titan family network driver" ); + +MODULE_DEVICE_TABLE (pci, vxge_id_table); + +static struct pci_error_handlers vxge_err_handler = { + .error_detected = vxge_io_error_detected, + .slot_reset = vxge_io_slot_reset, + .resume = vxge_io_resume, +}; + +static struct pci_driver vxge_driver = { + .name =VXGE_DRIVER_NAME , + .id_table = vxge_id_table, + .probe = vxge_probe, + .remove =__devexit_p (vxge_remove), +#ifdef CONFIG_PM + .suspend = vxge_pm_suspend, + .resume = vxge_pm_resume, +#endif + + .err_handler = &vxge_err_handler, + +}; + +VXGE_MODULE_PARAM_INT (vlan_tag_strip, +VXGE_HW_VPATH_RPA_STRIP_VLAN_TAG_ENABLE ); + +#if defined(CONFIG_XEN ) +VXGE_MODULE_PARAM_INT (addr_learn_en,VXGE_HW_RTS_MAC_ENABLE ); +#else +VXGE_MODULE_PARAM_INT (addr_learn_en,VXGE_HW_RTS_MAC_DISABLE ); +#endif + +VXGE_MODULE_PARAM_INT (ring_indicate_max_pkts, +VXGE_HW_DEF_RING_INDICATE_MAX_PKTS ); +VXGE_MODULE_PARAM_INT (fifo_indicate_max_pkts,VXGE_FIFO_INDICATE_MAX_PKTS ); +VXGE_MODULE_PARAM_INT (ring_blocks,VXGE_HW_DEF_RING_BLOCKS ); +VXGE_MODULE_PARAM_INT (fifo_length,VXGE_DEF_FIFO_LENGTH ); +VXGE_MODULE_PARAM_INT (max_config_port,VXGE_MAX_CONFIG_PORT ); + +#if defined(HAVE_NETDEV_POLL ) + +VXGE_MODULE_PARAM_INT (napi,VXGE_ENABLE_NAPI ); + +#endif + +int netq =VXGE_NETQ_DISABLE ; +VXGE_MODULE_PARAM_INT (lro,VXGE_HW_LRO_DONT_AGGR_FWD_PKTS ); +VXGE_MODULE_PARAM_INT (rx_steering_type,RTH_STEERING ); + +VXGE_MODULE_PARAM_INT (tx_steering_type,TX_MULTIQ_STEERING ); + +VXGE_MODULE_PARAM_INT (lro_max_bytes,VXGE_LRO_MAX_BYTES ); + +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 ); + +VXGE_MODULE_PARAM_INT (intr_type,MSI_X ); + +VXGE_MODULE_PARAM_INT (exec_mode,VXGE_EXEC_MODE_DISABLE ); + +VXGE_MODULE_PARAM_INT (tx_pause_enable,VXGE_PAUSE_FRM_ENABLE ); +VXGE_MODULE_PARAM_INT (rx_pause_enable,VXGE_PAUSE_FRM_ENABLE ); + +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 ); + +int config_dev_cnt =0 , total_dev_cnt =0 ; +int g_no_cpus; +char g_serial_num[VXGE_HW_VPD_LENGTH +1 ]; +char g_product_name[VXGE_HW_VPD_LENGTH +1 ]; +unsigned int vpath_per_dev; + +#define VXGE_LL_MAX_FRAME_SIZE (dev) ((dev)->mtu + VXGE_HW_MAC_HEADER_MAX_SIZE) + +struct vxge_dma_async_data *async_data; + +static inline int is_vxge_card_up (struct vxgedev *vdev) +{ +return test_bit (__VXGE_STATE_CARD_UP, + (unsigned long *) &vdev->state); +} + +static inline void VXGE_COMPLETE_VPATH_TX (struct vxge_fifo *fifo) +{ +unsigned long flags =0 ; +unsigned int got_tx; +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, &got_tx, + (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_VPATH_RX (struct vxge_ring *ring) +{ +unsigned int got_rx; + +vxge_hw_vpath_poll_rx (ring->handle, &got_rx); +} + +static inline void VXGE_COMPLETE_ALL_RX (struct vxgedev *vdev) +{ +int i; + +/* Complete all receives*/ +for (i =0 ; i < vdev->no_of_vpath; i++) +VXGE_COMPLETE_VPATH_RX (&vdev->vpaths[i].ring); +} + +/* +* MultiQ manipulation helper functions +*/ +void vxge_stop_all_tx_queue (struct vxgedev *vdev) +{ +int i; +struct net_device *dev = (struct net_device *) 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 = (struct net_device *) 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); + +} + +void vxge_wake_all_tx_queue (struct vxgedev *vdev) +{ +int i; +struct net_device *dev = (struct net_device *) 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 called during interrupt context to notify link up state +* change. +*/ +void +vxge_callback_link_up (struct __hw_device *devh,void *userdata) +{ +struct net_device *dev = (struct net_device *)userdata; +struct vxgedev *vdev = (struct vxgedev *)netdev_priv (dev); + +vxge_debug_entryexit (VXGE_TRACE ,"%s: %s:%d\n" , + 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...\n" , vdev->ndev->name, + __func__, __LINE__); +} + +/* +* vxge_callback_link_down +* +* This function called during interrupt context to notify link down state +* change. +*/ +void +vxge_callback_link_down (struct __hw_device *devh,void *userdata) +{ +struct net_device *dev = (struct net_device *)userdata; +struct vxgedev *vdev = (struct vxgedev *)netdev_priv (dev); + +vxge_debug_entryexit (VXGE_TRACE , +"%s: %s:%d\n" , 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...\n" , vdev->ndev->name, + __func__, __LINE__); +} + +/* +* vxge_rx_alloc +* +* Allocate SKB. +*/ +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\n" , + 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%llx\n" , ring->ndev->name, + __func__, __LINE__, (u64 )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...\n" , ring->ndev->name, + __func__, __LINE__); + +return skb; +} + +/* +* vxge_rx_free +*/ +void +vxge_rx_free (void *dtrh) +{ +struct vxge_rx_priv *rx_priv; + +vxge_debug_entryexit (VXGE_TRACE , +"%s:%d\n" , __func__, __LINE__); + rx_priv =vxge_hw_ring_rxd_private_get (dtrh); +dev_kfree_skb (rx_priv->skb); +vxge_debug_entryexit (VXGE_TRACE , +"%s:%d Exiting...\n" , __func__, __LINE__); +} + +/* +* vxge_rx_map +*/ +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\n" , + 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\n" , + ring->ndev->name, __func__, __LINE__, (u64 )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...\n" , + ring->ndev->name, __func__, __LINE__); + +return 0 ; +} + +/* +* vxge_rx_unmap +*/ + +void +vxge_rx_unmap (struct vxge_ring *ring,dma_addr_t data_dma, +dma_addr_t data_size) +{ +vxge_debug_entryexit (VXGE_TRACE ,"%s: %s:%d\n" , + ring->ndev->name, __func__, __LINE__); + +pci_unmap_single (ring->pdev, data_dma, data_size,PCI_DMA_FROMDEVICE ); + +vxge_debug_entryexit (VXGE_TRACE , +"%s: %s:%d Exiting...\n" , + ring->ndev->name, __func__, __LINE__); +} + +#define RX_FOR_DEVICE 1 +#define RX_FOR_CPU 2 + +/* +* vxge_rx_sync +*/ +void +vxge_rx_sync (dma_addr_t data_dma,dma_addr_t data_size, +struct vxge_ring *ring,int flags) +{ +vxge_debug_entryexit (VXGE_TRACE ,"%s: %s:%d\n" , + ring->ndev->name, __func__, __LINE__); + +if (flags ==RX_FOR_CPU ) { +pci_dma_sync_single_for_cpu (ring->pdev, + data_dma, data_size,PCI_DMA_FROMDEVICE ); + }else if (flags ==RX_FOR_DEVICE ) { +pci_dma_sync_single_for_device (ring->pdev, + data_dma, data_size,PCI_DMA_FROMDEVICE ); + } + +vxge_debug_entryexit (VXGE_TRACE , +"%s: %s:%d Exiting...\n" , + ring->ndev->name, __func__, __LINE__); +} + +/* +* vxge_rx_initial_replenish +* @dtrh: DTR handle to operate +* @userdata: channel's userdata, points to vdev usually +* +* Allocation of RxD as an initial replenish procedure. +*/ +enum vxge_hw_status +vxge_rx_initial_replenish (void *dtrh,u32 index, +void *userdata,enum vxge_hw_reopen reopen) +{ +struct vxge_ring *ring = (struct vxge_ring *)userdata; + +vxge_debug_entryexit (VXGE_TRACE ,"%s: %s:%d\n" , + 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)) { +vxge_rx_free (dtrh); +return VXGE_HW_FAIL ; + } +vxge_debug_entryexit (VXGE_TRACE , +"%s: %s:%d Exiting...\n" , + 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\n" , + ring->ndev->name, __func__, __LINE__); + + 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\n" , + ring->ndev->name, __func__, __LINE__, + skb->protocol); + +if (ring->lro_enable) { + +if (ring->vlgrp && ext_info->vlan && + (ring->vlan_tag_strip == +VXGE_HW_VPATH_RPA_STRIP_VLAN_TAG_ENABLE )) +lro_vlan_hwaccel_receive_skb (&ring->lro_mgr, skb, + ring->vlgrp, ext_info->vlan, ext_info); +else +lro_receive_skb (&ring->lro_mgr, skb, + (void *)ext_info); + + }else { + +if (ring->vlgrp && vlan && + (ring->vlan_tag_strip == +VXGE_HW_VPATH_RPA_STRIP_VLAN_TAG_ENABLE )) { +if (ring->napi_enable) +vlan_hwaccel_receive_skb (skb, ring->vlgrp, + vlan); +else +vlan_hwaccel_rx (skb, ring->vlgrp, vlan); + }else { + + { +/* It is an ARP packet, send it to +* the Linux stack */ +memset (skb->cb,0 ,sizeof (skb->cb)); + +if (ring->napi_enable) +netif_receive_skb (skb); +else +netif_rx (skb); + } + } + } +vxge_debug_entryexit (VXGE_TRACE , +"%s: %s:%d Exiting...\n" , + ring->ndev->name, __func__, __LINE__); +} + +static inline void vxge_re_pre_post (void *dtr, +struct vxge_ring *ring,struct vxge_rx_priv *rx_priv) +{ +vxge_rx_sync (rx_priv->data_dma, rx_priv->data_size, + ring,RX_FOR_DEVICE ); +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 __hw_ring *ringh) +{ +int dtr_count = *dtr_cnt; +if ((*dtr_cnt %XGE_HW_RXSYNC_FREQ_CNT ) ==0 ) { +if (*first_dtr) { +vxge_hw_ring_rxd_post_post_wmb (ringh, *first_dtr); + *first_dtr =NULL ; + } + *first_dtr = post_dtr; + }else +vxge_hw_ring_rxd_post_post (ringh, post_dtr); + dtr_count++; + *dtr_cnt = dtr_count; +} + +#ifdef VXGE_RX_ERR_DBG +void vxge_dump_packet (struct sk_buff *skb,int pkt_length) +{ +int i =0 , j; +u8 *ptr = skb->data; + +printk (KERN_CRIT "\n Packet contents : Length = %d\n" , pkt_length); +for (j =0 ; j < pkt_length; j +=2 , i +=2 ) { +printk (KERN_CRIT "%02x%02x " , *(ptr + j), *(ptr + j +1 )); +if (!((i +2 ) %16 )) +printk ("\n" ); + } + +} +#endif + +/* +* vxge_rx_1b_compl +* @channel: +* @userdata: +* +* 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 __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\n" , + ring->ndev->name, __func__, __LINE__); + ring->pkts_processed =0 ; + +vxge_hw_ring_replenish (ringh,VXGE_HW_OPEN_NORMAL ,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%llx\n" , + ring->ndev->name, __func__, + __LINE__, (u64 )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\n" , + 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)) { + +#ifdef VXGE_RX_ERR_DBG +if (t_code ==VXGE_HW_RING_T_CODE_L2_FRM_ERR ) +vxge_dump_packet (skb, pkt_length); +#endif + +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 \n" , + 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); +vxge_rx_unmap (ring, + data_dma, data_size); +vxge_hw_ring_rxd_pre_post (ringh, + dtr); +vxge_post (&dtr_cnt, &first_dtr, dtr, + ringh); + }else { +vxge_rx_free (dtr); + 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 ); + +vxge_rx_sync (data_dma, data_size, + ring,RX_FOR_CPU ); + +vxge_debug_mem (VXGE_TRACE , +"%s: %s:%d skb_up = 0x%llx\n" , + ring->ndev->name, __func__, + __LINE__, (u64 )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); + +if (ring->napi_enable) { + +/* NEW NAPI */ + 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...\n" , + __func__, __LINE__); +if (ring->lro_enable) + +lro_flush_all (&ring->lro_mgr); + +return VXGE_HW_OK ; +} + +/* +* vxge_xmit_compl +* @channel: +* @userdata: +* +* 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 __hw_fifo *fifo_hw,void *dtr, +void *dtr_priv,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....\n" , __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 = 0x%llx dtr = 0x%llx \ +dtr_priv = 0x%llx tcode = 0x%x\n" , + fifo->ndev->name, __func__, __LINE__, + (u64 )fifo_hw, (u64 )dtr, (u64 )dtr_priv, t_code); +/* check skb validity */ +vxge_assert (skb); +vxge_debug_tx (VXGE_TRACE , +"%s: %s:%d skb = 0x%llx itxd_priv = 0x%llx \ +frg_cnt = %d\n" , + fifo->ndev->name, __func__, __LINE__, + (u64 )skb, (u64 )txd_priv, frg_cnt); +if (unlikely (t_code)) { + fifo->stats.tx_errors++; +vxge_debug_tx (VXGE_ERR , +"%s: tx: dtr 0x%llx completed due to " +"error t_code %01x" , + fifo->ndev->name, (unsigned long long ) + (ulong_t )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, &dtr_priv, &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...\n" , + fifo->ndev->name, __func__, __LINE__); +return VXGE_HW_OK ; +} + +/* select a vpath to trasmit the packet */ +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; +} + +#if defined(CONFIG_XEN ) + +enum vxge_hw_status vxge_search_mac_addr_in_list (struct vxge_vpath *vpath, +u64 del_mac) +{ +struct vxge_list *entry, *next; +vxge_list_for_each_safe (entry, next, &vpath->mac_addr_list) { +if (((struct vxge_mac_addrs *)entry)->macaddr == del_mac) +return TRUE; + } +return FALSE; +} + +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 __hw_device *hldev; + + hldev = (struct __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_t )vxge_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; +} +#endif + +/* +* 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 __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_assert (dev); +vxge_assert (netdev_priv (dev)); + +vxge_debug_entryexit (VXGE_TRACE ,"%s: %s:%d\n" , + 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..\n" , dev->name); + goto _exit2; + } + + vdev = (struct vxgedev *)netdev_priv (dev); + +if (unlikely (!is_vxge_card_up (vdev))) { +vxge_debug_tx (VXGE_ERR , +"%s: vdev not initialized\n" , dev->name); + goto _exit2; + } + +#if defined(CONFIG_XEN ) +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..\n" , + dev->name); + goto _exit2; + } + } +#endif + +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\n" , 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\n" , 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 = 0x%llx dtr = 0x%llx dtr_priv = 0x%llx\n" , + dev->name, __func__, __LINE__, + (u64 )fifo_hw, (u64 )dtr, (u64 )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 (vxge_do_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 = 0x%llx txdl_priv = 0x%llx \ +frag_cnt = %d dma_pointer = 0x%llx\n" , dev->name, + __func__, __LINE__, (u64 )skb, (u64 )txdl_priv, + frg_cnt, 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 (vxge_do_pci_dma_mapping_error ( + fifo->pdev, + dma_pointer))) + goto _exit0; +vxge_debug_tx (VXGE_TRACE , +"%s: %s:%d frag = %d dma_pointer = 0x%llx\n" , + dev->name, __func__, __LINE__, i, + 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\n" , + 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...\n" , + 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); +if (fifo) { +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, +enum vxge_hw_reopen reopen) +{ +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\n" , + ring->ndev->name, __func__, __LINE__); +if (state !=VXGE_HW_RXD_STATE_POSTED ) +return ; + +vxge_rx_unmap (ring, rx_priv->data_dma, rx_priv->data_size); +vxge_rx_free (dtrh); +vxge_debug_entryexit (VXGE_TRACE , +"%s: %s:%d Exiting...\n" , + 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,void *dtr_priv, +enum vxge_hw_txdl_state state,void *userdata, +enum vxge_hw_reopen reopen) +{ +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\n" , __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...\n" , __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 __hw_device *hldev; +enum vxge_hw_status status =VXGE_HW_OK ; +struct macInfo mac_info; +int vpath_idx =0 ; +struct vxge_mac_addrs *mac_entry; +struct vxge_list *list_head; +struct vxge_list *entry, *next; +u8 *mac_address =NULL ; + +vxge_debug_entryexit (VXGE_TRACE , +"%s:%d\n" , __func__, __LINE__); +vxge_assert (dev); +vxge_assert (netdev_priv (dev)); + + vdev = (struct vxgedev *)netdev_priv (dev); + hldev = (struct __hw_device *)vdev->devh; + +if (unlikely (!is_vxge_card_up (vdev))) +return ; + +if ((dev->flags &IFF_ALLMULTI ) && (!vdev->all_multi_flg)) { +for (i =0 ; i < vdev->no_of_vpath; i++) { +vxge_assert (vdev->vpaths[i].is_open); + status =vxge_hw_vpath_mcast_enable ( + vdev->vpaths[i].handle); + vdev->all_multi_flg =1 ; + } + }else if ((dev->flags &IFF_ALLMULTI ) && (vdev->all_multi_flg)) { +for (i =0 ; i < vdev->no_of_vpath; i++) { +vxge_assert (vdev->vpaths[i].is_open); + status =vxge_hw_vpath_mcast_disable ( + vdev->vpaths[i].handle); + vdev->all_multi_flg =1 ; + } + } + +if (status !=VXGE_HW_OK ) +vxge_debug_init (VXGE_ERR , +"failed to %s multicast, status %d" , + dev->flags &IFF_ALLMULTI ? +"enable" :"disable" , status); + +if (!vdev->config.addr_learn_en) { +if (dev->flags &IFF_PROMISC ) { +for (i =0 ; i < vdev->no_of_vpath; i++) { +vxge_assert (vdev->vpaths[i].is_open); + status =vxge_hw_vpath_promisc_enable ( + vdev->vpaths[i].handle); + } + }else { +for (i =0 ; i < vdev->no_of_vpath; i++) { +vxge_assert (vdev->vpaths[i].is_open); + status =vxge_hw_vpath_promisc_disable ( + vdev->vpaths[i].handle); + } + } + } + +memset (&mac_info,0 ,sizeof (struct macInfo )); +/* Update individual M_CAST address list */ +if ((!vdev->all_multi_flg) && dev->mc_count) { + + mcast_cnt = vdev->vpaths[0 ].mcast_addr_cnt; + list_head = &vdev->vpaths[0 ].mac_addr_list; +if ((dev->mc_count + + (vdev->vpaths[0 ].mac_addr_cnt - mcast_cnt)) > + vdev->vpaths[0 ].max_mac_addr_cnt) + goto _set_all_mcast; + +/* Delete previous MC's */ +for (i =0 ; i < mcast_cnt; i++) { + mac_entry = (struct vxge_mac_addrs *) +vxge_list_first_get (list_head); + +vxge_list_for_each_safe (entry, next, list_head) { + + mac_entry = (struct vxge_mac_addrs *) entry; +/* Copy the mac address to delete */ + mac_address = (u8 *)&mac_entry->macaddr; +memcpy (mac_info.macaddr, mac_address,ETH_ALEN ); + +/* Is this a multicast address */ +if (0x01 & mac_info.macaddr[0 ]) { +for (vpath_idx =0 ; vpath_idx < + vdev->no_of_vpath; + vpath_idx++) { + mac_info.vpath_no = vpath_idx; + status =vxge_del_mac_addr ( + vdev, + &mac_info); + } + } + + } + } + +/* Add new ones */ +for (i =0 , mclist = dev->mc_list; i < dev->mc_count; + i++, mclist = mclist->next) { + +memcpy (mac_info.macaddr, mclist->dmi_addr,ETH_ALEN ); +for (vpath_idx =0 ; vpath_idx < vdev->no_of_vpath; + vpath_idx++) { + 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 ) { +vxge_debug_init (VXGE_ERR , +"%s:%d Setting individual \ +multicast address failed" , + __func__, __LINE__); + goto _set_all_mcast; + } + } + } + +return ; +_set_all_mcast: + mcast_cnt = vdev->vpaths[0 ].mcast_addr_cnt; +/* Delete previous MC's */ +for (i =0 ; i < mcast_cnt; i++) { + +vxge_list_for_each_safe (entry, next, list_head) { + + mac_entry = (struct vxge_mac_addrs *) entry; +/* Copy the mac address to delete */ + mac_address = (u8 *)&mac_entry->macaddr; +memcpy (mac_info.macaddr, mac_address,ETH_ALEN ); + +/* Is this a multicast address */ +if (0x01 & mac_info.macaddr[0 ]) +break ; + } + +for (vpath_idx =0 ; vpath_idx < vdev->no_of_vpath; + vpath_idx++) { + mac_info.vpath_no = vpath_idx; + status =vxge_del_mac_addr (vdev, &mac_info); + } + } + +/* Enable all multicast */ +for (i =0 ; i < vdev->no_of_vpath; i++) { +vxge_assert (vdev->vpaths[i].is_open); + status =vxge_hw_vpath_mcast_enable ( + vdev->vpaths[i].handle); +if (status !=VXGE_HW_OK ) { +vxge_debug_init (VXGE_ERR , +"%s:%d Enabling all multicasts failed" , + __func__, __LINE__); + } + vdev->all_multi_flg =1 ; + } + dev->flags |=IFF_ALLMULTI ; + } + +vxge_debug_entryexit (VXGE_TRACE , +"%s:%d Exiting...\n" , __func__, __LINE__); +} + +/* +* vxge_set_mac_addr +* @dev: pointer to the device structure +* +* Update entry "0" (default MAC addr) +*/ +static int vxge_set_mac_addr (struct net_device *dev,void *p) +{ +struct sockaddr *addr = p; +struct vxgedev *vdev; +struct __hw_device *hldev; +enum vxge_hw_status status =VXGE_HW_OK ; +struct macInfo mac_info_new, mac_info_old; +int vpath_idx =0 ; + +vxge_debug_entryexit (VXGE_TRACE , +"%s:%d\n" , __func__, __LINE__); +vxge_assert (dev); +vxge_assert (netdev_priv (dev)); + + vdev = (struct vxgedev *)netdev_priv (dev); + hldev = (struct __hw_device *)vdev->devh; + +if (!is_valid_ether_addr (addr->sa_data)) +return -EINVAL ; + +memset (&mac_info_new,0 ,sizeof (struct macInfo )); +memset (&mac_info_old,0 ,sizeof (struct macInfo )); + +vxge_debug_entryexit (VXGE_TRACE , +"%s:%d Exiting...\n" , + __func__, __LINE__); + +/* Get the old address */ +memcpy (mac_info_old.macaddr, dev->dev_addr, dev->addr_len); + +/* Copy the new address */ +memcpy (mac_info_new.macaddr, addr->sa_data, dev->addr_len); + +/* First delete the old mac address from all the vpaths +as we can't specify the index while adding new mac address */ +for (vpath_idx =0 ; vpath_idx < vdev->no_of_vpath; vpath_idx++) { +struct vxge_vpath *vpath = &vdev->vpaths[vpath_idx]; +if (!vpath->is_open) { +/* This can happen when this interface is added/removed +to the bonding interface. Delete this station address +from the linked list */ +vxge_mac_list_del (vpath, &mac_info_old); + +/* Add this new address to the linked list +for later restoring */ +vxge_mac_list_add (vpath, &mac_info_new); + +continue ; + } +/* Delete the station address */ + mac_info_old.vpath_no = vpath_idx; + status =vxge_del_mac_addr (vdev, &mac_info_old); + } + +if (unlikely (!is_vxge_card_up (vdev))) { +memcpy (dev->dev_addr, addr->sa_data, dev->addr_len); +return VXGE_HW_OK ; + } + +/* Set this mac address to all the vpaths */ +for (vpath_idx =0 ; vpath_idx < vdev->no_of_vpath; vpath_idx++) { + mac_info_new.vpath_no = vpath_idx; + mac_info_new.state =VXGE_LL_MAC_ADDR_IN_DA_TABLE ; + status =vxge_add_mac_addr (vdev, &mac_info_new); +if (status !=VXGE_HW_OK ) +return -EINVAL ; + } + +memcpy (dev->dev_addr, addr->sa_data, dev->addr_len); + +return status; +} + +/* +* vxge_vpath_intr_enable +* @vdev: pointer to vdev +* @vp_id: vpath for which to enable the interrupts +* +* Enables the interrupts for the vpath +*/ +void vxge_vpath_intr_enable (struct vxgedev *vdev,int vp_id) +{ +struct vxge_vpath *vpath = &vdev->vpaths[vp_id]; +int msix_id, alarm_msix_id; +int tim_msix_id[4 ] = {[0 ...3 ] =0 }; + +vxge_hw_vpath_intr_enable (vpath->handle); + +if (vdev->config.intr_type ==INTA ) +vxge_hw_vpath_inta_unmask_tx_rx (vpath->handle); +else { + msix_id = vp_id *VXGE_HW_VPATH_MSIX_ACTIVE ; + alarm_msix_id = +VXGE_HW_VPATH_MSIX_ACTIVE * vdev->no_of_vpath -2 ; + + tim_msix_id[0 ] = msix_id; + tim_msix_id[1 ] = msix_id +1 ; +vxge_hw_vpath_msix_set (vpath->handle, tim_msix_id, + alarm_msix_id); + +vxge_hw_vpath_msix_unmask (vpath->handle, msix_id); +vxge_hw_vpath_msix_unmask (vpath->handle, msix_id +1 ); + +/* enable the alarm vector */ +vxge_hw_vpath_msix_unmask (vpath->handle, alarm_msix_id); + } +} + +/* +* vxge_vpath_intr_disable +* @vdev: pointer to vdev +* @vp_id: vpath for which to disable the interrupts +* +* Disables the interrupts for the vpath +*/ +void vxge_vpath_intr_disable (struct vxgedev *vdev,int vp_id) +{ +struct vxge_vpath *vpath = &vdev->vpaths[vp_id]; +int msix_id; + +vxge_hw_vpath_intr_disable (vpath->handle); + +if (vdev->config.intr_type ==INTA ) +vxge_hw_vpath_inta_mask_tx_rx (vpath->handle); +else { + msix_id = vp_id *VXGE_HW_VPATH_MSIX_ACTIVE ; +vxge_hw_vpath_msix_mask (vpath->handle, msix_id); +vxge_hw_vpath_msix_mask (vpath->handle, msix_id +1 ); + +/* disable the alarm vector */ + msix_id =VXGE_HW_VPATH_MSIX_ACTIVE * vdev->no_of_vpath -2 ; +vxge_hw_vpath_msix_mask (vpath->handle, msix_id); + } +} + +/* +* vxge_reset_vpath +* @vdev: pointer to vdev +* @vp_id: vpath to reset +* +* Resets the vpath +*/ +int vxge_reset_vpath (struct vxgedev *vdev,int vp_id) +{ +enum vxge_hw_status status =VXGE_HW_OK ; +int ret =0 ; + +/* check if device is down already */ +if (unlikely (!is_vxge_card_up (vdev))) +return 0 ; + +/* is device reset already scheduled */ +if (test_bit (__VXGE_STATE_RESET_CARD, &vdev->state)) +return 0 ; + +if (vdev->vpaths[vp_id].handle) { +if (vxge_hw_vpath_reset (vdev->vpaths[vp_id].handle) + ==VXGE_HW_OK ) { +if (is_vxge_card_up (vdev) && +vxge_hw_vpath_recover_from_reset ( + vdev->vpaths[vp_id].handle) + !=VXGE_HW_OK ) { +vxge_debug_init (VXGE_ERR , +"vxge_hw_vpath_recover_from_reset \ +failed for vpath:%d\n" , vp_id); +return status; + } + }else { +vxge_debug_init (VXGE_ERR , +"vxge_hw_vpath_reset failed for \ +vpath:%d\n" , vp_id); +return status; + } + }else +return VXGE_HW_FAIL ; + +vxge_restore_vpath_mac_addr (&vdev->vpaths[vp_id]); +vxge_restore_vpath_vid_table (&vdev->vpaths[vp_id]); + +/* Enable all broadcast */ +vxge_hw_vpath_bcast_enable (vdev->vpaths[vp_id].handle); + +/* Enable the interrupts */ +vxge_vpath_intr_enable (vdev, vp_id); + +smp_wmb (); + +/* Enable the flow of traffic through the vpath */ +vxge_hw_vpath_enable (vdev->vpaths[vp_id].handle); + +/* Vpath reset done */ +clear_bit (vp_id, &vdev->vp_reset); + +/* Start the vpath queue */ +vxge_wake_tx_queue (&vdev->vpaths[vp_id].fifo,NULL ); + +return ret; +} + +int do_vxge_reset (struct vxgedev *vdev,int event) +{ +enum vxge_hw_status status; +int ret =0 , vp_id, i; + +vxge_debug_entryexit (VXGE_TRACE , +"%s:%d\n" , __func__, __LINE__); + +if ((event ==VXGE_LL_FULL_RESET ) || (event ==VXGE_LL_START_RESET )) { +/* check if device is down already */ +if (unlikely (!is_vxge_card_up (vdev))) +return 0 ; + +/* is reset already scheduled */ +if (test_and_set_bit (__VXGE_STATE_RESET_CARD, &vdev->state)) +return 0 ; + } + +if (event ==VXGE_LL_FULL_RESET ) { +/* wait for all the vpath reset to complete */ +for (vp_id =0 ; vp_id < vdev->no_of_vpath; vp_id++) { +while (test_bit (vp_id, &vdev->vp_reset)) +msleep (50 ); + } + +/* if execution mode is set to debug, don't reset the adapter */ +if (unlikely (vdev->exec_mode)) { +vxge_debug_init (VXGE_ERR , +"%s: execution mode is debug, returning.." , + vdev->ndev->name); +clear_bit (__VXGE_STATE_CARD_UP, &vdev->state); +vxge_stop_all_tx_queue (vdev); +return 0 ; + } + } + +printk (KERN_NOTICE "%s: reseting the chip\n" , + vdev->ndev->name); + +if (event ==VXGE_LL_FULL_RESET ) { +vxge_hw_device_intr_disable (vdev->devh); + +switch (vdev->cric_err_event.type) { +case VXGE_HW_EVENT_UNKNOWN : +vxge_stop_all_tx_queue (vdev); +vxge_debug_init (VXGE_ERR , +"fatal: %s: Disabling device due to" +"unknown error" , + vdev->ndev->name); + ret = -EPERM ; + goto out; +case VXGE_HW_EVENT_RESET_START : +break ; +case VXGE_HW_EVENT_RESET_COMPLETE : +case VXGE_HW_EVENT_LINK_DOWN : +case VXGE_HW_EVENT_LINK_UP : +case VXGE_HW_EVENT_ALARM_CLEARED : +case VXGE_HW_EVENT_ECCERR : +case VXGE_HW_EVENT_MRPCIM_ECCERR : + ret = -EPERM ; + goto out; +case VXGE_HW_EVENT_FIFO_ERR : +case VXGE_HW_EVENT_VPATH_ERR : +break ; +case VXGE_HW_EVENT_CRITICAL_ERR : +vxge_stop_all_tx_queue (vdev); +vxge_debug_init (VXGE_ERR , +"fatal: %s: Disabling device due to" +"serious error" , + vdev->ndev->name); +/* SOP or device reset required */ +/* This event is not currently used */ + ret = -EPERM ; + goto out; +case VXGE_HW_EVENT_SERR : +vxge_stop_all_tx_queue (vdev); +vxge_debug_init (VXGE_ERR , +"fatal: %s: Disabling device due to" +"serious error" , + vdev->ndev->name); + ret = -EPERM ; + goto out; +case VXGE_HW_EVENT_SRPCIM_SERR : +case VXGE_HW_EVENT_MRPCIM_SERR : + ret = -EPERM ; + goto out; +case VXGE_HW_EVENT_SLOT_FREEZE : +vxge_stop_all_tx_queue (vdev); +vxge_debug_init (VXGE_ERR , +"fatal: %s: Disabling device due to" +"slot freeze" , + vdev->ndev->name); + ret = -EPERM ; + goto out; +default : +break ; + + } + } + +if ((event ==VXGE_LL_FULL_RESET ) || (event ==VXGE_LL_START_RESET )) +vxge_stop_all_tx_queue (vdev); + +if (event ==VXGE_LL_FULL_RESET ) { + status =vxge_reset_all_vpaths (vdev); +if (status !=VXGE_HW_OK ) { +vxge_debug_init (VXGE_ERR , +"fatal: %s: can not reset vpaths" , + vdev->ndev->name); + ret = -EPERM ; + goto out; + } + } + +if (event ==VXGE_LL_COMPL_RESET ) { +for (i =0 ; i < vdev->no_of_vpath; i++) +if (vdev->vpaths[i].handle) { +if (vxge_hw_vpath_recover_from_reset ( + vdev->vpaths[i].handle) + !=VXGE_HW_OK ) { +vxge_debug_init (VXGE_ERR , +"vxge_hw_vpath_recover_" +"from_reset failed for vpath: \ +%d\n" , i); + ret = -EPERM ; + goto out; + } + }else { +vxge_debug_init (VXGE_ERR , +"vxge_hw_vpath_reset failed for \ +vpath:%d\n" , i); + ret = -EPERM ; + goto out; + } + } + +if ((event ==VXGE_LL_FULL_RESET ) || (event ==VXGE_LL_COMPL_RESET )) { +/* Reprogram the DA table with populated mac addresses */ +for (vp_id =0 ; vp_id < vdev->no_of_vpath; vp_id++) { +vxge_restore_vpath_mac_addr (&vdev->vpaths[vp_id]); +vxge_restore_vpath_vid_table (&vdev->vpaths[vp_id]); + } + +/* enable vpath interrupts */ +for (i =0 ; i < vdev->no_of_vpath; i++) +vxge_vpath_intr_enable (vdev, i); + +vxge_hw_device_intr_enable (vdev->devh); + +smp_wmb (); + +/* Indicate card up */ +set_bit (__VXGE_STATE_CARD_UP, &vdev->state); + +/* Get the traffic to flow through the vpaths */ +for (i =0 ; i < vdev->no_of_vpath; i++) +vxge_hw_vpath_enable (vdev->vpaths[i].handle); + +vxge_wake_all_tx_queue (vdev); + } + +out: +vxge_debug_entryexit (VXGE_TRACE , +"%s:%d Exiting...\n" , __func__, __LINE__); + +/* Indicate reset done */ +if ((event ==VXGE_LL_FULL_RESET ) || (event ==VXGE_LL_COMPL_RESET )) +clear_bit (__VXGE_STATE_RESET_CARD, &vdev->state); +return ret; +} + +/* +* vxge_reset +* @vdev: pointer to ll device +* +* driver may reset the chip on events of serr, eccerr, etc +*/ +int vxge_reset (struct vxgedev *vdev) +{ +do_vxge_reset (vdev,VXGE_LL_FULL_RESET ); +return 0 ; +} + +/** +* vxge_poll - Receive handler when Receive Polling is used. +* @dev: pointer to the device structure. +* @budget: Number of packets budgeted to be processed in this iteration. +* +* This function comes into picture only if Receive side is being handled +* through polling (called NAPI in linux). It mostly does what the normal +* Rx interrupt handler does in terms of descriptor and packet processing +* but not in an interrupt context. Also it will process a specified number +* of packets at most in one iteration. This value is passed down by the +* kernel as the function argument 'budget'. +*/ +#if defined(HAVE_NETDEV_POLL ) + +static int vxge_poll_msix (struct napi_struct *napi,int budget) +{ +struct vxge_ring *ring = + container_of(napi, struct vxge_ring, napi); +int budget_org = budget; + ring->budget = budget; + +VXGE_COMPLETE_VPATH_RX (ring); + +if (ring->pkts_processed < budget_org) { +napi_complete (napi); +/* Re enable the Rx interrupts for the vpath */ +vxge_hw_channel_msix_unmask ((struct __hw_channel *)ring->handle, + ring->rx_vector_no); + } + +return ring->pkts_processed; +} + +static int vxge_poll_inta (struct napi_struct *napi,int budget) +{ + struct vxgedev *vdev = container_of(napi, struct vxgedev, napi); +int pkts_processed =0 ; +int i; +int budget_org = budget; +struct vxge_ring *ring; + +struct __hw_device *hldev = (struct __hw_device *) +pci_get_drvdata (vdev->pdev); + +for (i =0 ; i < vdev->no_of_vpath; i++) { + ring = &vdev->vpaths[i].ring; + ring->budget = budget; +VXGE_COMPLETE_VPATH_RX (ring); + pkts_processed += ring->pkts_processed; + budget -= ring->pkts_processed; +if (budget <=0 ) +break ; + } + +VXGE_COMPLETE_ALL_TX (vdev); + +if (pkts_processed < budget_org) { +napi_complete (napi); +/* Re enable the Rx interrupts for the ring */ + +vxge_hw_device_unmask_all (hldev); +vxge_hw_device_flush_io (hldev); + + } + +return pkts_processed; +} + +#endif +#ifdef CONFIG_NET_POLL_CONTROLLER +/** +* vxge_netpoll - netpoll event handler entry point +* @dev : pointer to the device structure. +* Description: +* This function will be called by upper layer to check for events on the +* interface in situations where interrupts are disabled. It is used for +* specific in-kernel networking tasks, such as remote consoles and kernel +* debugging over the network (example netdump in RedHat). +*/ +static void vxge_netpoll (struct net_device *dev) +{ +struct __hw_device *hldev; +struct vxgedev *vdev; + + vdev = (struct vxgedev *)netdev_priv (dev); + hldev = (struct __hw_device *)pci_get_drvdata (vdev->pdev); + +vxge_debug_entryexit (VXGE_TRACE , +"%s:%d\n" , __func__, __LINE__); + +if (pci_channel_offline (vdev->pdev)) +return ; + +disable_irq (dev->irq); +vxge_hw_device_clear_tx_rx (hldev); + +vxge_hw_device_clear_tx_rx (hldev); +VXGE_COMPLETE_ALL_RX (vdev); +VXGE_COMPLETE_ALL_TX (vdev); + +enable_irq (dev->irq); + +vxge_debug_entryexit (VXGE_TRACE , +"%s:%d Exiting...\n" , __func__, __LINE__); +return ; +} +#endif + +/* RTH configuration */ +enum vxge_hw_status vxge_rth_configure (struct vxgedev *vdev) +{ +enum vxge_hw_status status =VXGE_HW_OK ; +struct vxge_hw_rth_hash_types hash_types; +u8 itable[256 ] = {0 };/* indirection table */ +u8 mtable[256 ] = {0 };/* CPU to vpath mapping */ +int index; + +/* +* Filling +* - itable with bucket numbers +* - mtable with bucket-to-vpath mapping +*/ +for (index =0 ; index < (1 << vdev->config.rth_bkt_sz); index++) { + itable[index] = index; + mtable[index] = index % vdev->no_of_vpath; + } + +/* Fill RTH hash types */ + hash_types.hash_type_tcpipv4_en = vdev->config.rth_hash_type_tcpipv4; + hash_types.hash_type_ipv4_en = vdev->config.rth_hash_type_ipv4; + hash_types.hash_type_tcpipv6_en = vdev->config.rth_hash_type_tcpipv6; + hash_types.hash_type_ipv6_en = vdev->config.rth_hash_type_ipv6; + hash_types.hash_type_tcpipv6ex_en = + vdev->config.rth_hash_type_tcpipv6ex; + hash_types.hash_type_ipv6ex_en = vdev->config.rth_hash_type_ipv6ex; + +/* set indirection table, bucket-to-vpath mapping */ + status =vxge_hw_vpath_rts_rth_itable_set (vdev->vp_handles, + vdev->no_of_vpath, + mtable, + itable, + vdev->config.rth_bkt_sz); +if (status !=VXGE_HW_OK ) { +vxge_debug_init (VXGE_ERR , +"RTH indirection table configuration failed \ +for vpath:%d:\n" , + vdev->vpaths[0 ].device_id); +return status; + } + +/* +* Because the itable_set() method uses the active_table field +* for the target virtual path the RTH config should be updated +* for all VPATHs. The h/w only uses the lowest numbered VPATH +* when steering frames. +*/ +for (index =0 ; index < vdev->no_of_vpath; index++) { + status =vxge_hw_vpath_rts_rth_set ( + vdev->vpaths[index].handle, + vdev->config.rth_algorithm, + &hash_types, + vdev->config.rth_bkt_sz); + +if (status !=VXGE_HW_OK ) { +vxge_debug_init (VXGE_ERR , +"RTH configuration failed for vpath:%d" , + vdev->vpaths[index].device_id); +return status; + } + } + +return status; +} + +int vxge_mac_list_add (struct vxge_vpath *vpath, +struct macInfo *mac) +{ +struct vxge_mac_addrs *new_mac_entry; +u8 *mac_address =NULL ; + +if (vpath->mac_addr_cnt >=VXGE_MAX_LEARN_MAC_ADDR_CNT ) +return TRUE; + + new_mac_entry =kzalloc (sizeof (struct vxge_mac_addrs ),GFP_ATOMIC ); +if (!new_mac_entry) { +vxge_debug_mem (VXGE_ERR , +"%s: memory allocation failed" , +VXGE_DRIVER_NAME ); +return FALSE; + } + +vxge_list_insert (&new_mac_entry->item, &vpath->mac_addr_list); + +/* Copy the new mac address to the list */ + mac_address = (u8 *)&new_mac_entry->macaddr; +memcpy (mac_address, mac->macaddr,ETH_ALEN ); + + new_mac_entry->state = mac->state; + vpath->mac_addr_cnt++; + +/* Is this a multicast address */ +if (0x01 & mac->macaddr[0 ]) + vpath->mcast_addr_cnt++; + +return TRUE; +} + +/* Add a mac address to DA table */ +enum vxge_hw_status vxge_add_mac_addr (struct vxgedev *vdev, +struct macInfo *mac) +{ +enum vxge_hw_status status =VXGE_HW_OK ; +struct vxge_vpath *vpath; +enum vxge_hw_vpath_mac_addr_add_mode duplicate_mode; + +if (0x01 & mac->macaddr[0 ])/* multicast address */ + duplicate_mode =VXGE_HW_VPATH_MAC_ADDR_ADD_DUPLICATE ; +else + duplicate_mode =VXGE_HW_VPATH_MAC_ADDR_REPLACE_DUPLICATE ; + + vpath = &vdev->vpaths[mac->vpath_no]; + status =vxge_hw_vpath_mac_addr_add (vpath->handle, mac->macaddr, + mac->macmask, + duplicate_mode); +if (status !=VXGE_HW_OK ) { +vxge_debug_init (VXGE_ERR , +"DA config add entry failed for vpath:%d" , + vpath->device_id); + }else +if (FALSE ==vxge_mac_list_add (vpath, mac)) + status = -EPERM ; + +return status; +} + +int vxge_mac_list_del (struct vxge_vpath *vpath, +struct macInfo *mac) +{ +struct vxge_list *entry, *next; +u64 del_mac =0 ; +u8 *mac_address = (u8 *) (&del_mac); + +/* Copy the mac address to delete from the list */ +memcpy (mac_address, mac->macaddr,ETH_ALEN ); + +vxge_list_for_each_safe (entry, next, &vpath->mac_addr_list) { +if (((struct vxge_mac_addrs *)entry)->macaddr == del_mac) { +vxge_list_remove (entry); +kfree ((struct vxge_mac_addrs *)entry); + vpath->mac_addr_cnt--; + +/* Is this a multicast address */ +if (0x01 & mac->macaddr[0 ]) + vpath->mcast_addr_cnt--; +return TRUE; + } + } + +return FALSE; +} +/* delete a mac address from DA table */ +enum vxge_hw_status vxge_del_mac_addr (struct vxgedev *vdev, +struct macInfo *mac) +{ +enum vxge_hw_status status =VXGE_HW_OK ; +struct vxge_vpath *vpath; + + vpath = &vdev->vpaths[mac->vpath_no]; + status =vxge_hw_vpath_mac_addr_delete (vpath->handle, mac->macaddr, + mac->macmask); +if (status !=VXGE_HW_OK ) { +vxge_debug_init (VXGE_ERR , +"DA config delete entry failed for vpath:%d" , + vpath->device_id); + }else +vxge_mac_list_del (vpath, mac); +return status; +} + +/* list all mac addresses from DA table */ +enum vxge_hw_status +vxge_search_mac_addr_in_da_table (struct vxge_vpath *vpath, +struct macInfo *mac) +{ +enum vxge_hw_status status =VXGE_HW_OK ; +unsigned char macmask[ETH_ALEN ]; +unsigned char macaddr[ETH_ALEN ]; + +/* get first entry */ + status =vxge_hw_vpath_mac_addr_get (vpath->handle, + macaddr, macmask); +if (status !=VXGE_HW_OK ) { +vxge_debug_init (VXGE_ERR , +"DA config list entry failed for vpath:%d" , + vpath->device_id); +return status; + } + +if (!compare_ether_addr (mac->macaddr, macaddr)) +return status; + +while (status ==VXGE_HW_OK ) { +/* Get next entries */ + status =vxge_hw_vpath_mac_addr_get_next (vpath->handle, + macaddr, macmask); + +if (status !=VXGE_HW_OK ) +return status; + +/* Is it already present? */ +if (!compare_ether_addr (mac->macaddr, macaddr)) +return status; + } + +return status; +} + +/* Store all vlan ids from the list to the vid table */ +enum vxge_hw_status vxge_restore_vpath_vid_table (struct vxge_vpath *vpath) +{ +enum vxge_hw_status status =VXGE_HW_OK ; +struct vxgedev *vdev = vpath->vdev; +u16 vid; + +if (vdev->vlgrp && vpath->is_open) { + +for (vid =0 ; vid <VLAN_GROUP_ARRAY_LEN ; vid++) { +if (!vlan_group_get_device (vdev->vlgrp, vid)) +continue ; +/* Add these vlan to the vid table */ + status =vxge_hw_vpath_vid_add (vpath->handle, vid); + } + } + +return status; +} + +/* Store all mac addresses from the list to the DA table */ +enum vxge_hw_status vxge_restore_vpath_mac_addr (struct vxge_vpath *vpath) +{ +enum vxge_hw_status status =VXGE_HW_OK ; +struct macInfo mac_info; +u8 *mac_address =NULL ; +struct vxge_list *entry, *next; + +memset (&mac_info,0 ,sizeof (struct macInfo )); + +if (vpath->is_open) { + +vxge_list_for_each_safe (entry, next, &vpath->mac_addr_list) { + mac_address = + (u8 *)& + ((struct vxge_mac_addrs *)entry)->macaddr; +memcpy (mac_info.macaddr, mac_address,ETH_ALEN ); + ((struct vxge_mac_addrs *)entry)->state = +VXGE_LL_MAC_ADDR_IN_DA_TABLE ; +/* does this mac address already exist in da table? */ + status =vxge_search_mac_addr_in_da_table (vpath, + &mac_info); +if (status !=VXGE_HW_OK ) { +/* Add this mac address to the DA table */ + status =vxge_hw_vpath_mac_addr_add ( + vpath->handle, mac_info.macaddr, + mac_info.macmask, +VXGE_HW_VPATH_MAC_ADDR_ADD_DUPLICATE ); +if (status !=VXGE_HW_OK ) { +vxge_debug_init (VXGE_ERR , +"DA add entry failed for vpath:%d" , + vpath->device_id); + ((struct vxge_mac_addrs *)entry)->state + =VXGE_LL_MAC_ADDR_IN_LIST ; + } + } + } + } + +return status; +} + +/* reset vpaths */ +enum vxge_hw_status vxge_reset_all_vpaths (struct vxgedev *vdev) +{ +int i; +enum vxge_hw_status status =VXGE_HW_OK ; + +for (i =0 ; i < vdev->no_of_vpath; i++) +if (vdev->vpaths[i].handle) { +if (vxge_hw_vpath_reset (vdev->vpaths[i].handle) + ==VXGE_HW_OK ) { +if (is_vxge_card_up (vdev) && +vxge_hw_vpath_recover_from_reset ( + vdev->vpaths[i].handle) + !=VXGE_HW_OK ) { +vxge_debug_init (VXGE_ERR , +"vxge_hw_vpath_recover_" +"from_reset failed for vpath: \ +%d\n" , i); +return status; + } + }else { +vxge_debug_init (VXGE_ERR , +"vxge_hw_vpath_reset failed for \ +vpath:%d\n" , i); +return status; + } + } +return status; +} + +/* close vpaths */ +void vxge_close_vpaths (struct vxgedev *vdev,int index) +{ +int i; +for (i = index; i < vdev->no_of_vpath; i++) { +if (vdev->vpaths[i].handle && vdev->vpaths[i].is_open) +vxge_hw_vpath_close (vdev->vpaths[i].handle); + vdev->vpaths[i].is_open =0 ; + vdev->vpaths[i].handle =NULL ; + } +} + +static int +vxge_get_skb_header (struct sk_buff *skb, +void **iphdr,void **tcphdr, +u64 *hdr_flags,void *priv) +{ +struct vxge_hw_ring_rxd_info *ext_info = + (struct vxge_hw_ring_rxd_info *)priv; + +/* Verify that this is a valid IPv4 TCP packet */ +if (!(ext_info->proto & (VXGE_HW_FRAME_PROTO_TCP | +VXGE_HW_FRAME_PROTO_IPV4 ))) +return -1 ; + +/* Set network headers */ +skb_reset_network_header (skb); +skb_set_transport_header (skb,ip_hdrlen (skb)); + *iphdr =ip_hdr (skb); + *tcphdr =tcp_hdr (skb); + *hdr_flags =LRO_IPV4 |LRO_TCP ; + +return 0 ; +} + +/* open vpaths */ +int vxge_open_vpaths (struct vxgedev *vdev) +{ +enum vxge_hw_status status; +int i; +u32 vp_id =0 ; +struct vxge_hw_vpath_attr attr; + +struct net_lro_mgr *lro_mgr =NULL ; + +if (vdev->config.lro_enable) { +/* Initialize max aggregatable pkts per session based on MTU */ + vdev->config.lro_max_aggr_per_sess = + (vdev->config.lro_max_bytes -1 ) / vdev->mtu; + +if (vdev->config.lro_max_aggr_per_sess <MIN_LRO_PACKETS ) + vdev->config.lro_max_aggr_per_sess =MIN_LRO_PACKETS ; + +if (vdev->config.lro_max_aggr_per_sess >MAX_LRO_PACKETS ) + vdev->config.lro_max_aggr_per_sess =MAX_LRO_PACKETS ; + } + +for (i =0 ; i < vdev->no_of_vpath; i++) { +vxge_assert (vdev->vpaths[i].is_configured); + attr.vp_id = vdev->vpaths[i].device_id; + attr.fifo_attr.callback = vxge_xmit_compl; + attr.fifo_attr.txdl_init =NULL ; + attr.fifo_attr.txdl_term = vxge_tx_term; + attr.fifo_attr.per_txdl_space =sizeof (struct vxge_tx_priv ); + attr.fifo_attr.userdata = (void *)&vdev->vpaths[i].fifo; + + attr.ring_attr.callback = vxge_rx_1b_compl; + attr.ring_attr.rxd_init = vxge_rx_initial_replenish; + attr.ring_attr.rxd_term = vxge_rx_term; + attr.ring_attr.per_rxd_space =sizeof (struct vxge_rx_priv ); + attr.ring_attr.userdata = (void *)&vdev->vpaths[i].ring; + + vdev->vpaths[i].ring.ndev = vdev->ndev; + vdev->vpaths[i].ring.pdev = vdev->pdev; + status =vxge_hw_vpath_open (vdev->devh, &attr, +NULL , &(vdev->vpaths[i].handle)); +if (status ==VXGE_HW_OK ) { + vdev->vpaths[i].fifo.handle = + (struct __hw_fifo *)attr.fifo_attr.userdata; + vdev->vpaths[i].ring.handle = + (struct __hw_ring *)attr.ring_attr.userdata; + vdev->vpaths[i].fifo.tx_steering_type = + vdev->config.tx_steering_type; + vdev->vpaths[i].fifo.ndev = vdev->ndev; + vdev->vpaths[i].fifo.pdev = vdev->pdev; + vdev->vpaths[i].fifo.indicate_max_pkts = + vdev->config.fifo_indicate_max_pkts; + vdev->vpaths[i].ring.aggr_ack = + vdev->config.aggr_ack; + vdev->vpaths[i].ring.rx_vector_no =0 ; + vdev->vpaths[i].ring.rx_csum = vdev->rx_csum; + vdev->vpaths[i].is_open =1 ; + vdev->vp_handles[i] = vdev->vpaths[i].handle; + vdev->vpaths[i].ring.lro_enable = + vdev->config. lro_enable; + vdev->vpaths[i].ring.vlan_tag_strip = + vdev->vlan_tag_strip; + vdev->stats.vpaths_open++; + }else { + vdev->stats.vpath_open_fail++; +vxge_debug_init (VXGE_ERR , +"%s: vpath: %d failed to open" +"with status: %d" , + vdev->ndev->name, vdev->vpaths[i].device_id, + status); +vxge_close_vpaths (vdev,0 ); +return -EPERM ; + } + +if (vdev->config.lro_enable) { + + lro_mgr = &vdev->vpaths[i].ring.lro_mgr; + lro_mgr->dev = vdev->ndev; +if (vdev->config.napi_enable) + lro_mgr->features = +LRO_F_NAPI |LRO_F_EXTRACT_VLAN_ID ; + +if (vdev->vpaths[i].ring.vlan_tag_strip) + lro_mgr->features |=LRO_F_EXTRACT_VLAN_ID ; + + lro_mgr->ip_summed =CHECKSUM_UNNECESSARY ; + lro_mgr->ip_summed_aggr =CHECKSUM_UNNECESSARY ; + lro_mgr->max_desc =MAX_LRO_SESSIONS ; + lro_mgr->lro_arr = vdev->vpaths[i].ring.lro_desc; + lro_mgr->get_skb_header = vxge_get_skb_header; + lro_mgr->max_aggr = vdev->config.lro_max_aggr_per_sess; + + } + + vp_id =vxge_hw_vpath_id (vdev->vpaths[i].handle); + vdev->vpaths_deployed |=vxge_mBIT (vp_id); + } +return VXGE_HW_OK ; +} + +/* +* vxge_isr_napi +* @irq: the irq of the device. +* @dev_id: a void pointer to the hldev structure of the Titan device +* @ptregs: pointer to the registers pushed on the stack. +* +* This function is the ISR handler of the device when napi is enabled. It +* identifies the reason for the interrupt and calls the relevant service +* routines. +*/ +irqreturn_t vxge_isr_napi (int irq,void *dev_id) +{ +struct __hw_device *hldev = (struct __hw_device *)dev_id; +struct vxgedev *vdev; +struct net_device *dev; +u64 reason; +enum vxge_hw_status status; + +vxge_debug_intr (VXGE_TRACE , +"%s:%d\n" , __func__, __LINE__); + dev =vxge_hw_device_private_get (hldev); + vdev =netdev_priv (dev); + +VXGE_CHECK_PCI_CHANNEL_OFFLINE (vdev->pdev); + +if (unlikely (!is_vxge_card_up (vdev))) +return IRQ_NONE ; + + dev = (struct net_device *)vdev->ndev; + status =vxge_hw_device_begin_irq (hldev, vdev->exec_mode, + &reason); +if (status ==VXGE_HW_OK ) { +vxge_hw_device_mask_all (hldev); + +if (reason & +VXGE_HW_TITAN_GENERAL_INT_STATUS_VPATH_TRAFFIC_INT ( + vdev->vpaths_deployed >> + (64 -VXGE_HW_MAX_VIRTUAL_PATHS ))) { + +vxge_hw_device_clear_tx_rx (hldev); +napi_schedule (&vdev->napi); +vxge_debug_intr (VXGE_TRACE , +"%s:%d Exiting...\n" , __func__, __LINE__); +return IRQ_HANDLED ; + }else +vxge_hw_device_unmask_all (hldev); + }else if (unlikely ((status ==VXGE_HW_ERR_VPATH ) || + (status ==VXGE_HW_ERR_CRITICAL ) || + (status ==VXGE_HW_ERR_FIFO ))) { +vxge_hw_device_mask_all (hldev); +vxge_hw_device_flush_io (hldev); +return IRQ_HANDLED ; + }else if (unlikely (status ==VXGE_HW_ERR_SLOT_FREEZE )) +return IRQ_HANDLED ; + +vxge_debug_intr (VXGE_TRACE , +"%s:%d Exiting...\n" , __func__, __LINE__); +return IRQ_NONE ; +} + +/* +* vxge_isr +* @irq: the irq of the device. +* @dev_id: a void pointer to the hldev structure of the Titan device +* @ptregs: pointer to the registers pushed on the stack. +* +* This function is the ISR handler of the device. It identifies the reason +* for the interrupt and calls the relevant service routines. +*/ +irqreturn_t vxge_isr (int irq,void *dev_id) +{ +struct __hw_device *hldev = (struct __hw_device *)dev_id; +struct vxgedev *vdev; +struct net_device *dev; +enum vxge_hw_status status; +u64 reason; + +vxge_debug_intr (VXGE_TRACE , +"%s:%d\n" , __func__, __LINE__); + dev =vxge_hw_device_private_get (hldev); + vdev =netdev_priv (dev); + +VXGE_CHECK_PCI_CHANNEL_OFFLINE (vdev->pdev); + +if (unlikely (!is_vxge_card_up (vdev))) +return IRQ_NONE ; + + status =vxge_hw_device_begin_irq (hldev, vdev->exec_mode, + &reason); +if (status ==VXGE_HW_OK ) { +vxge_hw_device_mask_all (hldev); + +if (reason & +VXGE_HW_TITAN_GENERAL_INT_STATUS_VPATH_TRAFFIC_INT ( + vdev->vpaths_deployed >> + (64 -VXGE_HW_MAX_VIRTUAL_PATHS ))) { + +vxge_hw_device_clear_tx_rx (hldev); +VXGE_COMPLETE_ALL_RX (vdev); +VXGE_COMPLETE_ALL_TX (vdev); +vxge_hw_device_unmask_all (hldev); +vxge_debug_intr (VXGE_TRACE , +"%s:%d Exiting...\n" , __func__, __LINE__); +return IRQ_HANDLED ; + }else +vxge_hw_device_unmask_all (hldev); + }else if (unlikely ((status ==VXGE_HW_ERR_VPATH ) || + (status ==VXGE_HW_ERR_CRITICAL ) || + (status ==VXGE_HW_ERR_FIFO ))) { +vxge_hw_device_mask_all (hldev); +vxge_hw_device_flush_io (hldev); +return IRQ_HANDLED ; + }else if (unlikely (status ==VXGE_HW_ERR_SLOT_FREEZE )) +return IRQ_HANDLED ; + +vxge_debug_intr (VXGE_TRACE , +"%s:%d Exiting...\n" , __func__, __LINE__); +return IRQ_NONE ; +} + +#ifdef CONFIG_PCI_MSI + +irqreturn_t +vxge_tx_msix_handle (int irq,void *dev_id) +{ +struct vxge_fifo *fifo = (struct vxge_fifo *)dev_id; + +VXGE_COMPLETE_VPATH_TX (fifo); + +return IRQ_HANDLED ; +} + +irqreturn_t +vxge_rx_msix_napi_handle (int irq,void *dev_id) +{ +struct vxge_ring *ring = (struct vxge_ring *)dev_id; + +/* MSIX_IDX for Rx is 1 */ +vxge_hw_channel_msix_mask ((struct __hw_channel *)ring->handle, + ring->rx_vector_no); + +napi_schedule (&ring->napi); +return IRQ_HANDLED ; +} + +irqreturn_t +vxge_rx_msix_handle (int irq,void *dev_id) +{ +struct vxge_ring *ring = (struct vxge_ring *)dev_id; + +VXGE_COMPLETE_VPATH_RX (ring); + +return IRQ_HANDLED ; +} + +irqreturn_t +vxge_alarm_msix_handle (int irq,void *dev_id) +{ +int i; +enum vxge_hw_status status; +struct vxge_vpath *vpath = (struct vxge_vpath *)dev_id; +struct vxgedev *vdev = vpath->vdev; +int alarm_msix_id = +VXGE_HW_VPATH_MSIX_ACTIVE * vdev->no_of_vpath -2 ; + +for (i =0 ; i < vdev->no_of_vpath; i++) { +vxge_hw_vpath_msix_mask (vdev->vpaths[i].handle, + alarm_msix_id); + + status =vxge_hw_vpath_alarm_process (vdev->vpaths[i].handle, + vdev->exec_mode); +if (status ==VXGE_HW_OK ) { + +vxge_hw_vpath_msix_unmask (vdev->vpaths[i].handle, + alarm_msix_id); + +continue ; + } +vxge_debug_intr (VXGE_ERR , +"%s: vxge_hw_vpath_alarm_process failed %x " , +VXGE_DRIVER_NAME , status); + } +return IRQ_HANDLED ; +} + +int vxge_alloc_msix (struct vxgedev *vdev) +{ +int j, i, ret =0 ; +int intr_cnt =0 ; +int alarm_msix_id =0 , msix_intr_vect =0 ;; + vdev->intr_cnt =0 ; + +/* Tx/Rx MSIX Vectors count */ + vdev->intr_cnt = vdev->no_of_vpath *2 ; + +/* Alarm MSIX Vectors count */ + vdev->intr_cnt++; + + intr_cnt = (vdev->max_vpath_supported *2 ) +1 ; + vdev->entries =kzalloc (intr_cnt *sizeof (struct msix_entry ), +GFP_KERNEL ); +if (!vdev->entries) { +vxge_debug_init (VXGE_ERR , +"%s: memory allocation failed" , +VXGE_DRIVER_NAME ); +return -ENOMEM ; + } + + vdev->vxge_entries =kmalloc (intr_cnt * +sizeof (struct vxge_msix_entry ), +GFP_KERNEL ); +if (!vdev->vxge_entries) { +vxge_debug_init (VXGE_ERR , +"%s: memory allocation failed" , +VXGE_DRIVER_NAME ); +kfree (vdev->entries); +return -ENOMEM ; + } + +/* Last vector in the list is used for alarm */ + alarm_msix_id =VXGE_HW_VPATH_MSIX_ACTIVE * vdev->no_of_vpath -2 ; +for (i =0 , j =0 ; i < vdev->max_vpath_supported; i++) { + + msix_intr_vect = i *VXGE_HW_VPATH_MSIX_ACTIVE ; + +/* Initialize the fifo vector */ + vdev->entries[j].entry = msix_intr_vect; + vdev->vxge_entries[j].entry = msix_intr_vect; + vdev->vxge_entries[j].in_use =0 ; + j++; + +/* Initialize the ring vector */ + vdev->entries[j].entry = msix_intr_vect +1 ; + vdev->vxge_entries[j].entry = msix_intr_vect +1 ; + vdev->vxge_entries[j].in_use =0 ; + j++; + } + +/* Initialize the alarm vector */ + vdev->entries[j].entry = alarm_msix_id; + vdev->vxge_entries[j].entry = alarm_msix_id; + vdev->vxge_entries[j].in_use =0 ; + + ret =pci_enable_msix (vdev->pdev, vdev->entries, intr_cnt); +/* if driver request exceeeds available irq's, request with a small +* number. +*/ +if (ret >0 ) { +vxge_debug_init (VXGE_ERR , +"%s: MSI-X enable failed for %d vectors, available: %d" , +VXGE_DRIVER_NAME , intr_cnt, ret); + vdev->max_vpath_supported = vdev->no_of_vpath; + intr_cnt = (vdev->max_vpath_supported *2 ) +1 ; + +/* Reset the alarm vector setting */ + vdev->entries[j].entry =0 ; + vdev->vxge_entries[j].entry =0 ; + +/* Initialize the alarm vector with new setting */ + vdev->entries[intr_cnt -1 ].entry = alarm_msix_id; + vdev->vxge_entries[intr_cnt -1 ].entry = alarm_msix_id; + vdev->vxge_entries[intr_cnt -1 ].in_use =0 ; + + ret =pci_enable_msix (vdev->pdev, vdev->entries, intr_cnt); +if (!ret) +vxge_debug_init (VXGE_ERR , +"%s: MSI-X enabled for %d vectors" , +VXGE_DRIVER_NAME , intr_cnt); + } + +if (ret) { +vxge_debug_init (VXGE_ERR , +"%s: MSI-X enable failed for %d vectors, ret: %d" , +VXGE_DRIVER_NAME , intr_cnt, ret); +kfree (vdev->entries); +kfree (vdev->vxge_entries); + vdev->entries =NULL ; + vdev->vxge_entries =NULL ; +return -ENODEV ; + } +return 0 ; +} + +static int vxge_enable_msix (struct vxgedev *vdev) +{ + +int i, ret =0 ; +enum vxge_hw_status status; +/* 0 - Tx, 1 - Rx */ +int tim_msix_id[4 ]; +int alarm_msix_id =0 , msix_intr_vect =0 ;; + vdev->intr_cnt =0 ; + +/* allocate msix vectors */ + ret =vxge_alloc_msix (vdev); +if (!ret) { +/* Last vector in the list is used for alarm */ + alarm_msix_id = +VXGE_HW_VPATH_MSIX_ACTIVE * vdev->no_of_vpath -2 ; +for (i =0 ; i < vdev->no_of_vpath; i++) { + +/* If fifo or ring are not enabled +the MSIX vector for that should be set to 0 +Hence initializeing this array to all 0s. +*/ +memset (tim_msix_id,0 ,sizeof (tim_msix_id)); + msix_intr_vect = i *VXGE_HW_VPATH_MSIX_ACTIVE ; + tim_msix_id[0 ] = msix_intr_vect; + + tim_msix_id[1 ] = msix_intr_vect +1 ; + vdev->vpaths[i].ring.rx_vector_no = tim_msix_id[1 ]; + + status =vxge_hw_vpath_msix_set ( + vdev->vpaths[i].handle, + tim_msix_id, + alarm_msix_id); +if (status !=VXGE_HW_OK ) { +vxge_debug_init (VXGE_ERR , +"vxge_hw_vpath_msix_set \ +failed with status : %x \n" , status); +kfree (vdev->entries); +kfree (vdev->vxge_entries); +pci_disable_msix (vdev->pdev); +return -ENODEV ; + } + } + } + +return ret; +} + +static void vxge_rem_msix_isr (struct vxgedev *vdev) +{ +int intr_cnt; + +for (intr_cnt =0 ; intr_cnt < (vdev->max_vpath_supported *2 +1 ); + intr_cnt++) { +if (vdev->vxge_entries[intr_cnt].in_use) { +synchronize_irq (vdev->entries[intr_cnt].vector); +free_irq (vdev->entries[intr_cnt].vector, + vdev->vxge_entries[intr_cnt].arg); + vdev->vxge_entries[intr_cnt].in_use =0 ; + } + } + +kfree (vdev->entries); +kfree (vdev->vxge_entries); + vdev->entries =NULL ; + vdev->vxge_entries =NULL ; + +if (vdev->config.intr_type ==MSI_X ) +pci_disable_msix (vdev->pdev); +} +#endif + +static void vxge_rem_isr (struct vxgedev *vdev) +{ +struct __hw_device *hldev; + hldev = (struct __hw_device *)pci_get_drvdata (vdev->pdev); + +#ifdef CONFIG_PCI_MSI +if (vdev->config.intr_type ==MSI_X ) { +vxge_rem_msix_isr (vdev); + } else +#endif +if ( + + (vdev->config.intr_type ==INTA )) { +synchronize_irq (vdev->pdev->irq); +free_irq (vdev->pdev->irq, hldev); + } +} + +int vxge_add_isr (struct vxgedev *vdev) +{ +int ret =0 ; +struct __hw_device *hldev; +#ifdef CONFIG_PCI_MSI +int vp_idx =0 , intr_idx =0 , intr_cnt =0 , msix_idx =0 , irq_req =0 ; +#endif +u64 function_mode = vdev->config.device_hw_info.function_mode; +int pci_fun =PCI_FUNC (vdev->pdev->devfn); + hldev = (struct __hw_device *)pci_get_drvdata (vdev->pdev); + +#ifdef CONFIG_PCI_MSI +if (vdev->config.intr_type ==MSI_X ) + ret =vxge_enable_msix (vdev); + +if (ret) { +vxge_debug_init (VXGE_ERR , +"%s: Enabling MSI-X Failed, " ,VXGE_DRIVER_NAME ); +if (function_mode ==VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION ) +return VXGE_HW_FAIL ; +else { +vxge_debug_init (VXGE_ERR , +"%s: Defaulting to INTA \n" ,VXGE_DRIVER_NAME ); + vdev->config.intr_type =INTA ; +vxge_hw_device_set_intr_type (vdev->devh, +VXGE_HW_INTR_MODE_IRQLINE ); +vxge_close_vpaths (vdev,1 ); + vdev->no_of_vpath =1 ; + vdev->stats.vpaths_open =1 ; + } + } + +if (vdev->config.intr_type ==MSI_X ) { +for (intr_idx =0 ; + intr_idx < (vdev->no_of_vpath * +VXGE_HW_VPATH_MSIX_ACTIVE ); intr_idx++) { + + msix_idx = intr_idx %VXGE_HW_VPATH_MSIX_ACTIVE ; + irq_req =0 ; + +switch (msix_idx) { +case 0 : +sprintf (vdev->desc[intr_cnt], +"%s:vxge fn: %d vpath: %d Tx MSI-X: %d" , + vdev->ndev->name, pci_fun, vp_idx, + vdev->entries[intr_cnt].entry); + ret =request_irq ( + vdev->entries[intr_cnt].vector, + vxge_tx_msix_handle,0 , + vdev->desc[intr_cnt], + &vdev->vpaths[vp_idx].fifo); + vdev->vxge_entries[intr_cnt].arg = + &vdev->vpaths[vp_idx].fifo; + irq_req =1 ; +break ; +case 1 : +sprintf (vdev->desc[intr_cnt], +"%s:vxge fn: %d vpath: %d Rx MSI-X: %d" , + vdev->ndev->name, pci_fun, vp_idx, + vdev->entries[intr_cnt].entry); + ret =request_irq ( + vdev->entries[intr_cnt].vector, + vdev->config.napi_enable ? + vxge_rx_msix_napi_handle : + vxge_rx_msix_handle,0 , + vdev->desc[intr_cnt], + &vdev->vpaths[vp_idx].ring); + vdev->vxge_entries[intr_cnt].arg = + &vdev->vpaths[vp_idx].ring; + irq_req =1 ; +break ; + } + +if (ret) { +vxge_debug_init (VXGE_ERR , +"%s: MSIX - %d Registration failed" , + vdev->ndev->name, intr_cnt); +vxge_rem_msix_isr (vdev); +if (function_mode == +VXGE_HW_FUNCTION_MODE_MULTI_FUNCTION ) +return VXGE_HW_FAIL ; +else { +vxge_hw_device_set_intr_type ( + vdev->devh, +VXGE_HW_INTR_MODE_IRQLINE ); + vdev->config.intr_type =INTA ; +vxge_debug_init (VXGE_ERR , +"%s: Defaulting to INTA" + , vdev->ndev->name); +vxge_close_vpaths (vdev,1 ); + vdev->no_of_vpath =1 ; + vdev->stats.vpaths_open =1 ; + goto INTA_MODE; + } + } + +if (irq_req) { +/* We requested for this msix interrupt */ + vdev->vxge_entries[intr_cnt].in_use =1 ; +vxge_hw_vpath_msix_unmask ( + vdev->vpaths[vp_idx].handle, + intr_idx); + intr_cnt++; + } +
Lines of this file are not shown. View the raw message.
drivers/net/vxge/vxge-main.h +623 -0
623 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.