Ammasso 1100 iWARP Driver
Subsystems (3)
v3 cover Ammasso 1100 iWARP Driver
This patchset implements the iWARP provider driver for the Ammasso 1100 RNIC. It is dependent on the "iWARP Core Support" patch set. We're submitting it for review with the goal for inclusion in the 2.6.19 kernel. This code has gone through several reviews in the openib-general list. Now we are submitting it for external review by the linux community.
This StGIT patchset is cloned from Roland Dreier's infiniband.git for-2.6.19 branch. The patchset consists of 7 patches:
1 - Low-level device interface and native stack support 2 - Work request definitions
3 - Provider interface
4 - Memory management
5 - User mode message queue implementation
6 - Verbs queue implementation
7 - Kconfig and Makefile
I believe I've addressed all the round 1 and 2 review comments. Details of the changes are tracked in each patch comment.
PATCH v3 1/7 AMSO1100 Low Level Driver.
This is the core of the driver and includes the hardware probe, low-level device interfaces and native Ethernet support.
V2 Review Changes:
- fixed private data memory leak incoming connect requests. No longer need to copy the private data. The IWCM will.
- correctly map host memory for DMA (don't use __pa()).
V1 Review Changes
- sizeof -> sizeof()
- dprintk() -> pr_debug()
- removed useless asserts
- assert() -> BUG_ON()
- C2_DEBUG -> DEBUG
- removed debug netevent code
- removed arp request squelch code from intr handler, replacing it with setting arp_ignore when the c2 netdev is brought up.
- removed c2_set_mac_addr().
---
drivers/infiniband/hw/amso1100/c2.c | 1255 ++++++++++++++++++++++++++++++ drivers/infiniband/hw/amso1100/c2.h | 552 +++++++++++++ drivers/infiniband/hw/amso1100/c2_ae.c | 321 ++++++++ drivers/infiniband/hw/amso1100/c2_intr.c | 209 +++++ drivers/infiniband/hw/amso1100/c2_rnic.c | 664 ++++++++++++++++ 5 files changed, 3001 insertions(+), 0 deletions(-)
drivers/infiniband/hw/amso1100/c2.c new file +1255 -0
@@ -0,0 +1,1255 @@ +/* +* Copyright (c) 2005 Ammasso, Inc. All rights reserved. +* Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - 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. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/inetdevice.h> +#include <linux/delay.h> +#include <linux/ethtool.h> +#include <linux/mii.h> +#include <linux/if_vlan.h> +#include <linux/crc32.h> +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/tcp.h> +#include <linux/init.h> +#include <linux/dma-mapping.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/byteorder.h> + +#include <rdma/ib_smi.h> +#include "c2.h" +#include "c2_provider.h" + +MODULE_AUTHOR ("Tom Tucker <[email protected]>" ); +MODULE_DESCRIPTION ("Ammasso AMSO1100 Low-level iWARP Driver" ); +MODULE_LICENSE ("Dual BSD/GPL" ); +MODULE_VERSION (DRV_VERSION ); + +static const u32 default_msg =NETIF_MSG_DRV |NETIF_MSG_PROBE |NETIF_MSG_LINK + |NETIF_MSG_IFUP |NETIF_MSG_IFDOWN ; + +static int debug =-1 ;/* defaults above */ +module_param (debug, int,0 ); +MODULE_PARM_DESC (debug,"Debug level (0=none,...,16=all)" ); + +static int c2_up (struct net_device *netdev); +static int c2_down (struct net_device *netdev); +static int c2_xmit_frame (struct sk_buff *skb,struct net_device *netdev); +static void c2_tx_interrupt (struct net_device *netdev); +static void c2_rx_interrupt (struct net_device *netdev); +static irqreturn_t c2_interrupt (int irq,void *dev_id,struct pt_regs *regs); +static void c2_tx_timeout (struct net_device *netdev); +static int c2_change_mtu (struct net_device *netdev,int new_mtu); +static void c2_reset (struct c2_port *c2_port); +static struct net_device_stats *c2_get_stats (struct net_device *netdev); + +static struct pci_device_id c2_pci_table[] = { + {0x18b8 ,0xb001 ,PCI_ANY_ID ,PCI_ANY_ID }, + {0 } +}; + +MODULE_DEVICE_TABLE (pci, c2_pci_table); + +static void c2_print_macaddr (struct net_device *netdev) +{ +pr_debug ("%s: MAC %02X:%02X:%02X:%02X:%02X:%02X, " +"IRQ %u\n" , netdev->name, + netdev->dev_addr[0 ], netdev->dev_addr[1 ], netdev->dev_addr[2 ], + netdev->dev_addr[3 ], netdev->dev_addr[4 ], netdev->dev_addr[5 ], + netdev->irq); +} + +static void c2_set_rxbufsize (struct c2_port *c2_port) +{ +struct net_device *netdev = c2_port->netdev; + +if (netdev->mtu >RX_BUF_SIZE ) + c2_port->rx_buf_size = + netdev->mtu +ETH_HLEN +sizeof (struct c2_rxp_hdr ) + +NET_IP_ALIGN ; +else + c2_port->rx_buf_size =sizeof (struct c2_rxp_hdr ) +RX_BUF_SIZE ; +} + +/* +* Allocate TX ring elements and chain them together. +* One-to-one association of adapter descriptors with ring elements. +*/ +static int c2_tx_ring_alloc (struct c2_ring *tx_ring,void *vaddr, +dma_addr_t base,void __iomem * mmio_txp_ring) +{ +struct c2_tx_desc *tx_desc; +struct c2_txp_desc __iomem *txp_desc; +struct c2_element *elem; +int i; + + tx_ring->start =kmalloc (sizeof (*elem) * tx_ring->count,GFP_KERNEL ); +if (!tx_ring->start) +return -ENOMEM ; + + elem = tx_ring->start; + tx_desc = vaddr; + txp_desc = mmio_txp_ring; +for (i =0 ; i < tx_ring->count; i++, elem++, tx_desc++, txp_desc++) { + tx_desc->len =0 ; + tx_desc->status =0 ; + +/* Set TXP_HTXD_UNINIT */ +__raw_writeq (cpu_to_be64 (0x1122334455667788ULL ), + (void __iomem *) txp_desc +C2_TXP_ADDR ); +__raw_writew (0 , (void __iomem *) txp_desc +C2_TXP_LEN ); +__raw_writew (cpu_to_be16 (TXP_HTXD_UNINIT ), + (void __iomem *) txp_desc +C2_TXP_FLAGS ); + + elem->skb =NULL ; + elem->ht_desc = tx_desc; + elem->hw_desc = txp_desc; + +if (i == tx_ring->count -1 ) { + elem->next = tx_ring->start; + tx_desc->next_offset = base; + }else { + elem->next = elem +1 ; + tx_desc->next_offset = + base + (i +1 ) *sizeof (*tx_desc); + } + } + + tx_ring->to_use = tx_ring->to_clean = tx_ring->start; + +return 0 ; +} + +/* +* Allocate RX ring elements and chain them together. +* One-to-one association of adapter descriptors with ring elements. +*/ +static int c2_rx_ring_alloc (struct c2_ring *rx_ring,void *vaddr, +dma_addr_t base,void __iomem * mmio_rxp_ring) +{ +struct c2_rx_desc *rx_desc; +struct c2_rxp_desc __iomem *rxp_desc; +struct c2_element *elem; +int i; + + rx_ring->start =kmalloc (sizeof (*elem) * rx_ring->count,GFP_KERNEL ); +if (!rx_ring->start) +return -ENOMEM ; + + elem = rx_ring->start; + rx_desc = vaddr; + rxp_desc = mmio_rxp_ring; +for (i =0 ; i < rx_ring->count; i++, elem++, rx_desc++, rxp_desc++) { + rx_desc->len =0 ; + rx_desc->status =0 ; + +/* Set RXP_HRXD_UNINIT */ +__raw_writew (cpu_to_be16 (RXP_HRXD_OK ), + (void __iomem *) rxp_desc +C2_RXP_STATUS ); +__raw_writew (0 , (void __iomem *) rxp_desc +C2_RXP_COUNT ); +__raw_writew (0 , (void __iomem *) rxp_desc +C2_RXP_LEN ); +__raw_writeq (cpu_to_be64 (0x99aabbccddeeffULL ), + (void __iomem *) rxp_desc +C2_RXP_ADDR ); +__raw_writew (cpu_to_be16 (RXP_HRXD_UNINIT ), + (void __iomem *) rxp_desc +C2_RXP_FLAGS ); + + elem->skb =NULL ; + elem->ht_desc = rx_desc; + elem->hw_desc = rxp_desc; + +if (i == rx_ring->count -1 ) { + elem->next = rx_ring->start; + rx_desc->next_offset = base; + }else { + elem->next = elem +1 ; + rx_desc->next_offset = + base + (i +1 ) *sizeof (*rx_desc); + } + } + + rx_ring->to_use = rx_ring->to_clean = rx_ring->start; + +return 0 ; +} + +/* Setup buffer for receiving */ +static inline int c2_rx_alloc (struct c2_port *c2_port,struct c2_element *elem) +{ +struct c2_dev *c2dev = c2_port->c2dev; +struct c2_rx_desc *rx_desc = elem->ht_desc; +struct sk_buff *skb; +dma_addr_t mapaddr; +u32 maplen; +struct c2_rxp_hdr *rxp_hdr; + + skb =dev_alloc_skb (c2_port->rx_buf_size); +if (unlikely (!skb)) { +pr_debug ("%s: out of memory for receive\n" , + c2_port->netdev->name); +return -ENOMEM ; + } + +/* Zero out the rxp hdr in the sk_buff */ +memset (skb->data,0 ,sizeof (*rxp_hdr)); + + skb->dev = c2_port->netdev; + + maplen = c2_port->rx_buf_size; + mapaddr = +pci_map_single (c2dev->pcidev, skb->data, maplen, +PCI_DMA_FROMDEVICE ); + +/* Set the sk_buff RXP_header to RXP_HRXD_READY */ + rxp_hdr = (struct c2_rxp_hdr *) skb->data; + rxp_hdr->flags =RXP_HRXD_READY ; + +__raw_writew (0 , elem->hw_desc +C2_RXP_STATUS ); +__raw_writew (cpu_to_be16 ((u16 ) maplen -sizeof (*rxp_hdr)), + elem->hw_desc +C2_RXP_LEN ); +__raw_writeq (cpu_to_be64 (mapaddr), elem->hw_desc +C2_RXP_ADDR ); +__raw_writew (cpu_to_be16 (RXP_HRXD_READY ), elem->hw_desc +C2_RXP_FLAGS ); + + elem->skb = skb; + elem->mapaddr = mapaddr; + elem->maplen = maplen; + rx_desc->len = maplen; + +return 0 ; +} + +/* +* Allocate buffers for the Rx ring +* For receive: rx_ring.to_clean is next received frame +*/ +static int c2_rx_fill (struct c2_port *c2_port) +{ +struct c2_ring *rx_ring = &c2_port->rx_ring; +struct c2_element *elem; +int ret =0 ; + + elem = rx_ring->start; +do { +if (c2_rx_alloc (c2_port, elem)) { + ret =1 ; +break ; + } + }while ((elem = elem->next) != rx_ring->start); + + rx_ring->to_clean = rx_ring->start; +return ret; +} + +/* Free all buffers in RX ring, assumes receiver stopped */ +static void c2_rx_clean (struct c2_port *c2_port) +{ +struct c2_dev *c2dev = c2_port->c2dev; +struct c2_ring *rx_ring = &c2_port->rx_ring; +struct c2_element *elem; +struct c2_rx_desc *rx_desc; + + elem = rx_ring->start; +do { + rx_desc = elem->ht_desc; + rx_desc->len =0 ; + +__raw_writew (0 , elem->hw_desc +C2_RXP_STATUS ); +__raw_writew (0 , elem->hw_desc +C2_RXP_COUNT ); +__raw_writew (0 , elem->hw_desc +C2_RXP_LEN ); +__raw_writeq (cpu_to_be64 (0x99aabbccddeeffULL ), + elem->hw_desc +C2_RXP_ADDR ); +__raw_writew (cpu_to_be16 (RXP_HRXD_UNINIT ), + elem->hw_desc +C2_RXP_FLAGS ); + +if (elem->skb) { +pci_unmap_single (c2dev->pcidev, elem->mapaddr, + elem->maplen,PCI_DMA_FROMDEVICE ); +dev_kfree_skb (elem->skb); + elem->skb =NULL ; + } + }while ((elem = elem->next) != rx_ring->start); +} + +static inline int c2_tx_free (struct c2_dev *c2dev,struct c2_element *elem) +{ +struct c2_tx_desc *tx_desc = elem->ht_desc; + + tx_desc->len =0 ; + +pci_unmap_single (c2dev->pcidev, elem->mapaddr, elem->maplen, +PCI_DMA_TODEVICE ); + +if (elem->skb) { +dev_kfree_skb_any (elem->skb); + elem->skb =NULL ; + } + +return 0 ; +} + +/* Free all buffers in TX ring, assumes transmitter stopped */ +static void c2_tx_clean (struct c2_port *c2_port) +{ +struct c2_ring *tx_ring = &c2_port->tx_ring; +struct c2_element *elem; +struct c2_txp_desc txp_htxd; +int retry; +unsigned long flags; + +spin_lock_irqsave (&c2_port->tx_lock, flags); + + elem = tx_ring->start; + +do { + retry =0 ; +do { + txp_htxd.flags = +readw (elem->hw_desc +C2_TXP_FLAGS ); + +if (txp_htxd.flags ==TXP_HTXD_READY ) { + retry =1 ; +__raw_writew (0 , + elem->hw_desc +C2_TXP_LEN ); +__raw_writeq (0 , + elem->hw_desc +C2_TXP_ADDR ); +__raw_writew (cpu_to_be16 (TXP_HTXD_DONE ), + elem->hw_desc +C2_TXP_FLAGS ); + c2_port->netstats.tx_dropped++; +break ; + }else { +__raw_writew (0 , + elem->hw_desc +C2_TXP_LEN ); +__raw_writeq (cpu_to_be64 (0x1122334455667788ULL ), + elem->hw_desc +C2_TXP_ADDR ); +__raw_writew (cpu_to_be16 (TXP_HTXD_UNINIT ), + elem->hw_desc +C2_TXP_FLAGS ); + } + +c2_tx_free (c2_port->c2dev, elem); + + }while ((elem = elem->next) != tx_ring->start); + }while (retry); + + c2_port->tx_avail = c2_port->tx_ring.count -1 ; + c2_port->c2dev->cur_tx = tx_ring->to_use - tx_ring->start; + +if (c2_port->tx_avail >MAX_SKB_FRAGS +1 ) +netif_wake_queue (c2_port->netdev); + +spin_unlock_irqrestore (&c2_port->tx_lock, flags); +} + +/* +* Process transmit descriptors marked 'DONE' by the firmware, +* freeing up their unneeded sk_buffs. +*/ +static void c2_tx_interrupt (struct net_device *netdev) +{ +struct c2_port *c2_port =netdev_priv (netdev); +struct c2_dev *c2dev = c2_port->c2dev; +struct c2_ring *tx_ring = &c2_port->tx_ring; +struct c2_element *elem; +struct c2_txp_desc txp_htxd; + +spin_lock (&c2_port->tx_lock); + +for (elem = tx_ring->to_clean; elem != tx_ring->to_use; + elem = elem->next) { + txp_htxd.flags = +be16_to_cpu (readw (elem->hw_desc +C2_TXP_FLAGS )); + +if (txp_htxd.flags !=TXP_HTXD_DONE ) +break ; + +if (netif_msg_tx_done (c2_port)) { +/* PCI reads are expensive in fast path */ + txp_htxd.len = +be16_to_cpu (readw (elem->hw_desc +C2_TXP_LEN )); +pr_debug ("%s: tx done slot %3Zu status 0x%x len " +"%5u bytes\n" , + netdev->name, elem - tx_ring->start, + txp_htxd.flags, txp_htxd.len); + } + +c2_tx_free (c2dev, elem); + ++(c2_port->tx_avail); + } + + tx_ring->to_clean = elem; + +if (netif_queue_stopped (netdev) + && c2_port->tx_avail >MAX_SKB_FRAGS +1 ) +netif_wake_queue (netdev); + +spin_unlock (&c2_port->tx_lock); +} + +static void c2_rx_error (struct c2_port *c2_port,struct c2_element *elem) +{ +struct c2_rx_desc *rx_desc = elem->ht_desc; +struct c2_rxp_hdr *rxp_hdr = (struct c2_rxp_hdr *) elem->skb->data; + +if (rxp_hdr->status !=RXP_HRXD_OK || + rxp_hdr->len > (rx_desc->len -sizeof (*rxp_hdr))) { +pr_debug ("BAD RXP_HRXD\n" ); +pr_debug (" rx_desc : %p\n" , rx_desc); +pr_debug (" index : %Zu\n" , + elem - c2_port->rx_ring.start); +pr_debug (" len : %u\n" , rx_desc->len); +pr_debug (" rxp_hdr : %p [PA %p]\n" , rxp_hdr, + (void *)__pa ((unsigned long ) rxp_hdr)); +pr_debug (" flags : 0x%x\n" , rxp_hdr->flags); +pr_debug (" status: 0x%x\n" , rxp_hdr->status); +pr_debug (" len : %u\n" , rxp_hdr->len); +pr_debug (" rsvd : 0x%x\n" , rxp_hdr->rsvd); + } + +/* Setup the skb for reuse since we're dropping this pkt */ + elem->skb->tail = elem->skb->data = elem->skb->head; + +/* Zero out the rxp hdr in the sk_buff */ +memset (elem->skb->data,0 ,sizeof (*rxp_hdr)); + +/* Write the descriptor to the adapter's rx ring */ +__raw_writew (0 , elem->hw_desc +C2_RXP_STATUS ); +__raw_writew (0 , elem->hw_desc +C2_RXP_COUNT ); +__raw_writew (cpu_to_be16 ((u16 ) elem->maplen -sizeof (*rxp_hdr)), + elem->hw_desc +C2_RXP_LEN ); +__raw_writeq (cpu_to_be64 (elem->mapaddr), elem->hw_desc +C2_RXP_ADDR ); +__raw_writew (cpu_to_be16 (RXP_HRXD_READY ), elem->hw_desc +C2_RXP_FLAGS ); + +pr_debug ("packet dropped\n" ); + c2_port->netstats.rx_dropped++; +} + +static void c2_rx_interrupt (struct net_device *netdev) +{ +struct c2_port *c2_port =netdev_priv (netdev); +struct c2_dev *c2dev = c2_port->c2dev; +struct c2_ring *rx_ring = &c2_port->rx_ring; +struct c2_element *elem; +struct c2_rx_desc *rx_desc; +struct c2_rxp_hdr *rxp_hdr; +struct sk_buff *skb; +dma_addr_t mapaddr; +u32 maplen, buflen; +unsigned long flags; + +spin_lock_irqsave (&c2dev->lock, flags); + +/* Begin where we left off */ + rx_ring->to_clean = rx_ring->start + c2dev->cur_rx; + +for (elem = rx_ring->to_clean; elem->next != rx_ring->to_clean; + elem = elem->next) { + rx_desc = elem->ht_desc; + mapaddr = elem->mapaddr; + maplen = elem->maplen; + skb = elem->skb; + rxp_hdr = (struct c2_rxp_hdr *) skb->data; + +if (rxp_hdr->flags !=RXP_HRXD_DONE ) +break ; + buflen = rxp_hdr->len; + +/* Sanity check the RXP header */ +if (rxp_hdr->status !=RXP_HRXD_OK || + buflen > (rx_desc->len -sizeof (*rxp_hdr))) { +c2_rx_error (c2_port, elem); +continue ; + } + +/* +* Allocate and map a new skb for replenishing the host +* RX desc +*/ +if (c2_rx_alloc (c2_port, elem)) { +c2_rx_error (c2_port, elem); +continue ; + } + +/* Unmap the old skb */ +pci_unmap_single (c2dev->pcidev, mapaddr, maplen, +PCI_DMA_FROMDEVICE ); + +prefetch (skb->data); + +/* +* Skip past the leading 8 bytes comprising of the +* "struct c2_rxp_hdr", prepended by the adapter +* to the usual Ethernet header ("struct ethhdr"), +* to the start of the raw Ethernet packet. +* +* Fix up the various fields in the sk_buff before +* passing it up to netif_rx(). The transfer size +* (in bytes) specified by the adapter len field of +* the "struct rxp_hdr_t" does NOT include the +* "sizeof(struct c2_rxp_hdr)". +*/ + skb->data +=sizeof (*rxp_hdr); + skb->tail = skb->data + buflen; + skb->len = buflen; + skb->dev = netdev; + skb->protocol =eth_type_trans (skb, netdev); + +netif_rx (skb); + + netdev->last_rx = jiffies; + c2_port->netstats.rx_packets++; + c2_port->netstats.rx_bytes += buflen; + } + +/* Save where we left off */ + rx_ring->to_clean = elem; + c2dev->cur_rx = elem - rx_ring->start; +C2_SET_CUR_RX (c2dev, c2dev->cur_rx); + +spin_unlock_irqrestore (&c2dev->lock, flags); +} + +/* +* Handle netisr0 TX & RX interrupts. +*/ +static irqreturn_t c2_interrupt (int irq,void *dev_id,struct pt_regs *regs) +{ +unsigned int netisr0, dmaisr; +int handled =0 ; +struct c2_dev *c2dev = (struct c2_dev *) dev_id; + +/* Process CCILNET interrupts */ + netisr0 =readl (c2dev->regs +C2_NISR0 ); +if (netisr0) { + +/* +* There is an issue with the firmware that always +* provides the status of RX for both TX & RX +* interrupts. So process both queues here. +*/ +c2_rx_interrupt (c2dev->netdev); +c2_tx_interrupt (c2dev->netdev); + +/* Clear the interrupt */ +writel (netisr0, c2dev->regs +C2_NISR0 ); + handled++; + } + +/* Process RNIC interrupts */ + dmaisr =readl (c2dev->regs +C2_DISR ); +if (dmaisr) { +writel (dmaisr, c2dev->regs +C2_DISR ); +c2_rnic_interrupt (c2dev); + handled++; + } + +if (handled) { +return IRQ_HANDLED ; + }else { +return IRQ_NONE ; + } +} + +static int c2_up (struct net_device *netdev) +{ +struct c2_port *c2_port =netdev_priv (netdev); +struct c2_dev *c2dev = c2_port->c2dev; +struct c2_element *elem; +struct c2_rxp_hdr *rxp_hdr; +struct in_device *in_dev; +size_t rx_size, tx_size; +int ret, i; +unsigned int netimr0; + +if (netif_msg_ifup (c2_port)) +pr_debug ("%s: enabling interface\n" , netdev->name); + +/* Set the Rx buffer size based on MTU */ +c2_set_rxbufsize (c2_port); + +/* Allocate DMA'able memory for Tx/Rx host descriptor rings */ + rx_size = c2_port->rx_ring.count *sizeof (struct c2_rx_desc ); + tx_size = c2_port->tx_ring.count *sizeof (struct c2_tx_desc ); + + c2_port->mem_size = tx_size + rx_size; + c2_port->mem =pci_alloc_consistent (c2dev->pcidev, c2_port->mem_size, + &c2_port->dma); +if (c2_port->mem ==NULL ) { +pr_debug ("Unable to allocate memory for " +"host descriptor rings\n" ); +return -ENOMEM ; + } + +memset (c2_port->mem,0 , c2_port->mem_size); + +/* Create the Rx host descriptor ring */ +if ((ret = +c2_rx_ring_alloc (&c2_port->rx_ring, c2_port->mem, c2_port->dma, + c2dev->mmio_rxp_ring))) { +pr_debug ("Unable to create RX ring\n" ); + goto bail0; + } + +/* Allocate Rx buffers for the host descriptor ring */ +if (c2_rx_fill (c2_port)) { +pr_debug ("Unable to fill RX ring\n" ); + goto bail1; + } + +/* Create the Tx host descriptor ring */ +if ((ret =c2_tx_ring_alloc (&c2_port->tx_ring, c2_port->mem + rx_size, + c2_port->dma + rx_size, + c2dev->mmio_txp_ring))) { +pr_debug ("Unable to create TX ring\n" ); + goto bail1; + } + +/* Set the TX pointer to where we left off */ + c2_port->tx_avail = c2_port->tx_ring.count -1 ; + c2_port->tx_ring.to_use = c2_port->tx_ring.to_clean = + c2_port->tx_ring.start + c2dev->cur_tx; + +/* missing: Initialize MAC */ + +BUG_ON (c2_port->tx_ring.to_use != c2_port->tx_ring.to_clean); + +/* Reset the adapter, ensures the driver is in sync with the RXP */ +c2_reset (c2_port); + +/* Reset the READY bit in the sk_buff RXP headers & adapter HRXDQ */ +for (i =0 , elem = c2_port->rx_ring.start; i < c2_port->rx_ring.count; + i++, elem++) { + rxp_hdr = (struct c2_rxp_hdr *) elem->skb->data; + rxp_hdr->flags =0 ; +__raw_writew (cpu_to_be16 (RXP_HRXD_READY ), + elem->hw_desc +C2_RXP_FLAGS ); + } + +/* Enable network packets */ +netif_start_queue (netdev); + +/* Enable IRQ */ +writel (0 , c2dev->regs +C2_IDIS ); + netimr0 =readl (c2dev->regs +C2_NIMR0 ); + netimr0 &= ~(C2_PCI_HTX_INT |C2_PCI_HRX_INT ); +writel (netimr0, c2dev->regs +C2_NIMR0 ); + +/* Tell the stack to ignore arp requests for ipaddrs bound to +* other interfaces. This is needed to prevent the host stack +* from responding to arp requests to the ipaddr bound on the +* rdma interface. +*/ + in_dev =in_dev_get (netdev); + in_dev->cnf.arp_ignore =1 ; +in_dev_put (in_dev); + +return 0 ; + + bail1: +c2_rx_clean (c2_port); +kfree (c2_port->rx_ring.start); + + bail0: +pci_free_consistent (c2dev->pcidev, c2_port->mem_size, c2_port->mem, + c2_port->dma); + +return ret; +} + +static int c2_down (struct net_device *netdev) +{ +struct c2_port *c2_port =netdev_priv (netdev); +struct c2_dev *c2dev = c2_port->c2dev; + +if (netif_msg_ifdown (c2_port)) +pr_debug ("%s: disabling interface\n" , + netdev->name); + +/* Wait for all the queued packets to get sent */ +c2_tx_interrupt (netdev); + +/* Disable network packets */ +netif_stop_queue (netdev); + +/* Disable IRQs by clearing the interrupt mask */ +writel (1 , c2dev->regs +C2_IDIS ); +writel (0 , c2dev->regs +C2_NIMR0 ); + +/* missing: Stop transmitter */ + +/* missing: Stop receiver */ + +/* Reset the adapter, ensures the driver is in sync with the RXP */ +c2_reset (c2_port); + +/* missing: Turn off LEDs here */ + +/* Free all buffers in the host descriptor rings */ +c2_tx_clean (c2_port); +c2_rx_clean (c2_port); + +/* Free the host descriptor rings */ +kfree (c2_port->rx_ring.start); +kfree (c2_port->tx_ring.start); +pci_free_consistent (c2dev->pcidev, c2_port->mem_size, c2_port->mem, + c2_port->dma); + +return 0 ; +} + +static void c2_reset (struct c2_port *c2_port) +{ +struct c2_dev *c2dev = c2_port->c2dev; +unsigned int cur_rx = c2dev->cur_rx; + +/* Tell the hardware to quiesce */ +C2_SET_CUR_RX (c2dev, cur_rx |C2_PCI_HRX_QUI ); + +/* +* The hardware will reset the C2_PCI_HRX_QUI bit once +* the RXP is quiesced. Wait 2 seconds for this. +*/ +ssleep (2 ); + + cur_rx =C2_GET_CUR_RX (c2dev); + +if (cur_rx &C2_PCI_HRX_QUI ) +pr_debug ("c2_reset: failed to quiesce the hardware!\n" ); + + cur_rx &= ~C2_PCI_HRX_QUI ; + + c2dev->cur_rx = cur_rx; + +pr_debug ("Current RX: %u\n" , c2dev->cur_rx); +} + +static int c2_xmit_frame (struct sk_buff *skb,struct net_device *netdev) +{ +struct c2_port *c2_port =netdev_priv (netdev); +struct c2_dev *c2dev = c2_port->c2dev; +struct c2_ring *tx_ring = &c2_port->tx_ring; +struct c2_element *elem; +dma_addr_t mapaddr; +u32 maplen; +unsigned long flags; +unsigned int i; + +spin_lock_irqsave (&c2_port->tx_lock, flags); + +if (unlikely (c2_port->tx_avail < (skb_shinfo (skb)->nr_frags +1 ))) { +netif_stop_queue (netdev); +spin_unlock_irqrestore (&c2_port->tx_lock, flags); + +pr_debug ("%s: Tx ring full when queue awake!\n" , + netdev->name); +return NETDEV_TX_BUSY ; + } + + maplen =skb_headlen (skb); + mapaddr = +pci_map_single (c2dev->pcidev, skb->data, maplen,PCI_DMA_TODEVICE ); + + elem = tx_ring->to_use; + elem->skb = skb; + elem->mapaddr = mapaddr; + elem->maplen = maplen; + +/* Tell HW to xmit */ +__raw_writeq (cpu_to_be64 (mapaddr), elem->hw_desc +C2_TXP_ADDR ); +__raw_writew (cpu_to_be16 (maplen), elem->hw_desc +C2_TXP_LEN ); +__raw_writew (cpu_to_be16 (TXP_HTXD_READY ), elem->hw_desc +C2_TXP_FLAGS ); + + c2_port->netstats.tx_packets++; + c2_port->netstats.tx_bytes += maplen; + +/* Loop thru additional data fragments and queue them */ +if (skb_shinfo (skb)->nr_frags) { +for (i =0 ; i <skb_shinfo (skb)->nr_frags; i++) { +skb_frag_t *frag = &skb_shinfo (skb)->frags[i]; + maplen = frag->size; + mapaddr = +pci_map_page (c2dev->pcidev, frag->page, + frag->page_offset, maplen, +PCI_DMA_TODEVICE ); + + elem = elem->next; + elem->skb =NULL ; + elem->mapaddr = mapaddr; + elem->maplen = maplen; + +/* Tell HW to xmit */ +__raw_writeq (cpu_to_be64 (mapaddr), + elem->hw_desc +C2_TXP_ADDR ); +__raw_writew (cpu_to_be16 (maplen), + elem->hw_desc +C2_TXP_LEN ); +__raw_writew (cpu_to_be16 (TXP_HTXD_READY ), + elem->hw_desc +C2_TXP_FLAGS ); + + c2_port->netstats.tx_packets++; + c2_port->netstats.tx_bytes += maplen; + } + } + + tx_ring->to_use = elem->next; + c2_port->tx_avail -= (skb_shinfo (skb)->nr_frags +1 ); + +if (c2_port->tx_avail <=MAX_SKB_FRAGS +1 ) { +netif_stop_queue (netdev); +if (netif_msg_tx_queued (c2_port)) +pr_debug ("%s: transmit queue full\n" , + netdev->name); + } + +spin_unlock_irqrestore (&c2_port->tx_lock, flags); + + netdev->trans_start = jiffies; + +return NETDEV_TX_OK ; +} + +static struct net_device_stats *c2_get_stats (struct net_device *netdev) +{ +struct c2_port *c2_port =netdev_priv (netdev); + +return &c2_port->netstats; +} + +static void c2_tx_timeout (struct net_device *netdev) +{ +struct c2_port *c2_port =netdev_priv (netdev); + +if (netif_msg_timer (c2_port)) +pr_debug ("%s: tx timeout\n" , netdev->name); + +c2_tx_clean (c2_port); +} + +static int c2_change_mtu (struct net_device *netdev,int new_mtu) +{ +int ret =0 ; + +if (new_mtu <ETH_ZLEN || new_mtu >ETH_JUMBO_MTU ) +return -EINVAL ; + + netdev->mtu = new_mtu; + +if (netif_running (netdev)) { +c2_down (netdev); + +c2_up (netdev); + } + +return ret; +} + +/* Initialize network device */ +static struct net_device *c2_devinit (struct c2_dev *c2dev, +void __iomem * mmio_addr) +{ +struct c2_port *c2_port =NULL ; +struct net_device *netdev =alloc_etherdev (sizeof (*c2_port)); + +if (!netdev) { +pr_debug ("c2_port etherdev alloc failed" ); +return NULL ; + } + +SET_MODULE_OWNER (netdev); +SET_NETDEV_DEV (netdev, &c2dev->pcidev->dev); + + netdev->open = c2_up; + netdev->stop = c2_down; + netdev->hard_start_xmit = c2_xmit_frame; + netdev->get_stats = c2_get_stats; + netdev->tx_timeout = c2_tx_timeout; + netdev->change_mtu = c2_change_mtu; + netdev->watchdog_timeo =C2_TX_TIMEOUT ; + netdev->irq = c2dev->pcidev->irq; + + c2_port =netdev_priv (netdev); + c2_port->netdev = netdev; + c2_port->c2dev = c2dev; + c2_port->msg_enable =netif_msg_init (debug, default_msg); + c2_port->tx_ring.count =C2_NUM_TX_DESC ; + c2_port->rx_ring.count =C2_NUM_RX_DESC ; + +spin_lock_init (&c2_port->tx_lock); + +/* Copy our 48-bit ethernet hardware address */ +memcpy_fromio (netdev->dev_addr, mmio_addr +C2_REGS_ENADDR ,6 ); + +/* Validate the MAC address */ +if (!is_valid_ether_addr (netdev->dev_addr)) { +pr_debug ("Invalid MAC Address\n" ); +c2_print_macaddr (netdev); +free_netdev (netdev); +return NULL ; + } + + c2dev->netdev = netdev; + +return netdev; +} + +static int __devinit c2_probe (struct pci_dev *pcidev, +const struct pci_device_id *ent) +{ +int ret =0 , i; +unsigned long reg0_start, reg0_flags, reg0_len; +unsigned long reg2_start, reg2_flags, reg2_len; +unsigned long reg4_start, reg4_flags, reg4_len; +unsigned kva_map_size; +struct net_device *netdev =NULL ; +struct c2_dev *c2dev =NULL ; +void __iomem *mmio_regs =NULL ; + + printk(KERN_INFO PFX "AMSO1100 Gigabit Ethernet driver v%s loaded\n", + DRV_VERSION); + +/* Enable PCI device */ + ret =pci_enable_device (pcidev); +if (ret) { + printk(KERN_ERR PFX "%s: Unable to enable PCI device\n", + pci_name(pcidev)); + goto bail0; + } + + reg0_start =pci_resource_start (pcidev,BAR_0 ); + reg0_len =pci_resource_len (pcidev,BAR_0 ); + reg0_flags =pci_resource_flags (pcidev,BAR_0 ); + + reg2_start =pci_resource_start (pcidev,BAR_2 ); + reg2_len =pci_resource_len (pcidev,BAR_2 ); + reg2_flags =pci_resource_flags (pcidev,BAR_2 ); + + reg4_start =pci_resource_start (pcidev,BAR_4 ); + reg4_len =pci_resource_len (pcidev,BAR_4 ); + reg4_flags =pci_resource_flags (pcidev,BAR_4 ); + +pr_debug ("BAR0 size = 0x%lX bytes\n" , reg0_len); +pr_debug ("BAR2 size = 0x%lX bytes\n" , reg2_len); +pr_debug ("BAR4 size = 0x%lX bytes\n" , reg4_len); + +/* Make sure PCI base addr are MMIO */ +if (!(reg0_flags &IORESOURCE_MEM ) || + !(reg2_flags &IORESOURCE_MEM ) || !(reg4_flags &IORESOURCE_MEM )) { + printk(KERN_ERR PFX "PCI regions not an MMIO resource\n"); + ret = -ENODEV ; + goto bail1; + } + +/* Check for weird/broken PCI region reporting */ +if ((reg0_len <C2_REG0_SIZE ) || + (reg2_len <C2_REG2_SIZE ) || (reg4_len <C2_REG4_SIZE )) { + printk(KERN_ERR PFX "Invalid PCI region sizes\n"); + ret = -ENODEV ; + goto bail1; + } + +/* Reserve PCI I/O and memory resources */ + ret =pci_request_regions (pcidev,DRV_NAME ); +if (ret) { + printk(KERN_ERR PFX "%s: Unable to request regions\n", + pci_name(pcidev)); + goto bail1; + } + +if ((sizeof (dma_addr_t) >4 )) { + ret =pci_set_dma_mask (pcidev,DMA_64BIT_MASK ); +if (ret <0 ) { + printk(KERN_ERR PFX "64b DMA configuration failed\n"); + goto bail2; + } + }else { + ret =pci_set_dma_mask (pcidev,DMA_32BIT_MASK ); +if (ret <0 ) { + printk(KERN_ERR PFX "32b DMA configuration failed\n"); + goto bail2; + } + } + +/* Enables bus-mastering on the device */ +pci_set_master (pcidev); + +/* Remap the adapter PCI registers in BAR4 */ + mmio_regs =ioremap_nocache (reg4_start +C2_PCI_REGS_OFFSET , +sizeof (struct c2_adapter_pci_regs )); +if (mmio_regs ==0UL ) { + printk(KERN_ERR PFX + "Unable to remap adapter PCI registers in BAR4\n"); + ret = -EIO ; + goto bail2; + } + +/* Validate PCI regs magic */ +for (i =0 ; i <sizeof (c2_magic); i++) { +if (c2_magic[i] !=readb (mmio_regs +C2_REGS_MAGIC + i)) { + printk(KERN_ERR PFX "Downlevel Firmware boot loader " +"[%d/%Zd: got 0x%x, exp 0x%x]. Use the cc_flash " +"utility to update your boot loader\n" , + i +1 ,sizeof (c2_magic), +readb (mmio_regs +C2_REGS_MAGIC + i), + c2_magic[i]); + printk(KERN_ERR PFX "Adapter not claimed\n"); +iounmap (mmio_regs); + ret = -EIO ; + goto bail2; + } + } + +/* Validate the adapter version */ +if (be32_to_cpu (readl (mmio_regs +C2_REGS_VERS )) !=C2_VERSION ) { + printk(KERN_ERR PFX "Version mismatch " +"[fw=%u, c2=%u], Adapter not claimed\n" , +be32_to_cpu (readl (mmio_regs +C2_REGS_VERS )), + C2_VERSION); + ret = -EINVAL ; +iounmap (mmio_regs); + goto bail2; + } + +/* Validate the adapter IVN */ +if (be32_to_cpu (readl (mmio_regs +C2_REGS_IVN )) !=C2_IVN ) { + printk(KERN_ERR PFX "Downlevel FIrmware level. You should be using " +"the OpenIB device support kit. " +"[fw=0x%x, c2=0x%x], Adapter not claimed\n" , +be32_to_cpu (readl (mmio_regs +C2_REGS_IVN )), + C2_IVN); + ret = -EINVAL ; +iounmap (mmio_regs); + goto bail2; + } + +/* Allocate hardware structure */ + c2dev = (struct c2_dev *)ib_alloc_device (sizeof (*c2dev)); +if (!c2dev) { + printk(KERN_ERR PFX "%s: Unable to alloc hardware struct\n", + pci_name(pcidev)); + ret = -ENOMEM ; +iounmap (mmio_regs); + goto bail2; + } + +memset (c2dev,0 ,sizeof (*c2dev)); +spin_lock_init (&c2dev->lock); + c2dev->pcidev = pcidev; + c2dev->cur_tx =0 ; + +/* Get the last RX index */ + c2dev->cur_rx = + (be32_to_cpu (readl (mmio_regs +C2_REGS_HRX_CUR )) - +0xffffc000 ) /sizeof (struct c2_rxp_desc ); + +/* Request an interrupt line for the driver */ + ret =request_irq (pcidev->irq, c2_interrupt,SA_SHIRQ ,DRV_NAME , c2dev); +if (ret) { + printk(KERN_ERR PFX "%s: requested IRQ %u is busy\n", + pci_name(pcidev), pcidev->irq); +iounmap (mmio_regs); + goto bail3; + } + +/* Set driver specific data */ +pci_set_drvdata (pcidev, c2dev); + +/* Initialize network device */ +if ((netdev =c2_devinit (c2dev, mmio_regs)) ==NULL ) { +iounmap (mmio_regs); + goto bail4; + } + +/* Save off the actual size prior to unmapping mmio_regs */ + kva_map_size =be32_to_cpu (readl (mmio_regs +C2_REGS_PCI_WINSIZE )); + +/* Unmap the adapter PCI registers in BAR4 */ +iounmap (mmio_regs); + +/* Register network device */ + ret =register_netdev (netdev); +if (ret) { + printk(KERN_ERR PFX "Unable to register netdev, ret = %d\n", + ret); + goto bail5; + } + +/* Disable network packets */ +netif_stop_queue (netdev); + +/* Remap the adapter HRXDQ PA space to kernel VA space */ + c2dev->mmio_rxp_ring =ioremap_nocache (reg4_start +C2_RXP_HRXDQ_OFFSET , +C2_RXP_HRXDQ_SIZE ); +if (c2dev->mmio_rxp_ring ==0UL ) { + printk(KERN_ERR PFX "Unable to remap MMIO HRXDQ region\n"); + ret = -EIO ; + goto bail6; + } + +/* Remap the adapter HTXDQ PA space to kernel VA space */ + c2dev->mmio_txp_ring =ioremap_nocache (reg4_start +C2_TXP_HTXDQ_OFFSET , +C2_TXP_HTXDQ_SIZE ); +if (c2dev->mmio_txp_ring ==0UL ) { + printk(KERN_ERR PFX "Unable to remap MMIO HTXDQ region\n"); + ret = -EIO ; + goto bail7; + } + +/* Save off the current RX index in the last 4 bytes of the TXP Ring */ +C2_SET_CUR_RX (c2dev, c2dev->cur_rx); + +/* Remap the PCI registers in adapter BAR0 to kernel VA space */ + c2dev->regs =ioremap_nocache (reg0_start, reg0_len); +if (c2dev->regs ==0UL ) { + printk(KERN_ERR PFX "Unable to remap BAR0\n"); + ret = -EIO ; + goto bail8; + } + +/* Remap the PCI registers in adapter BAR4 to kernel VA space */ + c2dev->pa = reg4_start +C2_PCI_REGS_OFFSET ; + c2dev->kva =ioremap_nocache (reg4_start +C2_PCI_REGS_OFFSET , + kva_map_size); +if (c2dev->kva ==0UL ) { + printk(KERN_ERR PFX "Unable to remap BAR4\n"); + ret = -EIO ; + goto bail9; + } + +/* Print out the MAC address */ +c2_print_macaddr (netdev); + + ret =c2_rnic_init (c2dev); +if (ret) { + printk(KERN_ERR PFX "c2_rnic_init failed: %d\n", ret); + goto bail10; + } + +c2_register_device (c2dev); + +return 0 ; + + bail10: +iounmap (c2dev->kva); + + bail9: +iounmap (c2dev->regs); + + bail8: +iounmap (c2dev->mmio_txp_ring); + + bail7: +iounmap (c2dev->mmio_rxp_ring); + + bail6: +unregister_netdev (netdev); + + bail5: +free_netdev (netdev); + + bail4: +free_irq (pcidev->irq, c2dev); + + bail3: +ib_dealloc_device (&c2dev->ibdev); + + bail2: +pci_release_regions (pcidev); + + bail1: +pci_disable_device (pcidev); + + bail0: +return ret; +} + +static void __devexit c2_remove(struct pci_dev *pcidev) +{ +struct c2_dev *c2dev =pci_get_drvdata (pcidev); +struct net_device *netdev = c2dev->netdev; + +/* Unregister with OpenIB */ +c2_unregister_device (c2dev); + +/* Clean up the RNIC resources */ +c2_rnic_term (c2dev); + +/* Remove network device from the kernel */ +unregister_netdev (netdev); + +/* Free network device */ +free_netdev (netdev); + +/* Free the interrupt line */ +free_irq (pcidev->irq, c2dev); + +/* missing: Turn LEDs off here */ + +/* Unmap adapter PA space */ +iounmap (c2dev->kva); +iounmap (c2dev->regs); +iounmap (c2dev->mmio_txp_ring); +iounmap (c2dev->mmio_rxp_ring); + +/* Free the hardware structure */ +ib_dealloc_device (&c2dev->ibdev); + +/* Release reserved PCI I/O and memory resources */ +pci_release_regions (pcidev); + +/* Disable PCI device */ +pci_disable_device (pcidev); + +/* Clear driver specific data */ +pci_set_drvdata (pcidev,NULL ); +} + +static struct pci_driver c2_pci_driver = { + .name =DRV_NAME , + .id_table = c2_pci_table, + .probe = c2_probe, + .remove =__devexit_p (c2_remove), +}; + +static int __init c2_init_module (void ) +{ +return pci_module_init (&c2_pci_driver); +} + +static void __exit c2_exit_module(void) +{ +pci_unregister_driver (&c2_pci_driver); +} + +module_init (c2_init_module); +module_exit (c2_exit_module);
drivers/infiniband/hw/amso1100/c2.h new file +552 -0
552 lines not on this page · raw message
drivers/infiniband/hw/amso1100/c2_ae.c new file +321 -0
321 lines not on this page · raw message
drivers/infiniband/hw/amso1100/c2_intr.c new file +209 -0
209 lines not on this page · raw message
drivers/infiniband/hw/amso1100/c2_rnic.c new file +664 -0
663 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.
Re: [PATCH v3 1/7] AMSO1100 Low Level Driver.
drivers/infiniband/hw/amso1100/c2.c line 121
Quoted 13 lines · Steve Wise
drivers/infiniband/hw/amso1100/c2.c new file +13
-0+/* +* Allocate TX ring elements and chain them together. +* One-to-one association of adapter descriptors with ring elements. +*/ +static int c2_tx_ring_alloc (struct c2_ring *tx_ring,void *vaddr, +dma_addr_t base,void __iomem * mmio_txp_ring) +{ +struct c2_tx_desc *tx_desc; +struct c2_txp_desc __iomem *txp_desc; +struct c2_element *elem; +int i; + + tx_ring->start =kmalloc (sizeof (*elem) * tx_ring->count,GFP_KERNEL );
I would think this needs a dma_alloc_coherent() rather than a kmalloc...
Quoted 7 lines
drivers/infiniband/hw/amso1100/c2.c new file +7
-0+ +/* Free all buffers in RX ring, assumes receiver stopped */ +static void c2_rx_clean (struct c2_port *c2_port) +{ +struct c2_dev *c2dev = c2_port->c2dev; +struct c2_ring *rx_ring = &c2_port->rx_ring; +struct c2_element *elem;
drivers/infiniband/hw/amso1100/c2.c line 292
Quoted 10 lines
drivers/infiniband/hw/amso1100/c2.c new file +10
-0+struct c2_rx_desc *rx_desc; + + elem = rx_ring->start; + do { + rx_desc = elem->ht_desc; + rx_desc->len = 0; + + __raw_writew(0, elem->hw_desc + C2_RXP_STATUS); + __raw_writew(0, elem->hw_desc + C2_RXP_COUNT); + __raw_writew(0, elem->hw_desc + C2_RXP_LEN);
you seem to be a fan of the __raw_write() functions... any reason why? __raw_ is not a magic "go faster" prefix....
Also on a related note, have you checked the driver for the needed PCI posting flushes?
drivers/infiniband/hw/amso1100/c2.c line 708
Quoted 4 lines
drivers/infiniband/hw/amso1100/c2.c new file +4
-0+ +/* Disable IRQs by clearing the interrupt mask */ +writel (1 , c2dev->regs +C2_IDIS ); +writel (0 , c2dev->regs +C2_NIMR0 );
like here...
drivers/infiniband/hw/amso1100/c2.c line 792
Quoted 10 lines
drivers/infiniband/hw/amso1100/c2.c new file +10
-0+ + elem = tx_ring->to_use; + elem->skb = skb; + elem->mapaddr = mapaddr; + elem->maplen = maplen; + +/* Tell HW to xmit */ +__raw_writeq (cpu_to_be64 (mapaddr), elem->hw_desc +C2_TXP_ADDR ); +__raw_writew (cpu_to_be16 (maplen), elem->hw_desc +C2_TXP_LEN ); +__raw_writew (cpu_to_be16 (TXP_HTXD_READY ), elem->hw_desc +C2_TXP_FLAGS );
or here
Quoted 4 lines
drivers/infiniband/hw/amso1100/c2.c new file +4
-0+static int c2_change_mtu (struct net_device *netdev,int new_mtu) +{ +int ret =0 ; +
drivers/infiniband/hw/amso1100/c2.c line 872
Quoted 10 lines
drivers/infiniband/hw/amso1100/c2.c new file +10
-0+if (new_mtu <ETH_ZLEN || new_mtu >ETH_JUMBO_MTU ) +return -EINVAL ; + + netdev->mtu = new_mtu; + +if (netif_running (netdev)) { +c2_down (netdev); + +c2_up (netdev); + }
this looks odd...
Re: [PATCH v3 1/7] AMSO1100 Low Level Driver.
Quoted 18 lines · Arjan van de Ven
drivers/infiniband/hw/amso1100/c2.c line 121
Quoted 13 lines · Steve Wise
drivers/infiniband/hw/amso1100/c2.c new file +13
-0+/* +* Allocate TX ring elements and chain them together. +* One-to-one association of adapter descriptors with ring elements. +*/ +static int c2_tx_ring_alloc (struct c2_ring *tx_ring,void *vaddr, +dma_addr_t base,void __iomem * mmio_txp_ring) +{ +struct c2_tx_desc *tx_desc; +struct c2_txp_desc __iomem *txp_desc; +struct c2_element *elem; +int i; + + tx_ring->start =kmalloc (sizeof (*elem) * tx_ring->count,GFP_KERNEL );I would think this needs a
dma_alloc_coherent()rather than a kmalloc...
No, this memory is used to describe the tx ring from the host's perspective. The HW never touches this memory. The HW's TX descriptor ring is in adapter memory and is mapped into host memory (see c2dev->mmio_txp_ring).
Quoted 13 lines
Quoted 13 lines
drivers/infiniband/hw/amso1100/c2.c new file +12
-0+ +/* Free all buffers in RX ring, assumes receiver stopped */ +static void c2_rx_clean (struct c2_port *c2_port) +{ +struct c2_dev *c2dev = c2_port->c2dev; +struct c2_ring *rx_ring = &c2_port->rx_ring; +struct c2_element *elem; +struct c2_rx_desc *rx_desc; + + elem = rx_ring->start; + do { + rx_desc = elem->ht_desc;
Quoted 9 lines
drivers/infiniband/hw/amso1100/c2.c line 292
Quoted 5 lines
drivers/infiniband/hw/amso1100/c2.c new file +5
-0+ rx_desc->len =0 ; + +__raw_writew (0 , elem->hw_desc +C2_RXP_STATUS ); +__raw_writew (0 , elem->hw_desc +C2_RXP_COUNT ); +__raw_writew (0 , elem->hw_desc +C2_RXP_LEN );you seem to be a fan of the
__raw_write()functions... any reason why?__raw_is not a magic "go faster" prefix....
In this particular case, I believe this is done to avoid a swap of '0' since its not necessary. In other places, __raw is used because the adapter needs the data in BE and we want to explicitly swap it using cpu_to_be* then raw_write it to the adapter memory...
Quoted 3 lines
Also on a related note, have you checked the driver for the needed PCI posting flushes?
Um, what's a 'PCI posting flush'? Can you point me where its described/used so I can see if we need it? Thanx.
Quoted 19 lines
drivers/infiniband/hw/amso1100/c2.c line 708
Quoted 4 lines
drivers/infiniband/hw/amso1100/c2.c new file +4
-0+ +/* Disable IRQs by clearing the interrupt mask */ +writel (1 , c2dev->regs +C2_IDIS ); +writel (0 , c2dev->regs +C2_NIMR0 );like here...
drivers/infiniband/hw/amso1100/c2.c line 792
Quoted 10 lines
drivers/infiniband/hw/amso1100/c2.c new file +10
-0+ + elem = tx_ring->to_use; + elem->skb = skb; + elem->mapaddr = mapaddr; + elem->maplen = maplen; + +/* Tell HW to xmit */ +__raw_writeq (cpu_to_be64 (mapaddr), elem->hw_desc +C2_TXP_ADDR ); +__raw_writew (cpu_to_be16 (maplen), elem->hw_desc +C2_TXP_LEN ); +__raw_writew (cpu_to_be16 (TXP_HTXD_READY ), elem->hw_desc +C2_TXP_FLAGS );or here
Quoted 17 lines
Quoted 4 lines
drivers/infiniband/hw/amso1100/c2.c new file +4
-0+static int c2_change_mtu (struct net_device *netdev,int new_mtu) +{ +int ret =0 ; +drivers/infiniband/hw/amso1100/c2.c line 872
Quoted 10 lines
drivers/infiniband/hw/amso1100/c2.c new file +10
-0+if (new_mtu <ETH_ZLEN || new_mtu >ETH_JUMBO_MTU ) +return -EINVAL ; + + netdev->mtu = new_mtu; + +if (netif_running (netdev)) { +c2_down (netdev); + +c2_up (netdev); + }this looks odd...
The 1100 hardware caches the dma address of the next skb that will be used to place data. When the MTU changes, we want to free the SKBs in the RX descriptor ring and get new ones that sufficient for the new MTU. To effectively flush that cached address of the old skb, we must quiesce the HW and firmware (via c2_down()), then reinitialize everything with skb's big enough for the new mtu.
Steve.
Re: [PATCH v3 1/7] AMSO1100 Low Level Driver.
Quoted 10 lines
0;
Quoted 8 lines
drivers/infiniband/hw/amso1100/c2.c line 292
Quoted 4 lines
drivers/infiniband/hw/amso1100/c2.c new file +4
-0+ +__raw_writew (0 , elem->hw_desc +C2_RXP_STATUS ); +__raw_writew (0 , elem->hw_desc +C2_RXP_COUNT ); +__raw_writew (0 , elem->hw_desc +C2_RXP_LEN );you seem to be a fan of the
__raw_write()functions... any reason why?__raw_is not a magic "go faster" prefix....
Quoted 2 lines
In this particular case, I believe this is done to avoid a swap of '0' since its not necessary.
but.. that should writew() and co just autodetect (or do it at compile time)...
(maybe it doesn't and we have an optimization opportunity here ;)
Quoted 6 lines
Quoted 3 lines
Also on a related note, have you checked the driver for the needed PCI posting flushes?
Um, what's a 'PCI posting flush'? Can you point me where its described/used so I can see if we need it? Thanx.
ok pci posting...
basically, if you use writel() and co, the PCI bridges in the middle are allowed (and the more fancy ones do) cache the write, to see if more writes follow, so that the bridge can do the writes as a single burst to the device, rather than as individual writes. This is of course great... ... except when you really want the write to hit the device before the driver continues with other actions.
Now the PCI spec is set up such that any traffic in the other direction (basically readl() and co) will first flush the write through the system before the read is actually sent to the device, so doing a dummy readl() is a good way to flush any pending posted writes.
Where does this matter?
it matters most at places such as irq enabling/disabling, IO submission and possibly IRQ acking, but also often in eeprom-like read/write logic (where you do manual clocking and need to do delays between the write()'s). But in general... any place where you do writel() without doing any readl() before doing nothing to the card for a long time, or where you are waiting for the card to do something (or want it done NOW, such as IRQ disabling) you need to issue a (dummy) readl() to flush pending writes out to the hardware.
does this explanation make any sense? if not please feel free to ask any questions, I know I'm not always very good at explaining things.
Greetings,
Arjan van de Ven
Re: [PATCH v3 1/7] AMSO1100 Low Level Driver.
Quoted 26 lines
ok pci posting...
basically, if you use
writel()and co, the PCI bridges in the middle are allowed (and the more fancy ones do) cache the write, to see if more writes follow, so that the bridge can do the writes as a single burst to the device, rather than as individual writes. This is of course great... ... except when you really want the write to hit the device before the driver continues with other actions.Now the PCI spec is set up such that any traffic in the other direction (basically
readl()and co) will first flush the write through the system before the read is actually sent to the device, so doing a dummyreadl()is a good way to flush any pending posted writes.Where does this matter?
it matters most at places such as irq enabling/disabling, IO submission and possibly IRQ acking, but also often in eeprom-like read/write logic (where you do manual clocking and need to do delays between the write()'s). But in general... any place where you dowritel()without doing anyreadl()before doing nothing to the card for a long time, or where you are waiting for the card to do something (or want it done NOW, such as IRQ disabling) you need to issue a (dummy)readl()to flush pending writes out to the hardware.
Quoted 2 lines
does this explanation make any sense? if not please feel free to ask any questions, I know I'm not always very good at explaining things.
Yep. I get it. I believe we're ok in this respect, but I'll review the code again with an eye for this issue...
Steve.
Re: [openib-general] [PATCH v3 1/7] AMSO1100 Low Level Driver.
In reply to Steve WiseQuoted 2 lines · Steve Wise
Um, what's a 'PCI posting flush'? Can you point me where its described/used so I can see if we need it? Thanx.
I've written this up before:
http://iou.parisc-linux.org/ols_2002/4Posted_vs_Non_Posted.html
grant
Re: [PATCH v3 1/7] AMSO1100 Low Level Driver.
In reply to Arjan van de VenQuoted 11 lines
Also on a related note, have you checked the driver for the needed PCI posting flushes?
drivers/infiniband/hw/amso1100/c2.c line 708
Quoted 4 lines
drivers/infiniband/hw/amso1100/c2.c new file +4
-0+ +/* Disable IRQs by clearing the interrupt mask */ +writel (1 , c2dev->regs +C2_IDIS ); +writel (0 , c2dev->regs +C2_NIMR0 );like here...
This code is followed by a call to c2_reset(), which interacts with the firmware on the adapter to quiesce the hardware. So I don't think we need to wait here for the posted writes to flush...
Quoted 4 lines
Quoted 4 lines
drivers/infiniband/hw/amso1100/c2.c new file +4
-0+ + elem = tx_ring->to_use; + elem->skb = skb; + elem->mapaddr = mapaddr;
Quoted 9 lines
drivers/infiniband/hw/amso1100/c2.c line 792
Quoted 6 lines
drivers/infiniband/hw/amso1100/c2.c new file +6
-0+ elem->maplen = maplen; + +/* Tell HW to xmit */ +__raw_writeq (cpu_to_be64 (mapaddr), elem->hw_desc +C2_TXP_ADDR ); +__raw_writew (cpu_to_be16 (maplen), elem->hw_desc +C2_TXP_LEN ); +__raw_writew (cpu_to_be16 (TXP_HTXD_READY ), elem->hw_desc +C2_TXP_FLAGS );or here
No need here. This logic submits the packet for transmission. We don't assume it is transmitted until we (after a completion interrupt usually) read back the HTXD entry and see the TXP_HTXD_DONE bit set (see c2_tx_interrupt()).
Steve.
Re: [PATCH v3 1/7] AMSO1100 Low Level Driver.
Quoted 9 lines
Quoted 8 lines
drivers/infiniband/hw/amso1100/c2.c line 792
Quoted 5 lines
drivers/infiniband/hw/amso1100/c2.c new file +4
-0+/* Tell HW to xmit */ +__raw_writeq (cpu_to_be64 (mapaddr), elem->hw_desc +C2_TXP_ADDR ); +__raw_writew (cpu_to_be16 (maplen), elem->hw_desc +C2_TXP_LEN ); +__raw_writew (cpu_to_be16 (TXP_HTXD_READY ), elem->hw_desc +C2_TXP_FLAGS );or here
Quoted 4 lines
No need here. This logic submits the packet for transmission. We don't assume it is transmitted until we (after a completion interrupt usually) read back the HTXD entry and see the
TXP_HTXD_DONEbit set (seec2_tx_interrupt()).
... but will that interrupt happen at all if these 3 writes never hit the hardware?
Re: [PATCH v3 1/7] AMSO1100 Low Level Driver.
Quoted 8 lines · Arjan van de Ven
Quoted 8 lines
Quoted 7 lines
drivers/infiniband/hw/amso1100/c2.c line 792
Quoted 4 lines
drivers/infiniband/hw/amso1100/c2.c new file +4
-0+/* Tell HW to xmit */ +__raw_writeq (cpu_to_be64 (mapaddr), elem->hw_desc +C2_TXP_ADDR ); +__raw_writew (cpu_to_be16 (maplen), elem->hw_desc +C2_TXP_LEN ); +__raw_writew (cpu_to_be16 (TXP_HTXD_READY ), elem->hw_desc +C2_TXP_FLAGS );or here
Quoted 8 lines
Quoted 4 lines
No need here. This logic submits the packet for transmission. We don't assume it is transmitted until we (after a completion interrupt usually) read back the HTXD entry and see the
TXP_HTXD_DONEbit set (seec2_tx_interrupt()).... but will that interrupt happen at all if these 3 writes never hit the hardware?
I thought the posted write WILL eventually get to adapter memory. Not stall forever cached in a bridge. I'm wrong?
My point is for a given HTXD entry, we write it to post a packet for transmission, then only free the packet memory and reuse this entry _after_ reading the HTXD and seeing the DONE bit set. So I still don't see a problem. But I've been wrong before ;-)
Steve.
Re: [PATCH v3 1/7] AMSO1100 Low Level Driver.
Quoted 14 lines · Steve Wise
Quoted 13 lines · Arjan van de Ven
Quoted 12 lines
Quoted 7 lines
drivers/infiniband/hw/amso1100/c2.c line 792
Quoted 4 lines
drivers/infiniband/hw/amso1100/c2.c new file +4
-0+/* Tell HW to xmit */ +__raw_writeq (cpu_to_be64 (mapaddr), elem->hw_desc +C2_TXP_ADDR ); +__raw_writew (cpu_to_be16 (maplen), elem->hw_desc +C2_TXP_LEN ); +__raw_writew (cpu_to_be16 (TXP_HTXD_READY ), elem->hw_desc +C2_TXP_FLAGS );or here
No need here. This logic submits the packet for transmission. We don't assume it is transmitted until we (after a completion interrupt usually) read back the HTXD entry and see the
TXP_HTXD_DONEbit set (seec2_tx_interrupt()).
Quoted 6 lines
Quoted 3 lines
... but will that interrupt happen at all if these 3 writes never hit the hardware?
I thought the posted write WILL eventually get to adapter memory. Not stall forever cached in a bridge. I'm wrong?
I'm not sure there is a theoretical upper bound....
(and if it's several msec per bridge, then you have a lot of latency anyway)
Re: [openib-general] [PATCH v3 1/7] AMSO1100 Low Level Driver.
Quoted 4 lines · Arjan van de Ven
Quoted 2 lines
I thought the posted write WILL eventually get to adapter memory. Not stall forever cached in a bridge. I'm wrong?
I'm not sure there is a theoretical upper bound....
I'm not aware of one either since MMIO writes can travel across many other chips that are not constrained by PCI ordering rules (I'm thinking of SGI Altix...)
Quoted 2 lines
(and if it's several msec per bridge, then you have a lot of latency anyway)
That's what my original concern was when I saw you point this out. But MMIO reads here would be expensive and many drivers tolerate this latency in exchange for avoiding the MMIO read in the performance path.
grant
Re: [PATCH v3 1/7] AMSO1100 Low Level Driver.
Quoted 9 lines · Grant Grundler
Quoted 4 lines · Arjan van de Ven
Quoted 2 lines
I thought the posted write WILL eventually get to adapter memory. Not stall forever cached in a bridge. I'm wrong?
I'm not sure there is a theoretical upper bound....
I'm not aware of one either since MMIO writes can travel across many other chips that are not constrained by PCI ordering rules (I'm thinking of SGI Altix...)
It is processor / coherency backplane technology specific as to the number of outstanding writes. There is also no guarantee that such writes will hit the top of the PCI hierarchy in the order they were posted in a multi-core / processor system. Hence, it is up to software to guarantee that ordering is preserved and to not assume anything about ordering from a hardware perspective. Once a transaction hits the PCI hierarchy, then the PCI ordering rules apply and depending upon the transaction type and other rules, what is guaranteed is deterministic in nature.
Quoted 7 lines
Quoted 2 lines
(and if it's several msec per bridge, then you have a lot of latency anyway)
That's what my original concern was when I saw you point this out. But MMIO reads here would be expensive and many drivers tolerate this latency in exchange for avoiding the MMIO read in the performance path.
As the saying goes, MMIO Reads are "pure evil" and should be avoided at all costs if performance is the goal. Even in a relatively flat I/O hierarchy, the additional latency is non-trivial and can lead to a significant loss in performance for the system.
Mike
PATCH v3 2/7 AMSO1100 WR / Event Definitions.
In reply to Steve WiseReview Changes:
- C2_DEBUG -> DEBUG
- removed useless comments
---
drivers/infiniband/hw/amso1100/c2_ae.h | 108 ++ drivers/infiniband/hw/amso1100/c2_status.h | 158 +++ drivers/infiniband/hw/amso1100/c2_wr.h | 1520 ++++++++++++++++++++++++++++ 3 files changed, 1786 insertions(+), 0 deletions(-)
drivers/infiniband/hw/amso1100/c2_ae.h new file +108 -0
@@ -0,0 +1,108 @@ +/* +* Copyright (c) 2005 Ammasso, Inc. All rights reserved. +* Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - 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. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ +#ifndef _C2_AE_H_ +#define _C2_AE_H_ + +/* +* WARNING: If you change this file, also bump C2_IVN_BASE +* in common/include/clustercore/c2_ivn.h. +*/ + +/* +* Asynchronous Event Identifiers +* +* These start at 0x80 only so it's obvious from inspection that +* they are not work-request statuses. This isn't critical. +* +* NOTE: these event id's must fit in eight bits. +*/ +enum c2_event_id { +CCAE_REMOTE_SHUTDOWN =0x80 , +CCAE_ACTIVE_CONNECT_RESULTS , +CCAE_CONNECTION_REQUEST , +CCAE_LLP_CLOSE_COMPLETE , +CCAE_TERMINATE_MESSAGE_RECEIVED , +CCAE_LLP_CONNECTION_RESET , +CCAE_LLP_CONNECTION_LOST , +CCAE_LLP_SEGMENT_SIZE_INVALID , +CCAE_LLP_INVALID_CRC , +CCAE_LLP_BAD_FPDU , +CCAE_INVALID_DDP_VERSION , +CCAE_INVALID_RDMA_VERSION , +CCAE_UNEXPECTED_OPCODE , +CCAE_INVALID_DDP_QUEUE_NUMBER , +CCAE_RDMA_READ_NOT_ENABLED , +CCAE_RDMA_WRITE_NOT_ENABLED , +CCAE_RDMA_READ_TOO_SMALL , +CCAE_NO_L_BIT , +CCAE_TAGGED_INVALID_STAG , +CCAE_TAGGED_BASE_BOUNDS_VIOLATION , +CCAE_TAGGED_ACCESS_RIGHTS_VIOLATION , +CCAE_TAGGED_INVALID_PD , +CCAE_WRAP_ERROR , +CCAE_BAD_CLOSE , +CCAE_BAD_LLP_CLOSE , +CCAE_INVALID_MSN_RANGE , +CCAE_INVALID_MSN_GAP , +CCAE_IRRQ_OVERFLOW , +CCAE_IRRQ_MSN_GAP , +CCAE_IRRQ_MSN_RANGE , +CCAE_IRRQ_INVALID_STAG , +CCAE_IRRQ_BASE_BOUNDS_VIOLATION , +CCAE_IRRQ_ACCESS_RIGHTS_VIOLATION , +CCAE_IRRQ_INVALID_PD , +CCAE_IRRQ_WRAP_ERROR , +CCAE_CQ_SQ_COMPLETION_OVERFLOW , +CCAE_CQ_RQ_COMPLETION_ERROR , +CCAE_QP_SRQ_WQE_ERROR , +CCAE_QP_LOCAL_CATASTROPHIC_ERROR , +CCAE_CQ_OVERFLOW , +CCAE_CQ_OPERATION_ERROR , +CCAE_SRQ_LIMIT_REACHED , +CCAE_QP_RQ_LIMIT_REACHED , +CCAE_SRQ_CATASTROPHIC_ERROR , +CCAE_RNIC_CATASTROPHIC_ERROR +/* WARNING If you add more id's, make sure their values fit in eight bits. */ +}; + +/* +* Resource Indicators and Identifiers +*/ +enum c2_resource_indicator { +C2_RES_IND_QP =1 , +C2_RES_IND_EP , +C2_RES_IND_CQ , +C2_RES_IND_SRQ , +}; + +#endif /* _C2_AE_H_ */
drivers/infiniband/hw/amso1100/c2_status.h new file +158 -0
158 lines not on this page · raw message
drivers/infiniband/hw/amso1100/c2_wr.h new file +1520 -0
1,520 lines not on this page · raw message
PATCH v3 2/7 AMSO1100 WR / Event Definitions.
In reply to Steve Wise2/7 gzipped & attached cuz it it gets dropped by lklm and netdev lists...
Steve.
PATCH v3 3/7 AMSO1100 OpenFabrics Provider.
In reply to Steve WiseV2 Review Changes:
- removed useless atomic_t in c2_pd struct.
- qp ids now allocated and mapped using IDR
- pd ids now allocated using private bitarrary allocator.
- correctly map host memory for DMA (don't use __pa()).
V1 Review Changes:
- sizeof -> sizeof()
- dprintk() -> pr_debug()
- assert() -> BUG_ON()
- C2_DEBUG -> DEBUG
- cleaned up comments
---
drivers/infiniband/hw/amso1100/c2_cm.c | 452 ++++++++++++ drivers/infiniband/hw/amso1100/c2_cq.c | 433 ++++++++++++ drivers/infiniband/hw/amso1100/c2_pd.c | 89 ++ drivers/infiniband/hw/amso1100/c2_provider.c | 867 +++++++++++++++++++++++ drivers/infiniband/hw/amso1100/c2_provider.h | 181 +++++ drivers/infiniband/hw/amso1100/c2_qp.c | 975 ++++++++++++++++++++++++++ drivers/infiniband/hw/amso1100/c2_user.h | 82 ++ 7 files changed, 3079 insertions(+), 0 deletions(-)
drivers/infiniband/hw/amso1100/c2_cm.c new file +452 -0
@@ -0,0 +1,452 @@ +/* +* Copyright (c) 2005 Ammasso, Inc. All rights reserved. +* Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - 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. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +* +*/ +#include "c2.h" +#include "c2_wr.h" +#include "c2_vq.h" +#include <rdma/iw_cm.h> + +int c2_llp_connect (struct iw_cm_id *cm_id,struct iw_cm_conn_param *iw_param) +{ +struct c2_dev *c2dev =to_c2dev (cm_id->device); +struct ib_qp *ibqp; +struct c2_qp *qp; +struct c2wr_qp_connect_req *wr;/* variable size needs a malloc. */ +struct c2_vq_req *vq_req; +int err; + + ibqp =c2_get_qp (cm_id->device, iw_param->qpn); +if (!ibqp) +return -EINVAL ; + qp =to_c2qp (ibqp); + +/* Associate QP <--> CM_ID */ + cm_id->provider_data = qp; + cm_id->add_ref (cm_id); + qp->cm_id = cm_id; + +/* +* only support the max private_data length +*/ +if (iw_param->private_data_len >C2_MAX_PRIVATE_DATA_SIZE ) { + err = -EINVAL ; + goto bail0; + } +/* +* Set the rdma read limits +*/ + err =c2_qp_set_read_limits (c2dev, qp, iw_param->ord, iw_param->ird); +if (err) + goto bail0; + +/* +* Create and send a WR_QP_CONNECT... +*/ + wr =kmalloc (c2dev->req_vq.msg_size,GFP_KERNEL ); +if (!wr) { + err = -ENOMEM ; + goto bail0; + } + + vq_req =vq_req_alloc (c2dev); +if (!vq_req) { + err = -ENOMEM ; + goto bail1; + } + +c2_wr_set_id (wr,CCWR_QP_CONNECT ); + wr->hdr.context =0 ; + wr->rnic_handle = c2dev->adapter_handle; + wr->qp_handle = qp->adapter_handle; + + wr->remote_addr = cm_id->remote_addr.sin_addr.s_addr; + wr->remote_port = cm_id->remote_addr.sin_port; + +/* +* Move any private data from the callers's buf into +* the WR. +*/ +if (iw_param->private_data) { + wr->private_data_length = +cpu_to_be32 (iw_param->private_data_len); +memcpy (&wr->private_data[0 ], iw_param->private_data, + iw_param->private_data_len); + }else + wr->private_data_length =0 ; + +/* +* Send WR to adapter. NOTE: There is no synch reply from +* the adapter. +*/ + err =vq_send_wr (c2dev, (union c2wr *) wr); +vq_req_free (c2dev, vq_req); + + bail1: +kfree (wr); + bail0: +if (err) { +/* +* If we fail, release reference on QP and +* disassociate QP from CM_ID +*/ + cm_id->provider_data =NULL ; + qp->cm_id =NULL ; + cm_id->rem_ref (cm_id); + } +return err; +} + +int c2_llp_service_create (struct iw_cm_id *cm_id,int backlog) +{ +struct c2_dev *c2dev; +struct c2wr_ep_listen_create_req wr; +struct c2wr_ep_listen_create_rep *reply; +struct c2_vq_req *vq_req; +int err; + + c2dev =to_c2dev (cm_id->device); +if (c2dev ==NULL ) +return -EINVAL ; + +/* +* Allocate verbs request. +*/ + vq_req =vq_req_alloc (c2dev); +if (!vq_req) +return -ENOMEM ; + +/* +* Build the WR +*/ +c2_wr_set_id (&wr,CCWR_EP_LISTEN_CREATE ); + wr.hdr.context = (u64 ) (unsigned long ) vq_req; + wr.rnic_handle = c2dev->adapter_handle; + wr.local_addr = cm_id->local_addr.sin_addr.s_addr; + wr.local_port = cm_id->local_addr.sin_port; + wr.backlog =cpu_to_be32 (backlog); + wr.user_context = (u64 ) (unsigned long ) cm_id; + +/* +* Reference the request struct. Dereferenced in the int handler. +*/ +vq_req_get (c2dev, vq_req); + +/* +* Send WR to adapter +*/ + err =vq_send_wr (c2dev, (union c2wr *) & wr); +if (err) { +vq_req_put (c2dev, vq_req); + goto bail0; + } + +/* +* Wait for reply from adapter +*/ + err =vq_wait_for_reply (c2dev, vq_req); +if (err) + goto bail0; + +/* +* Process reply +*/ + reply = + (struct c2wr_ep_listen_create_rep *) (unsigned long ) vq_req->reply_msg; +if (!reply) { + err = -ENOMEM ; + goto bail1; + } + +if ((err =c2_errno (reply)) !=0 ) + goto bail1; + +/* +* Keep the adapter handle. Used in subsequent destroy +*/ + cm_id->provider_data = (void *)(unsigned long ) reply->ep_handle; + +/* +* free vq stuff +*/ +vq_repbuf_free (c2dev, reply); +vq_req_free (c2dev, vq_req); + +return 0 ; + + bail1: +vq_repbuf_free (c2dev, reply); + bail0: +vq_req_free (c2dev, vq_req); +return err; +} + + +int c2_llp_service_destroy (struct iw_cm_id *cm_id) +{ + +struct c2_dev *c2dev; +struct c2wr_ep_listen_destroy_req wr; +struct c2wr_ep_listen_destroy_rep *reply; +struct c2_vq_req *vq_req; +int err; + + c2dev =to_c2dev (cm_id->device); +if (c2dev ==NULL ) +return -EINVAL ; + +/* +* Allocate verbs request. +*/ + vq_req =vq_req_alloc (c2dev); +if (!vq_req) +return -ENOMEM ; + +/* +* Build the WR +*/ +c2_wr_set_id (&wr,CCWR_EP_LISTEN_DESTROY ); + wr.hdr.context = (unsigned long ) vq_req; + wr.rnic_handle = c2dev->adapter_handle; + wr.ep_handle = (u32 )(unsigned long )cm_id->provider_data; + +/* +* reference the request struct. dereferenced in the int handler. +*/ +vq_req_get (c2dev, vq_req); + +/* +* Send WR to adapter +*/ + err =vq_send_wr (c2dev, (union c2wr *) & wr); +if (err) { +vq_req_put (c2dev, vq_req); + goto bail0; + } + +/* +* Wait for reply from adapter +*/ + err =vq_wait_for_reply (c2dev, vq_req); +if (err) + goto bail0; + +/* +* Process reply +*/ + reply=(struct c2wr_ep_listen_destroy_rep *)(unsigned long )vq_req->reply_msg; +if (!reply) { + err = -ENOMEM ; + goto bail0; + } +if ((err =c2_errno (reply)) !=0 ) + goto bail1; + + bail1: +vq_repbuf_free (c2dev, reply); + bail0: +vq_req_free (c2dev, vq_req); +return err; +} + +int c2_llp_accept (struct iw_cm_id *cm_id,struct iw_cm_conn_param *iw_param) +{ +struct c2_dev *c2dev =to_c2dev (cm_id->device); +struct c2_qp *qp; +struct ib_qp *ibqp; +struct c2wr_cr_accept_req *wr;/* variable length WR */ +struct c2_vq_req *vq_req; +struct c2wr_cr_accept_rep *reply;/* VQ Reply msg ptr. */ +int err; + + ibqp =c2_get_qp (cm_id->device, iw_param->qpn); +if (!ibqp) +return -EINVAL ; + qp =to_c2qp (ibqp); + +/* Set the RDMA read limits */ + err =c2_qp_set_read_limits (c2dev, qp, iw_param->ord, iw_param->ird); +if (err) + goto bail0; + +/* Allocate verbs request. */ + vq_req =vq_req_alloc (c2dev); +if (!vq_req) { + err = -ENOMEM ; + goto bail1; + } + vq_req->qp = qp; + vq_req->cm_id = cm_id; + vq_req->event =IW_CM_EVENT_ESTABLISHED ; + + wr =kmalloc (c2dev->req_vq.msg_size,GFP_KERNEL ); +if (!wr) { + err = -ENOMEM ; + goto bail2; + } + +/* Build the WR */ +c2_wr_set_id (wr,CCWR_CR_ACCEPT ); + wr->hdr.context = (unsigned long ) vq_req; + wr->rnic_handle = c2dev->adapter_handle; + wr->ep_handle = (u32 ) (unsigned long ) cm_id->provider_data; + wr->qp_handle = qp->adapter_handle; + +/* Replace the cr_handle with the QP after accept */ + cm_id->provider_data = qp; + cm_id->add_ref (cm_id); + qp->cm_id = cm_id; + + cm_id->provider_data = qp; + +/* Validate private_data length */ +if (iw_param->private_data_len >C2_MAX_PRIVATE_DATA_SIZE ) { + err = -EINVAL ; + goto bail2; + } + +if (iw_param->private_data) { + wr->private_data_length =cpu_to_be32 (iw_param->private_data_len); +memcpy (&wr->private_data[0 ], + iw_param->private_data, iw_param->private_data_len); + }else + wr->private_data_length =0 ; + +/* Reference the request struct. Dereferenced in the int handler. */ +vq_req_get (c2dev, vq_req); + +/* Send WR to adapter */ + err =vq_send_wr (c2dev, (union c2wr *) wr); +if (err) { +vq_req_put (c2dev, vq_req); + goto bail2; + } + +/* Wait for reply from adapter */ + err =vq_wait_for_reply (c2dev, vq_req); +if (err) + goto bail2; + +/* Check that reply is present */ + reply = (struct c2wr_cr_accept_rep *) (unsigned long ) vq_req->reply_msg; +if (!reply) { + err = -ENOMEM ; + goto bail2; + } + + err =c2_errno (reply); +vq_repbuf_free (c2dev, reply); + +if (!err) +c2_set_qp_state (qp,C2_QP_STATE_RTS ); + bail2: +kfree (wr); + bail1: +vq_req_free (c2dev, vq_req); + bail0: +if (err) { +/* +* If we fail, release reference on QP and +* disassociate QP from CM_ID +*/ + cm_id->provider_data =NULL ; + qp->cm_id =NULL ; + cm_id->rem_ref (cm_id); + } +return err; +} + +int c2_llp_reject (struct iw_cm_id *cm_id,const void *pdata,u8 pdata_len) +{ +struct c2_dev *c2dev; +struct c2wr_cr_reject_req wr; +struct c2_vq_req *vq_req; +struct c2wr_cr_reject_rep *reply; +int err; + + c2dev =to_c2dev (cm_id->device); + +/* +* Allocate verbs request. +*/ + vq_req =vq_req_alloc (c2dev); +if (!vq_req) +return -ENOMEM ; + +/* +* Build the WR +*/ +c2_wr_set_id (&wr,CCWR_CR_REJECT ); + wr.hdr.context = (unsigned long ) vq_req; + wr.rnic_handle = c2dev->adapter_handle; + wr.ep_handle = (u32 ) (unsigned long ) cm_id->provider_data; + +/* +* reference the request struct. dereferenced in the int handler. +*/ +vq_req_get (c2dev, vq_req); + +/* +* Send WR to adapter +*/ + err =vq_send_wr (c2dev, (union c2wr *) & wr); +if (err) { +vq_req_put (c2dev, vq_req); + goto bail0; + } + +/* +* Wait for reply from adapter +*/ + err =vq_wait_for_reply (c2dev, vq_req); +if (err) + goto bail0; + +/* +* Process reply +*/ + reply = (struct c2wr_cr_reject_rep *) (unsigned long ) + vq_req->reply_msg; +if (!reply) { + err = -ENOMEM ; + goto bail0; + } + err =c2_errno (reply); +/* +* free vq stuff +*/ +vq_repbuf_free (c2dev, reply); + + bail0: +vq_req_free (c2dev, vq_req); +return err; +}
drivers/infiniband/hw/amso1100/c2_cq.c new file +433 -0
433 lines not on this page · raw message
drivers/infiniband/hw/amso1100/c2_pd.c new file +89 -0
89 lines not on this page · raw message
drivers/infiniband/hw/amso1100/c2_provider.c new file +867 -0
867 lines not on this page · raw message
drivers/infiniband/hw/amso1100/c2_provider.h new file +181 -0
181 lines not on this page · raw message
drivers/infiniband/hw/amso1100/c2_qp.c new file +975 -0
975 lines not on this page · raw message
drivers/infiniband/hw/amso1100/c2_user.h new file +82 -0
3 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.
PATCH v3 4/7 AMSO1100 Memory Management.
In reply to Steve WiseV2 Review Changes:
- removed c2_array services and replaced them with the idr.
- removed c2_alloc services and made them pd-specific.
- don't use GFP_DMA.
- correctly map host memory for DMA (don't use __pa()).
V1 Review Changes:
- sizeof -> sizeof()
- cleaned up comments
---
drivers/infiniband/hw/amso1100/c2_alloc.c | 144 +++++++++++ drivers/infiniband/hw/amso1100/c2_mm.c | 375 +++++++++++++++++++++++++++++ 2 files changed, 519 insertions(+), 0 deletions(-)
drivers/infiniband/hw/amso1100/c2_alloc.c new file +144 -0
@@ -0,0 +1,144 @@ +/* +* Copyright (c) 2004 Topspin Communications. All rights reserved. +* Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - 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. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/bitmap.h> + +#include "c2.h" + +static int c2_alloc_mqsp_chunk (struct c2_dev *c2dev,gfp_t gfp_mask, +struct sp_chunk **head) +{ +int i; +struct sp_chunk *new_head; + + new_head = (struct sp_chunk *)__get_free_page (gfp_mask); +if (new_head ==NULL ) +return -ENOMEM ; + + new_head->dma_addr =dma_map_single (c2dev->ibdev.dma_device, new_head, +PAGE_SIZE ,DMA_FROM_DEVICE ); +pci_unmap_addr_set (new_head, mapping, new_head->dma_addr); + + new_head->next =NULL ; + new_head->head =0 ; + +/* build list where each index is the next free slot */ +for (i =0 ; + i < (PAGE_SIZE -sizeof (struct sp_chunk ) - +sizeof (u16)) /sizeof (u16) -1 ; + i++) { + new_head->shared_ptr[i] = i +1 ; + } +/* terminate list */ + new_head->shared_ptr[i] =0xFFFF ; + + *head = new_head; +return 0 ; +} + +int c2_init_mqsp_pool (struct c2_dev *c2dev,gfp_t gfp_mask, +struct sp_chunk **root) +{ +return c2_alloc_mqsp_chunk (c2dev, gfp_mask, root); +} + +void c2_free_mqsp_pool (struct c2_dev *c2dev,struct sp_chunk *root) +{ +struct sp_chunk *next; + +while (root) { + next = root->next; +dma_unmap_single (c2dev->ibdev.dma_device, +pci_unmap_addr (root, mapping),PAGE_SIZE , +DMA_FROM_DEVICE ); +__free_page ((struct page *) root); + root = next; + } +} + +u16 *c2_alloc_mqsp (struct c2_dev *c2dev,struct sp_chunk *head, +dma_addr_t *dma_addr,gfp_t gfp_mask) +{ +u16 mqsp; + +while (head) { + mqsp = head->head; +if (mqsp !=0xFFFF ) { + head->head = head->shared_ptr[mqsp]; +break ; + }else if (head->next ==NULL ) { +if (c2_alloc_mqsp_chunk (c2dev, gfp_mask, &head->next) == +0 ) { + head = head->next; + mqsp = head->head; + head->head = head->shared_ptr[mqsp]; +break ; + }else +return NULL ; + }else + head = head->next; + } +if (head) { + *dma_addr = head->dma_addr + + ((unsigned long ) &(head->shared_ptr[mqsp]) - + (unsigned long ) head); +pr_debug ("%s addr %p dma_addr %llx\n" , __FUNCTION__, + &(head->shared_ptr[mqsp]), (u64)*dma_addr); +return &(head->shared_ptr[mqsp]); + } +return NULL ; +} + +void c2_free_mqsp (u16 * mqsp) +{ +struct sp_chunk *head; +u16 idx; + +/* The chunk containing this ptr begins at the page boundary */ + head = (struct sp_chunk *) ((unsigned long ) mqsp &PAGE_MASK ); + +/* Link head to new mqsp */ + *mqsp = head->head; + +/* Compute the shared_ptr index */ + idx = ((unsigned long ) mqsp & ~PAGE_MASK ) >>1 ; + idx -= (unsigned long ) &(((struct sp_chunk *)0 )->shared_ptr[0 ]) >>1 ; + +/* Point this index at the head */ + head->shared_ptr[idx] = head->head; + +/* Point head at this index */ + head->head = idx; +}
drivers/infiniband/hw/amso1100/c2_mm.c new file +375 -0
375 lines not on this page · raw message
PATCH v3 5/7 AMSO1100 Message Queues.
In reply to Steve WiseV2 Review Changes:
- correctly map host memory for DMA (don't use __pa()).
V1 Review Changes:
- remove useless asserts
- assert() -> BUG_ON()
- C2_DEBUG -> DEBUG
---
drivers/infiniband/hw/amso1100/c2_mq.c | 175 ++++++++++++++++++++++++++++++++ drivers/infiniband/hw/amso1100/c2_mq.h | 107 ++++++++++++++++++++ 2 files changed, 282 insertions(+), 0 deletions(-)
drivers/infiniband/hw/amso1100/c2_mq.c new file +175 -0
@@ -0,0 +1,175 @@ +/* +* Copyright (c) 2005 Ammasso, Inc. All rights reserved. +* Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - 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. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ +#include "c2.h" +#include "c2_mq.h" + +void *c2_mq_alloc (struct c2_mq *q) +{ +BUG_ON (q->magic !=C2_MQ_MAGIC ); +BUG_ON (q->type !=C2_MQ_ADAPTER_TARGET ); + +if (c2_mq_full (q)) { +return NULL ; + }else { +#ifdef DEBUG +struct c2wr_hdr *m = + (struct c2wr_hdr *) (q->msg_pool.host + q->priv * q->msg_size); +#ifdef CCMSGMAGIC +BUG_ON (m->magic !=be32_to_cpu (~CCWR_MAGIC )); + m->magic =cpu_to_be32 (CCWR_MAGIC ); +#endif +return m; +#else +return q->msg_pool.host + q->priv * q->msg_size; +#endif + } +} + +void c2_mq_produce (struct c2_mq *q) +{ +BUG_ON (q->magic !=C2_MQ_MAGIC ); +BUG_ON (q->type !=C2_MQ_ADAPTER_TARGET ); + +if (!c2_mq_full (q)) { + q->priv = (q->priv +1 ) % q->q_size; + q->hint_count++; +/* Update peer's offset. */ +__raw_writew (cpu_to_be16 (q->priv), &q->peer->shared); + } +} + +void *c2_mq_consume (struct c2_mq *q) +{ +BUG_ON (q->magic !=C2_MQ_MAGIC ); +BUG_ON (q->type !=C2_MQ_HOST_TARGET ); + +if (c2_mq_empty (q)) { +return NULL ; + }else { +#ifdef DEBUG +struct c2wr_hdr *m = (struct c2wr_hdr *) + (q->msg_pool.host + q->priv * q->msg_size); +#ifdef CCMSGMAGIC +BUG_ON (m->magic !=be32_to_cpu (CCWR_MAGIC )); +#endif +return m; +#else +return q->msg_pool.host + q->priv * q->msg_size; +#endif + } +} + +void c2_mq_free (struct c2_mq *q) +{ +BUG_ON (q->magic !=C2_MQ_MAGIC ); +BUG_ON (q->type !=C2_MQ_HOST_TARGET ); + +if (!c2_mq_empty (q)) { + +#ifdef CCMSGMAGIC + { +struct c2wr_hdr __iomem *m = (struct c2wr_hdr __iomem *) + (q->msg_pool.adapter + q->priv * q->msg_size); +__raw_writel (cpu_to_be32 (~CCWR_MAGIC ), &m->magic); + } +#endif + q->priv = (q->priv +1 ) % q->q_size; +/* Update peer's offset. */ +__raw_writew (cpu_to_be16 (q->priv), &q->peer->shared); + } +} + + +void c2_mq_lconsume (struct c2_mq *q,u32 wqe_count) +{ +BUG_ON (q->magic !=C2_MQ_MAGIC ); +BUG_ON (q->type !=C2_MQ_ADAPTER_TARGET ); + +while (wqe_count--) { +BUG_ON (c2_mq_empty (q)); + *q->shared =cpu_to_be16 ((be16_to_cpu (*q->shared)+1 ) % q->q_size); + } +} + + +u32 c2_mq_count (struct c2_mq *q) +{ +s32 count; + +if (q->type ==C2_MQ_HOST_TARGET ) { + count =be16_to_cpu (*q->shared) - q->priv; + }else { + count = q->priv -be16_to_cpu (*q->shared); + } + +if (count <0 ) { + count += q->q_size; + } + +return (u32 ) count; +} + +void c2_mq_req_init (struct c2_mq *q,u32 index,u32 q_size,u32 msg_size, +u8 __iomem *pool_start,u16 __iomem *peer,u32 type) +{ +BUG_ON (!q->shared); + +/* This code assumes the byte swapping has already been done! */ + q->index = index; + q->q_size = q_size; + q->msg_size = msg_size; + q->msg_pool.adapter = pool_start; + q->peer = (struct c2_mq_shared __iomem *) peer; + q->magic =C2_MQ_MAGIC ; + q->type = type; + q->priv =0 ; + q->hint_count =0 ; +return ; +} +void c2_mq_rep_init (struct c2_mq *q,u32 index,u32 q_size,u32 msg_size, +u8 *pool_start,u16 __iomem *peer,u32 type) +{ +BUG_ON (!q->shared); + +/* This code assumes the byte swapping has already been done! */ + q->index = index; + q->q_size = q_size; + q->msg_size = msg_size; + q->msg_pool.host = pool_start; + q->peer = (struct c2_mq_shared __iomem *) peer; + q->magic =C2_MQ_MAGIC ; + q->type = type; + q->priv =0 ; + q->hint_count =0 ; +return ; +}
drivers/infiniband/hw/amso1100/c2_mq.h new file +107 -0
107 lines not on this page · raw message
PATCH v3 6/7 AMSO1100: Privileged Verbs Queues.
In reply to Steve WiseReview Changes:
dprintk() -> pr_debug()
---
drivers/infiniband/hw/amso1100/c2_vq.c | 260 ++++++++++++++++++++++++++++++++ drivers/infiniband/hw/amso1100/c2_vq.h | 63 ++++++++ 2 files changed, 323 insertions(+), 0 deletions(-)
drivers/infiniband/hw/amso1100/c2_vq.c new file +260 -0
@@ -0,0 +1,260 @@ +/* +* Copyright (c) 2005 Ammasso, Inc. All rights reserved. +* Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. +* +* This software is available to you under a choice of one of two +* licenses. You may choose to be licensed under the terms of the GNU +* General Public License (GPL) Version 2, available from the file +* COPYING in the main directory of this source tree, or the +* OpenIB.org BSD license below: +* +* Redistribution and use in source and binary forms, with or +* without modification, are permitted provided that the following +* conditions are met: +* +* - Redistributions of source code must retain the above +* copyright notice, this list of conditions and the following +* disclaimer. +* +* - 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. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include "c2_vq.h" +#include "c2_provider.h" + +/* +* Verbs Request Objects: +* +* VQ Request Objects are allocated by the kernel verbs handlers. +* They contain a wait object, a refcnt, an atomic bool indicating that the +* adapter has replied, and a copy of the verb reply work request. +* A pointer to the VQ Request Object is passed down in the context +* field of the work request message, and reflected back by the adapter +* in the verbs reply message. The function handle_vq() in the interrupt +* path will use this pointer to: +* 1) append a copy of the verbs reply message +* 2) mark that the reply is ready +* 3) wake up the kernel verbs handler blocked awaiting the reply. +* +* +* The kernel verbs handlers do a "get" to put a 2nd reference on the +* VQ Request object. If the kernel verbs handler exits before the adapter +* can respond, this extra reference will keep the VQ Request object around +* until the adapter's reply can be processed. The reason we need this is +* because a pointer to this object is stuffed into the context field of +* the verbs work request message, and reflected back in the reply message. +* It is used in the interrupt handler (handle_vq()) to wake up the appropriate +* kernel verb handler that is blocked awaiting the verb reply. +* So handle_vq() will do a "put" on the object when it's done accessing it. +* NOTE: If we guarantee that the kernel verb handler will never bail before +* getting the reply, then we don't need these refcnts. +* +* +* VQ Request objects are freed by the kernel verbs handlers only +* after the verb has been processed, or when the adapter fails and +* does not reply. +* +* +* Verbs Reply Buffers: +* +* VQ Reply bufs are local host memory copies of a +* outstanding Verb Request reply +* message. The are always allocated by the kernel verbs handlers, and _may_ be +* freed by either the kernel verbs handler -or- the interrupt handler. The +* kernel verbs handler _must_ free the repbuf, then free the vq request object +* in that order. +*/ + +int vq_init (struct c2_dev *c2dev) +{ +sprintf (c2dev->vq_cache_name,"c2-vq:dev%c" , + (char ) ('0' + c2dev->devnum)); + c2dev->host_msg_cache = +kmem_cache_create (c2dev->vq_cache_name, c2dev->rep_vq.msg_size,0 , +SLAB_HWCACHE_ALIGN ,NULL ,NULL ); +if (c2dev->host_msg_cache ==NULL ) { +return -ENOMEM ; + } +return 0 ; +} + +void vq_term (struct c2_dev *c2dev) +{ +kmem_cache_destroy (c2dev->host_msg_cache); +} + +/* vq_req_alloc - allocate a VQ Request Object and initialize it. +* The refcnt is set to 1. +*/ +struct c2_vq_req *vq_req_alloc (struct c2_dev *c2dev) +{ +struct c2_vq_req *r; + + r =kmalloc (sizeof (struct c2_vq_req ),GFP_KERNEL ); +if (r) { +init_waitqueue_head (&r->wait_object); + r->reply_msg = (u64 )NULL ; + r->event =0 ; + r->cm_id =NULL ; + r->qp =NULL ; +atomic_set (&r->refcnt,1 ); +atomic_set (&r->reply_ready,0 ); + } +return r; +} + + +/* vq_req_free - free the VQ Request Object. It is assumed the verbs handler +* has already free the VQ Reply Buffer if it existed. +*/ +void vq_req_free (struct c2_dev *c2dev,struct c2_vq_req *r) +{ + r->reply_msg = (u64 )NULL ; +if (atomic_dec_and_test (&r->refcnt)) { +kfree (r); + } +} + +/* vq_req_get - reference a VQ Request Object. Done +* only in the kernel verbs handlers. +*/ +void vq_req_get (struct c2_dev *c2dev,struct c2_vq_req *r) +{ +atomic_inc (&r->refcnt); +} + + +/* vq_req_put - dereference and potentially free a VQ Request Object. +* +* This is only called by handle_vq() on the +* interrupt when it is done processing +* a verb reply message. If the associated +* kernel verbs handler has already bailed, +* then this put will actually free the VQ +* Request object _and_ the VQ Reply Buffer +* if it exists. +*/ +void vq_req_put (struct c2_dev *c2dev,struct c2_vq_req *r) +{ +if (atomic_dec_and_test (&r->refcnt)) { +if (r->reply_msg != (u64 )NULL ) +vq_repbuf_free (c2dev, + (void *) (unsigned long ) r->reply_msg); +kfree (r); + } +} + + +/* +* vq_repbuf_alloc - allocate a VQ Reply Buffer. +*/ +void *vq_repbuf_alloc (struct c2_dev *c2dev) +{ +return kmem_cache_alloc (c2dev->host_msg_cache,SLAB_ATOMIC ); +} + +/* +* vq_send_wr - post a verbs request message to the Verbs Request Queue. +* If a message is not available in the MQ, then block until one is available. +* NOTE: handle_mq() on the interrupt context will wake up threads blocked here. +* When the adapter drains the Verbs Request Queue, +* it inserts MQ index 0 in to the +* adapter->host activity fifo and interrupts the host. +*/ +int vq_send_wr (struct c2_dev *c2dev,union c2wr *wr) +{ +void *msg; +wait_queue_t __wait; + +/* +* grab adapter vq lock +*/ +spin_lock (&c2dev->vqlock); + +/* +* allocate msg +*/ + msg =c2_mq_alloc (&c2dev->req_vq); + +/* +* If we cannot get a msg, then we'll wait +* When a messages are available, the int handler will wake_up() +* any waiters. +*/ +while (msg ==NULL ) { +pr_debug ("%s:%d no available msg in VQ, waiting...\n" , + __FUNCTION__, __LINE__); +init_waitqueue_entry (&__wait, current); +add_wait_queue (&c2dev->req_vq_wo, &__wait); +spin_unlock (&c2dev->vqlock); +for (;;) { +set_current_state (TASK_INTERRUPTIBLE ); +if (!c2_mq_full (&c2dev->req_vq)) { +break ; + } +if (!signal_pending (current)) { +schedule_timeout (1 *HZ );/* 1 second... */ +continue ; + } +set_current_state (TASK_RUNNING ); +remove_wait_queue (&c2dev->req_vq_wo, &__wait); +return -EINTR ; + } +set_current_state (TASK_RUNNING ); +remove_wait_queue (&c2dev->req_vq_wo, &__wait); +spin_lock (&c2dev->vqlock); + msg =c2_mq_alloc (&c2dev->req_vq); + } + +/* +* copy wr into adapter msg +*/ +memcpy (msg, wr, c2dev->req_vq.msg_size); + +/* +* post msg +*/ +c2_mq_produce (&c2dev->req_vq); + +/* +* release adapter vq lock +*/ +spin_unlock (&c2dev->vqlock); +return 0 ; +} + + +/* +* vq_wait_for_reply - block until the adapter posts a Verb Reply Message. +*/ +int vq_wait_for_reply (struct c2_dev *c2dev,struct c2_vq_req *req) +{ +if (!wait_event_timeout (req->wait_object, +atomic_read (&req->reply_ready), +60 *HZ )) +return -ETIMEDOUT ; + +return 0 ; +} + +/* +* vq_repbuf_free - Free a Verbs Reply Buffer. +*/ +void vq_repbuf_free (struct c2_dev *c2dev,void *reply) +{ +kmem_cache_free (c2dev->host_msg_cache, reply); +}
drivers/infiniband/hw/amso1100/c2_vq.h new file +63 -0
63 lines not on this page · raw message
PATCH v3 7/7 AMSO1100 Makefiles and Kconfig changes.
In reply to Steve WiseReview Changes:
- C2DEBUG -> DEBUG
---
drivers/infiniband/Kconfig | 1 + drivers/infiniband/Makefile | 1 + drivers/infiniband/hw/amso1100/Kbuild | 10 ++++++++++ drivers/infiniband/hw/amso1100/Kconfig | 15 +++++++++++++++ drivers/infiniband/hw/amso1100/README | 11 +++++++++++ 5 files changed, 38 insertions(+), 0 deletions(-)
drivers/infiniband/Kconfig +1 -0
@@ -36,6 +36,7 @@ config INFINIBAND_ADDR_TRANS source "drivers/infiniband/hw/mthca/Kconfig" source "drivers/infiniband/hw/ipath/Kconfig" +source "drivers/infiniband/hw/amso1100/Kconfig" source "drivers/infiniband/ulp/ipoib/Kconfig"
drivers/infiniband/Makefile +1 -0
@@ -1,5 +1,6 @@ obj-$(CONFIG_INFINIBAND) += core/ obj-$(CONFIG_INFINIBAND_MTHCA) += hw/mthca/ obj-$(CONFIG_IPATH_CORE) += hw/ipath/ +obj-$(CONFIG_INFINIBAND_AMSO1100) += hw/amso1100/ obj-$(CONFIG_INFINIBAND_IPOIB) += ulp/ipoib/ obj-$(CONFIG_INFINIBAND_SRP) += ulp/srp/
drivers/infiniband/hw/amso1100/Kbuild new file +10 -0
@@ -0,0 +1,10 @@ +EXTRA_CFLAGS += -Idrivers/infiniband/include + +ifdef CONFIG_INFINIBAND_AMSO1100_DEBUG +EXTRA_CFLAGS += -DDEBUG +endif + +obj-$(CONFIG_INFINIBAND_AMSO1100) += iw_c2.o + +iw_c2-y := c2.o c2_provider.o c2_rnic.o c2_alloc.o c2_mq.o c2_ae.o c2_vq.o \ + c2_intr.o c2_cq.o c2_qp.o c2_cm.o c2_mm.o c2_pd.o
drivers/infiniband/hw/amso1100/Kconfig new file +15 -0
@@ -0,0 +1,15 @@ +config INFINIBAND_AMSO1100 + tristate "Ammasso 1100 HCA support" + depends on PCI && INET && INFINIBAND + ---help--- + This is a low-level driver for the Ammasso 1100 host + channel adapter (HCA). + +config INFINIBAND_AMSO1100_DEBUG + bool "Verbose debugging output" + depends on INFINIBAND_AMSO1100 + default n + ---help--- + This option causes the amso1100 driver to produce a bunch of + debug messages. Select this if you are developing the driver + or trying to diagnose a problem.
drivers/infiniband/hw/amso1100/README new file +11 -0
@@ -0,0 +1,11 @@ +This is the OpenFabrics provider driver for the +AMSO1100 1Gb RNIC adapter. + +This adapter is available in limited quantities +for development purposes from Open Grid Computing. + +This driver requires the IWCM and CMA mods necessary +to support iWARP. + +Contact [email protected] for more information. +