lkml.org 
[lkml]   [2006]   [Mar]   [9]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Subject[PATCH 20 of 20] ipath - ethernet emulation driver
    Date
    From
    The ethernet emulation driver makes an eth* interface available.  It
    uses Infiniband UD packets, but is not IPoIB compatible. It provides
    higher bandwidth and lower latency than IPoIB.

    The driver is implemented using the ipath_layer code, as is the ipath
    driver's OpenIB support.

    Signed-off-by: Bryan O'Sullivan <bos@pathscale.com>

    diff -r d5a8cb977923 -r 7f00f404094f drivers/infiniband/hw/ipath/Kconfig
    --- a/drivers/infiniband/hw/ipath/Kconfig Thu Mar 9 16:17:14 2006 -0800
    +++ b/drivers/infiniband/hw/ipath/Kconfig Thu Mar 9 16:17:14 2006 -0800
    @@ -16,3 +16,10 @@ config INFINIBAND_IPATH
    allows these devices to be used with both kernel upper level
    protocols such as IP-over-InfiniBand as well as with userspace
    applications (in conjunction with InfiniBand userspace access).
    +
    +config IPATH_ETHER
    + tristate "PathScale InfiniPath ethernet driver"
    + depends on IPATH_CORE
    + ---help---
    + This is an ethernet emulator layer for the PathScale InfiniPath
    + host channel adapters (HCAs).
    diff -r d5a8cb977923 -r 7f00f404094f drivers/infiniband/hw/ipath/ipath_eth.c
    --- /dev/null Thu Jan 1 00:00:00 1970 +0000
    +++ b/drivers/infiniband/hw/ipath/ipath_eth.c Thu Mar 9 16:17:14 2006 -0800
    @@ -0,0 +1,1187 @@
    +/*
    + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
    + *
    + * This program is free software; you can redistribute it and/or modify it
    + * under the terms of version 2 of the GNU General Public License as
    + * published by the Free Software Foundation.
    + *
    + * This program is distributed in the hope that it would be useful, but
    + * WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    + *
    + * Further, this software is distributed without any warranty that it is
    + * free of the rightful claim of any third person regarding infringement
    + * or the like. Any license provided herein, whether implied or
    + * otherwise, applies only to this software file.
    + *
    + * You should have received a copy of the GNU General Public License along
    + * with this program; if not, write the Free Software Foundation, Inc., 59
    + * Temple Place - Suite 330, Boston MA 02111-1307, USA.
    + */
    +
    +/*
    + * ipath_ether.c ethernet driver emulation over PathScale Infinipath
    + * for Linux.
    + */
    +
    +#define ipath_ether_ioctl_support
    +
    +#include <linux/netdevice.h>
    +#include <linux/etherdevice.h>
    +#include <linux/ethtool.h>
    +
    +#include "ipath_debug.h"
    +#include "ips_common.h"
    +#include "ipath_layer.h"
    +
    +/* Not static, because we don't want the compiler removing it */
    +#define DRV_NAME "ipath_ether"
    +const char ipath_ether_version[] = DRV_NAME " " IPATH_IDSTR;
    +#define DRV_VERSION "1.0"
    +
    +#if _IPATH_DEBUGGING
    +
    +#define __IPATH_DBG_WHICH(which,fmt,...) \
    + do { \
    + if (unlikely(ipath_debug&(which))) \
    + printk(KERN_DEBUG DRV_NAME ": %s: " fmt, \
    + __func__,##__VA_ARGS__); \
    + } while (0)
    +
    +#define ipath_eth_dbg(fmt,...) \
    + __IPATH_DBG_WHICH(__IPATH_IPATHDBG,fmt,##__VA_ARGS__)
    +#define ipath_eth_cdbg(which,fmt,...) \
    + __IPATH_DBG_WHICH(__IPATH_##which##DBG,fmt,##__VA_ARGS__)
    +#define ipath_eth_warn(fmt,...) \
    + __IPATH_DBG_WHICH(__IPATH_IPATHWARN,fmt,##__VA_ARGS__)
    +#define ipath_eth_err(fmt,...) \
    + __IPATH_DBG_WHICH(__IPATH_IPATHERR ,fmt,##__VA_ARGS__)
    +#define ipath_eth_table(fmt,...) \
    + __IPATH_DBG_WHICH(__IPATH_IPATHTABLE ,fmt,##__VA_ARGS__)
    +
    +#else
    +
    +#define ipath_eth_dbg(fmt,...)
    +#define ipath_eth_warn(fmt,...)
    +#define ipath_eth_err(fmt,...)
    +#define ipath_eth_table(fmt,...)
    +
    +#endif
    +
    +#define MAX_IPATH_LAYER_DEVICE 4
    +#define ETHER_MAC_SIZE 6
    +
    +#define TX_TIMEOUT 2000
    +
    +#define BROADCAST_MASK 0x0001
    +
    +#define IPATH_LAYER_DOWN 0
    +#define IPATH_LAYER_UP 1
    +
    +#define MAC_LENGTH 6
    +
    +#define MAX_HASH_ENTRIES 4129
    +
    +#define LID_ARP_REQUEST 1
    +#define LID_ARP_RESPONSE 2
    +
    +#define ETH_ARP_PROTOCOL 0x0806 /* ARP protocol ID */
    +
    +#define HASH_ALLOC_ENTRIES 256
    +
    +#define priv_data(dev) ((struct ipath_ether_priv *)(dev)->priv)
    +
    +#define make_hash_key(mac) ((mac[0] + mac[1] + mac[2]) % MAX_HASH_ENTRIES)
    +
    +/* This structure is used to reassemble packets for large MTUs. */
    +struct ipath_frag_state {
    + spinlock_t lock;
    + struct sk_buff *skb;
    + struct sk_buff *last_skb;
    + uint16_t lid;
    + uint8_t frag_num; /* ips_message_header.unused */
    + uint8_t seq_num; /* ips_message_header.tinylen */
    + uint32_t len; /* ips_message_header.ack_seq_num */
    +};
    +
    +struct ipath_ether_priv {
    + struct ipath_devdata *dd;
    + int device_id;
    + uint16_t my_lid; /* set in network order */
    + uint16_t my_bcast; /* set in network order */
    + uint16_t my_mac_addr[3];
    + int ipath_ether_if_stat;
    + struct net_device_stats ipath_ether_stats;
    + wait_queue_head_t lid_wait; /* when waiting for LID at open */
    + struct copy_data_s cpc;
    + struct ipath_frag_state *fstate; /* Fragment reassembly table */
    + struct ether_header protocol_header;
    +};
    +
    +struct ether_hash { /* _ips_message_header */
    + struct ether_hash *next;
    + uint16_t mac[3];
    + uint16_t lid;
    +};
    +
    +static struct net_device *dev_ipath_ether[MAX_IPATH_LAYER_DEVICE];
    +static struct ipath_ether_priv private_data[MAX_IPATH_LAYER_DEVICE];
    +static int number_of_devices;
    +
    +static atomic_t send_continue;
    +
    +/*
    + * this will have to change to be per-device when we support
    + * multiple infinipath devices that aren't all on the saem fabric
    + */
    +static struct ether_hash hash_table[MAX_HASH_ENTRIES];
    +static struct ether_hash *all_hash_entries;
    +static struct ether_hash *free_hash_entries;
    +static DEFINE_SPINLOCK(ipath_ether_lock);
    +
    +MODULE_LICENSE("GPL");
    +MODULE_AUTHOR("PathScale <support@pathscale.com>");
    +MODULE_DESCRIPTION("Pathscale InfiniPath ethernet driver");
    +
    +static struct net_device_stats *ipath_ether_get_stats(
    + struct net_device *dev);
    +int ipath_ether_init(void);
    +void ipath_ether_exit(void);
    +
    +module_init(ipath_ether_init);
    +module_exit(ipath_ether_exit);
    +
    +static unsigned int ipath_fragtable_size = 1033;
    +module_param_named(fragtable_size, ipath_fragtable_size, uint, S_IRUGO);
    +MODULE_PARM_DESC(fragtable_size,
    + "size of the fragment reassembly hash table (prime)");
    +
    +static int _send_lid_message(uint16_t * mac,
    + uint8_t cmd,
    + uint16_t dest_lid, struct net_device *dev)
    +{
    + struct ether_header protocol_header;
    + uint32_t total_frame_size_in_words = sizeof(protocol_header) >> 2;
    +
    + protocol_header.lrh[0] = htons(IPS_LRH_BTH);
    + protocol_header.lrh[1] = dest_lid; /* DEST LID */
    + protocol_header.lrh[2] =
    + htons(total_frame_size_in_words + SIZE_OF_CRC);
    + protocol_header.lrh[3] =
    + priv_data(dev)->my_lid; /* SRC LID */
    +
    + protocol_header.bth[0] =
    + htonl((OPCODE_ITH4X << 24) + IPS_DEFAULT_P_KEY);
    + protocol_header.sub_opcode = OPCODE_LID_ARP;
    +
    + protocol_header.bth[1] = htonl(IPATH_KD_QP);
    + protocol_header.bth[2] = 0;
    +
    + /* port, version, and TID are already known to be in range, no
    + * masking needed; offset in low INFINIPATH_I_OFFSET_MASK bits */
    + protocol_header.iph.ver_port_tid_offset =
    + (IPS_PROTO_VERSION << INFINIPATH_I_VERS_SHIFT) +
    + (EAGER_TID_ID << INFINIPATH_I_TID_SHIFT);
    + /* port is zero */
    + /* offset is zero */
    +
    + /* generate an interrupt on the receive side */
    + protocol_header.iph.pkt_flags = INFINIPATH_KPF_INTR;
    +
    + protocol_header.iph.chksum =
    + (uint16_t) IPS_LRH_BTH +
    + (uint16_t) (total_frame_size_in_words + SIZE_OF_CRC) -
    + (uint16_t) ((protocol_header.iph.
    + ver_port_tid_offset >> 16) & 0xFFFF) -
    + (uint16_t) (protocol_header.iph.ver_port_tid_offset &
    + 0xFFFF) -
    + (uint16_t) protocol_header.iph.pkt_flags;
    +
    + protocol_header.cmd = cmd;
    + protocol_header.lid =
    + (cmd == LID_ARP_RESPONSE) ? priv_data(dev)->my_lid : 0;
    + protocol_header.mac[0] = mac[0];
    + protocol_header.mac[1] = mac[1];
    + protocol_header.mac[2] = mac[2];
    +
    + return ipath_layer_send_hdr(priv_data(dev)->dd, &protocol_header);
    +}
    +
    +/**
    + * _add_mac_lid - add a MAC LID
    + * @mac: the MAC
    + * @lid: the LID
    + @
    + * XXX problem? can grow to unbounded in size.
    + * NOTE: this should only be called from interrupt context.
    + */
    +static int _add_mac_lid(uint16_t * mac, uint16_t lid)
    +{
    + uint16_t hashkey = make_hash_key(mac);
    + struct ether_hash *hash_entry = &hash_table[hashkey];
    + struct ether_hash *last_entry = NULL;
    + struct ether_hash *index;
    + int counter;
    + int rc = 0;
    +
    + /* spin_lock_irq(&ipath_ether_lock); */
    +
    + if (!hash_entry->lid) {
    + memcpy(hash_entry->mac, mac, MAC_LENGTH);
    + hash_entry->lid = lid;
    + hash_entry->next = NULL;
    +
    + goto _add_mac_lid_complete;
    + }
    +
    + while (hash_entry) {
    + if ((hash_entry->mac[0] == mac[0]) &&
    + (hash_entry->mac[1] == mac[1]) &&
    + (hash_entry->mac[2] == mac[2])
    + ) {
    + hash_entry->lid = lid;
    + goto _add_mac_lid_complete;
    + }
    +
    + last_entry = hash_entry;
    + hash_entry = hash_entry->next;
    + }
    +
    + /* MAC address was not found - so add it! */
    + if (!free_hash_entries) {
    + index = kmalloc(HASH_ALLOC_ENTRIES *
    + sizeof(struct ether_hash), GFP_ATOMIC);
    +
    + if (!index) {
    + rc = -1;
    + goto _add_mac_lid_complete;
    + }
    +
    + /*
    + * The first entry is used to keep a list of all the
    + * entries.
    + */
    + index->next = all_hash_entries;
    + all_hash_entries = index;
    + free_hash_entries = ++index;
    +
    + for (counter = 2; counter < HASH_ALLOC_ENTRIES; counter++) {
    + index->next = index + 1;
    + index++;
    + }
    +
    + index->next = NULL;
    + }
    +
    + hash_entry = free_hash_entries;
    + free_hash_entries = free_hash_entries->next;
    +
    + /* Initialize the new entry before linking into the list. */
    + memcpy(hash_entry->mac, mac, MAC_LENGTH);
    + hash_entry->lid = lid;
    + hash_entry->next = NULL;
    +
    + last_entry->next = hash_entry;
    +
    +_add_mac_lid_complete:
    + /* spin_unlock_irq(&ipath_ether_lock); */
    + return rc;
    +}
    +
    +/**
    + * _lookup_lid - look up the LID for a MAC
    + * @mac: the MAC
    + * @dev: the network device
    + *
    + * LID '0' (zero) is returned when lookup failed.
    + * Since only one CPU can update the list at a time, we are
    + * careful to initialize the entry before linking into the list,
    + * and its a single linked list, the readers can safely walk the list
    + * without holding the lock.
    + */
    +static uint16_t _lookup_lid(uint16_t * mac, struct net_device *dev)
    +{
    + uint16_t hashkey = make_hash_key(mac);
    + struct ether_hash *hash_entry = &hash_table[hashkey];
    +
    + while (hash_entry) {
    + if ((hash_entry->mac[0] == mac[0]) &&
    + (hash_entry->mac[1] == mac[1]) &&
    + (hash_entry->mac[2] == mac[2])
    + )
    + break;
    +
    + hash_entry = hash_entry->next;
    + }
    +
    + if (hash_entry) {
    + return hash_entry->lid;
    + } else {
    + _send_lid_message(mac, LID_ARP_REQUEST,
    + priv_data(dev)->my_bcast, dev);
    +
    + return 0;
    + }
    +}
    +
    +static int ipath_ether_start_xmit(struct sk_buff *skb,
    + struct net_device *dev)
    +{
    + struct ether_header *protocol_header =
    + &priv_data(dev)->protocol_header;
    + int rc = 0;
    + uint16_t dest_lid;
    + static uint32_t jumboseq;
    +
    + if (skb->data[0] & BROADCAST_MASK) {
    +
    + ipath_eth_dbg("Dest MAC: %x:%x:%x:%x:%x:%x\n",
    + ((uint8_t *)skb->data)[0],
    + ((uint8_t *)skb->data)[1],
    + ((uint8_t *)skb->data)[2],
    + ((uint8_t *)skb->data)[3],
    + ((uint8_t *)skb->data)[4],
    + ((uint8_t *)skb->data)[5]);
    +
    + dest_lid = priv_data(dev)->my_bcast;
    + if (!dest_lid) {
    + /*
    + * Can't broadcast, broadcast LID isn't set yet.
    + * not the best possible error, but reasonable
    + */
    + dev_kfree_skb(skb);
    + priv_data(dev)->ipath_ether_stats.tx_dropped++;
    + /* just return 0. Old return was -ENOBUFS; */
    + return 0;
    + }
    + } else {
    + dest_lid = _lookup_lid((uint16_t *) skb->data, dev);
    + if (!dest_lid) {
    + dev_kfree_skb(skb);
    + priv_data(dev)->ipath_ether_stats.tx_dropped++;
    + /* just return 0. Old return was -ENOBUFS; */
    + return 0;
    + }
    + }
    +
    + if (ipath_debug & __IPATH_IPATHPD) {
    + int loop_count;
    +
    + printk("Send:\n");
    + for (loop_count = 0; loop_count < skb->len;
    + loop_count++) {
    + if (!(loop_count % 16))
    + printk("\n");
    +
    + printk("%02X ",
    + ((uint8_t *) skb->data)[loop_count]);
    + }
    +
    + printk("\n\n");
    + }
    +
    + /* This is used as the sequence ID of a jumbo packet. */
    + protocol_header->seq_num = ++jumboseq;
    + /* This is the total length the receiver should expect. */
    + protocol_header->len = skb->len;
    + /*
    + * This is used as the fragment number for segmented jumbo packets.
    + */
    + protocol_header->frag_num = 0;
    +
    + /*
    + * Copy 2 bytes of the ethernet header into the infinipath header so
    + * the rest of the data is 32-bit aligned.
    + */
    + protocol_header->first_2_bytes = *((uint16_t *) skb->data);
    + skb_pull(skb, sizeof(uint16_t));
    +
    + protocol_header->lrh[0] = htons(IPS_LRH_BTH);
    + protocol_header->lrh[1] = dest_lid;
    + protocol_header->lrh[3] =
    + priv_data(dev)->my_lid; /* SRC LID */
    +
    + protocol_header->bth[0] =
    + htonl((OPCODE_ITH4X << 24) + IPS_DEFAULT_P_KEY);
    + protocol_header->sub_opcode = OPCODE_ENCAP;
    +
    + protocol_header->bth[1] = htonl(IPATH_KD_QP);
    +
    + /* port, version, and TID are already known to be in range, no
    + * masking needed; offset in low INFINIPATH_I_OFFSET_MASK bits */
    + protocol_header->iph.ver_port_tid_offset =
    + (IPS_PROTO_VERSION << INFINIPATH_I_VERS_SHIFT) +
    + (EAGER_TID_ID << INFINIPATH_I_TID_SHIFT);
    + /* port is zero */
    + /* offset is zero */
    +
    + protocol_header->flags = NETIF_F_SG;
    + if ((dev->features & NETIF_F_HW_CSUM) &&
    + skb->ip_summed == CHECKSUM_HW)
    + protocol_header->flags |= NETIF_F_HW_CSUM;
    +
    + /* init cpc */
    + priv_data(dev)->cpc.hdr = protocol_header;
    + priv_data(dev)->cpc.to = NULL;
    + priv_data(dev)->cpc.error = 0;
    + priv_data(dev)->cpc.extra = 0;
    + priv_data(dev)->cpc.len = skb->len;
    + priv_data(dev)->cpc.flen = 0;
    + priv_data(dev)->cpc.skb = skb;
    + priv_data(dev)->cpc.csum = 0;
    + priv_data(dev)->cpc.pos = 0;
    + priv_data(dev)->cpc.offset = 0;
    + priv_data(dev)->cpc.checksum_calc = 0;
    +
    + rc = ipath_layer_send_skb(priv_data(dev)->dd, &priv_data(dev)->cpc);
    + if (rc == 0) {
    + priv_data(dev)->ipath_ether_stats.tx_packets++;
    + priv_data(dev)->ipath_ether_stats.tx_bytes += skb->len;
    +
    + priv_data(dev)->cpc.skb = NULL;
    + dev_kfree_skb(skb);
    + } else {
    + netif_stop_queue(dev);
    +
    + if (rc == -ENOBUFS)
    + priv_data(dev)->ipath_ether_stats.tx_fifo_errors++;
    +
    + if (rc == -EBUSY) {
    + atomic_set(&send_continue, 1);
    + ipath_layer_set_piointbufavail_int(
    + priv_data(dev)->dd);
    + rc = 0;
    + }
    + }
    +
    + return rc;
    +}
    +
    +/**
    + * ipath_ether_process_lid_arp - process an ARP message for a LID
    + * @device: the network device
    + * @hdr: the message header
    + */
    +static int ipath_ether_process_lid_arp(int device, void *hdr)
    +{
    + struct ipath_ether_priv *priv = &private_data[device];
    + struct ether_header *ihdr = (struct ether_header *) hdr;
    +
    + switch (ihdr->cmd) {
    + case LID_ARP_REQUEST:
    + if ((priv->my_mac_addr[0] == ihdr->mac[0]) &&
    + (priv->my_mac_addr[1] == ihdr->mac[1]) &&
    + (priv->my_mac_addr[2] == ihdr->mac[2])) {
    + _send_lid_message(priv->my_mac_addr,
    + LID_ARP_RESPONSE,
    + ihdr->lrh[3],
    + dev_ipath_ether[device]);
    + }
    + break;
    +
    + case LID_ARP_RESPONSE:
    + spin_lock_irq(&ipath_ether_lock);
    + _add_mac_lid(ihdr->mac, ihdr->lid);
    + spin_unlock_irq(&ipath_ether_lock);
    + break;
    + }
    +
    + return 0;
    +}
    +
    +/**
    + * get_state - get fragment state
    + * @table: the fragment state table
    + * @lid: the LID
    + *
    + * The purpose of the fragment reassembly hash table is to reduce the
    + * probability of losing state due to hash collisions.
    + * In order to add the least amount of overhead, there is no locking used
    + * on the lookup and a LID hashes to only one entry.
    + * Locking would be required if we tried to support removal of entries or
    + * dynamically growing the hash table.
    + */
    +static inline struct ipath_frag_state *get_state(
    + struct ipath_frag_state *table, uint16_t lid)
    +{
    + unsigned int probe = lid % ipath_fragtable_size;
    + struct ipath_frag_state *entry = table + probe;
    +
    + return entry;
    +}
    +
    +/**
    + * ipath_ether_rx - receive an ethernet packet
    + * @device: the network device
    + * @hdr: the packet header
    + * @skb: the sk_buff
    + *
    + * Callback handler called by the lower layer.
    + * Note that the skb is now our responsibility to either pass to the
    + * network stack or free it.
    + */
    +static int ipath_ether_rx(int device, void *hdr, struct sk_buff *skb)
    +{
    + struct ether_header *ihdr = (struct ether_header *) hdr;
    + struct ipath_ether_priv *priv = &private_data[device];
    + struct ipath_frag_state *fs = get_state(priv->fstate, ihdr->lrh[3]);
    + struct sk_buff *lskb;
    + uint16_t *h;
    +
    + spin_lock_irq(&fs->lock);
    + lskb = fs->skb;
    + if (lskb != NULL) {
    + if (fs->lid != ihdr->lrh[3] ||
    + fs->seq_num != ihdr->seq_num ||
    + fs->frag_num != ihdr->frag_num) {
    +
    + ipath_eth_warn("Drop %x %x, %u %u, %u %u, "
    + "%u %u, %x\n", fs->lid,
    + ihdr->lrh[3], fs->seq_num,
    + ihdr->seq_num, fs->frag_num,
    + ihdr->frag_num, fs->len,
    + ihdr->len,
    + ihdr->flags); /* XXX */
    +
    + dev_kfree_skb_irq(lskb);
    + fs->skb = NULL;
    + if (fs->lid != ihdr->lrh[3])
    + priv->ipath_ether_stats.collisions++;
    + else
    + priv->ipath_ether_stats.rx_dropped++;
    + goto restart;
    + }
    + fs->frag_num++;
    + /*
    + * Linux network stack expects the last buff's next pointer
    + * to be NULL.
    + */
    + if (skb_shinfo(lskb)->frag_list == NULL)
    + skb_shinfo(lskb)->frag_list = skb;
    + else
    + fs->last_skb->next = skb;
    + fs->last_skb = skb;
    + lskb->len += skb->len;
    + lskb->data_len += skb->len;
    + } else {
    + restart:
    + /* Check to be sure this is the first fragment. */
    + if (ihdr->frag_num != 0) {
    + spin_unlock_irq(&fs->lock);
    + dev_kfree_skb_irq(skb);
    + priv->ipath_ether_stats.rx_dropped++;
    + return 0;
    + }
    + skb->dev = dev_ipath_ether[device];
    + skb->ip_summed = (ihdr->flags & NETIF_F_NO_CSUM) ?
    + CHECKSUM_UNNECESSARY : CHECKSUM_NONE;
    + fs->skb = skb;
    + fs->lid = ihdr->lrh[3]; /* src LID */
    + fs->len = ihdr->len;
    + fs->frag_num = 1; /* next expected frag number */
    + fs->seq_num = ihdr->seq_num;
    + /*
    + * Copy two bytes of the ethernet hdr from the infinipath
    + * hdr
    + */
    + h = (uint16_t *) skb_push(skb, 2);
    + *h = ihdr->first_2_bytes;
    +
    + /*
    + * Is this an ARP frame?
    + * The data should now contain the 6 byte destination
    + * ether address, the source ether address, and then
    + * the protocol field.
    + */
    + if (h[6] == htons(ETH_ARP_PROTOCOL))
    + _add_mac_lid(&h[3], ihdr->lrh[3]);
    + }
    +
    + fs->len -= skb->len;
    + if (fs->len == 0) {
    + skb = fs->skb;
    + fs->skb = NULL;
    + spin_unlock_irq(&fs->lock);
    +
    + /* Stuff the checksum back into the message. */
    + if (ihdr->flags & NETIF_F_HW_CSUM) {
    + /*
    + * Check to be sure the offset is in the first
    + * fragment.
    + */
    + if (ihdr->csum_offset < skb_headlen(skb)) {
    + *((uint16_t *) (skb->data +
    + ihdr->csum_offset)) =
    + ihdr->csum;
    + } else {
    + /*
    + * This should "never happen" so drop
    + * packet to be safe.
    + */
    + dev_kfree_skb_irq(skb);
    + priv->ipath_ether_stats.rx_dropped++;
    + return 0;
    + }
    + }
    +
    + if (ipath_debug & __IPATH_IPATHPD) {
    + int loop_count;
    +
    + printk("Recv:\n");
    + for (loop_count = 0; loop_count < skb->len;
    + loop_count++) {
    + if (!(loop_count % 16))
    + printk("\n");
    +
    + printk("%02X ", skb->data[loop_count]);
    + }
    +
    + printk("\n\n");
    + }
    +
    + priv->ipath_ether_stats.rx_packets++;
    + priv->ipath_ether_stats.rx_bytes += skb->len;
    + skb->protocol = eth_type_trans(skb, skb->dev);
    + netif_rx(skb);
    + } else
    + spin_unlock_irq(&fs->lock);
    + dev_ipath_ether[device]->last_rx = jiffies;
    +
    + return 0;
    +}
    +
    +/**
    + * ipath_ether_interrupt - ether driver interrupt handler
    + * @device: the infinipath device number
    + * @interrupts: the interrupt mask
    + */
    +static int ipath_ether_interrupt(int device, uint32_t interrupts)
    +{
    + struct ipath_ether_priv *priv = &private_data[device];
    + unsigned wakeup_needed = 0;
    + int rc = 0;
    +
    + ipath_eth_cdbg(VERBOSE, "Took ipath_ether_interrupt\n");
    +
    + /*
    + * This can happen when hardware initialization fails in some way,
    + * and may avoid other bugs as well.
    + */
    + if ((uint32_t) device > MAX_IPATH_LAYER_DEVICE ||
    + !dev_ipath_ether[device]) {
    + ipath_eth_warn("ipath device %u not initialized, "
    + "ignoring interrupt\n", device);
    + return 0;
    + }
    +
    + if (interrupts & IPATH_LAYER_INT_SEND_CONTINUE) {
    + if (atomic_dec_and_test(&send_continue)) {
    + if (priv->cpc.skb)
    + ipath_layer_send_skb(priv->dd, &priv->cpc);
    +
    + if (priv->cpc.error == 0) {
    + if (priv->cpc.skb) {
    + dev_kfree_skb_any(priv->cpc.skb);
    + priv->cpc.skb = NULL;
    + }
    +
    + netif_wake_queue(dev_ipath_ether[device]);
    + } else {
    + atomic_set(&send_continue, 1);
    +
    + rc = 1; /* don't clean the interrupt */
    + }
    + }
    + }
    +
    + if (interrupts & IPATH_LAYER_INT_IF_DOWN) {
    + priv->ipath_ether_if_stat = IPATH_LAYER_DOWN;
    + dev_ipath_ether[device]->flags &= ~IFF_UP;
    + netif_stop_queue(dev_ipath_ether[device]);
    + }
    +
    + if (interrupts & IPATH_LAYER_INT_LID) {
    + wakeup_needed = 1;
    +
    + if (!priv->my_mac_addr[0] && !priv->my_mac_addr[1] &&
    + !priv->my_mac_addr[2]) {
    + if (ipath_layer_get_mac
    + (priv->dd, (uint8_t *) priv->my_mac_addr)) {
    + ipath_eth_warn("Fall back to default OUI, "
    + "couldn't get MAC\n");
    + priv->my_mac_addr[0] = IPATH_SRC_OUI_1 |
    + (IPATH_SRC_OUI_2 << 8);
    + priv->my_mac_addr[1] = IPATH_SRC_OUI_3;
    + }
    + }
    +
    + /* convert to network order */
    + priv->my_lid = htons(ipath_layer_get_lid(priv->dd));
    +
    + memcpy(dev_ipath_ether[device]->dev_addr,
    + priv->my_mac_addr,
    + dev_ipath_ether[device]->addr_len);
    +
    + /*
    + * else get it below, after possible BCAST processing as
    + * well or at open if mcast lid times out
    + */
    + }
    +
    + if (interrupts & IPATH_LAYER_INT_BCAST) {
    + wakeup_needed = 1;
    + /*
    + * we may never get this, because some SMs don't support
    + * multicast, so at open, we will do the same thing if the
    + * wait for bcast times out
    + */
    + priv->my_bcast = htons(ipath_layer_get_bcast(priv->dd));
    + }
    +
    + if (interrupts & IPATH_LAYER_INT_IF_UP) {
    + /* after LID/MLID processing */
    + priv->ipath_ether_if_stat = IPATH_LAYER_UP;
    + /* in case we get both set as result of open */
    + if (priv->my_lid && priv->my_bcast) {
    + netif_wake_queue(dev_ipath_ether[device]);
    + dev_ipath_ether[device]->flags |= IFF_UP;
    + }
    + }
    +
    + if (wakeup_needed) {
    + /*
    + * arguably this should be waiting for lid and mlid,
    + * but since mlid isn't the only possible path for now,
    + * just wait for the lid.
    + */
    + wake_up_interruptible(&priv->lid_wait);
    + }
    +
    + return rc;
    +}
    +
    +static int ipath_ether_open(struct net_device *dev)
    +{
    + uint32_t mtu;
    + int rc;
    +
    + rc = ipath_layer_open(priv_data(dev)->dd, &mtu);
    + if (rc != 0)
    + return rc;
    +
    + /*
    + * wait here until LID is set, otherwise "standard" networking
    + * over ipath won't work, because we'll continue on through
    + * starting up networking services, but ipath won't yet be usable,
    + * since it takes up to 30 seconds for SM and sma to chat and get
    + * our LID assigned.
    + */
    + wait_event_interruptible_timeout(priv_data(dev)->lid_wait,
    + priv_data(dev)->my_lid, 75 * HZ);
    +
    + if (!priv_data(dev)->my_lid) {
    + ipath_eth_err("ipath_ether_open timed out waiting for LID -"
    + " can't send packets\n");
    +
    + return -EPERM;
    + }
    +
    + wait_event_interruptible_timeout(priv_data(dev)->lid_wait,
    + priv_data(dev)->my_bcast, 75 * HZ);
    +
    + if (!priv_data(dev)->my_bcast) {
    + ipath_eth_err("ipath_ether_open timed out waiting for "
    + "MLID - can't send packets\n");
    +
    + return -EPERM;
    + }
    +
    + _send_lid_message(priv_data(dev)->my_mac_addr,
    + LID_ARP_RESPONSE, priv_data(dev)->my_bcast, dev);
    +
    + dev->flags |= IFF_UP;
    + netif_wake_queue(dev);
    +
    + return 0;
    +}
    +
    +static int ipath_ether_close(struct net_device *dev)
    +{
    + netif_stop_queue(dev);
    +
    + return 0;
    +}
    +
    +static struct net_device_stats *ipath_ether_get_stats(
    + struct net_device *dev)
    +{
    + return &priv_data(dev)->ipath_ether_stats;
    +}
    +
    +static int ipath_ether_change_mtu(struct net_device *dev, int new_mtu)
    +{
    + /*
    + * The MTU isn't really limited but we set an arbitrary limit of
    + * 16 * 2108 - 12. Which is 16 max infiniband sized packets minus
    + * the ethernet header (except for the 2 bytes we put in the
    + * ether_header header).
    + */
    + if ((new_mtu < 68) || new_mtu > 33716 || (new_mtu & 3))
    + return -EINVAL;
    + dev->mtu = new_mtu;
    + return 0;
    +}
    +
    +static void ipath_ether_set_multicast_list(struct net_device *dev)
    +{
    + struct dev_mc_list *mc_mac_entry = dev->mc_list;
    +
    + /*
    + * No entries are really added but the can be displayed for debug
    + * purpose
    + */
    +
    + while (mc_mac_entry) {
    + ipath_eth_table("Adding multicast MAC "
    + "[%02x:%02x:%02x:%02x:%02x:%02x]\n",
    + mc_mac_entry->dmi_addr[0],
    + mc_mac_entry->dmi_addr[1],
    + mc_mac_entry->dmi_addr[2],
    + mc_mac_entry->dmi_addr[3],
    + mc_mac_entry->dmi_addr[4],
    + mc_mac_entry->dmi_addr[5]);
    +
    + mc_mac_entry = mc_mac_entry->next;
    + }
    +}
    +
    +#ifdef ipath_ether_ioctl_support
    +
    +/**
    + * ipath_ether_get_settings - get ethernet device settings
    + * @dev: the network device
    + * @ecmd: the results are placed here
    + *
    + * This function is here to allow "ethtool eth<N>" to report something
    + * reasonable for infinipath. We return values for 10Gb ethernet
    + * as being reasonably similar.
    + */
    +static int ipath_ether_get_settings(struct net_device *dev,
    + struct ethtool_cmd *ecmd)
    +{
    + ecmd->supported = SUPPORTED_10000baseT_Full;
    + ecmd->port = PORT_TP;
    + ecmd->transceiver = XCVR_INTERNAL;
    + ecmd->advertising = ADVERTISED_10000baseT_Full;
    + ecmd->speed = SPEED_10000;
    + ecmd->duplex = DUPLEX_FULL;
    + return 0;
    +}
    +
    +static void ipath_ether_get_drvinfo(struct net_device *dev,
    + struct ethtool_drvinfo *info)
    +{
    + strcpy(info->driver, DRV_NAME);
    + strcpy(info->version, DRV_VERSION);
    + sprintf(info->bus_info, "InfiniPath");
    +}
    +
    +static u32 ipath_ether_get_rx_csum(struct net_device *dev)
    +{
    + return 0;
    +}
    +
    +static u32 ipath_ether_get_tx_csum(struct net_device *dev)
    +{
    + return (dev->features & NETIF_F_HW_CSUM) != 0;
    +}
    +
    +static int ipath_ether_set_tx_csum(struct net_device *dev, u32 data)
    +{
    + if (data)
    + dev->features |= NETIF_F_HW_CSUM;
    + else
    + dev->features &= ~NETIF_F_HW_CSUM;
    + return 0;
    +}
    +
    +static int ipath_ether_get_tables(struct net_device *dev)
    +{
    + int counter;
    + int index;
    + struct ether_hash *hash_entry;
    + uint8_t *mac_byte_ptr;
    + int num_of_entries = 0;
    +
    + /* Only dump the hash table if the interface is up */
    + if (!(dev->flags & IFF_UP))
    + return 0;
    +
    + ipath_eth_table("Dumping hash table ..\n");
    + for (counter = 0; counter < MAX_HASH_ENTRIES; counter++) {
    + hash_entry = &hash_table[counter];
    +
    + if (hash_entry->lid == 0)
    + continue;
    +
    + index = 1;
    + do {
    + num_of_entries++;
    +
    + mac_byte_ptr = (uint8_t *) hash_entry->mac;
    + ipath_eth_table("%4d.%02d MAC = "
    + "%02x:%02x:%02x:%02x:%02x:%02x, "
    + "LID = %4d [0x%04x]\n",
    + counter, index++, mac_byte_ptr[0],
    + mac_byte_ptr[1], mac_byte_ptr[2],
    + mac_byte_ptr[3], mac_byte_ptr[4],
    + mac_byte_ptr[5],
    + ntohs(hash_entry->lid),
    + ntohs(hash_entry->lid));
    + } while ((hash_entry = hash_entry->next) != NULL);
    + }
    +
    + ipath_eth_table("# of entries is %i\n", num_of_entries);
    +
    + return 0;
    +}
    +
    +static struct ethtool_ops ipath_ether_ethtool_ops = {
    + .get_settings = ipath_ether_get_settings,
    + .get_drvinfo = ipath_ether_get_drvinfo,
    + .get_rx_csum = ipath_ether_get_rx_csum,
    + .get_tx_csum = ipath_ether_get_tx_csum,
    + .set_tx_csum = ipath_ether_set_tx_csum,
    + .set_sg = ethtool_op_set_sg,
    + .get_sg = ethtool_op_get_sg,
    + .get_tso = ethtool_op_get_tso,
    + .get_stats_count = ipath_ether_get_tables
    +};
    +
    +static int ipath_ether_ioctl(struct net_device *dev, struct ifreq *ifr,
    + int cmd)
    +{
    + switch (cmd) {
    +
    + case SIOCGMIIPHY: /* Get address of MII PHY in use. */
    + ipath_eth_dbg("Get address of MII PHY in use [%x]\n", cmd);
    + return 0;
    + case SIOCGMIIREG: /* Read MII PHY register. */
    + ipath_eth_dbg("Read MII PHY register [%x]\n", cmd);
    + return 0;
    + case SIOCSMIIREG: /* Write to MII PHY register. */
    + ipath_eth_dbg("Write to MII PHY register [%x]\n", cmd);
    + return 0;
    +
    + case 0x8b01 /*SIOCGIWNAME*/:
    + /*
    + * Wireless getname; see this on every startup, so
    + * don't complain about it; don't want to include
    + * wireless.h, so just use the value
    + */
    + return -EOPNOTSUPP;
    +
    + default:
    + /*
    + * need to make this conditional, or remove it, some day
    + * for now, we want to know about ioctls we get that we
    + * don't support
    + */
    + ipath_eth_dbg("got unsupported ipath_ether_ioctl with "
    + "cmd = %x\n", cmd);
    + return -EOPNOTSUPP;
    + }
    +}
    +#endif
    +
    +int __init ipath_ether_probe(int device)
    +{
    + struct ipath_devdata *dd;
    + int rc = -ENODEV;
    + unsigned int i;
    +
    + /*
    + * check for being able to register first, in case fewer infinipath
    + * devices are present than are supported; we don't want to register
    + * network devices for non-existent infinipath devices.
    + */
    + rc = ipath_layer_register(device,
    + ipath_ether_interrupt,
    + ipath_ether_rx,
    + OPCODE_ITH4X,
    + ipath_ether_process_lid_arp, OPCODE_ITH4X,
    + &dd);
    + if (rc < 0) {
    + ipath_eth_warn("Unable to register device %u: %d\n", device,
    + -rc);
    + /*
    + * this could be just fine, since we may have fewer than the
    + * max supported chips present
    + */
    + return -ENODEV;
    + }
    +
    + dev_ipath_ether[device] = alloc_etherdev(32);
    + if (dev_ipath_ether[device] == NULL)
    + goto ipath_ether_probe_exit_level_0;
    +
    + dev_ipath_ether[device]->priv = &private_data[device];
    +
    + memset(&private_data[device], 0, sizeof(struct ipath_ether_priv));
    +
    + SET_MODULE_OWNER(dev_ipath_ether[device]);
    +
    + private_data[device].dd = dd;
    + private_data[device].device_id = device;
    + init_waitqueue_head(&private_data[device].lid_wait);
    +
    + dev_ipath_ether[device]->flags &= ~IFF_UP;
    +
    + dev_ipath_ether[device]->mtu = 16384;
    +
    + /* The ipath_ether-specific entries in the device structure. */
    + dev_ipath_ether[device]->open = ipath_ether_open;
    + dev_ipath_ether[device]->hard_start_xmit = ipath_ether_start_xmit;
    + dev_ipath_ether[device]->stop = ipath_ether_close;
    + dev_ipath_ether[device]->get_stats = ipath_ether_get_stats;
    + dev_ipath_ether[device]->change_mtu = ipath_ether_change_mtu;
    + dev_ipath_ether[device]->set_multicast_list =
    + ipath_ether_set_multicast_list;
    + dev_ipath_ether[device]->tx_timeout = NULL;
    + dev_ipath_ether[device]->watchdog_timeo = TX_TIMEOUT;
    + dev_ipath_ether[device]->features |= NETIF_F_HW_CSUM | NETIF_F_SG |
    + NETIF_F_FRAGLIST | NETIF_F_HIGHDMA;
    +
    +#ifdef ipath_ether_ioctl_support
    + dev_ipath_ether[device]->do_ioctl = ipath_ether_ioctl;
    + dev_ipath_ether[device]->ethtool_ops = &ipath_ether_ethtool_ops;
    +#else
    + dev_ipath_ether[device]->do_ioctl = NULL;
    +#endif
    +
    + private_data[device].fstate =
    + kzalloc(ipath_fragtable_size *
    + sizeof(struct ipath_frag_state), GFP_ATOMIC);
    +
    + if (private_data[device].fstate == NULL) {
    + rc = -ENOMEM;
    + goto ipath_ether_probe_exit_level_0;
    + }
    +
    + for (i = 0; i < ipath_fragtable_size; i++)
    + spin_lock_init(&private_data[device].fstate[i].lock);
    +
    + /* make sure that the queue is inactive */
    + netif_stop_queue(dev_ipath_ether[device]);
    +
    + /*
    + * make an attempt to get our MAC address before registering with
    + * the network layer. This works as long as we are not overriding
    + * the GUID or getting it from some method other than the flash. It
    + * increases the likelihood of SuSE network configuration working,
    + * and is pretty much the right thing to do, in any case.
    + */
    + (void)ipath_layer_get_mac(
    + private_data[device].dd,
    + (uint8_t *) private_data[device].my_mac_addr);
    + memcpy(dev_ipath_ether[device]->dev_addr,
    + private_data[device].my_mac_addr,
    + dev_ipath_ether[device]->addr_len);
    +
    + strcpy(dev_ipath_ether[device]->name, "eth%d");
    + rc = register_netdev(dev_ipath_ether[device]);
    + if (rc != 0)
    + goto ipath_ether_probe_exit_level_1;
    +
    + private_data[device].ipath_ether_stats.tx_fifo_errors = 0;
    + private_data[device].ipath_ether_stats.tx_carrier_errors = 0;
    +
    + return 0;
    +
    +ipath_ether_probe_exit_level_1:
    + if (private_data[device].fstate != NULL) {
    + kfree(private_data[device].fstate);
    + private_data[device].fstate = NULL;
    + }
    +
    + free_netdev(dev_ipath_ether[device]);
    +
    +ipath_ether_probe_exit_level_0:
    + return rc;
    +}
    +
    +int __init ipath_ether_init(void)
    +{
    + int counter, nfound = 0;
    + int rc, lasterr = 0;
    +
    + /* safety checks */
    + if (!&ipath_debug) {
    + /*
    + * This has occasionally been seen when the module load code
    + * has errors loading dependent modules. This prevents an
    + * oops, and makes it more obvious what happened. Have to
    + * use printk() directly for this one
    + */
    + printk(KERN_ERR "Module error, %s loading, but ipath_core "
    + "not loaded!\n", DRV_NAME);
    + return -ENODEV;
    + }
    + if (sizeof(struct ips_message_header) !=
    + sizeof(struct ether_header)) {
    + ipath_eth_err("FATAL ERROR (ipath_ether_init): header size "
    + "is wrong [%i<>%i]!!!\n",
    + (int)sizeof(struct ips_message_header),
    + (int)sizeof(struct ether_header));
    + return -ENODEV;
    + }
    +
    + number_of_devices = ipath_layer_get_num_of_dev();
    +
    + for (counter = 0; counter < number_of_devices; counter++) {
    + rc = ipath_ether_probe(counter);
    + if (rc)
    + lasterr = rc;
    + else
    + nfound++;
    + }
    + if (!nfound)
    + return lasterr; /* no usable devices were found */
    +
    + return 0;
    +}
    +
    +void __exit ipath_ether_exit(void)
    +{
    + int counter, ninuse = 0;
    +
    + for (counter = 0; counter < number_of_devices; counter++) {
    + ipath_layer_close(private_data[counter].dd);
    +
    + if (!dev_ipath_ether[counter])
    + /*
    + * never registered, probably infinipath device
    + * not present
    + */
    + continue;
    + ninuse++;
    + unregister_netdev(dev_ipath_ether[counter]);
    + free_netdev(dev_ipath_ether[counter]);
    +
    + dev_ipath_ether[counter] = NULL;
    +
    + if (private_data[counter].fstate != NULL) {
    + kfree(private_data[counter].fstate);
    + private_data[counter].fstate = NULL;
    + }
    + }
    +
    + if (ninuse <= 1) { /* only if none are in use */
    + while (all_hash_entries) {
    + struct ether_hash *next = all_hash_entries->next;
    +
    + kfree(all_hash_entries);
    + all_hash_entries = next;
    + }
    + }
    +}
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/
    \
     
     \ /
      Last update: 2006-03-10 01:44    [W:0.093 / U:29.364 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site