Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  myri10ge.c   Sprache: C

 
/*************************************************************************
 * myri10ge.c: Myricom Myri-10G Ethernet driver.
 *
 * Copyright (C) 2005 - 2011 Myricom, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of Myricom, Inc. nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * If the eeprom on your board is not recent enough, you will need to get a
 * newer firmware image at:
 *   http://www.myri.com/scs/download-Myri10GE.html
 *
 * Contact Information:
 *   <help@myri.com>
 *   Myricom, Inc., 325N Santa Anita Avenue, Arcadia, CA 91006
 *************************************************************************/


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/tcp.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/etherdevice.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/dca.h>
#include <linux/ip.h>
#include <linux/inet.h>
#include <linux/in.h>
#include <linux/ethtool.h>
#include <linux/firmware.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/vmalloc.h>
#include <linux/crc32.h>
#include <linux/moduleparam.h>
#include <linux/io.h>
#include <linux/log2.h>
#include <linux/slab.h>
#include <linux/prefetch.h>
#include <net/checksum.h>
#include <net/gso.h>
#include <net/ip.h>
#include <net/tcp.h>
#include <asm/byteorder.h>
#include <asm/processor.h>

#include "myri10ge_mcp.h"
#include "myri10ge_mcp_gen_header.h"

#define MYRI10GE_VERSION_STR "1.5.3-1.534"

MODULE_DESCRIPTION("Myricom 10G driver (10GbE)");
MODULE_AUTHOR("Maintainer: help@myri.com");
MODULE_VERSION(MYRI10GE_VERSION_STR);
MODULE_LICENSE("Dual BSD/GPL");

#define MYRI10GE_MAX_ETHER_MTU 9014

#define MYRI10GE_ETH_STOPPED 0
#define MYRI10GE_ETH_STOPPING 1
#define MYRI10GE_ETH_STARTING 2
#define MYRI10GE_ETH_RUNNING 3
#define MYRI10GE_ETH_OPEN_FAILED 4

#define MYRI10GE_EEPROM_STRINGS_SIZE 256
#define MYRI10GE_MAX_SEND_DESC_TSO ((65536 / 2048) * 2)

#define MYRI10GE_NO_CONFIRM_DATA htonl(0xffffffff)
#define MYRI10GE_NO_RESPONSE_RESULT 0xffffffff

#define MYRI10GE_ALLOC_ORDER 0
#define MYRI10GE_ALLOC_SIZE ((1 << MYRI10GE_ALLOC_ORDER) * PAGE_SIZE)
#define MYRI10GE_MAX_FRAGS_PER_FRAME (MYRI10GE_MAX_ETHER_MTU/MYRI10GE_ALLOC_SIZE + 1)

#define MYRI10GE_MAX_SLICES 32

struct myri10ge_rx_buffer_state {
 struct page *page;
 int page_offset;
 DEFINE_DMA_UNMAP_ADDR(bus);
 DEFINE_DMA_UNMAP_LEN(len);
};

struct myri10ge_tx_buffer_state {
 struct sk_buff *skb;
 int last;
 DEFINE_DMA_UNMAP_ADDR(bus);
 DEFINE_DMA_UNMAP_LEN(len);
};

struct myri10ge_cmd {
 u32 data0;
 u32 data1;
 u32 data2;
};

struct myri10ge_rx_buf {
 struct mcp_kreq_ether_recv __iomem *lanai; /* lanai ptr for recv ring */
 struct mcp_kreq_ether_recv *shadow; /* host shadow of recv ring */
 struct myri10ge_rx_buffer_state *info;
 struct page *page;
 dma_addr_t bus;
 int page_offset;
 int cnt;
 int fill_cnt;
 int alloc_fail;
 int mask;  /* number of rx slots -1 */
 int watchdog_needed;
};

struct myri10ge_tx_buf {
 struct mcp_kreq_ether_send __iomem *lanai; /* lanai ptr for sendq */
 __be32 __iomem *send_go; /* "go" doorbell ptr */
 __be32 __iomem *send_stop; /* "stop" doorbell ptr */
 struct mcp_kreq_ether_send *req_list; /* host shadow of sendq */
 char *req_bytes;
 struct myri10ge_tx_buffer_state *info;
 int mask;  /* number of transmit slots -1  */
 int req ____cacheline_aligned; /* transmit slots submitted     */
 int pkt_start;  /* packets started */
 int stop_queue;
 int linearized;
 int done ____cacheline_aligned; /* transmit slots completed     */
 int pkt_done;  /* packets completed */
 int wake_queue;
 int queue_active;
};

struct myri10ge_rx_done {
 struct mcp_slot *entry;
 dma_addr_t bus;
 int cnt;
 int idx;
};

struct myri10ge_slice_netstats {
 unsigned long rx_packets;
 unsigned long tx_packets;
 unsigned long rx_bytes;
 unsigned long tx_bytes;
 unsigned long rx_dropped;
 unsigned long tx_dropped;
};

struct myri10ge_slice_state {
 struct myri10ge_tx_buf tx; /* transmit ring        */
 struct myri10ge_rx_buf rx_small;
 struct myri10ge_rx_buf rx_big;
 struct myri10ge_rx_done rx_done;
 struct net_device *dev;
 struct napi_struct napi;
 struct myri10ge_priv *mgp;
 struct myri10ge_slice_netstats stats;
 __be32 __iomem *irq_claim;
 struct mcp_irq_data *fw_stats;
 dma_addr_t fw_stats_bus;
 int watchdog_tx_done;
 int watchdog_tx_req;
 int watchdog_rx_done;
 int stuck;
#ifdef CONFIG_MYRI10GE_DCA
 int cached_dca_tag;
 int cpu;
 __be32 __iomem *dca_tag;
#endif
 char irq_desc[32];
};

struct myri10ge_priv {
 struct myri10ge_slice_state *ss;
 int tx_boundary; /* boundary transmits cannot cross */
 int num_slices;
 int running;  /* running?             */
 int small_bytes;
 int big_bytes;
 int max_intr_slots;
 struct net_device *dev;
 u8 __iomem *sram;
 int sram_size;
 unsigned long board_span;
 unsigned long iomem_base;
 __be32 __iomem *irq_deassert;
 char *mac_addr_string;
 struct mcp_cmd_response *cmd;
 dma_addr_t cmd_bus;
 struct pci_dev *pdev;
 int msi_enabled;
 int msix_enabled;
 struct msix_entry *msix_vectors;
#ifdef CONFIG_MYRI10GE_DCA
 int dca_enabled;
 int relaxed_order;
#endif
 u32 link_state;
 unsigned int rdma_tags_available;
 int intr_coal_delay;
 __be32 __iomem *intr_coal_delay_ptr;
 int wc_cookie;
 int down_cnt;
 wait_queue_head_t down_wq;
 struct work_struct watchdog_work;
 struct timer_list watchdog_timer;
 int watchdog_resets;
 int watchdog_pause;
 int pause;
 bool fw_name_allocated;
 char *fw_name;
 char eeprom_strings[MYRI10GE_EEPROM_STRINGS_SIZE];
 char *product_code_string;
 char fw_version[128];
 int fw_ver_major;
 int fw_ver_minor;
 int fw_ver_tiny;
 int adopted_rx_filter_bug;
 u8 mac_addr[ETH_ALEN];  /* eeprom mac address */
 unsigned long serial_number;
 int vendor_specific_offset;
 int fw_multicast_support;
 u32 features;
 u32 max_tso6;
 u32 read_dma;
 u32 write_dma;
 u32 read_write_dma;
 u32 link_changes;
 u32 msg_enable;
 unsigned int board_number;
 int rebooted;
};

static char *myri10ge_fw_unaligned = "myri10ge_ethp_z8e.dat";
static char *myri10ge_fw_aligned = "myri10ge_eth_z8e.dat";
static char *myri10ge_fw_rss_unaligned = "myri10ge_rss_ethp_z8e.dat";
static char *myri10ge_fw_rss_aligned = "myri10ge_rss_eth_z8e.dat";
MODULE_FIRMWARE("myri10ge_ethp_z8e.dat");
MODULE_FIRMWARE("myri10ge_eth_z8e.dat");
MODULE_FIRMWARE("myri10ge_rss_ethp_z8e.dat");
MODULE_FIRMWARE("myri10ge_rss_eth_z8e.dat");

/* Careful: must be accessed under kernel_param_lock() */
static char *myri10ge_fw_name = NULL;
module_param(myri10ge_fw_name, charp, 0644);
MODULE_PARM_DESC(myri10ge_fw_name, "Firmware image name");

#define MYRI10GE_MAX_BOARDS 8
static char *myri10ge_fw_names[MYRI10GE_MAX_BOARDS] =
    {[0 ... (MYRI10GE_MAX_BOARDS - 1)] = NULL };
module_param_array_named(myri10ge_fw_names, myri10ge_fw_names, charp, NULL,
    0444);
MODULE_PARM_DESC(myri10ge_fw_names, "Firmware image names per board");

static int myri10ge_ecrc_enable = 1;
module_param(myri10ge_ecrc_enable, int, 0444);
MODULE_PARM_DESC(myri10ge_ecrc_enable, "Enable Extended CRC on PCI-E");

static int myri10ge_small_bytes = -1; /* -1 == auto */
module_param(myri10ge_small_bytes, int, 0644);
MODULE_PARM_DESC(myri10ge_small_bytes, "Threshold of small packets");

static int myri10ge_msi = 1; /* enable msi by default */
module_param(myri10ge_msi, int, 0644);
MODULE_PARM_DESC(myri10ge_msi, "Enable Message Signalled Interrupts");

static int myri10ge_intr_coal_delay = 75;
module_param(myri10ge_intr_coal_delay, int, 0444);
MODULE_PARM_DESC(myri10ge_intr_coal_delay, "Interrupt coalescing delay");

static int myri10ge_flow_control = 1;
module_param(myri10ge_flow_control, int, 0444);
MODULE_PARM_DESC(myri10ge_flow_control, "Pause parameter");

static int myri10ge_deassert_wait = 1;
module_param(myri10ge_deassert_wait, int, 0644);
MODULE_PARM_DESC(myri10ge_deassert_wait,
   "Wait when deasserting legacy interrupts");

static int myri10ge_force_firmware = 0;
module_param(myri10ge_force_firmware, int, 0444);
MODULE_PARM_DESC(myri10ge_force_firmware,
   "Force firmware to assume aligned completions");

static int myri10ge_initial_mtu = MYRI10GE_MAX_ETHER_MTU - ETH_HLEN;
module_param(myri10ge_initial_mtu, int, 0444);
MODULE_PARM_DESC(myri10ge_initial_mtu, "Initial MTU");

static int myri10ge_napi_weight = 64;
module_param(myri10ge_napi_weight, int, 0444);
MODULE_PARM_DESC(myri10ge_napi_weight, "Set NAPI weight");

static int myri10ge_watchdog_timeout = 1;
module_param(myri10ge_watchdog_timeout, int, 0444);
MODULE_PARM_DESC(myri10ge_watchdog_timeout, "Set watchdog timeout");

static int myri10ge_max_irq_loops = 1048576;
module_param(myri10ge_max_irq_loops, int, 0444);
MODULE_PARM_DESC(myri10ge_max_irq_loops,
   "Set stuck legacy IRQ detection threshold");

#define MYRI10GE_MSG_DEFAULT NETIF_MSG_LINK

static int myri10ge_debug = -1; /* defaults above */
module_param(myri10ge_debug, int, 0);
MODULE_PARM_DESC(myri10ge_debug, "Debug level (0=none,...,16=all)");

static int myri10ge_fill_thresh = 256;
module_param(myri10ge_fill_thresh, int, 0644);
MODULE_PARM_DESC(myri10ge_fill_thresh, "Number of empty rx slots allowed");

static int myri10ge_reset_recover = 1;

static int myri10ge_max_slices = 1;
module_param(myri10ge_max_slices, int, 0444);
MODULE_PARM_DESC(myri10ge_max_slices, "Max tx/rx queues");

static int myri10ge_rss_hash = MXGEFW_RSS_HASH_TYPE_SRC_DST_PORT;
module_param(myri10ge_rss_hash, int, 0444);
MODULE_PARM_DESC(myri10ge_rss_hash, "Type of RSS hashing to do");

static int myri10ge_dca = 1;
module_param(myri10ge_dca, int, 0444);
MODULE_PARM_DESC(myri10ge_dca, "Enable DCA if possible");

#define MYRI10GE_FW_OFFSET 1024*1024
#define MYRI10GE_HIGHPART_TO_U32(X) \
(sizeof (X) == 8) ? ((u32)((u64)(X) >> 32)) : (0)
#define MYRI10GE_LOWPART_TO_U32(X) ((u32)(X))

#define myri10ge_pio_copy(to,from,size) __iowrite64_copy(to,from,size/8)

static void myri10ge_set_multicast_list(struct net_device *dev);
static netdev_tx_t myri10ge_sw_tso(struct sk_buff *skb,
      struct net_device *dev);

static inline void put_be32(__be32 val, __be32 __iomem * p)
{
 __raw_writel((__force __u32) val, (__force void __iomem *)p);
}

static void myri10ge_get_stats(struct net_device *dev,
          struct rtnl_link_stats64 *stats);

static void set_fw_name(struct myri10ge_priv *mgp, char *name, bool allocated)
{
 if (mgp->fw_name_allocated)
  kfree(mgp->fw_name);
 mgp->fw_name = name;
 mgp->fw_name_allocated = allocated;
}

static int
myri10ge_send_cmd(struct myri10ge_priv *mgp, u32 cmd,
    struct myri10ge_cmd *data, int atomic)
{
 struct mcp_cmd *buf;
 char buf_bytes[sizeof(*buf) + 8];
 struct mcp_cmd_response *response = mgp->cmd;
 char __iomem *cmd_addr = mgp->sram + MXGEFW_ETH_CMD;
 u32 dma_low, dma_high, result, value;
 int sleep_total = 0;

 /* ensure buf is aligned to 8 bytes */
 buf = (struct mcp_cmd *)ALIGN((unsigned long)buf_bytes, 8);

 buf->data0 = htonl(data->data0);
 buf->data1 = htonl(data->data1);
 buf->data2 = htonl(data->data2);
 buf->cmd = htonl(cmd);
 dma_low = MYRI10GE_LOWPART_TO_U32(mgp->cmd_bus);
 dma_high = MYRI10GE_HIGHPART_TO_U32(mgp->cmd_bus);

 buf->response_addr.low = htonl(dma_low);
 buf->response_addr.high = htonl(dma_high);
 response->result = htonl(MYRI10GE_NO_RESPONSE_RESULT);
 mb();
 myri10ge_pio_copy(cmd_addr, buf, sizeof(*buf));

 /* wait up to 15ms. Longest command is the DMA benchmark,
 * which is capped at 5ms, but runs from a timeout handler
 * that runs every 7.8ms. So a 15ms timeout leaves us with
 * a 2.2ms margin
 */

 if (atomic) {
  /* if atomic is set, do not sleep,
 * and try to get the completion quickly
 * (1ms will be enough for those commands) */

  for (sleep_total = 0;
       sleep_total < 1000 &&
       response->result == htonl(MYRI10GE_NO_RESPONSE_RESULT);
       sleep_total += 10) {
   udelay(10);
   mb();
  }
 } else {
  /* use msleep for most command */
  for (sleep_total = 0;
       sleep_total < 15 &&
       response->result == htonl(MYRI10GE_NO_RESPONSE_RESULT);
       sleep_total++)
   msleep(1);
 }

 result = ntohl(response->result);
 value = ntohl(response->data);
 if (result != MYRI10GE_NO_RESPONSE_RESULT) {
  if (result == 0) {
   data->data0 = value;
   return 0;
  } else if (result == MXGEFW_CMD_UNKNOWN) {
   return -ENOSYS;
  } else if (result == MXGEFW_CMD_ERROR_UNALIGNED) {
   return -E2BIG;
  } else if (result == MXGEFW_CMD_ERROR_RANGE &&
      cmd == MXGEFW_CMD_ENABLE_RSS_QUEUES &&
      (data->
       data1 & MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES) !=
      0) {
   return -ERANGE;
  } else {
   dev_err(&mgp->pdev->dev,
    "command %d failed, result = %d\n",
    cmd, result);
   return -ENXIO;
  }
 }

 dev_err(&mgp->pdev->dev, "command %d timed out, result = %d\n",
  cmd, result);
 return -EAGAIN;
}

/*
 * The eeprom strings on the lanaiX have the format
 * SN=x\0
 * MAC=x:x:x:x:x:x\0
 * PT:ddd mmm xx xx:xx:xx xx\0
 * PV:ddd mmm xx xx:xx:xx xx\0
 */

static int myri10ge_read_mac_addr(struct myri10ge_priv *mgp)
{
 char *ptr, *limit;
 int i;

 ptr = mgp->eeprom_strings;
 limit = mgp->eeprom_strings + MYRI10GE_EEPROM_STRINGS_SIZE;

 while (*ptr != '\0' && ptr < limit) {
  if (memcmp(ptr, "MAC=", 4) == 0) {
   ptr += 4;
   mgp->mac_addr_string = ptr;
   for (i = 0; i < 6; i++) {
    if ((ptr + 2) > limit)
     goto abort;
    mgp->mac_addr[i] =
        simple_strtoul(ptr, &ptr, 16);
    ptr += 1;
   }
  }
  if (memcmp(ptr, "PC=", 3) == 0) {
   ptr += 3;
   mgp->product_code_string = ptr;
  }
  if (memcmp((const void *)ptr, "SN=", 3) == 0) {
   ptr += 3;
   mgp->serial_number = simple_strtoul(ptr, &ptr, 10);
  }
  while (ptr < limit && *ptr++) ;
 }

 return 0;

abort:
 dev_err(&mgp->pdev->dev, "failed to parse eeprom_strings\n");
 return -ENXIO;
}

/*
 * Enable or disable periodic RDMAs from the host to make certain
 * chipsets resend dropped PCIe messages
 */


static void myri10ge_dummy_rdma(struct myri10ge_priv *mgp, int enable)
{
 char __iomem *submit;
 __be32 buf[16] __attribute__ ((__aligned__(8)));
 u32 dma_low, dma_high;
 int i;

 /* clear confirmation addr */
 mgp->cmd->data = 0;
 mb();

 /* send a rdma command to the PCIe engine, and wait for the
 * response in the confirmation address.  The firmware should
 * write a -1 there to indicate it is alive and well
 */

 dma_low = MYRI10GE_LOWPART_TO_U32(mgp->cmd_bus);
 dma_high = MYRI10GE_HIGHPART_TO_U32(mgp->cmd_bus);

 buf[0] = htonl(dma_high); /* confirm addr MSW */
 buf[1] = htonl(dma_low); /* confirm addr LSW */
 buf[2] = MYRI10GE_NO_CONFIRM_DATA; /* confirm data */
 buf[3] = htonl(dma_high); /* dummy addr MSW */
 buf[4] = htonl(dma_low); /* dummy addr LSW */
 buf[5] = htonl(enable); /* enable? */

 submit = mgp->sram + MXGEFW_BOOT_DUMMY_RDMA;

 myri10ge_pio_copy(submit, &buf, sizeof(buf));
 for (i = 0; mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA && i < 20; i++)
  msleep(1);
 if (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA)
  dev_err(&mgp->pdev->dev, "dummy rdma %s failed\n",
   (enable ? "enable" : "disable"));
}

static int
myri10ge_validate_firmware(struct myri10ge_priv *mgp,
      struct mcp_gen_header *hdr)
{
 struct device *dev = &mgp->pdev->dev;

 /* check firmware type */
 if (ntohl(hdr->mcp_type) != MCP_TYPE_ETH) {
  dev_err(dev, "Bad firmware type: 0x%x\n", ntohl(hdr->mcp_type));
  return -EINVAL;
 }

 /* save firmware version for ethtool */
 strscpy(mgp->fw_version, hdr->version, sizeof(mgp->fw_version));

 sscanf(mgp->fw_version, "%d.%d.%d", &mgp->fw_ver_major,
        &mgp->fw_ver_minor, &mgp->fw_ver_tiny);

 if (!(mgp->fw_ver_major == MXGEFW_VERSION_MAJOR &&
       mgp->fw_ver_minor == MXGEFW_VERSION_MINOR)) {
  dev_err(dev, "Found firmware version %s\n", mgp->fw_version);
  dev_err(dev, "Driver needs %d.%d\n", MXGEFW_VERSION_MAJOR,
   MXGEFW_VERSION_MINOR);
  return -EINVAL;
 }
 return 0;
}

static int myri10ge_load_hotplug_firmware(struct myri10ge_priv *mgp, u32 * size)
{
 unsigned crc, reread_crc;
 const struct firmware *fw;
 struct device *dev = &mgp->pdev->dev;
 unsigned char *fw_readback;
 struct mcp_gen_header *hdr;
 size_t hdr_offset;
 int status;
 unsigned i;

 if (request_firmware(&fw, mgp->fw_name, dev) < 0) {
  dev_err(dev, "Unable to load %s firmware image via hotplug\n",
   mgp->fw_name);
  status = -EINVAL;
  goto abort_with_nothing;
 }

 /* check size */

 if (fw->size >= mgp->sram_size - MYRI10GE_FW_OFFSET ||
     fw->size < MCP_HEADER_PTR_OFFSET + 4) {
  dev_err(dev, "Firmware size invalid:%d\n", (int)fw->size);
  status = -EINVAL;
  goto abort_with_fw;
 }

 /* check id */
 hdr_offset = ntohl(*(__be32 *) (fw->data + MCP_HEADER_PTR_OFFSET));
 if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > fw->size) {
  dev_err(dev, "Bad firmware file\n");
  status = -EINVAL;
  goto abort_with_fw;
 }
 hdr = (void *)(fw->data + hdr_offset);

 status = myri10ge_validate_firmware(mgp, hdr);
 if (status != 0)
  goto abort_with_fw;

 crc = crc32(~0, fw->data, fw->size);
 for (i = 0; i < fw->size; i += 256) {
  myri10ge_pio_copy(mgp->sram + MYRI10GE_FW_OFFSET + i,
      fw->data + i,
      min(256U, (unsigned)(fw->size - i)));
  mb();
  readb(mgp->sram);
 }
 fw_readback = vmalloc(fw->size);
 if (!fw_readback) {
  status = -ENOMEM;
  goto abort_with_fw;
 }
 /* corruption checking is good for parity recovery and buggy chipset */
 memcpy_fromio(fw_readback, mgp->sram + MYRI10GE_FW_OFFSET, fw->size);
 reread_crc = crc32(~0, fw_readback, fw->size);
 vfree(fw_readback);
 if (crc != reread_crc) {
  dev_err(dev, "CRC failed(fw-len=%u), got 0x%x (expect 0x%x)\n",
   (unsigned)fw->size, reread_crc, crc);
  status = -EIO;
  goto abort_with_fw;
 }
 *size = (u32) fw->size;

abort_with_fw:
 release_firmware(fw);

abort_with_nothing:
 return status;
}

static int myri10ge_adopt_running_firmware(struct myri10ge_priv *mgp)
{
 struct mcp_gen_header *hdr;
 struct device *dev = &mgp->pdev->dev;
 const size_t bytes = sizeof(struct mcp_gen_header);
 size_t hdr_offset;
 int status;

 /* find running firmware header */
 hdr_offset = swab32(readl(mgp->sram + MCP_HEADER_PTR_OFFSET));

 if ((hdr_offset & 3) || hdr_offset + sizeof(*hdr) > mgp->sram_size) {
  dev_err(dev, "Running firmware has bad header offset (%d)\n",
   (int)hdr_offset);
  return -EIO;
 }

 /* copy header of running firmware from SRAM to host memory to
 * validate firmware */

 hdr = kmalloc(bytes, GFP_KERNEL);
 if (hdr == NULL)
  return -ENOMEM;

 memcpy_fromio(hdr, mgp->sram + hdr_offset, bytes);
 status = myri10ge_validate_firmware(mgp, hdr);
 kfree(hdr);

 /* check to see if adopted firmware has bug where adopting
 * it will cause broadcasts to be filtered unless the NIC
 * is kept in ALLMULTI mode */

 if (mgp->fw_ver_major == 1 && mgp->fw_ver_minor == 4 &&
     mgp->fw_ver_tiny >= 4 && mgp->fw_ver_tiny <= 11) {
  mgp->adopted_rx_filter_bug = 1;
  dev_warn(dev, "Adopting fw %d.%d.%d: "
    "working around rx filter bug\n",
    mgp->fw_ver_major, mgp->fw_ver_minor,
    mgp->fw_ver_tiny);
 }
 return status;
}

static int myri10ge_get_firmware_capabilities(struct myri10ge_priv *mgp)
{
 struct myri10ge_cmd cmd;
 int status;

 /* probe for IPv6 TSO support */
 mgp->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO;
 status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_MAX_TSO6_HDR_SIZE,
       &cmd, 0);
 if (status == 0) {
  mgp->max_tso6 = cmd.data0;
  mgp->features |= NETIF_F_TSO6;
 }

 status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd, 0);
 if (status != 0) {
  dev_err(&mgp->pdev->dev,
   "failed MXGEFW_CMD_GET_RX_RING_SIZE\n");
  return -ENXIO;
 }

 mgp->max_intr_slots = 2 * (cmd.data0 / sizeof(struct mcp_dma_addr));

 return 0;
}

static int myri10ge_load_firmware(struct myri10ge_priv *mgp, int adopt)
{
 char __iomem *submit;
 __be32 buf[16] __attribute__ ((__aligned__(8)));
 u32 dma_low, dma_high, size;
 int status, i;

 size = 0;
 status = myri10ge_load_hotplug_firmware(mgp, &size);
 if (status) {
  if (!adopt)
   return status;
  dev_warn(&mgp->pdev->dev, "hotplug firmware loading failed\n");

  /* Do not attempt to adopt firmware if there
 * was a bad crc */

  if (status == -EIO)
   return status;

  status = myri10ge_adopt_running_firmware(mgp);
  if (status != 0) {
   dev_err(&mgp->pdev->dev,
    "failed to adopt running firmware\n");
   return status;
  }
  dev_info(&mgp->pdev->dev,
    "Successfully adopted running firmware\n");
  if (mgp->tx_boundary == 4096) {
   dev_warn(&mgp->pdev->dev,
     "Using firmware currently running on NIC"
     ". For optimal\n");
   dev_warn(&mgp->pdev->dev,
     "performance consider loading optimized "
     "firmware\n");
   dev_warn(&mgp->pdev->dev, "via hotplug\n");
  }

  set_fw_name(mgp, "adopted"false);
  mgp->tx_boundary = 2048;
  myri10ge_dummy_rdma(mgp, 1);
  status = myri10ge_get_firmware_capabilities(mgp);
  return status;
 }

 /* clear confirmation addr */
 mgp->cmd->data = 0;
 mb();

 /* send a reload command to the bootstrap MCP, and wait for the
 *  response in the confirmation address.  The firmware should
 * write a -1 there to indicate it is alive and well
 */

 dma_low = MYRI10GE_LOWPART_TO_U32(mgp->cmd_bus);
 dma_high = MYRI10GE_HIGHPART_TO_U32(mgp->cmd_bus);

 buf[0] = htonl(dma_high); /* confirm addr MSW */
 buf[1] = htonl(dma_low); /* confirm addr LSW */
 buf[2] = MYRI10GE_NO_CONFIRM_DATA; /* confirm data */

 /* FIX: All newest firmware should un-protect the bottom of
 * the sram before handoff. However, the very first interfaces
 * do not. Therefore the handoff copy must skip the first 8 bytes
 */

 buf[3] = htonl(MYRI10GE_FW_OFFSET + 8); /* where the code starts */
 buf[4] = htonl(size - 8); /* length of code */
 buf[5] = htonl(8); /* where to copy to */
 buf[6] = htonl(0); /* where to jump to */

 submit = mgp->sram + MXGEFW_BOOT_HANDOFF;

 myri10ge_pio_copy(submit, &buf, sizeof(buf));
 mb();
 msleep(1);
 mb();
 i = 0;
 while (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA && i < 9) {
  msleep(1 << i);
  i++;
 }
 if (mgp->cmd->data != MYRI10GE_NO_CONFIRM_DATA) {
  dev_err(&mgp->pdev->dev, "handoff failed\n");
  return -ENXIO;
 }
 myri10ge_dummy_rdma(mgp, 1);
 status = myri10ge_get_firmware_capabilities(mgp);

 return status;
}

static int myri10ge_update_mac_address(struct myri10ge_priv *mgp,
           const u8 * addr)
{
 struct myri10ge_cmd cmd;
 int status;

 cmd.data0 = ((addr[0] << 24) | (addr[1] << 16)
       | (addr[2] << 8) | addr[3]);

 cmd.data1 = ((addr[4] << 8) | (addr[5]));

 status = myri10ge_send_cmd(mgp, MXGEFW_SET_MAC_ADDRESS, &cmd, 0);
 return status;
}

static int myri10ge_change_pause(struct myri10ge_priv *mgp, int pause)
{
 struct myri10ge_cmd cmd;
 int status, ctl;

 ctl = pause ? MXGEFW_ENABLE_FLOW_CONTROL : MXGEFW_DISABLE_FLOW_CONTROL;
 status = myri10ge_send_cmd(mgp, ctl, &cmd, 0);

 if (status) {
  netdev_err(mgp->dev, "Failed to set flow control mode\n");
  return status;
 }
 mgp->pause = pause;
 return 0;
}

static void
myri10ge_change_promisc(struct myri10ge_priv *mgp, int promisc, int atomic)
{
 struct myri10ge_cmd cmd;
 int status, ctl;

 ctl = promisc ? MXGEFW_ENABLE_PROMISC : MXGEFW_DISABLE_PROMISC;
 status = myri10ge_send_cmd(mgp, ctl, &cmd, atomic);
 if (status)
  netdev_err(mgp->dev, "Failed to set promisc mode\n");
}

static int myri10ge_dma_test(struct myri10ge_priv *mgp, int test_type)
{
 struct myri10ge_cmd cmd;
 int status;
 u32 len;
 struct page *dmatest_page;
 dma_addr_t dmatest_bus;
 char *test = " ";

 dmatest_page = alloc_page(GFP_KERNEL);
 if (!dmatest_page)
  return -ENOMEM;
 dmatest_bus = dma_map_page(&mgp->pdev->dev, dmatest_page, 0,
       PAGE_SIZE, DMA_BIDIRECTIONAL);
 if (unlikely(dma_mapping_error(&mgp->pdev->dev, dmatest_bus))) {
  __free_page(dmatest_page);
  return -ENOMEM;
 }

 /* Run a small DMA test.
 * The magic multipliers to the length tell the firmware
 * to do DMA read, write, or read+write tests.  The
 * results are returned in cmd.data0.  The upper 16
 * bits or the return is the number of transfers completed.
 * The lower 16 bits is the time in 0.5us ticks that the
 * transfers took to complete.
 */


 len = mgp->tx_boundary;

 cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus);
 cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus);
 cmd.data2 = len * 0x10000;
 status = myri10ge_send_cmd(mgp, test_type, &cmd, 0);
 if (status != 0) {
  test = "read";
  goto abort;
 }
 mgp->read_dma = ((cmd.data0 >> 16) * len * 2) / (cmd.data0 & 0xffff);
 cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus);
 cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus);
 cmd.data2 = len * 0x1;
 status = myri10ge_send_cmd(mgp, test_type, &cmd, 0);
 if (status != 0) {
  test = "write";
  goto abort;
 }
 mgp->write_dma = ((cmd.data0 >> 16) * len * 2) / (cmd.data0 & 0xffff);

 cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus);
 cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus);
 cmd.data2 = len * 0x10001;
 status = myri10ge_send_cmd(mgp, test_type, &cmd, 0);
 if (status != 0) {
  test = "read/write";
  goto abort;
 }
 mgp->read_write_dma = ((cmd.data0 >> 16) * len * 2 * 2) /
     (cmd.data0 & 0xffff);

abort:
 dma_unmap_page(&mgp->pdev->dev, dmatest_bus, PAGE_SIZE,
         DMA_BIDIRECTIONAL);
 put_page(dmatest_page);

 if (status != 0 && test_type != MXGEFW_CMD_UNALIGNED_TEST)
  dev_warn(&mgp->pdev->dev, "DMA %s benchmark failed: %d\n",
    test, status);

 return status;
}

static int myri10ge_reset(struct myri10ge_priv *mgp)
{
 struct myri10ge_cmd cmd;
 struct myri10ge_slice_state *ss;
 int i, status;
 size_t bytes;
#ifdef CONFIG_MYRI10GE_DCA
 unsigned long dca_tag_off;
#endif

 /* try to send a reset command to the card to see if it
 * is alive */

 memset(&cmd, 0, sizeof(cmd));
 status = myri10ge_send_cmd(mgp, MXGEFW_CMD_RESET, &cmd, 0);
 if (status != 0) {
  dev_err(&mgp->pdev->dev, "failed reset\n");
  return -ENXIO;
 }

 (void)myri10ge_dma_test(mgp, MXGEFW_DMA_TEST);
 /*
 * Use non-ndis mcp_slot (eg, 4 bytes total,
 * no toeplitz hash value returned.  Older firmware will
 * not understand this command, but will use the correct
 * sized mcp_slot, so we ignore error returns
 */

 cmd.data0 = MXGEFW_RSS_MCP_SLOT_TYPE_MIN;
 (void)myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_RSS_MCP_SLOT_TYPE, &cmd, 0);

 /* Now exchange information about interrupts  */

 bytes = mgp->max_intr_slots * sizeof(*mgp->ss[0].rx_done.entry);
 cmd.data0 = (u32) bytes;
 status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_SIZE, &cmd, 0);

 /*
 * Even though we already know how many slices are supported
 * via myri10ge_probe_slices() MXGEFW_CMD_GET_MAX_RSS_QUEUES
 * has magic side effects, and must be called after a reset.
 * It must be called prior to calling any RSS related cmds,
 * including assigning an interrupt queue for anything but
 * slice 0.  It must also be called *after*
 * MXGEFW_CMD_SET_INTRQ_SIZE, since the intrq size is used by
 * the firmware to compute offsets.
 */


 if (mgp->num_slices > 1) {

  /* ask the maximum number of slices it supports */
  status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_MAX_RSS_QUEUES,
        &cmd, 0);
  if (status != 0) {
   dev_err(&mgp->pdev->dev,
    "failed to get number of slices\n");
  }

  /*
 * MXGEFW_CMD_ENABLE_RSS_QUEUES must be called prior
 * to setting up the interrupt queue DMA
 */


  cmd.data0 = mgp->num_slices;
  cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
  if (mgp->dev->real_num_tx_queues > 1)
   cmd.data1 |= MXGEFW_SLICE_ENABLE_MULTIPLE_TX_QUEUES;
  status = myri10ge_send_cmd(mgp, MXGEFW_CMD_ENABLE_RSS_QUEUES,
        &cmd, 0);

  /* Firmware older than 1.4.32 only supports multiple
 * RX queues, so if we get an error, first retry using a
 * single TX queue before giving up */

  if (status != 0 && mgp->dev->real_num_tx_queues > 1) {
   netif_set_real_num_tx_queues(mgp->dev, 1);
   cmd.data0 = mgp->num_slices;
   cmd.data1 = MXGEFW_SLICE_INTR_MODE_ONE_PER_SLICE;
   status = myri10ge_send_cmd(mgp,
         MXGEFW_CMD_ENABLE_RSS_QUEUES,
         &cmd, 0);
  }

  if (status != 0) {
   dev_err(&mgp->pdev->dev,
    "failed to set number of slices\n");

   return status;
  }
 }
 for (i = 0; i < mgp->num_slices; i++) {
  ss = &mgp->ss[i];
  cmd.data0 = MYRI10GE_LOWPART_TO_U32(ss->rx_done.bus);
  cmd.data1 = MYRI10GE_HIGHPART_TO_U32(ss->rx_done.bus);
  cmd.data2 = i;
  status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_INTRQ_DMA,
         &cmd, 0);
 }

 status |=
     myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_IRQ_ACK_OFFSET, &cmd, 0);
 for (i = 0; i < mgp->num_slices; i++) {
  ss = &mgp->ss[i];
  ss->irq_claim =
      (__iomem __be32 *) (mgp->sram + cmd.data0 + 8 * i);
 }
 status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_IRQ_DEASSERT_OFFSET,
        &cmd, 0);
 mgp->irq_deassert = (__iomem __be32 *) (mgp->sram + cmd.data0);

 status |= myri10ge_send_cmd
     (mgp, MXGEFW_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd, 0);
 mgp->intr_coal_delay_ptr = (__iomem __be32 *) (mgp->sram + cmd.data0);
 if (status != 0) {
  dev_err(&mgp->pdev->dev, "failed set interrupt parameters\n");
  return status;
 }
 put_be32(htonl(mgp->intr_coal_delay), mgp->intr_coal_delay_ptr);

#ifdef CONFIG_MYRI10GE_DCA
 status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_DCA_OFFSET, &cmd, 0);
 dca_tag_off = cmd.data0;
 for (i = 0; i < mgp->num_slices; i++) {
  ss = &mgp->ss[i];
  if (status == 0) {
   ss->dca_tag = (__iomem __be32 *)
       (mgp->sram + dca_tag_off + 4 * i);
  } else {
   ss->dca_tag = NULL;
  }
 }
#endif    /* CONFIG_MYRI10GE_DCA */

 /* reset mcp/driver shared state back to 0 */

 mgp->link_changes = 0;
 for (i = 0; i < mgp->num_slices; i++) {
  ss = &mgp->ss[i];

  memset(ss->rx_done.entry, 0, bytes);
  ss->tx.req = 0;
  ss->tx.done = 0;
  ss->tx.pkt_start = 0;
  ss->tx.pkt_done = 0;
  ss->rx_big.cnt = 0;
  ss->rx_small.cnt = 0;
  ss->rx_done.idx = 0;
  ss->rx_done.cnt = 0;
  ss->tx.wake_queue = 0;
  ss->tx.stop_queue = 0;
 }

 status = myri10ge_update_mac_address(mgp, mgp->dev->dev_addr);
 myri10ge_change_pause(mgp, mgp->pause);
 myri10ge_set_multicast_list(mgp->dev);
 return status;
}

#ifdef CONFIG_MYRI10GE_DCA
static int myri10ge_toggle_relaxed(struct pci_dev *pdev, int on)
{
 int ret;
 u16 ctl;

 pcie_capability_read_word(pdev, PCI_EXP_DEVCTL, &ctl);

 ret = (ctl & PCI_EXP_DEVCTL_RELAX_EN) >> 4;
 if (ret != on) {
  ctl &= ~PCI_EXP_DEVCTL_RELAX_EN;
  ctl |= (on << 4);
  pcie_capability_write_word(pdev, PCI_EXP_DEVCTL, ctl);
 }
 return ret;
}

static void
myri10ge_write_dca(struct myri10ge_slice_state *ss, int cpu, int tag)
{
 ss->cached_dca_tag = tag;
 put_be32(htonl(tag), ss->dca_tag);
}

static inline void myri10ge_update_dca(struct myri10ge_slice_state *ss)
{
 int cpu = get_cpu();
 int tag;

 if (cpu != ss->cpu) {
  tag = dca3_get_tag(&ss->mgp->pdev->dev, cpu);
  if (ss->cached_dca_tag != tag)
   myri10ge_write_dca(ss, cpu, tag);
  ss->cpu = cpu;
 }
 put_cpu();
}

static void myri10ge_setup_dca(struct myri10ge_priv *mgp)
{
 int err, i;
 struct pci_dev *pdev = mgp->pdev;

 if (mgp->ss[0].dca_tag == NULL || mgp->dca_enabled)
  return;
 if (!myri10ge_dca) {
  dev_err(&pdev->dev, "dca disabled by administrator\n");
  return;
 }
 err = dca_add_requester(&pdev->dev);
 if (err) {
  if (err != -ENODEV)
   dev_err(&pdev->dev,
    "dca_add_requester() failed, err=%d\n", err);
  return;
 }
 mgp->relaxed_order = myri10ge_toggle_relaxed(pdev, 0);
 mgp->dca_enabled = 1;
 for (i = 0; i < mgp->num_slices; i++) {
  mgp->ss[i].cpu = -1;
  mgp->ss[i].cached_dca_tag = -1;
  myri10ge_update_dca(&mgp->ss[i]);
 }
}

static void myri10ge_teardown_dca(struct myri10ge_priv *mgp)
{
 struct pci_dev *pdev = mgp->pdev;

 if (!mgp->dca_enabled)
  return;
 mgp->dca_enabled = 0;
 if (mgp->relaxed_order)
  myri10ge_toggle_relaxed(pdev, 1);
 dca_remove_requester(&pdev->dev);
}

static int myri10ge_notify_dca_device(struct device *dev, void *data)
{
 struct myri10ge_priv *mgp;
 unsigned long event;

 mgp = dev_get_drvdata(dev);
 event = *(unsigned long *)data;

 if (event == DCA_PROVIDER_ADD)
  myri10ge_setup_dca(mgp);
 else if (event == DCA_PROVIDER_REMOVE)
  myri10ge_teardown_dca(mgp);
 return 0;
}
#endif    /* CONFIG_MYRI10GE_DCA */

static inline void
myri10ge_submit_8rx(struct mcp_kreq_ether_recv __iomem * dst,
      struct mcp_kreq_ether_recv *src)
{
 __be32 low;

 low = src->addr_low;
 src->addr_low = htonl(DMA_BIT_MASK(32));
 myri10ge_pio_copy(dst, src, 4 * sizeof(*src));
 mb();
 myri10ge_pio_copy(dst + 4, src + 4, 4 * sizeof(*src));
 mb();
 src->addr_low = low;
 put_be32(low, &dst->addr_low);
 mb();
}

static void
myri10ge_alloc_rx_pages(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx,
   int bytes, int watchdog)
{
 struct page *page;
 dma_addr_t bus;
 int idx;
#if MYRI10GE_ALLOC_SIZE > 4096
 int end_offset;
#endif

 if (unlikely(rx->watchdog_needed && !watchdog))
  return;

 /* try to refill entire ring */
 while (rx->fill_cnt != (rx->cnt + rx->mask + 1)) {
  idx = rx->fill_cnt & rx->mask;
  if (rx->page_offset + bytes <= MYRI10GE_ALLOC_SIZE) {
   /* we can use part of previous page */
   get_page(rx->page);
  } else {
   /* we need a new page */
   page =
       alloc_pages(GFP_ATOMIC | __GFP_COMP,
     MYRI10GE_ALLOC_ORDER);
   if (unlikely(page == NULL)) {
    if (rx->fill_cnt - rx->cnt < 16)
     rx->watchdog_needed = 1;
    return;
   }

   bus = dma_map_page(&mgp->pdev->dev, page, 0,
        MYRI10GE_ALLOC_SIZE,
        DMA_FROM_DEVICE);
   if (unlikely(dma_mapping_error(&mgp->pdev->dev, bus))) {
    __free_pages(page, MYRI10GE_ALLOC_ORDER);
    if (rx->fill_cnt - rx->cnt < 16)
     rx->watchdog_needed = 1;
    return;
   }

   rx->page = page;
   rx->page_offset = 0;
   rx->bus = bus;

  }
  rx->info[idx].page = rx->page;
  rx->info[idx].page_offset = rx->page_offset;
  /* note that this is the address of the start of the
 * page */

  dma_unmap_addr_set(&rx->info[idx], bus, rx->bus);
  rx->shadow[idx].addr_low =
      htonl(MYRI10GE_LOWPART_TO_U32(rx->bus) + rx->page_offset);
  rx->shadow[idx].addr_high =
      htonl(MYRI10GE_HIGHPART_TO_U32(rx->bus));

  /* start next packet on a cacheline boundary */
  rx->page_offset += SKB_DATA_ALIGN(bytes);

#if MYRI10GE_ALLOC_SIZE > 4096
  /* don't cross a 4KB boundary */
  end_offset = rx->page_offset + bytes - 1;
  if ((unsigned)(rx->page_offset ^ end_offset) > 4095)
   rx->page_offset = end_offset & ~4095;
#endif
  rx->fill_cnt++;

  /* copy 8 descriptors to the firmware at a time */
  if ((idx & 7) == 7) {
   myri10ge_submit_8rx(&rx->lanai[idx - 7],
         &rx->shadow[idx - 7]);
  }
 }
}

static inline void
myri10ge_unmap_rx_page(struct pci_dev *pdev,
         struct myri10ge_rx_buffer_state *info, int bytes)
{
 /* unmap the recvd page if we're the only or last user of it */
 if (bytes >= MYRI10GE_ALLOC_SIZE / 2 ||
     (info->page_offset + 2 * bytes) > MYRI10GE_ALLOC_SIZE) {
  dma_unmap_page(&pdev->dev, (dma_unmap_addr(info, bus)
         & ~(MYRI10GE_ALLOC_SIZE - 1)),
          MYRI10GE_ALLOC_SIZE, DMA_FROM_DEVICE);
 }
}

/*
 * GRO does not support acceleration of tagged vlan frames, and
 * this NIC does not support vlan tag offload, so we must pop
 * the tag ourselves to be able to achieve GRO performance that
 * is comparable to LRO.
 */


static inline void
myri10ge_vlan_rx(struct net_device *dev, void *addr, struct sk_buff *skb)
{
 u8 *va;
 struct vlan_ethhdr *veh;
 skb_frag_t *frag;
 __wsum vsum;

 va = addr;
 va += MXGEFW_PAD;
 veh = (struct vlan_ethhdr *)va;
 if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) ==
     NETIF_F_HW_VLAN_CTAG_RX &&
     veh->h_vlan_proto == htons(ETH_P_8021Q)) {
  /* fixup csum if needed */
  if (skb->ip_summed == CHECKSUM_COMPLETE) {
   vsum = csum_partial(va + ETH_HLEN, VLAN_HLEN, 0);
   skb->csum = csum_sub(skb->csum, vsum);
  }
  /* pop tag */
  __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(veh->h_vlan_TCI));
  memmove(va + VLAN_HLEN, va, 2 * ETH_ALEN);
  skb->len -= VLAN_HLEN;
  skb->data_len -= VLAN_HLEN;
  frag = skb_shinfo(skb)->frags;
  skb_frag_off_add(frag, VLAN_HLEN);
  skb_frag_size_sub(frag, VLAN_HLEN);
 }
}

#define MYRI10GE_HLEN 64 /* Bytes to copy from page to skb linear memory */

static inline int
myri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum)
{
 struct myri10ge_priv *mgp = ss->mgp;
 struct sk_buff *skb;
 skb_frag_t *rx_frags;
 struct myri10ge_rx_buf *rx;
 int i, idx, remainder, bytes;
 struct pci_dev *pdev = mgp->pdev;
 struct net_device *dev = mgp->dev;
 u8 *va;

 if (len <= mgp->small_bytes) {
  rx = &ss->rx_small;
  bytes = mgp->small_bytes;
 } else {
  rx = &ss->rx_big;
  bytes = mgp->big_bytes;
 }

 len += MXGEFW_PAD;
 idx = rx->cnt & rx->mask;
 va = page_address(rx->info[idx].page) + rx->info[idx].page_offset;
 prefetch(va);

 skb = napi_get_frags(&ss->napi);
 if (unlikely(skb == NULL)) {
  ss->stats.rx_dropped++;
  for (i = 0, remainder = len; remainder > 0; i++) {
   myri10ge_unmap_rx_page(pdev, &rx->info[idx], bytes);
   put_page(rx->info[idx].page);
   rx->cnt++;
   idx = rx->cnt & rx->mask;
   remainder -= MYRI10GE_ALLOC_SIZE;
  }
  return 0;
 }
 rx_frags = skb_shinfo(skb)->frags;
 /* Fill skb_frag_t(s) with data from our receive */
 for (i = 0, remainder = len; remainder > 0; i++) {
  myri10ge_unmap_rx_page(pdev, &rx->info[idx], bytes);
  skb_fill_page_desc(skb, i, rx->info[idx].page,
       rx->info[idx].page_offset,
       remainder < MYRI10GE_ALLOC_SIZE ?
       remainder : MYRI10GE_ALLOC_SIZE);
  rx->cnt++;
  idx = rx->cnt & rx->mask;
  remainder -= MYRI10GE_ALLOC_SIZE;
 }

 /* remove padding */
 skb_frag_off_add(&rx_frags[0], MXGEFW_PAD);
 skb_frag_size_sub(&rx_frags[0], MXGEFW_PAD);
 len -= MXGEFW_PAD;

 skb->len = len;
 skb->data_len = len;
 skb->truesize += len;
 if (dev->features & NETIF_F_RXCSUM) {
  skb->ip_summed = CHECKSUM_COMPLETE;
  skb->csum = csum;
 }
 myri10ge_vlan_rx(mgp->dev, va, skb);
 skb_record_rx_queue(skb, ss - &mgp->ss[0]);

 napi_gro_frags(&ss->napi);

 return 1;
}

static inline void
myri10ge_tx_done(struct myri10ge_slice_state *ss, int mcp_index)
{
 struct pci_dev *pdev = ss->mgp->pdev;
 struct myri10ge_tx_buf *tx = &ss->tx;
 struct netdev_queue *dev_queue;
 struct sk_buff *skb;
 int idx, len;

 while (tx->pkt_done != mcp_index) {
  idx = tx->done & tx->mask;
  skb = tx->info[idx].skb;

  /* Mark as free */
  tx->info[idx].skb = NULL;
  if (tx->info[idx].last) {
   tx->pkt_done++;
   tx->info[idx].last = 0;
  }
  tx->done++;
  len = dma_unmap_len(&tx->info[idx], len);
  dma_unmap_len_set(&tx->info[idx], len, 0);
  if (skb) {
   ss->stats.tx_bytes += skb->len;
   ss->stats.tx_packets++;
   dev_consume_skb_irq(skb);
   if (len)
    dma_unmap_single(&pdev->dev,
       dma_unmap_addr(&tx->info[idx],
        bus), len,
       DMA_TO_DEVICE);
  } else {
   if (len)
    dma_unmap_page(&pdev->dev,
            dma_unmap_addr(&tx->info[idx],
             bus), len,
            DMA_TO_DEVICE);
  }
 }

 dev_queue = netdev_get_tx_queue(ss->dev, ss - ss->mgp->ss);
 /*
 * Make a minimal effort to prevent the NIC from polling an
 * idle tx queue.  If we can't get the lock we leave the queue
 * active. In this case, either a thread was about to start
 * using the queue anyway, or we lost a race and the NIC will
 * waste some of its resources polling an inactive queue for a
 * while.
 */


 if ((ss->mgp->dev->real_num_tx_queues > 1) &&
     __netif_tx_trylock(dev_queue)) {
  if (tx->req == tx->done) {
   tx->queue_active = 0;
   put_be32(htonl(1), tx->send_stop);
   mb();
  }
  __netif_tx_unlock(dev_queue);
 }

 /* start the queue if we've stopped it */
 if (netif_tx_queue_stopped(dev_queue) &&
     tx->req - tx->done < (tx->mask >> 1) &&
     ss->mgp->running == MYRI10GE_ETH_RUNNING) {
  tx->wake_queue++;
  netif_tx_wake_queue(dev_queue);
 }
}

static inline int
myri10ge_clean_rx_done(struct myri10ge_slice_state *ss, int budget)
{
 struct myri10ge_rx_done *rx_done = &ss->rx_done;
 struct myri10ge_priv *mgp = ss->mgp;
 unsigned long rx_bytes = 0;
 unsigned long rx_packets = 0;
 unsigned long rx_ok;
 int idx = rx_done->idx;
 int cnt = rx_done->cnt;
 int work_done = 0;
 u16 length;
 __wsum checksum;

 while (rx_done->entry[idx].length != 0 && work_done < budget) {
  length = ntohs(rx_done->entry[idx].length);
  rx_done->entry[idx].length = 0;
  checksum = csum_unfold(rx_done->entry[idx].checksum);
  rx_ok = myri10ge_rx_done(ss, length, checksum);
  rx_packets += rx_ok;
  rx_bytes += rx_ok * (unsigned long)length;
  cnt++;
  idx = cnt & (mgp->max_intr_slots - 1);
  work_done++;
 }
 rx_done->idx = idx;
 rx_done->cnt = cnt;
 ss->stats.rx_packets += rx_packets;
 ss->stats.rx_bytes += rx_bytes;

 /* restock receive rings if needed */
 if (ss->rx_small.fill_cnt - ss->rx_small.cnt < myri10ge_fill_thresh)
  myri10ge_alloc_rx_pages(mgp, &ss->rx_small,
     mgp->small_bytes + MXGEFW_PAD, 0);
 if (ss->rx_big.fill_cnt - ss->rx_big.cnt < myri10ge_fill_thresh)
  myri10ge_alloc_rx_pages(mgp, &ss->rx_big, mgp->big_bytes, 0);

 return work_done;
}

static inline void myri10ge_check_statblock(struct myri10ge_priv *mgp)
{
 struct mcp_irq_data *stats = mgp->ss[0].fw_stats;

 if (unlikely(stats->stats_updated)) {
  unsigned link_up = ntohl(stats->link_up);
  if (mgp->link_state != link_up) {
   mgp->link_state = link_up;

   if (mgp->link_state == MXGEFW_LINK_UP) {
    netif_info(mgp, link, mgp->dev, "link up\n");
    netif_carrier_on(mgp->dev);
    mgp->link_changes++;
   } else {
    netif_info(mgp, link, mgp->dev, "link %s\n",
        (link_up == MXGEFW_LINK_MYRINET ?
         "mismatch (Myrinet detected)" :
         "down"));
    netif_carrier_off(mgp->dev);
    mgp->link_changes++;
   }
  }
  if (mgp->rdma_tags_available !=
      ntohl(stats->rdma_tags_available)) {
   mgp->rdma_tags_available =
       ntohl(stats->rdma_tags_available);
   netdev_warn(mgp->dev, "RDMA timed out! %d tags left\n",
        mgp->rdma_tags_available);
  }
  mgp->down_cnt += stats->link_down;
  if (stats->link_down)
   wake_up(&mgp->down_wq);
 }
}

static int myri10ge_poll(struct napi_struct *napi, int budget)
{
 struct myri10ge_slice_state *ss =
     container_of(napi, struct myri10ge_slice_state, napi);
 int work_done;

#ifdef CONFIG_MYRI10GE_DCA
 if (ss->mgp->dca_enabled)
  myri10ge_update_dca(ss);
#endif
 /* process as many rx events as NAPI will allow */
 work_done = myri10ge_clean_rx_done(ss, budget);

 if (work_done < budget) {
  napi_complete_done(napi, work_done);
  put_be32(htonl(3), ss->irq_claim);
 }
 return work_done;
}

static irqreturn_t myri10ge_intr(int irq, void *arg)
{
 struct myri10ge_slice_state *ss = arg;
 struct myri10ge_priv *mgp = ss->mgp;
 struct mcp_irq_data *stats = ss->fw_stats;
 struct myri10ge_tx_buf *tx = &ss->tx;
 u32 send_done_count;
 int i;

 /* an interrupt on a non-zero receive-only slice is implicitly
 * valid  since MSI-X irqs are not shared */

 if ((mgp->dev->real_num_tx_queues == 1) && (ss != mgp->ss)) {
  napi_schedule(&ss->napi);
  return IRQ_HANDLED;
 }

 /* make sure it is our IRQ, and that the DMA has finished */
 if (unlikely(!stats->valid))
  return IRQ_NONE;

 /* low bit indicates receives are present, so schedule
 * napi poll handler */

 if (stats->valid & 1)
  napi_schedule(&ss->napi);

 if (!mgp->msi_enabled && !mgp->msix_enabled) {
  put_be32(0, mgp->irq_deassert);
  if (!myri10ge_deassert_wait)
   stats->valid = 0;
  mb();
 } else
  stats->valid = 0;

 /* Wait for IRQ line to go low, if using INTx */
 i = 0;
 while (1) {
  i++;
  /* check for transmit completes and receives */
  send_done_count = ntohl(stats->send_done_count);
  if (send_done_count != tx->pkt_done)
   myri10ge_tx_done(ss, (int)send_done_count);
  if (unlikely(i > myri10ge_max_irq_loops)) {
   netdev_warn(mgp->dev, "irq stuck?\n");
   stats->valid = 0;
   schedule_work(&mgp->watchdog_work);
  }
  if (likely(stats->valid == 0))
   break;
  cpu_relax();
  barrier();
 }

 /* Only slice 0 updates stats */
 if (ss == mgp->ss)
  myri10ge_check_statblock(mgp);

 put_be32(htonl(3), ss->irq_claim + 1);
 return IRQ_HANDLED;
}

static int
myri10ge_get_link_ksettings(struct net_device *netdev,
       struct ethtool_link_ksettings *cmd)
{
 struct myri10ge_priv *mgp = netdev_priv(netdev);
 char *ptr;
 int i;

 cmd->base.autoneg = AUTONEG_DISABLE;
 cmd->base.speed = SPEED_10000;
 cmd->base.duplex = DUPLEX_FULL;

 /*
 * parse the product code to deterimine the interface type
 * (CX4, XFP, Quad Ribbon Fiber) by looking at the character
 * after the 3rd dash in the driver's cached copy of the
 * EEPROM's product code string.
 */

 ptr = mgp->product_code_string;
 if (ptr == NULL) {
  netdev_err(netdev, "Missing product code\n");
  return 0;
 }
 for (i = 0; i < 3; i++, ptr++) {
  ptr = strchr(ptr, '-');
  if (ptr == NULL) {
   netdev_err(netdev, "Invalid product code %s\n",
       mgp->product_code_string);
   return 0;
  }
 }
 if (*ptr == '2')
  ptr++;
 if (*ptr == 'R' || *ptr == 'Q' || *ptr == 'S') {
  /* We've found either an XFP, quad ribbon fiber, or SFP+ */
  cmd->base.port = PORT_FIBRE;
  ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
  ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE);
 } else {
  cmd->base.port = PORT_OTHER;
 }

 return 0;
}

static void
myri10ge_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info)
{
 struct myri10ge_priv *mgp = netdev_priv(netdev);

 strscpy(info->driver, "myri10ge"sizeof(info->driver));
 strscpy(info->version, MYRI10GE_VERSION_STR, sizeof(info->version));
 strscpy(info->fw_version, mgp->fw_version, sizeof(info->fw_version));
 strscpy(info->bus_info, pci_name(mgp->pdev), sizeof(info->bus_info));
}

static int myri10ge_get_coalesce(struct net_device *netdev,
     struct ethtool_coalesce *coal,
     struct kernel_ethtool_coalesce *kernel_coal,
     struct netlink_ext_ack *extack)
{
 struct myri10ge_priv *mgp = netdev_priv(netdev);

 coal->rx_coalesce_usecs = mgp->intr_coal_delay;
 return 0;
}

static int myri10ge_set_coalesce(struct net_device *netdev,
     struct ethtool_coalesce *coal,
     struct kernel_ethtool_coalesce *kernel_coal,
     struct netlink_ext_ack *extack)
{
 struct myri10ge_priv *mgp = netdev_priv(netdev);

 mgp->intr_coal_delay = coal->rx_coalesce_usecs;
 put_be32(htonl(mgp->intr_coal_delay), mgp->intr_coal_delay_ptr);
 return 0;
}

static void
myri10ge_get_pauseparam(struct net_device *netdev,
   struct ethtool_pauseparam *pause)
{
 struct myri10ge_priv *mgp = netdev_priv(netdev);

 pause->autoneg = 0;
 pause->rx_pause = mgp->pause;
 pause->tx_pause = mgp->pause;
}

static int
myri10ge_set_pauseparam(struct net_device *netdev,
   struct ethtool_pauseparam *pause)
{
 struct myri10ge_priv *mgp = netdev_priv(netdev);

 if (pause->tx_pause != mgp->pause)
  return myri10ge_change_pause(mgp, pause->tx_pause);
 if (pause->rx_pause != mgp->pause)
  return myri10ge_change_pause(mgp, pause->rx_pause);
 if (pause->autoneg != 0)
  return -EINVAL;
 return 0;
}

static void
myri10ge_get_ringparam(struct net_device *netdev,
         struct ethtool_ringparam *ring,
         struct kernel_ethtool_ringparam *kernel_ring,
         struct netlink_ext_ack *extack)
{
 struct myri10ge_priv *mgp = netdev_priv(netdev);

 ring->rx_mini_max_pending = mgp->ss[0].rx_small.mask + 1;
 ring->rx_max_pending = mgp->ss[0].rx_big.mask + 1;
 ring->rx_jumbo_max_pending = 0;
 ring->tx_max_pending = mgp->ss[0].tx.mask + 1;
 ring->rx_mini_pending = ring->rx_mini_max_pending;
 ring->rx_pending = ring->rx_max_pending;
 ring->rx_jumbo_pending = ring->rx_jumbo_max_pending;
 ring->tx_pending = ring->tx_max_pending;
}

static const char myri10ge_gstrings_main_stats[][ETH_GSTRING_LEN] = {
 "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",
 /* device-specific stats */
 "tx_boundary""irq""MSI""MSIX",
 "read_dma_bw_MBs""write_dma_bw_MBs""read_write_dma_bw_MBs",
 "serial_number""watchdog_resets",
#ifdef CONFIG_MYRI10GE_DCA
 "dca_capable_firmware""dca_device_present",
#endif
 "link_changes""link_up""dropped_link_overflow",
 "dropped_link_error_or_filtered",
 "dropped_pause""dropped_bad_phy""dropped_bad_crc32",
 "dropped_unicast_filtered""dropped_multicast_filtered",
 "dropped_runt""dropped_overrun""dropped_no_small_buffer",
 "dropped_no_big_buffer"
};

static const char myri10ge_gstrings_slice_stats[][ETH_GSTRING_LEN] = {
 "----------- slice ---------",
 "tx_pkt_start""tx_pkt_done""tx_req""tx_done",
 "rx_small_cnt""rx_big_cnt",
 "wake_queue""stop_queue""tx_linearized",
};

#define MYRI10GE_NET_STATS_LEN      21
#define MYRI10GE_MAIN_STATS_LEN  ARRAY_SIZE(myri10ge_gstrings_main_stats)
#define MYRI10GE_SLICE_STATS_LEN  ARRAY_SIZE(myri10ge_gstrings_slice_stats)

static void
myri10ge_get_strings(struct net_device *netdev, u32 stringset, u8 * data)
{
 struct myri10ge_priv *mgp = netdev_priv(netdev);
 int i;

 switch (stringset) {
 case ETH_SS_STATS:
  memcpy(data, *myri10ge_gstrings_main_stats,
         sizeof(myri10ge_gstrings_main_stats));
  data += sizeof(myri10ge_gstrings_main_stats);
  for (i = 0; i < mgp->num_slices; i++) {
   memcpy(data, *myri10ge_gstrings_slice_stats,
          sizeof(myri10ge_gstrings_slice_stats));
   data += sizeof(myri10ge_gstrings_slice_stats);
  }
  break;
 }
}

static int myri10ge_get_sset_count(struct net_device *netdev, int sset)
{
 struct myri10ge_priv *mgp = netdev_priv(netdev);

 switch (sset) {
 case ETH_SS_STATS:
  return MYRI10GE_MAIN_STATS_LEN +
      mgp->num_slices * MYRI10GE_SLICE_STATS_LEN;
 default:
  return -EOPNOTSUPP;
 }
}

static void
myri10ge_get_ethtool_stats(struct net_device *netdev,
      struct ethtool_stats *stats, u64 * data)
{
 struct myri10ge_priv *mgp = netdev_priv(netdev);
 struct myri10ge_slice_state *ss;
 struct rtnl_link_stats64 link_stats;
 int slice;
 int i;

 /* force stats update */
 memset(&link_stats, 0, sizeof(link_stats));
 (void)myri10ge_get_stats(netdev, &link_stats);
 for (i = 0; i < MYRI10GE_NET_STATS_LEN; i++)
  data[i] = ((u64 *)&link_stats)[i];

 data[i++] = (unsigned int)mgp->tx_boundary;
 data[i++] = (unsigned int)mgp->pdev->irq;
 data[i++] = (unsigned int)mgp->msi_enabled;
 data[i++] = (unsigned int)mgp->msix_enabled;
 data[i++] = (unsigned int)mgp->read_dma;
 data[i++] = (unsigned int)mgp->write_dma;
 data[i++] = (unsigned int)mgp->read_write_dma;
 data[i++] = (unsigned int)mgp->serial_number;
 data[i++] = (unsigned int)mgp->watchdog_resets;
#ifdef CONFIG_MYRI10GE_DCA
 data[i++] = (unsigned int)(mgp->ss[0].dca_tag != NULL);
 data[i++] = (unsigned int)(mgp->dca_enabled);
#endif
 data[i++] = (unsigned int)mgp->link_changes;

 /* firmware stats are useful only in the first slice */
 ss = &mgp->ss[0];
 data[i++] = (unsigned int)ntohl(ss->fw_stats->link_up);
 data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_link_overflow);
 data[i++] =
     (unsigned int)ntohl(ss->fw_stats->dropped_link_error_or_filtered);
 data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_pause);
 data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_bad_phy);
 data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_bad_crc32);
 data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_unicast_filtered);
 data[i++] =
     (unsigned int)ntohl(ss->fw_stats->dropped_multicast_filtered);
 data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_runt);
 data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_overrun);
 data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_no_small_buffer);
 data[i++] = (unsigned int)ntohl(ss->fw_stats->dropped_no_big_buffer);

 for (slice = 0; slice < mgp->num_slices; slice++) {
  ss = &mgp->ss[slice];
  data[i++] = slice;
  data[i++] = (unsigned int)ss->tx.pkt_start;
  data[i++] = (unsigned int)ss->tx.pkt_done;
  data[i++] = (unsigned int)ss->tx.req;
  data[i++] = (unsigned int)ss->tx.done;
  data[i++] = (unsigned int)ss->rx_small.cnt;
  data[i++] = (unsigned int)ss->rx_big.cnt;
  data[i++] = (unsigned int)ss->tx.wake_queue;
  data[i++] = (unsigned int)ss->tx.stop_queue;
  data[i++] = (unsigned int)ss->tx.linearized;
 }
}

static void myri10ge_set_msglevel(struct net_device *netdev, u32 value)
{
 struct myri10ge_priv *mgp = netdev_priv(netdev);
 mgp->msg_enable = value;
}

static u32 myri10ge_get_msglevel(struct net_device *netdev)
{
 struct myri10ge_priv *mgp = netdev_priv(netdev);
 return mgp->msg_enable;
}

/*
 * Use a low-level command to change the LED behavior. Rather than
 * blinking (which is the normal case), when identify is used, the
 * yellow LED turns solid.
 */

static int myri10ge_led(struct myri10ge_priv *mgp, int on)
{
 struct mcp_gen_header *hdr;
 struct device *dev = &mgp->pdev->dev;
 size_t hdr_off, pattern_off, hdr_len;
 u32 pattern = 0xfffffffe;

 /* find running firmware header */
 hdr_off = swab32(readl(mgp->sram + MCP_HEADER_PTR_OFFSET));
 if ((hdr_off & 3) || hdr_off + sizeof(*hdr) > mgp->sram_size) {
  dev_err(dev, "Running firmware has bad header offset (%d)\n",
   (int)hdr_off);
  return -EIO;
 }
 hdr_len = swab32(readl(mgp->sram + hdr_off +
          offsetof(struct mcp_gen_header, header_length)));
 pattern_off = hdr_off + offsetof(struct mcp_gen_header, led_pattern);
 if (pattern_off >= (hdr_len + hdr_off)) {
  dev_info(dev, "Firmware does not support LED identification\n");
  return -EINVAL;
 }
 if (!on)
  pattern = swab32(readl(mgp->sram + pattern_off + 4));
 writel(swab32(pattern), mgp->sram + pattern_off);
 return 0;
}

static int
myri10ge_phys_id(struct net_device *netdev, enum ethtool_phys_id_state state)
{
 struct myri10ge_priv *mgp = netdev_priv(netdev);
 int rc;

 switch (state) {
 case ETHTOOL_ID_ACTIVE:
  rc = myri10ge_led(mgp, 1);
  break;

 case ETHTOOL_ID_INACTIVE:
  rc =  myri10ge_led(mgp, 0);
  break;

 default:
  rc = -EINVAL;
 }

 return rc;
}

static const struct ethtool_ops myri10ge_ethtool_ops = {
 .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS,
 .get_drvinfo = myri10ge_get_drvinfo,
 .get_coalesce = myri10ge_get_coalesce,
 .set_coalesce = myri10ge_set_coalesce,
 .get_pauseparam = myri10ge_get_pauseparam,
 .set_pauseparam = myri10ge_set_pauseparam,
 .get_ringparam = myri10ge_get_ringparam,
 .get_link = ethtool_op_get_link,
 .get_strings = myri10ge_get_strings,
 .get_sset_count = myri10ge_get_sset_count,
 .get_ethtool_stats = myri10ge_get_ethtool_stats,
 .set_msglevel = myri10ge_set_msglevel,
 .get_msglevel = myri10ge_get_msglevel,
 .set_phys_id = myri10ge_phys_id,
 .get_link_ksettings = myri10ge_get_link_ksettings,
};

static int myri10ge_allocate_rings(struct myri10ge_slice_state *ss)
{
 struct myri10ge_priv *mgp = ss->mgp;
 struct myri10ge_cmd cmd;
 struct net_device *dev = mgp->dev;
 int tx_ring_size, rx_ring_size;
 int tx_ring_entries, rx_ring_entries;
 int i, slice, status;
 size_t bytes;

 /* get ring sizes */
 slice = ss - mgp->ss;
 cmd.data0 = slice;
 status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd, 0);
 tx_ring_size = cmd.data0;
 cmd.data0 = slice;
 status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd, 0);
 if (status != 0)
  return status;
 rx_ring_size = cmd.data0;

 tx_ring_entries = tx_ring_size / sizeof(struct mcp_kreq_ether_send);
 rx_ring_entries = rx_ring_size / sizeof(struct mcp_dma_addr);
 ss->tx.mask = tx_ring_entries - 1;
 ss->rx_small.mask = ss->rx_big.mask = rx_ring_entries - 1;

 status = -ENOMEM;

 /* allocate the host shadow rings */

 bytes = 8 + (MYRI10GE_MAX_SEND_DESC_TSO + 4)
     * sizeof(*ss->tx.req_list);
 ss->tx.req_bytes = kzalloc(bytes, GFP_KERNEL);
 if (ss->tx.req_bytes == NULL)
  goto abort_with_nothing;

 /* ensure req_list entries are aligned to 8 bytes */
 ss->tx.req_list = (struct mcp_kreq_ether_send *)
     ALIGN((unsigned long)ss->tx.req_bytes, 8);
 ss->tx.queue_active = 0;

 bytes = rx_ring_entries * sizeof(*ss->rx_small.shadow);
 ss->rx_small.shadow = kzalloc(bytes, GFP_KERNEL);
 if (ss->rx_small.shadow == NULL)
  goto abort_with_tx_req_bytes;

 bytes = rx_ring_entries * sizeof(*ss->rx_big.shadow);
 ss->rx_big.shadow = kzalloc(bytes, GFP_KERNEL);
 if (ss->rx_big.shadow == NULL)
  goto abort_with_rx_small_shadow;

 /* allocate the host info rings */

 bytes = tx_ring_entries * sizeof(*ss->tx.info);
 ss->tx.info = kzalloc(bytes, GFP_KERNEL);
 if (ss->tx.info == NULL)
  goto abort_with_rx_big_shadow;

 bytes = rx_ring_entries * sizeof(*ss->rx_small.info);
 ss->rx_small.info = kzalloc(bytes, GFP_KERNEL);
 if (ss->rx_small.info == NULL)
  goto abort_with_tx_info;

 bytes = rx_ring_entries * sizeof(*ss->rx_big.info);
 ss->rx_big.info = kzalloc(bytes, GFP_KERNEL);
 if (ss->rx_big.info == NULL)
  goto abort_with_rx_small_info;

 /* Fill the receive rings */
 ss->rx_big.cnt = 0;
 ss->rx_small.cnt = 0;
 ss->rx_big.fill_cnt = 0;
 ss->rx_small.fill_cnt = 0;
 ss->rx_small.page_offset = MYRI10GE_ALLOC_SIZE;
 ss->rx_big.page_offset = MYRI10GE_ALLOC_SIZE;
 ss->rx_small.watchdog_needed = 0;
 ss->rx_big.watchdog_needed = 0;
 if (mgp->small_bytes == 0) {
  ss->rx_small.fill_cnt = ss->rx_small.mask + 1;
 } else {
  myri10ge_alloc_rx_pages(mgp, &ss->rx_small,
     mgp->small_bytes + MXGEFW_PAD, 0);
 }

 if (ss->rx_small.fill_cnt < ss->rx_small.mask + 1) {
  netdev_err(dev, "slice-%d: alloced only %d small bufs\n",
      slice, ss->rx_small.fill_cnt);
  goto abort_with_rx_small_ring;
 }

 myri10ge_alloc_rx_pages(mgp, &ss->rx_big, mgp->big_bytes, 0);
 if (ss->rx_big.fill_cnt < ss->rx_big.mask + 1) {
  netdev_err(dev, "slice-%d: alloced only %d big bufs\n",
      slice, ss->rx_big.fill_cnt);
  goto abort_with_rx_big_ring;
 }

 return 0;

abort_with_rx_big_ring:
 for (i = ss->rx_big.cnt; i < ss->rx_big.fill_cnt; i++) {
  int idx = i & ss->rx_big.mask;
  myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_big.info[idx],
           mgp->big_bytes);
  put_page(ss->rx_big.info[idx].page);
 }

abort_with_rx_small_ring:
 if (mgp->small_bytes == 0)
  ss->rx_small.fill_cnt = ss->rx_small.cnt;
 for (i = ss->rx_small.cnt; i < ss->rx_small.fill_cnt; i++) {
  int idx = i & ss->rx_small.mask;
  myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_small.info[idx],
           mgp->small_bytes + MXGEFW_PAD);
  put_page(ss->rx_small.info[idx].page);
 }

 kfree(ss->rx_big.info);

abort_with_rx_small_info:
 kfree(ss->rx_small.info);

abort_with_tx_info:
 kfree(ss->tx.info);

abort_with_rx_big_shadow:
 kfree(ss->rx_big.shadow);

abort_with_rx_small_shadow:
 kfree(ss->rx_small.shadow);

abort_with_tx_req_bytes:
 kfree(ss->tx.req_bytes);
 ss->tx.req_bytes = NULL;
 ss->tx.req_list = NULL;

abort_with_nothing:
 return status;
}

static void myri10ge_free_rings(struct myri10ge_slice_state *ss)
{
 struct myri10ge_priv *mgp = ss->mgp;
 struct sk_buff *skb;
 struct myri10ge_tx_buf *tx;
 int i, len, idx;

 /* If not allocated, skip it */
 if (ss->tx.req_list == NULL)
  return;

 for (i = ss->rx_big.cnt; i < ss->rx_big.fill_cnt; i++) {
  idx = i & ss->rx_big.mask;
  if (i == ss->rx_big.fill_cnt - 1)
   ss->rx_big.info[idx].page_offset = MYRI10GE_ALLOC_SIZE;
  myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_big.info[idx],
           mgp->big_bytes);
  put_page(ss->rx_big.info[idx].page);
 }

 if (mgp->small_bytes == 0)
  ss->rx_small.fill_cnt = ss->rx_small.cnt;
 for (i = ss->rx_small.cnt; i < ss->rx_small.fill_cnt; i++) {
  idx = i & ss->rx_small.mask;
  if (i == ss->rx_small.fill_cnt - 1)
   ss->rx_small.info[idx].page_offset =
       MYRI10GE_ALLOC_SIZE;
  myri10ge_unmap_rx_page(mgp->pdev, &ss->rx_small.info[idx],
           mgp->small_bytes + MXGEFW_PAD);
  put_page(ss->rx_small.info[idx].page);
 }
 tx = &ss->tx;
 while (tx->done != tx->req) {
  idx = tx->done & tx->mask;
  skb = tx->info[idx].skb;

  /* Mark as free */
  tx->info[idx].skb = NULL;
  tx->done++;
  len = dma_unmap_len(&tx->info[idx], len);
  dma_unmap_len_set(&tx->info[idx], len, 0);
  if (skb) {
   ss->stats.tx_dropped++;
   dev_kfree_skb_any(skb);
   if (len)
    dma_unmap_single(&mgp->pdev->dev,
       dma_unmap_addr(&tx->info[idx],
        bus), len,
       DMA_TO_DEVICE);
  } else {
   if (len)
    dma_unmap_page(&mgp->pdev->dev,
            dma_unmap_addr(&tx->info[idx],
             bus), len,
            DMA_TO_DEVICE);
  }
 }
 kfree(ss->rx_big.info);

 kfree(ss->rx_small.info);

 kfree(ss->tx.info);

 kfree(ss->rx_big.shadow);

 kfree(ss->rx_small.shadow);

 kfree(ss->tx.req_bytes);
 ss->tx.req_bytes = NULL;
 ss->tx.req_list = NULL;
}

static int myri10ge_request_irq(struct myri10ge_priv *mgp)
{
 struct pci_dev *pdev = mgp->pdev;
 struct myri10ge_slice_state *ss;
 struct net_device *netdev = mgp->dev;
 int i;
 int status;

 mgp->msi_enabled = 0;
 mgp->msix_enabled = 0;
 status = 0;
 if (myri10ge_msi) {
  if (mgp->num_slices > 1) {
   status = pci_enable_msix_range(pdev, mgp->msix_vectors,
     mgp->num_slices, mgp->num_slices);
   if (status < 0) {
    dev_err(&pdev->dev,
     "Error %d setting up MSI-X\n", status);
    return status;
   }
   mgp->msix_enabled = 1;
  }
  if (mgp->msix_enabled == 0) {
   status = pci_enable_msi(pdev);
   if (status != 0) {
    dev_err(&pdev->dev,
     "Error %d setting up MSI; falling back to xPIC\n",
     status);
   } else {
    mgp->msi_enabled = 1;
   }
  }
 }
 if (mgp->msix_enabled) {
  for (i = 0; i < mgp->num_slices; i++) {
   ss = &mgp->ss[i];
   snprintf(ss->irq_desc, sizeof(ss->irq_desc),
     "%s:slice-%d", netdev->name, i);
   status = request_irq(mgp->msix_vectors[i].vector,
          myri10ge_intr, 0, ss->irq_desc,
          ss);
   if (status != 0) {
    dev_err(&pdev->dev,
     "slice %d failed to allocate IRQ\n", i);
    i--;
    while (i >= 0) {
     free_irq(mgp->msix_vectors[i].vector,
       &mgp->ss[i]);
     i--;
    }
    pci_disable_msix(pdev);
    return status;
   }
  }
 } else {
  status = request_irq(pdev->irq, myri10ge_intr, IRQF_SHARED,
         mgp->dev->name, &mgp->ss[0]);
  if (status != 0) {
   dev_err(&pdev->dev, "failed to allocate IRQ\n");
   if (mgp->msi_enabled)
    pci_disable_msi(pdev);
  }
 }
 return status;
}

static void myri10ge_free_irq(struct myri10ge_priv *mgp)
{
 struct pci_dev *pdev = mgp->pdev;
 int i;

 if (mgp->msix_enabled) {
  for (i = 0; i < mgp->num_slices; i++)
   free_irq(mgp->msix_vectors[i].vector, &mgp->ss[i]);
 } else {
  free_irq(pdev->irq, &mgp->ss[0]);
 }
 if (mgp->msi_enabled)
  pci_disable_msi(pdev);
 if (mgp->msix_enabled)
  pci_disable_msix(pdev);
}

static int myri10ge_get_txrx(struct myri10ge_priv *mgp, int slice)
{
 struct myri10ge_cmd cmd;
 struct myri10ge_slice_state *ss;
 int status;

 ss = &mgp->ss[slice];
 status = 0;
 if (slice == 0 || (mgp->dev->real_num_tx_queues > 1)) {
  cmd.data0 = slice;
  status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SEND_OFFSET,
        &cmd, 0);
  ss->tx.lanai = (struct mcp_kreq_ether_send __iomem *)
      (mgp->sram + cmd.data0);
 }
 cmd.data0 = slice;
 status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SMALL_RX_OFFSET,
        &cmd, 0);
 ss->rx_small.lanai = (struct mcp_kreq_ether_recv __iomem *)
     (mgp->sram + cmd.data0);

 cmd.data0 = slice;
 status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_BIG_RX_OFFSET, &cmd, 0);
 ss->rx_big.lanai = (struct mcp_kreq_ether_recv __iomem *)
     (mgp->sram + cmd.data0);

 ss->tx.send_go = (__iomem __be32 *)
     (mgp->sram + MXGEFW_ETH_SEND_GO + 64 * slice);
 ss->tx.send_stop = (__iomem __be32 *)
     (mgp->sram + MXGEFW_ETH_SEND_STOP + 64 * slice);
 return status;

}

static int myri10ge_set_stats(struct myri10ge_priv *mgp, int slice)
{
 struct myri10ge_cmd cmd;
 struct myri10ge_slice_state *ss;
 int status;

 ss = &mgp->ss[slice];
 cmd.data0 = MYRI10GE_LOWPART_TO_U32(ss->fw_stats_bus);
 cmd.data1 = MYRI10GE_HIGHPART_TO_U32(ss->fw_stats_bus);
 cmd.data2 = sizeof(struct mcp_irq_data) | (slice << 16);
 status = myri10ge_send_cmd(mgp, MXGEFW_CMD_SET_STATS_DMA_V2, &cmd, 0);
 if (status == -ENOSYS) {
  dma_addr_t bus = ss->fw_stats_bus;
  if (slice != 0)
   return -EINVAL;
  bus += offsetof(struct mcp_irq_data, send_done_count);
  cmd.data0 = MYRI10GE_LOWPART_TO_U32(bus);
  cmd.data1 = MYRI10GE_HIGHPART_TO_U32(bus);
  status = myri10ge_send_cmd(mgp,
        MXGEFW_CMD_SET_STATS_DMA_OBSOLETE,
        &cmd, 0);
  /* Firmware cannot support multicast without STATS_DMA_V2 */
  mgp->fw_multicast_support = 0;
 } else {
  mgp->fw_multicast_support = 1;
 }
 return 0;
}

static int myri10ge_open(struct net_device *dev)
{
 struct myri10ge_slice_state *ss;
 struct myri10ge_priv *mgp = netdev_priv(dev);
 struct myri10ge_cmd cmd;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=90 G=93

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge