2006-05-17 22:02:22

by Brice Goglin

[permalink] [raw]
Subject: [PATCH 0/4] myri10ge - Myri-10G Ethernet driver - v2

[PATCH 0/4] myri10ge - Myri-10G Ethernet driver - v2

The following patches introduce the myri10ge driver for Myricom Myri-10G
boards in Ethernet mode. The driver is called myri10ge. The patches are
against 2.6.17-rc4-mm1.

[1/4] revive pci_find_ext_capability
[2/4] myri10ge driver header files.
[3/4] myri10ge driver core.
[4/4] Add Kconfig and Makefile support for the myri10ge driver.

It also uses the following patches that have been sent on May 2
(http://lkml.org/lkml/2006/5/2/286 and 288) and merged into -mm.
add-__iowrite64_copy.patch
Introduce __iowrite64_copy.
add-pci_cap_id_vndr.patch
Add the vendor specific extended capability PCI_CAP_ID_VNDR.

We think we have addressed will problems that have been reported after
the first submission. Major changes include:
- We have dropped the "does msi work on this chipset ?" detection code
and we simply enable msi by default. We are working on patches to
improve MSI detection in the core PCI code.
- The spinlock that was held during up to 2s has been replaced with
using rtnl_lock (and the delay has been reduced to 15ms).
- no X86||X86_64 specific code anymore
- non-NAPI support has been dropped
- TSO is enabled by default
- some comments have been added to justify some design choices

Other changes:
- use netdev_priv, setup_timer and skb_checksum_help helps
- PCI_DEVICE, ALIGN, DMA_MASK and ETH_ALEN macros
- use wait_event_timeout in the close routine
- fix cksum regarding to vlan and tso
- rename MYRI10GE_MCP_M{IN,AJ}OR into MYRI10GE_MCP_VERSION_M{IN,AJ}OR
- only add NETIF_F_HIGHDMA only if dma_mask bas been set
- add MODULE_PARM_DESC
- check/use myri10ge_send_cmd return status
- return pci_register_driver return value init the driver init function
- drop empty functions (set_setting, init and ioctl)
- add __init and __exit to module init and exit routines
- add missing static and const, remove useless volatile
- do not typedef struct and enum
- {u,s}{8,16,32,64} instead of {u,}int{8,16,32,64}_t
- remove useless casts
- uppercase _myri10ge_mcp_h and _myri10ge_mcp_gen_header_h
- drop unused ugly macro in the headers
- fix multiple indentation problems

We still have some chipset specific code to deal with alignment of PCI-E
completions. When aligned, we can get much better performance by using an
optimized firmware. The HT2000 chipset is known to provide aligned completions.
See myri10ge_select_firmware() for details. For other chipsets, we try to
enable ECRC if the board is attached to a root port (so that we do not
disturb any other device that would not like ECRC).
We don't think that this ECRC and PCI-E completion alignment code has to
be moved to the PCI core since it is very specific to our driver.

Enabling ECRC on the nVidia CK804 PCI-E bridge is problematic since the
PCI_EXT_CAP_ID_ERR capability is not linked as it is supposed. We didn't
find any way to do this with a quirk. So we work around it with a local fix.
See myri10ge_enable_ecrc() for details.





The Myri-10G board operates as a regular PCI-Express Ethernet NIC.
If a firmware is available through hotplug, the driver will load it if its
version matches the driver requirements. If not, the driver will adopt the
running firmware that came in the board's eeprom if it is recent enough.

This driver supports in particular NAPI, power management, IPv4 and IPv6
checksum offload, 802.1q VLAN, and TCP Segmentation Offload.

Brice Goglin


2006-05-17 22:03:49

by Brice Goglin

[permalink] [raw]
Subject: [PATCH 1/4] myri10ge - Revive pci_find_ext_capability

[PATCH 1/4] myri10ge - Revive pci_find_ext_capability

This patch revives pci_find_ext_capability (has been disabled a couple month
ago since it was not used anywhere. See http://lkml.org/lkml/2006/1/20/247).
It will now be used by the myri10ge driver.

Signed-off-by: Brice Goglin <[email protected]>
Signed-off-by: Andrew J. Gallatin <[email protected]>

drivers/pci/pci.c | 3 +--
include/linux/pci.h | 2 ++
2 files changed, 3 insertions(+), 2 deletions(-)

--- linux-mm/drivers/pci/pci.c.old
+++ linux-mm/drivers/pci/pci.c
@@ -164,7 +164,6 @@ int pci_bus_find_capability(struct pci_b
return __pci_bus_find_cap(bus, devfn, hdr_type & 0x7f, cap);
}

-#if 0
/**
* pci_find_ext_capability - Find an extended capability
* @dev: PCI device to query
@@ -212,7 +211,7 @@ int pci_find_ext_capability(struct pci_d

return 0;
}
-#endif /* 0 */
+EXPORT_SYMBOL_GPL(pci_find_ext_capability);

/**
* pci_find_parent_resource - return resource region of parent bus of given region
--- linux-mm/include/linux/pci.h.old
+++ linux-mm/include/linux/pci.h
@@ -443,6 +443,7 @@ struct pci_dev *pci_find_device_reverse
struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn);
int pci_find_capability (struct pci_dev *dev, int cap);
int pci_find_next_capability (struct pci_dev *dev, u8 pos, int cap);
+int pci_find_ext_capability (struct pci_dev *dev, int cap);
struct pci_bus * pci_find_next_bus(const struct pci_bus *from);

struct pci_dev *pci_get_device (unsigned int vendor, unsigned int device, struct pci_dev *from);
@@ -670,6 +671,7 @@ static inline int pci_register_driver(st
static inline void pci_unregister_driver(struct pci_driver *drv) { }
static inline int pci_find_capability (struct pci_dev *dev, int cap) {return 0; }
static inline int pci_find_next_capability (struct pci_dev *dev, u8 post, int cap) { return 0; }
+static inline int pci_find_ext_capability (struct pci_dev *dev, int cap) {return 0; }
static inline const struct pci_device_id *pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev) { return NULL; }

/* Power management related routines */

2006-05-17 22:04:38

by Brice Goglin

[permalink] [raw]
Subject: [PATCH 2/4] myri10ge - Driver header files

[PATCH 2/4] myri10ge - Driver header files

myri10ge driver header files.
myri10ge_mcp.h is the generic header, while myri10ge_mcp_gen_header.h
is automatically generated from our firmware image.

Signed-off-by: Brice Goglin <[email protected]>
Signed-off-by: Andrew J. Gallatin <[email protected]>

myri10ge_mcp.h | 205 ++++++++++++++++++++++++++++++++++++++++++++++
myri10ge_mcp_gen_header.h | 58 +++++++++++++
2 files changed, 263 insertions(+)

--- /dev/null 2006-05-16 20:08:50.920483500 +0200
+++ linux-tmp//drivers/net/myri10ge/myri10ge_mcp.h 2006-05-17 11:02:48.000000000 +0200
@@ -0,0 +1,205 @@
+#ifndef __MYRI10GE_MCP_H__
+#define __MYRI10GE_MCP_H__
+
+#define MYRI10GE_MCP_VERSION_MAJOR 1
+#define MYRI10GE_MCP_VERSION_MINOR 4
+
+/* 8 Bytes */
+struct mcp_dma_addr {
+ u32 high;
+ u32 low;
+};
+
+/* 16 Bytes */
+struct mcp_slot {
+ u16 checksum;
+ u16 length;
+};
+
+/* 64 Bytes */
+struct mcp_cmd {
+ u32 cmd;
+ u32 data0; /* will be low portion if data > 32 bits */
+ /* 8 */
+ u32 data1; /* will be high portion if data > 32 bits */
+ u32 data2; /* currently unused.. */
+ /* 16 */
+ struct mcp_dma_addr response_addr;
+ /* 24 */
+ u8 pad[40];
+};
+
+/* 8 Bytes */
+struct mcp_cmd_response {
+ u32 data;
+ u32 result;
+};
+
+/*
+ * flags used in mcp_kreq_ether_send_t:
+ *
+ * The SMALL flag is only needed in the first segment. It is raised
+ * for packets that are total less or equal 512 bytes.
+ *
+ * The CKSUM flag must be set in all segments.
+ *
+ * The PADDED flags is set if the packet needs to be padded, and it
+ * must be set for all segments.
+ *
+ * The MYRI10GE_MCP_ETHER_FLAGS_ALIGN_ODD must be set if the cumulative
+ * length of all previous segments was odd.
+ */
+
+#define MYRI10GE_MCP_ETHER_FLAGS_SMALL 0x1
+#define MYRI10GE_MCP_ETHER_FLAGS_TSO_HDR 0x1
+#define MYRI10GE_MCP_ETHER_FLAGS_FIRST 0x2
+#define MYRI10GE_MCP_ETHER_FLAGS_ALIGN_ODD 0x4
+#define MYRI10GE_MCP_ETHER_FLAGS_CKSUM 0x8
+#define MYRI10GE_MCP_ETHER_FLAGS_TSO_LAST 0x8
+#define MYRI10GE_MCP_ETHER_FLAGS_NO_TSO 0x10
+#define MYRI10GE_MCP_ETHER_FLAGS_TSO_CHOP 0x10
+#define MYRI10GE_MCP_ETHER_FLAGS_TSO_PLD 0x20
+
+#define MYRI10GE_MCP_ETHER_SEND_SMALL_SIZE 1520
+#define MYRI10GE_MCP_ETHER_MAX_MTU 9400
+
+union mcp_pso_or_cumlen {
+ u16 pseudo_hdr_offset;
+ u16 cum_len;
+};
+
+#define MYRI10GE_MCP_ETHER_MAX_SEND_DESC 12
+#define MYRI10GE_MCP_ETHER_PAD 2
+
+/* 16 Bytes */
+struct mcp_kreq_ether_send {
+ u32 addr_high;
+ u32 addr_low;
+ u16 pseudo_hdr_offset;
+ u16 length;
+ u8 pad;
+ u8 rdma_count;
+ u8 cksum_offset; /* where to start computing cksum */
+ u8 flags; /* as defined above */
+};
+
+/* 8 Bytes */
+struct mcp_kreq_ether_recv {
+ u32 addr_high;
+ u32 addr_low;
+};
+
+/* Commands */
+
+#define MYRI10GE_MCP_CMD_OFFSET 0xf80000
+
+enum myri10ge_mcp_cmd_type {
+ MYRI10GE_MCP_CMD_NONE = 0,
+ /* Reset the mcp, it is left in a safe state, waiting
+ * for the driver to set all its parameters */
+ MYRI10GE_MCP_CMD_RESET,
+
+ /* get the version number of the current firmware..
+ * (may be available in the eeprom strings..? */
+ MYRI10GE_MCP_GET_MCP_VERSION,
+
+ /* Parameters which must be set by the driver before it can
+ * issue MYRI10GE_MCP_CMD_ETHERNET_UP. They persist until the next
+ * MYRI10GE_MCP_CMD_RESET is issued */
+
+ MYRI10GE_MCP_CMD_SET_INTRQ_DMA,
+ MYRI10GE_MCP_CMD_SET_BIG_BUFFER_SIZE, /* in bytes, power of 2 */
+ MYRI10GE_MCP_CMD_SET_SMALL_BUFFER_SIZE, /* in bytes */
+
+ /* Parameters which refer to lanai SRAM addresses where the
+ * driver must issue PIO writes for various things */
+
+ MYRI10GE_MCP_CMD_GET_SEND_OFFSET,
+ MYRI10GE_MCP_CMD_GET_SMALL_RX_OFFSET,
+ MYRI10GE_MCP_CMD_GET_BIG_RX_OFFSET,
+ MYRI10GE_MCP_CMD_GET_IRQ_ACK_OFFSET,
+ MYRI10GE_MCP_CMD_GET_IRQ_DEASSERT_OFFSET,
+
+ /* Parameters which refer to rings stored on the MCP,
+ * and whose size is controlled by the mcp */
+
+ MYRI10GE_MCP_CMD_GET_SEND_RING_SIZE, /* in bytes */
+ MYRI10GE_MCP_CMD_GET_RX_RING_SIZE, /* in bytes */
+
+ /* Parameters which refer to rings stored in the host,
+ * and whose size is controlled by the host. Note that
+ * all must be physically contiguous and must contain
+ * a power of 2 number of entries. */
+
+ MYRI10GE_MCP_CMD_SET_INTRQ_SIZE, /* in bytes */
+
+ /* command to bring ethernet interface up. Above parameters
+ * (plus mtu & mac address) must have been exchanged prior
+ * to issuing this command */
+ MYRI10GE_MCP_CMD_ETHERNET_UP,
+
+ /* command to bring ethernet interface down. No further sends
+ * or receives may be processed until an MYRI10GE_MCP_CMD_ETHERNET_UP
+ * is issued, and all interrupt queues must be flushed prior
+ * to ack'ing this command */
+
+ MYRI10GE_MCP_CMD_ETHERNET_DOWN,
+
+ /* commands the driver may issue live, without resetting
+ * the nic. Note that increasing the mtu "live" should
+ * only be done if the driver has already supplied buffers
+ * sufficiently large to handle the new mtu. Decreasing
+ * the mtu live is safe */
+
+ MYRI10GE_MCP_CMD_SET_MTU,
+ MYRI10GE_MCP_CMD_GET_INTR_COAL_DELAY_OFFSET, /* in microseconds */
+ MYRI10GE_MCP_CMD_SET_STATS_INTERVAL, /* in microseconds */
+ MYRI10GE_MCP_CMD_SET_STATS_DMA,
+
+ MYRI10GE_MCP_ENABLE_PROMISC,
+ MYRI10GE_MCP_DISABLE_PROMISC,
+ MYRI10GE_MCP_SET_MAC_ADDRESS,
+
+ MYRI10GE_MCP_ENABLE_FLOW_CONTROL,
+ MYRI10GE_MCP_DISABLE_FLOW_CONTROL,
+
+ /* do a DMA test
+ * data0,data1 = DMA address
+ * data2 = RDMA length (MSH), WDMA length (LSH)
+ * command return data = repetitions (MSH), 0.5-ms ticks (LSH)
+ */
+ MYRI10GE_MCP_DMA_TEST
+};
+
+enum myri10ge_mcp_cmd_status {
+ MYRI10GE_MCP_CMD_OK = 0,
+ MYRI10GE_MCP_CMD_UNKNOWN,
+ MYRI10GE_MCP_CMD_ERROR_RANGE,
+ MYRI10GE_MCP_CMD_ERROR_BUSY,
+ MYRI10GE_MCP_CMD_ERROR_EMPTY,
+ MYRI10GE_MCP_CMD_ERROR_CLOSED,
+ MYRI10GE_MCP_CMD_ERROR_HASH_ERROR,
+ MYRI10GE_MCP_CMD_ERROR_BAD_PORT,
+ MYRI10GE_MCP_CMD_ERROR_RESOURCES
+};
+
+/* 40 Bytes */
+struct mcp_irq_data {
+ u32 send_done_count;
+
+ u32 link_up;
+ u32 dropped_link_overflow;
+ u32 dropped_link_error_or_filtered;
+ u32 dropped_runt;
+ u32 dropped_overrun;
+ u32 dropped_no_small_buffer;
+ u32 dropped_no_big_buffer;
+ u32 rdma_tags_available;
+
+ u8 tx_stopped;
+ u8 link_down;
+ u8 stats_updated;
+ u8 valid;
+};
+
+#endif /* __MYRI10GE_MCP_H__ */
--- /dev/null 2006-05-16 20:08:50.920483500 +0200
+++ linux-tmp//drivers/net/myri10ge/myri10ge_mcp_gen_header.h 2006-05-17 11:02:48.000000000 +0200
@@ -0,0 +1,58 @@
+#ifndef __MYRI10GE_MCP_GEN_HEADER_H__
+#define __MYRI10GE_MCP_GEN_HEADER_H__
+
+/* this file define a standard header used as a first entry point to
+ * exchange information between firmware/driver and driver. The
+ * header structure can be anywhere in the mcp. It will usually be in
+ * the .data section, because some fields needs to be initialized at
+ * compile time.
+ * The 32bit word at offset MX_HEADER_PTR_OFFSET in the mcp must
+ * contains the location of the header.
+ *
+ * Typically a MCP will start with the following:
+ * .text
+ * .space 52 ! to help catch MEMORY_INT errors
+ * bt start ! jump to real code
+ * nop
+ * .long _gen_mcp_header
+ *
+ * The source will have a definition like:
+ *
+ * mcp_gen_header_t gen_mcp_header = {
+ * .header_length = sizeof(mcp_gen_header_t),
+ * .mcp_type = MCP_TYPE_XXX,
+ * .version = "something $Id: mcp_gen_header.h,v 1.2 2006/05/13 10:04:35 bgoglin Exp $",
+ * .mcp_globals = (unsigned)&Globals
+ * };
+ */
+
+#define MCP_HEADER_PTR_OFFSET 0x3c
+
+#define MCP_TYPE_MX 0x4d582020 /* "MX " */
+#define MCP_TYPE_PCIE 0x70636965 /* "PCIE" pcie-only MCP */
+#define MCP_TYPE_ETH 0x45544820 /* "ETH " */
+#define MCP_TYPE_MCP0 0x4d435030 /* "MCP0" */
+
+struct mcp_gen_header {
+ /* the first 4 fields are filled at compile time */
+ unsigned header_length;
+ unsigned mcp_type;
+ char version[128];
+ unsigned mcp_globals; /* pointer to mcp-type specific structure */
+
+ /* filled by the MCP at run-time */
+ unsigned sram_size;
+ unsigned string_specs; /* either the original STRING_SPECS or a superset */
+ unsigned string_specs_len;
+
+ /* Fields above this comment are guaranteed to be present.
+ *
+ * Fields below this comment are extensions added in later versions
+ * of this struct, drivers should compare the header_length against
+ * offsetof(field) to check wether a given MCP implements them.
+ *
+ * Never remove any field. Keep everything naturally align.
+ */
+};
+
+#endif /* __MYRI10GE_MCP_GEN_HEADER_H__ */

2006-05-17 22:06:17

by Brice Goglin

[permalink] [raw]
Subject: [PATCH 3/4] myri10ge - Driver core

[PATCH 3/4] myri10ge - Driver core

The core of the myri10ge driver.

Signed-off-by: Brice Goglin <[email protected]>
Signed-off-by: Andrew J. Gallatin <[email protected]>

myri10ge.c | 2836 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 2836 insertions(+)

--- /dev/null 2006-05-16 20:08:50.920483500 +0200
+++ linux-tmp//drivers/net/myri10ge/myri10ge.c 2006-05-17 11:17:26.000000000 +0200
@@ -0,0 +1,2836 @@
+/*************************************************************************
+ * myri10ge.c: Myricom Myri-10G Ethernet driver.
+ *
+ * Copyright (C) 2005, 2006 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 REGENTS 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 REGENTS 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:
+ * <[email protected]>
+ * Myricom, Inc., 325N Santa Anita Avenue, Arcadia, CA 91006
+ *************************************************************************/
+
+#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/etherdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.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/version.h>
+#include <linux/timer.h>
+#include <linux/vmalloc.h>
+#include <linux/crc32.h>
+#include <linux/moduleparam.h>
+#include <linux/io.h>
+#include <net/checksum.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/pci.h>
+#include <asm/processor.h>
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+
+#include "myri10ge_mcp.h"
+#include "myri10ge_mcp_gen_header.h"
+
+#define MYRI10GE_VERSION_STR "0.9.0"
+
+MODULE_DESCRIPTION("Myricom 10G driver (10GbE)");
+MODULE_AUTHOR("Maintainer: [email protected]");
+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_MCP_ETHER_MAX_SEND_DESC_TSO ((65536 / 2048) * 2)
+
+struct myri10ge_rx_buffer_state {
+ struct sk_buff *skb;
+ DECLARE_PCI_UNMAP_ADDR(bus)
+ DECLARE_PCI_UNMAP_LEN(len)
+};
+
+struct myri10ge_tx_buffer_state {
+ struct sk_buff *skb;
+ int last;
+ DECLARE_PCI_UNMAP_ADDR(bus)
+ DECLARE_PCI_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 */
+ u8 __iomem *wc_fifo; /* w/c rx dma addr fifo address */
+ struct mcp_kreq_ether_recv *shadow; /* host shadow of recv ring */
+ struct myri10ge_rx_buffer_state *info;
+ int cnt;
+ int alloc_fail;
+ int mask; /* number of rx slots -1 */
+};
+
+struct myri10ge_tx_buf {
+ struct mcp_kreq_ether_send __iomem *lanai; /* lanai ptr for sendq */
+ u8 __iomem *wc_fifo; /* w/c send fifo address */
+ 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 boundary; /* boundary transmits cannot cross */
+ int req ____cacheline_aligned; /* transmit slots submitted */
+ int pkt_start; /* packets started */
+ int done ____cacheline_aligned; /* transmit slots completed */
+ int pkt_done; /* packets completed */
+};
+
+struct myri10ge_rx_done {
+ struct mcp_slot *entry;
+ dma_addr_t bus;
+ int cnt;
+ int idx;
+};
+
+struct myri10ge_priv {
+ int running; /* running? */
+ int csum_flag; /* rx_csums? */
+ 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;
+ int small_bytes;
+ struct net_device *dev;
+ struct net_device_stats stats;
+ volatile u8 __iomem *sram;
+ int sram_size;
+ unsigned long board_span;
+ unsigned long iomem_base;
+ volatile u32 __iomem *irq_claim;
+ volatile u32 __iomem *irq_deassert;
+ char *mac_addr_string;
+ struct mcp_cmd_response *cmd;
+ dma_addr_t cmd_bus;
+ struct mcp_irq_data *fw_stats;
+ dma_addr_t fw_stats_bus;
+ struct pci_dev *pdev;
+ int msi_enabled;
+ unsigned int link_state;
+ unsigned int rdma_tags_available;
+ int intr_coal_delay;
+ volatile u32 __iomem *intr_coal_delay_ptr;
+ int mtrr;
+ int wake_queue;
+ int stop_queue;
+ int down_cnt;
+ wait_queue_head_t down_wq;
+ struct work_struct watchdog_work;
+ struct timer_list watchdog_timer;
+ int watchdog_tx_done;
+ int watchdog_resets;
+ int tx_linearized;
+ int pause;
+ char *fw_name;
+ char eeprom_strings[MYRI10GE_EEPROM_STRINGS_SIZE];
+ char fw_version[128];
+ u8 mac_addr[6]; /* eeprom mac address */
+ unsigned long serial_number;
+ int vendor_specific_offset;
+ u32 devctl;
+ u16 msi_flags;
+ u32 pm_state[16];
+ u32 read_dma;
+ u32 write_dma;
+ u32 read_write_dma;
+};
+
+static char *myri10ge_fw_name = NULL;
+static char *myri10ge_fw_unaligned = "myri10ge_ethp_z8e.dat";
+static char *myri10ge_fw_aligned = "myri10ge_eth_z8e.dat";
+static int myri10ge_ecrc_enable = 1;
+static int myri10ge_max_intr_slots = 1024;
+static int myri10ge_small_bytes = -1; /* -1 == auto */
+static int myri10ge_msi = 1; /* enable msi by default */
+static int myri10ge_intr_coal_delay = 25;
+static int myri10ge_flow_control = 1;
+static int myri10ge_deassert_wait = 1;
+static int myri10ge_force_firmware = 0;
+static int myri10ge_skb_cross_4k = 0;
+static int myri10ge_initial_mtu = MYRI10GE_MAX_ETHER_MTU - ETH_HLEN;
+static int myri10ge_napi_weight = 64;
+static int myri10ge_watchdog_timeout = 1;
+static int myri10ge_max_irq_loops = 1048576;
+
+module_param(myri10ge_fw_name, charp, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(myri10ge_fw_name, "Firmware image name\n");
+module_param(myri10ge_max_intr_slots, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_max_intr_slots, "Interrupt queue slots\n");
+module_param(myri10ge_small_bytes, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(myri10ge_small_bytes, "Threshold of small packets\n");
+module_param(myri10ge_msi, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_msi, "Enable Message Signalled Interrupts\n");
+module_param(myri10ge_intr_coal_delay, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_intr_coal_delay, "Interrupt coalescing delay\n");
+module_param(myri10ge_ecrc_enable, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_ecrc_enable, "Enable Extended CRC on PCI-E\n");
+module_param(myri10ge_flow_control, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_flow_control, "Pause parameter\n");
+module_param(myri10ge_deassert_wait, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(myri10ge_deassert_wait,
+ "Wait when deasserting legacy interrupts\n");
+module_param(myri10ge_force_firmware, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_force_firmware,
+ "Force firmware to assume aligned completions\n");
+module_param(myri10ge_skb_cross_4k, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(myri10ge_skb_cross_4k,
+ "Can a small skb cross a 4KB boundary?\n");
+module_param(myri10ge_initial_mtu, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_initial_mtu, "Initial MTU\n");
+module_param(myri10ge_napi_weight, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_napi_weight, "Set NAPI weight\n");
+module_param(myri10ge_watchdog_timeout, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_watchdog_timeout, "Set watchdog timeout\n");
+module_param(myri10ge_max_irq_loops, int, S_IRUGO);
+MODULE_PARM_DESC(myri10ge_max_irq_loops,
+ "Set stuck legacy IRQ detection threshold\n");
+
+#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 int
+myri10ge_send_cmd(struct myri10ge_priv *mgp, u32 cmd, struct myri10ge_cmd *data)
+{
+ struct mcp_cmd *buf;
+ char buf_bytes[sizeof(*buf) + 8];
+ volatile struct mcp_cmd_response *response = mgp->cmd;
+ volatile char __iomem *cmd_addr = mgp->sram + MYRI10GE_MCP_CMD_OFFSET;
+ 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 = 0xffffffff;
+ mb();
+ myri10ge_pio_copy((void __iomem *)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
+ */
+ for (sleep_total = 0;
+ sleep_total < (15 * 1000) && response->result == 0xffffffff;
+ sleep_total += 10) {
+ udelay(10);
+ }
+ result = ntohl(response->result);
+ value = ntohl(response->data);
+ if (result != 0xffffffff) {
+ if (result == 0) {
+ data->data0 = value;
+ return 0;
+ } 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((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)
+{
+ volatile u32 *confirm;
+ volatile char __iomem *submit;
+ u32 buf[16];
+ u32 dma_low, dma_high;
+ int i;
+
+ /* clear confirmation addr */
+ confirm = (volatile u32 *)mgp->cmd;
+ *confirm = 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] = htonl(0xffffffff); /* 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 + 0xfc01c0;
+
+ myri10ge_pio_copy((void __iomem *)submit, &buf, sizeof(buf));
+ for (i = 0; *confirm != 0xffffffff && i < 20; i++)
+ udelay(1000);
+ if (*confirm != 0xffffffff) {
+ 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;
+ int major, minor;
+
+ /* 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 */
+ strncpy(mgp->fw_version, hdr->version, sizeof(mgp->fw_version));
+
+ sscanf(mgp->fw_version, "%d.%d", &major, &minor);
+
+ if (!(major == MYRI10GE_MCP_VERSION_MAJOR
+ && minor == MYRI10GE_MCP_VERSION_MINOR)) {
+ dev_err(dev, "Found firmware version %s\n", mgp->fw_version);
+ dev_err(dev, "Driver needs %d.%d\n", MYRI10GE_MCP_VERSION_MAJOR,
+ MYRI10GE_MCP_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;
+ struct mcp_gen_header *hdr;
+ size_t hdr_offset;
+ int status;
+
+ if ((status = 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(*(u32 *) (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);
+ memcpy_toio(mgp->sram + MYRI10GE_FW_OFFSET, fw->data, fw->size);
+ /* corruption checking is good for parity recovery and buggy chipset */
+ memcpy_fromio(fw->data, mgp->sram + MYRI10GE_FW_OFFSET, fw->size);
+ reread_crc = crc32(~0, fw->data, fw->size);
+ 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 = ntohl(__raw_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) {
+ dev_err(dev, "could not malloc firmware hdr\n");
+ return -ENOMEM;
+ }
+ memcpy_fromio(hdr, mgp->sram + hdr_offset, bytes);
+ status = myri10ge_validate_firmware(mgp, hdr);
+ kfree(hdr);
+ return status;
+}
+
+static int myri10ge_load_firmware(struct myri10ge_priv *mgp)
+{
+ volatile u32 *confirm;
+ volatile char __iomem *submit;
+ u32 buf[16];
+ u32 dma_low, dma_high, size;
+ int status, i;
+
+ status = myri10ge_load_hotplug_firmware(mgp, &size);
+ if (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");
+ }
+
+ mgp->fw_name = "adopted";
+ mgp->tx.boundary = 2048;
+ return status;
+ }
+
+ /* clear confirmation addr */
+ confirm = (volatile u32 *)mgp->cmd;
+ *confirm = 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] = htonl(0xffffffff); /* 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 + 0xfc0000;
+
+ myri10ge_pio_copy((void __iomem *)submit, &buf, sizeof(buf));
+ mb();
+ udelay(1000);
+ mb();
+ i = 0;
+ while (*confirm != 0xffffffff && i < 20) {
+ udelay(1000);
+ i++;
+ }
+ if (*confirm != 0xffffffff) {
+ dev_err(&mgp->pdev->dev, "handoff failed\n");
+ return -ENXIO;
+ }
+ dev_info(&mgp->pdev->dev, "handoff confirmed\n");
+ myri10ge_dummy_rdma(mgp, mgp->tx.boundary != 4096);
+
+ return 0;
+}
+
+static int myri10ge_update_mac_address(struct myri10ge_priv *mgp, 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, MYRI10GE_MCP_SET_MAC_ADDRESS, &cmd);
+ return status;
+}
+
+static int myri10ge_change_pause(struct myri10ge_priv *mgp, int pause)
+{
+ struct myri10ge_cmd cmd;
+ int status, ctl;
+
+ ctl = pause ? MYRI10GE_MCP_ENABLE_FLOW_CONTROL :
+ MYRI10GE_MCP_DISABLE_FLOW_CONTROL;
+ status = myri10ge_send_cmd(mgp, ctl, &cmd);
+
+ if (status) {
+ printk(KERN_ERR
+ "myri10ge: %s: Failed to set flow control mode\n",
+ mgp->dev->name);
+ return status;
+ }
+ mgp->pause = pause;
+ return 0;
+}
+
+static void myri10ge_change_promisc(struct myri10ge_priv *mgp, int promisc)
+{
+ struct myri10ge_cmd cmd;
+ int status, ctl;
+
+ ctl = promisc ? MYRI10GE_MCP_ENABLE_PROMISC :
+ MYRI10GE_MCP_DISABLE_PROMISC;
+ status = myri10ge_send_cmd(mgp, ctl, &cmd);
+ if (status) {
+ printk(KERN_ERR "myri10ge: %s: Failed to set promisc mode\n",
+ mgp->dev->name);
+ }
+}
+
+static int myri10ge_reset(struct myri10ge_priv *mgp)
+{
+
+ struct myri10ge_cmd cmd;
+ int status;
+ size_t bytes;
+ u32 len;
+
+ /* 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, MYRI10GE_MCP_CMD_RESET, &cmd);
+ if (status != 0) {
+ dev_err(&mgp->pdev->dev, "failed reset\n");
+ return -ENXIO;
+ }
+
+ /* Now exchange information about interrupts */
+
+ bytes = myri10ge_max_intr_slots * sizeof(*mgp->rx_done.entry);
+ memset(mgp->rx_done.entry, 0, bytes);
+ cmd.data0 = (u32) bytes;
+ status = myri10ge_send_cmd(mgp, MYRI10GE_MCP_CMD_SET_INTRQ_SIZE, &cmd);
+ cmd.data0 = MYRI10GE_LOWPART_TO_U32(mgp->rx_done.bus);
+ cmd.data1 = MYRI10GE_HIGHPART_TO_U32(mgp->rx_done.bus);
+ status |= myri10ge_send_cmd(mgp, MYRI10GE_MCP_CMD_SET_INTRQ_DMA, &cmd);
+
+ status |=
+ myri10ge_send_cmd(mgp, MYRI10GE_MCP_CMD_GET_IRQ_ACK_OFFSET, &cmd);
+ mgp->irq_claim = (__iomem u32 *) (mgp->sram + cmd.data0);
+ if (!mgp->msi_enabled) {
+ status |= myri10ge_send_cmd
+ (mgp, MYRI10GE_MCP_CMD_GET_IRQ_DEASSERT_OFFSET, &cmd);
+ mgp->irq_deassert = (__iomem u32 *) (mgp->sram + cmd.data0);
+
+ }
+ status |= myri10ge_send_cmd
+ (mgp, MYRI10GE_MCP_CMD_GET_INTR_COAL_DELAY_OFFSET, &cmd);
+ mgp->intr_coal_delay_ptr = (__iomem u32 *) (mgp->sram + cmd.data0);
+ if (status != 0) {
+ dev_err(&mgp->pdev->dev, "failed set interrupt parameters\n");
+ return status;
+ }
+ __raw_writel(htonl(mgp->intr_coal_delay), mgp->intr_coal_delay_ptr);
+
+ /* 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(mgp->rx_done.bus);
+ cmd.data1 = MYRI10GE_HIGHPART_TO_U32(mgp->rx_done.bus);
+ cmd.data2 = len * 0x10000;
+ status = myri10ge_send_cmd(mgp, MYRI10GE_MCP_DMA_TEST, &cmd);
+ if (status == 0)
+ mgp->read_dma = ((cmd.data0 >> 16) * len * 2) /
+ (cmd.data0 & 0xffff);
+ else
+ dev_warn(&mgp->pdev->dev, "DMA read benchmark failed: %d\n",
+ status);
+ cmd.data0 = MYRI10GE_LOWPART_TO_U32(mgp->rx_done.bus);
+ cmd.data1 = MYRI10GE_HIGHPART_TO_U32(mgp->rx_done.bus);
+ cmd.data2 = len * 0x1;
+ status = myri10ge_send_cmd(mgp, MYRI10GE_MCP_DMA_TEST, &cmd);
+ if (status == 0)
+ mgp->write_dma = ((cmd.data0 >> 16) * len * 2) /
+ (cmd.data0 & 0xffff);
+ else
+ dev_warn(&mgp->pdev->dev, "DMA write benchmark failed: %d\n",
+ status);
+
+ cmd.data0 = MYRI10GE_LOWPART_TO_U32(mgp->rx_done.bus);
+ cmd.data1 = MYRI10GE_HIGHPART_TO_U32(mgp->rx_done.bus);
+ cmd.data2 = len * 0x10001;
+ status = myri10ge_send_cmd(mgp, MYRI10GE_MCP_DMA_TEST, &cmd);
+ if (status == 0)
+ mgp->read_write_dma = ((cmd.data0 >> 16) * len * 2 * 2) /
+ (cmd.data0 & 0xffff);
+ else
+ dev_warn(&mgp->pdev->dev,
+ "DMA read/write benchmark failed: %d\n", status);
+
+ memset(mgp->rx_done.entry, 0, bytes);
+
+ /* reset mcp/driver shared state back to 0 */
+ mgp->tx.req = 0;
+ mgp->tx.done = 0;
+ mgp->tx.pkt_start = 0;
+ mgp->tx.pkt_done = 0;
+ mgp->rx_big.cnt = 0;
+ mgp->rx_small.cnt = 0;
+ mgp->rx_done.idx = 0;
+ mgp->rx_done.cnt = 0;
+ status = myri10ge_update_mac_address(mgp, mgp->dev->dev_addr);
+ myri10ge_change_promisc(mgp, 0);
+ myri10ge_change_pause(mgp, mgp->pause);
+ return status;
+}
+
+static inline void
+myri10ge_submit_8rx(struct mcp_kreq_ether_recv __iomem * dst,
+ struct mcp_kreq_ether_recv *src)
+{
+ u32 low;
+
+ low = src->addr_low;
+ src->addr_low = DMA_32BIT_MASK;
+ myri10ge_pio_copy(dst, src, 8 * sizeof(*src));
+ mb();
+ src->addr_low = low;
+ *(u32 __force *) & dst->addr_low = src->addr_low;
+ mb();
+}
+
+/*
+ * Set of routunes to get a new receive buffer. Any buffer which
+ * crosses a 4KB boundary must start on a 4KB boundary due to PCIe
+ * wdma restrictions. We also try to align any smaller allocation to
+ * at least a 16 byte boundary for efficiency. We assume the linux
+ * memory allocator works by powers of 2, and will not return memory
+ * smaller than 2KB which crosses a 4KB boundary. If it does, we fall
+ * back to allocating 2x as much space as required.
+ *
+ * We intend to replace large (>4KB) skb allocations by using
+ * pages directly and building a fraglist in the near future.
+ */
+
+static inline struct sk_buff *myri10ge_alloc_big(int bytes)
+{
+ struct sk_buff *skb;
+ unsigned long data, roundup;
+
+ skb = dev_alloc_skb(bytes + 4096 + MYRI10GE_MCP_ETHER_PAD);
+ if (skb == NULL)
+ return NULL;
+
+ /* Correct skb->truesize so that socket buffer
+ * accounting is not confused the rounding we must
+ * do to satisfy alignment constraints.
+ */
+ skb->truesize -= 4096;
+
+ data = (unsigned long)(skb->data);
+ roundup = (-data) & (4095);
+ skb_reserve(skb, roundup);
+ return skb;
+}
+
+/* Allocate 2x as much space as required and use whichever portion
+ * does not cross a 4KB boundary */
+static inline struct sk_buff *myri10ge_alloc_small_safe(unsigned int bytes)
+{
+ struct sk_buff *skb;
+ unsigned long data, boundary;
+
+ skb = dev_alloc_skb(2 * (bytes + MYRI10GE_MCP_ETHER_PAD) - 1);
+ if (unlikely(skb == NULL))
+ return NULL;
+
+ /* Correct skb->truesize so that socket buffer
+ * accounting is not confused the rounding we must
+ * do to satisfy alignment constraints.
+ */
+ skb->truesize -= bytes + MYRI10GE_MCP_ETHER_PAD;
+
+ data = (unsigned long)(skb->data);
+ boundary = (data + 4095UL) & ~4095UL;
+ if ((boundary - data) >= (bytes + MYRI10GE_MCP_ETHER_PAD)) {
+ return skb;
+ }
+ skb_reserve(skb, boundary - data);
+ return skb;
+}
+
+/* Allocate just enough space, and verify that the allocated
+ * space does not cross a 4KB boundary */
+static inline struct sk_buff *myri10ge_alloc_small(int bytes)
+{
+ struct sk_buff *skb;
+ unsigned long roundup, data, end;
+
+ skb = dev_alloc_skb(bytes + 16 + MYRI10GE_MCP_ETHER_PAD);
+ if (unlikely(skb == NULL))
+ return NULL;
+
+ /* Round allocated buffer to 16 byte boundary */
+ data = (unsigned long)(skb->data);
+ roundup = (-data) & 15UL;
+ skb_reserve(skb, roundup);
+ /* Verify that the data buffer does not cross a page boundary */
+ data = (unsigned long)(skb->data);
+ end = data + bytes + MYRI10GE_MCP_ETHER_PAD - 1;
+ if (unlikely(((end >> 12) != (data >> 12)) && (data & 4095UL))) {
+ printk
+ ("myri10ge_alloc_small: small skb crossed 4KB boundary\n");
+ myri10ge_skb_cross_4k = 1;
+ dev_kfree_skb_any(skb);
+ skb = myri10ge_alloc_small_safe(bytes);
+ }
+ return skb;
+}
+
+static inline int
+myri10ge_getbuf(struct myri10ge_rx_buf *rx, struct pci_dev *pdev, int bytes,
+ int idx)
+{
+ struct sk_buff *skb;
+ dma_addr_t bus;
+ int len, retval = 0;
+
+ bytes += VLAN_HLEN; /* account for 802.1q vlan tag */
+
+ if ((bytes + MYRI10GE_MCP_ETHER_PAD) >
+ (4096 - 16) /* linux overhead */ ) {
+ skb = myri10ge_alloc_big(bytes);
+ } else {
+ if (myri10ge_skb_cross_4k) {
+ skb = myri10ge_alloc_small_safe(bytes);
+ } else {
+ skb = myri10ge_alloc_small(bytes);
+ }
+ }
+ if (unlikely(skb == NULL)) {
+ rx->alloc_fail++;
+ retval = -ENOBUFS;
+ goto done;
+ }
+
+ /* set len so that it only covers the area we
+ * need mapped for DMA */
+ len = bytes + MYRI10GE_MCP_ETHER_PAD;
+
+ bus = pci_map_single(pdev, skb->data, len, PCI_DMA_FROMDEVICE);
+ rx->info[idx].skb = skb;
+ pci_unmap_addr_set(&rx->info[idx], bus, bus);
+ pci_unmap_len_set(&rx->info[idx], len, len);
+ rx->shadow[idx].addr_low = htonl(MYRI10GE_LOWPART_TO_U32(bus));
+ rx->shadow[idx].addr_high = htonl(MYRI10GE_HIGHPART_TO_U32(bus));
+
+ done:
+ /* copy 8 descriptors (64-bytes) to the mcp at a time */
+ if ((idx & 7) == 7) {
+ if (rx->wc_fifo == NULL) {
+ myri10ge_submit_8rx(&rx->lanai[idx - 7],
+ &rx->shadow[idx - 7]);
+ } else {
+ mb();
+ myri10ge_pio_copy((void __iomem *)rx->wc_fifo,
+ &rx->shadow[idx - 7], 64);
+ }
+ }
+ return retval;
+}
+
+static inline void myri10ge_vlan_ip_csum(struct sk_buff *skb, u16 hw_csum)
+{
+ struct vlan_hdr *vh = (struct vlan_hdr *)(skb->data);
+
+ if ((skb->protocol == ntohs(ETH_P_8021Q)) &&
+ (vh->h_vlan_encapsulated_proto == htons(ETH_P_IP) ||
+ vh->h_vlan_encapsulated_proto == htons(ETH_P_IPV6))) {
+ skb->csum = hw_csum;
+ skb->ip_summed = CHECKSUM_HW;
+ }
+}
+
+static inline unsigned long
+myri10ge_rx_done(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx,
+ int bytes, int len, int csum)
+{
+ dma_addr_t bus;
+ struct sk_buff *skb;
+ int idx, unmap_len;
+
+ idx = rx->cnt & rx->mask;
+ rx->cnt++;
+
+ /* save a pointer to the received skb */
+ skb = rx->info[idx].skb;
+ bus = pci_unmap_addr(&rx->info[idx], bus);
+ unmap_len = pci_unmap_len(&rx->info[idx], len);
+
+ /* try to replace the received skb */
+ if (myri10ge_getbuf(rx, mgp->pdev, bytes, idx)) {
+ /* drop the frame -- the old skbuf is re-cycled */
+ mgp->stats.rx_dropped += 1;
+ return 0;
+ }
+
+ /* unmap the recvd skb */
+ pci_unmap_single(mgp->pdev, bus, unmap_len, PCI_DMA_FROMDEVICE);
+
+ /* mcp implicitly skips 1st bytes so that packet is properly
+ * aligned */
+ skb_reserve(skb, MYRI10GE_MCP_ETHER_PAD);
+
+ /* set the length of the frame */
+ skb_put(skb, len);
+
+ skb->protocol = eth_type_trans(skb, mgp->dev);
+ skb->dev = mgp->dev;
+ if (mgp->csum_flag) {
+ if ((skb->protocol == ntohs(ETH_P_IP)) ||
+ (skb->protocol == ntohs(ETH_P_IPV6))) {
+ skb->csum = ntohs((u16) csum);
+ skb->ip_summed = CHECKSUM_HW;
+ } else {
+ myri10ge_vlan_ip_csum(skb, ntohs((u16) csum));
+ }
+ }
+
+ netif_receive_skb(skb);
+ mgp->dev->last_rx = jiffies;
+ return 1;
+}
+
+static inline void myri10ge_tx_done(struct myri10ge_priv *mgp, int mcp_index)
+{
+ struct pci_dev *pdev = mgp->pdev;
+ struct myri10ge_tx_buf *tx = &mgp->tx;
+ struct sk_buff *skb;
+ int idx, len;
+ int limit = 0;
+
+ 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 = pci_unmap_len(&tx->info[idx], len);
+ pci_unmap_len_set(&tx->info[idx], len, 0);
+ if (skb) {
+ mgp->stats.tx_bytes += skb->len;
+ mgp->stats.tx_packets++;
+ dev_kfree_skb_irq(skb);
+ if (len)
+ pci_unmap_single(pdev,
+ pci_unmap_addr(&tx->info[idx],
+ bus), len,
+ PCI_DMA_TODEVICE);
+ } else {
+ if (len)
+ pci_unmap_page(pdev,
+ pci_unmap_addr(&tx->info[idx],
+ bus), len,
+ PCI_DMA_TODEVICE);
+ }
+
+ /* limit potential for livelock by only handling
+ * 2 full tx rings per call */
+ if (unlikely(++limit > 2 * tx->mask))
+ break;
+ }
+ /* start the queue if we've stopped it */
+ if (netif_queue_stopped(mgp->dev)
+ && tx->req - tx->done < (tx->mask >> 1)) {
+ mgp->wake_queue++;
+ netif_wake_queue(mgp->dev);
+ }
+}
+
+static inline void myri10ge_clean_rx_done(struct myri10ge_priv *mgp, int *limit)
+{
+ struct myri10ge_rx_done *rx_done = &mgp->rx_done;
+ unsigned long rx_bytes = 0;
+ unsigned long rx_packets = 0;
+ unsigned long rx_ok;
+
+ int idx = rx_done->idx;
+ int cnt = rx_done->cnt;
+ u16 length;
+ u16 checksum;
+
+ while (rx_done->entry[idx].length != 0 && *limit != 0) {
+ length = ntohs(rx_done->entry[idx].length);
+ rx_done->entry[idx].length = 0;
+ checksum = ntohs(rx_done->entry[idx].checksum);
+ if (length <= mgp->small_bytes)
+ rx_ok = myri10ge_rx_done(mgp, &mgp->rx_small,
+ mgp->small_bytes,
+ length, checksum);
+ else
+ rx_ok = myri10ge_rx_done(mgp, &mgp->rx_big,
+ mgp->dev->mtu + ETH_HLEN,
+ length, checksum);
+ rx_packets += rx_ok;
+ rx_bytes += rx_ok * (unsigned long)length;
+ cnt++;
+ idx = cnt & (myri10ge_max_intr_slots - 1);
+
+ /* limit potential for livelock by only handling a
+ * limited number of frames. */
+ (*limit)--;
+ }
+ rx_done->idx = idx;
+ rx_done->cnt = cnt;
+ mgp->stats.rx_packets += rx_packets;
+ mgp->stats.rx_bytes += rx_bytes;
+}
+
+static inline void myri10ge_check_statblock(struct myri10ge_priv *mgp)
+{
+ struct mcp_irq_data *stats = mgp->fw_stats;
+
+ if (unlikely(stats->stats_updated)) {
+ if (mgp->link_state != stats->link_up) {
+ mgp->link_state = stats->link_up;
+ if (mgp->link_state) {
+ printk("myri10ge: %s: link up\n",
+ mgp->dev->name);
+ netif_carrier_on(mgp->dev);
+ } else {
+ printk("myri10ge: %s: link down\n",
+ mgp->dev->name);
+ netif_carrier_off(mgp->dev);
+ }
+ }
+ if (mgp->rdma_tags_available !=
+ ntohl(mgp->fw_stats->rdma_tags_available)) {
+ mgp->rdma_tags_available =
+ ntohl(mgp->fw_stats->rdma_tags_available);
+ printk("myri10ge: %s: RDMA timed out! "
+ "%d tags left\n", mgp->dev->name,
+ mgp->rdma_tags_available);
+ }
+ mgp->down_cnt += stats->link_down;
+ if (stats->link_down)
+ wake_up(&mgp->down_wq);
+ }
+}
+
+static int myri10ge_poll(struct net_device *netdev, int *budget)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+ struct myri10ge_rx_done *rx_done = &mgp->rx_done;
+ int limit, orig_limit, work_done;
+
+ /* process as many rx events as NAPI will allow */
+ limit = min(*budget, netdev->quota);
+ orig_limit = limit;
+ myri10ge_clean_rx_done(mgp, &limit);
+ work_done = orig_limit - limit;
+ *budget -= work_done;
+ netdev->quota -= work_done;
+
+ if (rx_done->entry[rx_done->idx].length == 0 || !netif_running(netdev)) {
+ netif_rx_complete(netdev);
+ __raw_writel(htonl(3), mgp->irq_claim);
+ return 0;
+ }
+ return 1;
+}
+
+static irqreturn_t myri10ge_intr(int irq, void *arg, struct pt_regs *regs)
+{
+ struct myri10ge_priv *mgp = arg;
+ struct mcp_irq_data *stats = mgp->fw_stats;
+ struct myri10ge_tx_buf *tx = &mgp->tx;
+ u32 send_done_count;
+ int i;
+
+ /* 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) {
+ netif_rx_schedule(mgp->dev);
+ }
+
+ if (!mgp->msi_enabled) {
+ __raw_writel(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;
+ do {
+ 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(mgp, (int)send_done_count);
+ if (*((u8 * volatile)&stats->valid) == 0)
+ cpu_relax();
+ if (unlikely(i > myri10ge_max_irq_loops)) {
+ printk("myri10ge: %s: irq stuck?\n", mgp->dev->name);
+ stats->valid = 0;
+ schedule_work(&mgp->watchdog_work);
+ }
+ } while (*((u8 * volatile)&stats->valid));
+
+ myri10ge_check_statblock(mgp);
+
+ __raw_writel(htonl(3), mgp->irq_claim + 1);
+ return (IRQ_HANDLED);
+}
+
+static int
+myri10ge_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+{
+ cmd->autoneg = AUTONEG_DISABLE;
+ cmd->speed = SPEED_10000;
+ cmd->duplex = DUPLEX_FULL;
+ return 0;
+}
+
+static void
+myri10ge_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+
+ strlcpy(info->driver, "myri10ge", sizeof(info->driver));
+ strlcpy(info->version, MYRI10GE_VERSION_STR, sizeof(info->version));
+ strlcpy(info->fw_version, mgp->fw_version, sizeof(info->fw_version));
+ strlcpy(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 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 myri10ge_priv *mgp = netdev_priv(netdev);
+
+ mgp->intr_coal_delay = coal->rx_coalesce_usecs;
+ __raw_writel(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->tx_pause));
+ }
+ if (pause->autoneg != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static void
+myri10ge_get_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+
+ ring->rx_mini_max_pending = mgp->rx_small.mask + 1;
+ ring->rx_max_pending = mgp->rx_big.mask + 1;
+ ring->rx_jumbo_max_pending = 0;
+ ring->tx_max_pending = mgp->rx_small.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 u32 myri10ge_get_rx_csum(struct net_device *netdev)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+ if (mgp->csum_flag)
+ return 1;
+ else
+ return 0;
+}
+
+static int myri10ge_set_rx_csum(struct net_device *netdev, u32 csum_enabled)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+ if (csum_enabled)
+ mgp->csum_flag = MYRI10GE_MCP_ETHER_FLAGS_CKSUM;
+ else
+ mgp->csum_flag = 0;
+ return 0;
+}
+
+static const char myri10ge_gstrings_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 */
+ "read_dma_bw_MBs", "write_dma_bw_MBs", "read_write_dma_bw_MBs",
+ "serial_number", "tx_pkt_start", "tx_pkt_done",
+ "tx_req", "tx_done", "rx_small_cnt", "rx_big_cnt",
+ "wake_queue", "stop_queue", "watchdog_resets", "tx_linearized",
+ "link_up", "dropped_link_overflow", "dropped_link_error_or_filtered",
+ "dropped_runt", "dropped_overrun", "dropped_no_small_buffer",
+ "dropped_no_big_buffer"
+};
+
+#define MYRI10GE_NET_STATS_LEN 21
+#define MYRI10GE_STATS_LEN sizeof(myri10ge_gstrings_stats) / ETH_GSTRING_LEN
+
+static void
+myri10ge_get_strings(struct net_device *netdev, u32 stringset, u8 * data)
+{
+ switch (stringset) {
+ case ETH_SS_STATS:
+ memcpy(data, *myri10ge_gstrings_stats,
+ sizeof(myri10ge_gstrings_stats));
+ break;
+ }
+}
+
+static int myri10ge_get_stats_count(struct net_device *netdev)
+{
+ return MYRI10GE_STATS_LEN;
+}
+
+static void
+myri10ge_get_ethtool_stats(struct net_device *netdev,
+ struct ethtool_stats *stats, u64 * data)
+{
+ struct myri10ge_priv *mgp = netdev_priv(netdev);
+ int i;
+
+ for (i = 0; i < MYRI10GE_NET_STATS_LEN; i++)
+ data[i] = ((unsigned long *)&mgp->stats)[i];
+
+ 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->tx.pkt_start;
+ data[i++] = (unsigned int)mgp->tx.pkt_done;
+ data[i++] = (unsigned int)mgp->tx.req;
+ data[i++] = (unsigned int)mgp->tx.done;
+ data[i++] = (unsigned int)mgp->rx_small.cnt;
+ data[i++] = (unsigned int)mgp->rx_big.cnt;
+ data[i++] = (unsigned int)mgp->wake_queue;
+ data[i++] = (unsigned int)mgp->stop_queue;
+ data[i++] = (unsigned int)mgp->watchdog_resets;
+ data[i++] = (unsigned int)mgp->tx_linearized;
+ data[i++] = (unsigned int)ntohl(mgp->fw_stats->link_up);
+ data[i++] = (unsigned int)ntohl(mgp->fw_stats->dropped_link_overflow);
+ data[i++] =
+ (unsigned int)ntohl(mgp->fw_stats->dropped_link_error_or_filtered);
+ data[i++] = (unsigned int)ntohl(mgp->fw_stats->dropped_runt);
+ data[i++] = (unsigned int)ntohl(mgp->fw_stats->dropped_overrun);
+ data[i++] = (unsigned int)ntohl(mgp->fw_stats->dropped_no_small_buffer);
+ data[i++] = (unsigned int)ntohl(mgp->fw_stats->dropped_no_big_buffer);
+}
+
+static struct ethtool_ops myri10ge_ethtool_ops = {
+ .get_settings = myri10ge_get_settings,
+ .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_rx_csum = myri10ge_get_rx_csum,
+ .set_rx_csum = myri10ge_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,
+#ifdef NETIF_F_TSO
+ .get_tso = ethtool_op_get_tso,
+ .set_tso = ethtool_op_set_tso,
+#endif
+ .get_strings = myri10ge_get_strings,
+ .get_stats_count = myri10ge_get_stats_count,
+ .get_ethtool_stats = myri10ge_get_ethtool_stats
+};
+
+static int myri10ge_open(struct net_device *dev)
+{
+ struct myri10ge_priv *mgp;
+ size_t bytes;
+ struct myri10ge_cmd cmd;
+ int tx_ring_size, rx_ring_size;
+ int tx_ring_entries, rx_ring_entries;
+ int i, status, big_pow2;
+
+ mgp = netdev_priv(dev);
+
+ if (mgp->running != MYRI10GE_ETH_STOPPED)
+ return -EBUSY;
+
+ mgp->running = MYRI10GE_ETH_STARTING;
+ status = myri10ge_reset(mgp);
+ if (status != 0) {
+ printk(KERN_ERR "myri10ge: %s: failed reset\n", dev->name);
+ mgp->running = MYRI10GE_ETH_STOPPED;
+ return -ENXIO;
+ }
+
+ /* decide what small buffer size to use. For good TCP rx
+ * performance, it is important to not receive 1514 byte
+ * frames into jumbo buffers, as it confuses the socket buffer
+ * accounting code, leading to drops and erratic performance.
+ */
+
+ if (dev->mtu <= ETH_DATA_LEN) {
+ mgp->small_bytes = 128; /* enough for a TCP header */
+ } else {
+ mgp->small_bytes = ETH_FRAME_LEN; /* enough for an ETH_DATA_LEN frame */
+ }
+ /* Override the small buffer size? */
+ if (myri10ge_small_bytes > 0) {
+ mgp->small_bytes = myri10ge_small_bytes;
+ }
+
+ /* If the user sets an obscenely small MTU, adjust the small
+ * bytes down to nearly nothing */
+ if (mgp->small_bytes >= (dev->mtu + ETH_HLEN))
+ mgp->small_bytes = 64;
+
+ /* get ring sizes */
+ status =
+ myri10ge_send_cmd(mgp, MYRI10GE_MCP_CMD_GET_SEND_RING_SIZE, &cmd);
+ tx_ring_size = cmd.data0;
+ status |=
+ myri10ge_send_cmd(mgp, MYRI10GE_MCP_CMD_GET_RX_RING_SIZE, &cmd);
+ rx_ring_size = cmd.data0;
+
+ /* get the lanai pointers to the send and receive rings */
+
+ status |=
+ myri10ge_send_cmd(mgp, MYRI10GE_MCP_CMD_GET_SEND_OFFSET, &cmd);
+ mgp->tx.lanai =
+ (struct mcp_kreq_ether_send __iomem *)(mgp->sram + cmd.data0);
+
+ status |=
+ myri10ge_send_cmd(mgp, MYRI10GE_MCP_CMD_GET_SMALL_RX_OFFSET, &cmd);
+ mgp->rx_small.lanai =
+ (struct mcp_kreq_ether_recv __iomem *)(mgp->sram + cmd.data0);
+
+ status |=
+ myri10ge_send_cmd(mgp, MYRI10GE_MCP_CMD_GET_BIG_RX_OFFSET, &cmd);
+ mgp->rx_big.lanai =
+ (struct mcp_kreq_ether_recv __iomem *)(mgp->sram + cmd.data0);
+
+ if (status != 0) {
+ printk(KERN_ERR
+ "myri10ge: %s: failed to get ring sizes or locations\n",
+ dev->name);
+ mgp->running = MYRI10GE_ETH_STOPPED;
+ return -ENXIO;
+ }
+
+ if (mgp->mtrr >= 0) {
+ mgp->tx.wc_fifo = (u8 __iomem *) mgp->sram + 0x200000;
+ mgp->rx_small.wc_fifo = (u8 __iomem *) mgp->sram + 0x300000;
+ mgp->rx_big.wc_fifo = (u8 __iomem *) mgp->sram + 0x340000;
+ } else {
+ mgp->tx.wc_fifo = NULL;
+ mgp->rx_small.wc_fifo = NULL;
+ mgp->rx_big.wc_fifo = NULL;
+ }
+
+ tx_ring_entries = tx_ring_size / sizeof(struct mcp_kreq_ether_send);
+ rx_ring_entries = rx_ring_size / sizeof(struct mcp_dma_addr);
+ mgp->tx.mask = tx_ring_entries - 1;
+ mgp->rx_small.mask = mgp->rx_big.mask = rx_ring_entries - 1;
+
+ /* allocate the host shadow rings */
+
+ bytes = 8 + (MYRI10GE_MCP_ETHER_MAX_SEND_DESC_TSO + 4)
+ * sizeof(*mgp->tx.req_list);
+ mgp->tx.req_bytes = kmalloc(bytes, GFP_KERNEL);
+ if (mgp->tx.req_bytes == NULL)
+ goto abort_with_nothing;
+ memset(mgp->tx.req_bytes, 0, bytes);
+
+ /* ensure req_list entries are aligned to 8 bytes */
+ mgp->tx.req_list = (struct mcp_kreq_ether_send *)
+ ALIGN((unsigned long)mgp->tx.req_bytes, 8);
+
+ bytes = rx_ring_entries * sizeof(*mgp->rx_small.shadow);
+ mgp->rx_small.shadow = kmalloc(bytes, GFP_KERNEL);
+ if (mgp->rx_small.shadow == NULL)
+ goto abort_with_tx_req_bytes;
+ memset(mgp->rx_small.shadow, 0, bytes);
+
+ bytes = rx_ring_entries * sizeof(*mgp->rx_big.shadow);
+ mgp->rx_big.shadow = kmalloc(bytes, GFP_KERNEL);
+ if (mgp->rx_big.shadow == NULL)
+ goto abort_with_rx_small_shadow;
+ memset(mgp->rx_big.shadow, 0, bytes);
+
+ /* allocate the host info rings */
+
+ bytes = tx_ring_entries * sizeof(*mgp->tx.info);
+ mgp->tx.info = kmalloc(bytes, GFP_KERNEL);
+ if (mgp->tx.info == NULL)
+ goto abort_with_rx_big_shadow;
+ memset(mgp->tx.info, 0, bytes);
+
+ bytes = rx_ring_entries * sizeof(*mgp->rx_small.info);
+ mgp->rx_small.info = kmalloc(bytes, GFP_KERNEL);
+ if (mgp->rx_small.info == NULL)
+ goto abort_with_tx_info;
+ memset(mgp->rx_small.info, 0, bytes);
+
+ bytes = rx_ring_entries * sizeof(*mgp->rx_big.info);
+ mgp->rx_big.info = kmalloc(bytes, GFP_KERNEL);
+ if (mgp->rx_big.info == NULL)
+ goto abort_with_rx_small_info;
+ memset(mgp->rx_big.info, 0, bytes);
+
+ /* Fill the receive rings */
+ for (i = 0; i <= mgp->rx_small.mask; i++) {
+ status = myri10ge_getbuf(&mgp->rx_small, mgp->pdev,
+ mgp->small_bytes, i);
+ if (status) {
+ printk(KERN_ERR
+ "myri10ge: %s: alloced only %d small bufs\n",
+ dev->name, i);
+ goto abort_with_rx_small_ring;
+ }
+ }
+
+ for (i = 0; i <= mgp->rx_big.mask; i++) {
+ status =
+ myri10ge_getbuf(&mgp->rx_big, mgp->pdev,
+ dev->mtu + ETH_HLEN, i);
+ if (status) {
+ printk(KERN_ERR
+ "myri10ge: %s: alloced only %d big bufs\n",
+ dev->name, i);
+ goto abort_with_rx_big_ring;
+ }
+ }
+
+ /* Firmware needs the big buff size as a power of 2. Lie and
+ * tell him the buffer is larger, because we only use 1
+ * buffer/pkt, and the mtu will prevent overruns.
+ */
+ big_pow2 = dev->mtu + ETH_HLEN + MYRI10GE_MCP_ETHER_PAD;
+ while ((big_pow2 & (big_pow2 - 1)) != 0)
+ big_pow2++;
+
+ /* now give firmware buffers sizes, and MTU */
+ cmd.data0 = dev->mtu + ETH_HLEN + VLAN_HLEN;
+ status = myri10ge_send_cmd(mgp, MYRI10GE_MCP_CMD_SET_MTU, &cmd);
+ cmd.data0 = mgp->small_bytes;
+ status |=
+ myri10ge_send_cmd(mgp, MYRI10GE_MCP_CMD_SET_SMALL_BUFFER_SIZE,
+ &cmd);
+ cmd.data0 = big_pow2;
+ status |=
+ myri10ge_send_cmd(mgp, MYRI10GE_MCP_CMD_SET_BIG_BUFFER_SIZE, &cmd);
+ if (status) {
+ printk(KERN_ERR "myri10ge: %s: Couldn't set buffer sizes\n",
+ dev->name);
+ goto abort_with_rx_big_ring;
+ }
+
+ cmd.data0 = MYRI10GE_LOWPART_TO_U32(mgp->fw_stats_bus);
+ cmd.data1 = MYRI10GE_HIGHPART_TO_U32(mgp->fw_stats_bus);
+ status = myri10ge_send_cmd(mgp, MYRI10GE_MCP_CMD_SET_STATS_DMA, &cmd);
+ if (status) {
+ printk(KERN_ERR "myri10ge: %s: Couldn't set stats DMA\n",
+ dev->name);
+ goto abort_with_rx_big_ring;
+ }
+
+ mgp->link_state = -1;
+ mgp->rdma_tags_available = 15;
+
+ netif_poll_enable(mgp->dev); /* must happen prior to any irq */
+
+ status = myri10ge_send_cmd(mgp, MYRI10GE_MCP_CMD_ETHERNET_UP, &cmd);
+ if (status) {
+ printk(KERN_ERR "myri10ge: %s: Couldn't bring up link\n",
+ dev->name);
+ goto abort_with_rx_big_ring;
+ }
+
+ mgp->wake_queue = 0;
+ mgp->stop_queue = 0;
+ mgp->running = MYRI10GE_ETH_RUNNING;
+ mgp->watchdog_timer.expires = jiffies + myri10ge_watchdog_timeout * HZ;
+ add_timer(&mgp->watchdog_timer);
+ netif_wake_queue(dev);
+ return 0;
+
+ abort_with_rx_big_ring:
+ for (i = 0; i <= mgp->rx_big.mask; i++) {
+ if (mgp->rx_big.info[i].skb != NULL)
+ dev_kfree_skb_any(mgp->rx_big.info[i].skb);
+ if (pci_unmap_len(&mgp->rx_big.info[i], len)) {
+ pci_unmap_single(mgp->pdev,
+ pci_unmap_addr(&mgp->rx_big.info[i],
+ bus),
+ pci_unmap_len(&mgp->rx_big.info[i],
+ len),
+ PCI_DMA_FROMDEVICE);
+ }
+ }
+
+ abort_with_rx_small_ring:
+ for (i = 0; i <= mgp->rx_small.mask; i++) {
+ if (mgp->rx_small.info[i].skb != NULL)
+ dev_kfree_skb_any(mgp->rx_small.info[i].skb);
+ if (pci_unmap_len(&mgp->rx_small.info[i], len)) {
+ pci_unmap_single(mgp->pdev,
+ pci_unmap_addr(&mgp->rx_small.info[i],
+ bus),
+ pci_unmap_len(&mgp->rx_small.info[i],
+ len),
+ PCI_DMA_FROMDEVICE);
+ }
+ }
+ kfree(mgp->rx_big.info);
+
+ abort_with_rx_small_info:
+ kfree(mgp->rx_small.info);
+
+ abort_with_tx_info:
+ kfree(mgp->tx.info);
+
+ abort_with_rx_big_shadow:
+ kfree(mgp->rx_big.shadow);
+
+ abort_with_rx_small_shadow:
+ kfree(mgp->rx_small.shadow);
+
+ abort_with_tx_req_bytes:
+ kfree(mgp->tx.req_bytes);
+ mgp->tx.req_bytes = NULL;
+ mgp->tx.req_list = NULL;
+
+ abort_with_nothing:
+ mgp->running = MYRI10GE_ETH_STOPPED;
+ return -ENOMEM;
+}
+
+static int myri10ge_close(struct net_device *dev)
+{
+ struct myri10ge_priv *mgp;
+ struct sk_buff *skb;
+ struct myri10ge_tx_buf *tx;
+ int status, i, old_down_cnt, len, idx;
+ struct myri10ge_cmd cmd;
+
+ mgp = netdev_priv(dev);
+
+ if (mgp->running != MYRI10GE_ETH_RUNNING)
+ return 0;
+
+ if (mgp->tx.req_bytes == NULL)
+ return 0;
+
+ del_timer_sync(&mgp->watchdog_timer);
+ mgp->running = MYRI10GE_ETH_STOPPING;
+ netif_poll_disable(mgp->dev);
+ netif_carrier_off(dev);
+ netif_stop_queue(dev);
+ old_down_cnt = mgp->down_cnt;
+ mb();
+ status = myri10ge_send_cmd(mgp, MYRI10GE_MCP_CMD_ETHERNET_DOWN, &cmd);
+ if (status) {
+ printk(KERN_ERR "myri10ge: %s: Couldn't bring down link\n",
+ dev->name);
+ }
+ wait_event_timeout(mgp->down_wq, old_down_cnt != mgp->down_cnt, HZ);
+ if (old_down_cnt == mgp->down_cnt) {
+ printk(KERN_ERR "myri10ge: %s never got down irq\n", dev->name);
+ }
+ netif_tx_disable(dev);
+
+ for (i = 0; i <= mgp->rx_big.mask; i++) {
+ if (mgp->rx_big.info[i].skb != NULL)
+ dev_kfree_skb_any(mgp->rx_big.info[i].skb);
+ if (pci_unmap_len(&mgp->rx_big.info[i], len)) {
+ pci_unmap_single(mgp->pdev,
+ pci_unmap_addr(&mgp->rx_big.info[i],
+ bus),
+ pci_unmap_len(&mgp->rx_big.info[i],
+ len),
+ PCI_DMA_FROMDEVICE);
+ }
+ }
+
+ for (i = 0; i <= mgp->rx_small.mask; i++) {
+ if (mgp->rx_small.info[i].skb != NULL)
+ dev_kfree_skb_any(mgp->rx_small.info[i].skb);
+ if (pci_unmap_len(&mgp->rx_small.info[i], len)) {
+ pci_unmap_single(mgp->pdev,
+ pci_unmap_addr(&mgp->rx_small.info[i],
+ bus),
+ pci_unmap_len(&mgp->rx_small.info[i],
+ len),
+ PCI_DMA_FROMDEVICE);
+ }
+ }
+
+ tx = &mgp->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 = pci_unmap_len(&tx->info[idx], len);
+ pci_unmap_len_set(&tx->info[idx], len, 0);
+ if (skb) {
+ mgp->stats.tx_dropped++;
+ dev_kfree_skb_any(skb);
+ if (len)
+ pci_unmap_single(mgp->pdev,
+ pci_unmap_addr(&tx->info[idx],
+ bus), len,
+ PCI_DMA_TODEVICE);
+ } else {
+ if (len)
+ pci_unmap_page(mgp->pdev,
+ pci_unmap_addr(&tx->info[idx],
+ bus), len,
+ PCI_DMA_TODEVICE);
+ }
+ }
+ kfree(mgp->rx_big.info);
+
+ kfree(mgp->rx_small.info);
+
+ kfree(mgp->tx.info);
+
+ kfree(mgp->rx_big.shadow);
+
+ kfree(mgp->rx_small.shadow);
+
+ kfree(mgp->tx.req_bytes);
+ mgp->tx.req_bytes = NULL;
+ mgp->tx.req_list = NULL;
+ mgp->running = MYRI10GE_ETH_STOPPED;
+ return 0;
+}
+
+/* copy an array of struct mcp_kreq_ether_send's to the mcp. Copy
+ * backwards one at a time and handle ring wraps */
+
+static inline void
+myri10ge_submit_req_backwards(struct myri10ge_tx_buf *tx,
+ struct mcp_kreq_ether_send *src, int cnt)
+{
+ int idx, starting_slot;
+ starting_slot = tx->req;
+ while (cnt > 1) {
+ cnt--;
+ idx = (starting_slot + cnt) & tx->mask;
+ myri10ge_pio_copy(&tx->lanai[idx], &src[cnt], sizeof(*src));
+ mb();
+ }
+}
+
+/*
+ * copy an array of struct mcp_kreq_ether_send's to the mcp. Copy
+ * at most 32 bytes at a time, so as to avoid involving the software
+ * pio handler in the nic. We re-write the first segment's flags
+ * to mark them valid only after writing the entire chain.
+ */
+
+static inline void
+myri10ge_submit_req(struct myri10ge_tx_buf *tx, struct mcp_kreq_ether_send *src,
+ int cnt)
+{
+ int idx, i;
+ u32 __iomem *dst_ints;
+ u32 *src_ints;
+ struct mcp_kreq_ether_send __iomem *dstp, *dst;
+ struct mcp_kreq_ether_send *srcp;
+ u8 last_flags;
+
+ idx = tx->req & tx->mask;
+
+ last_flags = src->flags;
+ src->flags = 0;
+ mb();
+ dst = dstp = &tx->lanai[idx];
+ srcp = src;
+
+ if ((idx + cnt) < tx->mask) {
+ for (i = 0; i < (cnt - 1); i += 2) {
+ myri10ge_pio_copy(dstp, srcp, 2 * sizeof(*src));
+ mb(); /* force write every 32 bytes */
+ srcp += 2;
+ dstp += 2;
+ }
+ } else {
+ /* submit all but the first request, and ensure
+ * that it is submitted below */
+ myri10ge_submit_req_backwards(tx, src, cnt);
+ i = 0;
+ }
+ if (i < cnt) {
+ /* submit the first request */
+ myri10ge_pio_copy(dstp, srcp, sizeof(*src));
+ mb(); /* barrier before setting valid flag */
+ }
+
+ /* re-write the last 32-bits with the valid flags */
+ src->flags = last_flags;
+ src_ints = (u32 *) src;
+ src_ints += 3;
+ dst_ints = (u32 __iomem *) dst;
+ dst_ints += 3;
+ *(u32 __force *) dst_ints = *src_ints;
+ tx->req += cnt;
+ mb();
+}
+
+static inline void
+myri10ge_submit_req_wc(struct myri10ge_tx_buf *tx,
+ struct mcp_kreq_ether_send *src, int cnt)
+{
+ tx->req += cnt;
+ mb();
+ while (cnt >= 4) {
+ myri10ge_pio_copy(tx->wc_fifo, src, 64);
+ mb();
+ src += 4;
+ cnt -= 4;
+ }
+ if (cnt > 0) {
+ /* pad it to 64 bytes. The src is 64 bytes bigger than it
+ * needs to be so that we don't overrun it */
+ myri10ge_pio_copy(tx->wc_fifo + (cnt << 18), src, 64);
+ mb();
+ }
+}
+
+/*
+ * Transmit a packet. We need to split the packet so that a single
+ * segment does not cross myri10ge->tx.boundary, so this makes segment
+ * counting tricky. So rather than try to count segments up front, we
+ * just give up if there are too few segments to hold a reasonably
+ * fragmented packet currently available. If we run
+ * out of segments while preparing a packet for DMA, we just linearize
+ * it and try again.
+ */
+
+static int myri10ge_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct myri10ge_priv *mgp = netdev_priv(dev);
+ struct mcp_kreq_ether_send *req;
+ struct myri10ge_tx_buf *tx = &mgp->tx;
+ struct skb_frag_struct *frag;
+ dma_addr_t bus;
+ u32 low, high_swapped;
+ unsigned int len;
+ int idx, last_idx, avail, frag_cnt, frag_idx, count, mss, max_segments;
+ u16 pseudo_hdr_offset, cksum_offset;
+ int cum_len, seglen, boundary, rdma_count;
+ u8 flags, odd_flag;
+
+ again:
+ req = tx->req_list;
+ avail = tx->mask - 1 - (tx->req - tx->done);
+
+ mss = 0;
+ max_segments = MYRI10GE_MCP_ETHER_MAX_SEND_DESC;
+
+#ifdef NETIF_F_TSO
+ if (skb->len > (dev->mtu + ETH_HLEN)) {
+ mss = skb_shinfo(skb)->tso_size;
+ if (mss != 0)
+ max_segments = MYRI10GE_MCP_ETHER_MAX_SEND_DESC_TSO;
+ }
+#endif /*NETIF_F_TSO */
+
+ if ((unlikely(avail < max_segments))) {
+ /* we are out of transmit resources */
+ mgp->stop_queue++;
+ netif_stop_queue(dev);
+ return 1;
+ }
+
+ /* Setup checksum offloading, if needed */
+ cksum_offset = 0;
+ pseudo_hdr_offset = 0;
+ odd_flag = 0;
+ flags = (MYRI10GE_MCP_ETHER_FLAGS_NO_TSO |
+ MYRI10GE_MCP_ETHER_FLAGS_FIRST);
+ if (likely(skb->ip_summed == CHECKSUM_HW)) {
+ cksum_offset = (skb->h.raw - skb->data);
+ pseudo_hdr_offset = (skb->h.raw + skb->csum) - skb->data;
+ /* If the headers are excessively large, then we must
+ * fall back to a software checksum */
+ if (unlikely(cksum_offset > 255 || pseudo_hdr_offset > 127)) {
+ if (skb_checksum_help(skb, 0))
+ goto drop;
+ cksum_offset = 0;
+ pseudo_hdr_offset = 0;
+ } else {
+ pseudo_hdr_offset = htons(pseudo_hdr_offset);
+ odd_flag = MYRI10GE_MCP_ETHER_FLAGS_ALIGN_ODD;
+ flags |= MYRI10GE_MCP_ETHER_FLAGS_CKSUM;
+ }
+ }
+
+ cum_len = 0;
+
+#ifdef NETIF_F_TSO
+ if (mss) { /* TSO */
+ /* this removes any CKSUM flag from before */
+ flags = (MYRI10GE_MCP_ETHER_FLAGS_TSO_HDR |
+ MYRI10GE_MCP_ETHER_FLAGS_FIRST);
+
+ /* negative cum_len signifies to the
+ * send loop that we are still in the
+ * header portion of the TSO packet.
+ * TSO header must be at most 134 bytes long */
+ cum_len = -((skb->h.raw - skb->data) + (skb->h.th->doff << 2));
+
+ /* for TSO, pseudo_hdr_offset holds mss.
+ * The firmware figures out where to put
+ * the checksum by parsing the header. */
+ pseudo_hdr_offset = htons(mss);
+ } else
+#endif /*NETIF_F_TSO */
+ /* Mark small packets, and pad out tiny packets */
+ if (skb->len <= MYRI10GE_MCP_ETHER_SEND_SMALL_SIZE) {
+ flags |= MYRI10GE_MCP_ETHER_FLAGS_SMALL;
+
+ /* pad frames to at least ETH_ZLEN bytes */
+ if (unlikely(skb->len < ETH_ZLEN)) {
+ skb = skb_padto(skb, ETH_ZLEN);
+ if (skb == NULL) {
+ /* The packet is gone, so we must
+ * return 0 */
+ mgp->stats.tx_dropped += 1;
+ return 0;
+ }
+ /* adjust the len to account for the zero pad
+ * so that the nic can know how long it is */
+ skb->len = ETH_ZLEN;
+ }
+ }
+
+ /* map the skb for DMA */
+ len = skb->len - skb->data_len;
+ idx = tx->req & tx->mask;
+ tx->info[idx].skb = skb;
+ bus = pci_map_single(mgp->pdev, skb->data, len, PCI_DMA_TODEVICE);
+ pci_unmap_addr_set(&tx->info[idx], bus, bus);
+ pci_unmap_len_set(&tx->info[idx], len, len);
+
+ frag_cnt = skb_shinfo(skb)->nr_frags;
+ frag_idx = 0;
+ count = 0;
+ rdma_count = 0;
+
+ /* "rdma_count" is the number of RDMAs belonging to the
+ * current packet BEFORE the current send request. For
+ * non-TSO packets, this is equal to "count".
+ * For TSO packets, rdma_count needs to be reset
+ * to 0 after a segment cut.
+ *
+ * The rdma_count field of the send request is
+ * the number of RDMAs of the packet starting at
+ * that request. For TSO send requests with one ore more cuts
+ * in the middle, this is the number of RDMAs starting
+ * after the last cut in the request. All previous
+ * segments before the last cut implicitly have 1 RDMA.
+ *
+ * Since the number of RDMAs is not known beforehand,
+ * it must be filled-in retroactively - after each
+ * segmentation cut or at the end of the entire packet.
+ */
+
+ while (1) {
+ /* Break the SKB or Fragment up into pieces which
+ * do not cross mgp->tx.boundary */
+ low = MYRI10GE_LOWPART_TO_U32(bus);
+ high_swapped = htonl(MYRI10GE_HIGHPART_TO_U32(bus));
+ while (len) {
+ u8 flags_next;
+ int cum_len_next;
+
+ if (unlikely(count == max_segments))
+ goto abort_linearize;
+
+ boundary = (low + tx->boundary) & ~(tx->boundary - 1);
+ seglen = boundary - low;
+ if (seglen > len)
+ seglen = len;
+ flags_next = flags & ~MYRI10GE_MCP_ETHER_FLAGS_FIRST;
+ cum_len_next = cum_len + seglen;
+#ifdef NETIF_F_TSO
+ if (mss) { /* TSO */
+ (req - rdma_count)->rdma_count = rdma_count + 1;
+
+ if (likely(cum_len >= 0)) { /* payload */
+ int next_is_first, chop;
+
+ chop = (cum_len_next > mss);
+ cum_len_next = cum_len_next % mss;
+ next_is_first = (cum_len_next == 0);
+ flags |= chop *
+ MYRI10GE_MCP_ETHER_FLAGS_TSO_CHOP;
+ flags_next |= next_is_first *
+ MYRI10GE_MCP_ETHER_FLAGS_FIRST;
+ rdma_count |= -(chop | next_is_first);
+ rdma_count += chop & !next_is_first;
+ } else if (likely(cum_len_next >= 0)) { /* header ends */
+ int small;
+
+ rdma_count = -1;
+ cum_len_next = 0;
+ seglen = -cum_len;
+ small =
+ (mss <=
+ MYRI10GE_MCP_ETHER_SEND_SMALL_SIZE);
+ flags_next =
+ MYRI10GE_MCP_ETHER_FLAGS_TSO_PLD |
+ MYRI10GE_MCP_ETHER_FLAGS_FIRST |
+ (small *
+ MYRI10GE_MCP_ETHER_FLAGS_SMALL);
+ }
+ }
+#endif /* NETIF_F_TSO */
+ req->addr_high = high_swapped;
+ req->addr_low = htonl(low);
+ req->pseudo_hdr_offset = pseudo_hdr_offset;
+ req->pad = 0; /* complete solid 16-byte block; does this matter? */
+ req->rdma_count = 1;
+ req->length = htons(seglen);
+ req->cksum_offset = cksum_offset;
+ req->flags = flags | ((cum_len & 1) * odd_flag);
+
+ low += seglen;
+ len -= seglen;
+ cum_len = cum_len_next;
+ flags = flags_next;
+ req++;
+ count++;
+ rdma_count++;
+ if (unlikely(cksum_offset > seglen))
+ cksum_offset -= seglen;
+ else
+ cksum_offset = 0;
+ }
+ if (frag_idx == frag_cnt)
+ break;
+
+ /* map next fragment for DMA */
+ idx = (count + tx->req) & tx->mask;
+ frag = &skb_shinfo(skb)->frags[frag_idx];
+ frag_idx++;
+ len = frag->size;
+ bus = pci_map_page(mgp->pdev, frag->page, frag->page_offset,
+ len, PCI_DMA_TODEVICE);
+ pci_unmap_addr_set(&tx->info[idx], bus, bus);
+ pci_unmap_len_set(&tx->info[idx], len, len);
+ }
+
+ (req - rdma_count)->rdma_count = rdma_count;
+#ifdef NETIF_F_TSO
+ if (mss) {
+ do {
+ req--;
+ req->flags |= MYRI10GE_MCP_ETHER_FLAGS_TSO_LAST;
+ } while (!(req->flags & (MYRI10GE_MCP_ETHER_FLAGS_TSO_CHOP |
+ MYRI10GE_MCP_ETHER_FLAGS_FIRST)));
+ }
+#endif
+ idx = ((count - 1) + tx->req) & tx->mask;
+ tx->info[idx].last = 1;
+ if (tx->wc_fifo == NULL)
+ myri10ge_submit_req(tx, tx->req_list, count);
+ else
+ myri10ge_submit_req_wc(tx, tx->req_list, count);
+ tx->pkt_start++;
+ if ((avail - count) < MYRI10GE_MCP_ETHER_MAX_SEND_DESC) {
+ mgp->stop_queue++;
+ netif_stop_queue(dev);
+ }
+ dev->trans_start = jiffies;
+ return 0;
+
+ abort_linearize:
+ /* Free any DMA resources we've alloced and clear out the skb
+ * slot so as to not trip up assertions, and to avoid a
+ * double-free if linearizing fails */
+
+ last_idx = (idx + 1) & tx->mask;
+ idx = tx->req & tx->mask;
+ tx->info[idx].skb = NULL;
+ do {
+ len = pci_unmap_len(&tx->info[idx], len);
+ if (len) {
+ if (tx->info[idx].skb != NULL) {
+ pci_unmap_single(mgp->pdev,
+ pci_unmap_addr(&tx->info[idx],
+ bus), len,
+ PCI_DMA_TODEVICE);
+ } else {
+ pci_unmap_page(mgp->pdev,
+ pci_unmap_addr(&tx->info[idx],
+ bus), len,
+ PCI_DMA_TODEVICE);
+ }
+ pci_unmap_len_set(&tx->info[idx], len, 0);
+ tx->info[idx].skb = NULL;
+ }
+ idx = (idx + 1) & tx->mask;
+ } while (idx != last_idx);
+ if (skb_shinfo(skb)->tso_size) {
+ printk(KERN_ERR
+ "myri10ge: %s: TSO but wanted to linearize?!?!?\n",
+ mgp->dev->name);
+ goto drop;
+ }
+
+ if (skb_linearize(skb, GFP_ATOMIC)) {
+ goto drop;
+ }
+ mgp->tx_linearized++;
+ goto again;
+
+ drop:
+ dev_kfree_skb_any(skb);
+ mgp->stats.tx_dropped += 1;
+ return 0;
+
+}
+
+static struct net_device_stats *myri10ge_get_stats(struct net_device *dev)
+{
+ struct myri10ge_priv *mgp = netdev_priv(dev);
+ return &mgp->stats;
+}
+
+static void myri10ge_set_multicast_list(struct net_device *dev)
+{
+ myri10ge_change_promisc(netdev_priv(dev), dev->flags & IFF_PROMISC);
+}
+
+static int myri10ge_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sa = addr;
+ struct myri10ge_priv *mgp = netdev_priv(dev);
+ int status;
+
+ if (!is_valid_ether_addr(sa->sa_data))
+ return -EADDRNOTAVAIL;
+
+ status = myri10ge_update_mac_address(mgp, sa->sa_data);
+ if (status != 0) {
+ printk(KERN_ERR
+ "myri10ge: %s: changing mac address failed with %d\n",
+ dev->name, status);
+ return status;
+ }
+
+ /* change the dev structure */
+ memcpy(dev->dev_addr, sa->sa_data, 6);
+ return 0;
+}
+
+static int myri10ge_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct myri10ge_priv *mgp = netdev_priv(dev);
+ int error = 0;
+
+ if ((new_mtu < 68) || (ETH_HLEN + new_mtu > MYRI10GE_MAX_ETHER_MTU)) {
+ printk(KERN_ERR "myri10ge: %s: new mtu (%d) is not valid\n",
+ dev->name, new_mtu);
+ return -EINVAL;
+ }
+ printk("%s: changing mtu from %d to %d\n",
+ dev->name, dev->mtu, new_mtu);
+ if (mgp->running) {
+ /* if we change the mtu on an active device, we must
+ * reset the device so the firmware sees the change */
+ myri10ge_close(dev);
+ dev->mtu = new_mtu;
+ myri10ge_open(dev);
+ } else {
+ dev->mtu = new_mtu;
+ }
+ return error;
+}
+
+/*
+ * Enable ECRC to align PCI-E Completion packets on an 8-byte boundary.
+ * Only do it if the bridge is a root port since we don't want to disturb
+ * any other device, except if forced with myri10ge_ecrc_enable > 1.
+ */
+
+#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_PCIE 0x005d
+
+static void myri10ge_enable_ecrc(struct myri10ge_priv *mgp)
+{
+ struct pci_dev *bridge = mgp->pdev->bus->self;
+ struct device *dev = &mgp->pdev->dev;
+ unsigned cap;
+ unsigned err_cap;
+ u16 val;
+ u8 ext_type;
+ int ret;
+
+ if (!myri10ge_ecrc_enable || !bridge)
+ return;
+
+ /* check that the bridge is a root port */
+ cap = pci_find_capability(bridge, PCI_CAP_ID_EXP);
+ pci_read_config_word(bridge, cap + PCI_CAP_FLAGS, &val);
+ ext_type = (val & PCI_EXP_FLAGS_TYPE) >> 4;
+ if (ext_type != PCI_EXP_TYPE_ROOT_PORT) {
+ if (myri10ge_ecrc_enable > 1) {
+ struct pci_dev *old_bridge = bridge;
+
+ /* Walk the hierarchy up to the root port
+ * where ECRC has to be enabled */
+ do {
+ bridge = bridge->bus->self;
+ if (!bridge) {
+ dev_err(dev,
+ "Failed to find root port"
+ " to force ECRC\n");
+ return;
+ }
+ cap =
+ pci_find_capability(bridge, PCI_CAP_ID_EXP);
+ pci_read_config_word(bridge,
+ cap + PCI_CAP_FLAGS, &val);
+ ext_type = (val & PCI_EXP_FLAGS_TYPE) >> 4;
+ } while (ext_type != PCI_EXP_TYPE_ROOT_PORT);
+
+ dev_info(dev,
+ "Forcing ECRC on non-root port %s"
+ " (enabling on root port %s)\n",
+ pci_name(old_bridge), pci_name(bridge));
+ } else {
+ dev_err(dev,
+ "Not enabling ECRC on non-root port %s\n",
+ pci_name(bridge));
+ return;
+ }
+ }
+
+ cap = pci_find_ext_capability(bridge, PCI_EXT_CAP_ID_ERR);
+ /* nvidia ext cap is not always linked in ext cap chain */
+ if (!cap
+ && bridge->vendor == PCI_VENDOR_ID_NVIDIA
+ && bridge->device == PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_PCIE)
+ cap = 0x160;
+
+ if (!cap)
+ return;
+
+ ret = pci_read_config_dword(bridge, cap + PCI_ERR_CAP, &err_cap);
+ if (ret) {
+ dev_err(dev, "failed reading ext-conf-space of %s\n",
+ pci_name(bridge));
+ dev_err(dev, "\t pci=nommconf in use? "
+ "or buggy/incomplete/absent acpi MCFG attr?\n");
+ return;
+ }
+ if (!(err_cap & PCI_ERR_CAP_ECRC_GENC))
+ return;
+
+ err_cap |= PCI_ERR_CAP_ECRC_GENE;
+ pci_write_config_dword(bridge, cap + PCI_ERR_CAP, err_cap);
+ dev_info(dev, "Enabled ECRC on upstream bridge %s\n", pci_name(bridge));
+ mgp->tx.boundary = 4096;
+ mgp->fw_name = myri10ge_fw_aligned;
+}
+
+/*
+ * The Lanai Z8E PCI-E interface achieves higher Read-DMA throughput
+ * when the PCI-E Completion packets are aligned on an 8-byte
+ * boundary. Some PCI-E chip sets always align Completion packets; on
+ * the ones that do not, the alignment can be enforced by enabling
+ * ECRC generation (if supported).
+ *
+ * When PCI-E Completion packets are not aligned, it is actually more
+ * efficient to limit Read-DMA transactions to 2KB, rather than 4KB.
+ *
+ * If the driver can neither enable ECRC nor verify that it has
+ * already been enabled, then it must use a firmware image which works
+ * around unaligned completion packets (myri10ge_ethp_z8e.dat), and it
+ * should also ensure that it never gives the device a Read-DMA which is
+ * larger than 2KB by setting the tx.boundary to 2KB. If ECRC is
+ * enabled, then the driver should use the aligned (myri10ge_eth_z8e.dat)
+ * firmware image, and set tx.boundary to 4KB.
+ */
+
+#define PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE 0x0132
+
+static void myri10ge_select_firmware(struct myri10ge_priv *mgp)
+{
+ struct pci_dev *bridge = mgp->pdev->bus->self;
+
+ mgp->tx.boundary = 2048;
+ mgp->fw_name = myri10ge_fw_unaligned;
+
+ if (myri10ge_force_firmware == 0) {
+ myri10ge_enable_ecrc(mgp);
+
+ /* Check to see if the upstream bridge is known to
+ * provide aligned completions */
+ if (bridge
+ /* ServerWorks HT2000/HT1000 */
+ && bridge->vendor == PCI_VENDOR_ID_SERVERWORKS
+ && bridge->device ==
+ PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE) {
+ dev_info(&mgp->pdev->dev,
+ "Assuming aligned completions (0x%x:0x%x)\n",
+ bridge->vendor, bridge->device);
+ mgp->tx.boundary = 4096;
+ mgp->fw_name = myri10ge_fw_aligned;
+ }
+ } else {
+ if (myri10ge_force_firmware == 1) {
+ dev_info(&mgp->pdev->dev,
+ "Assuming aligned completions (forced)\n");
+ mgp->tx.boundary = 4096;
+ mgp->fw_name = myri10ge_fw_aligned;
+ } else {
+ dev_info(&mgp->pdev->dev,
+ "Assuming unaligned completions (forced)\n");
+ mgp->tx.boundary = 2048;
+ mgp->fw_name = myri10ge_fw_unaligned;
+ }
+ }
+ if (myri10ge_fw_name != NULL) {
+ dev_info(&mgp->pdev->dev, "overriding firmware to %s\n",
+ myri10ge_fw_name);
+ mgp->fw_name = myri10ge_fw_name;
+ }
+}
+
+static void myri10ge_save_state(struct myri10ge_priv *mgp)
+{
+ struct pci_dev *pdev = mgp->pdev;
+ int cap;
+
+ pci_save_state(pdev);
+ /* now save PCIe and MSI state that Linux will not
+ * save for us */
+ cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ pci_read_config_dword(pdev, cap + PCI_EXP_DEVCTL, &mgp->devctl);
+ cap = pci_find_capability(pdev, PCI_CAP_ID_MSI);
+ pci_read_config_word(pdev, cap + PCI_MSI_FLAGS, &mgp->msi_flags);
+}
+
+static void myri10ge_restore_state(struct myri10ge_priv *mgp)
+{
+ struct pci_dev *pdev = mgp->pdev;
+ int cap;
+
+ /* restore PCIe and MSI state that linux will not */
+ cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ pci_write_config_dword(pdev, cap + PCI_CAP_ID_EXP, mgp->devctl);
+ cap = pci_find_capability(pdev, PCI_CAP_ID_MSI);
+ pci_write_config_word(pdev, cap + PCI_MSI_FLAGS, mgp->msi_flags);
+
+ pci_restore_state(pdev);
+}
+
+#ifdef CONFIG_PM
+
+static int myri10ge_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct myri10ge_priv *mgp;
+ struct net_device *netdev;
+
+ mgp = pci_get_drvdata(pdev);
+ if (mgp == NULL)
+ return -EINVAL;
+ netdev = mgp->dev;
+
+ netif_device_detach(netdev);
+ if (netif_running(netdev)) {
+ printk("myri10ge: closing %s\n", netdev->name);
+ rtnl_lock();
+ myri10ge_close(netdev);
+ rtnl_unlock();
+ }
+ myri10ge_dummy_rdma(mgp, 0);
+ free_irq(pdev->irq, mgp);
+ myri10ge_save_state(mgp);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ return 0;
+}
+
+static int myri10ge_resume(struct pci_dev *pdev)
+{
+ struct myri10ge_priv *mgp;
+ struct net_device *netdev;
+ int status;
+ u16 vendor;
+
+ mgp = pci_get_drvdata(pdev);
+ if (mgp == NULL)
+ return -EINVAL;
+ netdev = mgp->dev;
+ pci_set_power_state(pdev, 0); /* zeros conf space as a side effect */
+ udelay(5000); /* give card time to respond */
+ pci_read_config_word(mgp->pdev, PCI_VENDOR_ID, &vendor);
+ if (vendor == 0xffff) {
+ printk(KERN_ERR "myri10ge: %s: device disappeared!\n",
+ mgp->dev->name);
+ return -EIO;
+ }
+ myri10ge_restore_state(mgp);
+ pci_enable_device(pdev);
+ pci_set_master(pdev);
+
+ status = request_irq(pdev->irq, myri10ge_intr, SA_SHIRQ,
+ netdev->name, mgp);
+ if (status != 0) {
+ dev_err(&pdev->dev, "failed to allocate IRQ\n");
+ goto abort_with_msi;
+ }
+
+ myri10ge_reset(mgp);
+ myri10ge_dummy_rdma(mgp, mgp->tx.boundary != 4096);
+
+ /* Save configuration space to be restored if the
+ * nic resets due to a parity error */
+ myri10ge_save_state(mgp);
+
+ if (netif_running(netdev)) {
+ rtnl_lock();
+ myri10ge_open(netdev);
+ rtnl_unlock();
+ }
+ netif_device_attach(netdev);
+
+ return 0;
+
+ abort_with_msi:
+ return -EIO;
+
+}
+
+#endif /* CONFIG_PM */
+
+static u32 myri10ge_read_reboot(struct myri10ge_priv *mgp)
+{
+ struct pci_dev *pdev = mgp->pdev;
+ int vs = mgp->vendor_specific_offset;
+ u32 reboot;
+
+ /*enter read32 mode */
+ pci_write_config_byte(pdev, vs + 0x10, 0x3);
+
+ /*read REBOOT_STATUS (0xfffffff0) */
+ pci_write_config_dword(pdev, vs + 0x18, 0xfffffff0);
+ pci_read_config_dword(pdev, vs + 0x14, &reboot);
+ return reboot;
+}
+
+/*
+ * This watchdog is used to check whether the board has suffered
+ * from a parity error and needs to be recovered.
+ */
+static void myri10ge_watchdog(void *arg)
+{
+ struct myri10ge_priv *mgp = arg;
+ u32 reboot;
+ int status;
+ u16 cmd, vendor;
+
+ mgp->watchdog_resets++;
+ pci_read_config_word(mgp->pdev, PCI_COMMAND, &cmd);
+ if ((cmd & PCI_COMMAND_MASTER) == 0) {
+ /* Bus master DMA disabled? Check to see
+ * if the card rebooted due to a parity error
+ * For now, just report it */
+ reboot = myri10ge_read_reboot(mgp);
+ printk(KERN_ERR
+ "myri10ge: %s: NIC rebooted (0x%x), resetting\n",
+ mgp->dev->name, reboot);
+ /*
+ * A rebooted nic will come back with config space as
+ * it was after power was applied to PCIe bus.
+ * Attempt to restore config space which was saved
+ * when the driver was loaded, or the last time the
+ * nic was resumed from power saving mode.
+ */
+ myri10ge_restore_state(mgp);
+ } else {
+ /* if we get back -1's from our slot, perhaps somebody
+ * powered off our card. Don't try to reset it in
+ * this case */
+ if (cmd == 0xffff) {
+ pci_read_config_word(mgp->pdev, PCI_VENDOR_ID, &vendor);
+ if (vendor == 0xffff) {
+ printk(KERN_ERR
+ "myri10ge: %s: device disappeared!\n",
+ mgp->dev->name);
+ return;
+ }
+ }
+ /* Perhaps it is a software error. Try to reset */
+
+ printk(KERN_ERR "myri10ge: %s: device timeout, resetting\n",
+ mgp->dev->name);
+ printk("myri10ge: %s: %d %d %d %d %d\n", mgp->dev->name,
+ mgp->tx.req, mgp->tx.done, mgp->tx.pkt_start,
+ mgp->tx.pkt_done,
+ (int)ntohl(mgp->fw_stats->send_done_count));
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ * 2);
+ set_current_state(TASK_RUNNING);
+ printk("myri10ge: %s: %d %d %d %d %d\n", mgp->dev->name,
+ mgp->tx.req, mgp->tx.done, mgp->tx.pkt_start,
+ mgp->tx.pkt_done,
+ (int)ntohl(mgp->fw_stats->send_done_count));
+ }
+ rtnl_lock();
+ myri10ge_close(mgp->dev);
+ status = myri10ge_load_firmware(mgp);
+ if (status != 0) {
+ printk(KERN_ERR "myri10ge: %s: failed to load firmware\n",
+ mgp->dev->name);
+ } else {
+ myri10ge_open(mgp->dev);
+ }
+ rtnl_unlock();
+}
+
+/*
+ * We use our own timer routine rather than relying upon
+ * netdev->tx_timeout because we have a very large hardware transmit
+ * queue. Due to the large queue, the netdev->tx_timeout function
+ * cannot detect a NIC with a parity error in a timely fashion if the
+ * NIC is lightly loaded.
+ */
+static void myri10ge_watchdog_timer(unsigned long arg)
+{
+ struct myri10ge_priv *mgp;
+
+ mgp = (struct myri10ge_priv *)arg;
+ if (mgp->tx.req != mgp->tx.done &&
+ mgp->tx.done == mgp->watchdog_tx_done) {
+ /* nic seems like it might be stuck.. */
+ schedule_work(&mgp->watchdog_work);
+ } else {
+ /* rearm timer */
+ mod_timer(&mgp->watchdog_timer,
+ jiffies + myri10ge_watchdog_timeout * HZ);
+ }
+ mgp->watchdog_tx_done = mgp->tx.done;
+}
+
+static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct net_device *netdev;
+ struct myri10ge_priv *mgp;
+ struct device *dev = &pdev->dev;
+ size_t bytes;
+ int i;
+ int status = -ENXIO;
+ int cap;
+ int dac_enabled;
+ u16 val;
+
+ netdev = alloc_etherdev(sizeof(*mgp));
+ if (netdev == NULL) {
+ dev_err(dev, "Could not allocate ethernet device\n");
+ return -ENOMEM;
+ }
+
+ mgp = netdev_priv(netdev);
+ memset(mgp, 0, sizeof(*mgp));
+ mgp->dev = netdev;
+ mgp->pdev = pdev;
+ mgp->csum_flag = MYRI10GE_MCP_ETHER_FLAGS_CKSUM;
+ mgp->pause = myri10ge_flow_control;
+ mgp->intr_coal_delay = myri10ge_intr_coal_delay;
+ init_waitqueue_head(&mgp->down_wq);
+
+ if (pci_enable_device(pdev)) {
+ dev_err(&pdev->dev, "pci_enable_device call failed\n");
+ status = -ENODEV;
+ goto abort_with_netdev;
+ }
+ myri10ge_select_firmware(mgp);
+
+ /* Find the vendor-specific cap so we can check
+ * the reboot register later on */
+ mgp->vendor_specific_offset
+ = pci_find_capability(pdev, PCI_CAP_ID_VNDR);
+
+ /* Set our max read request to 4KB */
+ cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ if (cap < 64) {
+ dev_err(&pdev->dev, "Bad PCI_CAP_ID_EXP location %d\n", cap);
+ goto abort_with_netdev;
+ }
+ status = pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &val);
+ if (status != 0) {
+ dev_err(&pdev->dev, "Error %d reading PCI_EXP_DEVCTL\n",
+ status);
+ goto abort_with_netdev;
+ }
+ val = (val & ~PCI_EXP_DEVCTL_READRQ) | (5 << 12);
+ status = pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, val);
+ if (status != 0) {
+ dev_err(&pdev->dev, "Error %d writing PCI_EXP_DEVCTL\n",
+ status);
+ goto abort_with_netdev;
+ }
+
+ pci_set_master(pdev);
+ dac_enabled = 1;
+ status = pci_set_dma_mask(pdev, DMA_64BIT_MASK);
+ if (status != 0) {
+ dac_enabled = 0;
+ dev_err(&pdev->dev,
+ "64-bit pci address mask was refused, trying 32-bit");
+ status = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ }
+ if (status != 0) {
+ dev_err(&pdev->dev, "Error %d setting DMA mask\n", status);
+ goto abort_with_netdev;
+ }
+ mgp->cmd = pci_alloc_consistent(pdev, sizeof(*mgp->cmd), &mgp->cmd_bus);
+ if (mgp->cmd == NULL) {
+ goto abort_with_netdev;
+ }
+
+ mgp->fw_stats = pci_alloc_consistent(pdev, sizeof(*mgp->fw_stats),
+ &mgp->fw_stats_bus);
+ if (mgp->fw_stats == NULL) {
+ goto abort_with_cmd;
+ }
+
+ mgp->board_span = pci_resource_len(pdev, 0);
+ mgp->iomem_base = pci_resource_start(pdev, 0);
+ mgp->mtrr = -1;
+#ifdef CONFIG_MTRR
+ mgp->mtrr = mtrr_add(mgp->iomem_base, mgp->board_span,
+ MTRR_TYPE_WRCOMB, 1);
+#endif
+ /* Hack. need to get rid of these magic numbers */
+ mgp->sram_size =
+ 2 * 1024 * 1024 - (2 * (48 * 1024) + (32 * 1024)) - 0x100;
+ if (mgp->sram_size > mgp->board_span) {
+ dev_err(&pdev->dev, "board span %ld bytes too small\n",
+ mgp->board_span);
+ goto abort_with_wc;
+ }
+ mgp->sram = ioremap(mgp->iomem_base, mgp->board_span);
+ if (mgp->sram == NULL) {
+ dev_err(&pdev->dev, "ioremap failed for %ld bytes at 0x%lx\n",
+ mgp->board_span, mgp->iomem_base);
+ status = -ENXIO;
+ goto abort_with_wc;
+ }
+ memcpy_fromio(mgp->eeprom_strings,
+ mgp->sram + mgp->sram_size - MYRI10GE_EEPROM_STRINGS_SIZE,
+ MYRI10GE_EEPROM_STRINGS_SIZE);
+ memset(mgp->eeprom_strings + MYRI10GE_EEPROM_STRINGS_SIZE - 2, 0, 2);
+ status = myri10ge_read_mac_addr(mgp);
+ if (status)
+ goto abort_with_ioremap;
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ netdev->dev_addr[i] = mgp->mac_addr[i];
+ }
+ /* allocate rx done ring */
+ bytes = myri10ge_max_intr_slots * sizeof(*mgp->rx_done.entry);
+ mgp->rx_done.entry =
+ pci_alloc_consistent(pdev, bytes, &mgp->rx_done.bus);
+ if (mgp->rx_done.entry == NULL)
+ goto abort_with_ioremap;
+ memset(mgp->rx_done.entry, 0, bytes);
+
+ status = myri10ge_load_firmware(mgp);
+ if (status != 0) {
+ dev_err(&pdev->dev, "failed to load firmware\n");
+ goto abort_with_rx_done;
+ }
+
+ status = myri10ge_reset(mgp);
+ if (status != 0) {
+ dev_err(&pdev->dev, "failed reset\n");
+ goto abort_with_firmware;
+ }
+
+ if (myri10ge_msi) {
+ 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;
+ }
+ }
+
+ status = request_irq(pdev->irq, myri10ge_intr, SA_SHIRQ,
+ netdev->name, mgp);
+ if (status != 0) {
+ dev_err(&pdev->dev, "failed to allocate IRQ\n");
+ goto abort_with_firmware;
+ }
+
+ pci_set_drvdata(pdev, mgp);
+ if ((myri10ge_initial_mtu + ETH_HLEN) > MYRI10GE_MAX_ETHER_MTU)
+ myri10ge_initial_mtu = MYRI10GE_MAX_ETHER_MTU - ETH_HLEN;
+ if ((myri10ge_initial_mtu + ETH_HLEN) < 68)
+ myri10ge_initial_mtu = 68;
+ netdev->mtu = myri10ge_initial_mtu;
+ netdev->open = myri10ge_open;
+ netdev->stop = myri10ge_close;
+ netdev->hard_start_xmit = myri10ge_xmit;
+ netdev->get_stats = myri10ge_get_stats;
+ netdev->base_addr = mgp->iomem_base;
+ netdev->irq = pdev->irq;
+ netdev->change_mtu = myri10ge_change_mtu;
+ netdev->set_multicast_list = myri10ge_set_multicast_list;
+ netdev->set_mac_address = myri10ge_set_mac_address;
+ netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_TSO;
+ if (dac_enabled)
+ netdev->features |= NETIF_F_HIGHDMA;
+ netdev->poll = myri10ge_poll;
+ netdev->weight = myri10ge_napi_weight;
+
+ /* Save configuration space to be restored if the
+ * nic resets due to a parity error */
+ myri10ge_save_state(mgp);
+ /* Restore state immediately since pci_save_msi_state disables MSI */
+ myri10ge_restore_state(mgp);
+
+ /* Setup the watchdog timer */
+ setup_timer(&mgp->watchdog_timer, myri10ge_watchdog_timer,
+ (unsigned long)mgp);
+
+ SET_ETHTOOL_OPS(netdev, &myri10ge_ethtool_ops);
+ INIT_WORK(&mgp->watchdog_work, myri10ge_watchdog, mgp);
+ status = register_netdev(netdev);
+ if (status != 0) {
+ dev_err(&pdev->dev, "register_netdev failed: %d\n", status);
+ goto abort_with_irq;
+ }
+
+ printk("myri10ge: %s: %s IRQ %d, tx bndry %d, fw %s, WC %s\n",
+ netdev->name, (mgp->msi_enabled ? "MSI" : "xPIC"),
+ pdev->irq, mgp->tx.boundary, mgp->fw_name,
+ (mgp->mtrr >= 0 ? "Enabled" : "Disabled"));
+
+ return 0;
+
+ abort_with_irq:
+ free_irq(pdev->irq, mgp);
+ if (mgp->msi_enabled)
+ pci_disable_msi(pdev);
+
+ abort_with_firmware:
+ myri10ge_dummy_rdma(mgp, 0);
+
+ abort_with_rx_done:
+ bytes = myri10ge_max_intr_slots * sizeof(*mgp->rx_done.entry);
+ pci_free_consistent(pdev, bytes, mgp->rx_done.entry, mgp->rx_done.bus);
+
+ abort_with_ioremap:
+ iounmap((void __iomem *)mgp->sram);
+
+ abort_with_wc:
+#ifdef CONFIG_MTRR
+ if (mgp->mtrr >= 0)
+ mtrr_del(mgp->mtrr, mgp->iomem_base, mgp->board_span);
+#endif
+ pci_free_consistent(pdev, sizeof(*mgp->fw_stats),
+ mgp->fw_stats, mgp->fw_stats_bus);
+
+ abort_with_cmd:
+ pci_free_consistent(pdev, sizeof(*mgp->cmd), mgp->cmd, mgp->cmd_bus);
+
+ abort_with_netdev:
+
+ free_netdev(netdev);
+ return status;
+}
+
+/*
+ * myri10ge_remove
+ *
+ * Does what is necessary to shutdown one Myrinet device. Called
+ * once for each Myrinet card by the kernel when a module is
+ * unloaded.
+ */
+static void myri10ge_remove(struct pci_dev *pdev)
+{
+ struct myri10ge_priv *mgp;
+ struct net_device *netdev;
+ size_t bytes;
+
+ mgp = pci_get_drvdata(pdev);
+ if (mgp == NULL)
+ return;
+
+ flush_scheduled_work();
+ netdev = mgp->dev;
+ unregister_netdev(netdev);
+ free_irq(pdev->irq, mgp);
+ if (mgp->msi_enabled)
+ pci_disable_msi(pdev);
+
+ myri10ge_dummy_rdma(mgp, 0);
+
+ bytes = myri10ge_max_intr_slots * sizeof(*mgp->rx_done.entry);
+ pci_free_consistent(pdev, bytes, mgp->rx_done.entry, mgp->rx_done.bus);
+
+ iounmap((void __iomem *)mgp->sram);
+
+#ifdef CONFIG_MTRR
+ if (mgp->mtrr >= 0)
+ mtrr_del(mgp->mtrr, mgp->iomem_base, mgp->board_span);
+#endif
+ pci_free_consistent(pdev, sizeof(*mgp->fw_stats),
+ mgp->fw_stats, mgp->fw_stats_bus);
+
+ pci_free_consistent(pdev, sizeof(*mgp->cmd), mgp->cmd, mgp->cmd_bus);
+
+ free_netdev(netdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+#define MYRI10GE_PCI_VENDOR_MYRICOM 0x14c1
+#define MYRI10GE_PCI_DEVICE_Z8E 0x0008
+static struct pci_device_id myri10ge_pci_tbl[] = {
+ {PCI_DEVICE(MYRI10GE_PCI_VENDOR_MYRICOM, MYRI10GE_PCI_DEVICE_Z8E)},
+ {0},
+};
+
+static struct pci_driver myri10ge_driver = {
+ .name = "myri10ge",
+ .probe = myri10ge_probe,
+ .remove = myri10ge_remove,
+ .id_table = myri10ge_pci_tbl,
+#ifdef CONFIG_PM
+ .suspend = myri10ge_suspend,
+ .resume = myri10ge_resume,
+#endif
+};
+
+static __init int myri10ge_init_module(void)
+{
+ printk("%s: Version %s\n", myri10ge_driver.name, MYRI10GE_VERSION_STR);
+ return pci_register_driver(&myri10ge_driver);
+}
+
+static __exit void myri10ge_cleanup_module(void)
+{
+ pci_unregister_driver(&myri10ge_driver);
+}
+
+module_init(myri10ge_init_module);
+module_exit(myri10ge_cleanup_module);

2006-05-17 22:07:05

by Brice Goglin

[permalink] [raw]
Subject: [PATCH 4/4] myri10ge - Kconfig and Makefile

[PATCH 4/4] myri10ge - Kconfig and Makefile

Add Kconfig and Makefile support for the myri10ge driver.

Signed-off-by: Brice Goglin <[email protected]>
Signed-off-by: Andrew J. Gallatin <[email protected]>

Kconfig | 16 ++++++++++++++++
Makefile | 1 +
myri10ge/Makefile | 5 +++++
3 files changed, 22 insertions(+)

--- linux-mm/drivers/net/Kconfig.old 2006-04-10 03:44:01.000000000 -0700
+++ linux-mm/drivers/net/Kconfig 2006-04-18 03:49:11.000000000 -0700
@@ -2327,6 +2327,23 @@ config S2IO_NAPI

If in doubt, say N.

+config MYRI10GE
+ tristate "Myricom Myri-10G Ethernet support"
+ depends on PCI
+ select FW_LOADER
+ select CRC32
+ ---help---
+ This driver supports Myricom Myri-10G Dual Protocol interface in
+ Ethernet mode. If the eeprom on your board is not recent enough,
+ you will need a newer firmware image.
+ You may get this image or more information, at:
+
+ <http://www.myri.com/Myri-10G/>
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/networking/net-modules.txt>. The module
+ will be called myri10ge.
+
endmenu

source "drivers/net/tokenring/Kconfig"
--- linux-mm/drivers/net/Makefile.old 2006-04-08 04:49:53.000000000 -0700
+++ linux-mm/drivers/net/Makefile 2006-04-21 08:10:27.000000000 -0700
@@ -192,6 +192,7 @@ obj-$(CONFIG_R8169) += r8169.o
obj-$(CONFIG_AMD8111_ETH) += amd8111e.o
obj-$(CONFIG_IBMVETH) += ibmveth.o
obj-$(CONFIG_S2IO) += s2io.o
+obj-$(CONFIG_MYRI10GE) += myri10ge/
obj-$(CONFIG_SMC91X) += smc91x.o
obj-$(CONFIG_SMC911X) += smc911x.o
obj-$(CONFIG_DM9000) += dm9000.o
--- /dev/null 2006-04-21 00:45:09.064430000 -0700
+++ linux-mm/drivers/net/myri10ge/Makefile 2006-04-21 08:14:21.000000000 -0700
@@ -0,0 +1,5 @@
+#
+# Makefile for the Myricom Myri-10G ethernet driver
+#
+
+obj-$(CONFIG_MYRI10GE) += myri10ge.o

2006-05-17 22:25:52

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH 2/4] myri10ge - Driver header files

On Wed, 17 May 2006 18:04:35 -0400 Brice Goglin wrote:

> [PATCH 2/4] myri10ge - Driver header files
>
> myri10ge_mcp.h | 205 ++++++++++++++++++++++++++++++++++++++++++++++
> myri10ge_mcp_gen_header.h | 58 +++++++++++++

Please use "diffstat -p 1 -w 70" is documented in
Documentation/SubmittingPatches.

> 2 files changed, 263 insertions(+)
>
> --- /dev/null 2006-05-16 20:08:50.920483500 +0200
> +++ linux-tmp//drivers/net/myri10ge/myri10ge_mcp.h 2006-05-17 11:02:48.000000000 +0200
> @@ -0,0 +1,205 @@
> +#ifndef __MYRI10GE_MCP_H__
> +#define __MYRI10GE_MCP_H__
> +
> +#define MYRI10GE_MCP_VERSION_MAJOR 1
> +#define MYRI10GE_MCP_VERSION_MINOR 4
> +
> +/* 16 Bytes */

What is 16 bytes here?

> +struct mcp_slot {
> + u16 checksum;
> + u16 length;
> +};


---
~Randy

2006-05-17 22:36:24

by Roland Dreier

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

Still some suspicious uses of volatile here.

For example:

> +struct myri10ge_priv {
...
> + volatile u8 __iomem *sram;

as far as I can see this is always used with proper __iomem accessors,
often with casts to strip the volatile anyway. So why is volatile needed?

I would suggest an audit of all uses of volatile in the driver, since
"volatile" in drivers really should be read "there's probably a bug
here, and if not something very tricky is going on." If there are any
valid uses of volatile then a comment should explain why, so that
future reviewers don't have to try and puzzle out which of the
two possible translations of volatile is correct.

- R.

2006-05-17 23:08:29

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

Am Thursday 18 May 2006 00:06 schrieb Brice Goglin:

> +static char *myri10ge_fw_name = NULL;
> +static char *myri10ge_fw_unaligned = "myri10ge_ethp_z8e.dat";
> +static char *myri10ge_fw_aligned = "myri10ge_eth_z8e.dat";
> +static int myri10ge_ecrc_enable = 1;
> +static int myri10ge_max_intr_slots = 1024;
> +static int myri10ge_small_bytes = -1; /* -1 == auto */
> +static int myri10ge_msi = 1; /* enable msi by default */
> +static int myri10ge_intr_coal_delay = 25;
> +static int myri10ge_flow_control = 1;
> +static int myri10ge_deassert_wait = 1;
> +static int myri10ge_force_firmware = 0;
> +static int myri10ge_skb_cross_4k = 0;
> +static int myri10ge_initial_mtu = MYRI10GE_MAX_ETHER_MTU - ETH_HLEN;
> +static int myri10ge_napi_weight = 64;
> +static int myri10ge_watchdog_timeout = 1;
> +static int myri10ge_max_irq_loops = 1048576;
> +
> +module_param(myri10ge_fw_name, charp, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(myri10ge_fw_name, "Firmware image name\n");
> +module_param(myri10ge_max_intr_slots, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_max_intr_slots, "Interrupt queue slots\n");
> +module_param(myri10ge_small_bytes, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(myri10ge_small_bytes, "Threshold of small packets\n");
> +module_param(myri10ge_msi, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_msi, "Enable Message Signalled Interrupts\n");
> +module_param(myri10ge_intr_coal_delay, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_intr_coal_delay, "Interrupt coalescing
> delay\n"); +module_param(myri10ge_ecrc_enable, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_ecrc_enable, "Enable Extended CRC on PCI-E\n");
> +module_param(myri10ge_flow_control, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_flow_control, "Pause parameter\n");
> +module_param(myri10ge_deassert_wait, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(myri10ge_deassert_wait,
> + "Wait when deasserting legacy interrupts\n");
> +module_param(myri10ge_force_firmware, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_force_firmware,
> + "Force firmware to assume aligned completions\n");
> +module_param(myri10ge_skb_cross_4k, int, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(myri10ge_skb_cross_4k,
> + "Can a small skb cross a 4KB boundary?\n");
> +module_param(myri10ge_initial_mtu, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_initial_mtu, "Initial MTU\n");
> +module_param(myri10ge_napi_weight, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_napi_weight, "Set NAPI weight\n");
> +module_param(myri10ge_watchdog_timeout, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_watchdog_timeout, "Set watchdog timeout\n");
> +module_param(myri10ge_max_irq_loops, int, S_IRUGO);
> +MODULE_PARM_DESC(myri10ge_max_irq_loops,
> + "Set stuck legacy IRQ detection threshold\n");

How about writing the module_param() and MODULE_PARM_DESC() calls
directly after each declaration? That would make it clearer
that they are all parameters.

> + response->result = 0xffffffff;

0xffffffff appears throughout your code as a return value. maybe
use a named constant for it?

> + for (sleep_total = 0;
> + sleep_total < (15 * 1000) && response->result == 0xffffffff;
> + sleep_total += 10) {
> + udelay(10);
> + }

udelay does not sleep. If you want to sleep, use msleep instead.

> +
> + myri10ge_pio_copy((void __iomem *)submit, &buf, sizeof(buf));
> + for (i = 0; *confirm != 0xffffffff && i < 20; i++)
> + udelay(1000);
> + if (*confirm != 0xffffffff) {
> + dev_err(&mgp->pdev->dev, "dummy rdma %s failed\n",
> + (enable ? "enable" : "disable"));
> + }
> +}

Can you use msleep here instead of udelay?

> +static int myri10ge_load_firmware(struct myri10ge_priv *mgp)
> +{
> + volatile u32 *confirm;
> + volatile char __iomem *submit;

The __iomem variable need not be volatile.

> + myri10ge_pio_copy((void __iomem *)submit, &buf, sizeof(buf));
> + mb();
> + udelay(1000);
> + mb();

can't you use msleep(1) instead?

> +static inline void
> +myri10ge_submit_8rx(struct mcp_kreq_ether_recv __iomem * dst,
> + struct mcp_kreq_ether_recv *src)
> +{
> + u32 low;
> +
> + low = src->addr_low;
> + src->addr_low = DMA_32BIT_MASK;
> + myri10ge_pio_copy(dst, src, 8 * sizeof(*src));
> + mb();
> + src->addr_low = low;
> + *(u32 __force *) & dst->addr_low = src->addr_low;
> + mb();
> +}

The __force dereference seems fishy.

> + if (unlikely(((end >> 12) != (data >> 12)) && (data & 4095UL))) {
> + printk
> + ("myri10ge_alloc_small: small skb crossed 4KB boundary\n");

Printk level is missing.


> +static int myri10ge_open(struct net_device *dev)

This function is too long to read easily.

> + /* allocate the host shadow rings */
> +
> + bytes = 8 + (MYRI10GE_MCP_ETHER_MAX_SEND_DESC_TSO + 4)
> + * sizeof(*mgp->tx.req_list);
> + mgp->tx.req_bytes = kmalloc(bytes, GFP_KERNEL);
> + if (mgp->tx.req_bytes == NULL)
> + goto abort_with_nothing;
> + memset(mgp->tx.req_bytes, 0, bytes);
> +
> + /* ensure req_list entries are aligned to 8 bytes */
> + mgp->tx.req_list = (struct mcp_kreq_ether_send *)
> + ALIGN((unsigned long)mgp->tx.req_bytes, 8);
> +
> + bytes = rx_ring_entries * sizeof(*mgp->rx_small.shadow);
> + mgp->rx_small.shadow = kmalloc(bytes, GFP_KERNEL);
> + if (mgp->rx_small.shadow == NULL)
> + goto abort_with_tx_req_bytes;
> + memset(mgp->rx_small.shadow, 0, bytes);
> +
> + bytes = rx_ring_entries * sizeof(*mgp->rx_big.shadow);
> + mgp->rx_big.shadow = kmalloc(bytes, GFP_KERNEL);
> + if (mgp->rx_big.shadow == NULL)
> + goto abort_with_rx_small_shadow;
> + memset(mgp->rx_big.shadow, 0, bytes);
> +
> + /* allocate the host info rings */
> +
> + bytes = tx_ring_entries * sizeof(*mgp->tx.info);
> + mgp->tx.info = kmalloc(bytes, GFP_KERNEL);
> + if (mgp->tx.info == NULL)
> + goto abort_with_rx_big_shadow;
> + memset(mgp->tx.info, 0, bytes);
> +
> + bytes = rx_ring_entries * sizeof(*mgp->rx_small.info);
> + mgp->rx_small.info = kmalloc(bytes, GFP_KERNEL);
> + if (mgp->rx_small.info == NULL)
> + goto abort_with_tx_info;
> + memset(mgp->rx_small.info, 0, bytes);
> +
> + bytes = rx_ring_entries * sizeof(*mgp->rx_big.info);
> + mgp->rx_big.info = kmalloc(bytes, GFP_KERNEL);
> + if (mgp->rx_big.info == NULL)
> + goto abort_with_rx_small_info;
> + memset(mgp->rx_big.info, 0, bytes);
> +

Can you do all these allocations at once? Maybe you can even
move them into the size passed to alloc_etherdev.

If you need separate allocations, using kzalloc simplifies
your code.

> + /* re-write the last 32-bits with the valid flags */
> + src->flags = last_flags;
> + src_ints = (u32 *) src;
> + src_ints += 3;
> + dst_ints = (u32 __iomem *) dst;
> + dst_ints += 3;
> + *(u32 __force *) dst_ints = *src_ints;
> + tx->req += cnt;
> + mb();
> +}

All these casts indicate that you do something wrong here.
In particular, dereferencing an __iomem pointer should
not be done in a device driver.

> + /* Break the SKB or Fragment up into pieces which
> + * do not cross mgp->tx.boundary */
> + low = MYRI10GE_LOWPART_TO_U32(bus);
> + high_swapped = htonl(MYRI10GE_HIGHPART_TO_U32(bus));
> + while (len) {
> + u8 flags_next;
> + int cum_len_next;
> +
> + if (unlikely(count == max_segments))
> + goto abort_linearize;
> +
> + boundary = (low + tx->boundary) & ~(tx->boundary - 1);
> + seglen = boundary - low;
> + if (seglen > len)
> + seglen = len;
> + flags_next = flags & ~MYRI10GE_MCP_ETHER_FLAGS_FIRST;
> + cum_len_next = cum_len + seglen;
> +#ifdef NETIF_F_TSO
> + if (mss) { /* TSO */
> + (req - rdma_count)->rdma_count = rdma_count + 1;
> +
> + if (likely(cum_len >= 0)) { /* payload */
> + int next_is_first, chop;
> +
> + chop = (cum_len_next > mss);
> + cum_len_next = cum_len_next % mss;
> + next_is_first = (cum_len_next == 0);
> + flags |= chop *
> + MYRI10GE_MCP_ETHER_FLAGS_TSO_CHOP;
> + flags_next |= next_is_first *
> + MYRI10GE_MCP_ETHER_FLAGS_FIRST;
> + rdma_count |= -(chop | next_is_first);
> + rdma_count += chop & !next_is_first;
> + } else if (likely(cum_len_next >= 0)) { /* header ends */
> + int small;
> +
> + rdma_count = -1;
> + cum_len_next = 0;
> + seglen = -cum_len;
> + small =
> + (mss <=
> + MYRI10GE_MCP_ETHER_SEND_SMALL_SIZE);
> + flags_next =
> + MYRI10GE_MCP_ETHER_FLAGS_TSO_PLD |
> + MYRI10GE_MCP_ETHER_FLAGS_FIRST |
> + (small *
> + MYRI10GE_MCP_ETHER_FLAGS_SMALL);
> + }
> + }

100 characters per line are too much, as are six levels of intentation,
or the number of lines in this function.
You should try to split into into smaller ones.

>
> + if ((new_mtu < 68) || (ETH_HLEN + new_mtu > MYRI10GE_MAX_ETHER_MTU)) {
> + printk(KERN_ERR "myri10ge: %s: new mtu (%d) is not valid\n",
> + dev->name, new_mtu);
> + return -EINVAL;
> + }
> + printk("%s: changing mtu from %d to %d\n",
> + dev->name, dev->mtu, new_mtu);

You shouldn't use printk as a basic feedback mechanism to user space.
The return code already contains the information.
Also the printk misses a message level.

> + pci_set_power_state(pdev, 0); /* zeros conf space as a side effect */
> + udelay(5000); /* give card time to respond */
> + pci_read_config_word(mgp->pdev, PCI_VENDOR_ID, &vendor);

5000 is a long time for udelay. If you can't convert this to msleep,
use at least mdelay.

> + printk(KERN_ERR "myri10ge: %s: device timeout, resetting\n",
> + mgp->dev->name);
> + printk("myri10ge: %s: %d %d %d %d %d\n", mgp->dev->name,
> + mgp->tx.req, mgp->tx.done, mgp->tx.pkt_start,
> + mgp->tx.pkt_done,
> + (int)ntohl(mgp->fw_stats->send_done_count));
> + set_current_state(TASK_UNINTERRUPTIBLE);
> + schedule_timeout(HZ * 2);
> + set_current_state(TASK_RUNNING);
> + printk("myri10ge: %s: %d %d %d %d %d\n", mgp->dev->name,
> + mgp->tx.req, mgp->tx.done, mgp->tx.pkt_start,
> + mgp->tx.pkt_done,
> + (int)ntohl(mgp->fw_stats->send_done_count));

missing printk levels here.

instead of schedule_timeout, you probably want to use msleep().

> + if (status != 0) {
> + printk(KERN_ERR "myri10ge: %s: failed to load firmware\n",
> + mgp->dev->name);

dev_err?

> + for (i = 0; i < ETH_ALEN; i++) {
> + netdev->dev_addr[i] = mgp->mac_addr[i];
> + }

Don't need the curly braces here.

> +
> + printk("myri10ge: %s: %s IRQ %d, tx bndry %d, fw %s, WC %s\n",
> + netdev->name, (mgp->msi_enabled ? "MSI" : "xPIC"),
> + pdev->irq, mgp->tx.boundary, mgp->fw_name,
> + (mgp->mtrr >= 0 ? "Enabled" : "Disabled"));
> +

missing printk level (KERN_DEBUG?). Could probably use dev_printk.

> + abort_with_irq:
> + free_irq(pdev->irq, mgp);
> + if (mgp->msi_enabled)
> + pci_disable_msi(pdev);
> +
> + abort_with_firmware:
> + myri10ge_dummy_rdma(mgp, 0);
> +
> + abort_with_rx_done:
> + bytes = myri10ge_max_intr_slots * sizeof(*mgp->rx_done.entry);
> + pci_free_consistent(pdev, bytes, mgp->rx_done.entry, mgp->rx_done.bus);
> +
> + abort_with_ioremap:
> + iounmap((void __iomem *)mgp->sram);
> +
> + abort_with_wc:
> +#ifdef CONFIG_MTRR
> + if (mgp->mtrr >= 0)
> + mtrr_del(mgp->mtrr, mgp->iomem_base, mgp->board_span);
> +#endif
> + pci_free_consistent(pdev, sizeof(*mgp->fw_stats),
> + mgp->fw_stats, mgp->fw_stats_bus);
> +
> + abort_with_cmd:
> + pci_free_consistent(pdev, sizeof(*mgp->cmd), mgp->cmd, mgp->cmd_bus);
> +
> + abort_with_netdev:
> +
> + free_netdev(netdev);
> + return status;
> +}

Goto labels are conventionally indented all the way
to the left. Yes, lindent/indent gets this wrong.

> + iounmap((void __iomem *)mgp->sram);

unnecessary cast.

> +
> +#define MYRI10GE_PCI_VENDOR_MYRICOM 0x14c1
> +#define MYRI10GE_PCI_DEVICE_Z8E 0x0008

Shouldn't the vendor ID go to pci_ids.h?

> +
> +static __init int myri10ge_init_module(void)
> +{
> + printk("%s: Version %s\n", myri10ge_driver.name, MYRI10GE_VERSION_STR);
> + return pci_register_driver(&myri10ge_driver);
> +}
> +
> +static __exit void myri10ge_cleanup_module(void)
> +{
> + pci_unregister_driver(&myri10ge_driver);
> +}
> +
> +module_init(myri10ge_init_module);

This line should go right under the function it refers to.

Arnd <><

2006-05-18 23:36:40

by Brice Goglin

[permalink] [raw]
Subject: Re: [PATCH 2/4] myri10ge - Driver header files

Randy.Dunlap wrote:
>> [PATCH 2/4] myri10ge - Driver header files
>>
>> myri10ge_mcp.h | 205 ++++++++++++++++++++++++++++++++++++++++++++++
>> myri10ge_mcp_gen_header.h | 58 +++++++++++++
>>
>
> Please use "diffstat -p 1 -w 70" is documented in
> Documentation/SubmittingPatches.
>

Ok, will do.

>> +/* 16 Bytes */
>>
> What is 16 bytes here?
>
>> +struct mcp_slot {
>> + u16 checksum;
>> + u16 length;
>> +};
>>

Looks like I have problems to compute the size of this type.

Thanks,
Brice

2006-05-18 23:38:46

by Brice Goglin

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

Roland Dreier wrote:
> Still some suspicious uses of volatile here.
>
> For example:
>
>
>> +struct myri10ge_priv {
>>
> ...
>
>> + volatile u8 __iomem *sram;
>>
>
> as far as I can see this is always used with proper __iomem accessors,
> often with casts to strip the volatile anyway. So why is volatile needed?
>
> I would suggest an audit of all uses of volatile in the driver, since
> "volatile" in drivers really should be read "there's probably a bug
> here, and if not something very tricky is going on." If there are any
> valid uses of volatile then a comment should explain why, so that
> future reviewers don't have to try and puzzle out which of the
> two possible translations of volatile is correct.
>

You are right, we audited the code and it looks like we don't need any
volatile.

Thanks,
Brice

2006-05-18 23:56:36

by Brice Goglin

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

Arnd Bergmann wrote:
>> + for (sleep_total = 0;
>> + sleep_total < (15 * 1000) && response->result == 0xffffffff;
>> + sleep_total += 10) {
>> + udelay(10);
>> + }
>>
>
> udelay does not sleep. If you want to sleep, use msleep instead.
>

This place is actually the only one where we don't want to use msleep.
This function (myri10ge_send_cmd) might be called from various context
(spinlocked or not) and pass orders to the NIC whose processing time
depends a lot on the command. Of course, we don't have any place where a
long operation is passed from a spinlocked context :) But, we need the
tiny udelay granularity for the spinlocked case, and the long loop for
operations that are long to process in the NIC.

Concerning all the other places where you suggested to use msleep, you
were right.

> The __iomem variable need not be volatile.

As Roland pointed out, there was too many volatile in this code. We are
reworking this together with the sparse annotations.

>> + printk("myri10ge: %s: %s IRQ %d, tx bndry %d, fw %s, WC %s\n",
>> + netdev->name, (mgp->msi_enabled ? "MSI" : "xPIC"),
>> + pdev->irq, mgp->tx.boundary, mgp->fw_name,
>> + (mgp->mtrr >= 0 ? "Enabled" : "Disabled"));
>> +
>>
>
> missing printk level (KERN_DEBUG?). Could probably use dev_printk.
>

When are we supposed to call dev_printk or not in such a driver ?

>> +#define MYRI10GE_PCI_VENDOR_MYRICOM 0x14c1
>> +#define MYRI10GE_PCI_DEVICE_Z8E 0x0008
>>
>
> Shouldn't the vendor ID go to pci_ids.h?
>

That's what I thought but i was told that the fashion these days is to
keep the IDs with the driver that uses them. I'll happy to move as long
as everybody agrees :)


Thanks a lot for all your comments,
Brice

2006-05-19 01:55:06

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

Am Friday 19 May 2006 01:56 schrieb Brice Goglin:
> This place is actually the only one where we don't want to use msleep.
> This function (myri10ge_send_cmd) might be called from various context
> (spinlocked or not) and pass orders to the NIC whose processing time
> depends a lot on the command. Of course, we don't have any place where a
> long operation is passed from a spinlocked context :) But, we need the
> tiny udelay granularity for the spinlocked case, and the long loop for
> operations that are long to process in the NIC.

I don't see any spinlocks in your code and the function does not
seem to be called from the interrupt handler or the softirq either.
Maybe I'm missed something, but where is this ever called in an
atomic context?

> > missing printk level (KERN_DEBUG?). Could probably use dev_printk.
>
> When are we supposed to call dev_printk or not in such a driver ?

Whenever you have a device associated with the message, it makes
sense to use the dev_printk family of functions.

> >> +#define MYRI10GE_PCI_VENDOR_MYRICOM 0x14c1
> >> +#define MYRI10GE_PCI_DEVICE_Z8E 0x0008
> >
> > Shouldn't the vendor ID go to pci_ids.h?
>
> That's what I thought but i was told that the fashion these days is to
> keep the IDs with the driver that uses them. I'll happy to move as long
> as everybody agrees :)

My understanding is that vendor IDs should go to the common file
because they are likely to be used by multiple drivers whereas
device IDs only need to be present in the one device driver for
that particular device.

Arnd <><

2006-05-19 02:26:05

by Brice Goglin

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

Arnd Bergmann wrote:
> Am Friday 19 May 2006 01:56 schrieb Brice Goglin:
>
>> This place is actually the only one where we don't want to use msleep.
>> This function (myri10ge_send_cmd) might be called from various context
>> (spinlocked or not) and pass orders to the NIC whose processing time
>> depends a lot on the command. Of course, we don't have any place where a
>> long operation is passed from a spinlocked context :) But, we need the
>> tiny udelay granularity for the spinlocked case, and the long loop for
>> operations that are long to process in the NIC.
>>
>
> I don't see any spinlocks in your code and the function does not
> seem to be called from the interrupt handler or the softirq either.
> Maybe I'm missed something, but where is this ever called in an
> atomic context?
>

dev_mc_upload() from net/core/dev_mcast.c does

spin_lock_bh(&dev->xmit_lock);
__dev_mc_upload(dev);

which calls dev->set_multicast_list(), which is
myri10ge_set_multicast_list()

which calls myri10ge_change_promisc

which calls myri10ge_send_cmd



> Whenever you have a device associated with the message, it makes
> sense to use the dev_printk family of functions.
>

Ok, thanks.

> My understanding is that vendor IDs should go to the common file
> because they are likely to be used by multiple drivers whereas
> device IDs only need to be present in the one device driver for
> that particular device.
>

Make sense. I will change it.

Thanks again,
Brice

2006-05-19 10:00:58

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

On Friday 19 May 2006 04:25, Brice Goglin wrote:
> dev_mc_upload() from net/core/dev_mcast.c does
>
> spin_lock_bh(&dev->xmit_lock);
> __dev_mc_upload(dev);
>
> which calls dev->set_multicast_list(), which is
> myri10ge_set_multicast_list()
>
> which calls myri10ge_change_promisc
>
> which calls myri10ge_send_cmd

Hmm, if that is the only path where you call it, it may be
helpful to change myri10ge_change_promisc to call a special
atomic version of myri10ge_send_cmd then, while all others
use the regular version, which you can then convert to
do msleep as well.

Arnd <><

2006-05-19 11:09:44

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

On Friday 19 May 2006 12:00, Arnd Bergmann wrote:
> On Friday 19 May 2006 04:25, Brice Goglin wrote:
> > dev_mc_upload() from net/core/dev_mcast.c does
> >
> > spin_lock_bh(&dev->xmit_lock);
> > __dev_mc_upload(dev);
> >
> > which calls dev->set_multicast_list(), which is
> > myri10ge_set_multicast_list()
> >
> > which calls myri10ge_change_promisc
> >
> > which calls myri10ge_send_cmd
>
> Hmm, if that is the only path where you call it, it may be
> helpful to change myri10ge_change_promisc to call a special
> atomic version of myri10ge_send_cmd then, while all others
> use the regular version, which you can then convert to
> do msleep as well.

Or just drop the xmit lock while you do that. As long as you
handle races with ->start_xmit yourself it's ok

-Andi

2006-05-19 14:40:13

by Brice Goglin

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core


> dev_err?
>
[...]

> Could probably use dev_printk.
>

When the interface name is known, we prefer having the message look like
"myri10ge: eth2: something" since it might be easier to read than
""myri10ge: 00005:000e: something". The administrator usually knows the
name of the network interface better than its bus ID.

Brice

2006-05-19 15:49:55

by Brice Goglin

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

Andi Kleen wrote:
> On Friday 19 May 2006 12:00, Arnd Bergmann wrote:
>
>> On Friday 19 May 2006 04:25, Brice Goglin wrote:
>>
>>> dev_mc_upload() from net/core/dev_mcast.c does
>>>
>>> spin_lock_bh(&dev->xmit_lock);
>>> __dev_mc_upload(dev);
>>>
>>> which calls dev->set_multicast_list(), which is
>>> myri10ge_set_multicast_list()
>>>
>>> which calls myri10ge_change_promisc
>>>
>>> which calls myri10ge_send_cmd
>>>
>> Hmm, if that is the only path where you call it, it may be
>> helpful to change myri10ge_change_promisc to call a special
>> atomic version of myri10ge_send_cmd then, while all others
>> use the regular version, which you can then convert to
>> do msleep as well.
>>
>
> Or just drop the xmit lock while you do that. As long as you
> handle races with ->start_xmit yourself it's ok
>

Looks like we can do that.
Thanks,
Brice

2006-05-19 23:16:20

by Brice Goglin

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

Arnd Bergmann wrote:
>> +static int myri10ge_open(struct net_device *dev)
>>
>
> This function is too long to read easily.
>

Ok we have split it a little bit.

>> + /* allocate the host shadow rings */
>> +
>> + bytes = 8 + (MYRI10GE_MCP_ETHER_MAX_SEND_DESC_TSO + 4)
>> + * sizeof(*mgp->tx.req_list);
>> + mgp->tx.req_bytes = kmalloc(bytes, GFP_KERNEL);
>> + if (mgp->tx.req_bytes == NULL)
>> + goto abort_with_nothing;
>> + memset(mgp->tx.req_bytes, 0, bytes);
>> +
>> + /* ensure req_list entries are aligned to 8 bytes */
>> + mgp->tx.req_list = (struct mcp_kreq_ether_send *)
>> + ALIGN((unsigned long)mgp->tx.req_bytes, 8);
>> +
>> + bytes = rx_ring_entries * sizeof(*mgp->rx_small.shadow);
>> + mgp->rx_small.shadow = kmalloc(bytes, GFP_KERNEL);
>> + if (mgp->rx_small.shadow == NULL)
>> + goto abort_with_tx_req_bytes;
>> + memset(mgp->rx_small.shadow, 0, bytes);
>> +
>> + bytes = rx_ring_entries * sizeof(*mgp->rx_big.shadow);
>> + mgp->rx_big.shadow = kmalloc(bytes, GFP_KERNEL);
>> + if (mgp->rx_big.shadow == NULL)
>> + goto abort_with_rx_small_shadow;
>> + memset(mgp->rx_big.shadow, 0, bytes);
>> +
>> + /* allocate the host info rings */
>> +
>> + bytes = tx_ring_entries * sizeof(*mgp->tx.info);
>> + mgp->tx.info = kmalloc(bytes, GFP_KERNEL);
>> + if (mgp->tx.info == NULL)
>> + goto abort_with_rx_big_shadow;
>> + memset(mgp->tx.info, 0, bytes);
>> +
>> + bytes = rx_ring_entries * sizeof(*mgp->rx_small.info);
>> + mgp->rx_small.info = kmalloc(bytes, GFP_KERNEL);
>> + if (mgp->rx_small.info == NULL)
>> + goto abort_with_tx_info;
>> + memset(mgp->rx_small.info, 0, bytes);
>> +
>> + bytes = rx_ring_entries * sizeof(*mgp->rx_big.info);
>> + mgp->rx_big.info = kmalloc(bytes, GFP_KERNEL);
>> + if (mgp->rx_big.info == NULL)
>> + goto abort_with_rx_small_info;
>> + memset(mgp->rx_big.info, 0, bytes);
>> +
>>
>
> Can you do all these allocations at once? Maybe you can even
> move them into the size passed to alloc_etherdev.
>

They are different things conceptually, so we prefer to allocate
them separately.

> If you need separate allocations, using kzalloc simplifies
> your code.
>

Right, will do.

>> + /* Break the SKB or Fragment up into pieces which
>> + * do not cross mgp->tx.boundary */
>> + low = MYRI10GE_LOWPART_TO_U32(bus);
>> + high_swapped = htonl(MYRI10GE_HIGHPART_TO_U32(bus));
>> + while (len) {
>> + u8 flags_next;
>> + int cum_len_next;
>> +
>> + if (unlikely(count == max_segments))
>> + goto abort_linearize;
>> +
>> + boundary = (low + tx->boundary) & ~(tx->boundary - 1);
>> + seglen = boundary - low;
>> + if (seglen > len)
>> + seglen = len;
>> + flags_next = flags & ~MYRI10GE_MCP_ETHER_FLAGS_FIRST;
>> + cum_len_next = cum_len + seglen;
>> +#ifdef NETIF_F_TSO
>> + if (mss) { /* TSO */
>> + (req - rdma_count)->rdma_count = rdma_count + 1;
>> +
>> + if (likely(cum_len >= 0)) { /* payload */
>> + int next_is_first, chop;
>> +
>> + chop = (cum_len_next > mss);
>> + cum_len_next = cum_len_next % mss;
>> + next_is_first = (cum_len_next == 0);
>> + flags |= chop *
>> + MYRI10GE_MCP_ETHER_FLAGS_TSO_CHOP;
>> + flags_next |= next_is_first *
>> + MYRI10GE_MCP_ETHER_FLAGS_FIRST;
>> + rdma_count |= -(chop | next_is_first);
>> + rdma_count += chop & !next_is_first;
>> + } else if (likely(cum_len_next >= 0)) { /* header ends */
>> + int small;
>> +
>> + rdma_count = -1;
>> + cum_len_next = 0;
>> + seglen = -cum_len;
>> + small =
>> + (mss <=
>> + MYRI10GE_MCP_ETHER_SEND_SMALL_SIZE);
>> + flags_next =
>> + MYRI10GE_MCP_ETHER_FLAGS_TSO_PLD |
>> + MYRI10GE_MCP_ETHER_FLAGS_FIRST |
>> + (small *
>> + MYRI10GE_MCP_ETHER_FLAGS_SMALL);
>> + }
>> + }
>>
>
> 100 characters per line are too much, as are six levels of intentation,
> or the number of lines in this function.
> You should try to split into into smaller ones.
>

We have tried :) But there is no nice way to split this code. So I guess
we will have to keep it like this.

Thanks,
Brice

2006-05-20 00:01:50

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core


> We have tried :) But there is no nice way to split this code. So I guess
> we will have to keep it like this.

You could shorten the #define names a bit (s/MYRI10GE_MCP_ETHER_/MCP_/) Then everything
will fit better.

-Andi

2006-05-20 07:59:18

by Brice Goglin

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

Brice Goglin wrote:
> Andi Kleen wrote:
>
>> On Friday 19 May 2006 12:00, Arnd Bergmann wrote:
>>
>>
>>> On Friday 19 May 2006 04:25, Brice Goglin wrote:
>>>
>>>
>>>> dev_mc_upload() from net/core/dev_mcast.c does
>>>>
>>>> spin_lock_bh(&dev->xmit_lock);
>>>> __dev_mc_upload(dev);
>>>>
>>>> which calls dev->set_multicast_list(), which is
>>>> myri10ge_set_multicast_list()
>>>>
>>>> which calls myri10ge_change_promisc
>>>>
>>>> which calls myri10ge_send_cmd
>>>>
>>>>
>>> Hmm, if that is the only path where you call it, it may be
>>> helpful to change myri10ge_change_promisc to call a special
>>> atomic version of myri10ge_send_cmd then, while all others
>>> use the regular version, which you can then convert to
>>> do msleep as well.
>>>
>>>
>> Or just drop the xmit lock while you do that. As long as you
>> handle races with ->start_xmit yourself it's ok
>>
>>
>
> Looks like we can do that.
>

Forget what I said. It's not that simple. __dev_mc_upload() is also
called from igmp6_group_added() (through dev_mc_add()). In this path,
there are two spin_lock_bh that are held:

igmp6_group_added(...)
[...]
spin_lock_bh(&mc->mca_lock);
[...]
dev_mc_add(...)

dev_mv_add(..)
[...]
spin_lock_bh(&dev->xmit_lock);
[...]
__dev_mc_upload(...)

We have actually seen this path being taken by adding a
if (in_interrupt()) {
printk("bad bad bad\n");
dump_stack();
}
in our myri10ge_send_cmd(...).

We are going to add a "sleepable" parameter to myri10ge_send_cmd() so
that only set_multicast_list() will use it with our previous udelay loop.

Brice

2006-05-23 16:14:21

by Anton Blanchard

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core


Hi Brice,

Sorry this review is based on your previous submission, I just noticed
it was still sitting in my mailbox. Please ignore anything that has been
fixed in the meantime :)

> +/*
> + * Set of routunes to get a new receive buffer. Any buffer which
> + * crosses a 4KB boundary must start on a 4KB boundary due to PCIe
> + * wdma restrictions. We also try to align any smaller allocation to
> + * at least a 16 byte boundary for efficiency. We assume the linux
> + * memory allocator works by powers of 2, and will not return memory
> + * smaller than 2KB which crosses a 4KB boundary. If it does, we fall
> + * back to allocating 2x as much space as required.
> + *
> + * We intend to replace large (>4KB) skb allocations by using
> + * pages directly and building a fraglist in the near future.
> + */

You go to a lot of trouble to align things. One thing on ppc64 is that
we really want to start all DMA writes on a cacheline boundary. We
enforce that in network drivers by making NET_IP_ALIGN = 0 and having
the drivers do:

skb_reserve(skb, NET_IP_ALIGN);

It sounds like your small allocations will be only aligned to 16 bytes.

> + mgp->cmd = pci_alloc_consistent(pdev, sizeof(*mgp->cmd), &mgp->cmd_bus);

Id suggest using the dma API instead of the pci API. We have seen
machines in the field that have failed large pci_alloc_consistent calls
because it always asks for GFP_ATOMIC memory (it presumes the worst).
The dma API allows you to pass a GFP_ flag in which will have a much
better chance of succeeding when you dont need GFP_ATOMIC memory.

> +#ifdef CONFIG_MTRR
> + mgp->mtrr = mtrr_add(mgp->iomem_base, mgp->board_span,
> + MTRR_TYPE_WRCOMB, 1);
> +#endif
...
> + mgp->sram = ioremap(mgp->iomem_base, mgp->board_span);

Not sure how we are meant to specify write through in drivers. Any ideas Ben?

Anton

2006-05-24 08:05:08

by Brice Goglin

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

Anton Blanchard wrote:
>> +/*
>> + * Set of routunes to get a new receive buffer. Any buffer which
>> + * crosses a 4KB boundary must start on a 4KB boundary due to PCIe
>> + * wdma restrictions. We also try to align any smaller allocation to
>> + * at least a 16 byte boundary for efficiency. We assume the linux
>> + * memory allocator works by powers of 2, and will not return memory
>> + * smaller than 2KB which crosses a 4KB boundary. If it does, we fall
>> + * back to allocating 2x as much space as required.
>> + *
>> + * We intend to replace large (>4KB) skb allocations by using
>> + * pages directly and building a fraglist in the near future.
>> + */
>>
>
> You go to a lot of trouble to align things. One thing on ppc64 is that
> we really want to start all DMA writes on a cacheline boundary. We
> enforce that in network drivers by making NET_IP_ALIGN = 0 and having
> the drivers do:
>
> skb_reserve(skb, NET_IP_ALIGN);
>
> It sounds like your small allocations will be only aligned to 16 bytes.
>

We didn't get any ppc64 with PCI-E to run Linux so far. What performance
drop should we expect with our current code ?

> Id suggest using the dma API instead of the pci API. We have seen
> machines in the field that have failed large pci_alloc_consistent calls
> because it always asks for GFP_ATOMIC memory (it presumes the worst).
> The dma API allows you to pass a GFP_ flag in which will have a much
> better chance of succeeding when you dont need GFP_ATOMIC memory.
>

Good idea, thanks,

>> +#ifdef CONFIG_MTRR
>> + mgp->mtrr = mtrr_add(mgp->iomem_base, mgp->board_span,
>> + MTRR_TYPE_WRCOMB, 1);
>> +#endif
>>
> ...
>
>> + mgp->sram = ioremap(mgp->iomem_base, mgp->board_span);
>>
>
> Not sure how we are meant to specify write through in drivers. Any ideas Ben?
>

I am not sure what you mean.
The only ppc64 with PCI-E that we have seen so far (a G5) couldn't do
write combining according to Apple.

Thanks,
Brice

2006-05-24 21:24:22

by Anton Blanchard

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core


Hi,

> We didn't get any ppc64 with PCI-E to run Linux so far. What performance
> drop should we expect with our current code ?

We have seen > 20% improvement on ppc64 running some networking
workloads when forcing 128 byte alignment (instead of 16 byte
alignment). DMA writes have to get cacheline aligned (in power of 2
steps) on some IO chips.

> I am not sure what you mean.
> The only ppc64 with PCI-E that we have seen so far (a G5) couldn't do
> write combining according to Apple.

Im thinking more generally, MTRRs are x86 specific and it would be good
to have a more generic way to enable write combining.

Anton

2006-05-25 07:57:20

by Benjamin Herrenschmidt

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

On Wed, 2006-05-24 at 01:39 +1000, Anton Blanchard wrote:

> > +#ifdef CONFIG_MTRR
> > + mgp->mtrr = mtrr_add(mgp->iomem_base, mgp->board_span,
> > + MTRR_TYPE_WRCOMB, 1);
> > +#endif
> ...
> > + mgp->sram = ioremap(mgp->iomem_base, mgp->board_span);
>
> Not sure how we are meant to specify write through in drivers. Any ideas Ben?

No proper interface exposed, he'll have to do an #ifdef powerpc here or
such and use __ioremap with explicit page attributes. I have a hack to
do that automatically for memory covered by prefetchable PCI BARs when
mmap'ing from userland but not for kernel ioremap.

Ben.

2006-05-25 07:59:35

by Benjamin Herrenschmidt

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

On Wed, 2006-05-24 at 10:04 +0200, Brice Goglin wrote:

> I am not sure what you mean.
> The only ppc64 with PCI-E that we have seen so far (a G5) couldn't do
> write combining according to Apple.

That is not 100% true.... I don't know what apple had in mind. It also
depends in what slot you are.

Do you have ways to measure the difference ?

Try doing __ioremap(mgp->iomem_base, mgp->board_span, _PAGE_NO_CACHE);
instead of using the normal ioremap for #ifdef powerpc and tell us if it
makes a difference.

Ben.

2006-05-25 09:08:00

by Brice Goglin

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

Benjamin Herrenschmidt wrote:
> On Wed, 2006-05-24 at 10:04 +0200, Brice Goglin wrote:
>
>
>> I am not sure what you mean.
>> The only ppc64 with PCI-E that we have seen so far (a G5) couldn't do
>> write combining according to Apple.
>>
>
> That is not 100% true.... I don't know what apple had in mind. It also
> depends in what slot you are.
>
> Do you have ways to measure the difference ?
>

No, we don't have any PPC with PCIe running Linux. The only G5 PCIe that
we have is running MacOSX.

> Try doing __ioremap(mgp->iomem_base, mgp->board_span, _PAGE_NO_CACHE);
> instead of using the normal ioremap for #ifdef powerpc and tell us if it
> makes a difference.
>

I'll try it as soon as we get our G5 PCIe to run Linux.

thanks,
Brice

2006-05-26 09:49:43

by Ingo Oeser

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

Hi there,

Benjamin Herrenschmidt wrote:
> On Wed, 2006-05-24 at 01:39 +1000, Anton Blanchard wrote:
>
> > > +#ifdef CONFIG_MTRR
> > > + mgp->mtrr = mtrr_add(mgp->iomem_base, mgp->board_span,
> > > + MTRR_TYPE_WRCOMB, 1);
> > > +#endif
> > ...
> > > + mgp->sram = ioremap(mgp->iomem_base, mgp->board_span);
> >
> > Not sure how we are meant to specify write through in drivers. Any ideas Ben?
>
> No proper interface exposed, he'll have to do an #ifdef powerpc here or
> such and use __ioremap with explicit page attributes. I have a hack to
> do that automatically for memory covered by prefetchable PCI BARs when
> mmap'ing from userland but not for kernel ioremap.

Stupid question: pci_iomap() is NOT what you are looking for, right?

Implementation is at the end of lib/iomap.c


Regards

Ingo Oeser

2006-05-26 10:06:14

by Benjamin Herrenschmidt

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core


> > No proper interface exposed, he'll have to do an #ifdef powerpc here or
> > such and use __ioremap with explicit page attributes. I have a hack to
> > do that automatically for memory covered by prefetchable PCI BARs when
> > mmap'ing from userland but not for kernel ioremap.
>
> Stupid question: pci_iomap() is NOT what you are looking for, right?
>
> Implementation is at the end of lib/iomap.c

No, there is no difference there, pci_iomap won't help for passing in
platform specific page attributes for things like write combining.

Ben.


2006-05-26 10:31:21

by Jeff Garzik

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

Benjamin Herrenschmidt wrote:
>>> No proper interface exposed, he'll have to do an #ifdef powerpc here or
>>> such and use __ioremap with explicit page attributes. I have a hack to
>>> do that automatically for memory covered by prefetchable PCI BARs when
>>> mmap'ing from userland but not for kernel ioremap.
>> Stupid question: pci_iomap() is NOT what you are looking for, right?
>>
>> Implementation is at the end of lib/iomap.c
>
> No, there is no difference there, pci_iomap won't help for passing in
> platform specific page attributes for things like write combining.

Since we already have ioremap_nocache(), maybe adding ioremap_wcomb() is
appropriate?

Jeff


2006-05-26 10:56:26

by Benjamin Herrenschmidt

[permalink] [raw]
Subject: Re: [PATCH 3/4] myri10ge - Driver core

On Fri, 2006-05-26 at 06:30 -0400, Jeff Garzik wrote:
> Benjamin Herrenschmidt wrote:
> >>> No proper interface exposed, he'll have to do an #ifdef powerpc here or
> >>> such and use __ioremap with explicit page attributes. I have a hack to
> >>> do that automatically for memory covered by prefetchable PCI BARs when
> >>> mmap'ing from userland but not for kernel ioremap.
> >> Stupid question: pci_iomap() is NOT what you are looking for, right?
> >>
> >> Implementation is at the end of lib/iomap.c
> >
> > No, there is no difference there, pci_iomap won't help for passing in
> > platform specific page attributes for things like write combining.
>
> Since we already have ioremap_nocache(), maybe adding ioremap_wcomb() is
> appropriate?

Yes, but be careful there.. on ppc, not setting the page "guarded" bit
will enable write combining on various CPUs but will also enable
speculative reads, prefetch, etc... so it's not "just" WC, you have to
know for sure what you are doing with that memory (basically, enable
side-effects).

Ben.