[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20080603101620.7a64e14c@extreme>
Date: Tue, 3 Jun 2008 10:16:20 -0700
From: Stephen Hemminger <shemminger@...tta.com>
To: "Subbu Seetharaman" <subbus@...verengines.com>
Cc: netdev@...r.kernel.org
Subject: Re: [PATCH 2/12] BE NIC driver - interrupt, ethtool, stack i/f
functions
On Tue, 03 Jun 2008 02:39:11 -0700
"Subbu Seetharaman" <subbus@...verengines.com> wrote:
> Signed-off-by: Subbu Seetharaman <subbus@...verengines.com>
> ---
> drivers/net/benet/be_ethtool.c | 337 ++++++++++++++++
> drivers/net/benet/be_int.c | 843 ++++++++++++++++++++++++++++++++++++++++
> drivers/net/benet/be_netif.c | 693 +++++++++++++++++++++++++++++++++
> 3 files changed, 1873 insertions(+), 0 deletions(-)
> create mode 100644 drivers/net/benet/be_ethtool.c
> create mode 100644 drivers/net/benet/be_int.c
> create mode 100644 drivers/net/benet/be_netif.c
>
> diff --git a/drivers/net/benet/be_ethtool.c b/drivers/net/benet/be_ethtool.c
> new file mode 100644
> index 0000000..0841580
> --- /dev/null
> +++ b/drivers/net/benet/be_ethtool.c
> @@ -0,0 +1,337 @@
> +/*
> + * Copyright (C) 2005 - 2008 ServerEngines
> + * All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation. The full GNU General
> + * Public License is included in this distribution in the file called COPYING.
> + *
> + * Contact Information:
> + * linux-drivers@...verengines.com
> + *
> + * ServerEngines
> + * 209 N. Fair Oaks Ave
> + * Sunnyvale, CA 94085
> + */
> +/*
> + * be_ethtool.c
> + *
> + * This file contains various functions that ethtool can use
> + * to talk to the driver and the BE H/W.
> + */
> +
> +#include <linux/pci.h>
> +#include "benet.h"
> +
> +#include <linux/ethtool.h>
> +
> +static const char benet_gstrings_stats[][ETH_GSTRING_LEN] = {
> +/* net_device_stats */
> + "rx_packets",
> + "tx_packets",
> + "rx_bytes",
> + "tx_bytes",
> + "rx_errors",
> + "tx_errors",
> + "rx_dropped",
> + "tx_dropped",
> + "multicast",
> + "collisions",
> + "rx_length_errors",
> + "rx_over_errors",
> + "rx_crc_errors",
> + "rx_frame_errors",
> + "rx_fifo_errors",
> + "rx_missed_errors",
> + "tx_aborted_errors",
> + "tx_carrier_errors",
> + "tx_fifo_errors",
> + "tx_heartbeat_errors",
> + "tx_window_errors",
> + "rx_compressed",
> + "tc_compressed",
> +/* BE driver Stats */
> + "bes_tx_reqs",
> + "bes_tx_fails",
> + "bes_fwd_reqs",
> + "bes_tx_wrbs",
> + "bes_interrupts",
> + "bes_events",
> + "bes_tx_events",
> + "bes_ucrx_events",
> + "bes_bcrx_events",
> + "bes_tx_compl",
> + "bes_ucrx_compl",
> + "bes_bcrx_compl",
> + "bes_ethrx_post_fail",
> + "bes_802_3_dropped_frames",
> + "bes_802_3_malformed_frames",
> + "bes_rx_misc_pkts",
> + "bes_eth_tx_rate",
> + "bes_eth_rx_rate",
> + "Num Packets collected",
> + "Num Times Flushed",
> +};
> +
> +#define NET_DEV_STATS_LEN \
> + (sizeof(struct net_device_stats)/sizeof(unsigned long))
> +#define BENET_STATS_LEN ARRAY_SIZE(benet_gstrings_stats)
> +
> +static void
> +be_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
> +{
> + struct bni_net_object *pnob = netdev->priv;
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> +
> + strncpy(drvinfo->driver, be_driver_name, 32);
> + strncpy(drvinfo->version, be_drvr_ver, 32);
> + strncpy(drvinfo->fw_version, be_fw_ver, 32);
> + strcpy(drvinfo->bus_info, pci_name(adapter->pdev));
> + drvinfo->testinfo_len = 0;
> + drvinfo->regdump_len = 0;
> + drvinfo->eedump_len = 0;
> +}
> +
> +static int
> +be_get_coalesce(struct net_device *netdev,
> + struct ethtool_coalesce *coalesce)
> +{
> + struct bni_net_object *pnob = netdev->priv;
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> +
> + coalesce->rx_max_coalesced_frames = adapter->max_rx_coal;
> +
> + coalesce->rx_coalesce_usecs = adapter->cur_eqd;
> + coalesce->rx_coalesce_usecs_high = adapter->max_eqd;
> + coalesce->rx_coalesce_usecs_low = adapter->min_eqd;
> +
> + coalesce->tx_coalesce_usecs = adapter->cur_eqd;
> + coalesce->tx_coalesce_usecs_high = adapter->max_eqd;
> + coalesce->tx_coalesce_usecs_low = adapter->min_eqd;
> +
> + coalesce->use_adaptive_rx_coalesce = adapter->enable_aic;
> + coalesce->use_adaptive_tx_coalesce = adapter->enable_aic;
> +
> + return 0;
> +}
> +
> +/*
> + * This routine is used to set interrup coalescing delay *as well as*
> + * the number of pkts to coalesce for LRO.
> + */
> +static int
> +be_set_coalesce(struct net_device *netdev,
> + struct ethtool_coalesce *coalesce)
> +{
> + struct bni_net_object *pnob = netdev->priv;
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> + u32 max, min, cur;
> +
> + adapter->max_rx_coal = coalesce->rx_max_coalesced_frames;
> + if (adapter->max_rx_coal >= BE_LRO_MAX_PKTS)
> + adapter->max_rx_coal = BE_LRO_MAX_PKTS;
> +
> + if (adapter->enable_aic == 0 &&
> + coalesce->use_adaptive_rx_coalesce == 1) {
> + /* if AIC is being turned on now, start with an EQD of 0 */
> + adapter->cur_eqd = 0;
> + }
> + adapter->enable_aic = coalesce->use_adaptive_rx_coalesce;
> +
> + /* round off to nearest multiple of 8 */
> + max = (((coalesce->rx_coalesce_usecs_high + 4) >> 3) << 3);
> + min = (((coalesce->rx_coalesce_usecs_low + 4) >> 3) << 3);
> + cur = (((coalesce->rx_coalesce_usecs + 4) >> 3) << 3);
> +
> + if (adapter->enable_aic) {
> + /* accept low and high if AIC is enabled */
> + if (max > MAX_EQD)
> + min = MAX_EQD;
> + if (min > max)
> + min = max;
> + adapter->max_eqd = max;
> + adapter->min_eqd = min;
> + if (adapter->cur_eqd > max)
> + adapter->cur_eqd = max;
> + if (adapter->cur_eqd < min)
> + adapter->cur_eqd = min;
> + } else {
> + /* accept specified coalesce_usecs only if AIC is disabled */
> + if (cur > MAX_EQD)
> + cur = MAX_EQD;
> + if (bni_change_eqd(pnob, cur) == BE_SUCCESS)
> + adapter->cur_eqd = cur;
> + }
> +
> + return 0;
> +}
> +
> +static u32 be_get_rx_csum(struct net_device *netdev)
> +{
> + struct bni_net_object *pnob = netdev->priv;
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> + return adapter->rx_csum;
> +}
> +
> +static int be_set_rx_csum(struct net_device *netdev, uint32_t data)
> +{
> + struct bni_net_object *pnob = netdev->priv;
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> + if (data)
> + adapter->rx_csum = 1;
> + else
> + adapter->rx_csum = 0;
> +
> + return 0;
> +}
> +
> +static void
> +be_get_strings(struct net_device *netdev, uint32_t stringset,
> + uint8_t *data)
> +{
> +
> + switch (stringset) {
> + case ETH_SS_STATS:
> + memcpy(data, *benet_gstrings_stats,
> + sizeof(benet_gstrings_stats));
> + break;
> + }
> +}
> +
> +static int be_get_stats_count(struct net_device *netdev)
> +{
> + return BENET_STATS_LEN;
> +}
> +
> +static void
> +be_get_ethtool_stats(struct net_device *netdev,
> + struct ethtool_stats *stats, uint64_t *data)
> +{
> + struct bni_net_object *pnob = netdev->priv;
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> + int i;
> +
> + benet_get_stats(netdev);
> +
> + for (i = 0; i <= NET_DEV_STATS_LEN; i++)
> + data[i] = ((unsigned long *)&adapter->benet_stats)[i];
> +
> + data[i] = adapter->be_stat.bes_tx_reqs;
You can use dev->stats rather than adapter->be_stat.
> + data[i++] = adapter->be_stat.bes_tx_fails;
> + data[i++] = adapter->be_stat.bes_fwd_reqs;
> + data[i++] = adapter->be_stat.bes_tx_wrbs;
> +
> + data[i++] = adapter->be_stat.bes_ints;
> + data[i++] = adapter->be_stat.bes_events;
> + data[i++] = adapter->be_stat.bes_tx_events;
> + data[i++] = adapter->be_stat.bes_ucrx_events;
> + data[i++] = adapter->be_stat.bes_bcrx_events;
> + data[i++] = adapter->be_stat.bes_tx_compl;
> + data[i++] = adapter->be_stat.bes_ucrx_compl;
> + data[i++] = adapter->be_stat.bes_bcrx_compl;
> + data[i++] = adapter->be_stat.bes_ethrx_post_fail;
> + data[i++] = adapter->be_stat.bes_802_3_dropped_frames;
> + data[i++] = adapter->be_stat.bes_802_3_malformed_frames;
> + data[i++] = adapter->be_stat.bes_rx_misc_pkts;
> + data[i++] = adapter->be_stat.bes_eth_tx_rate;
> + data[i++] = adapter->be_stat.bes_eth_rx_rate;
> + data[i++] = adapter->be_stat.bes_rx_coal;
> + data[i++] = adapter->be_stat.bes_rx_flush;
> +
> +}
> +
> +/* Get the Ring parameters from the pnob */
> +static void
> +be_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring)
> +{
> + struct bni_net_object *pnob = netdev->priv;
> +
> + /* Pre Set Maxims */
> + ring->rx_max_pending = pnob->rx_q_len;
> + ring->rx_mini_max_pending = ring->rx_mini_max_pending;
> + ring->rx_jumbo_max_pending = ring->rx_jumbo_max_pending;
> + ring->tx_max_pending = pnob->tx_q_len;
> +
> + /* Current hardware Settings */
> + ring->rx_pending = atomic_read(&pnob->rx_q_posted);
> + ring->rx_mini_pending = ring->rx_mini_pending;
> + ring->rx_jumbo_pending = ring->rx_jumbo_pending;
> + ring->tx_pending = atomic_read(&pnob->tx_q_used);
> +
> +}
> +
> +static void
> +be_get_pauseparam(struct net_device *netdev,
> + struct ethtool_pauseparam *ecmd)
> +{
> + struct bni_net_object *pnob = netdev->priv;
> + bool rxfc = FALSE;
> + bool txfc = FALSE;
> + BESTATUS status;
> +
> + status = bni_get_flow_ctl(&pnob->fn_obj, &txfc, &rxfc);
> + if (status != BE_SUCCESS)
> + printk(KERN_WARNING "Unable to get pause frame settings\n");
> +
> + if (txfc == TRUE)
> + ecmd->tx_pause = 1;
> + else
> + ecmd->tx_pause = 0;
> +
> + if (rxfc == TRUE)
> + ecmd->rx_pause = 1;
> + else
> + ecmd->rx_pause = 0;
> +
> + /* Always setting autoneg to TRUE */
> + ecmd->autoneg = 1;
> +}
> +
> +static int
> +be_set_pauseparam(struct net_device *netdev,
> + struct ethtool_pauseparam *ecmd)
> +{
> + struct bni_net_object *pnob = netdev->priv;
> + bool txfc = FALSE;
> + bool rxfc = FALSE;
> + BESTATUS status;
> +
> + if (ecmd->tx_pause)
> + txfc = TRUE;
> + else
> + txfc = FALSE;
> +
> + if (ecmd->rx_pause)
> + rxfc = TRUE;
> + else
> + rxfc = FALSE;
> +
> + status = bni_set_flow_ctll(&pnob->fn_obj, txfc, rxfc);
> + if (status != BE_SUCCESS) {
> + printk(KERN_ERR "Unable to set pause frame settings\n");
> + return -1;
> + }
> + return 0;
> +}
> +
> +struct ethtool_ops be_ethtool_ops = {
> + .get_drvinfo = be_get_drvinfo,
> + .get_link = ethtool_op_get_link,
> + .get_coalesce = be_get_coalesce,
> + .set_coalesce = be_set_coalesce,
> + .get_ringparam = be_get_ringparam,
> + .get_pauseparam = be_get_pauseparam,
> + .set_pauseparam = be_set_pauseparam,
> + .get_rx_csum = be_get_rx_csum,
> + .set_rx_csum = be_set_rx_csum,
> + .get_tx_csum = ethtool_op_get_tx_csum,
> + .set_tx_csum = ethtool_op_set_tx_csum,
> + .get_sg = ethtool_op_get_sg,
> + .set_sg = ethtool_op_set_sg,
> + .get_tso = ethtool_op_get_tso,
> + .set_tso = ethtool_op_set_tso,
> + .get_strings = be_get_strings,
> + .get_stats_count = be_get_stats_count,
> + .get_ethtool_stats = be_get_ethtool_stats,
> +};
> diff --git a/drivers/net/benet/be_int.c b/drivers/net/benet/be_int.c
> new file mode 100644
> index 0000000..1ec2a61
> --- /dev/null
> +++ b/drivers/net/benet/be_int.c
> @@ -0,0 +1,843 @@
> +/*
> + * Copyright (C) 2005 - 2008 ServerEngines
> + * All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation. The full GNU General
> + * Public License is included in this distribution in the file called COPYING.
> + *
> + * Contact Information:
> + * linux-drivers@...verengines.com
> + *
> + * ServerEngines
> + * 209 N. Fair Oaks Ave
> + * Sunnyvale, CA 94085
> + */
> +#include <linux/pci.h>
> +#include <linux/if_vlan.h>
> +
> +#include <linux/inet_lro.h>
> +
> +#include "benet.h"
> +
> +/* number of bytes of RX frame that are copied to skb->data */
> +#define BE_HDR_LEN 64
> +
> +#ifdef CONFIG_BENET_NAPI
> +#define NETIF_RX(skb) netif_receive_skb(skb)
> +#define VLAN_ACCEL_RX(skb, pnob, vt) \
> + vlan_hwaccel_rx(skb, OSM_NOB(pnob)->vlan_grp, vt)
> +#else
> +#define NETIF_RX(skb) netif_rx(skb)
> +#define VLAN_ACCEL_RX(skb, pnob, vt) \
> + vlan_hwaccel_rx(skb, OSM_NOB(pnob)->vlan_grp, vt)
> +#endif
> +
> +/*
> + * adds additional receive frags indicated by BE starting from given
> + * frag index (fi) to specified skb's frag list
> + */
> +static void
> +add_skb_frags(struct bni_net_object *pnob, struct sk_buff *skb,
> + u32 nresid, u32 fi)
> +{
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> + u32 sk_frag_idx, n;
> + struct be_rx_page_info *rx_page_info;
> + u32 frag_sz = pnob->rx_buf_size;
> +
> + sk_frag_idx = skb_shinfo(skb)->nr_frags;
> + while (nresid) {
> + index_advance(&fi, pnob->rx_q_len);
> +
> + rx_page_info = (struct be_rx_page_info *)pnob->rx_ctxt[fi];
> + pnob->rx_ctxt[fi] = (void *)NULL;
> + if ((rx_page_info->page_offset) ||
> + (OSM_NOB(pnob)->rx_pg_shared == FALSE)) {
> + pci_unmap_page(adapter->pdev,
> + pci_unmap_addr(rx_page_info, bus),
> + frag_sz, PCI_DMA_FROMDEVICE);
> + }
> +
> + n = min(nresid, frag_sz);
> + skb_shinfo(skb)->frags[sk_frag_idx].page = rx_page_info->page;
> + skb_shinfo(skb)->frags[sk_frag_idx].page_offset
> + = rx_page_info->page_offset;
> + skb_shinfo(skb)->frags[sk_frag_idx].size = n;
> +
> + sk_frag_idx++;
> + skb->len += n;
> + skb->data_len += n;
> + skb_shinfo(skb)->nr_frags++;
> + nresid -= n;
> +
> + memset(rx_page_info, 0, sizeof(struct be_rx_page_info));
> + atomic_dec(&pnob->rx_q_posted);
> + }
> +}
> +
> +/*
> + * This function processes incoming nic packets over various Rx queues.
> + * This function takes the adapter, the current Rx status descriptor
> + * entry and the Rx completion queue ID as argument.
> + */
> +static inline int process_nic_rx_completion(struct bni_net_object *pnob,
> + struct ETH_RX_COMPL_AMAP *rxcp)
> +{
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> + struct sk_buff *skb;
> + int udpcksm, tcpcksm;
> + int n, fi;
> + u32 nresid;
> + u32 frag_sz = pnob->rx_buf_size;
> + u8 *va;
> + struct be_rx_page_info *rx_page_info;
> + u32 numfrags, vtp, vtm, vlan_tag, pktsize;
> +
> + fi = AMAP_GET_BITS_PTR(ETH_RX_COMPL, fragndx, rxcp);
> + BUG_ON(fi >= (int)pnob->rx_q_len);
> + BUG_ON(fi < 0);
> +
> + rx_page_info = (struct be_rx_page_info *)pnob->rx_ctxt[fi];
> + BUG_ON(!rx_page_info->page);
> + pnob->rx_ctxt[fi] = NULL;
> +
> + /*
> + * If one page is used per fragment or if this is the second half of
> + * of the page, unmap the page here
> + */
> + if ((rx_page_info->page_offset) ||
> + (OSM_NOB(pnob)->rx_pg_shared == FALSE)) {
> + pci_unmap_page(adapter->pdev,
> + pci_unmap_addr(rx_page_info, bus), frag_sz,
> + PCI_DMA_FROMDEVICE);
> + }
> +
> + atomic_dec(&pnob->rx_q_posted);
> + udpcksm = AMAP_GET_BITS_PTR(ETH_RX_COMPL, udpcksm, rxcp);
> + tcpcksm = AMAP_GET_BITS_PTR(ETH_RX_COMPL, tcpcksm, rxcp);
> + pktsize = AMAP_GET_BITS_PTR(ETH_RX_COMPL, pktsize, rxcp);
> + /*
> + * get rid of RX flush completions first.
> + */
> + if ((tcpcksm) && (udpcksm) && (pktsize == 32)) {
> + put_page(rx_page_info->page);
> + memset(rx_page_info, 0, sizeof(struct be_rx_page_info));
> + return 0;
> + }
> + skb = alloc_skb(BE_HDR_LEN + 16, GFP_ATOMIC);
Use netdev_allocskb, it addes padding necessary for bridging etc.
> + if (skb == NULL) {
> + printk(KERN_WARNING "alloc_skb() failed\n");
> + put_page(rx_page_info->page);
> + memset(rx_page_info, 0, sizeof(struct be_rx_page_info));
> + goto free_frags;
> + }
> + skb_reserve(skb, NET_IP_ALIGN);
> +
> + skb->dev = OSM_NOB(pnob)->netdev;
> +
> + n = min(pktsize, frag_sz);
> +
> + va = page_address(rx_page_info->page) + rx_page_info->page_offset;
> + prefetch(va);
> +
> + skb->len = skb->data_len = n;
> + if (n <= BE_HDR_LEN) {
> + memcpy(skb->data, va, n);
> + put_page(rx_page_info->page);
> + skb->data_len -= n;
> + skb->tail += n;
> + } else {
> +
> + /* Setup the SKB with page buffer information */
> + skb_shinfo(skb)->frags[0].page = rx_page_info->page;
> + skb_shinfo(skb)->nr_frags++;
> +
> + /* Copy the header into the skb_data */
> + memcpy(skb->data, va, BE_HDR_LEN);
> + skb_shinfo(skb)->frags[0].page_offset =
> + rx_page_info->page_offset + BE_HDR_LEN;
> + skb_shinfo(skb)->frags[0].size = n - BE_HDR_LEN;
> + skb->data_len -= BE_HDR_LEN;
> + skb->tail += BE_HDR_LEN;
> + }
> + memset(rx_page_info, 0, sizeof(struct be_rx_page_info));
> + nresid = pktsize - n;
> +
> + skb->protocol = eth_type_trans(skb, OSM_NOB(pnob)->netdev);
> +
> + if ((tcpcksm || udpcksm) && adapter->rx_csum)
> + skb->ip_summed = CHECKSUM_UNNECESSARY;
> + else
> + skb->ip_summed = CHECKSUM_NONE;
> + /*
> + * if we have more bytes left, the frame has been
> + * given to us in multiple fragments. This happens
> + * with Jumbo frames. Add the remaining fragments to
> + * skb->frags[] array.
> + */
> + if (nresid)
> + add_skb_frags(pnob, skb, nresid, fi);
> +
> + /* update the the true size of the skb. */
> + skb->truesize = skb->len + sizeof(struct sk_buff);
> +
> + /*
> + * If a 802.3 frame or 802.2 LLC frame
> + * (i.e) contains length field in MAC Hdr
> + * and frame len is greater than 64 bytes
> + */
> + if (((skb->protocol == ntohs(ETH_P_802_2)) ||
> + (skb->protocol == ntohs(ETH_P_802_3)))
> + && (pktsize > BE_HDR_LEN)) {
> + /*
> + * If the length given in Mac Hdr is less than frame size
> + * Erraneous frame, Drop it
> + */
> + if ((ntohs(*(u16 *) (va + 12)) + ETH_HLEN) < pktsize) {
> + /* Increment Non Ether type II frames dropped */
> + adapter->be_stat.bes_802_3_dropped_frames++;
> +
> + kfree_skb(skb);
> + return 0;
> + }
> + /*
> + * else if the length given in Mac Hdr is greater than
> + * frame size, should not be seeing this sort of frames
> + * dump the pkt and pass to stack
> + */
> + else if ((ntohs(*(u16 *) (va + 12)) + ETH_HLEN) > pktsize) {
> + /* Increment Non Ether type II frames malformed */
> + adapter->be_stat.bes_802_3_malformed_frames++;
> + }
> + }
> +
> + vtp = AMAP_GET_BITS_PTR(ETH_RX_COMPL, vtp, rxcp);
> + vtm = AMAP_GET_BITS_PTR(ETH_RX_COMPL, vtm, rxcp);
> + if (vtp && vtm) {
> + /* Vlan tag present in pkt and BE found
> + * that the tag matched an entry in VLAN table
> + */
> + if (!(OSM_NOB(pnob)->vlan_grp) ||
> + OSM_NOB(pnob)->num_vlans == 0) {
> + /* But we have no VLANs configured.
> + * This should never happen. Drop the packet.
> + */
> + printk(KERN_ERR
> + "BladeEngine: Unexpected vlan tagged packet\n");
> + kfree_skb(skb);
> + return 0;
> + }
> + /* pass the VLAN packet to stack */
> + vlan_tag = AMAP_GET_BITS_PTR(ETH_RX_COMPL, vlan_tag, rxcp);
> + VLAN_ACCEL_RX(skb, pnob, be16_to_cpu(vlan_tag));
> +
> + } else {
> + NETIF_RX(skb);
> + }
> +
> + return 0;
> +free_frags:
> + /* free all frags associated with the current rxcp */
> + numfrags = AMAP_GET_BITS_PTR(ETH_RX_COMPL, numfrags, rxcp);
> + while (numfrags-- > 1) {
> + index_advance(&fi, pnob->rx_q_len);
> +
> + rx_page_info = (struct be_rx_page_info *)
> + pnob->rx_ctxt[fi];
> + pnob->rx_ctxt[fi] = (void *)NULL;
> + if ((rx_page_info->page_offset) ||
> + (OSM_NOB(pnob)->rx_pg_shared == FALSE)) {
> + pci_unmap_page(adapter->pdev,
> + pci_unmap_addr(rx_page_info, bus),
> + frag_sz, PCI_DMA_FROMDEVICE);
> + }
> +
> + put_page(rx_page_info->page);
> + memset(rx_page_info, 0, sizeof(struct be_rx_page_info));
> + atomic_dec(&pnob->rx_q_posted);
> + }
> + return -ENOMEM;
> +}
> +
> +static void process_nic_rx_completion_lro(struct bni_net_object *pnob,
> + struct ETH_RX_COMPL_AMAP *rxcp)
> +{
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> + struct skb_frag_struct rx_frags[BE_MAX_FRAGS_PER_FRAME];
> + unsigned int udpcksm, tcpcksm;
> + u32 numfrags, vlanf, vtm, vlan_tag, nresid;
> + u16 vlant;
> + unsigned int fi, idx, n;
> + struct be_rx_page_info *rx_page_info;
> + u32 frag_sz = pnob->rx_buf_size, pktsize;
> + bool rx_coal = (adapter->max_rx_coal <= 1) ? 0 : 1;
> + u8 err, *va;
> + __wsum csum = 0;
> +
> + if (AMAP_GET_BITS_PTR(ETH_RX_COMPL, ipsec, rxcp)) {
> + /* Drop the pkt and move to the next completion. */
> + adapter->be_stat.bes_rx_misc_pkts++;
> + return;
> + }
> + err = AMAP_GET_BITS_PTR(ETH_RX_COMPL, err, rxcp);
> + if (err || !rx_coal) {
> + /* We won't coalesce Rx pkts if the err bit set.
> + * take the path of normal completion processing */
> + process_nic_rx_completion(pnob, rxcp);
> + return;
> + }
> +
> + fi = AMAP_GET_BITS_PTR(ETH_RX_COMPL, fragndx, rxcp);
> + BUG_ON(fi >= (int)pnob->rx_q_len);
> + BUG_ON(fi < 0);
> + rx_page_info = (struct be_rx_page_info *)pnob->rx_ctxt[fi];
> + BUG_ON(!rx_page_info->page);
> + pnob->rx_ctxt[fi] = (void *)NULL;
> + /* If one page is used per fragment or if this is the
> + * second half of the page, unmap the page here
> + */
> + if ((rx_page_info->page_offset) ||
> + (OSM_NOB(pnob)->rx_pg_shared == FALSE)) {
> + pci_unmap_page(adapter->pdev,
> + pci_unmap_addr(rx_page_info, bus),
> + frag_sz, PCI_DMA_FROMDEVICE);
> + }
> +
> + numfrags = AMAP_GET_BITS_PTR(ETH_RX_COMPL, numfrags, rxcp);
> + udpcksm = AMAP_GET_BITS_PTR(ETH_RX_COMPL, udpcksm, rxcp);
> + tcpcksm = AMAP_GET_BITS_PTR(ETH_RX_COMPL, tcpcksm, rxcp);
> + vlan_tag = AMAP_GET_BITS_PTR(ETH_RX_COMPL, vlan_tag, rxcp);
> + vlant = be16_to_cpu(vlan_tag);
> + vlanf = AMAP_GET_BITS_PTR(ETH_RX_COMPL, vtp, rxcp);
> + vtm = AMAP_GET_BITS_PTR(ETH_RX_COMPL, vtm, rxcp);
> + pktsize = AMAP_GET_BITS_PTR(ETH_RX_COMPL, pktsize, rxcp);
> +
> + atomic_dec(&pnob->rx_q_posted);
> +
> + if (tcpcksm && udpcksm && pktsize == 32) {
> + /* flush completion entries */
> + put_page(rx_page_info->page);
> + memset(rx_page_info, 0, sizeof(struct be_rx_page_info));
> + return;
> + }
> + /* Only one of udpcksum and tcpcksum can be set */
> + BUG_ON(udpcksm && tcpcksm);
> +
> + /* jumbo frames could come in multiple fragments */
> + BUG_ON(numfrags != ((pktsize + (frag_sz - 1)) / frag_sz));
> + n = min(pktsize, frag_sz);
> + nresid = pktsize - n; /* will be useful for jumbo pkts */
> + idx = 0;
> +
> + va = page_address(rx_page_info->page) + rx_page_info->page_offset;
> + prefetch(va);
> + rx_frags[idx].page = rx_page_info->page;
> + rx_frags[idx].page_offset = (rx_page_info->page_offset);
> + rx_frags[idx].size = n;
> + memset(rx_page_info, 0, sizeof(struct be_rx_page_info));
> +
> + /* If we got multiple fragments, we have more data. */
> + while (nresid) {
> + idx++;
> + index_advance(&fi, pnob->rx_q_len);
> +
> + rx_page_info = (struct be_rx_page_info *)pnob->rx_ctxt[fi];
> + pnob->rx_ctxt[fi] = (void *)NULL;
> + if ((rx_page_info->page_offset) ||
> + (OSM_NOB(pnob)->rx_pg_shared == FALSE)) {
> + pci_unmap_page(adapter->pdev,
> + pci_unmap_addr(rx_page_info, bus),
> + frag_sz, PCI_DMA_FROMDEVICE);
> + }
> +
> + n = min(nresid, frag_sz);
> + rx_frags[idx].page = rx_page_info->page;
> + rx_frags[idx].page_offset = (rx_page_info->page_offset);
> + rx_frags[idx].size = n;
> +
> + nresid -= n;
> + memset(rx_page_info, 0, sizeof(struct be_rx_page_info));
> + atomic_dec(&pnob->rx_q_posted);
> + }
> +
> + if (likely(!(vlanf && vtm))) {
> + lro_receive_frags(&OSM_NOB(pnob)->lro_mgr, rx_frags,
> + pktsize, pktsize,
> + (void *)(unsigned long)csum, csum);
> + } else {
> + /* Vlan tag present in pkt and BE found
> + * that the tag matched an entry in VLAN table
> + */
> + if (unlikely(!(OSM_NOB(pnob)->vlan_grp) ||
> + OSM_NOB(pnob)->num_vlans == 0)) {
> + /* But we have no VLANs configured.
> + * This should never happen. Drop the packet.
> + */
> + printk(KERN_ERR "BladeEngine: Unexpected"
> + " vlan tagged packet\n");
> + return;
> + }
> + /* pass the VLAN packet to stack */
> + lro_vlan_hwaccel_receive_frags(&OSM_NOB(pnob)->lro_mgr,
> + rx_frags, pktsize, pktsize,
> + OSM_NOB(pnob)->vlan_grp, vlant,
> + (void *)(unsigned long)csum,
> + csum);
> + }
> +
> + adapter->be_stat.bes_rx_coal++;
> +}
> +
> +static void process_ucast_rx_completion(struct bni_net_object *pnob)
> +{
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> + struct ETH_RX_COMPL_AMAP *rxcp;
> + u32 nc = 0;
> + unsigned int pktsize;
> + int rearm = 1;
> +
> +#ifdef CONFIG_BENET_NAPI
> + if (OSM_NOB(pnob)->work_quota == 0)
> + /*
> + * We were called from process_events without quota
> + * because the device is not open yet. Give ourselves
> + * a large quota.
> + */
> + OSM_NOB(pnob)->work_quota = 128;
> + while ((OSM_NOB(pnob)->work_quota) && (rxcp = bni_get_ucrx_cmpl(pnob)))
> +#else
> + while ((rxcp = bni_get_ucrx_cmpl(pnob)))
> +#endif
> + {
> + prefetch(rxcp);
> + pktsize = AMAP_GET_BITS_PTR(ETH_RX_COMPL, pktsize, rxcp);
> + process_nic_rx_completion_lro(pnob, rxcp);
> + adapter->eth_rx_bytes += pktsize;
> +
> + /* RX rate calculation. */
> + update_rx_rate(adapter);
> + nc++; /* number of cq entries that we have processed */
> + adapter->be_stat.bes_ucrx_compl++;
> +#ifdef CONFIG_BENET_NAPI
> + OSM_NOB(pnob)->work_quota--;
> +#endif
> + }
> + if (likely(adapter->max_rx_coal > 1)) {
> + adapter->be_stat.bes_rx_flush++;
> + lro_flush_all(&OSM_NOB(pnob)->lro_mgr);
> + }
> +
> +#ifdef CONFIG_BENET_NAPI
> + if (OSM_NOB(pnob)->work_quota == 0) {
> + /* we ran out of work budget */
> + rearm = 0;
> + } else {
> + /* we finished all work. We are in interrupt mode */
> + rearm = 1;
> + }
> +#endif
> + /*
> + * we call notfiy completions even when nc is zero, since
> + * rearm value needs to take effect
> + */
> + bni_notify_cmpl(pnob, nc, pnob->ucrx_cq_id, rearm);
> +}
> +
> +/*
> + * Process broadcast and multicat completions
> + */
> +static void process_bcast_rx_completion(struct bni_net_object *pnob)
> +{
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> + struct ETH_RX_COMPL_AMAP *rxcp;
> +
> + u32 nc = 0;
> +
> + adapter->be_stat.bes_bcrx_events++;
> +
> + while ((rxcp = (bni_get_bcrx_cmpl(pnob)))) {
> + process_nic_rx_completion(pnob, rxcp);
> + nc++;
> + adapter->be_stat.bes_bcrx_compl++;
> + }
> + bni_notify_cmpl(pnob, nc, pnob->bcrx_cq_id, 1);
> +
> +}
> +
> +/* Process NIC TX COMPLETIONS */
> +static void process_nic_tx_completions(struct bni_net_object *pnob)
> +{
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> + struct ETH_TX_COMPL_AMAP *txcp; /* Eth Tx completion entry */
> + struct net_device *netdev = (struct net_device *)
> + OSM_NOB(pnob)->netdev;
> + int num_processed = 0, cur_index, tx_wrbs_completed = 0, exp_index;
> + struct sk_buff *skb;
> + u64 busaddr, pa, pa_lo, pa_hi;
> + struct ETH_WRB_AMAP *curr_wrb;
> + u32 frag_len, wrb_index;
> +
> + adapter->be_stat.bes_tx_events++;
> + /*
> + * there is no need to take an SMP lock here since currently
> + * we have only one instance of the tasklet that does completion
> + * processing.
> + */
> +
> + /* process each valid completion entry */
> + while ((txcp = bni_get_tx_cmpl(pnob))) {
> + /* Get the expected completion index */
> + exp_index = (pnob->tx_q_tl +
> + ((int)pnob->tx_ctxt[pnob->tx_q_tl] - 1))
> + & (pnob->tx_q_len - 1);
> + pnob->tx_ctxt[pnob->tx_q_tl] = NULL;
> + wrb_index = AMAP_GET_BITS_PTR(ETH_TX_COMPL, wrb_index, txcp);
> + if (exp_index != wrb_index) {
> + printk(KERN_ERR "Expected Wrb Index (=%d) does not"
> + "match with completion Wrb Index (=%d)\n",
> + exp_index, wrb_index);
> + }
> + /*
> + * All reqs in the TX ring from the current tail index upto
> + * the one indicated in this completion entry's wrb_index
> + * are now completed.
> + */
> + do {
> + cur_index = pnob->tx_q_tl;
> +
> + curr_wrb = &pnob->tx_q[cur_index];
> + pa_hi = AMAP_GET_BITS_PTR(ETH_WRB, frag_pa_hi,
> + curr_wrb);
> + pa_lo = AMAP_GET_BITS_PTR(ETH_WRB, frag_pa_lo,
> + curr_wrb);
> + frag_len = AMAP_GET_BITS_PTR(ETH_WRB, frag_len,
> + curr_wrb);
> + busaddr = (pa_hi << 32) | pa_lo;
> + if (busaddr != 0) {
> + pa = le64_to_cpu(busaddr);
> + pci_unmap_single(adapter->pdev, pa,
> + frag_len, PCI_DMA_TODEVICE);
> + }
> + /*
> + * this Tx request is complete. The OSM context
> + * we stored is the skb address. free this skb.
> + */
> + skb = (struct sk_buff *)pnob->tx_ctxt[cur_index];
> + if (skb) {
> + unsigned int j;
> +
> + for (j = 0; j < skb_shinfo(skb)->nr_frags;
> + j++) {
> + struct skb_frag_struct *frag;
> + frag = &skb_shinfo(skb)->frags[j];
> + pci_unmap_page(adapter->pdev,
> + (ulong) frag->page,
> + frag->size,
> + PCI_DMA_TODEVICE);
> + }
> + kfree_skb(skb);
> + pnob->tx_ctxt[cur_index] = NULL;
> + }
> +
> + tx_wrbs_completed++;
> + bni_adv_txq_tl(pnob);
> + } while (cur_index != wrb_index);
> +
> + num_processed++;
> + adapter->be_stat.bes_tx_compl++;
> + }
> + atomic_sub(tx_wrbs_completed, &pnob->tx_q_used);
> + bni_notify_cmpl(pnob, num_processed, pnob->tx_cq_id, 1);
> + /*
> + * We got Tx completions and have usable WRBs.
> + * If the netdev's queue has been stopped
> + * because we had run out of WRBs, wake it now.
> + */
> + spin_lock(&adapter->txq_lock);
> + if (netif_queue_stopped(netdev)
> + && atomic_read(&pnob->tx_q_used) < pnob->tx_q_len / 2) {
> + netif_wake_queue(netdev);
> + }
> + spin_unlock(&adapter->txq_lock);
> +}
> +
> +/*
> + * posts receive buffers to the Eth receive queue.
> + */
> +void be_post_eth_rx_buffs(struct bni_net_object *pnob)
> +{
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> + u32 num_bufs, r;
> + u64 busaddr = 0, tmp_pa;
> + u32 max_bufs, pg_hd;
> + u32 frag_size;
> + struct bni_recv_buffer *rxbp;
> + struct list_head rxbl;
> + struct be_rx_page_info *rx_page_info;
> + struct page *page = NULL;
> + u32 page_order = 0;
> + gfp_t alloc_flags = GFP_ATOMIC;
> +
> + BUG_ON(!adapter);
> +
> + max_bufs = 64; /* should be even # <= 255. */
> +
> + frag_size = pnob->rx_buf_size;
> + page_order = get_order(frag_size);
> +
> + if (frag_size == 8192)
> + alloc_flags |= (gfp_t) __GFP_COMP;
> + /*
> + * Form a linked list of RECV_BUFFFER structure to be be posted.
> + * We will post even number of buffer so that pages can be
> + * shared.
> + */
> + INIT_LIST_HEAD(&rxbl);
> +
> + for (num_bufs = 0; num_bufs < max_bufs; ++num_bufs) {
> +
> + rxbp = &(OSM_NOB(pnob)->eth_rx_bufs[num_bufs]);
> + pg_hd = OSM_NOB(pnob)->rx_pg_info_hd;
> + rx_page_info = &OSM_NOB(pnob)->rx_page_info[pg_hd];
> +
> + if (!page) {
> + /*
> + * before we allocate a page make sure that we
> + * have space in the RX queue to post the buffer.
> + * We check for two vacant slots since with
> + * 2K frags, we will need two slots.
> + */
> + if ((pnob->rx_ctxt[(pnob->rx_q_hd + num_bufs) &
> + (pnob->rx_q_len - 1)] != NULL)
> + || (pnob->rx_ctxt[(pnob->rx_q_hd + num_bufs + 1) %
> + pnob->rx_q_len] != NULL)) {
> + break;
> + }
> + page = alloc_pages(alloc_flags, page_order);
> + if (unlikely(page == NULL)) {
> + adapter->be_stat.bes_ethrx_post_fail++;
> + OSM_NOB(pnob)->rxbuf_post_fail++;
> + break;
> + }
> + OSM_NOB(pnob)->rxbuf_post_fail = 0;
> + busaddr = pci_map_page(adapter->pdev, page, 0,
> + frag_size, PCI_DMA_FROMDEVICE);
> + rx_page_info->page_offset = 0;
> + rx_page_info->page = page;
> + /*
> + * If we are sharing a page among two skbs,
> + * alloc a new one on the next iteration
> + */
> + if (OSM_NOB(pnob)->rx_pg_shared == FALSE)
> + page = NULL;
> + } else {
> + get_page(page);
> + rx_page_info->page_offset += frag_size;
> + rx_page_info->page = page;
> + /*
> + * We are finished with the alloced page,
> + * Alloc a new one on the next iteration
> + */
> + page = NULL;
> + }
> + rxbp->rxb_ctxt = (void *)rx_page_info;
> + index_advance(&OSM_NOB(pnob)->rx_pg_info_hd, pnob->rx_q_len);
> +
> + pci_unmap_addr_set(rx_page_info, bus, busaddr);
> + tmp_pa = busaddr + rx_page_info->page_offset;
> + rxbp->rxb_pa_lo = (tmp_pa & 0xFFFFFFFF);
> + rxbp->rxb_pa_hi = (tmp_pa >> 32);
> + rxbp->rxb_len = frag_size;
> + list_add_tail(&rxbp->rxb_list, &rxbl);
> + } /* End of for */
> +
> + r = bni_post_rx_buffs(pnob, &rxbl);
> + BUG_ON(r != num_bufs);
> + return;
> +}
> +
> +/*
> + * Interrupt service for network function. We just schedule the
> + * tasklet which does all completion processing.
> + */
> +irqreturn_t be_int(int irq, void *dev)
> +{
> + struct net_device *netdev = dev;
> + struct bni_net_object *pnob = (struct bni_net_object *)(netdev->priv);
> + struct be_adapter *adapter = (struct be_adapter *)
> + OSM_NOB(pnob)->adapter;
> + u32 isr;
> +
> + /*
> + * If not our interrupt, just return.
> + */
> + isr = bni_get_isr(pnob);
> + if (unlikely(!isr))
> + return 0;
> +
> + spin_lock(&adapter->int_lock);
> + adapter->isr |= isr;
> + spin_unlock(&adapter->int_lock);
> +
> + adapter->be_stat.bes_ints++;
> +
> + tasklet_schedule(&adapter->sts_handler);
> + return 1;
> +}
> +
> +#ifdef CONFIG_BENET_NAPI
> +/*
> + * Poll function called by NAPI with a work budget.
> + * We process as many UC. BC and MC receive completions
> + * as the budget allows and return the actual number of
> + * RX ststutses processed.
> + */
> +int be_poll(struct napi_struct *napi, int budget)
> +{
> + struct net_device *netdev = napi->dev;
> + struct bni_net_object *pnob = (struct bni_net_object *)netdev->priv;
> + struct be_adapter *adapter = (struct be_adapter *)
> + OSM_NOB(pnob)->adapter;
> + u32 work_done;
> +
> + adapter->be_stat.bes_polls++;
> + OSM_NOB(pnob)->work_quota = budget;
> + process_ucast_rx_completion(pnob);
> + process_bcast_rx_completion(pnob);
> + if (atomic_read(&pnob->rx_q_posted) < 900)
> + be_post_eth_rx_buffs(pnob);
> +
> + work_done = (budget - OSM_NOB(pnob)->work_quota);
> +
> + if (OSM_NOB(pnob)->work_quota) {
> + netif_rx_complete(netdev, napi);
> +
> + /* If another rx was attempted while we were in poll,
> + * schedule again */
> + spin_lock_bh(&OSM_NOB(pnob)->rx_lock);
> + if (OSM_NOB(pnob)->rx_sched) {
> + OSM_NOB(pnob)->rx_sched = FALSE;
> + if (netif_rx_schedule_prep(netdev, napi))
> + __netif_rx_schedule(netdev, napi);
> + }
> + spin_unlock_bh(&OSM_NOB(pnob)->rx_lock);
> + }
> + return work_done;
> +}
> +
> +static inline void napi_rx_schedule(struct bni_net_object *no,
> + struct net_device *nd)
> +{
> + spin_lock_bh(&OSM_NOB(no)->rx_lock);
> + if (netif_rx_schedule_prep(nd, &OSM_NOB(no)->napi)) {
> + __netif_rx_schedule(nd, &OSM_NOB(no)->napi);
> + OSM_NOB(no)->rx_sched = FALSE;
> + } else {
> + OSM_NOB(no)->rx_sched = TRUE;
> + }
> + spin_unlock_bh(&OSM_NOB(no)->rx_lock);
> +}
> +#endif
If you didn't keep rx_sched, you would not need the additional
lock round trip.
> +
> +/*
> + * Processes all valid events in the event ring associated with given
> + * NetObject. Also, notifies BE the number of events processed.
> + */
> +static inline u32 process_events(struct bni_net_object *pnob)
> +{
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> + struct EQ_ENTRY_AMAP *eqp;
> + u32 rid, num_events = 0;
> +
> +#ifdef CONFIG_BENET_NAPI
> + struct net_device *netdev = OSM_NOB(pnob)->netdev;
> +#endif
> +
> + while ((eqp = bni_get_event(pnob)) != NULL) {
> + adapter->be_stat.bes_events++;
> + rid = AMAP_GET_BITS_PTR(EQ_ENTRY, ResourceID, eqp);
> +
> + if (rid == pnob->ucrx_cq_id) {
> + adapter->be_stat.bes_ucrx_events++;
> +#ifdef CONFIG_BENET_NAPI
> + if (adapter->dev_state == BE_DEV_STATE_OPEN)
> + napi_rx_schedule(pnob, netdev);
> + else
> +#endif
> + process_ucast_rx_completion(pnob);
> + } else if (rid == pnob->bcrx_cq_id) {
> + adapter->be_stat.bes_bcrx_events++;
> +#ifdef CONFIG_BENET_NAPI
> + if (adapter->dev_state == BE_DEV_STATE_OPEN)
> + napi_rx_schedule(pnob, netdev);
> + else
> +#endif
> + process_bcast_rx_completion(pnob);
> + } else if (rid == pnob->tx_cq_id) {
> + process_nic_tx_completions(pnob);
> + } else if (rid == pnob->mcc_cq_id) {
> + bni_process_mcc_cmpl(&pnob->mcc_q_obj);
> + } else {
> + printk("Invalid EQ ResourceID %d\n", rid);
> + }
> + AMAP_SET_BITS_PTR(EQ_ENTRY, Valid, eqp, 0);
> + AMAP_SET_BITS_PTR(EQ_ENTRY, ResourceID, eqp, 0);
> + num_events++;
> + }
> + return (num_events);
> +}
> +
> +/*
> + * Called from the tasklet scheduled by ISR. All real interrupt processing
> + * is done here.
> + */
> +void be_process_intr(unsigned long context)
> +{
> + struct be_adapter *adapter = (struct be_adapter *)context;
> + struct bni_net_object *pnob;
> + u32 isr, n;
> + ulong flags = 0;
> +
> + isr = adapter->isr;
> +
> + /*
> + * we create only one NIC event queue in Linux. Event is
> + * expected only in the first event queue
> + */
> + BUG_ON(isr & 0xfffffffe);
> + if ((isr & 1) == 0)
> + return; /* not our interrupt */
> + pnob = adapter->net_obj;
> + n = process_events(pnob);
> + /*
> + * Clear the event bit. adapter->isr is set by
> + * hard interrupt. Prevent race with lock.
> + */
> + spin_lock_irqsave(&adapter->int_lock, flags);
> + adapter->isr &= ~1;
> + spin_unlock_irqrestore(&adapter->int_lock, flags);
> + bni_notify_event(pnob, n, 1);
> +
> +#ifdef CONFIG_BENET_NAPI
> + /*
> + * In NAPI, posting of rx bufs is normally done
> + * in poll. However, if the device is not open
> + * or if previous allocation attempts had failed and
> + * BE has used up all posted buffers, we need to
> + * post here, since be_poll may never be called.
> + */
> + if ((adapter->dev_state != BE_DEV_STATE_OPEN &&
> + atomic_read(&pnob->rx_q_posted) < 900) ||
> + (OSM_NOB(pnob)->rxbuf_post_fail &&
> + atomic_read(&pnob->rx_q_posted) == 0)) {
> + be_post_eth_rx_buffs(pnob);
> + }
> +#else
> + if (atomic_read(&pnob->rx_q_posted) < 900)
> + be_post_eth_rx_buffs(pnob);
> +#endif
> + update_eqd(adapter, pnob);
> + return;
> +}
> diff --git a/drivers/net/benet/be_netif.c b/drivers/net/benet/be_netif.c
> new file mode 100644
> index 0000000..dbd6895
> --- /dev/null
> +++ b/drivers/net/benet/be_netif.c
> @@ -0,0 +1,693 @@
> +/*
> + * Copyright (C) 2005 - 2008 ServerEngines
> + * All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License version 2
> + * as published by the Free Software Foundation. The full GNU General
> + * Public License is included in this distribution in the file called COPYING.
> + *
> + * Contact Information:
> + * linux-drivers@...verengines.com
> + *
> + * ServerEngines
> + * 209 N. Fair Oaks Ave
> + * Sunnyvale, CA 94085
> + */
> +/*
> + * be_netif.c
> + *
> + * This file contains various entry points of drivers seen by tcp/ip stack.
> + */
> +
> +#include <linux/pci.h>
> +#include <linux/if_vlan.h>
> +#include <linux/in.h>
> +#include "benet.h"
> +#include <linux/ip.h>
> +#include <linux/inet_lro.h>
> +
> +/* Strings to print Link properties */
> +static char *link_speed[] = {
static const char *link_speed[]
> + "Invalid link Speed Value",
> + "10 Mbps",
> + "100 Mbps",
> + "1 Gbps",
> + "10 Gbps"
> +};
> +
> +static char *link_duplex[] = {
> + "Invalid Duplex Value",
> + "Half Duplex",
> + "Full Duplex"
> +};
> +
> +static char *link_state[] = {
> + "",
> + "(active)"
> +};
> +
> +
> +void be_print_link_info(struct BE_LINK_STATUS *lnk_status)
> +{
> + u16 si, di, ai;
> +
> + /* Port 0 */
> + if (lnk_status->mac0_speed && lnk_status->mac0_duplex) {
> + /* Port is up and running */
> + si = (lnk_status->mac0_speed < 5) ?
> + lnk_status->mac0_speed : 0;
> + di = (lnk_status->mac0_duplex < 3) ?
> + lnk_status->mac0_duplex : 0;
> + ai = (lnk_status->active_port == 0) ? 1 : 0;
> + printk(KERN_INFO "PortNo. 0: Speed - %s %s %s\n",
> + link_speed[si], link_duplex[di], link_state[ai]);
> + } else
> + printk(KERN_INFO "PortNo. 0: Down\n");
> +
> + /* Port 1 */
> + if (lnk_status->mac1_speed && lnk_status->mac1_duplex) {
> + /* Port is up and running */
> + si = (lnk_status->mac1_speed < 5) ?
> + lnk_status->mac1_speed : 0;
> + di = (lnk_status->mac1_duplex < 3) ?
> + lnk_status->mac1_duplex : 0;
> + ai = (lnk_status->active_port == 0) ? 1 : 0;
> + printk(KERN_INFO "PortNo. 1: Speed - %s %s %s\n",
> + link_speed[si], link_duplex[di], link_state[ai]);
> + } else
> + printk(KERN_INFO "PortNo. 1: Down\n");
> +
> + return;
> +}
> +
> +static int
> +be_get_frag_header(struct skb_frag_struct *frag, void **mac_hdr,
> + void **ip_hdr, void **tcpudp_hdr,
> + u64 *hdr_flags, void *priv)
> +{
> + struct ethhdr *eh;
> + struct vlan_ethhdr *veh;
> + struct iphdr *iph;
> + u8 *va = page_address(frag->page) + frag->page_offset;
> + unsigned long ll_hlen;
> +
> + /* find the mac header, abort if not IPv4 */
> +
> + prefetch(va);
> + eh = (struct ethhdr *)va;
> + *mac_hdr = eh;
> + ll_hlen = ETH_HLEN;
> + if (eh->h_proto != htons(ETH_P_IP)) {
> + if (eh->h_proto == htons(ETH_P_8021Q)) {
> + veh = (struct vlan_ethhdr *)va;
> + if (veh->h_vlan_encapsulated_proto != htons(ETH_P_IP))
> + return -1;
> +
> + ll_hlen += VLAN_HLEN;
> +
> + } else {
> + return -1;
> + }
> + }
> + *hdr_flags = LRO_IPV4;
> +
> + iph = (struct iphdr *)(va + ll_hlen);
> + *ip_hdr = iph;
> + if (iph->protocol != IPPROTO_TCP)
> + return -1;
> + *hdr_flags |= LRO_TCP;
> + *tcpudp_hdr = (u8 *) (*ip_hdr) + (iph->ihl << 2);
> +
> + return 0;
> +}
> +
> +static int benet_open(struct net_device *netdev)
> +{
> + struct bni_net_object *pnob = (struct bni_net_object *) netdev->priv;
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> + struct net_lro_mgr *lro_mgr;
> +
> + if (adapter->dev_state < BE_DEV_STATE_INIT)
> + return -EAGAIN;
> +
> + lro_mgr = &OSM_NOB(pnob)->lro_mgr;
> + lro_mgr->dev = netdev;
> +
> +#ifdef CONFIG_BENET_NAPI
> + lro_mgr->features = LRO_F_NAPI;
> +#endif
> + lro_mgr->ip_summed = CHECKSUM_UNNECESSARY;
> + lro_mgr->ip_summed_aggr = CHECKSUM_UNNECESSARY;
> + lro_mgr->max_desc = BE_MAX_LRO_DESCRIPTORS;
> + lro_mgr->lro_arr = OSM_NOB(pnob)->lro_desc;
> + lro_mgr->get_frag_header = be_get_frag_header;
> + lro_mgr->max_aggr = adapter->max_rx_coal;
> + lro_mgr->frag_align_pad = 2;
> + if (lro_mgr->max_aggr > MAX_SKB_FRAGS)
> + lro_mgr->max_aggr = MAX_SKB_FRAGS;
> +
> + be_update_link_status(adapter);
> +
> + /*
> + * Set carrier on only if Physical Link up
> + * Either of the port link status up signifies this
> + */
> + if ((adapter->port0_link_sts == BE_PORT_LINK_UP) ||
> + (adapter->port1_link_sts == BE_PORT_LINK_UP)) {
> + netif_start_queue(netdev);
> + netif_carrier_on(netdev);
> + }
> +
> + bni_enable_eq_intr(pnob);
> + adapter->dev_state = BE_DEV_STATE_OPEN;
> +
> +#ifdef CONFIG_BENET_NAPI
> + napi_enable(&OSM_NOB(pnob)->napi);
> +#endif
> + return 0;
> +}
> +
> +static int benet_close(struct net_device *netdev)
> +{
> + struct bni_net_object *pnob = (struct bni_net_object *) netdev->priv;
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> +
> + /* Stop Transmitting */
> + netif_stop_queue(netdev);
> +
> + synchronize_irq(netdev->irq);
> +
> + /* Wait until no more pending transmits */
> + be_wait_nic_tx_cmplx_cmpl(pnob);
> +
> + adapter->dev_state = BE_DEV_STATE_INIT;
> +
> + netif_carrier_off(netdev);
> +
> + adapter->port0_link_sts = BE_PORT_LINK_DOWN;
> + adapter->port1_link_sts = BE_PORT_LINK_DOWN;
> +
> +#ifdef CONFIG_BENET_NAPI
> + napi_disable(&OSM_NOB(pnob)->napi);
> +#endif
> + return 0;
> +}
> +
> +/*
> + * Setting a Mac Address for BE
> + * Takes netdev and a void pointer as arguments.
> + * The pointer holds the new addres to be used.
> + */
> +static int benet_set_mac_addr(struct net_device *netdev, void *p)
> +{
> + struct sockaddr *addr = p;
> + struct bni_net_object *pnob;
> +
> + pnob = (struct bni_net_object *) netdev->priv;
> +
> + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
> + bni_set_uc_mac_adr(pnob, 0, 0, OSM_NOB(pnob)->devno,
> + netdev->dev_addr, NULL, NULL);
> + /*
> + * Since we are doing Active-Passive failover, both
> + * ports should have matching MAC addresses everytime.
> + */
> + bni_set_uc_mac_adr(pnob, 1, 0, OSM_NOB(pnob)->devno,
> + netdev->dev_addr, NULL, NULL);
> +
> + return 0;
> +}
> +
> +void be_get_stats_timer_handler(unsigned long context)
> +{
> + struct be_timer_ctxt *ctxt = (struct be_timer_ctxt *) context;
> +
> + if (atomic_read(&ctxt->get_stat_flag)) {
> + atomic_dec(&ctxt->get_stat_flag);
> + up((void *) ctxt->get_stat_sem_addr);
> + }
> + del_timer(&ctxt->get_stats_timer);
> + return;
> +}
> +
> +void be_get_stat_cb(void *context, BESTATUS status,
> + struct MCC_WRB_AMAP *optional_wrb)
> +{
> + struct be_timer_ctxt *ctxt = (struct be_timer_ctxt *) context;
> + /*
> + * just up the semaphore if the get_stat_flag
> + * reads 1. so that the waiter can continue.
> + * If it is 0, then it was handled by the timer handler.
> + */
> + del_timer(&ctxt->get_stats_timer);
> + if (atomic_read(&ctxt->get_stat_flag)) {
> + atomic_dec(&ctxt->get_stat_flag);
> + up((void *) ctxt->get_stat_sem_addr);
> + }
> +}
> +
> +struct net_device_stats *benet_get_stats(struct net_device *dev)
> +{
> + struct bni_net_object *pnob = dev->priv;
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> + u64 pa;
> + struct be_timer_ctxt *ctxt = &adapter->timer_ctxt;
> +
> + if (adapter->dev_state != BE_DEV_STATE_OPEN) {
> + /* Return previously read stats */
> + return &(adapter->benet_stats);
> + }
> + /* Get Physical Addr */
> + pa = pci_map_single(adapter->pdev, adapter->eth_statsp,
> + sizeof(struct FWCMD_ETH_GET_STATISTICS),
> + PCI_DMA_FROMDEVICE);
> + ctxt->get_stat_sem_addr = (unsigned long)&adapter->get_eth_stat_sem;
> + atomic_inc(&ctxt->get_stat_flag);
> + bni_get_stats(adapter->net_obj, adapter->eth_statsp,
> + cpu_to_le64(pa), be_get_stat_cb, (void *) ctxt);
> + ctxt->get_stats_timer.data = (unsigned long)ctxt;
> + mod_timer(&ctxt->get_stats_timer, (jiffies + (HZ * 2)));
> + down((void *) ctxt->get_stat_sem_addr); /* callback will unblock us */
> +
> + /* Adding port0 and port1 stats. */
> + adapter->benet_stats.rx_packets =
> + adapter->eth_statsp->params.response.p0recvdtotalframes +
> + adapter->eth_statsp->params.response.p1recvdtotalframes;
> + adapter->benet_stats.tx_packets =
> + adapter->eth_statsp->params.response.p0xmitunicastframes +
> + adapter->eth_statsp->params.response.p1xmitunicastframes;
> + adapter->benet_stats.tx_bytes =
> + adapter->eth_statsp->params.response.p0xmitbyteslsd +
> + adapter->eth_statsp->params.response.p1xmitbyteslsd;
> + adapter->benet_stats.rx_errors =
> + adapter->eth_statsp->params.response.p0crcerrors +
> + adapter->eth_statsp->params.response.p1crcerrors;
> + adapter->benet_stats.rx_errors +=
> + adapter->eth_statsp->params.response.p0alignmentsymerrs +
> + adapter->eth_statsp->params.response.p1alignmentsymerrs;
> + adapter->benet_stats.rx_errors +=
> + adapter->eth_statsp->params.response.p0inrangelenerrors +
> + adapter->eth_statsp->params.response.p1inrangelenerrors;
> + adapter->benet_stats.rx_bytes =
> + adapter->eth_statsp->params.response.p0recvdtotalbytesLSD +
> + adapter->eth_statsp->params.response.p1recvdtotalbytesLSD;
> + adapter->benet_stats.rx_crc_errors =
> + adapter->eth_statsp->params.response.p0crcerrors +
> + adapter->eth_statsp->params.response.p1crcerrors;
> +
> + adapter->benet_stats.tx_packets +=
> + adapter->eth_statsp->params.response.p0xmitmulticastframes +
> + adapter->eth_statsp->params.response.p1xmitmulticastframes;
> + adapter->benet_stats.tx_packets +=
> + adapter->eth_statsp->params.response.p0xmitbroadcastframes +
> + adapter->eth_statsp->params.response.p1xmitbroadcastframes;
> + adapter->benet_stats.tx_errors = 0;
> +
> + adapter->benet_stats.multicast =
> + adapter->eth_statsp->params.response.p0xmitmulticastframes +
> + adapter->eth_statsp->params.response.p1xmitmulticastframes;
> +
> + adapter->benet_stats.rx_fifo_errors =
> + adapter->eth_statsp->params.response.p0rxfifooverflowdropped +
> + adapter->eth_statsp->params.response.p1rxfifooverflowdropped;
> + adapter->benet_stats.rx_frame_errors =
> + adapter->eth_statsp->params.response.p0alignmentsymerrs +
> + adapter->eth_statsp->params.response.p1alignmentsymerrs;
> + adapter->benet_stats.rx_length_errors =
> + adapter->eth_statsp->params.response.p0inrangelenerrors +
> + adapter->eth_statsp->params.response.p1inrangelenerrors;
> + adapter->benet_stats.rx_length_errors +=
> + adapter->eth_statsp->params.response.p0outrangeerrors +
> + adapter->eth_statsp->params.response.p1outrangeerrors;
> + adapter->benet_stats.rx_length_errors +=
> + adapter->eth_statsp->params.response.p0frametoolongerrors +
> + adapter->eth_statsp->params.response.p1frametoolongerrors;
> +
> + pci_unmap_single(adapter->pdev, (ulong) adapter->eth_statsp,
> + sizeof(struct FWCMD_ETH_GET_STATISTICS),
> + PCI_DMA_FROMDEVICE);
> + return &(adapter->benet_stats);
> +
> +}
> +
> +/* Transmit Function */
> +int betx_ether_frame(struct be_adapter *adapter, struct bni_net_object *pnob,
> + struct sk_buff *skb, u8 proto, u8 forward,
> + u16 lso_mss)
> +{
> + unsigned int nfrags = 0, j, frame_size = 0;
> + struct bni_tx_frag_list tx_frag_list[BE_MAX_TX_FRAG_COUNT];
> + unsigned int tx_flags;
> + void *ctxtp;
> + unsigned short vlant = 0;
> + unsigned short tx_mss = 0;
> + u64 busaddr;
> + int status;
> +
> + tx_flags = ETHCOMPLETE;
> +
> + if (OSM_NOB(pnob)->vlan_grp && vlan_tx_tag_present(skb)) {
> + tx_flags |= ETHVLAN;
> + vlant = vlan_tx_tag_get(skb);
> + }
> + ctxtp = (void *)skb;
> +
> + if (proto == IPPROTO_TCP)
> + tx_flags |= TCPCS;
> +
> + if (proto == IPPROTO_UDP)
> + tx_flags |= UDPCS;
> +
> + if (forward) {
> + tx_flags |= FORWARD;
> + adapter->be_stat.bes_fwd_reqs++;
> + }
> +
> + if (lso_mss) {
> + tx_flags |= LSO;
> + tx_mss = lso_mss;
> + }
> +
> + adapter->be_stat.bes_tx_reqs++;
> + /* populate the fragment (SG) list for this request */
> + while (skb) {
> + /*
> + * Check whether Fragment count goes above
> + * BE_MAX_TX_FRAG_COUNT
> + */
> + if ((nfrags + 1) > BE_MAX_TX_FRAG_COUNT)
> + goto max_tx_frag_error;
> +
> + /*
> + * Get required info from main fragment of skb
> + * First get Quad Address
> + */
> + busaddr = pci_map_single(adapter->pdev, skb->data,
> + (skb->len - skb->data_len),
> + PCI_DMA_TODEVICE);
> + busaddr = cpu_to_le64(busaddr);
> + tx_frag_list[nfrags].txb_pa_lo = (busaddr & 0xFFFFFFFF);
> + tx_frag_list[nfrags].txb_pa_hi = busaddr >> 32;
> + /* Next get Length */
> + tx_frag_list[nfrags].txb_len = skb->len - skb->data_len;
> + frame_size += tx_frag_list[nfrags].txb_len;
> + nfrags++;
> +
> + /* For all the data fragments in this skb */
> + for (j = 0; j < skb_shinfo(skb)->nr_frags; j++) {
> + struct skb_frag_struct *frag;
> + /*
> + * Check whether Fragment count goes
> + * above BE_MAX_TX_FRAG_COUNT
> + */
> + if ((nfrags + 1) > BE_MAX_TX_FRAG_COUNT)
> + goto max_tx_frag_error;
> +
> + /* For each fragment get required info */
> + frag = &skb_shinfo(skb)->frags[j];
> + /* First get Quad Address */
> + busaddr = pci_map_page(adapter->pdev,
> + frag->page,
> + frag->page_offset,
> + frag->size,
> + PCI_DMA_TODEVICE);
> + busaddr = cpu_to_le64(busaddr);
> + tx_frag_list[nfrags].txb_pa_lo = busaddr & 0xFFFFFFFF;
> + tx_frag_list[nfrags].txb_pa_hi = busaddr >> 32;
> + /* Next get Length */
> + tx_frag_list[nfrags].txb_len = frag->size;
> + frame_size += tx_frag_list[nfrags].txb_len;
> + nfrags++;
> + }
> +
> + /*
> + * If the skb shared info points to another
> + * sk_buff then traverse this pointed
> + * skbuff in the same way till the end of the list
> + */
> + skb = skb_shinfo(skb)->frag_list;
> + }
> +
> + spin_lock_bh(&adapter->txq_lock);
> +
> + /* Transmit the packet */
> + status = bni_tx_pkt(pnob, tx_frag_list,
> + tx_flags, vlant, tx_mss, ctxtp, nfrags);
> + if (status != BE_SUCCESS) {
> + /* Tell the stack that Tx failed. */
> + netif_stop_queue((struct net_device *)
> + OSM_NOB(pnob)->netdev);
> + adapter->be_stat.bes_tx_fails++;
> + spin_unlock_bh(&adapter->txq_lock);
> + return BE_ETH_TX_ERROR;
> + }
> + adapter->eth_tx_bytes += frame_size; /* for rate calculation */
> + /*
> + * TX rate calculation. If one second has passed since
> + * last calculation update the rate now.
> + */
> + update_tx_rate(adapter);
> + if (nfrags & 1)
> + nfrags++;
> +
> + adapter->be_stat.bes_tx_wrbs += nfrags;
> +
> + /* Ring the send doorbell */
> + bni_start_tx(pnob, nfrags);
> + spin_unlock_bh(&adapter->txq_lock);
> +
> + return BE_SUCCESS;
> +
> +max_tx_frag_error:
> + /*
> + * This skb cannot be transmitted since it exceeds max tx frag count
> + * Return with appropriate error
> + */
> + printk(KERN_WARNING "%s: Exceeds Max Tx Frags\n", __func__);
> + return BE_ETH_TX_ERROR;
> +}
> +
> +/*
> + * function called by the stack for transmitting an ether frame
> + */
> +static int benet_xmit(struct sk_buff *skb, struct net_device *netdev)
> +{
> + struct bni_net_object *pnob = netdev->priv;
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> + u8 proto;
> + struct iphdr *ip;
> + u16 lso_mss;
> + u32 segs;
> +
> + lso_mss = skb_shinfo(skb)->gso_size;
> + segs = skb_shinfo(skb)->gso_segs;
> + /*
> + * bug# 3356.
> + * If a LSO request translates into a single segment,
> + * it should be posted as a ethernet WRB with no LSO.
> + */
> + if (segs == 1)
> + lso_mss = 0;
> +
> + if (skb->ip_summed == CHECKSUM_PARTIAL) {
> + ip = (struct iphdr *)ip_hdr(skb);
> + proto = ip->protocol;
> + } else {
> + proto = 0;
> + }
> +
> + if (betx_ether_frame(adapter, pnob, skb, proto, 0, lso_mss) !=
> + BE_SUCCESS) {
> + return NETDEV_TX_BUSY;
> + }
> +
> + netdev->trans_start = jiffies;
> + return NETDEV_TX_OK;
> +
> +}
> +
> +/*
> + * This is the driver entry point to change the mtu of the device
> + * Returns 0 for success and errno for failure.
> + */
> +static int benet_change_mtu(struct net_device *netdev, int new_mtu)
> +{
> + /*
> + * BE supports jumbo frame size upto 9000 bytes including the link layer
> + * header. Considering the different variants of frame formats possible
> + * like VLAN, SNAP/LLC, the maximum possible value for MTU is 8974 bytes
> + */
> +
> + if (new_mtu < (ETH_ZLEN + ETH_FCS_LEN) || (new_mtu > BE_MAX_MTU)) {
> + printk(KERN_WARNING "Invalid MTU requested. "
> + "Must be between %d and %d bytes\n",
> + (ETH_ZLEN+ETH_FCS_LEN), BE_MAX_MTU);
> + return -EINVAL;
> + }
> + printk(KERN_INFO "MTU changed from %d to %d\n", netdev->mtu,
> + new_mtu);
> + netdev->mtu = new_mtu;
> + return 0;
> +}
> +
> +/*
> + * This is the driver entry point to register a vlan with the device
> + */
> +static void benet_vlan_register(struct net_device *netdev,
> + struct vlan_group *grp)
> +{
> + struct bni_net_object *pnob = netdev->priv;
> +
> + bni_disable_eq_intr(pnob);
> + OSM_NOB(pnob)->vlan_grp = grp;
> + OSM_NOB(pnob)->num_vlans = 0;
> + bni_enable_eq_intr(pnob);
> +}
> +
> +/*
> + * This is the driver entry point to add a vlan vlan_id
> + * with the device netdev
> + */
> +static void benet_vlan_add_vid(struct net_device *netdev, u16 vlan_id)
> +{
> + struct bni_net_object *pnob = netdev->priv;
> +
> + if (OSM_NOB(pnob)->num_vlans == (BE_NUM_VLAN_SUPPORTED-1)) {
> + /* no way to return an error */
> + printk(KERN_ERR
> + "BladeEngine: Cannot configure more than %d Vlans\n",
> + BE_NUM_VLAN_SUPPORTED);
> + return;
> + }
> + /*The new vlan tag will be in the slot indicated by num_vlans. */
> + OSM_NOB(pnob)->vlan_tag[OSM_NOB(pnob)->num_vlans++] = vlan_id;
> + bni_config_vlan(pnob, OSM_NOB(pnob)->vlan_tag,
> + OSM_NOB(pnob)->num_vlans, NULL, NULL, 0);
> +}
> +
> +/*
> + * This is the driver entry point to remove a vlan vlan_id
> + * with the device netdev
> + */
> +static void benet_vlan_rem_vid(struct net_device *netdev, u16 vlan_id)
> +{
> + struct bni_net_object *pnob = netdev->priv;
> +
> + u32 i, value;
> +
> + /*
> + * In Blade Engine, we support 32 vlan tag filters across both ports.
> + * To program a vlan tag, the RXF_RTPR_CSR register is used.
> + * Each 32-bit value of RXF_RTDR_CSR can address 2 vlan tag entries.
> + * The Vlan table is of depth 16. thus we support 32 tags.
> + */
> +
> + value = vlan_id | VLAN_VALID_BIT;
> + for (i = 0; i < BE_NUM_VLAN_SUPPORTED; i++) {
> + if (OSM_NOB(pnob)->vlan_tag[i] == vlan_id)
> + break;
> + }
> +
> + if (i == BE_NUM_VLAN_SUPPORTED)
> + return;
> + /* Now compact the vlan tag array by removing hole created. */
> + while ((i + 1) < BE_NUM_VLAN_SUPPORTED) {
> + OSM_NOB(pnob)->vlan_tag[i] = OSM_NOB(pnob)->vlan_tag[i + 1];
> + i++;
> + }
> + if ((i + 1) == BE_NUM_VLAN_SUPPORTED)
> + OSM_NOB(pnob)->vlan_tag[i] = (u16) 0x0;
> + OSM_NOB(pnob)->num_vlans--;
> + bni_config_vlan(pnob, OSM_NOB(pnob)->vlan_tag,
> + OSM_NOB(pnob)->num_vlans, NULL, NULL, 0);
> +}
> +
> +/*
> + * This function is called to program multicast
> + * address in the multicast filter of the ASIC.
> + */
> +static void be_set_multicast_filter(struct net_device *netdev)
> +{
> + struct bni_net_object *pnob = netdev->priv;
> + struct dev_mc_list *mc_ptr;
> + u8 mac_addr[32][ETH_ALEN];
> + int i;
> +
> + if (netdev->flags & IFF_ALLMULTI) {
> + /* set BE in Multicast promiscuous */
> + bni_set_mc_filter(pnob, 0, TRUE, NULL, NULL, NULL);
> + return;
> + }
> +
> + for (mc_ptr = netdev->mc_list, i = 0; mc_ptr;
> + mc_ptr = mc_ptr->next, i++) {
> + memcpy(&mac_addr[i][0], mc_ptr->dmi_addr, ETH_ALEN);
> + }
> + /* reset the promiscuous mode also. */
> + bni_set_mc_filter(pnob, i, FALSE, &mac_addr[0][0], NULL, NULL);
> +
> +}
> +
> +/*
> + * This is the driver entry point to set multicast list
> + * with the device netdev. This function will be used to
> + * set promiscuous mode or multicast promiscuous mode
> + * or multicast mode....
> + */
> +static void benet_set_multicast_list(struct net_device *netdev)
> +{
> + struct bni_net_object *pnob = netdev->priv;
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> +
> + if (netdev->flags & IFF_PROMISC) {
> + bni_set_promisc(adapter->net_obj);
> +
> + } else if (netdev->flags & IFF_ALLMULTI) {
> + bni_reset_promisc(adapter->net_obj);
> + be_set_multicast_filter(netdev);
> + } else {
> + bni_reset_promisc(adapter->net_obj);
> + be_set_multicast_filter(netdev);
> + }
> +}
> +
> +
> +/*
> + * standard entry point functions for all Linux network interface drivers
> + */
> +int benet_probe(struct net_device *netdev)
> +{
> + struct bni_net_object *pnob = netdev->priv;
> + struct be_adapter *adapter = OSM_NOB(pnob)->adapter;
> +
> + ether_setup(netdev);
> +
> + netdev->open = &benet_open;
> + netdev->stop = &benet_close;
> + netdev->hard_start_xmit = &benet_xmit;
> +
> + netdev->get_stats = &benet_get_stats;
> +
> + netdev->set_multicast_list = &benet_set_multicast_list;
> +
> + netdev->change_mtu = &benet_change_mtu;
> + netdev->set_mac_address = &benet_set_mac_addr;
> +
> + netdev->vlan_rx_register = benet_vlan_register;
> + netdev->vlan_rx_add_vid = benet_vlan_add_vid;
> + netdev->vlan_rx_kill_vid = benet_vlan_rem_vid;
> +
> + netdev->features =
> + NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_HW_VLAN_RX | NETIF_F_TSO |
> + NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_FILTER | NETIF_F_IP_CSUM;
> +
> + netdev->flags |= IFF_MULTICAST;
> +
> + /* If device is DAC Capable, set the HIGHDMA flag for netdevice. */
> + if (adapter->dma_64bit_cap)
> + netdev->features |= NETIF_F_HIGHDMA;
> +
> + SET_ETHTOOL_OPS(netdev, &be_ethtool_ops);
> + return 0;
> +}
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists