netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [ofa-general] [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver
@ 2008-04-30 17:15 Ramachandra K
  2008-04-30 17:16 ` [ofa-general] [PATCH 01/13] QLogic VNIC: Driver - netdev implementation Ramachandra K
                   ` (13 more replies)
  0 siblings, 14 replies; 32+ messages in thread
From: Ramachandra K @ 2008-04-30 17:15 UTC (permalink / raw)
  To: rdreier, general, netdev; +Cc: poornima.kamath, amar.mudrankit

Roland,

This is the QLogic Virtual NIC driver patch series which has been tested
against your for-2.6.26 and for-2.6.27 branches. We intended these patches to
make it to the 2.6.26 kernel, but if it is too late for the 2.6.26 merge window
please consider them for 2.6.27.

This patch series adds the QLogic Virtual NIC (VNIC) driver which works in
conjunction with the the QLogic Ethernet Virtual I/O Controller (EVIC) hardware.
The VNIC driver along with the QLogic EVIC's two 10 Gigabit ethernet ports,
enables Infiniband clusters to connect to Ethernet networks. This driver also
works with the earlier version of the I/O Controller, the VEx.

The QLogic VNIC driver creates virtual ethernet interfaces and tunnels the
Ethernet data to/from the EVIC over Infiniband using an Infiniband reliable
connection.

The driver compiles cleanly with sparse endianness checking enabled. We have
also tested the driver with lockdep checking enabled.

We have run these patches through checkpatch.pl and the only warnings are
related to lines slightly longer than 80 columns in some of the statements.

The driver itself has has been tested with long duration iperf, netperf TCP,
UDP streams.

---

      [PATCH 01/13] QLogic VNIC: Driver - netdev implementation
      [PATCH 02/13] QLogic VNIC: Netpath - abstraction of connection to EVIC/VEx
      [PATCH 03/13] QLogic VNIC: Implementation of communication protocol with EVIC/VEx
      [PATCH 04/13] QLogic VNIC: Implementation of Control path of communication protocol
      [PATCH 05/13] QLogic VNIC: Implementation of Data path of communication protocol
      [PATCH 06/13] QLogic VNIC: IB core stack interaction
      [PATCH 07/13] QLogic VNIC: Handling configurable parameters of the driver
      [PATCH 08/13] QLogic VNIC: sysfs interface implementation for the driver
      [PATCH 09/13] QLogic VNIC: IB Multicast for Ethernet broadcast/multicast
      [PATCH 10/13] QLogic VNIC: Driver Statistics collection
      [PATCH 11/13] QLogic VNIC: Driver utility file - implements various utility macros
      [PATCH 12/13] QLogic VNIC: Driver Kconfig and Makefile.
      [PATCH 13/13] QLogic VNIC: Modifications to IB Kconfig and Makefile


 drivers/infiniband/Kconfig                         |    2 
 drivers/infiniband/Makefile                        |    1 
 drivers/infiniband/ulp/qlgc_vnic/Kconfig           |   28 
 drivers/infiniband/ulp/qlgc_vnic/Makefile          |   13 
 drivers/infiniband/ulp/qlgc_vnic/vnic_config.c     |  380 +++
 drivers/infiniband/ulp/qlgc_vnic/vnic_config.h     |  242 ++
 drivers/infiniband/ulp/qlgc_vnic/vnic_control.c    | 2288 ++++++++++++++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_control.h    |  180 ++
 .../infiniband/ulp/qlgc_vnic/vnic_control_pkt.h    |  368 +++
 drivers/infiniband/ulp/qlgc_vnic/vnic_data.c       | 1473 +++++++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_data.h       |  206 ++
 drivers/infiniband/ulp/qlgc_vnic/vnic_ib.c         | 1046 +++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_ib.h         |  206 ++
 drivers/infiniband/ulp/qlgc_vnic/vnic_main.c       | 1052 +++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_main.h       |  167 +
 drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.c  |  332 +++
 drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.h  |   76 +
 drivers/infiniband/ulp/qlgc_vnic/vnic_netpath.c    |  112 +
 drivers/infiniband/ulp/qlgc_vnic/vnic_netpath.h    |   80 +
 drivers/infiniband/ulp/qlgc_vnic/vnic_stats.c      |  234 ++
 drivers/infiniband/ulp/qlgc_vnic/vnic_stats.h      |  497 ++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_sys.c        | 1127 ++++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_sys.h        |   62 +
 drivers/infiniband/ulp/qlgc_vnic/vnic_trailer.h    |  103 +
 drivers/infiniband/ulp/qlgc_vnic/vnic_util.h       |  251 ++
 drivers/infiniband/ulp/qlgc_vnic/vnic_viport.c     | 1233 +++++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_viport.h     |  176 ++
 27 files changed, 11935 insertions(+), 0 deletions(-)
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/Kconfig
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/Makefile
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_config.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_config.h
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_control.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_control.h
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_control_pkt.h
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_data.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_data.h
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_ib.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_ib.h
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_main.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_main.h
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.h
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_netpath.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_netpath.h
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_stats.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_stats.h
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_sys.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_sys.h
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_trailer.h
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_util.h
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_viport.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_viport.h

-- 
Regards,
Ram

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [ofa-general] [PATCH 01/13] QLogic VNIC: Driver - netdev implementation
  2008-04-30 17:15 [ofa-general] [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Ramachandra K
@ 2008-04-30 17:16 ` Ramachandra K
  2008-05-02 18:15   ` Roland Dreier
  2008-04-30 17:16 ` [ofa-general] [PATCH 02/13] QLogic VNIC: Netpath - abstraction of connection to EVIC/VEx Ramachandra K
                   ` (12 subsequent siblings)
  13 siblings, 1 reply; 32+ messages in thread
From: Ramachandra K @ 2008-04-30 17:16 UTC (permalink / raw)
  To: rdreier, general, netdev; +Cc: poornima.kamath, amar.mudrankit

From: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>

QLogic Virtual NIC Driver. This patch implements netdev registration,
netdev functions and state maintenance of the QLogic Virtual NIC
corresponding to the various events associated with the QLogic Ethernet 
Virtual I/O Controller (EVIC/VEx) connection.

Signed-off-by: Poornima Kamath <poornima.kamath@qlogic.com>
Signed-off-by: Amar Mudrankit <amar.mudrankit@qlogic.com>
---

 drivers/infiniband/ulp/qlgc_vnic/vnic_main.c | 1052 ++++++++++++++++++++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_main.h |  167 ++++
 2 files changed, 1219 insertions(+), 0 deletions(-)
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_main.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_main.h

diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_main.c b/drivers/infiniband/ulp/qlgc_vnic/vnic_main.c
new file mode 100644
index 0000000..393c79a
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_main.c
@@ -0,0 +1,1052 @@
+/*
+ * Copyright (c) 2006 QLogic, 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/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/completion.h>
+
+#include <rdma/ib_cache.h>
+
+#include "vnic_util.h"
+#include "vnic_main.h"
+#include "vnic_netpath.h"
+#include "vnic_viport.h"
+#include "vnic_ib.h"
+#include "vnic_stats.h"
+
+#define MODULEVERSION "1.3.0.0.4"
+#define MODULEDETAILS	\
+		"QLogic Corp. Virtual NIC (VNIC) driver version " MODULEVERSION
+
+MODULE_AUTHOR("QLogic Corp.");
+MODULE_DESCRIPTION(MODULEDETAILS);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_SUPPORTED_DEVICE("QLogic Ethernet Virtual I/O Controller");
+
+u32 vnic_debug;
+
+module_param(vnic_debug, uint, 0444);
+MODULE_PARM_DESC(vnic_debug, "Enable debug tracing if > 0");
+
+LIST_HEAD(vnic_list);
+
+static DECLARE_WAIT_QUEUE_HEAD(vnic_npevent_queue);
+static LIST_HEAD(vnic_npevent_list);
+static DECLARE_COMPLETION(vnic_npevent_thread_exit);
+static spinlock_t vnic_npevent_list_lock;
+static struct task_struct *vnic_npevent_thread;
+static int vnic_npevent_thread_end;
+
+
+void vnic_connected(struct vnic *vnic, struct netpath *netpath)
+{
+	VNIC_FUNCTION("vnic_connected()\n");
+	if (netpath->second_bias)
+		vnic_npevent_queue_evt(netpath, VNIC_SECNP_CONNECTED);
+	else
+		vnic_npevent_queue_evt(netpath, VNIC_PRINP_CONNECTED);
+
+	vnic_connected_stats(vnic);
+}
+
+void vnic_disconnected(struct vnic *vnic, struct netpath *netpath)
+{
+	VNIC_FUNCTION("vnic_disconnected()\n");
+	if (netpath->second_bias)
+		vnic_npevent_queue_evt(netpath, VNIC_SECNP_DISCONNECTED);
+	else
+		vnic_npevent_queue_evt(netpath, VNIC_PRINP_DISCONNECTED);
+}
+
+void vnic_link_up(struct vnic *vnic, struct netpath *netpath)
+{
+	VNIC_FUNCTION("vnic_link_up()\n");
+	if (netpath->second_bias)
+		vnic_npevent_queue_evt(netpath, VNIC_SECNP_LINKUP);
+	else
+		vnic_npevent_queue_evt(netpath, VNIC_PRINP_LINKUP);
+}
+
+void vnic_link_down(struct vnic *vnic, struct netpath *netpath)
+{
+	VNIC_FUNCTION("vnic_link_down()\n");
+	if (netpath->second_bias)
+		vnic_npevent_queue_evt(netpath, VNIC_SECNP_LINKDOWN);
+	else
+		vnic_npevent_queue_evt(netpath, VNIC_PRINP_LINKDOWN);
+}
+
+void vnic_stop_xmit(struct vnic *vnic, struct netpath *netpath)
+{
+	VNIC_FUNCTION("vnic_stop_xmit()\n");
+	if (netpath == vnic->current_path) {
+		if (vnic->xmit_started) {
+			netif_stop_queue(vnic->netdevice);
+			vnic->xmit_started = 0;
+		}
+
+		vnic_stop_xmit_stats(vnic);
+	}
+}
+
+void vnic_restart_xmit(struct vnic *vnic, struct netpath *netpath)
+{
+	VNIC_FUNCTION("vnic_restart_xmit()\n");
+	if (netpath == vnic->current_path) {
+		if (!vnic->xmit_started) {
+			netif_wake_queue(vnic->netdevice);
+			vnic->xmit_started = 1;
+		}
+
+		vnic_restart_xmit_stats(vnic);
+	}
+}
+
+void vnic_recv_packet(struct vnic *vnic, struct netpath *netpath,
+		      struct sk_buff *skb)
+{
+	VNIC_FUNCTION("vnic_recv_packet()\n");
+	if ((netpath != vnic->current_path) || !vnic->open) {
+		VNIC_INFO("tossing packet\n");
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	vnic->netdevice->last_rx = jiffies;
+	skb->dev = vnic->netdevice;
+	skb->protocol = eth_type_trans(skb, skb->dev);
+	if (!vnic->config->use_rx_csum)
+		skb->ip_summed = CHECKSUM_NONE;
+	netif_rx(skb);
+	vnic_recv_pkt_stats(vnic);
+}
+
+static struct net_device_stats *vnic_get_stats(struct net_device *device)
+{
+	struct vnic *vnic;
+	struct netpath *np;
+
+	VNIC_FUNCTION("vnic_get_stats()\n");
+	vnic = (struct vnic *)device->priv;
+
+	np = vnic->current_path;
+	if (np && np->viport && !np->cleanup_started)
+		viport_get_stats(np->viport, &vnic->stats);
+	return &vnic->stats;
+}
+
+static int vnic_open(struct net_device *device)
+{
+	struct vnic *vnic;
+
+	VNIC_FUNCTION("vnic_open()\n");
+	vnic = (struct vnic *)device->priv;
+
+	vnic->open++;
+	vnic_npevent_queue_evt(&vnic->primary_path, VNIC_PRINP_SETLINK);
+	vnic->xmit_started = 1;
+	netif_start_queue(vnic->netdevice);
+
+	return 0;
+}
+
+static int vnic_stop(struct net_device *device)
+{
+	struct vnic *vnic;
+	int ret = 0;
+
+	VNIC_FUNCTION("vnic_stop()\n");
+	vnic = (struct vnic *)device->priv;
+	netif_stop_queue(device);
+	vnic->xmit_started = 0;
+	vnic->open--;
+	vnic_npevent_queue_evt(&vnic->primary_path, VNIC_PRINP_SETLINK);
+
+	return ret;
+}
+
+static int vnic_hard_start_xmit(struct sk_buff *skb,
+				struct net_device *device)
+{
+	struct vnic *vnic;
+	struct netpath *np;
+	cycles_t xmit_time;
+	int	 ret = -1;
+
+	VNIC_FUNCTION("vnic_hard_start_xmit()\n");
+	vnic = (struct vnic *)device->priv;
+	np = vnic->current_path;
+
+	vnic_pre_pkt_xmit_stats(&xmit_time);
+
+	if (np && np->viport)
+		ret = viport_xmit_packet(np->viport, skb);
+
+	if (ret) {
+		vnic_xmit_fail_stats(vnic);
+		dev_kfree_skb_any(skb);
+		vnic->stats.tx_dropped++;
+		goto out;
+	}
+
+	device->trans_start = jiffies;
+	vnic_post_pkt_xmit_stats(vnic, xmit_time);
+out:
+	return 0;
+}
+
+static void vnic_tx_timeout(struct net_device *device)
+{
+	struct vnic *vnic;
+
+	VNIC_FUNCTION("vnic_tx_timeout()\n");
+	vnic = (struct vnic *)device->priv;
+	device->trans_start = jiffies;
+
+	if (vnic->current_path->viport)
+		viport_failure(vnic->current_path->viport);
+
+	VNIC_ERROR("vnic_tx_timeout\n");
+}
+
+static void vnic_set_multicast_list(struct net_device *device)
+{
+	struct vnic *vnic;
+	unsigned long flags;
+
+	VNIC_FUNCTION("vnic_set_multicast_list()\n");
+	vnic = (struct vnic *)device->priv;
+
+	spin_lock_irqsave(&vnic->lock, flags);
+	if (device->mc_count == 0) {
+		if (vnic->mc_list_len) {
+			vnic->mc_list_len = vnic->mc_count = 0;
+			kfree(vnic->mc_list);
+		}
+	} else {
+		struct dev_mc_list *mc_list = device->mc_list;
+		int i;
+
+		if (device->mc_count > vnic->mc_list_len) {
+			if (vnic->mc_list_len)
+				kfree(vnic->mc_list);
+			vnic->mc_list_len = device->mc_count + 10;
+			vnic->mc_list = kmalloc(vnic->mc_list_len *
+						sizeof *mc_list, GFP_ATOMIC);
+			if (!vnic->mc_list) {
+				vnic->mc_list_len = vnic->mc_count = 0;
+				VNIC_ERROR("failed allocating mc_list\n");
+				goto failure;
+			}
+		}
+		vnic->mc_count = device->mc_count;
+		for (i = 0; i < device->mc_count; i++) {
+			vnic->mc_list[i] = *mc_list;
+			vnic->mc_list[i].next = &vnic->mc_list[i + 1];
+			mc_list = mc_list->next;
+		}
+	}
+	spin_unlock_irqrestore(&vnic->lock, flags);
+
+	if (vnic->primary_path.viport)
+		viport_set_multicast(vnic->primary_path.viport,
+				     vnic->mc_list, vnic->mc_count);
+
+	if (vnic->secondary_path.viport)
+		viport_set_multicast(vnic->secondary_path.viport,
+				     vnic->mc_list, vnic->mc_count);
+
+	vnic_npevent_queue_evt(&vnic->primary_path, VNIC_PRINP_SETLINK);
+	return;
+failure:
+	spin_unlock_irqrestore(&vnic->lock, flags);
+}
+
+/**
+ * Following set of functions queues up the events for EVIC and the
+ * kernel thread queuing up the event might return.
+ */
+static int vnic_set_mac_address(struct net_device *device, void *addr)
+{
+	struct vnic	*vnic;
+	struct sockaddr	*sockaddr = addr;
+	u8		*address;
+	int		ret = -1;
+
+	VNIC_FUNCTION("vnic_set_mac_address()\n");
+	vnic = (struct vnic *)device->priv;
+
+	if (!is_valid_ether_addr(sockaddr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	if (netif_running(device))
+		return -EBUSY;
+
+	memcpy(device->dev_addr, sockaddr->sa_data, ETH_ALEN);
+	address = sockaddr->sa_data;
+
+	if (vnic->primary_path.viport)
+		ret = viport_set_unicast(vnic->primary_path.viport,
+					 address);
+
+	if (ret)
+		return ret;
+
+	if (vnic->secondary_path.viport)
+		viport_set_unicast(vnic->secondary_path.viport, address);
+
+	vnic->mac_set = 1;
+	return 0;
+}
+
+static int vnic_change_mtu(struct net_device *device, int mtu)
+{
+	struct vnic	*vnic;
+	int		ret = 0;
+	int		pri_max_mtu;
+	int		sec_max_mtu;
+
+	VNIC_FUNCTION("vnic_change_mtu()\n");
+	vnic = (struct vnic *)device->priv;
+
+	if (vnic->primary_path.viport)
+		pri_max_mtu = viport_max_mtu(vnic->primary_path.viport);
+	else
+		pri_max_mtu = MAX_PARAM_VALUE;
+
+	if (vnic->secondary_path.viport)
+		sec_max_mtu = viport_max_mtu(vnic->secondary_path.viport);
+	else
+		sec_max_mtu = MAX_PARAM_VALUE;
+
+	if ((mtu < pri_max_mtu) && (mtu < sec_max_mtu)) {
+		device->mtu = mtu;
+		vnic_npevent_queue_evt(&vnic->primary_path,
+				       VNIC_PRINP_SETLINK);
+		vnic_npevent_queue_evt(&vnic->secondary_path,
+				       VNIC_SECNP_SETLINK);
+	} else if (pri_max_mtu < sec_max_mtu)
+		printk(KERN_WARNING PFX "%s: Maximum "
+					"supported MTU size is %d. "
+					"Cannot set MTU to %d\n",
+					vnic->config->name, pri_max_mtu, mtu);
+	else
+		printk(KERN_WARNING PFX "%s: Maximum "
+					"supported MTU size is %d. "
+					"Cannot set MTU to %d\n",
+					vnic->config->name, sec_max_mtu, mtu);
+
+	return ret;
+}
+
+static int vnic_npevent_register(struct vnic *vnic, struct netpath *netpath)
+{
+	u8	*address;
+	int	ret;
+
+	if (!vnic->mac_set) {
+		/* if netpath == secondary_path, then the primary path isn't
+		 * connected.  MAC address will be set when the primary
+		 * connects.
+		 */
+		netpath_get_hw_addr(netpath, vnic->netdevice->dev_addr);
+		address = vnic->netdevice->dev_addr;
+
+		if (vnic->secondary_path.viport)
+			viport_set_unicast(vnic->secondary_path.viport,
+					   address);
+
+		vnic->mac_set = 1;
+	}
+	ret = register_netdev(vnic->netdevice);
+	if (ret) {
+		printk(KERN_ERR PFX "%s failed registering netdev "
+			"error %d - calling viport_failure\n",
+			config_viport_name(vnic->primary_path.viport->config),
+				ret);
+		vnic_free(vnic);
+		printk(KERN_ERR PFX "%s DELETED : register_netdev failure\n",
+			config_viport_name(vnic->primary_path.viport->config));
+		return ret;
+	}
+
+	vnic->state = VNIC_REGISTERED;
+	vnic->carrier = 2; /*special value to force netif_carrier_(on|off)*/
+	return 0;
+}
+
+static void vnic_npevent_dequeue_all(struct vnic *vnic)
+{
+	unsigned long flags;
+	struct vnic_npevent *npevt, *tmp;
+
+	spin_lock_irqsave(&vnic_npevent_list_lock, flags);
+	if (list_empty(&vnic_npevent_list))
+		goto out;
+	list_for_each_entry_safe(npevt, tmp, &vnic_npevent_list,
+				 list_ptrs) {
+		if ((npevt->vnic == vnic)) {
+			list_del(&npevt->list_ptrs);
+			kfree(npevt);
+		}
+	}
+out:
+	spin_unlock_irqrestore(&vnic_npevent_list_lock, flags);
+}
+
+static void update_path_and_reconnect(struct netpath *netpath,
+				      struct vnic *vnic)
+{
+	struct viport_config *config = netpath->viport->config;
+	int delay = 1;
+
+	if (vnic_ib_get_path(netpath, vnic))
+		return;
+	/*
+	 * tell viport_connect to wait for default_no_path_timeout
+	 * before connecting if  we are retrying the same path index
+	 * within default_no_path_timeout.
+	 * This prevents flooding connect requests to a path (or set
+	 * of paths) that aren't successfully connecting for some reason.
+	 */
+	if (jiffies > netpath->connect_time +
+		      vnic->config->no_path_timeout) {
+		netpath->path_idx = config->path_idx;
+		netpath->connect_time = jiffies;
+		netpath->delay_reconnect = 0;
+		delay = 0;
+	} else if (config->path_idx != netpath->path_idx) {
+		delay = netpath->delay_reconnect;
+		netpath->path_idx = config->path_idx;
+		netpath->delay_reconnect = 1;
+	} else
+		delay = 1;
+	viport_connect(netpath->viport, delay);
+}
+
+static void vnic_set_uni_multicast(struct vnic *vnic,
+				   struct netpath *netpath)
+{
+	unsigned long	flags;
+	u8		*address;
+
+	if (vnic->mac_set) {
+		address = vnic->netdevice->dev_addr;
+
+		if (netpath->viport)
+			viport_set_unicast(netpath->viport, address);
+	}
+	spin_lock_irqsave(&vnic->lock, flags);
+
+	if (vnic->mc_list && netpath->viport)
+		viport_set_multicast(netpath->viport, vnic->mc_list,
+				     vnic->mc_count);
+
+	spin_unlock_irqrestore(&vnic->lock, flags);
+	if (vnic->state == VNIC_REGISTERED) {
+		if (!netpath->viport)
+			return;
+		viport_set_link(netpath->viport,
+				vnic->netdevice->flags & ~IFF_UP,
+				vnic->netdevice->mtu);
+	}
+}
+
+static void vnic_set_netpath_timers(struct vnic *vnic,
+				    struct netpath *netpath)
+{
+	switch (netpath->timer_state) {
+	case NETPATH_TS_IDLE:
+		netpath->timer_state = NETPATH_TS_ACTIVE;
+		if (vnic->state == VNIC_UNINITIALIZED)
+			netpath_timer(netpath,
+				      vnic->config->
+				      primary_connect_timeout);
+		else
+			netpath_timer(netpath,
+				      vnic->config->
+				      primary_reconnect_timeout);
+			break;
+	case NETPATH_TS_ACTIVE:
+		/*nothing to do*/
+		break;
+	case NETPATH_TS_EXPIRED:
+		if (vnic->state == VNIC_UNINITIALIZED)
+			vnic_npevent_register(vnic, netpath);
+
+		break;
+	}
+}
+
+static void vnic_check_primary_path_timer(struct vnic *vnic)
+{
+	switch (vnic->primary_path.timer_state) {
+	case NETPATH_TS_ACTIVE:
+		/* nothing to do. just wait */
+		break;
+	case NETPATH_TS_IDLE:
+		netpath_timer(&vnic->primary_path,
+			      vnic->config->
+			      primary_switch_timeout);
+		break;
+	case NETPATH_TS_EXPIRED:
+		printk(KERN_INFO PFX
+		       "%s: switching to primary path\n",
+		       vnic->config->name);
+
+		vnic->current_path = &vnic->primary_path;
+		if (vnic->config->use_tx_csum
+		    && netpath_can_tx_csum(vnic->
+					   current_path)) {
+			vnic->netdevice->features |=
+					    NETIF_F_IP_CSUM;
+		}
+		break;
+	}
+}
+
+static void vnic_carrier_loss(struct vnic *vnic,
+			      struct netpath *last_path)
+{
+	if (vnic->primary_path.carrier) {
+		vnic->carrier = 1;
+		vnic->current_path = &vnic->primary_path;
+
+		if (last_path && last_path != vnic->current_path)
+			printk(KERN_INFO PFX
+			       "%s: failing over to primary path\n",
+			       vnic->config->name);
+		else if (!last_path)
+			printk(KERN_INFO PFX "%s: using primary path\n",
+			       vnic->config->name);
+
+		if (vnic->config->use_tx_csum &&
+		    netpath_can_tx_csum(vnic->current_path))
+			vnic->netdevice->features |= NETIF_F_IP_CSUM;
+
+	} else if ((vnic->secondary_path.carrier) &&
+		   (vnic->secondary_path.timer_state != NETPATH_TS_ACTIVE)) {
+		vnic->carrier = 1;
+		vnic->current_path = &vnic->secondary_path;
+
+		if (last_path && last_path != vnic->current_path)
+			printk(KERN_INFO PFX
+			       "%s: failing over to secondary path\n",
+			       vnic->config->name);
+		else if (!last_path)
+			printk(KERN_INFO PFX "%s: using secondary path\n",
+			       vnic->config->name);
+
+		if (vnic->config->use_tx_csum &&
+		    netpath_can_tx_csum(vnic->current_path))
+			vnic->netdevice->features |= NETIF_F_IP_CSUM;
+
+	}
+
+}
+
+static void vnic_handle_path_change(struct vnic *vnic,
+				    struct netpath **path)
+{
+	struct netpath *last_path = *path;
+
+	if (!last_path) {
+		if (vnic->current_path == &vnic->primary_path)
+			last_path = &vnic->secondary_path;
+		else
+			last_path = &vnic->primary_path;
+
+	}
+
+	if (vnic->current_path && vnic->current_path->viport)
+		viport_set_link(vnic->current_path->viport,
+				vnic->netdevice->flags,
+				vnic->netdevice->mtu);
+
+	if (last_path->viport)
+		viport_set_link(last_path->viport,
+				 vnic->netdevice->flags &
+				 ~IFF_UP, vnic->netdevice->mtu);
+
+	vnic_restart_xmit(vnic, vnic->current_path);
+}
+
+static void vnic_report_path_change(struct vnic *vnic,
+				    struct netpath *last_path,
+				    int other_path_ok)
+{
+	if (!vnic->current_path) {
+		if (last_path == &vnic->primary_path)
+			printk(KERN_INFO PFX "%s: primary path lost, "
+			       "no failover path available\n",
+			       vnic->config->name);
+		else
+			printk(KERN_INFO PFX "%s: secondary path lost, "
+			       "no failover path available\n",
+			       vnic->config->name);
+		return;
+	}
+
+	if (last_path != vnic->current_path)
+		return;
+
+	if (vnic->current_path == &vnic->secondary_path) {
+		if (other_path_ok != vnic->primary_path.carrier) {
+			if (other_path_ok)
+				printk(KERN_INFO PFX "%s: primary path no"
+				       " longer available for failover\n",
+				       vnic->config->name);
+			else
+				printk(KERN_INFO PFX "%s: primary path now"
+				       " available for failover\n",
+				       vnic->config->name);
+		}
+	} else {
+		if (other_path_ok != vnic->secondary_path.carrier) {
+			if (other_path_ok)
+				printk(KERN_INFO PFX "%s: secondary path no"
+				       " longer available for failover\n",
+				       vnic->config->name);
+			else
+				printk(KERN_INFO PFX "%s: secondary path now"
+				       " available for failover\n",
+				       vnic->config->name);
+		}
+	}
+}
+
+static void vnic_handle_free_vnic_evt(struct vnic *vnic)
+{
+	netpath_timer_stop(&vnic->primary_path);
+	netpath_timer_stop(&vnic->secondary_path);
+	vnic->current_path = NULL;
+	netpath_free(&vnic->primary_path);
+	netpath_free(&vnic->secondary_path);
+	if (vnic->state == VNIC_REGISTERED) {
+		unregister_netdev(vnic->netdevice);
+		free_netdev(vnic->netdevice);
+	}
+	vnic_npevent_dequeue_all(vnic);
+	kfree(vnic->config);
+	if (vnic->mc_list_len) {
+		vnic->mc_list_len = vnic->mc_count = 0;
+		kfree(vnic->mc_list);
+	}
+
+	sysfs_remove_group(&vnic->dev_info.dev.kobj,
+			   &vnic_dev_attr_group);
+	vnic_cleanup_stats_files(vnic);
+	device_unregister(&vnic->dev_info.dev);
+	wait_for_completion(&vnic->dev_info.released);
+}
+
+static struct vnic *vnic_handle_npevent(struct vnic *vnic,
+					 enum vnic_npevent_type npevt_type)
+{
+	struct netpath	*netpath;
+	const char *netpath_str;
+
+	if (npevt_type <= VNIC_PRINP_LASTTYPE)
+		netpath_str = netpath_to_string(vnic, &vnic->primary_path);
+	else if	(npevt_type <= VNIC_SECNP_LASTTYPE)
+		netpath_str = netpath_to_string(vnic, &vnic->secondary_path);
+	else
+		netpath_str = netpath_to_string(vnic, vnic->current_path);
+
+	VNIC_INFO("%s: processing %s, netpath=%s, carrier=%d\n",
+		  vnic->config->name, vnic_npevent_str[npevt_type],
+		  netpath_str, vnic->carrier);
+
+	switch (npevt_type) {
+	case VNIC_PRINP_CONNECTED:
+		netpath = &vnic->primary_path;
+		if (vnic->state == VNIC_UNINITIALIZED) {
+			if (vnic_npevent_register(vnic, netpath))
+				break;
+		}
+		vnic_set_uni_multicast(vnic, netpath);
+		break;
+	case VNIC_SECNP_CONNECTED:
+		vnic_set_uni_multicast(vnic, &vnic->secondary_path);
+		break;
+	case VNIC_PRINP_TIMEREXPIRED:
+		netpath = &vnic->primary_path;
+		netpath->timer_state = NETPATH_TS_EXPIRED;
+		if (!netpath->carrier)
+			update_path_and_reconnect(netpath, vnic);
+		break;
+	case VNIC_SECNP_TIMEREXPIRED:
+		netpath = &vnic->secondary_path;
+		netpath->timer_state = NETPATH_TS_EXPIRED;
+		if (!netpath->carrier)
+			update_path_and_reconnect(netpath, vnic);
+		else {
+			if (vnic->state == VNIC_UNINITIALIZED)
+				vnic_npevent_register(vnic, netpath);
+		}
+		break;
+	case VNIC_PRINP_LINKUP:
+		vnic->primary_path.carrier = 1;
+		break;
+	case VNIC_SECNP_LINKUP:
+		netpath = &vnic->secondary_path;
+		netpath->carrier = 1;
+		if (!vnic->carrier)
+			vnic_set_netpath_timers(vnic, netpath);
+		break;
+	case VNIC_PRINP_LINKDOWN:
+		vnic->primary_path.carrier = 0;
+		break;
+	case VNIC_SECNP_LINKDOWN:
+		if (vnic->state == VNIC_UNINITIALIZED)
+			netpath_timer_stop(&vnic->secondary_path);
+		vnic->secondary_path.carrier = 0;
+		break;
+	case VNIC_PRINP_DISCONNECTED:
+		netpath = &vnic->primary_path;
+		netpath_timer_stop(netpath);
+		netpath->carrier = 0;
+		update_path_and_reconnect(netpath, vnic);
+		break;
+	case VNIC_SECNP_DISCONNECTED:
+		netpath = &vnic->secondary_path;
+		netpath_timer_stop(netpath);
+		netpath->carrier = 0;
+		update_path_and_reconnect(netpath, vnic);
+		break;
+	case VNIC_PRINP_SETLINK:
+		netpath = vnic->current_path;
+		if (!netpath || !netpath->viport)
+			break;
+		viport_set_link(netpath->viport,
+				vnic->netdevice->flags,
+				vnic->netdevice->mtu);
+		break;
+	case VNIC_SECNP_SETLINK:
+		netpath = &vnic->secondary_path;
+		if (!netpath || !netpath->viport)
+			break;
+		viport_set_link(netpath->viport,
+				vnic->netdevice->flags,
+				vnic->netdevice->mtu);
+		break;
+	case VNIC_NP_FREEVNIC:
+		vnic_handle_free_vnic_evt(vnic);
+		kfree(vnic);
+		vnic = NULL;
+		break;
+	}
+	return vnic;
+}
+
+static int vnic_npevent_statemachine(void *context)
+{
+	struct vnic_npevent	*vnic_link_evt;
+	enum vnic_npevent_type	npevt_type;
+	struct vnic		*vnic;
+	int			last_carrier;
+	int			other_path_ok = 0;
+	struct netpath		*last_path;
+
+	while (!vnic_npevent_thread_end ||
+	       !list_empty(&vnic_npevent_list)) {
+		unsigned long flags;
+
+		wait_event_interruptible(vnic_npevent_queue,
+					 !list_empty(&vnic_npevent_list)
+					 || vnic_npevent_thread_end);
+		spin_lock_irqsave(&vnic_npevent_list_lock, flags);
+		if (list_empty(&vnic_npevent_list)) {
+			spin_unlock_irqrestore(&vnic_npevent_list_lock,
+					       flags);
+			VNIC_INFO("netpath statemachine wake"
+				  " on empty list\n");
+			continue;
+		}
+
+		vnic_link_evt = list_entry(vnic_npevent_list.next,
+					   struct vnic_npevent,
+					   list_ptrs);
+		list_del(&vnic_link_evt->list_ptrs);
+		spin_unlock_irqrestore(&vnic_npevent_list_lock, flags);
+		vnic = vnic_link_evt->vnic;
+		npevt_type = vnic_link_evt->event_type;
+		kfree(vnic_link_evt);
+
+		if (vnic->current_path == &vnic->secondary_path)
+			other_path_ok = vnic->primary_path.carrier;
+		else if (vnic->current_path == &vnic->primary_path)
+			other_path_ok = vnic->secondary_path.carrier;
+
+		vnic = vnic_handle_npevent(vnic, npevt_type);
+
+		if (!vnic)
+			continue;
+
+		last_carrier = vnic->carrier;
+		last_path = vnic->current_path;
+
+		if (!vnic->current_path ||
+		    !vnic->current_path->carrier) {
+			vnic->carrier = 0;
+			vnic->current_path = NULL;
+			vnic->netdevice->features &= ~NETIF_F_IP_CSUM;
+		}
+
+		if (!vnic->carrier)
+			vnic_carrier_loss(vnic, last_path);
+		else if ((vnic->current_path != &vnic->primary_path) &&
+			 (vnic->config->prefer_primary) &&
+			 (vnic->primary_path.carrier))
+				vnic_check_primary_path_timer(vnic);
+
+		if (last_path)
+			vnic_report_path_change(vnic, last_path,
+						other_path_ok);
+
+		VNIC_INFO("new netpath=%s, carrier=%d\n",
+			  netpath_to_string(vnic, vnic->current_path),
+			  vnic->carrier);
+
+		if (vnic->current_path != last_path)
+			vnic_handle_path_change(vnic, &last_path);
+
+		if (vnic->carrier != last_carrier) {
+			if (vnic->carrier) {
+				VNIC_INFO("netif_carrier_on\n");
+				netif_carrier_on(vnic->netdevice);
+				vnic_carrier_loss_stats(vnic);
+			} else {
+				VNIC_INFO("netif_carrier_off\n");
+				netif_carrier_off(vnic->netdevice);
+				vnic_disconn_stats(vnic);
+			}
+
+		}
+	}
+	complete_and_exit(&vnic_npevent_thread_exit, 0);
+	return 0;
+}
+
+void vnic_npevent_queue_evt(struct netpath *netpath,
+			    enum vnic_npevent_type evt)
+{
+	struct vnic_npevent *npevent;
+	unsigned long flags;
+
+	npevent = kmalloc(sizeof *npevent, GFP_ATOMIC);
+	if (!npevent) {
+		VNIC_ERROR("Could not allocate memory for vnic event\n");
+		return;
+	}
+	npevent->vnic = netpath->parent;
+	npevent->event_type = evt;
+	INIT_LIST_HEAD(&npevent->list_ptrs);
+	spin_lock_irqsave(&vnic_npevent_list_lock, flags);
+	list_add_tail(&npevent->list_ptrs, &vnic_npevent_list);
+	spin_unlock_irqrestore(&vnic_npevent_list_lock, flags);
+	wake_up(&vnic_npevent_queue);
+}
+
+void vnic_npevent_dequeue_evt(struct netpath *netpath,
+			      enum vnic_npevent_type evt)
+{
+	unsigned long flags;
+	struct vnic_npevent *npevt, *tmp;
+	struct vnic *vnic = netpath->parent;
+
+	spin_lock_irqsave(&vnic_npevent_list_lock, flags);
+	if (list_empty(&vnic_npevent_list))
+		goto out;
+	list_for_each_entry_safe(npevt, tmp, &vnic_npevent_list,
+				 list_ptrs) {
+		if ((npevt->vnic == vnic) &&
+		    (npevt->event_type == evt)) {
+			list_del(&npevt->list_ptrs);
+			kfree(npevt);
+			break;
+		}
+	}
+out:
+	spin_unlock_irqrestore(&vnic_npevent_list_lock, flags);
+}
+
+static int vnic_npevent_start(void)
+{
+	VNIC_FUNCTION("vnic_npevent_start()\n");
+
+	spin_lock_init(&vnic_npevent_list_lock);
+	vnic_npevent_thread = kthread_run(vnic_npevent_statemachine, NULL,
+						"qlgc_vnic_npevent_s_m");
+	if (IS_ERR(vnic_npevent_thread)) {
+		printk(KERN_WARNING PFX "failed to create vnic npevent"
+		       " thread; error %d\n",
+			(int) PTR_ERR(vnic_npevent_thread));
+		vnic_npevent_thread = NULL;
+		return 1;
+	}
+
+	return 0;
+}
+
+void vnic_npevent_cleanup(void)
+{
+	if (vnic_npevent_thread) {
+		vnic_npevent_thread_end = 1;
+		wake_up(&vnic_npevent_queue);
+		wait_for_completion(&vnic_npevent_thread_exit);
+		vnic_npevent_thread = NULL;
+	}
+}
+
+static void vnic_setup(struct net_device *device)
+{
+	ether_setup(device);
+
+	/* ether_setup is used to fill
+	 * device parameters for ethernet devices.
+	 * We override some of the parameters
+	 * which are specific to VNIC.
+	 */
+	device->get_stats		= vnic_get_stats;
+	device->open			= vnic_open;
+	device->stop			= vnic_stop;
+	device->hard_start_xmit		= vnic_hard_start_xmit;
+	device->tx_timeout		= vnic_tx_timeout;
+	device->set_multicast_list	= vnic_set_multicast_list;
+	device->set_mac_address		= vnic_set_mac_address;
+	device->change_mtu		= vnic_change_mtu;
+	device->watchdog_timeo 		= 10 * HZ;
+	device->features		= 0;
+}
+
+struct vnic *vnic_allocate(struct vnic_config *config)
+{
+	struct vnic *vnic = NULL;
+
+	VNIC_FUNCTION("vnic_allocate()\n");
+	vnic = kzalloc(sizeof *vnic, GFP_KERNEL);
+	if (!vnic) {
+		VNIC_ERROR("failed allocating vnic structure\n");
+		return NULL;
+	}
+
+	spin_lock_init(&vnic->lock);
+	vnic_alloc_stats(vnic);
+	vnic->state = VNIC_UNINITIALIZED;
+	vnic->config = config;
+
+	/* Allocating a VNIC network device.
+	 * The private data structure for VNIC will be taken care by the
+	 * VNIC driver, hence setting size of private data structure to 0.
+	 */
+	vnic->netdevice = alloc_netdev((int) 0, config->name, vnic_setup);
+	vnic->netdevice->priv = (void *)vnic;
+
+	netpath_init(&vnic->primary_path, vnic, 0);
+	netpath_init(&vnic->secondary_path, vnic, 1);
+
+	vnic->current_path = NULL;
+
+	list_add_tail(&vnic->list_ptrs, &vnic_list);
+
+	return vnic;
+}
+
+void vnic_free(struct vnic *vnic)
+{
+	VNIC_FUNCTION("vnic_free()\n");
+	list_del(&vnic->list_ptrs);
+	vnic_npevent_queue_evt(&vnic->primary_path, VNIC_NP_FREEVNIC);
+}
+
+static void __exit vnic_cleanup(void)
+{
+	VNIC_FUNCTION("vnic_cleanup()\n");
+
+	VNIC_INIT("unloading %s\n", MODULEDETAILS);
+
+	while (!list_empty(&vnic_list)) {
+		struct vnic *vnic =
+		    list_entry(vnic_list.next, struct vnic, list_ptrs);
+		vnic_free(vnic);
+	}
+
+	vnic_npevent_cleanup();
+	viport_cleanup();
+	vnic_ib_cleanup();
+}
+
+static int __init vnic_init(void)
+{
+	int ret;
+	VNIC_FUNCTION("vnic_init()\n");
+	VNIC_INIT("Initializing %s\n", MODULEDETAILS);
+
+	ret = config_start();
+	if (ret) {
+		VNIC_ERROR("config_start failed\n");
+		goto failure;
+	}
+
+	ret = vnic_ib_init();
+	if (ret) {
+		VNIC_ERROR("ib_start failed\n");
+		goto failure;
+	}
+
+	ret = viport_start();
+	if (ret) {
+		VNIC_ERROR("viport_start failed\n");
+		goto failure;
+	}
+
+	ret = vnic_npevent_start();
+	if (ret) {
+		VNIC_ERROR("vnic_npevent_start failed\n");
+		goto failure;
+	}
+
+	return 0;
+failure:
+	vnic_cleanup();
+	return ret;
+}
+
+module_init(vnic_init);
+module_exit(vnic_cleanup);
diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_main.h b/drivers/infiniband/ulp/qlgc_vnic/vnic_main.h
new file mode 100644
index 0000000..c5ccd8b
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_main.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2006 QLogic, 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 VNIC_MAIN_H_INCLUDED
+#define VNIC_MAIN_H_INCLUDED
+
+#include <linux/timex.h>
+#include <linux/netdevice.h>
+#include <linux/kthread.h>
+#include <linux/fs.h>
+
+#include "vnic_config.h"
+#include "vnic_netpath.h"
+
+extern u16 vnic_max_mtu;
+extern struct list_head vnic_list;
+extern struct attribute_group vnic_stats_attr_group;
+extern cycles_t recv_ref;
+
+enum vnic_npevent_type {
+	VNIC_PRINP_CONNECTED	= 0,
+	VNIC_PRINP_DISCONNECTED	= 1,
+	VNIC_PRINP_LINKUP	= 2,
+	VNIC_PRINP_LINKDOWN	= 3,
+	VNIC_PRINP_TIMEREXPIRED	= 4,
+	VNIC_PRINP_SETLINK	= 5,
+
+	/* used to figure out PRI vs SEC types for dbg msg*/
+	VNIC_PRINP_LASTTYPE     = VNIC_PRINP_SETLINK,
+
+	VNIC_SECNP_CONNECTED	= 6,
+	VNIC_SECNP_DISCONNECTED	= 7,
+	VNIC_SECNP_LINKUP	= 8,
+	VNIC_SECNP_LINKDOWN	= 9,
+	VNIC_SECNP_TIMEREXPIRED	= 10,
+	VNIC_SECNP_SETLINK	= 11,
+
+	/* used to figure out PRI vs SEC types for dbg msg*/
+	VNIC_SECNP_LASTTYPE     = VNIC_SECNP_SETLINK,
+
+	VNIC_NP_FREEVNIC	= 12,
+};
+
+/* This array should be kept next to enum above since a change to npevent_type
+   enum affects this array. */
+static const char *const vnic_npevent_str[] = {
+    "PRIMARY CONNECTED",
+    "PRIMARY DISCONNECTED",
+    "PRIMARY CARRIER",
+    "PRIMARY NO CARRIER",
+    "PRIMARY TIMER EXPIRED",
+    "PRIMARY SETLINK",
+    "SECONDARY CONNECTED",
+    "SECONDARY DISCONNECTED",
+    "SECONDARY CARRIER",
+    "SECONDARY NO CARRIER",
+    "SECONDARY TIMER EXPIRED",
+    "SECONDARY SETLINK",
+    "FREE VNIC",
+};
+
+
+struct vnic_npevent {
+	struct list_head	list_ptrs;
+	struct vnic		*vnic;
+	enum vnic_npevent_type	event_type;
+};
+
+void vnic_npevent_queue_evt(struct netpath *netpath,
+			    enum vnic_npevent_type evt);
+void vnic_npevent_dequeue_evt(struct netpath *netpath,
+			      enum vnic_npevent_type evt);
+
+enum vnic_state {
+	VNIC_UNINITIALIZED	= 0,
+	VNIC_REGISTERED		= 1
+};
+
+struct vnic {
+	struct list_head		list_ptrs;
+	enum vnic_state			state;
+	struct vnic_config		*config;
+	struct netpath			*current_path;
+	struct netpath			primary_path;
+	struct netpath			secondary_path;
+	int				open;
+	int				carrier;
+	int				xmit_started;
+	int				mac_set;
+	struct net_device_stats 	stats;
+	struct net_device		*netdevice;
+	struct dev_info			dev_info;
+	struct dev_mc_list		*mc_list;
+	int				mc_list_len;
+	int				mc_count;
+	spinlock_t			lock;
+#ifdef CONFIG_INFINIBAND_QLGC_VNIC_STATS
+	struct {
+		cycles_t	start_time;
+		cycles_t	conn_time;
+		cycles_t	disconn_ref;	/* intermediate time */
+		cycles_t	disconn_time;
+		u32		disconn_num;
+		cycles_t	xmit_time;
+		u32		xmit_num;
+		u32		xmit_fail;
+		cycles_t	recv_time;
+		u32		recv_num;
+		u32		multicast_recv_num;
+		cycles_t	xmit_ref;	/* intermediate time */
+		cycles_t	xmit_off_time;
+		u32		xmit_off_num;
+		cycles_t	carrier_ref;	/* intermediate time */
+		cycles_t	carrier_off_time;
+		u32		carrier_off_num;
+	} statistics;
+	struct dev_info		stat_info;
+#endif	/* CONFIG_INFINIBAND_QLGC_VNIC_STATS */
+};
+
+struct vnic *vnic_allocate(struct vnic_config *config);
+
+void vnic_free(struct vnic *vnic);
+
+void vnic_connected(struct vnic *vnic, struct netpath *netpath);
+void vnic_disconnected(struct vnic *vnic, struct netpath *netpath);
+
+void vnic_link_up(struct vnic *vnic, struct netpath *netpath);
+void vnic_link_down(struct vnic *vnic, struct netpath *netpath);
+
+void vnic_stop_xmit(struct vnic *vnic, struct netpath *netpath);
+void vnic_restart_xmit(struct vnic *vnic, struct netpath *netpath);
+
+void vnic_recv_packet(struct vnic *vnic, struct netpath *netpath,
+		      struct sk_buff *skb);
+void vnic_npevent_cleanup(void);
+void completion_callback_cleanup(struct vnic_ib_conn *ib_conn);
+#endif	/* VNIC_MAIN_H_INCLUDED */

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [ofa-general] [PATCH 02/13] QLogic VNIC: Netpath - abstraction of connection to EVIC/VEx
  2008-04-30 17:15 [ofa-general] [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Ramachandra K
  2008-04-30 17:16 ` [ofa-general] [PATCH 01/13] QLogic VNIC: Driver - netdev implementation Ramachandra K
@ 2008-04-30 17:16 ` Ramachandra K
  2008-04-30 17:17 ` [ofa-general] [PATCH 03/13] QLogic VNIC: Implementation of communication protocol with EVIC/VEx Ramachandra K
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 32+ messages in thread
From: Ramachandra K @ 2008-04-30 17:16 UTC (permalink / raw)
  To: rdreier, general, netdev; +Cc: poornima.kamath, amar.mudrankit

From: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>

This patch implements the netpath layer of QLogic VNIC. Netpath is an 
abstraction of a connection to EVIC. It primarily includes the 
implementation which maintains the timers to monitor the status of
the connection to EVIC/VEx.

Signed-off-by: Poornima Kamath <poornima.kamath@qlogic.com>
Signed-off-by: Amar Mudrankit <amar.mudrankit@qlogic.com>
---

 drivers/infiniband/ulp/qlgc_vnic/vnic_netpath.c |  112 +++++++++++++++++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_netpath.h |   80 ++++++++++++++++
 2 files changed, 192 insertions(+), 0 deletions(-)
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_netpath.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_netpath.h

diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_netpath.c b/drivers/infiniband/ulp/qlgc_vnic/vnic_netpath.c
new file mode 100644
index 0000000..820b996
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_netpath.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2006 QLogic, 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/netdevice.h>
+#include <linux/skbuff.h>
+
+#include "vnic_util.h"
+#include "vnic_main.h"
+#include "vnic_viport.h"
+#include "vnic_netpath.h"
+
+static void vnic_npevent_timeout(unsigned long data)
+{
+	struct netpath *netpath = (struct netpath *)data;
+
+	if (netpath->second_bias)
+		vnic_npevent_queue_evt(netpath, VNIC_SECNP_TIMEREXPIRED);
+	else
+		vnic_npevent_queue_evt(netpath, VNIC_PRINP_TIMEREXPIRED);
+}
+
+void netpath_timer(struct netpath *netpath, int timeout)
+{
+	if (netpath->timer_state == NETPATH_TS_ACTIVE)
+		del_timer_sync(&netpath->timer);
+	if (timeout) {
+		init_timer(&netpath->timer);
+		netpath->timer_state = NETPATH_TS_ACTIVE;
+		netpath->timer.expires = jiffies + timeout;
+		netpath->timer.data = (unsigned long)netpath;
+		netpath->timer.function = vnic_npevent_timeout;
+		add_timer(&netpath->timer);
+	} else
+		vnic_npevent_timeout((unsigned long)netpath);
+}
+
+void netpath_timer_stop(struct netpath *netpath)
+{
+	if (netpath->timer_state != NETPATH_TS_ACTIVE)
+		return;
+	del_timer_sync(&netpath->timer);
+	if (netpath->second_bias)
+		vnic_npevent_dequeue_evt(netpath, VNIC_SECNP_TIMEREXPIRED);
+	else
+		vnic_npevent_dequeue_evt(netpath, VNIC_PRINP_TIMEREXPIRED);
+
+	netpath->timer_state = NETPATH_TS_IDLE;
+}
+
+void netpath_free(struct netpath *netpath)
+{
+	if (!netpath->viport)
+		return;
+	viport_free(netpath->viport);
+	netpath->viport = NULL;
+	sysfs_remove_group(&netpath->dev_info.dev.kobj,
+			   &vnic_path_attr_group);
+	device_unregister(&netpath->dev_info.dev);
+	wait_for_completion(&netpath->dev_info.released);
+}
+
+void netpath_init(struct netpath *netpath, struct vnic *vnic,
+		  int second_bias)
+{
+	netpath->parent = vnic;
+	netpath->carrier = 0;
+	netpath->viport = NULL;
+	netpath->second_bias = second_bias;
+	netpath->timer_state = NETPATH_TS_IDLE;
+	init_timer(&netpath->timer);
+}
+
+const char *netpath_to_string(struct vnic *vnic, struct netpath *netpath)
+{
+	if (!netpath)
+		return "NULL";
+	else if (netpath == &vnic->primary_path)
+		return "PRIMARY";
+	else if (netpath == &vnic->secondary_path)
+		return "SECONDARY";
+	else
+		return "UNKNOWN";
+}
diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_netpath.h b/drivers/infiniband/ulp/qlgc_vnic/vnic_netpath.h
new file mode 100644
index 0000000..1259ae0
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_netpath.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2006 QLogic, 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 VNIC_NETPATH_H_INCLUDED
+#define VNIC_NETPATH_H_INCLUDED
+
+#include <linux/spinlock.h>
+
+#include "vnic_sys.h"
+
+struct viport;
+struct vnic;
+
+enum netpath_ts {
+	NETPATH_TS_IDLE		= 0,
+	NETPATH_TS_ACTIVE	= 1,
+	NETPATH_TS_EXPIRED	= 2
+};
+
+struct netpath {
+	int			carrier;
+	struct vnic		*parent;
+	struct viport		*viport;
+	size_t			path_idx;
+	u32			connect_time;
+	int			second_bias;
+	u8			is_primary_path;
+	u8 			delay_reconnect;
+	int 			cleanup_started;
+	struct timer_list	timer;
+	enum netpath_ts		timer_state;
+	struct dev_info		dev_info;
+};
+
+void netpath_init(struct netpath *netpath, struct vnic *vnic,
+		  int second_bias);
+void netpath_free(struct netpath *netpath);
+
+void netpath_timer(struct netpath *netpath, int timeout);
+void netpath_timer_stop(struct netpath *netpath);
+
+const char *netpath_to_string(struct vnic *vnic, struct netpath *netpath);
+
+#define netpath_get_hw_addr(netpath, address)		\
+	viport_get_hw_addr((netpath)->viport, address)
+#define netpath_is_connected(netpath)			\
+	(netpath->state == NETPATH_CONNECTED)
+#define netpath_can_tx_csum(netpath)			\
+	viport_can_tx_csum(netpath->viport)
+
+#endif	/* VNIC_NETPATH_H_INCLUDED */

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [ofa-general] [PATCH 03/13] QLogic VNIC: Implementation of communication protocol with EVIC/VEx
  2008-04-30 17:15 [ofa-general] [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Ramachandra K
  2008-04-30 17:16 ` [ofa-general] [PATCH 01/13] QLogic VNIC: Driver - netdev implementation Ramachandra K
  2008-04-30 17:16 ` [ofa-general] [PATCH 02/13] QLogic VNIC: Netpath - abstraction of connection to EVIC/VEx Ramachandra K
@ 2008-04-30 17:17 ` Ramachandra K
  2008-04-30 17:17 ` [ofa-general] [PATCH 04/13] QLogic VNIC: Implementation of Control path of communication protocol Ramachandra K
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 32+ messages in thread
From: Ramachandra K @ 2008-04-30 17:17 UTC (permalink / raw)
  To: rdreier, general, netdev; +Cc: poornima.kamath, amar.mudrankit

From: Poornima Kamath <poornima.kamath@qlogic.com>

Implementation of the statemachine for the protocol used while 
communicating with the EVIC. The patch also implements the viport
abstraction which represents the virtual ethernet port on EVIC.

Signed-off-by: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>
Signed-off-by: Amar Mudrankit <amar.mudrankit@qlogic.com>
---

 drivers/infiniband/ulp/qlgc_vnic/vnic_viport.c | 1233 ++++++++++++++++++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_viport.h |  176 +++
 2 files changed, 1409 insertions(+), 0 deletions(-)
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_viport.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_viport.h

diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_viport.c b/drivers/infiniband/ulp/qlgc_vnic/vnic_viport.c
new file mode 100644
index 0000000..e44e31b
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_viport.c
@@ -0,0 +1,1233 @@
+/*
+ * Copyright (c) 2006 QLogic, 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/list.h>
+#include <linux/spinlock.h>
+#include <linux/netdevice.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/net.h>
+
+#include "vnic_util.h"
+#include "vnic_main.h"
+#include "vnic_viport.h"
+#include "vnic_netpath.h"
+#include "vnic_control.h"
+#include "vnic_data.h"
+#include "vnic_config.h"
+#include "vnic_control_pkt.h"
+
+#define VIPORT_DISCONN_TIMER	10000 	 /* 10 seconds */
+
+#define MAX_RETRY_INTERVAL 	  20000  /* 20 seconds */
+#define RETRY_INCREMENT		  5000   /* 5 seconds  */
+#define MAX_CONNECT_RETRY_TIMEOUT 600000 /* 10 minutes */
+
+static DECLARE_WAIT_QUEUE_HEAD(viport_queue);
+static LIST_HEAD(viport_list);
+static DECLARE_COMPLETION(viport_thread_exit);
+static spinlock_t viport_list_lock;
+
+static struct task_struct *viport_thread;
+static int viport_thread_end;
+
+static void viport_timer(struct viport *viport, int timeout);
+
+struct viport *viport_allocate(struct viport_config *config)
+{
+	struct viport *viport;
+
+	VIPORT_FUNCTION("viport_allocate()\n");
+	viport = kzalloc(sizeof *viport, GFP_KERNEL);
+	if (!viport) {
+		VIPORT_ERROR("failed allocating viport structure\n");
+		return NULL;
+	}
+
+	viport->state = VIPORT_DISCONNECTED;
+	viport->link_state = LINK_FIRSTCONNECT;
+	viport->connect = WAIT;
+	viport->new_mtu = 1500;
+	viport->new_flags = 0;
+	viport->config = config;
+	viport->connect = DELAY;
+	viport->data.max_mtu = vnic_max_mtu;
+	spin_lock_init(&viport->lock);
+	init_waitqueue_head(&viport->stats_queue);
+	init_waitqueue_head(&viport->disconnect_queue);
+	init_waitqueue_head(&viport->reference_queue);
+	INIT_LIST_HEAD(&viport->list_ptrs);
+
+	vnic_mc_init(viport);
+
+	return viport;
+}
+
+void viport_connect(struct viport *viport, int delay)
+{
+	VIPORT_FUNCTION("viport_connect()\n");
+
+	if (viport->connect != DELAY)
+		viport->connect = (delay) ? DELAY : NOW;
+	if (viport->link_state == LINK_FIRSTCONNECT) {
+		u32 duration;
+		duration = (net_random() & 0x1ff);
+		if (!viport->parent->is_primary_path)
+			duration += 0x1ff;
+		viport->link_state = LINK_RETRYWAIT;
+		viport_timer(viport, duration);
+	} else
+		viport_kick(viport);
+}
+
+void viport_disconnect(struct viport *viport)
+{
+	VIPORT_FUNCTION("viport_disconnect()\n");
+	viport->disconnect = 1;
+	viport_failure(viport);
+	wait_event(viport->disconnect_queue, viport->disconnect == 0);
+}
+
+void viport_free(struct viport *viport)
+{
+	VIPORT_FUNCTION("viport_free()\n");
+	vnic_mc_uninit(viport);
+	viport_disconnect(viport);	/* NOTE: this can sleep */
+	kfree(viport->config);
+	kfree(viport);
+}
+
+void viport_set_link(struct viport *viport, u16 flags, u16 mtu)
+{
+	unsigned long localflags;
+	int i;
+
+	VIPORT_FUNCTION("viport_set_link()\n");
+	if (mtu > data_max_mtu(&viport->data)) {
+		VIPORT_ERROR("configuration error."
+			     " mtu of %d unsupported by %s\n", mtu,
+			     config_viport_name(viport->config));
+		goto failure;
+	}
+
+	spin_lock_irqsave(&viport->lock, localflags);
+	flags &= IFF_UP | IFF_ALLMULTI | IFF_PROMISC;
+	if ((viport->new_flags != flags)
+	    || (viport->new_mtu != mtu)) {
+		viport->new_flags = flags;
+		viport->new_mtu = mtu;
+		viport->updates |= NEED_LINK_CONFIG;
+		if (viport->features_supported & VNIC_FEAT_INBOUND_IB_MC) {
+			if (((viport->mtu <= MCAST_MSG_SIZE) && (mtu >  MCAST_MSG_SIZE)) ||
+			    ((viport->mtu >  MCAST_MSG_SIZE) && (mtu <= MCAST_MSG_SIZE))) {
+			/*
+			 * MTU value will enable/disable the multicast. In
+			 * either case, need to send the CMD_CONFIG_ADDRESS2 to
+			 * EVIC. Hence, setting the NEED_ADDRESS_CONFIG flag.
+			 */
+				viport->updates |= NEED_ADDRESS_CONFIG;
+				if (mtu <= MCAST_MSG_SIZE) {
+				    VIPORT_PRINT("%s: MTU changed; "
+						"old:%d new:%d (threshold:%d);"
+						" MULTICAST will be enabled.\n",
+						config_viport_name(viport->config),
+						viport->mtu, mtu,
+						(int)MCAST_MSG_SIZE);
+				} else {
+				    VIPORT_PRINT("%s: MTU changed; "
+						"old:%d new:%d (threshold:%d); "
+						"MULTICAST will be disabled.\n",
+						config_viport_name(viport->config),
+						viport->mtu, mtu,
+						(int)MCAST_MSG_SIZE);
+				}
+				/* When we resend these addresses, EVIC will
+				 * send mgid=0 back in response. So no need to
+				 * shutoff ib_multicast.
+				 */
+				for (i = MCAST_ADDR_START; i < viport->num_mac_addresses; i++) {
+					if (viport->mac_addresses[i].valid)
+						viport->mac_addresses[i].operation = VNIC_OP_SET_ENTRY;
+				}
+			}
+		}
+		viport_kick(viport);
+	}
+
+	spin_unlock_irqrestore(&viport->lock, localflags);
+	return;
+failure:
+	viport_failure(viport);
+}
+
+int viport_set_unicast(struct viport *viport, u8 *address)
+{
+	unsigned long flags;
+	int	ret = -1;
+	VIPORT_FUNCTION("viport_set_unicast()\n");
+	spin_lock_irqsave(&viport->lock, flags);
+
+	if (!viport->mac_addresses)
+		goto out;
+
+	if (memcmp(viport->mac_addresses[UNICAST_ADDR].address,
+		   address, ETH_ALEN)) {
+		memcpy(viport->mac_addresses[UNICAST_ADDR].address,
+		       address, ETH_ALEN);
+		viport->mac_addresses[UNICAST_ADDR].operation
+		    = VNIC_OP_SET_ENTRY;
+		viport->updates |= NEED_ADDRESS_CONFIG;
+		viport_kick(viport);
+	}
+	ret = 0;
+out:
+	spin_unlock_irqrestore(&viport->lock, flags);
+	return ret;
+}
+
+int viport_set_multicast(struct viport *viport,
+			 struct dev_mc_list *mc_list, int mc_count)
+{
+	u32 old_update_list;
+	int i;
+	int ret = -1;
+	unsigned long flags;
+
+	VIPORT_FUNCTION("viport_set_multicast()\n");
+	spin_lock_irqsave(&viport->lock, flags);
+
+	if (!viport->mac_addresses)
+		goto out;
+
+	old_update_list = viport->updates;
+	if (mc_count > viport->num_mac_addresses - MCAST_ADDR_START)
+		viport->updates |= NEED_LINK_CONFIG | MCAST_OVERFLOW;
+	else {
+		if (mc_count == 0) {
+			ret = 0;
+			goto out;
+		}
+		if (viport->updates & MCAST_OVERFLOW) {
+			viport->updates &= ~MCAST_OVERFLOW;
+			viport->updates |= NEED_LINK_CONFIG;
+		}
+		for (i = MCAST_ADDR_START; i < mc_count + MCAST_ADDR_START;
+						i++, mc_list = mc_list->next) {
+			if (viport->mac_addresses[i].valid &&
+				!memcmp(viport->mac_addresses[i].address,
+						mc_list->dmi_addr, ETH_ALEN))
+			continue;
+		memcpy(viport->mac_addresses[i].address,
+					 mc_list->dmi_addr, ETH_ALEN);
+		viport->mac_addresses[i].valid = 1;
+		viport->mac_addresses[i].operation = VNIC_OP_SET_ENTRY;
+	}
+	for (; i < viport->num_mac_addresses; i++) {
+		if (!viport->mac_addresses[i].valid)
+			continue;
+		viport->mac_addresses[i].valid = 0;
+		viport->mac_addresses[i].operation = VNIC_OP_SET_ENTRY;
+	}
+	if (mc_count)
+		viport->updates |= NEED_ADDRESS_CONFIG;
+	}
+
+	if (viport->updates != old_update_list)
+		viport_kick(viport);
+	ret = 0;
+out:
+	spin_unlock_irqrestore(&viport->lock, flags);
+	return ret;
+}
+
+static inline void viport_disable_multicast(struct viport *viport)
+{
+	VIPORT_INFO("turned off IB_MULTICAST\n");
+	viport->config->control_config.ib_multicast = 0;
+	viport->config->control_config.ib_config.conn_data.features_supported &=
+				__constant_cpu_to_be32((u32)~VNIC_FEAT_INBOUND_IB_MC);
+	viport->link_state = LINK_RESET;
+}
+
+void viport_get_stats(struct viport *viport,
+		     struct net_device_stats *stats)
+{
+	unsigned long flags;
+
+	VIPORT_FUNCTION("viport_get_stats()\n");
+	if (jiffies > viport->last_stats_time +
+		      viport->config->stats_interval) {
+
+		spin_lock_irqsave(&viport->lock, flags);
+		viport->updates |= NEED_STATS;
+		/* increment reference count which indicates
+		 * that viport structure is being used, which
+		 * prevents its freeing when this task sleeps
+		 */
+		viport->reference_count++;
+		spin_unlock_irqrestore(&viport->lock, flags);
+		viport_kick(viport);
+		wait_event(viport->stats_queue,
+			   !(viport->updates & NEED_STATS));
+
+		if (viport->stats.ethernet_status)
+			vnic_link_up(viport->vnic, viport->parent);
+		else
+			vnic_link_down(viport->vnic, viport->parent);
+
+	} else {
+		spin_lock_irqsave(&viport->lock, flags);
+		viport->reference_count++;
+		spin_unlock_irqrestore(&viport->lock, flags);
+	}
+
+	stats->rx_packets = be64_to_cpu(viport->stats.if_in_ok);
+	stats->tx_packets = be64_to_cpu(viport->stats.if_out_ok);
+	stats->rx_bytes   = be64_to_cpu(viport->stats.if_in_octets);
+	stats->tx_bytes   = be64_to_cpu(viport->stats.if_out_octets);
+	stats->rx_errors  = be64_to_cpu(viport->stats.if_in_errors);
+	stats->tx_errors  = be64_to_cpu(viport->stats.if_out_errors);
+	stats->rx_dropped = 0;	/* EIOC doesn't track */
+	stats->tx_dropped = 0;	/* EIOC doesn't track */
+	stats->multicast  = be64_to_cpu(viport->stats.if_in_nucast_pkts);
+	stats->collisions = 0;	/* EIOC doesn't track */
+
+	spin_lock_irqsave(&viport->lock, flags);
+	viport->reference_count--;
+	spin_unlock_irqrestore(&viport->lock, flags);
+	wake_up(&viport->reference_queue);
+}
+
+int viport_xmit_packet(struct viport *viport, struct sk_buff *skb)
+{
+	int status = -1;
+	unsigned long flags;
+
+	VIPORT_FUNCTION("viport_xmit_packet()\n");
+	spin_lock_irqsave(&viport->lock, flags);
+	if (viport->state == VIPORT_CONNECTED)
+		status = data_xmit_packet(&viport->data, skb);
+	spin_unlock_irqrestore(&viport->lock, flags);
+
+	return status;
+}
+
+void viport_kick(struct viport *viport)
+{
+	unsigned long flags;
+
+	VIPORT_FUNCTION("viport_kick()\n");
+	spin_lock_irqsave(&viport_list_lock, flags);
+	if (list_empty(&viport->list_ptrs)) {
+		list_add_tail(&viport->list_ptrs, &viport_list);
+		wake_up(&viport_queue);
+	}
+	spin_unlock_irqrestore(&viport_list_lock, flags);
+}
+
+void viport_failure(struct viport *viport)
+{
+	unsigned long flags;
+
+	VIPORT_FUNCTION("viport_failure()\n");
+	spin_lock_irqsave(&viport_list_lock, flags);
+	viport->errored = 1;
+	if (list_empty(&viport->list_ptrs)) {
+		list_add_tail(&viport->list_ptrs, &viport_list);
+		wake_up(&viport_queue);
+	}
+	spin_unlock_irqrestore(&viport_list_lock, flags);
+}
+
+static void viport_timeout(unsigned long data)
+{
+	struct viport *viport;
+
+	VIPORT_FUNCTION("viport_timeout()\n");
+	viport = (struct viport *)data;
+	viport->timer_active = 0;
+	viport_kick(viport);
+}
+
+static void viport_timer(struct viport *viport, int timeout)
+{
+	VIPORT_FUNCTION("viport_timer()\n");
+	if (viport->timer_active)
+		del_timer(&viport->timer);
+	init_timer(&viport->timer);
+	viport->timer.expires = jiffies + timeout;
+	viport->timer.data = (unsigned long)viport;
+	viport->timer.function = viport_timeout;
+	viport->timer_active = 1;
+	add_timer(&viport->timer);
+}
+
+static void viport_timer_stop(struct viport *viport)
+{
+	VIPORT_FUNCTION("viport_timer_stop()\n");
+	if (viport->timer_active)
+		del_timer(&viport->timer);
+	viport->timer_active = 0;
+}
+
+static int viport_init_mac_addresses(struct viport *viport)
+{
+	struct vnic_address_op2	*temp;
+	unsigned long		flags;
+	int			i;
+
+	VIPORT_FUNCTION("viport_init_mac_addresses()\n");
+	i = viport->num_mac_addresses * sizeof *temp;
+	temp = kzalloc(viport->num_mac_addresses * sizeof *temp,
+		       GFP_KERNEL);
+	if (!temp) {
+		VIPORT_ERROR("failed allocating MAC address table\n");
+		return -ENOMEM;
+	}
+
+	spin_lock_irqsave(&viport->lock, flags);
+	viport->mac_addresses = temp;
+	for (i = 0; i < viport->num_mac_addresses; i++) {
+		viport->mac_addresses[i].index = cpu_to_be16(i);
+		viport->mac_addresses[i].vlan =
+				cpu_to_be16(viport->default_vlan);
+	}
+	memset(viport->mac_addresses[BROADCAST_ADDR].address,
+	       0xFF, ETH_ALEN);
+	viport->mac_addresses[BROADCAST_ADDR].valid = 1;
+	memcpy(viport->mac_addresses[UNICAST_ADDR].address,
+	       viport->hw_mac_address, ETH_ALEN);
+	viport->mac_addresses[UNICAST_ADDR].valid = 1;
+
+	spin_unlock_irqrestore(&viport->lock, flags);
+
+	return 0;
+}
+
+static inline void viport_match_mac_address(struct vnic *vnic,
+					    struct viport *viport)
+{
+	if (vnic && vnic->current_path &&
+	    viport == vnic->current_path->viport &&
+	    vnic->mac_set &&
+	    memcmp(vnic->netdevice->dev_addr, viport->hw_mac_address, ETH_ALEN)) {
+		VIPORT_ERROR("*** ERROR MAC address mismatch; "
+				"current = %02x:%02x:%02x:%02x:%02x:%02x "
+				"From EVIC = %02x:%02x:%02x:%02x:%02x:%02x\n",
+				vnic->netdevice->dev_addr[0],
+				vnic->netdevice->dev_addr[1],
+				vnic->netdevice->dev_addr[2],
+				vnic->netdevice->dev_addr[3],
+				vnic->netdevice->dev_addr[4],
+				vnic->netdevice->dev_addr[5],
+				viport->hw_mac_address[0],
+				viport->hw_mac_address[1],
+				viport->hw_mac_address[2],
+				viport->hw_mac_address[3],
+				viport->hw_mac_address[4],
+				viport->hw_mac_address[5]);
+	}
+}
+
+static int viport_handle_init_states(struct viport *viport)
+{
+	enum link_state old_state;
+
+	do {
+		switch (old_state = viport->link_state) {
+		case LINK_UNINITIALIZED:
+			LINK_STATE("state LINK_UNINITIALIZED\n");
+			viport->updates = 0;
+			/* cleanup_started will ensure that
+			 * no more get_stats request will be
+			 * be sent.Old stats will be returned
+			 */
+			viport->parent->cleanup_started = 1;
+			wake_up(&viport->stats_queue);
+			spin_lock_irq(&viport_list_lock);
+			list_del_init(&viport->list_ptrs);
+			spin_unlock_irq(&viport_list_lock);
+			spin_lock_irq(&viport->lock);
+			if (viport->reference_count) {
+				spin_unlock_irq(&viport->lock);
+				wait_event(viport->reference_queue,
+						 viport->reference_count == 0);
+			} else
+				spin_unlock_irq(&viport->lock);
+			/* No more references to viport structure
+			 * so it is safe to delete it by waking disconnect
+			 * queue
+			 */
+
+			viport->disconnect = 0;
+			wake_up(&viport->disconnect_queue);
+			break;
+		case LINK_INITIALIZE:
+			LINK_STATE("state LINK_INITIALIZE\n");
+			viport->errored = 0;
+			viport->connect = WAIT;
+			viport->last_stats_time = 0;
+			if (viport->disconnect)
+				viport->link_state = LINK_UNINITIALIZED;
+			else
+				viport->link_state = LINK_INITIALIZECONTROL;
+			break;
+		case LINK_INITIALIZECONTROL:
+			LINK_STATE("state LINK_INITIALIZECONTROL\n");
+			viport->pd = ib_alloc_pd(viport->config->ibdev);
+			if (IS_ERR(viport->pd))
+				viport->link_state = LINK_DISCONNECTED;
+			else if (control_init(&viport->control, viport,
+					    &viport->config->control_config,
+					    viport->pd)) {
+				ib_dealloc_pd(viport->pd);
+				viport->link_state = LINK_DISCONNECTED;
+
+			} else
+				viport->link_state = LINK_INITIALIZEDATA;
+			break;
+		case LINK_INITIALIZEDATA:
+			LINK_STATE("state LINK_INITIALIZEDATA\n");
+			if (data_init(&viport->data, viport,
+				      &viport->config->data_config,
+				      viport->pd))
+				viport->link_state = LINK_CLEANUPCONTROL;
+			else
+				viport->link_state = LINK_CONTROLCONNECT;
+			break;
+		default:
+			return -1;
+		}
+	} while (viport->link_state != old_state);
+
+	return 0;
+}
+
+static int viport_handle_control_states(struct viport *viport)
+{
+	enum link_state old_state;
+	struct vnic *vnic;
+
+	do {
+		switch (old_state = viport->link_state) {
+		case LINK_CONTROLCONNECT:
+			if (vnic_ib_cm_connect(&viport->control.ib_conn))
+				viport->link_state = LINK_CLEANUPDATA;
+			else
+				viport->link_state = LINK_CONTROLCONNECTWAIT;
+			break;
+		case LINK_CONTROLCONNECTWAIT:
+			LINK_STATE("state LINK_CONTROLCONNECTWAIT\n");
+			if (control_is_connected(&viport->control))
+				viport->link_state = LINK_INITVNICREQ;
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_CONTROLDISCONNECT;
+			}
+			break;
+		case LINK_INITVNICREQ:
+			LINK_STATE("state LINK_INITVNICREQ\n");
+			if (control_init_vnic_req(&viport->control))
+				viport->link_state = LINK_RESETCONTROL;
+			else
+				viport->link_state = LINK_INITVNICRSP;
+			break;
+		case LINK_INITVNICRSP:
+			LINK_STATE("state LINK_INITVNICRSP\n");
+			control_process_async(&viport->control);
+
+			if (!control_init_vnic_rsp(&viport->control,
+						  &viport->features_supported,
+						  viport->hw_mac_address,
+						  &viport->num_mac_addresses,
+						  &viport->default_vlan)) {
+				if (viport_init_mac_addresses(viport))
+					viport->link_state =
+							LINK_RESETCONTROL;
+				else {
+					viport->link_state =
+							LINK_BEGINDATAPATH;
+					/*
+					 * Ensure that the current path's MAC
+					 * address matches the one returned by
+					 * EVIC - we've had cases of mismatch
+					 * which then caused havoc.
+					 */
+					vnic = viport->parent->parent;
+					viport_match_mac_address(vnic, viport);
+				}
+			}
+
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_RESETCONTROL;
+			}
+			break;
+		default:
+			return -1;
+		}
+	} while (viport->link_state != old_state);
+
+	return 0;
+}
+
+static int viport_handle_data_states(struct viport *viport)
+{
+	enum link_state old_state;
+
+	do {
+		switch (old_state = viport->link_state) {
+		case LINK_BEGINDATAPATH:
+			LINK_STATE("state LINK_BEGINDATAPATH\n");
+			viport->link_state = LINK_CONFIGDATAPATHREQ;
+			break;
+		case LINK_CONFIGDATAPATHREQ:
+			LINK_STATE("state LINK_CONFIGDATAPATHREQ\n");
+			if (control_config_data_path_req(&viport->control,
+						data_path_id(&viport->
+							     data),
+						data_host_pool_max
+						(&viport->data),
+						data_eioc_pool_max
+						(&viport->data)))
+				viport->link_state = LINK_RESETCONTROL;
+			else
+				viport->link_state = LINK_CONFIGDATAPATHRSP;
+			break;
+		case LINK_CONFIGDATAPATHRSP:
+			LINK_STATE("state LINK_CONFIGDATAPATHRSP\n");
+			control_process_async(&viport->control);
+
+			if (!control_config_data_path_rsp(&viport->control,
+							 data_host_pool
+							 (&viport->data),
+							 data_eioc_pool
+							 (&viport->data),
+							 data_host_pool_max
+							 (&viport->data),
+							 data_eioc_pool_max
+							 (&viport->data),
+							 data_host_pool_min
+							 (&viport->data),
+							 data_eioc_pool_min
+							 (&viport->data)))
+				viport->link_state = LINK_DATACONNECT;
+
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_RESETCONTROL;
+			}
+			break;
+		case LINK_DATACONNECT:
+			LINK_STATE("state LINK_DATACONNECT\n");
+			if (data_connect(&viport->data))
+				viport->link_state = LINK_RESETCONTROL;
+			else
+				viport->link_state = LINK_DATACONNECTWAIT;
+			break;
+		case LINK_DATACONNECTWAIT:
+			LINK_STATE("state LINK_DATACONNECTWAIT\n");
+			control_process_async(&viport->control);
+			if (data_is_connected(&viport->data))
+				viport->link_state = LINK_XCHGPOOLREQ;
+
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_RESET;
+			}
+			break;
+		default:
+			return -1;
+		}
+	} while (viport->link_state != old_state);
+
+	return 0;
+}
+
+static int viport_handle_xchgpool_states(struct viport *viport)
+{
+	enum link_state old_state;
+
+	do {
+		switch (old_state = viport->link_state) {
+		case LINK_XCHGPOOLREQ:
+			LINK_STATE("state LINK_XCHGPOOLREQ\n");
+			if (control_exchange_pools_req(&viport->control,
+						       data_local_pool_addr
+						       (&viport->data),
+						       data_local_pool_rkey
+						       (&viport->data)))
+				viport->link_state = LINK_RESET;
+			else
+				viport->link_state = LINK_XCHGPOOLRSP;
+			break;
+		case LINK_XCHGPOOLRSP:
+			LINK_STATE("state LINK_XCHGPOOLRSP\n");
+			control_process_async(&viport->control);
+
+			if (!control_exchange_pools_rsp(&viport->control,
+						       data_remote_pool_addr
+						       (&viport->data),
+						       data_remote_pool_rkey
+						       (&viport->data)))
+				viport->link_state = LINK_INITIALIZED;
+
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_RESET;
+			}
+			break;
+		case LINK_INITIALIZED:
+			LINK_STATE("state LINK_INITIALIZED\n");
+			viport->state = VIPORT_CONNECTED;
+			printk(KERN_INFO PFX
+			       "%s: connection established\n",
+			       config_viport_name(viport->config));
+			data_connected(&viport->data);
+			vnic_connected(viport->parent->parent,
+				       viport->parent);
+			if (viport->features_supported & VNIC_FEAT_INBOUND_IB_MC) {
+				printk(KERN_INFO PFX "%s: Supports Inbound IB "
+					"Multicast\n",
+					config_viport_name(viport->config));
+				if (mc_data_init(&viport->mc_data, viport,
+						&viport->config->data_config,
+						viport->pd)) {
+					viport_disable_multicast(viport);
+					break;
+				}
+			}
+			spin_lock_irq(&viport->lock);
+			viport->mtu = 1500;
+			viport->flags = 0;
+			if ((viport->mtu != viport->new_mtu) ||
+			    (viport->flags != viport->new_flags))
+				viport->updates |= NEED_LINK_CONFIG;
+			spin_unlock_irq(&viport->lock);
+			viport->link_state = LINK_IDLE;
+			viport->retry_duration = 0;
+			viport->total_retry_duration = 0;
+			break;
+		default:
+			return -1;
+		}
+	} while (viport->link_state != old_state);
+
+	return 0;
+}
+
+static int viport_handle_idle_states(struct viport *viport)
+{
+	enum link_state old_state;
+	int handle_mc_join_compl, handle_mc_join;
+
+	do {
+		switch (old_state = viport->link_state) {
+		case LINK_IDLE:
+			LINK_STATE("state LINK_IDLE\n");
+			if (viport->config->hb_interval)
+				viport_timer(viport,
+					     viport->config->hb_interval);
+			viport->link_state = LINK_IDLING;
+			break;
+		case LINK_IDLING:
+			LINK_STATE("state LINK_IDLING\n");
+			control_process_async(&viport->control);
+			if (viport->errored) {
+				viport_timer_stop(viport);
+				viport->errored = 0;
+				viport->link_state = LINK_RESET;
+				break;
+			}
+
+			spin_lock_irq(&viport->lock);
+			handle_mc_join = (viport->updates & NEED_MCAST_JOIN);
+			handle_mc_join_compl =
+				      (viport->updates & NEED_MCAST_COMPLETION);
+			/*
+			 * Turn off both flags, the handler functions will
+			 * rearm them if necessary.
+			 */
+			viport->updates &= ~(NEED_MCAST_JOIN | NEED_MCAST_COMPLETION);
+
+			if (viport->updates & NEED_LINK_CONFIG) {
+				viport_timer_stop(viport);
+				viport->link_state = LINK_CONFIGLINKREQ;
+			} else if (viport->updates & NEED_ADDRESS_CONFIG) {
+				viport_timer_stop(viport);
+				viport->link_state = LINK_CONFIGADDRSREQ;
+			} else if (viport->updates & NEED_STATS) {
+				viport_timer_stop(viport);
+				viport->link_state = LINK_REPORTSTATREQ;
+			} else if (viport->config->hb_interval) {
+				if (!viport->timer_active)
+					viport->link_state =
+						LINK_HEARTBEATREQ;
+			}
+			spin_unlock_irq(&viport->lock);
+			if (handle_mc_join) {
+				if (vnic_mc_join(viport))
+					viport_disable_multicast(viport);
+			}
+			if (handle_mc_join_compl)
+				vnic_mc_join_handle_completion(viport);
+
+			break;
+		default:
+			return -1;
+		}
+	} while (viport->link_state != old_state);
+
+	return 0;
+}
+
+static int viport_handle_config_states(struct viport *viport)
+{
+	enum link_state old_state;
+	int res;
+
+	do {
+		switch (old_state = viport->link_state) {
+		case LINK_CONFIGLINKREQ:
+			LINK_STATE("state LINK_CONFIGLINKREQ\n");
+			spin_lock_irq(&viport->lock);
+			viport->updates &= ~NEED_LINK_CONFIG;
+			viport->flags = viport->new_flags;
+			if (viport->updates & MCAST_OVERFLOW)
+				viport->flags |= IFF_ALLMULTI;
+			viport->mtu = viport->new_mtu;
+			spin_unlock_irq(&viport->lock);
+			if (control_config_link_req(&viport->control,
+						    viport->flags,
+						    viport->mtu))
+				viport->link_state = LINK_RESET;
+			else
+				viport->link_state = LINK_CONFIGLINKRSP;
+			break;
+		case LINK_CONFIGLINKRSP:
+			LINK_STATE("state LINK_CONFIGLINKRSP\n");
+			control_process_async(&viport->control);
+
+			if (!control_config_link_rsp(&viport->control,
+						    &viport->flags,
+						    &viport->mtu))
+				viport->link_state = LINK_IDLE;
+
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_RESET;
+			}
+			break;
+		case LINK_CONFIGADDRSREQ:
+			LINK_STATE("state LINK_CONFIGADDRSREQ\n");
+
+			spin_lock_irq(&viport->lock);
+			res = control_config_addrs_req(&viport->control,
+						       viport->mac_addresses,
+						       viport->
+						       num_mac_addresses);
+
+			if (res > 0) {
+				viport->updates &= ~NEED_ADDRESS_CONFIG;
+				viport->link_state = LINK_CONFIGADDRSRSP;
+			} else if (res == 0)
+				viport->link_state = LINK_CONFIGADDRSRSP;
+			else
+				viport->link_state = LINK_RESET;
+			spin_unlock_irq(&viport->lock);
+			break;
+		case LINK_CONFIGADDRSRSP:
+			LINK_STATE("state LINK_CONFIGADDRSRSP\n");
+			control_process_async(&viport->control);
+
+			if (!control_config_addrs_rsp(&viport->control))
+				viport->link_state = LINK_IDLE;
+
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_RESET;
+			}
+			break;
+		default:
+			return -1;
+		}
+	} while (viport->link_state != old_state);
+
+	return 0;
+}
+
+static int viport_handle_stat_states(struct viport *viport)
+{
+	enum link_state old_state;
+
+	do {
+		switch (old_state = viport->link_state) {
+		case LINK_REPORTSTATREQ:
+			LINK_STATE("state LINK_REPORTSTATREQ\n");
+			if (control_report_statistics_req(&viport->control))
+				viport->link_state = LINK_RESET;
+			else
+				viport->link_state = LINK_REPORTSTATRSP;
+			break;
+		case LINK_REPORTSTATRSP:
+			LINK_STATE("state LINK_REPORTSTATRSP\n");
+			control_process_async(&viport->control);
+
+			spin_lock_irq(&viport->lock);
+			if (control_report_statistics_rsp(&viport->control,
+						  &viport->stats) == 0) {
+				viport->updates &= ~NEED_STATS;
+				viport->last_stats_time = jiffies;
+				wake_up(&viport->stats_queue);
+				viport->link_state = LINK_IDLE;
+			}
+
+			spin_unlock_irq(&viport->lock);
+
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_RESET;
+			}
+			break;
+		default:
+			return -1;
+		}
+	} while (viport->link_state != old_state);
+
+	return 0;
+}
+
+static int viport_handle_heartbeat_states(struct viport *viport)
+{
+	enum link_state old_state;
+
+	do {
+		switch (old_state = viport->link_state) {
+		case LINK_HEARTBEATREQ:
+			LINK_STATE("state LINK_HEARTBEATREQ\n");
+			if (control_heartbeat_req(&viport->control,
+						  viport->config->hb_timeout))
+				viport->link_state = LINK_RESET;
+			else
+				viport->link_state = LINK_HEARTBEATRSP;
+			break;
+		case LINK_HEARTBEATRSP:
+			LINK_STATE("state LINK_HEARTBEATRSP\n");
+			control_process_async(&viport->control);
+
+			if (!control_heartbeat_rsp(&viport->control))
+				viport->link_state = LINK_IDLE;
+
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_RESET;
+			}
+			break;
+		default:
+			return -1;
+		}
+	} while (viport->link_state != old_state);
+
+	return 0;
+}
+
+static int viport_handle_reset_states(struct viport *viport)
+{
+	enum link_state old_state;
+	int handle_mc_join_compl = 0, handle_mc_join = 0;
+
+	do {
+		switch (old_state = viport->link_state) {
+		case LINK_RESET:
+			LINK_STATE("state LINK_RESET\n");
+			viport->errored = 0;
+			spin_lock_irq(&viport->lock);
+			viport->state = VIPORT_DISCONNECTED;
+			/*
+			 * Turn off both flags, the handler functions will
+			 * rearm them if necessary
+			 */
+			viport->updates &= ~(NEED_MCAST_JOIN | NEED_MCAST_COMPLETION);
+
+			spin_unlock_irq(&viport->lock);
+			vnic_link_down(viport->vnic, viport->parent);
+			printk(KERN_INFO PFX
+			       "%s: connection lost\n",
+			       config_viport_name(viport->config));
+			if (handle_mc_join) {
+				if (vnic_mc_join(viport))
+					viport_disable_multicast(viport);
+			}
+			if (handle_mc_join_compl)
+				vnic_mc_join_handle_completion(viport);
+			if (viport->features_supported & VNIC_FEAT_INBOUND_IB_MC) {
+				VIPORT_ERROR("calling vnic_mc_leave\n");
+				vnic_mc_leave(viport);
+				VIPORT_ERROR("calling mc_data_cleanup\n");
+				mc_data_cleanup(&viport->mc_data);
+			}
+
+			if (control_reset_req(&viport->control))
+				viport->link_state = LINK_DATADISCONNECT;
+			else
+				viport->link_state = LINK_RESETRSP;
+			break;
+		case LINK_RESETRSP:
+			LINK_STATE("state LINK_RESETRSP\n");
+			control_process_async(&viport->control);
+
+			if (!control_reset_rsp(&viport->control))
+				viport->link_state = LINK_DATADISCONNECT;
+
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_DATADISCONNECT;
+			}
+			break;
+		case LINK_RESETCONTROL:
+			LINK_STATE("state LINK_RESETCONTROL\n");
+			if (control_reset_req(&viport->control))
+				viport->link_state = LINK_CONTROLDISCONNECT;
+			else
+				viport->link_state = LINK_RESETCONTROLRSP;
+			break;
+		case LINK_RESETCONTROLRSP:
+			LINK_STATE("state LINK_RESETCONTROLRSP\n");
+			control_process_async(&viport->control);
+
+			if (!control_reset_rsp(&viport->control))
+				viport->link_state = LINK_CONTROLDISCONNECT;
+
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_CONTROLDISCONNECT;
+			}
+			break;
+		default:
+			return -1;
+		}
+	} while (viport->link_state != old_state);
+
+	return 0;
+}
+
+static int viport_handle_disconn_states(struct viport *viport)
+{
+	enum link_state old_state;
+
+	do {
+		switch (old_state = viport->link_state) {
+		case LINK_DATADISCONNECT:
+			LINK_STATE("state LINK_DATADISCONNECT\n");
+			data_disconnect(&viport->data);
+			viport->link_state = LINK_CONTROLDISCONNECT;
+			break;
+		case LINK_CONTROLDISCONNECT:
+			LINK_STATE("state LINK_CONTROLDISCONNECT\n");
+			viport->link_state = LINK_CLEANUPDATA;
+			break;
+		case LINK_CLEANUPDATA:
+			LINK_STATE("state LINK_CLEANUPDATA\n");
+			data_cleanup(&viport->data);
+			viport->link_state = LINK_CLEANUPCONTROL;
+			break;
+		case LINK_CLEANUPCONTROL:
+			LINK_STATE("state LINK_CLEANUPCONTROL\n");
+			spin_lock_irq(&viport->lock);
+			kfree(viport->mac_addresses);
+			viport->mac_addresses = NULL;
+			spin_unlock_irq(&viport->lock);
+			control_cleanup(&viport->control);
+			ib_dealloc_pd(viport->pd);
+			viport->link_state = LINK_DISCONNECTED;
+			break;
+		case LINK_DISCONNECTED:
+			LINK_STATE("state LINK_DISCONNECTED\n");
+			vnic_disconnected(viport->parent->parent,
+					  viport->parent);
+			if (viport->disconnect != 0)
+				viport->link_state = LINK_UNINITIALIZED;
+			else if (viport->retry == 1) {
+				viport->retry = 0;
+			/*
+			 * Check if the initial retry interval has crossed
+			 * 20 seconds.
+			 * The retry interval is initially 5 seconds which
+			 * is incremented by 5. Once it is 20 the interval
+			 * is fixed to 20 seconds till 10 minutes,
+			 * after which retrying is stopped
+			 */
+				if (viport->retry_duration  < MAX_RETRY_INTERVAL)
+					viport->retry_duration +=
+								RETRY_INCREMENT;
+
+				viport->total_retry_duration +=
+							 viport->retry_duration;
+
+				if (viport->total_retry_duration >=
+					MAX_CONNECT_RETRY_TIMEOUT) {
+					viport->link_state = LINK_UNINITIALIZED;
+					printk("Timed out after retrying"
+					       " for retry_duration %d msecs\n"
+						, viport->total_retry_duration);
+				} else {
+					viport->connect = DELAY;
+					viport->link_state = LINK_RETRYWAIT;
+				}
+				viport_timer(viport,
+				     msecs_to_jiffies(viport->retry_duration));
+			} else {
+				u32 duration = 5000 + ((net_random()) & 0x1FF);
+				if (!viport->parent->is_primary_path)
+					duration += 0x1ff;
+				viport_timer(viport,
+					     msecs_to_jiffies(duration));
+				viport->connect = DELAY;
+				viport->link_state = LINK_RETRYWAIT;
+			}
+			break;
+		case LINK_RETRYWAIT:
+			LINK_STATE("state LINK_RETRYWAIT\n");
+			viport->stats.ethernet_status = 0;
+			viport->updates = 0;
+			wake_up(&viport->stats_queue);
+			if (viport->disconnect != 0) {
+				viport_timer_stop(viport);
+				viport->link_state = LINK_UNINITIALIZED;
+			} else if (viport->connect == DELAY) {
+				if (!viport->timer_active)
+					viport->link_state = LINK_INITIALIZE;
+			} else if (viport->connect == NOW) {
+				viport_timer_stop(viport);
+				viport->link_state = LINK_INITIALIZE;
+			}
+			break;
+		case LINK_FIRSTCONNECT:
+			viport->stats.ethernet_status = 0;
+			viport->updates = 0;
+			wake_up(&viport->stats_queue);
+			if (viport->disconnect != 0) {
+				viport_timer_stop(viport);
+				viport->link_state = LINK_UNINITIALIZED;
+			}
+
+			break;
+		default:
+			return -1;
+		}
+	} while (viport->link_state != old_state);
+
+	return 0;
+}
+
+static int viport_statemachine(void *context)
+{
+	struct viport *viport;
+	enum link_state old_link_state;
+
+	VIPORT_FUNCTION("viport_statemachine()\n");
+	while (!viport_thread_end || !list_empty(&viport_list)) {
+		wait_event_interruptible(viport_queue,
+					 !list_empty(&viport_list)
+					 || viport_thread_end);
+		spin_lock_irq(&viport_list_lock);
+		if (list_empty(&viport_list)) {
+			spin_unlock_irq(&viport_list_lock);
+			continue;
+		}
+		viport = list_entry(viport_list.next, struct viport,
+				    list_ptrs);
+		list_del_init(&viport->list_ptrs);
+		spin_unlock_irq(&viport_list_lock);
+
+		do {
+			old_link_state = viport->link_state;
+
+			/*
+			 * Optimize for the state machine steady state
+			 * by checking for the most common states first.
+			 *
+			 */
+			if (viport_handle_idle_states(viport) == 0)
+				break;
+			if (viport_handle_heartbeat_states(viport) == 0)
+				break;
+			if (viport_handle_stat_states(viport) == 0)
+				break;
+			if (viport_handle_config_states(viport) == 0)
+				break;
+
+			if (viport_handle_init_states(viport) == 0)
+				break;
+			if (viport_handle_control_states(viport) == 0)
+				break;
+			if (viport_handle_data_states(viport) == 0)
+				break;
+			if (viport_handle_xchgpool_states(viport) == 0)
+				break;
+			if (viport_handle_reset_states(viport) == 0)
+				break;
+			if (viport_handle_disconn_states(viport) == 0)
+				break;
+		} while (viport->link_state != old_link_state);
+	}
+
+	complete_and_exit(&viport_thread_exit, 0);
+}
+
+int viport_start(void)
+{
+	VIPORT_FUNCTION("viport_start()\n");
+
+	spin_lock_init(&viport_list_lock);
+	viport_thread = kthread_run(viport_statemachine, NULL,
+					"qlgc_vnic_viport_s_m");
+	if (IS_ERR(viport_thread)) {
+		printk(KERN_WARNING PFX "Could not create viport_thread;"
+		       " error %d\n", (int) PTR_ERR(viport_thread));
+		viport_thread = NULL;
+		return 1;
+	}
+
+	return 0;
+}
+
+void viport_cleanup(void)
+{
+	VIPORT_FUNCTION("viport_cleanup()\n");
+	if (viport_thread) {
+		viport_thread_end = 1;
+		wake_up(&viport_queue);
+		wait_for_completion(&viport_thread_exit);
+		viport_thread = NULL;
+	}
+}
diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_viport.h b/drivers/infiniband/ulp/qlgc_vnic/vnic_viport.h
new file mode 100644
index 0000000..bb0e7e1
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_viport.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2006 QLogic, 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 VNIC_VIPORT_H_INCLUDED
+#define VNIC_VIPORT_H_INCLUDED
+
+#include "vnic_control.h"
+#include "vnic_data.h"
+#include "vnic_multicast.h"
+
+enum viport_state {
+	VIPORT_DISCONNECTED	= 0,
+	VIPORT_CONNECTED	= 1
+};
+
+enum link_state {
+	LINK_UNINITIALIZED	= 0,
+	LINK_INITIALIZE		= 1,
+	LINK_INITIALIZECONTROL	= 2,
+	LINK_INITIALIZEDATA	= 3,
+	LINK_CONTROLCONNECT	= 4,
+	LINK_CONTROLCONNECTWAIT	= 5,
+	LINK_INITVNICREQ	= 6,
+	LINK_INITVNICRSP	= 7,
+	LINK_BEGINDATAPATH	= 8,
+	LINK_CONFIGDATAPATHREQ	= 9,
+	LINK_CONFIGDATAPATHRSP	= 10,
+	LINK_DATACONNECT	= 11,
+	LINK_DATACONNECTWAIT	= 12,
+	LINK_XCHGPOOLREQ	= 13,
+	LINK_XCHGPOOLRSP	= 14,
+	LINK_INITIALIZED	= 15,
+	LINK_IDLE		= 16,
+	LINK_IDLING		= 17,
+	LINK_CONFIGLINKREQ	= 18,
+	LINK_CONFIGLINKRSP	= 19,
+	LINK_CONFIGADDRSREQ	= 20,
+	LINK_CONFIGADDRSRSP	= 21,
+	LINK_REPORTSTATREQ	= 22,
+	LINK_REPORTSTATRSP	= 23,
+	LINK_HEARTBEATREQ	= 24,
+	LINK_HEARTBEATRSP	= 25,
+	LINK_RESET		= 26,
+	LINK_RESETRSP		= 27,
+	LINK_RESETCONTROL	= 28,
+	LINK_RESETCONTROLRSP	= 29,
+	LINK_DATADISCONNECT	= 30,
+	LINK_CONTROLDISCONNECT	= 31,
+	LINK_CLEANUPDATA	= 32,
+	LINK_CLEANUPCONTROL	= 33,
+	LINK_DISCONNECTED	= 34,
+	LINK_RETRYWAIT		= 35,
+	LINK_FIRSTCONNECT	= 36
+};
+
+enum {
+	BROADCAST_ADDR		= 0,
+	UNICAST_ADDR		= 1,
+	MCAST_ADDR_START	= 2
+};
+
+#define current_mac_address	mac_addresses[UNICAST_ADDR].address
+
+enum {
+	NEED_STATS           	= 0x00000001,
+	NEED_ADDRESS_CONFIG  	= 0x00000002,
+	NEED_LINK_CONFIG     	= 0x00000004,
+	MCAST_OVERFLOW       	= 0x00000008,
+	NEED_MCAST_COMPLETION	= 0x00000010,
+	NEED_MCAST_JOIN      	= 0x00000020
+};
+
+struct viport {
+	struct list_head		list_ptrs;
+	struct netpath			*parent;
+	struct vnic			*vnic;
+	struct viport_config		*config;
+	struct control			control;
+	struct data			data;
+	spinlock_t			lock;
+	struct ib_pd			*pd;
+	enum viport_state		state;
+	enum link_state			link_state;
+	struct vnic_cmd_report_stats_rsp stats;
+	wait_queue_head_t		stats_queue;
+	u32				last_stats_time;
+	u32				features_supported;
+	u8				hw_mac_address[ETH_ALEN];
+	u16				default_vlan;
+	u16				num_mac_addresses;
+	struct vnic_address_op2		*mac_addresses;
+	u32				updates;
+	u16				flags;
+	u16				new_flags;
+	u16				mtu;
+	u16				new_mtu;
+	u32				errored;
+	enum { WAIT, DELAY, NOW }	connect;
+	u32				disconnect;
+	u32 				retry;
+	wait_queue_head_t		disconnect_queue;
+	int				timer_active;
+	struct timer_list		timer;
+	u32 				retry_duration;
+	u32 				total_retry_duration;
+	int 				reference_count;
+	wait_queue_head_t		reference_queue;
+	struct mc_info	mc_info;
+	struct mc_data	mc_data;
+};
+
+int  viport_start(void);
+void viport_cleanup(void);
+
+struct viport *viport_allocate(struct viport_config *config);
+void viport_free(struct viport *viport);
+
+void viport_connect(struct viport *viport, int delay);
+void viport_disconnect(struct viport *viport);
+
+void viport_set_link(struct viport *viport, u16 flags, u16 mtu);
+void viport_get_stats(struct viport *viport,
+		      struct net_device_stats *stats);
+int  viport_xmit_packet(struct viport *viport, struct sk_buff *skb);
+void viport_kick(struct viport *viport);
+
+void viport_failure(struct viport *viport);
+
+int viport_set_unicast(struct viport *viport, u8 *address);
+int viport_set_multicast(struct viport *viport,
+			 struct dev_mc_list *mc_list,
+			 int mc_count);
+
+#define viport_max_mtu(viport)		data_max_mtu(&(viport)->data)
+
+#define viport_get_hw_addr(viport, address)			\
+	memcpy(address, (viport)->hw_mac_address, ETH_ALEN)
+
+#define viport_features(viport) ((viport)->features_supported)
+
+#define viport_can_tx_csum(viport)				\
+	(((viport)->features_supported & 			\
+	(VNIC_FEAT_IPV4_CSUM_TX | VNIC_FEAT_TCP_CSUM_TX |	\
+	VNIC_FEAT_UDP_CSUM_TX)) == (VNIC_FEAT_IPV4_CSUM_TX |	\
+	VNIC_FEAT_TCP_CSUM_TX | VNIC_FEAT_UDP_CSUM_TX))
+
+#endif /* VNIC_VIPORT_H_INCLUDED */

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [ofa-general] [PATCH 04/13] QLogic VNIC: Implementation of Control path of communication protocol
  2008-04-30 17:15 [ofa-general] [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Ramachandra K
                   ` (2 preceding siblings ...)
  2008-04-30 17:17 ` [ofa-general] [PATCH 03/13] QLogic VNIC: Implementation of communication protocol with EVIC/VEx Ramachandra K
@ 2008-04-30 17:17 ` Ramachandra K
  2008-04-30 17:18 ` [ofa-general] [PATCH 05/13] QLogic VNIC: Implementation of Data " Ramachandra K
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 32+ messages in thread
From: Ramachandra K @ 2008-04-30 17:17 UTC (permalink / raw)
  To: rdreier, general, netdev; +Cc: poornima.kamath, amar.mudrankit

From: Poornima Kamath <poornima.kamath@qlogic.com>

This patch adds the files that define the control packet formats
and implements various control messages that are exchanged as part
of the communication protocol with the EVIC/VEx.

Signed-off-by: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>
Signed-off-by: Amar Mudrankit <amar.mudrankit@qlogic.com>
---

 drivers/infiniband/ulp/qlgc_vnic/vnic_control.c    | 2288 ++++++++++++++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_control.h    |  180 ++
 .../infiniband/ulp/qlgc_vnic/vnic_control_pkt.h    |  368 +++
 3 files changed, 2836 insertions(+), 0 deletions(-)
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_control.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_control.h
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_control_pkt.h

diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_control.c b/drivers/infiniband/ulp/qlgc_vnic/vnic_control.c
new file mode 100644
index 0000000..470f22e
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_control.c
@@ -0,0 +1,2288 @@
+/*
+ * Copyright (c) 2006 QLogic, 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/netdevice.h>
+#include <linux/list.h>
+#include <linux/vmalloc.h>
+
+#include "vnic_util.h"
+#include "vnic_main.h"
+#include "vnic_viport.h"
+#include "vnic_control.h"
+#include "vnic_control_pkt.h"
+#include "vnic_stats.h"
+
+#define vnic_multicast_address(rsp2_address, index)           \
+	((rsp2_address)->list_address_ops[index].address[0] & 0x01)
+
+static void control_log_control_packet(struct vnic_control_packet *pkt);
+
+static inline char *control_ifcfg_name(struct control *control)
+{
+	if (!control)
+		return "nctl";
+	if (!control->parent)
+		return "np";
+	if (!control->parent->parent)
+		return "npp";
+	if (!control->parent->parent->parent)
+		return "nppp";
+	if (!control->parent->parent->parent->config)
+		return "npppc";
+	return (control->parent->parent->parent->config->name);
+}
+
+static void control_recv(struct control *control, struct recv_io *recv_io)
+{
+	if (vnic_ib_post_recv(&control->ib_conn, &recv_io->io))
+		viport_failure(control->parent);
+}
+
+static void control_recv_complete(struct io *io)
+{
+	struct recv_io			*recv_io = (struct recv_io *)io;
+	struct recv_io			*last_recv_io;
+	struct control			*control = &io->viport->control;
+	struct vnic_control_packet	*pkt = control_packet(recv_io);
+	struct vnic_control_header	*c_hdr = &pkt->hdr;
+	unsigned long			flags;
+	cycles_t			response_time;
+
+	CONTROL_FUNCTION("%s: control_recv_complete() State=%d\n",
+			 control_ifcfg_name(control), control->req_state);
+
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->recv_dma, control->recv_len,
+				   DMA_FROM_DEVICE);
+	control_note_rsptime_stats(&response_time);
+	CONTROL_PACKET(pkt);
+	spin_lock_irqsave(&control->io_lock, flags);
+	if (c_hdr->pkt_type == TYPE_INFO) {
+		last_recv_io = control->info;
+		control->info = recv_io;
+		spin_unlock_irqrestore(&control->io_lock, flags);
+		viport_kick(control->parent);
+		if (last_recv_io)
+			control_recv(control, last_recv_io);
+	} else if (c_hdr->pkt_type == TYPE_RSP) {
+		u8 repost = 0;
+		u8 fail = 0;
+		u8 kick = 0;
+
+		switch (control->req_state) {
+		case REQ_INACTIVE:
+		case RSP_RECEIVED:
+		case REQ_COMPLETED:
+			CONTROL_ERROR("%s: Unexpected control"
+					"response received: CMD = %d\n",
+					control_ifcfg_name(control),
+					c_hdr->pkt_cmd);
+			control_log_control_packet(pkt);
+			control->req_state = REQ_FAILED;
+			fail = 1;
+			break;
+		case REQ_POSTED:
+		case REQ_SENT:
+			if (c_hdr->pkt_cmd != control->last_cmd
+				|| c_hdr->pkt_seq_num != control->seq_num) {
+				CONTROL_ERROR("%s: Incorrect Control Response "
+					      "received\n",
+					      control_ifcfg_name(control));
+				CONTROL_ERROR("%s: Sent control request:\n",
+					      control_ifcfg_name(control));
+				control_log_control_packet(control_last_req(control));
+				CONTROL_ERROR("%s: Received control response:\n",
+					      control_ifcfg_name(control));
+				control_log_control_packet(pkt);
+				control->req_state = REQ_FAILED;
+				fail = 1;
+			} else {
+				control->response = recv_io;
+				control_update_rsptime_stats(control,
+							    response_time);
+				if (control->req_state == REQ_POSTED) {
+					CONTROL_INFO("%s: Recv CMD RSP %d"
+						     "before Send Completion\n",
+						     control_ifcfg_name(control),
+						     c_hdr->pkt_cmd);
+					control->req_state = RSP_RECEIVED;
+				} else {
+					control->req_state = REQ_COMPLETED;
+					kick = 1;
+				}
+			}
+			break;
+		case REQ_FAILED:
+			/* stay in REQ_FAILED state */
+			repost = 1;
+			break;
+		}
+		spin_unlock_irqrestore(&control->io_lock, flags);
+		/* we must do this outside the lock*/
+		if (kick)
+			viport_kick(control->parent);
+		if (repost || fail) {
+			control_recv(control, recv_io);
+			if (fail)
+				viport_failure(control->parent);
+		}
+
+	} else {
+		list_add_tail(&recv_io->io.list_ptrs,
+			      &control->failure_list);
+		spin_unlock_irqrestore(&control->io_lock, flags);
+		viport_kick(control->parent);
+	}
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+}
+
+static void control_timeout(unsigned long data)
+{
+	struct control *control;
+	unsigned long 		  flags;
+	u8 fail = 0;
+	u8 kick = 0;
+
+	control = (struct control *)data;
+	CONTROL_FUNCTION("%s: control_timeout(), State=%d\n",
+			 control_ifcfg_name(control), control->req_state);
+	control->timer_state = TIMER_EXPIRED;
+
+	spin_lock_irqsave(&control->io_lock, flags);
+	switch (control->req_state) {
+	case REQ_INACTIVE:
+		kick = 1;
+		/* stay in REQ_INACTIVE state */
+		break;
+	case REQ_POSTED:
+	case REQ_SENT:
+		control->req_state = REQ_FAILED;
+		CONTROL_ERROR("%s: No send Completion for Cmd=%d \n",
+			      control_ifcfg_name(control), control->last_cmd);
+		control_timeout_stats(control);
+		fail = 1;
+		break;
+	case RSP_RECEIVED:
+		control->req_state = REQ_FAILED;
+		CONTROL_ERROR("%s: No response received from EIOC for Cmd=%d\n",
+			      control_ifcfg_name(control), control->last_cmd);
+		control_timeout_stats(control);
+		fail = 1;
+		break;
+	case REQ_COMPLETED:
+		/* stay in REQ_COMPLETED state*/
+		kick = 1;
+		break;
+	case REQ_FAILED:
+		/* stay in REQ_FAILED state*/
+		break;
+	}
+	spin_unlock_irqrestore(&control->io_lock, flags);
+	/* we must do this outside the lock */
+	if (fail)
+		viport_failure(control->parent);
+	if (kick)
+		viport_kick(control->parent);
+
+	return;
+}
+
+static void control_timer(struct control *control, int timeout)
+{
+	CONTROL_FUNCTION("%s: control_timer()\n",
+			 control_ifcfg_name(control));
+	if (control->timer_state == TIMER_ACTIVE)
+		mod_timer(&control->timer, jiffies + timeout);
+	else {
+		init_timer(&control->timer);
+		control->timer.expires = jiffies + timeout;
+		control->timer.data = (unsigned long)control;
+		control->timer.function = control_timeout;
+		control->timer_state = TIMER_ACTIVE;
+		add_timer(&control->timer);
+	}
+}
+
+static void control_timer_stop(struct control *control)
+{
+	CONTROL_FUNCTION("%s: control_timer_stop()\n",
+			 control_ifcfg_name(control));
+	if (control->timer_state == TIMER_ACTIVE)
+		del_timer_sync(&control->timer);
+
+	control->timer_state = TIMER_IDLE;
+}
+
+static int control_send(struct control *control, struct send_io *send_io)
+{
+	unsigned long 	flags;
+	u8 ret = -1;
+	u8 fail = 0;
+	struct vnic_control_packet *pkt = control_packet(send_io);
+
+	CONTROL_FUNCTION("%s: control_send(), State=%d\n",
+			 control_ifcfg_name(control), control->req_state);
+	spin_lock_irqsave(&control->io_lock, flags);
+	switch (control->req_state) {
+	case REQ_INACTIVE:
+		CONTROL_PACKET(pkt);
+		control_timer(control, control->config->rsp_timeout);
+		control_note_reqtime_stats(control);
+		if (vnic_ib_post_send(&control->ib_conn, &control->send_io.io)) {
+			CONTROL_ERROR("%s: Failed to post send\n",
+				control_ifcfg_name(control));
+			/* stay in REQ_INACTIVE state*/
+			fail = 1;
+		} else {
+			control->last_cmd = pkt->hdr.pkt_cmd;
+			control->req_state = REQ_POSTED;
+			ret = 0;
+		}
+		break;
+	case REQ_POSTED:
+	case REQ_SENT:
+	case RSP_RECEIVED:
+	case REQ_COMPLETED:
+		CONTROL_ERROR("%s:Previous Command is not completed."
+			      "New CMD: %d Last CMD: %d Seq: %d\n",
+			      control_ifcfg_name(control), pkt->hdr.pkt_cmd,
+			      control->last_cmd, control->seq_num);
+
+		control->req_state = REQ_FAILED;
+		fail = 1;
+		break;
+	case REQ_FAILED:
+		/* this can occur after an error when ViPort state machine
+		 * attempts to reset the link.
+		 */
+		CONTROL_INFO("%s:Attempt to send in failed state."
+			     "New CMD: %d Last CMD: %d\n",
+			     control_ifcfg_name(control), pkt->hdr.pkt_cmd,
+			     control->last_cmd);
+		/* stay in REQ_FAILED state*/
+		break;
+	}
+	spin_unlock_irqrestore(&control->io_lock, flags);
+
+	/* we must do this outside the lock */
+	if (fail)
+		viport_failure(control->parent);
+	return ret;
+
+}
+
+static void control_send_complete(struct io *io)
+{
+	struct control *control = &io->viport->control;
+	unsigned long 		  flags;
+	u8 fail = 0;
+	u8 kick = 0;
+
+	CONTROL_FUNCTION("%s: control_sendComplete(), State=%d\n",
+			 control_ifcfg_name(control), control->req_state);
+	spin_lock_irqsave(&control->io_lock, flags);
+	switch (control->req_state) {
+	case REQ_INACTIVE:
+	case REQ_SENT:
+	case REQ_COMPLETED:
+		CONTROL_ERROR("%s: Unexpected control send completion\n",
+			      control_ifcfg_name(control));
+		fail = 1;
+		control->req_state = REQ_FAILED;
+		break;
+	case REQ_POSTED:
+		control->req_state = REQ_SENT;
+		break;
+	case RSP_RECEIVED:
+		control->req_state = REQ_COMPLETED;
+		kick = 1;
+		break;
+	case REQ_FAILED:
+		/* stay in REQ_FAILED state */
+		break;
+	}
+	spin_unlock_irqrestore(&control->io_lock, flags);
+	/* we must do this outside the lock */
+	if (fail)
+		viport_failure(control->parent);
+	if (kick)
+		viport_kick(control->parent);
+
+	return;
+}
+
+void control_process_async(struct control *control)
+{
+	struct recv_io			*recv_io;
+	struct vnic_control_packet	*pkt;
+	unsigned long			flags;
+
+	CONTROL_FUNCTION("%s: control_process_async()\n",
+			 control_ifcfg_name(control));
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->recv_dma, control->recv_len,
+				   DMA_FROM_DEVICE);
+
+	spin_lock_irqsave(&control->io_lock, flags);
+	recv_io = control->info;
+	if (recv_io) {
+		CONTROL_INFO("%s: processing info packet\n",
+			     control_ifcfg_name(control));
+		control->info = NULL;
+		spin_unlock_irqrestore(&control->io_lock, flags);
+		pkt = control_packet(recv_io);
+		if (pkt->hdr.pkt_cmd == CMD_REPORT_STATUS) {
+			u32		status;
+			status =
+			  be32_to_cpu(pkt->cmd.report_status.status_number);
+			switch (status) {
+			case VNIC_STATUS_LINK_UP:
+				CONTROL_INFO("%s: link up\n",
+					     control_ifcfg_name(control));
+				vnic_link_up(control->parent->vnic,
+					     control->parent->parent);
+				break;
+			case VNIC_STATUS_LINK_DOWN:
+				CONTROL_INFO("%s: link down\n",
+					     control_ifcfg_name(control));
+				vnic_link_down(control->parent->vnic,
+					       control->parent->parent);
+				break;
+			default:
+				CONTROL_ERROR("%s: asynchronous status"
+					      " received from EIOC\n",
+					      control_ifcfg_name(control));
+				control_log_control_packet(pkt);
+				break;
+			}
+		}
+		if ((pkt->hdr.pkt_cmd != CMD_REPORT_STATUS) ||
+		     pkt->cmd.report_status.is_fatal)
+			viport_failure(control->parent);
+
+		control_recv(control, recv_io);
+		spin_lock_irqsave(&control->io_lock, flags);
+	}
+
+	while (!list_empty(&control->failure_list)) {
+		CONTROL_INFO("%s: processing error packet\n",
+			     control_ifcfg_name(control));
+		recv_io = (struct recv_io *)
+		    list_entry(control->failure_list.next, struct io,
+			       list_ptrs);
+		list_del(&recv_io->io.list_ptrs);
+		spin_unlock_irqrestore(&control->io_lock, flags);
+		pkt = control_packet(recv_io);
+		CONTROL_ERROR("%s: asynchronous error received from EIOC\n",
+			      control_ifcfg_name(control));
+		control_log_control_packet(pkt);
+		if ((pkt->hdr.pkt_type != TYPE_ERR)
+		    || (pkt->hdr.pkt_cmd != CMD_REPORT_STATUS)
+		    || pkt->cmd.report_status.is_fatal)
+			viport_failure(control->parent);
+
+		control_recv(control, recv_io);
+		spin_lock_irqsave(&control->io_lock, flags);
+	}
+	spin_unlock_irqrestore(&control->io_lock, flags);
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+
+	CONTROL_FUNCTION("%s: done control_process_async\n",
+		     control_ifcfg_name(control));
+}
+
+static struct send_io *control_init_hdr(struct control *control, u8 cmd)
+{
+	struct control_config		*config;
+	struct vnic_control_packet	*pkt;
+	struct vnic_control_header	*hdr;
+
+	CONTROL_FUNCTION("control_init_hdr()\n");
+	config = control->config;
+
+	pkt = control_packet(&control->send_io);
+	hdr = &pkt->hdr;
+
+	hdr->pkt_type = TYPE_REQ;
+	hdr->pkt_cmd = cmd;
+	control->seq_num++;
+	hdr->pkt_seq_num = control->seq_num;
+	hdr->pkt_retry_count = 0;
+
+	return &control->send_io;
+}
+
+static struct recv_io *control_get_rsp(struct control *control)
+{
+	struct recv_io	*recv_io = NULL;
+	unsigned long	flags;
+	u8 fail = 0;
+
+	CONTROL_FUNCTION("%s: control_getRsp(), State=%d\n",
+			 control_ifcfg_name(control), control->req_state);
+	spin_lock_irqsave(&control->io_lock, flags);
+	switch (control->req_state) {
+	case REQ_INACTIVE:
+		CONTROL_ERROR("%s: Checked for Response with no"
+			      "command pending\n",
+			      control_ifcfg_name(control));
+		control->req_state = REQ_FAILED;
+		fail = 1;
+		break;
+	case REQ_POSTED:
+	case REQ_SENT:
+	case RSP_RECEIVED:
+		/* no response available yet
+		 stay in present state*/
+		break;
+	case REQ_COMPLETED:
+		recv_io = control->response;
+		if (!recv_io) {
+			control->req_state = REQ_FAILED;
+			fail = 1;
+			break;
+		}
+		control->response = NULL;
+		control->last_cmd = CMD_INVALID;
+		control_timer_stop(control);
+		control->req_state = REQ_INACTIVE;
+		break;
+	case REQ_FAILED:
+		control_timer_stop(control);
+		/* stay in REQ_FAILED state*/
+		break;
+	}
+	spin_unlock_irqrestore(&control->io_lock, flags);
+	if (fail)
+		viport_failure(control->parent);
+	return recv_io;
+}
+
+int control_init_vnic_req(struct control *control)
+{
+	struct send_io			*send_io;
+	struct control_config		*config = control->config;
+	struct vnic_control_packet	*pkt;
+	struct vnic_cmd_init_vnic_req	*init_vnic_req;
+
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->send_dma, control->send_len,
+				   DMA_TO_DEVICE);
+
+	send_io = control_init_hdr(control, CMD_INIT_VNIC);
+	if (!send_io)
+		goto failure;
+
+	pkt = control_packet(send_io);
+	init_vnic_req = &pkt->cmd.init_vnic_req;
+	init_vnic_req->vnic_major_version =
+				 __constant_cpu_to_be16(VNIC_MAJORVERSION);
+	init_vnic_req->vnic_minor_version =
+				 __constant_cpu_to_be16(VNIC_MINORVERSION);
+	init_vnic_req->vnic_instance = config->vnic_instance;
+	init_vnic_req->num_data_paths = 1;
+	init_vnic_req->num_address_entries =
+				cpu_to_be16(config->max_address_entries);
+
+	control->last_cmd = pkt->hdr.pkt_cmd;
+	CONTROL_PACKET(pkt);
+
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->send_dma, control->send_len,
+				      DMA_TO_DEVICE);
+
+	return control_send(control, send_io);
+failure:
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->send_dma, control->send_len,
+				      DMA_TO_DEVICE);
+	return -1;
+}
+
+static int control_chk_vnic_rsp_values(struct control *control,
+				       u16 *num_addrs,
+				       u8 num_data_paths,
+				       u8 num_lan_switches,
+				       u32 *features)
+{
+
+	struct control_config		*config = control->config;
+
+	if ((control->maj_ver > VNIC_MAJORVERSION)
+	    || ((control->maj_ver == VNIC_MAJORVERSION)
+		&& (control->min_ver > VNIC_MINORVERSION))) {
+		CONTROL_ERROR("%s: unsupported version\n",
+			      control_ifcfg_name(control));
+		goto failure;
+	}
+	if (num_data_paths != 1) {
+		CONTROL_ERROR("%s: EIOC returned too many datapaths\n",
+			      control_ifcfg_name(control));
+		goto failure;
+	}
+	if (*num_addrs > config->max_address_entries) {
+		CONTROL_ERROR("%s: EIOC returned more address"
+			      " entries than requested\n",
+			      control_ifcfg_name(control));
+		goto failure;
+	}
+	if (*num_addrs < config->min_address_entries) {
+		CONTROL_ERROR("%s: not enough address entries\n",
+			      control_ifcfg_name(control));
+		goto failure;
+	}
+	if (num_lan_switches < 1) {
+		CONTROL_ERROR("%s: EIOC returned no lan switches\n",
+			      control_ifcfg_name(control));
+		goto failure;
+	}
+	if (num_lan_switches > 1) {
+		CONTROL_ERROR("%s: EIOC returned multiple lan switches\n",
+			      control_ifcfg_name(control));
+		goto failure;
+	}
+	CONTROL_ERROR("%s checking features %x ib_multicast:%d\n",
+			control_ifcfg_name(control),
+			*features, config->ib_multicast);
+	if ((*features & VNIC_FEAT_INBOUND_IB_MC) && !config->ib_multicast) {
+		/* disable multicast if it is not on in the cfg file, or
+		   if we turned it off because join failed */
+		*features &= ~VNIC_FEAT_INBOUND_IB_MC;
+	}
+
+	return 0;
+failure:
+	return -1;
+}
+
+int control_init_vnic_rsp(struct control *control, u32 *features,
+			  u8 *mac_address, u16 *num_addrs, u16 *vlan)
+{
+	u8 num_data_paths;
+	u8 num_lan_switches;
+	struct recv_io			*recv_io;
+	struct vnic_control_packet	*pkt;
+	struct vnic_cmd_init_vnic_rsp	*init_vnic_rsp;
+
+
+	CONTROL_FUNCTION("%s: control_init_vnic_rsp()\n",
+			 control_ifcfg_name(control));
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->recv_dma, control->recv_len,
+				   DMA_FROM_DEVICE);
+
+	recv_io = control_get_rsp(control);
+	if (!recv_io)
+		goto out;
+
+	pkt = control_packet(recv_io);
+	if (pkt->hdr.pkt_cmd != CMD_INIT_VNIC)
+		goto failure;
+
+	init_vnic_rsp = &pkt->cmd.init_vnic_rsp;
+	control->maj_ver = be16_to_cpu(init_vnic_rsp->vnic_major_version);
+	control->min_ver = be16_to_cpu(init_vnic_rsp->vnic_minor_version);
+	num_data_paths = init_vnic_rsp->num_data_paths;
+	num_lan_switches = init_vnic_rsp->num_lan_switches;
+	*features = be32_to_cpu(init_vnic_rsp->features_supported);
+	*num_addrs = be16_to_cpu(init_vnic_rsp->num_address_entries);
+
+	if (control_chk_vnic_rsp_values(control, num_addrs,
+					num_data_paths,
+					num_lan_switches,
+					features))
+		goto failure;
+
+	control->lan_switch.lan_switch_num =
+			init_vnic_rsp->lan_switch[0].lan_switch_num;
+	control->lan_switch.num_enet_ports =
+			init_vnic_rsp->lan_switch[0].num_enet_ports;
+	control->lan_switch.default_vlan =
+			init_vnic_rsp->lan_switch[0].default_vlan;
+	*vlan = be16_to_cpu(control->lan_switch.default_vlan);
+	memcpy(control->lan_switch.hw_mac_address,
+	       init_vnic_rsp->lan_switch[0].hw_mac_address, ETH_ALEN);
+	memcpy(mac_address, init_vnic_rsp->lan_switch[0].hw_mac_address,
+	       ETH_ALEN);
+
+	control_recv(control, recv_io);
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+	return 0;
+failure:
+	viport_failure(control->parent);
+out:
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+	return -1;
+}
+
+static void copy_recv_pool_config(struct vnic_recv_pool_config *src,
+				  struct vnic_recv_pool_config *dst)
+{
+	dst->size_recv_pool_entry  = src->size_recv_pool_entry;
+	dst->num_recv_pool_entries = src->num_recv_pool_entries;
+	dst->timeout_before_kick   = src->timeout_before_kick;
+	dst->num_recv_pool_entries_before_kick =
+				src->num_recv_pool_entries_before_kick;
+	dst->num_recv_pool_bytes_before_kick =
+				src->num_recv_pool_bytes_before_kick;
+	dst->free_recv_pool_entries_per_update =
+				src->free_recv_pool_entries_per_update;
+}
+
+static int check_recv_pool_config_value(__be32 *src, __be32 *dst,
+					__be32 *max, __be32 *min,
+					char *name)
+{
+	u32 value;
+
+	value = be32_to_cpu(*src);
+	if (value > be32_to_cpu(*max)) {
+		CONTROL_ERROR("value %s too large\n", name);
+		return -1;
+	} else if (value < be32_to_cpu(*min)) {
+		CONTROL_ERROR("value %s too small\n", name);
+		return -1;
+	}
+
+	*dst = cpu_to_be32(value);
+	return 0;
+}
+
+static int check_recv_pool_config(struct vnic_recv_pool_config *src,
+				  struct vnic_recv_pool_config *dst,
+				  struct vnic_recv_pool_config *max,
+				  struct vnic_recv_pool_config *min)
+{
+	if (check_recv_pool_config_value(&src->size_recv_pool_entry,
+				     &dst->size_recv_pool_entry,
+				     &max->size_recv_pool_entry,
+				     &min->size_recv_pool_entry,
+				     "size_recv_pool_entry")
+	    || check_recv_pool_config_value(&src->num_recv_pool_entries,
+				     &dst->num_recv_pool_entries,
+				     &max->num_recv_pool_entries,
+				     &min->num_recv_pool_entries,
+				     "num_recv_pool_entries")
+	    || check_recv_pool_config_value(&src->timeout_before_kick,
+				     &dst->timeout_before_kick,
+				     &max->timeout_before_kick,
+				     &min->timeout_before_kick,
+				     "timeout_before_kick")
+	    || check_recv_pool_config_value(&src->
+				     num_recv_pool_entries_before_kick,
+				     &dst->
+				     num_recv_pool_entries_before_kick,
+				     &max->
+				     num_recv_pool_entries_before_kick,
+				     &min->
+				     num_recv_pool_entries_before_kick,
+				     "num_recv_pool_entries_before_kick")
+	    || check_recv_pool_config_value(&src->
+				     num_recv_pool_bytes_before_kick,
+				     &dst->
+				     num_recv_pool_bytes_before_kick,
+				     &max->
+				     num_recv_pool_bytes_before_kick,
+				     &min->
+				     num_recv_pool_bytes_before_kick,
+				     "num_recv_pool_bytes_before_kick")
+	    || check_recv_pool_config_value(&src->
+				     free_recv_pool_entries_per_update,
+				     &dst->
+				     free_recv_pool_entries_per_update,
+				     &max->
+				     free_recv_pool_entries_per_update,
+				     &min->
+				     free_recv_pool_entries_per_update,
+				     "free_recv_pool_entries_per_update"))
+		goto failure;
+
+	if (!is_power_of2(be32_to_cpu(dst->num_recv_pool_entries))) {
+		CONTROL_ERROR("num_recv_pool_entries (%d)"
+			      " must be power of 2\n",
+			      dst->num_recv_pool_entries);
+		goto failure;
+	}
+
+	if (!is_power_of2(be32_to_cpu(dst->
+				      free_recv_pool_entries_per_update))) {
+		CONTROL_ERROR("free_recv_pool_entries_per_update (%d)"
+			      " must be power of 2\n",
+			      dst->free_recv_pool_entries_per_update);
+		goto failure;
+	}
+
+	if (be32_to_cpu(dst->free_recv_pool_entries_per_update) >=
+	    be32_to_cpu(dst->num_recv_pool_entries)) {
+		CONTROL_ERROR("free_recv_pool_entries_per_update (%d) must"
+			      " be less than num_recv_pool_entries (%d)\n",
+			      dst->free_recv_pool_entries_per_update,
+			      dst->num_recv_pool_entries);
+		goto failure;
+	}
+
+	if (be32_to_cpu(dst->num_recv_pool_entries_before_kick) >=
+	    be32_to_cpu(dst->num_recv_pool_entries)) {
+		CONTROL_ERROR("num_recv_pool_entries_before_kick (%d) must"
+			      " be less than num_recv_pool_entries (%d)\n",
+			      dst->num_recv_pool_entries_before_kick,
+			      dst->num_recv_pool_entries);
+		goto failure;
+	}
+
+	return 0;
+failure:
+	return -1;
+}
+
+int control_config_data_path_req(struct control *control, u64 path_id,
+				     struct vnic_recv_pool_config *host,
+				     struct vnic_recv_pool_config *eioc)
+{
+	struct send_io				*send_io;
+	struct vnic_control_packet		*pkt;
+	struct vnic_cmd_config_data_path	*config_data_path;
+
+	CONTROL_FUNCTION("%s: control_config_data_path_req()\n",
+			 control_ifcfg_name(control));
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->send_dma, control->send_len,
+				   DMA_TO_DEVICE);
+
+	send_io = control_init_hdr(control, CMD_CONFIG_DATA_PATH);
+	if (!send_io)
+		goto failure;
+
+	pkt = control_packet(send_io);
+	config_data_path = &pkt->cmd.config_data_path_req;
+	config_data_path->data_path = 0;
+	config_data_path->path_identifier = path_id;
+	copy_recv_pool_config(host,
+			      &config_data_path->host_recv_pool_config);
+	copy_recv_pool_config(eioc,
+			      &config_data_path->eioc_recv_pool_config);
+	CONTROL_PACKET(pkt);
+
+	control->last_cmd = pkt->hdr.pkt_cmd;
+
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->send_dma, control->send_len,
+				      DMA_TO_DEVICE);
+
+	return control_send(control, send_io);
+failure:
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->send_dma, control->send_len,
+				      DMA_TO_DEVICE);
+	return -1;
+}
+
+int control_config_data_path_rsp(struct control *control,
+				 struct vnic_recv_pool_config *host,
+				 struct vnic_recv_pool_config *eioc,
+				 struct vnic_recv_pool_config *max_host,
+				 struct vnic_recv_pool_config *max_eioc,
+				 struct vnic_recv_pool_config *min_host,
+				 struct vnic_recv_pool_config *min_eioc)
+{
+	struct recv_io				*recv_io;
+	struct vnic_control_packet		*pkt;
+	struct vnic_cmd_config_data_path	*config_data_path;
+
+	CONTROL_FUNCTION("%s: control_config_data_path_rsp()\n",
+			 control_ifcfg_name(control));
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->recv_dma, control->recv_len,
+				   DMA_FROM_DEVICE);
+
+	recv_io = control_get_rsp(control);
+	if (!recv_io)
+		goto out;
+
+	pkt = control_packet(recv_io);
+	if (pkt->hdr.pkt_cmd != CMD_CONFIG_DATA_PATH)
+		goto failure;
+
+	config_data_path = &pkt->cmd.config_data_path_rsp;
+	if (config_data_path->data_path != 0) {
+		CONTROL_ERROR("%s: received CMD_CONFIG_DATA_PATH response"
+			      " for wrong data path: %u\n",
+			      control_ifcfg_name(control),
+			      config_data_path->data_path);
+		goto failure;
+	}
+
+	if (check_recv_pool_config(&config_data_path->
+				   host_recv_pool_config,
+				   host, max_host, min_host)
+	    || check_recv_pool_config(&config_data_path->
+				      eioc_recv_pool_config,
+				      eioc, max_eioc, min_eioc)) {
+		goto failure;
+	}
+
+	control_recv(control, recv_io);
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+
+	return 0;
+failure:
+	viport_failure(control->parent);
+out:
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+	return -1;
+}
+
+int control_exchange_pools_req(struct control *control, u64 addr, u32 rkey)
+{
+	struct send_io			*send_io;
+	struct vnic_control_packet	*pkt;
+	struct vnic_cmd_exchange_pools	*exchange_pools;
+
+	CONTROL_FUNCTION("%s: control_exchange_pools_req()\n",
+			 control_ifcfg_name(control));
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->send_dma, control->send_len,
+				   DMA_TO_DEVICE);
+
+	send_io = control_init_hdr(control, CMD_EXCHANGE_POOLS);
+	if (!send_io)
+		goto failure;
+
+	pkt = control_packet(send_io);
+	exchange_pools = &pkt->cmd.exchange_pools_req;
+	exchange_pools->data_path = 0;
+	exchange_pools->pool_rkey = cpu_to_be32(rkey);
+	exchange_pools->pool_addr = cpu_to_be64(addr);
+
+	control->last_cmd = pkt->hdr.pkt_cmd;
+
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->send_dma, control->send_len,
+				      DMA_TO_DEVICE);
+	return control_send(control, send_io);
+failure:
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->send_dma, control->send_len,
+				      DMA_TO_DEVICE);
+	return -1;
+}
+
+int control_exchange_pools_rsp(struct control *control, u64 *addr,
+			       u32 *rkey)
+{
+	struct recv_io			*recv_io;
+	struct vnic_control_packet	*pkt;
+	struct vnic_cmd_exchange_pools	*exchange_pools;
+
+	CONTROL_FUNCTION("%s: control_exchange_pools_rsp()\n",
+			 control_ifcfg_name(control));
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->recv_dma, control->recv_len,
+				   DMA_FROM_DEVICE);
+
+	recv_io = control_get_rsp(control);
+	if (!recv_io)
+		goto out;
+
+	pkt = control_packet(recv_io);
+	if (pkt->hdr.pkt_cmd != CMD_EXCHANGE_POOLS)
+		goto failure;
+
+	exchange_pools = &pkt->cmd.exchange_pools_rsp;
+	*rkey = be32_to_cpu(exchange_pools->pool_rkey);
+	*addr = be64_to_cpu(exchange_pools->pool_addr);
+
+	if (exchange_pools->data_path != 0) {
+		CONTROL_ERROR("%s: received CMD_EXCHANGE_POOLS response"
+			      " for wrong data path: %u\n",
+			      control_ifcfg_name(control),
+			      exchange_pools->data_path);
+		goto failure;
+	}
+
+	control_recv(control, recv_io);
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+	return 0;
+failure:
+	viport_failure(control->parent);
+out:
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+	return -1;
+}
+
+int control_config_link_req(struct control *control, u16 flags, u16 mtu)
+{
+	struct send_io			*send_io;
+	struct vnic_cmd_config_link	*config_link_req;
+	struct vnic_control_packet	*pkt;
+
+	CONTROL_FUNCTION("%s: control_config_link_req()\n",
+			 control_ifcfg_name(control));
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->send_dma, control->send_len,
+				   DMA_TO_DEVICE);
+
+	send_io = control_init_hdr(control, CMD_CONFIG_LINK);
+	if (!send_io)
+		goto failure;
+
+	pkt = control_packet(send_io);
+	config_link_req = &pkt->cmd.config_link_req;
+	config_link_req->lan_switch_num =
+				control->lan_switch.lan_switch_num;
+	config_link_req->cmd_flags = VNIC_FLAG_SET_MTU;
+	if (flags & IFF_UP)
+		config_link_req->cmd_flags |= VNIC_FLAG_ENABLE_NIC;
+	else
+		config_link_req->cmd_flags |= VNIC_FLAG_DISABLE_NIC;
+	if (flags & IFF_ALLMULTI)
+		config_link_req->cmd_flags |= VNIC_FLAG_ENABLE_MCAST_ALL;
+	else
+		config_link_req->cmd_flags |= VNIC_FLAG_DISABLE_MCAST_ALL;
+	if (flags & IFF_PROMISC) {
+		config_link_req->cmd_flags |= VNIC_FLAG_ENABLE_PROMISC;
+		/* the EIOU doesn't really do PROMISC mode.
+		 * if PROMISC is set, it only receives unicast packets
+		 * I also have to set MCAST_ALL if I want real
+		 * PROMISC mode.
+		 */
+		config_link_req->cmd_flags &= ~VNIC_FLAG_DISABLE_MCAST_ALL;
+		config_link_req->cmd_flags |= VNIC_FLAG_ENABLE_MCAST_ALL;
+	} else
+		config_link_req->cmd_flags |= VNIC_FLAG_DISABLE_PROMISC;
+
+	config_link_req->mtu_size = cpu_to_be16(mtu);
+
+	control->last_cmd = pkt->hdr.pkt_cmd;
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->send_dma, control->send_len,
+				      DMA_TO_DEVICE);
+	return control_send(control, send_io);
+failure:
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->send_dma, control->send_len,
+				      DMA_TO_DEVICE);
+	return -1;
+}
+
+int control_config_link_rsp(struct control *control, u16 *flags, u16 *mtu)
+{
+	struct recv_io			*recv_io;
+	struct vnic_control_packet	*pkt;
+	struct vnic_cmd_config_link	*config_link_rsp;
+
+	CONTROL_FUNCTION("%s: control_config_link_rsp()\n",
+			 control_ifcfg_name(control));
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->recv_dma, control->recv_len,
+				   DMA_FROM_DEVICE);
+
+	recv_io = control_get_rsp(control);
+	if (!recv_io)
+		goto out;
+
+	pkt = control_packet(recv_io);
+	if (pkt->hdr.pkt_cmd != CMD_CONFIG_LINK)
+		goto failure;
+	config_link_rsp = &pkt->cmd.config_link_rsp;
+	if (config_link_rsp->cmd_flags & VNIC_FLAG_ENABLE_NIC)
+		*flags |= IFF_UP;
+	if (config_link_rsp->cmd_flags & VNIC_FLAG_ENABLE_MCAST_ALL)
+		*flags |= IFF_ALLMULTI;
+	if (config_link_rsp->cmd_flags & VNIC_FLAG_ENABLE_PROMISC)
+		*flags |= IFF_PROMISC;
+
+	*mtu = be16_to_cpu(config_link_rsp->mtu_size);
+
+	if (control->parent->features_supported & VNIC_FEAT_INBOUND_IB_MC) {
+		/* featuresSupported might include INBOUND_IB_MC but
+		   MTU might cause it to be auto-disabled at embedded */
+		if (config_link_rsp->cmd_flags & VNIC_FLAG_ENABLE_MCAST_ALL) {
+			union ib_gid mgid = config_link_rsp->allmulti_mgid;
+			if (mgid.raw[0] != 0xff) {
+				CONTROL_ERROR("%s: invalid formatprefix "
+						VNIC_GID_FMT "\n",
+						control_ifcfg_name(control),
+						VNIC_GID_RAW_ARG(mgid.raw));
+			} else {
+				/* rather than issuing join here, which might
+				 * arrive at SM before EVIC creates the MC
+				 * group, postpone it.
+				 */
+				vnic_mc_join_setup(control->parent, &mgid);
+				CONTROL_ERROR("join setup for ALL_MULTI\n");
+			}
+		}
+		/* we don't want to leave mcast group if MCAST_ALL is disabled
+		 * because there are no doubt multicast addresses set and we
+		 * want to stay joined so we can get that traffic via the
+		 * mcast group.
+		 */
+	}
+
+	control_recv(control, recv_io);
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+	return 0;
+failure:
+	viport_failure(control->parent);
+out:
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+	return -1;
+}
+
+/* control_config_addrs_req:
+ * return values:
+ *          -1: failure
+ *           0: incomplete (successful operation, but more address
+ *              table entries to be updated)
+ *           1: complete
+ */
+int control_config_addrs_req(struct control *control,
+			     struct vnic_address_op2 *addrs, u16 num)
+{
+	u16  i;
+	u8   j;
+	int  ret = 1;
+	struct send_io				*send_io;
+	struct vnic_control_packet		*pkt;
+	struct vnic_cmd_config_addresses	*config_addrs_req;
+    struct vnic_cmd_config_addresses2   *config_addrs_req2;
+
+	CONTROL_FUNCTION("%s: control_config_addrs_req()\n",
+			 control_ifcfg_name(control));
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->send_dma, control->send_len,
+				   DMA_TO_DEVICE);
+
+	if (control->parent->features_supported & VNIC_FEAT_INBOUND_IB_MC) {
+		CONTROL_INFO("Sending CMD_CONFIG_ADDRESSES2 %lx MAX:%d "
+				"sizes:%d %d(off:%d) sizes2:%d %d %d"
+				"(off:%d - %d %d %d %d %d %d %d)\n", jiffies,
+				(int)MAX_CONFIG_ADDR_ENTRIES2,
+				(int)sizeof(struct vnic_cmd_config_addresses),
+			(int)sizeof(struct vnic_address_op),
+			(int)offsetof(struct vnic_cmd_config_addresses,
+							list_address_ops),
+			(int)sizeof(struct vnic_cmd_config_addresses2),
+			(int)sizeof(struct vnic_address_op2),
+			(int)sizeof(union ib_gid),
+			(int)offsetof(struct vnic_cmd_config_addresses2,
+							list_address_ops),
+			(int)offsetof(struct vnic_address_op2, index),
+			(int)offsetof(struct vnic_address_op2, operation),
+			(int)offsetof(struct vnic_address_op2, valid),
+			(int)offsetof(struct vnic_address_op2, address),
+			(int)offsetof(struct vnic_address_op2, vlan),
+			(int)offsetof(struct vnic_address_op2, reserved),
+			(int)offsetof(struct vnic_address_op2, mgid)
+			);
+		send_io = control_init_hdr(control, CMD_CONFIG_ADDRESSES2);
+		if (!send_io)
+			goto failure;
+
+		pkt = control_packet(send_io);
+		config_addrs_req2 = &pkt->cmd.config_addresses_req2;
+		memset(pkt->cmd.cmd_data, 0, VNIC_MAX_CONTROLDATASZ);
+		config_addrs_req2->lan_switch_num =
+			control->lan_switch.lan_switch_num;
+		for (i = 0, j = 0; (i < num) && (j < MAX_CONFIG_ADDR_ENTRIES2); i++) {
+			if (!addrs[i].operation)
+				continue;
+			config_addrs_req2->list_address_ops[j].index =
+								 cpu_to_be16(i);
+			config_addrs_req2->list_address_ops[j].operation =
+							VNIC_OP_SET_ENTRY;
+			config_addrs_req2->list_address_ops[j].valid =
+								 addrs[i].valid;
+			memcpy(config_addrs_req2->list_address_ops[j].address,
+			       addrs[i].address, ETH_ALEN);
+			config_addrs_req2->list_address_ops[j].vlan =
+								 addrs[i].vlan;
+			addrs[i].operation = 0;
+			CONTROL_INFO("%s i=%d "
+				"addr[%d]=%02x:%02x:%02x:%02x:%02x:%02x "
+				"valid:%d\n", control_ifcfg_name(control), i, j,
+				addrs[i].address[0], addrs[i].address[1],
+				addrs[i].address[2], addrs[i].address[3],
+				addrs[i].address[4], addrs[i].address[5],
+				addrs[i].valid);
+			j++;
+		}
+		config_addrs_req2->num_address_ops = j;
+	} else {
+		send_io = control_init_hdr(control, CMD_CONFIG_ADDRESSES);
+		if (!send_io)
+			goto failure;
+
+		pkt = control_packet(send_io);
+		config_addrs_req = &pkt->cmd.config_addresses_req;
+		config_addrs_req->lan_switch_num =
+					control->lan_switch.lan_switch_num;
+		for (i = 0, j = 0; (i < num) && (j < 16); i++) {
+			if (!addrs[i].operation)
+				continue;
+			config_addrs_req->list_address_ops[j].index =
+								 cpu_to_be16(i);
+			config_addrs_req->list_address_ops[j].operation =
+							VNIC_OP_SET_ENTRY;
+			config_addrs_req->list_address_ops[j].valid =
+								 addrs[i].valid;
+			memcpy(config_addrs_req->list_address_ops[j].address,
+			       addrs[i].address, ETH_ALEN);
+			config_addrs_req->list_address_ops[j].vlan =
+								 addrs[i].vlan;
+			addrs[i].operation = 0;
+			j++;
+		}
+		config_addrs_req->num_address_ops = j;
+	}
+	for (; i < num; i++) {
+		if (addrs[i].operation) {
+			ret = 0;
+			break;
+		}
+	}
+
+	control->last_cmd = pkt->hdr.pkt_cmd;
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->send_dma, control->send_len,
+				      DMA_TO_DEVICE);
+
+	if (control_send(control, send_io))
+		return -1;
+	return ret;
+failure:
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->send_dma, control->send_len,
+				      DMA_TO_DEVICE);
+	return -1;
+}
+
+static int process_cmd_config_address2_rsp(struct control *control,
+					   struct vnic_control_packet *pkt,
+					   struct recv_io *recv_io)
+{
+	struct vnic_cmd_config_addresses2 *config_addrs_rsp2;
+	int idx, mcaddrs, nomgid;
+	union ib_gid mgid, rsp_mgid;
+
+	config_addrs_rsp2 = &pkt->cmd.config_addresses_rsp2;
+	CONTROL_INFO("%s rsp to CONFIG_ADDRESSES2\n",
+				 control_ifcfg_name(control));
+
+	for (idx = 0, mcaddrs = 0, nomgid = 1;
+			idx < config_addrs_rsp2->num_address_ops;
+				idx++) {
+		if (!config_addrs_rsp2->list_address_ops[idx].valid)
+			continue;
+
+		/* check if address is multicasts */
+		if (!vnic_multicast_address(config_addrs_rsp2, idx))
+			continue;
+
+		mcaddrs++;
+		mgid = config_addrs_rsp2->list_address_ops[idx].mgid;
+		CONTROL_INFO("%s: got mgid " VNIC_GID_FMT
+				" MCAST_MSG_SIZE:%d mtu:%d\n",
+				control_ifcfg_name(control),
+				VNIC_GID_RAW_ARG(mgid.raw),
+				(int)MCAST_MSG_SIZE,
+				control->parent->mtu);
+
+		/* Embedded should have turned off multicast
+		 * due to large MTU size; mgid had better be 0.
+		 */
+		if (control->parent->mtu > MCAST_MSG_SIZE) {
+			if ((mgid.global.subnet_prefix != 0) ||
+				(mgid.global.interface_id != 0)) {
+				CONTROL_ERROR("%s: invalid mgid; "
+						"expected 0 "
+						VNIC_GID_FMT "\n",
+						control_ifcfg_name(control),
+						VNIC_GID_RAW_ARG(mgid.raw));
+				}
+				continue;
+			}
+		if (mgid.raw[0] != 0xff) {
+			CONTROL_ERROR("%s: invalid formatprefix "
+					VNIC_GID_FMT "\n",
+					control_ifcfg_name(control),
+					VNIC_GID_RAW_ARG(mgid.raw));
+			continue;
+		}
+		nomgid = 0; /* got a valid mgid */
+
+		/* let's verify that all the mgids match this one */
+		for (; idx < config_addrs_rsp2->num_address_ops; idx++) {
+			if (!config_addrs_rsp2->list_address_ops[idx].valid)
+				continue;
+
+			/* check if address is multicasts */
+			if (!vnic_multicast_address(config_addrs_rsp2, idx))
+				continue;
+
+			rsp_mgid = config_addrs_rsp2->list_address_ops[idx].mgid;
+			if (memcmp(&mgid, &rsp_mgid, sizeof(union ib_gid)) == 0)
+				continue;
+
+			CONTROL_ERROR("%s: Multicast Group MGIDs not "
+					"unique; mgids: " VNIC_GID_FMT
+					 " " VNIC_GID_FMT "\n",
+					control_ifcfg_name(control),
+					VNIC_GID_RAW_ARG(mgid.raw),
+					VNIC_GID_RAW_ARG(rsp_mgid.raw));
+			return 1;
+		}
+
+		/* rather than issuing join here, which might arrive
+		 * at SM before EVIC creates the MC group, postpone it.
+		 */
+		vnic_mc_join_setup(control->parent, &mgid);
+
+		/* there is only one multicast group to join, so we're done. */
+		break;
+	}
+
+	/* we sent atleast one multicast address but got no MGID
+	 * back so, if it is not allmulti case, leave the group
+	 * we joined before. (for allmulti case we have to stay
+	 * joined)
+	 */
+	if ((config_addrs_rsp2->num_address_ops > 0) && (mcaddrs > 0) &&
+		nomgid && !(control->parent->flags & IFF_ALLMULTI)) {
+		CONTROL_INFO("numaddrops:%d mcadrs:%d nomgid:%d\n",
+			config_addrs_rsp2->num_address_ops,
+				mcaddrs > 0, nomgid);
+
+		vnic_mc_leave(control->parent);
+	}
+
+	return 0;
+}
+
+int control_config_addrs_rsp(struct control *control)
+{
+	struct recv_io *recv_io;
+	struct vnic_control_packet *pkt;
+
+	CONTROL_FUNCTION("%s: control_config_addrs_rsp()\n",
+			 control_ifcfg_name(control));
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->recv_dma, control->recv_len,
+				   DMA_FROM_DEVICE);
+
+	recv_io = control_get_rsp(control);
+	if (!recv_io)
+		goto out;
+
+	pkt = control_packet(recv_io);
+	if ((pkt->hdr.pkt_cmd != CMD_CONFIG_ADDRESSES) &&
+		(pkt->hdr.pkt_cmd != CMD_CONFIG_ADDRESSES2))
+		goto failure;
+
+	if (((pkt->hdr.pkt_cmd == CMD_CONFIG_ADDRESSES2) &&
+	      !control->parent->features_supported & VNIC_FEAT_INBOUND_IB_MC) ||
+	      ((pkt->hdr.pkt_cmd == CMD_CONFIG_ADDRESSES) &&
+	       control->parent->features_supported & VNIC_FEAT_INBOUND_IB_MC)) {
+		CONTROL_ERROR("%s unexpected response pktCmd:%d flag:%x\n",
+				control_ifcfg_name(control), pkt->hdr.pkt_cmd,
+				control->parent->features_supported &
+				VNIC_FEAT_INBOUND_IB_MC);
+		goto failure;
+	}
+
+	if (pkt->hdr.pkt_cmd == CMD_CONFIG_ADDRESSES2) {
+		if (process_cmd_config_address2_rsp(control, pkt, recv_io))
+			goto failure;
+	} else {
+		struct vnic_cmd_config_addresses *config_addrs_rsp;
+		config_addrs_rsp = &pkt->cmd.config_addresses_rsp;
+	}
+
+	control_recv(control, recv_io);
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+	return 0;
+failure:
+	viport_failure(control->parent);
+out:
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+	return -1;
+}
+
+int control_report_statistics_req(struct control *control)
+{
+	struct send_io				*send_io;
+	struct vnic_control_packet		*pkt;
+	struct vnic_cmd_report_stats_req	*report_statistics_req;
+
+	CONTROL_FUNCTION("%s: control_report_statistics_req()\n",
+			 control_ifcfg_name(control));
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->send_dma, control->send_len,
+				   DMA_TO_DEVICE);
+
+	send_io = control_init_hdr(control, CMD_REPORT_STATISTICS);
+	if (!send_io)
+		goto failure;
+
+	pkt = control_packet(send_io);
+	report_statistics_req = &pkt->cmd.report_statistics_req;
+	report_statistics_req->lan_switch_num =
+	    control->lan_switch.lan_switch_num;
+
+	control->last_cmd = pkt->hdr.pkt_cmd;
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->send_dma, control->send_len,
+				      DMA_TO_DEVICE);
+	return control_send(control, send_io);
+failure:
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->send_dma, control->send_len,
+				      DMA_TO_DEVICE);
+	return -1;
+}
+
+int control_report_statistics_rsp(struct control *control,
+				  struct vnic_cmd_report_stats_rsp *stats)
+{
+	struct recv_io				*recv_io;
+	struct vnic_control_packet		*pkt;
+	struct vnic_cmd_report_stats_rsp	*rep_stat_rsp;
+
+	CONTROL_FUNCTION("%s: control_report_statistics_rsp()\n",
+			 control_ifcfg_name(control));
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->recv_dma, control->recv_len,
+				   DMA_FROM_DEVICE);
+
+	recv_io = control_get_rsp(control);
+	if (!recv_io)
+		goto out;
+
+	pkt = control_packet(recv_io);
+	if (pkt->hdr.pkt_cmd != CMD_REPORT_STATISTICS)
+		goto failure;
+
+	rep_stat_rsp = &pkt->cmd.report_statistics_rsp;
+
+	stats->if_in_broadcast_pkts   = rep_stat_rsp->if_in_broadcast_pkts;
+	stats->if_in_multicast_pkts   = rep_stat_rsp->if_in_multicast_pkts;
+	stats->if_in_octets	      = rep_stat_rsp->if_in_octets;
+	stats->if_in_ucast_pkts       = rep_stat_rsp->if_in_ucast_pkts;
+	stats->if_in_nucast_pkts      = rep_stat_rsp->if_in_nucast_pkts;
+	stats->if_in_underrun	      = rep_stat_rsp->if_in_underrun;
+	stats->if_in_errors	      = rep_stat_rsp->if_in_errors;
+	stats->if_out_errors	      = rep_stat_rsp->if_out_errors;
+	stats->if_out_octets	      = rep_stat_rsp->if_out_octets;
+	stats->if_out_ucast_pkts      = rep_stat_rsp->if_out_ucast_pkts;
+	stats->if_out_multicast_pkts  = rep_stat_rsp->if_out_multicast_pkts;
+	stats->if_out_broadcast_pkts  = rep_stat_rsp->if_out_broadcast_pkts;
+	stats->if_out_nucast_pkts     = rep_stat_rsp->if_out_nucast_pkts;
+	stats->if_out_ok	      = rep_stat_rsp->if_out_ok;
+	stats->if_in_ok		      = rep_stat_rsp->if_in_ok;
+	stats->if_out_ucast_bytes     = rep_stat_rsp->if_out_ucast_bytes;
+	stats->if_out_multicast_bytes = rep_stat_rsp->if_out_multicast_bytes;
+	stats->if_out_broadcast_bytes = rep_stat_rsp->if_out_broadcast_bytes;
+	stats->if_in_ucast_bytes      = rep_stat_rsp->if_in_ucast_bytes;
+	stats->if_in_multicast_bytes  = rep_stat_rsp->if_in_multicast_bytes;
+	stats->if_in_broadcast_bytes  = rep_stat_rsp->if_in_broadcast_bytes;
+	stats->ethernet_status	      = rep_stat_rsp->ethernet_status;
+
+	control_recv(control, recv_io);
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+
+	return 0;
+failure:
+	viport_failure(control->parent);
+out:
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+	return -1;
+}
+
+int control_reset_req(struct control *control)
+{
+	struct send_io			*send_io;
+	struct vnic_control_packet	*pkt;
+
+	CONTROL_FUNCTION("%s: control_reset_req()\n",
+			 control_ifcfg_name(control));
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->send_dma, control->send_len,
+				   DMA_TO_DEVICE);
+
+	send_io = control_init_hdr(control, CMD_RESET);
+	if (!send_io)
+		goto failure;
+
+	pkt = control_packet(send_io);
+
+	control->last_cmd = pkt->hdr.pkt_cmd;
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->send_dma, control->send_len,
+				      DMA_TO_DEVICE);
+	return control_send(control, send_io);
+failure:
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->send_dma, control->send_len,
+				      DMA_TO_DEVICE);
+	return -1;
+}
+
+int control_reset_rsp(struct control *control)
+{
+	struct recv_io			*recv_io;
+	struct vnic_control_packet	*pkt;
+
+	CONTROL_FUNCTION("%s: control_reset_rsp()\n",
+			 control_ifcfg_name(control));
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->recv_dma, control->recv_len,
+				   DMA_FROM_DEVICE);
+
+	recv_io = control_get_rsp(control);
+	if (!recv_io)
+		goto out;
+
+	pkt = control_packet(recv_io);
+	if (pkt->hdr.pkt_cmd != CMD_RESET)
+		goto failure;
+
+	control_recv(control, recv_io);
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+	return 0;
+failure:
+	viport_failure(control->parent);
+out:
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+	return -1;
+}
+
+int control_heartbeat_req(struct control *control, u32 hb_interval)
+{
+	struct send_io			*send_io;
+	struct vnic_control_packet	*pkt;
+	struct vnic_cmd_heartbeat	*heartbeat_req;
+
+	CONTROL_FUNCTION("%s: control_heartbeat_req()\n",
+			 control_ifcfg_name(control));
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->send_dma, control->send_len,
+				   DMA_TO_DEVICE);
+
+	send_io = control_init_hdr(control, CMD_HEARTBEAT);
+	if (!send_io)
+		goto failure;
+
+	pkt = control_packet(send_io);
+	heartbeat_req = &pkt->cmd.heartbeat_req;
+	heartbeat_req->hb_interval = cpu_to_be32(hb_interval);
+
+	control->last_cmd = pkt->hdr.pkt_cmd;
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->send_dma, control->send_len,
+				      DMA_TO_DEVICE);
+	return control_send(control, send_io);
+failure:
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->send_dma, control->send_len,
+				      DMA_TO_DEVICE);
+	return -1;
+}
+
+int control_heartbeat_rsp(struct control *control)
+{
+	struct recv_io			*recv_io;
+	struct vnic_control_packet	*pkt;
+	struct vnic_cmd_heartbeat	*heartbeat_rsp;
+
+	CONTROL_FUNCTION("%s: control_heartbeat_rsp()\n",
+			 control_ifcfg_name(control));
+	ib_dma_sync_single_for_cpu(control->parent->config->ibdev,
+				   control->recv_dma, control->recv_len,
+				   DMA_FROM_DEVICE);
+
+	recv_io = control_get_rsp(control);
+	if (!recv_io)
+		goto out;
+
+	pkt = control_packet(recv_io);
+	if (pkt->hdr.pkt_cmd != CMD_HEARTBEAT)
+		goto failure;
+
+	heartbeat_rsp = &pkt->cmd.heartbeat_rsp;
+
+	control_recv(control, recv_io);
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+	return 0;
+failure:
+	viport_failure(control->parent);
+out:
+	ib_dma_sync_single_for_device(control->parent->config->ibdev,
+				      control->recv_dma, control->recv_len,
+				      DMA_FROM_DEVICE);
+	return -1;
+}
+
+static int control_init_recv_ios(struct control *control,
+				 struct viport *viport,
+				 struct vnic_control_packet *pkt)
+{
+	struct io		*io;
+	struct ib_device	*ibdev = viport->config->ibdev;
+	struct control_config	*config = control->config;
+	dma_addr_t		recv_dma;
+	unsigned int		i;
+
+
+	control->recv_len = sizeof *pkt * config->num_recvs;
+	control->recv_dma = ib_dma_map_single(ibdev,
+					      pkt, control->recv_len,
+					      DMA_FROM_DEVICE);
+
+	if (ib_dma_mapping_error(ibdev, control->recv_dma)) {
+		CONTROL_ERROR("control recv dma map error\n");
+		goto failure;
+	}
+
+	recv_dma = control->recv_dma;
+	for (i = 0; i < config->num_recvs; i++) {
+		io = &control->recv_ios[i].io;
+		io->viport = viport;
+		io->routine = control_recv_complete;
+		io->type = RECV;
+
+		control->recv_ios[i].virtual_addr = (u8 *)pkt;
+		control->recv_ios[i].list.addr = recv_dma;
+		control->recv_ios[i].list.length = sizeof *pkt;
+		control->recv_ios[i].list.lkey = control->mr->lkey;
+
+		recv_dma = recv_dma + sizeof *pkt;
+		pkt++;
+
+		io->rwr.wr_id = (u64)io;
+		io->rwr.sg_list = &control->recv_ios[i].list;
+		io->rwr.num_sge = 1;
+		if (vnic_ib_post_recv(&control->ib_conn, io))
+			goto unmap_recv;
+	}
+
+	return 0;
+unmap_recv:
+	ib_dma_unmap_single(control->parent->config->ibdev,
+			    control->recv_dma, control->recv_len,
+			    DMA_FROM_DEVICE);
+failure:
+	return -1;
+}
+
+static int control_init_send_ios(struct control *control,
+				 struct viport *viport,
+				 struct vnic_control_packet *pkt)
+{
+	struct io		*io;
+	struct ib_device	*ibdev = viport->config->ibdev;
+
+	control->send_io.virtual_addr = (u8 *)pkt;
+	control->send_len = sizeof *pkt;
+	control->send_dma = ib_dma_map_single(ibdev, pkt,
+					      control->send_len,
+					      DMA_TO_DEVICE);
+	if (ib_dma_mapping_error(ibdev, control->send_dma)) {
+		CONTROL_ERROR("control send dma map error\n");
+		goto failure;
+	}
+
+	io = &control->send_io.io;
+	io->viport = viport;
+	io->routine = control_send_complete;
+
+	control->send_io.list.addr = control->send_dma;
+	control->send_io.list.length = sizeof *pkt;
+	control->send_io.list.lkey = control->mr->lkey;
+
+	io->swr.wr_id = (u64)io;
+	io->swr.sg_list = &control->send_io.list;
+	io->swr.num_sge = 1;
+	io->swr.opcode = IB_WR_SEND;
+	io->swr.send_flags = IB_SEND_SIGNALED;
+	io->type = SEND;
+
+	return 0;
+failure:
+	return -1;
+}
+
+int control_init(struct control *control, struct viport *viport,
+		 struct control_config *config, struct ib_pd *pd)
+{
+	struct vnic_control_packet	*pkt;
+	unsigned int sz;
+
+	CONTROL_FUNCTION("%s: control_init()\n",
+			 control_ifcfg_name(control));
+	control->parent = viport;
+	control->config = config;
+	control->ib_conn.viport = viport;
+	control->ib_conn.ib_config = &config->ib_config;
+	control->ib_conn.state = IB_CONN_UNINITTED;
+	control->ib_conn.callback_thread = NULL;
+	control->ib_conn.callback_thread_end = 0;
+	control->req_state = REQ_INACTIVE;
+	control->last_cmd  = CMD_INVALID;
+	control->seq_num = 0;
+	control->response = NULL;
+	control->info = NULL;
+	INIT_LIST_HEAD(&control->failure_list);
+	spin_lock_init(&control->io_lock);
+
+	if (vnic_ib_conn_init(&control->ib_conn, viport, pd,
+			      &config->ib_config)) {
+		CONTROL_ERROR("Control IB connection"
+			      " initialization failed\n");
+		goto failure;
+	}
+
+	control->mr = ib_get_dma_mr(pd, IB_ACCESS_LOCAL_WRITE);
+	if (IS_ERR(control->mr)) {
+		CONTROL_ERROR("%s: failed to register memory"
+			      " for control connection\n",
+			      control_ifcfg_name(control));
+		goto destroy_conn;
+	}
+
+	control->ib_conn.cm_id = ib_create_cm_id(viport->config->ibdev,
+						 vnic_ib_cm_handler,
+						 &control->ib_conn);
+	if (IS_ERR(control->ib_conn.cm_id)) {
+		CONTROL_ERROR("creating control CM ID failed\n");
+		goto destroy_mr;
+	}
+
+	sz = sizeof(struct recv_io) * config->num_recvs;
+	control->recv_ios = vmalloc(sz);
+
+	if (!control->recv_ios) {
+		CONTROL_ERROR("%s: failed allocating space for recv ios\n",
+			      control_ifcfg_name(control));
+		goto destroy_cm_id;
+	}
+
+	memset(control->recv_ios, 0, sz);
+	/*One send buffer and num_recvs recv buffers */
+	control->local_storage = kzalloc(sizeof *pkt *
+					 (config->num_recvs + 1),
+					 GFP_KERNEL);
+
+	if (!control->local_storage) {
+		CONTROL_ERROR("%s: failed allocating space"
+			      " for local storage\n",
+			      control_ifcfg_name(control));
+		goto free_recv_ios;
+	}
+
+	pkt = control->local_storage;
+	if (control_init_send_ios(control, viport, pkt))
+		goto free_storage;
+
+	pkt++;
+	if (control_init_recv_ios(control, viport, pkt))
+		goto unmap_send;
+
+	return 0;
+
+unmap_send:
+	ib_dma_unmap_single(control->parent->config->ibdev,
+			    control->send_dma, control->send_len,
+			    DMA_TO_DEVICE);
+free_storage:
+	kfree(control->local_storage);
+free_recv_ios:
+	vfree(control->recv_ios);
+destroy_cm_id:
+	ib_destroy_cm_id(control->ib_conn.cm_id);
+destroy_mr:
+	ib_dereg_mr(control->mr);
+destroy_conn:
+	ib_destroy_qp(control->ib_conn.qp);
+	ib_destroy_cq(control->ib_conn.cq);
+failure:
+	return -1;
+}
+
+void control_cleanup(struct control *control)
+{
+	CONTROL_FUNCTION("%s: control_disconnect()\n",
+			 control_ifcfg_name(control));
+
+	if (ib_send_cm_dreq(control->ib_conn.cm_id, NULL, 0))
+		printk(KERN_DEBUG "control CM DREQ sending failed\n");
+
+	control->ib_conn.state = IB_CONN_DISCONNECTED;
+	control_timer_stop(control);
+	control->req_state  = REQ_INACTIVE;
+	control->response   = NULL;
+	control->last_cmd   = CMD_INVALID;
+	completion_callback_cleanup(&control->ib_conn);
+	ib_destroy_cm_id(control->ib_conn.cm_id);
+	ib_destroy_qp(control->ib_conn.qp);
+	ib_destroy_cq(control->ib_conn.cq);
+	ib_dereg_mr(control->mr);
+	ib_dma_unmap_single(control->parent->config->ibdev,
+			    control->send_dma, control->send_len,
+			    DMA_TO_DEVICE);
+	ib_dma_unmap_single(control->parent->config->ibdev,
+			    control->recv_dma, control->recv_len,
+			    DMA_FROM_DEVICE);
+	vfree(control->recv_ios);
+	kfree(control->local_storage);
+
+}
+
+static void control_log_report_status_pkt(struct vnic_control_packet *pkt)
+{
+	printk(KERN_INFO
+	       "               pkt_cmd = CMD_REPORT_STATUS\n");
+	printk(KERN_INFO
+	       "               pkt_seq_num = %u,"
+	       " pkt_retry_count = %u\n",
+	       pkt->hdr.pkt_seq_num,
+	       pkt->hdr.pkt_retry_count);
+	printk(KERN_INFO
+	       "               lan_switch_num = %u, is_fatal = %u\n",
+	       pkt->cmd.report_status.lan_switch_num,
+	       pkt->cmd.report_status.is_fatal);
+	printk(KERN_INFO
+	       "               status_number = %u, status_info = %u\n",
+	       be32_to_cpu(pkt->cmd.report_status.status_number),
+	       be32_to_cpu(pkt->cmd.report_status.status_info));
+	pkt->cmd.report_status.file_name[31] = '\0';
+	pkt->cmd.report_status.routine[31] = '\0';
+	printk(KERN_INFO "               filename = %s, routine = %s\n",
+	       pkt->cmd.report_status.file_name,
+	       pkt->cmd.report_status.routine);
+	printk(KERN_INFO
+	       "               line_num = %u, error_parameter = %u\n",
+	       be32_to_cpu(pkt->cmd.report_status.line_num),
+	       be32_to_cpu(pkt->cmd.report_status.error_parameter));
+	pkt->cmd.report_status.desc_text[127] = '\0';
+	printk(KERN_INFO "               desc_text = %s\n",
+	       pkt->cmd.report_status.desc_text);
+}
+
+static void control_log_report_stats_pkt(struct vnic_control_packet *pkt)
+{
+	printk(KERN_INFO
+	       "               pkt_cmd = CMD_REPORT_STATISTICS\n");
+	printk(KERN_INFO
+	       "               pkt_seq_num = %u,"
+	       " pkt_retry_count = %u\n",
+	       pkt->hdr.pkt_seq_num,
+	       pkt->hdr.pkt_retry_count);
+	printk(KERN_INFO "               lan_switch_num = %u\n",
+	       pkt->cmd.report_statistics_req.lan_switch_num);
+	if (pkt->hdr.pkt_type == TYPE_REQ)
+		return;
+	printk(KERN_INFO "               if_in_broadcast_pkts = %llu",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_in_broadcast_pkts));
+	printk(" if_in_multicast_pkts = %llu\n",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_in_multicast_pkts));
+	printk(KERN_INFO "               if_in_octets = %llu",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_in_octets));
+	printk(" if_in_ucast_pkts = %llu\n",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_in_ucast_pkts));
+	printk(KERN_INFO "               if_in_nucast_pkts = %llu",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_in_nucast_pkts));
+	printk(" if_in_underrun = %llu\n",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_in_underrun));
+	printk(KERN_INFO "               if_in_errors = %llu",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_in_errors));
+	printk(" if_out_errors = %llu\n",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_out_errors));
+	printk(KERN_INFO "               if_out_octets = %llu",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_out_octets));
+	printk(" if_out_ucast_pkts = %llu\n",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_out_ucast_pkts));
+	printk(KERN_INFO "               if_out_multicast_pkts = %llu",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_out_multicast_pkts));
+	printk(" if_out_broadcast_pkts = %llu\n",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_out_broadcast_pkts));
+	printk(KERN_INFO "               if_out_nucast_pkts = %llu",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_out_nucast_pkts));
+	printk(" if_out_ok = %llu\n",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.if_out_ok));
+	printk(KERN_INFO "               if_in_ok = %llu",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.if_in_ok));
+	printk(" if_out_ucast_bytes = %llu\n",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_out_ucast_bytes));
+	printk(KERN_INFO "               if_out_multicast_bytes = %llu",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+		      if_out_multicast_bytes));
+	printk(" if_out_broadcast_bytes = %llu\n",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_out_broadcast_bytes));
+	printk(KERN_INFO "               if_in_ucast_bytes = %llu",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_in_ucast_bytes));
+	printk(" if_in_multicast_bytes = %llu\n",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_in_multicast_bytes));
+	printk(KERN_INFO "               if_in_broadcast_bytes = %llu",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   if_in_broadcast_bytes));
+	printk(" ethernet_status = %llu\n",
+	       be64_to_cpu(pkt->cmd.report_statistics_rsp.
+			   ethernet_status));
+}
+
+static void control_log_config_link_pkt(struct vnic_control_packet *pkt)
+{
+	printk(KERN_INFO
+	       "               pkt_cmd = CMD_CONFIG_LINK\n");
+	printk(KERN_INFO
+	       "               pkt_seq_num = %u,"
+	       " pkt_retry_count = %u\n",
+	       pkt->hdr.pkt_seq_num,
+	       pkt->hdr.pkt_retry_count);
+	printk(KERN_INFO "               cmd_flags = %x\n",
+	       pkt->cmd.config_link_req.cmd_flags);
+	if (pkt->cmd.config_link_req.cmd_flags & VNIC_FLAG_ENABLE_NIC)
+		printk(KERN_INFO
+		       "                      VNIC_FLAG_ENABLE_NIC\n");
+	if (pkt->cmd.config_link_req.cmd_flags & VNIC_FLAG_DISABLE_NIC)
+		printk(KERN_INFO
+		       "                      VNIC_FLAG_DISABLE_NIC\n");
+	if (pkt->cmd.config_link_req.
+	    cmd_flags & VNIC_FLAG_ENABLE_MCAST_ALL)
+		printk(KERN_INFO
+		       "                     VNIC_FLAG_ENABLE_"
+		       "MCAST_ALL\n");
+	if (pkt->cmd.config_link_req.
+	    cmd_flags & VNIC_FLAG_DISABLE_MCAST_ALL)
+		printk(KERN_INFO
+		       "                       VNIC_FLAG_DISABLE_"
+		       "MCAST_ALL\n");
+	if (pkt->cmd.config_link_req.
+	    cmd_flags & VNIC_FLAG_ENABLE_PROMISC)
+		printk(KERN_INFO
+		       "                       VNIC_FLAG_ENABLE_"
+		       "PROMISC\n");
+	if (pkt->cmd.config_link_req.
+	    cmd_flags & VNIC_FLAG_DISABLE_PROMISC)
+		printk(KERN_INFO
+		       "                       VNIC_FLAG_DISABLE_"
+		       "PROMISC\n");
+	if (pkt->cmd.config_link_req.cmd_flags & VNIC_FLAG_SET_MTU)
+		printk(KERN_INFO
+		       "                       VNIC_FLAG_SET_MTU\n");
+	printk(KERN_INFO
+	       "               lan_switch_num = %x, mtu_size = %d\n",
+	       pkt->cmd.config_link_req.lan_switch_num,
+	       be16_to_cpu(pkt->cmd.config_link_req.mtu_size));
+	if (pkt->hdr.pkt_type == TYPE_RSP) {
+		printk(KERN_INFO
+		       "               default_vlan = %u,"
+		       " hw_mac_address ="
+		       " %02x:%02x:%02x:%02x:%02x:%02x\n",
+		       be16_to_cpu(pkt->cmd.config_link_req.
+				   default_vlan),
+		       pkt->cmd.config_link_req.hw_mac_address[0],
+		       pkt->cmd.config_link_req.hw_mac_address[1],
+		       pkt->cmd.config_link_req.hw_mac_address[2],
+		       pkt->cmd.config_link_req.hw_mac_address[3],
+		       pkt->cmd.config_link_req.hw_mac_address[4],
+		       pkt->cmd.config_link_req.hw_mac_address[5]);
+	}
+}
+
+static void print_config_addr(struct vnic_address_op *list,
+				int num_address_ops, size_t mgidoff)
+{
+	int i = 0;
+
+	while (i < num_address_ops && i < 16) {
+		printk(KERN_INFO "               list_address_ops[%u].index"
+				 " = %u\n", i, be16_to_cpu(list->index));
+		switch (list->operation) {
+		case VNIC_OP_GET_ENTRY:
+			printk(KERN_INFO "               list_address_ops[%u]."
+					 "operation = VNIC_OP_GET_ENTRY\n", i);
+			break;
+		case VNIC_OP_SET_ENTRY:
+			printk(KERN_INFO "               list_address_ops[%u]."
+					 "operation = VNIC_OP_SET_ENTRY\n", i);
+			break;
+		default:
+			printk(KERN_INFO "               list_address_ops[%u]."
+					 "operation = UNKNOWN(%d)\n", i,
+					 list->operation);
+			break;
+		}
+		printk(KERN_INFO "               list_address_ops[%u].valid"
+				 " = %u\n", i, list->valid);
+		printk(KERN_INFO "               list_address_ops[%u].address"
+				 " = %02x:%02x:%02x:%02x:%02x:%02x\n", i,
+				 list->address[0], list->address[1],
+				 list->address[2], list->address[3],
+				 list->address[4], list->address[5]);
+		printk(KERN_INFO "               list_address_ops[%u].vlan"
+				 " = %u\n", i, be16_to_cpu(list->vlan));
+		if (mgidoff) {
+			printk(KERN_INFO
+				 "               list_address_ops[%u].mgid"
+				 " = " VNIC_GID_FMT "\n", i,
+				 VNIC_GID_RAW_ARG((char *)list + mgidoff));
+			list = (struct vnic_address_op *)
+			       ((char *)list + sizeof(struct vnic_address_op2));
+		} else
+			list = (struct vnic_address_op *)
+			       ((char *)list + sizeof(struct vnic_address_op));
+	i++;
+	}
+}
+
+static void control_log_config_addrs_pkt(struct vnic_control_packet *pkt,
+					u8 addresses2)
+{
+	struct vnic_address_op *list;
+	int no_address_ops;
+
+	if (addresses2)
+		printk(KERN_INFO
+			"               pkt_cmd = CMD_CONFIG_ADDRESSES2\n");
+	else
+		printk(KERN_INFO
+			"               pkt_cmd = CMD_CONFIG_ADDRESSES\n");
+	printk(KERN_INFO "               pkt_seq_num = %u,"
+			" pkt_retry_count = %u\n",
+			pkt->hdr.pkt_seq_num, pkt->hdr.pkt_retry_count);
+	if (addresses2) {
+		printk(KERN_INFO "               num_address_ops = %x,"
+				" lan_switch_num = %d\n",
+				pkt->cmd.config_addresses_req2.num_address_ops,
+				pkt->cmd.config_addresses_req2.lan_switch_num);
+		list = (struct vnic_address_op *)
+				pkt->cmd.config_addresses_req2.list_address_ops;
+		no_address_ops = pkt->cmd.config_addresses_req2.num_address_ops;
+		print_config_addr(list, no_address_ops,
+				offsetof(struct vnic_address_op2, mgid));
+	} else {
+		printk(KERN_INFO "               num_address_ops = %x,"
+				" lan_switch_num = %d\n",
+				pkt->cmd.config_addresses_req.num_address_ops,
+				pkt->cmd.config_addresses_req.lan_switch_num);
+		list = pkt->cmd.config_addresses_req.list_address_ops;
+		no_address_ops = pkt->cmd.config_addresses_req.num_address_ops;
+		print_config_addr(list, no_address_ops, 0);
+	}
+}
+
+static void control_log_exch_pools_pkt(struct vnic_control_packet *pkt)
+{
+	printk(KERN_INFO
+	       "               pkt_cmd = CMD_EXCHANGE_POOLS\n");
+	printk(KERN_INFO
+	       "               pkt_seq_num = %u,"
+	       " pkt_retry_count = %u\n",
+	       pkt->hdr.pkt_seq_num,
+	       pkt->hdr.pkt_retry_count);
+	printk(KERN_INFO "               datapath = %u\n",
+	       pkt->cmd.exchange_pools_req.data_path);
+	printk(KERN_INFO "               pool_rkey = %08x"
+	       " pool_addr = %llx\n",
+	       be32_to_cpu(pkt->cmd.exchange_pools_req.pool_rkey),
+	       be64_to_cpu(pkt->cmd.exchange_pools_req.pool_addr));
+}
+
+static void control_log_data_path_pkt(struct vnic_control_packet *pkt)
+{
+	printk(KERN_INFO
+	       "               pkt_cmd = CMD_CONFIG_DATA_PATH\n");
+	printk(KERN_INFO
+	       "               pkt_seq_num = %u,"
+	       " pkt_retry_count = %u\n",
+	       pkt->hdr.pkt_seq_num,
+	       pkt->hdr.pkt_retry_count);
+	printk(KERN_INFO "               path_identifier = %llx,"
+	       " data_path = %u\n",
+	       pkt->cmd.config_data_path_req.path_identifier,
+	       pkt->cmd.config_data_path_req.data_path);
+	printk(KERN_INFO
+	       "host config    size_recv_pool_entry = %u,"
+	       " num_recv_pool_entries = %u\n",
+	       be32_to_cpu(pkt->cmd.config_data_path_req.
+		      host_recv_pool_config.size_recv_pool_entry),
+	       be32_to_cpu(pkt->cmd.config_data_path_req.
+		      host_recv_pool_config.num_recv_pool_entries));
+	printk(KERN_INFO
+	       "               timeout_before_kick = %u,"
+	       " num_recv_pool_entries_before_kick = %u\n",
+	       be32_to_cpu(pkt->cmd.config_data_path_req.
+		      host_recv_pool_config.timeout_before_kick),
+	       be32_to_cpu(pkt->cmd.config_data_path_req.
+		      host_recv_pool_config.
+		      num_recv_pool_entries_before_kick));
+	printk(KERN_INFO
+	       "               num_recv_pool_bytes_before_kick = %u,"
+	       " free_recv_pool_entries_per_update = %u\n",
+	       be32_to_cpu(pkt->cmd.config_data_path_req.
+		      host_recv_pool_config.
+		      num_recv_pool_bytes_before_kick),
+	       be32_to_cpu(pkt->cmd.config_data_path_req.
+		      host_recv_pool_config.
+		      free_recv_pool_entries_per_update));
+	printk(KERN_INFO
+	       "eioc config    size_recv_pool_entry = %u,"
+	       " num_recv_pool_entries = %u\n",
+	       be32_to_cpu(pkt->cmd.config_data_path_req.
+		      eioc_recv_pool_config.size_recv_pool_entry),
+	       be32_to_cpu(pkt->cmd.config_data_path_req.
+		      eioc_recv_pool_config.num_recv_pool_entries));
+	printk(KERN_INFO
+	       "               timeout_before_kick = %u,"
+	       " num_recv_pool_entries_before_kick = %u\n",
+	       be32_to_cpu(pkt->cmd.config_data_path_req.
+		      eioc_recv_pool_config.timeout_before_kick),
+	       be32_to_cpu(pkt->cmd.config_data_path_req.
+		      eioc_recv_pool_config.
+		      num_recv_pool_entries_before_kick));
+	printk(KERN_INFO
+	       "               num_recv_pool_bytes_before_kick = %u,"
+	       " free_recv_pool_entries_per_update = %u\n",
+	       be32_to_cpu(pkt->cmd.config_data_path_req.
+		      eioc_recv_pool_config.
+		      num_recv_pool_bytes_before_kick),
+	       be32_to_cpu(pkt->cmd.config_data_path_req.
+		      eioc_recv_pool_config.
+		      free_recv_pool_entries_per_update));
+}
+
+static void control_log_init_vnic_pkt(struct vnic_control_packet *pkt)
+{
+	printk(KERN_INFO
+	       "               pkt_cmd = CMD_INIT_VNIC\n");
+	printk(KERN_INFO
+	       "               pkt_seq_num = %u,"
+	       " pkt_retry_count = %u\n",
+	       pkt->hdr.pkt_seq_num,
+	       pkt->hdr.pkt_retry_count);
+	printk(KERN_INFO
+	       "               vnic_major_version = %u,"
+	       " vnic_minor_version = %u\n",
+	       be16_to_cpu(pkt->cmd.init_vnic_req.vnic_major_version),
+	       be16_to_cpu(pkt->cmd.init_vnic_req.vnic_minor_version));
+	if (pkt->hdr.pkt_type == TYPE_REQ) {
+		printk(KERN_INFO
+		       "               vnic_instance = %u,"
+		       " num_data_paths = %u\n",
+		       pkt->cmd.init_vnic_req.vnic_instance,
+		       pkt->cmd.init_vnic_req.num_data_paths);
+		printk(KERN_INFO
+		       "               num_address_entries = %u\n",
+		       be16_to_cpu(pkt->cmd.init_vnic_req.
+			      num_address_entries));
+	} else {
+		printk(KERN_INFO
+		       "               num_lan_switches = %u,"
+		       " num_data_paths = %u\n",
+		       pkt->cmd.init_vnic_rsp.num_lan_switches,
+		       pkt->cmd.init_vnic_rsp.num_data_paths);
+		printk(KERN_INFO
+		       "               num_address_entries = %u,"
+		       " features_supported = %08x\n",
+		       be16_to_cpu(pkt->cmd.init_vnic_rsp.
+			      num_address_entries),
+		       be32_to_cpu(pkt->cmd.init_vnic_rsp.
+			      features_supported));
+		if (pkt->cmd.init_vnic_rsp.num_lan_switches != 0) {
+			printk(KERN_INFO
+			       "lan_switch[0]  lan_switch_num = %u,"
+			       " num_enet_ports = %08x\n",
+			       pkt->cmd.init_vnic_rsp.
+			       lan_switch[0].lan_switch_num,
+			       pkt->cmd.init_vnic_rsp.
+			       lan_switch[0].num_enet_ports);
+			printk(KERN_INFO
+			       "               default_vlan = %u,"
+			       " hw_mac_address ="
+			       " %02x:%02x:%02x:%02x:%02x:%02x\n",
+			       be16_to_cpu(pkt->cmd.init_vnic_rsp.
+				      lan_switch[0].default_vlan),
+			       pkt->cmd.init_vnic_rsp.lan_switch[0].
+			       hw_mac_address[0],
+			       pkt->cmd.init_vnic_rsp.lan_switch[0].
+			       hw_mac_address[1],
+			       pkt->cmd.init_vnic_rsp.lan_switch[0].
+			       hw_mac_address[2],
+			       pkt->cmd.init_vnic_rsp.lan_switch[0].
+			       hw_mac_address[3],
+			       pkt->cmd.init_vnic_rsp.lan_switch[0].
+			       hw_mac_address[4],
+			       pkt->cmd.init_vnic_rsp.lan_switch[0].
+			       hw_mac_address[5]);
+		}
+	}
+}
+
+static void control_log_control_packet(struct vnic_control_packet *pkt)
+{
+	switch (pkt->hdr.pkt_type) {
+	case TYPE_INFO:
+		printk(KERN_INFO "control_packet: pkt_type = TYPE_INFO\n");
+		break;
+	case TYPE_REQ:
+		printk(KERN_INFO "control_packet: pkt_type = TYPE_REQ\n");
+		break;
+	case TYPE_RSP:
+		printk(KERN_INFO "control_packet: pkt_type = TYPE_RSP\n");
+		break;
+	case TYPE_ERR:
+		printk(KERN_INFO "control_packet: pkt_type = TYPE_ERR\n");
+		break;
+	default:
+		printk(KERN_INFO "control_packet: pkt_type = UNKNOWN\n");
+	}
+
+	switch (pkt->hdr.pkt_cmd) {
+	case CMD_INIT_VNIC:
+		control_log_init_vnic_pkt(pkt);
+		break;
+	case CMD_CONFIG_DATA_PATH:
+		control_log_data_path_pkt(pkt);
+		break;
+	case CMD_EXCHANGE_POOLS:
+		control_log_exch_pools_pkt(pkt);
+		break;
+	case CMD_CONFIG_ADDRESSES:
+		control_log_config_addrs_pkt(pkt, 0);
+		break;
+	case CMD_CONFIG_ADDRESSES2:
+		control_log_config_addrs_pkt(pkt, 1);
+		break;
+	case CMD_CONFIG_LINK:
+		control_log_config_link_pkt(pkt);
+		break;
+	case CMD_REPORT_STATISTICS:
+		control_log_report_stats_pkt(pkt);
+		break;
+	case CMD_CLEAR_STATISTICS:
+		printk(KERN_INFO
+		       "               pkt_cmd = CMD_CLEAR_STATISTICS\n");
+		printk(KERN_INFO
+		       "               pkt_seq_num = %u,"
+		       " pkt_retry_count = %u\n",
+		       pkt->hdr.pkt_seq_num,
+		       pkt->hdr.pkt_retry_count);
+		break;
+	case CMD_REPORT_STATUS:
+		control_log_report_status_pkt(pkt);
+
+		break;
+	case CMD_RESET:
+		printk(KERN_INFO
+		       "               pkt_cmd = CMD_RESET\n");
+		printk(KERN_INFO
+		       "               pkt_seq_num = %u,"
+		       " pkt_retry_count = %u\n",
+		       pkt->hdr.pkt_seq_num,
+		       pkt->hdr.pkt_retry_count);
+		break;
+	case CMD_HEARTBEAT:
+		printk(KERN_INFO
+		       "               pkt_cmd = CMD_HEARTBEAT\n");
+		printk(KERN_INFO
+		       "               pkt_seq_num = %u,"
+		       " pkt_retry_count = %u\n",
+		       pkt->hdr.pkt_seq_num,
+		       pkt->hdr.pkt_retry_count);
+		printk(KERN_INFO "               hb_interval = %d\n",
+		       be32_to_cpu(pkt->cmd.heartbeat_req.hb_interval));
+		break;
+	default:
+		printk(KERN_INFO
+		       "               pkt_cmd = UNKNOWN (%u)\n",
+		       pkt->hdr.pkt_cmd);
+		printk(KERN_INFO
+		       "               pkt_seq_num = %u,"
+		       " pkt_retry_count = %u\n",
+		       pkt->hdr.pkt_seq_num,
+		       pkt->hdr.pkt_retry_count);
+		break;
+	}
+}
diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_control.h b/drivers/infiniband/ulp/qlgc_vnic/vnic_control.h
new file mode 100644
index 0000000..3cf1fc0
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_control.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2006 QLogic, 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 VNIC_CONTROL_H_INCLUDED
+#define VNIC_CONTROL_H_INCLUDED
+
+#ifdef CONFIG_INFINIBAND_QLGC_VNIC_STATS
+#include <linux/timex.h>
+#include <linux/completion.h>
+#endif	/* CONFIG_INFINIBAND_QLGC_VNIC_STATS */
+
+#include "vnic_ib.h"
+#include "vnic_control_pkt.h"
+
+enum control_timer_state {
+	TIMER_IDLE	= 0,
+	TIMER_ACTIVE	= 1,
+	TIMER_EXPIRED	= 2
+};
+
+enum control_request_state {
+	REQ_INACTIVE,  /* quiet state, all previous operations done
+			*      response is NULL
+			*      last_cmd = CMD_INVALID
+			*      timer_state = IDLE
+			*/
+	REQ_POSTED,    /* REQ put on send Q
+			*      response is NULL
+			*      last_cmd = command issued
+			*      timer_state = ACTIVE
+			*/
+	REQ_SENT,      /* Send completed for REQ
+			*      response is NULL
+			*      last_cmd = command issued
+			*      timer_state = ACTIVE
+			*/
+	RSP_RECEIVED,  /* Received Resp, but no Send completion yet
+			*      response is response buffer received
+			*      last_cmd = command issued
+			*      timer_state = ACTIVE
+			*/
+	REQ_COMPLETED, /* all processing for REQ completed, ready to be gotten
+			*      response is response buffer received
+			*      last_cmd = command issued
+			*      timer_state = ACTIVE
+			*/
+	REQ_FAILED,    /* processing of REQ/RSP failed.
+			*      response is NULL
+			*      last_cmd = CMD_INVALID
+			*      timer_state = IDLE or EXPIRED
+			*      viport has been moved to error state to force
+			*      recovery
+			*/
+};
+
+struct control {
+	struct viport			*parent;
+	struct control_config		*config;
+	struct ib_mr			*mr;
+	struct vnic_ib_conn		ib_conn;
+	struct vnic_control_packet	*local_storage;
+	int				send_len;
+	int				recv_len;
+	u16				maj_ver;
+	u16				min_ver;
+	struct vnic_lan_switch_attribs	lan_switch;
+	struct send_io			send_io;
+	struct recv_io			*recv_ios;
+	dma_addr_t			send_dma;
+	dma_addr_t			recv_dma;
+	enum control_timer_state	timer_state;
+	enum control_request_state      req_state;
+	struct timer_list		timer;
+	u8				seq_num;
+	u8				last_cmd;
+	struct recv_io			*response;
+	struct recv_io			*info;
+	struct list_head		failure_list;
+	spinlock_t			io_lock;
+	struct completion		done;
+#ifdef CONFIG_INFINIBAND_QLGC_VNIC_STATS
+	struct {
+		cycles_t	request_time;	/* intermediate value */
+		cycles_t	response_time;
+		u32		response_num;
+		cycles_t	response_max;
+		cycles_t	response_min;
+		u32		timeout_num;
+	} statistics;
+#endif	/* CONFIG_INFINIBAND_QLGC_VNIC_STATS */
+};
+
+int control_init(struct control *control, struct viport *viport,
+		 struct control_config *config, struct ib_pd *pd);
+
+void control_cleanup(struct control *control);
+
+void control_process_async(struct control *control);
+
+int control_init_vnic_req(struct control *control);
+int control_init_vnic_rsp(struct control *control, u32 *features,
+			  u8 *mac_address, u16 *num_addrs, u16 *vlan);
+
+int control_config_data_path_req(struct control *control, u64 path_id,
+				 struct vnic_recv_pool_config *host,
+				 struct vnic_recv_pool_config *eioc);
+int control_config_data_path_rsp(struct control *control,
+				 struct vnic_recv_pool_config *host,
+				 struct vnic_recv_pool_config *eioc,
+				 struct vnic_recv_pool_config *max_host,
+				 struct vnic_recv_pool_config *max_eioc,
+				 struct vnic_recv_pool_config *min_host,
+				 struct vnic_recv_pool_config *min_eioc);
+
+int control_exchange_pools_req(struct control *control,
+			       u64 addr, u32 rkey);
+int control_exchange_pools_rsp(struct control *control,
+			       u64 *addr, u32 *rkey);
+
+int control_config_link_req(struct control *control,
+			    u16 flags, u16 mtu);
+int control_config_link_rsp(struct control *control,
+			    u16 *flags, u16 *mtu);
+
+int control_config_addrs_req(struct control *control,
+			     struct vnic_address_op2 *addrs, u16 num);
+int control_config_addrs_rsp(struct control *control);
+
+int control_report_statistics_req(struct control *control);
+int control_report_statistics_rsp(struct control *control,
+				  struct vnic_cmd_report_stats_rsp *stats);
+
+int control_heartbeat_req(struct control *control, u32 hb_interval);
+int control_heartbeat_rsp(struct control *control);
+
+int control_reset_req(struct control *control);
+int control_reset_rsp(struct control *control);
+
+
+#define control_packet(io) 					\
+	(struct vnic_control_packet *)(io)->virtual_addr
+#define control_is_connected(control) 				\
+	(vnic_ib_conn_connected(&((control)->ib_conn)))
+
+#define control_last_req(control)	control_packet(&(control)->send_io)
+#define control_features(control)	(control)->features_supported
+
+#define control_get_mac_address(control,addr) 				\
+	memcpy(addr, (control)->lan_switch.hw_mac_address, ETH_ALEN)
+
+#endif	/* VNIC_CONTROL_H_INCLUDED */
diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_control_pkt.h b/drivers/infiniband/ulp/qlgc_vnic/vnic_control_pkt.h
new file mode 100644
index 0000000..1fc62fb
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_control_pkt.h
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2006 QLogic, 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 VNIC_CONTROL_PKT_H_INCLUDED
+#define VNIC_CONTROL_PKT_H_INCLUDED
+
+#include <linux/utsname.h>
+#include <rdma/ib_verbs.h>
+
+#define VNIC_MAX_NODENAME_LEN	64
+
+struct vnic_connection_data {
+	u64	path_id;
+	u8	vnic_instance;
+	u8	path_num;
+	u8	nodename[VNIC_MAX_NODENAME_LEN + 1];
+	u8  	reserved; /* for alignment */
+	__be32 	features_supported;
+};
+
+struct vnic_control_header {
+	u8	pkt_type;
+	u8	pkt_cmd;
+	u8	pkt_seq_num;
+	u8	pkt_retry_count;
+	u32	reserved;	/* for 64-bit alignmnet */
+};
+
+/* ptk_type values */
+enum {
+	TYPE_INFO	= 0,
+	TYPE_REQ	= 1,
+	TYPE_RSP	= 2,
+	TYPE_ERR	= 3
+};
+
+/* ptk_cmd values */
+enum {
+	CMD_INVALID		= 0,
+	CMD_INIT_VNIC		= 1,
+	CMD_CONFIG_DATA_PATH	= 2,
+	CMD_EXCHANGE_POOLS	= 3,
+	CMD_CONFIG_ADDRESSES	= 4,
+	CMD_CONFIG_LINK		= 5,
+	CMD_REPORT_STATISTICS	= 6,
+	CMD_CLEAR_STATISTICS	= 7,
+	CMD_REPORT_STATUS	= 8,
+	CMD_RESET		= 9,
+	CMD_HEARTBEAT		= 10,
+	CMD_CONFIG_ADDRESSES2	= 11,
+};
+
+/* pkt_cmd CMD_INIT_VNIC, pkt_type TYPE_REQ data format */
+struct vnic_cmd_init_vnic_req {
+	__be16	vnic_major_version;
+	__be16	vnic_minor_version;
+	u8	vnic_instance;
+	u8	num_data_paths;
+	__be16	num_address_entries;
+};
+
+/* pkt_cmd CMD_INIT_VNIC, pkt_type TYPE_RSP subdata format */
+struct vnic_lan_switch_attribs {
+	u8	lan_switch_num;
+	u8	num_enet_ports;
+	__be16	default_vlan;
+	u8	hw_mac_address[ETH_ALEN];
+};
+
+/* pkt_cmd CMD_INIT_VNIC, pkt_type TYPE_RSP data format */
+struct vnic_cmd_init_vnic_rsp {
+	__be16				vnic_major_version;
+	__be16				vnic_minor_version;
+	u8				num_lan_switches;
+	u8				num_data_paths;
+	__be16				num_address_entries;
+	__be32				features_supported;
+	struct vnic_lan_switch_attribs	lan_switch[1];
+};
+
+/* features_supported values */
+enum {
+	VNIC_FEAT_IPV4_HEADERS		= 0x0001,
+	VNIC_FEAT_IPV6_HEADERS		= 0x0002,
+	VNIC_FEAT_IPV4_CSUM_RX		= 0x0004,
+	VNIC_FEAT_IPV4_CSUM_TX		= 0x0008,
+	VNIC_FEAT_TCP_CSUM_RX		= 0x0010,
+	VNIC_FEAT_TCP_CSUM_TX		= 0x0020,
+	VNIC_FEAT_UDP_CSUM_RX		= 0x0040,
+	VNIC_FEAT_UDP_CSUM_TX		= 0x0080,
+	VNIC_FEAT_TCP_SEGMENT		= 0x0100,
+	VNIC_FEAT_IPV4_IPSEC_OFFLOAD	= 0x0200,
+	VNIC_FEAT_IPV6_IPSEC_OFFLOAD	= 0x0400,
+	VNIC_FEAT_FCS_PROPAGATE		= 0x0800,
+	VNIC_FEAT_PF_KICK		= 0x1000,
+	VNIC_FEAT_PF_FORCE_ROUTE	= 0x2000,
+	VNIC_FEAT_CHASH_OFFLOAD		= 0x4000,
+	/* host send with immediate data */
+	VNIC_FEAT_RDMA_IMMED		= 0x8000,
+	/* host ignore inbound PF_VLAN_INSERT flag */
+	VNIC_FEAT_IGNORE_VLAN		= 0x10000,
+	/* host supports IB multicast for inbound Ethernet mcast traffic */
+	VNIC_FEAT_INBOUND_IB_MC 	= 0x20000,
+};
+
+/* pkt_cmd CMD_CONFIG_DATA_PATH subdata format */
+struct vnic_recv_pool_config {
+	__be32	size_recv_pool_entry;
+	__be32	num_recv_pool_entries;
+	__be32	timeout_before_kick;
+	__be32	num_recv_pool_entries_before_kick;
+	__be32	num_recv_pool_bytes_before_kick;
+	__be32	free_recv_pool_entries_per_update;
+};
+
+/* pkt_cmd CMD_CONFIG_DATA_PATH data format */
+struct vnic_cmd_config_data_path {
+	u64				path_identifier;
+	u8				data_path;
+	u8				reserved[3];
+	struct vnic_recv_pool_config	host_recv_pool_config;
+	struct vnic_recv_pool_config	eioc_recv_pool_config;
+};
+
+/* pkt_cmd CMD_EXCHANGE_POOLS data format */
+struct vnic_cmd_exchange_pools {
+	u8	data_path;
+	u8	reserved[3];
+	__be32	pool_rkey;
+	__be64	pool_addr;
+};
+
+/* pkt_cmd CMD_CONFIG_ADDRESSES subdata format */
+struct vnic_address_op {
+	__be16	index;
+	u8	operation;
+	u8	valid;
+	u8	address[6];
+	__be16	vlan;
+};
+
+/* pkt_cmd CMD_CONFIG_ADDRESSES2 subdata format */
+struct vnic_address_op2 {
+	__be16	index;
+	u8	operation;
+	u8	valid;
+	u8	address[6];
+	__be16	vlan;
+	u32 reserved; /* for alignment */
+	union ib_gid mgid; /* valid in rsp only if both ends support mcast */
+};
+
+/* operation values */
+enum {
+	VNIC_OP_SET_ENTRY = 0x01,
+	VNIC_OP_GET_ENTRY = 0x02
+};
+
+/* pkt_cmd CMD_CONFIG_ADDRESSES data format */
+struct vnic_cmd_config_addresses {
+	u8			num_address_ops;
+	u8			lan_switch_num;
+	struct vnic_address_op	list_address_ops[1];
+};
+
+/* pkt_cmd CMD_CONFIG_ADDRESSES2 data format */
+struct vnic_cmd_config_addresses2 {
+	u8			num_address_ops;
+	u8			lan_switch_num;
+	u8			reserved1;
+	u8			reserved2;
+	u8			reserved3;
+	struct vnic_address_op2	list_address_ops[1];
+};
+
+/* CMD_CONFIG_LINK data format */
+struct vnic_cmd_config_link {
+	u8	cmd_flags;
+	u8	lan_switch_num;
+	__be16	mtu_size;
+	__be16	default_vlan;
+	u8	hw_mac_address[6];
+	u32	reserved; /* for alignment */
+	/* valid in rsp only if both ends support mcast */
+	union ib_gid allmulti_mgid;
+};
+
+/* cmd_flags values */
+enum {
+	VNIC_FLAG_ENABLE_NIC		= 0x01,
+	VNIC_FLAG_DISABLE_NIC		= 0x02,
+	VNIC_FLAG_ENABLE_MCAST_ALL	= 0x04,
+	VNIC_FLAG_DISABLE_MCAST_ALL	= 0x08,
+	VNIC_FLAG_ENABLE_PROMISC	= 0x10,
+	VNIC_FLAG_DISABLE_PROMISC	= 0x20,
+	VNIC_FLAG_SET_MTU		= 0x40
+};
+
+/* pkt_cmd CMD_REPORT_STATISTICS, pkt_type TYPE_REQ data format */
+struct vnic_cmd_report_stats_req {
+	u8	lan_switch_num;
+};
+
+/* pkt_cmd CMD_REPORT_STATISTICS, pkt_type TYPE_RSP data format */
+struct vnic_cmd_report_stats_rsp {
+	u8	lan_switch_num;
+	u8	reserved[7];		/* for 64-bit alignment */
+	__be64	if_in_broadcast_pkts;
+	__be64	if_in_multicast_pkts;
+	__be64	if_in_octets;
+	__be64	if_in_ucast_pkts;
+	__be64	if_in_nucast_pkts;	/* if_in_broadcast_pkts
+					 + if_in_multicast_pkts */
+	__be64	if_in_underrun;		/* (OID_GEN_RCV_NO_BUFFER) */
+	__be64	if_in_errors;		/* (OID_GEN_RCV_ERROR) */
+	__be64	if_out_errors;		/* (OID_GEN_XMIT_ERROR) */
+	__be64	if_out_octets;
+	__be64	if_out_ucast_pkts;
+	__be64	if_out_multicast_pkts;
+	__be64	if_out_broadcast_pkts;
+	__be64	if_out_nucast_pkts;	/* if_out_broadcast_pkts
+					 + if_out_multicast_pkts */
+	__be64	if_out_ok;		/* if_out_nucast_pkts
+					 + if_out_ucast_pkts(OID_GEN_XMIT_OK) */
+	__be64	if_in_ok;		/* if_in_nucast_pkts
+					 + if_in_ucast_pkts(OID_GEN_RCV_OK) */
+	__be64	if_out_ucast_bytes;	/* (OID_GEN_DIRECTED_BYTES_XMT) */
+	__be64	if_out_multicast_bytes;	/* (OID_GEN_MULTICAST_BYTES_XMT) */
+	__be64	if_out_broadcast_bytes;	/* (OID_GEN_BROADCAST_BYTES_XMT) */
+	__be64	if_in_ucast_bytes;	/* (OID_GEN_DIRECTED_BYTES_RCV) */
+	__be64	if_in_multicast_bytes;	/* (OID_GEN_MULTICAST_BYTES_RCV) */
+	__be64	if_in_broadcast_bytes;	/* (OID_GEN_BROADCAST_BYTES_RCV) */
+	__be64	 ethernet_status;	/* OID_GEN_MEDIA_CONNECT_STATUS) */
+};
+
+/* pkt_cmd CMD_CLEAR_STATISTICS data format */
+struct vnic_cmd_clear_statistics {
+	u8	lan_switch_num;
+};
+
+/* pkt_cmd CMD_REPORT_STATUS data format */
+struct vnic_cmd_report_status {
+	u8	lan_switch_num;
+	u8	is_fatal;
+	u8	reserved[2];		/* for 32-bit alignment */
+	__be32	status_number;
+	__be32	status_info;
+	u8	file_name[32];
+	u8	routine[32];
+	__be32	line_num;
+	__be32	error_parameter;
+	u8	desc_text[128];
+};
+
+/* pkt_cmd CMD_HEARTBEAT data format */
+struct vnic_cmd_heartbeat {
+	__be32	hb_interval;
+};
+
+enum {
+	VNIC_STATUS_LINK_UP			= 1,
+	VNIC_STATUS_LINK_DOWN			= 2,
+	VNIC_STATUS_ENET_AGGREGATION_CHANGE	= 3,
+	VNIC_STATUS_EIOC_SHUTDOWN		= 4,
+	VNIC_STATUS_CONTROL_ERROR		= 5,
+	VNIC_STATUS_EIOC_ERROR			= 6
+};
+
+#define VNIC_MAX_CONTROLPKTSZ		256
+#define VNIC_MAX_CONTROLDATASZ						\
+	(VNIC_MAX_CONTROLPKTSZ - sizeof(struct vnic_control_header))
+
+struct vnic_control_packet {
+	struct vnic_control_header	hdr;
+	union {
+		struct vnic_cmd_init_vnic_req		init_vnic_req;
+		struct vnic_cmd_init_vnic_rsp		init_vnic_rsp;
+		struct vnic_cmd_config_data_path	config_data_path_req;
+		struct vnic_cmd_config_data_path	config_data_path_rsp;
+		struct vnic_cmd_exchange_pools		exchange_pools_req;
+		struct vnic_cmd_exchange_pools		exchange_pools_rsp;
+		struct vnic_cmd_config_addresses	config_addresses_req;
+		struct vnic_cmd_config_addresses2	config_addresses_req2;
+		struct vnic_cmd_config_addresses	config_addresses_rsp;
+		struct vnic_cmd_config_addresses2	config_addresses_rsp2;
+		struct vnic_cmd_config_link		config_link_req;
+		struct vnic_cmd_config_link		config_link_rsp;
+		struct vnic_cmd_report_stats_req	report_statistics_req;
+		struct vnic_cmd_report_stats_rsp	report_statistics_rsp;
+		struct vnic_cmd_clear_statistics	clear_statistics_req;
+		struct vnic_cmd_clear_statistics	clear_statistics_rsp;
+		struct vnic_cmd_report_status		report_status;
+		struct vnic_cmd_heartbeat		heartbeat_req;
+		struct vnic_cmd_heartbeat		heartbeat_rsp;
+
+		char   cmd_data[VNIC_MAX_CONTROLDATASZ];
+	} cmd;
+};
+
+union ib_gid_cpu {
+	u8      raw[16];
+	struct {
+		u64  subnet_prefix;
+		u64  interface_id;
+	} global;
+};
+
+static inline void bswap_ib_gid(union ib_gid *mgid1, union ib_gid_cpu *mgid2)
+{
+    /* swap hi & low */
+    __be64 low = mgid1->global.subnet_prefix;
+    mgid2->global.subnet_prefix = be64_to_cpu(mgid1->global.interface_id);
+    mgid2->global.interface_id = be64_to_cpu(low);
+}
+
+#define VNIC_GID_FMT 	"%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
+
+#define VNIC_GID_RAW_ARG(gid) 	be16_to_cpu(*(__be16 *)&(gid)[0]),	\
+				be16_to_cpu(*(__be16 *)&(gid)[2]),	\
+				be16_to_cpu(*(__be16 *)&(gid)[4]),	\
+				be16_to_cpu(*(__be16 *)&(gid)[6]),	\
+				be16_to_cpu(*(__be16 *)&(gid)[8]),	\
+				be16_to_cpu(*(__be16 *)&(gid)[10]),	\
+				be16_to_cpu(*(__be16 *)&(gid)[12]),	\
+				be16_to_cpu(*(__be16 *)&(gid)[14])
+
+
+/* These defines are used to figure out how many address entries can be passed
+ * in config_addresses request.
+ */
+#define MAX_CONFIG_ADDR_ENTRIES \
+	((VNIC_MAX_CONTROLDATASZ - (sizeof(struct vnic_cmd_config_addresses) \
+	- sizeof(struct vnic_address_op)))/sizeof(struct vnic_address_op))
+#define MAX_CONFIG_ADDR_ENTRIES2 \
+	((VNIC_MAX_CONTROLDATASZ - (sizeof(struct vnic_cmd_config_addresses2) \
+	- sizeof(struct vnic_address_op2)))/sizeof(struct vnic_address_op2))
+
+
+#endif	/* VNIC_CONTROL_PKT_H_INCLUDED */

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [ofa-general] [PATCH 05/13] QLogic VNIC: Implementation of Data path of communication protocol
  2008-04-30 17:15 [ofa-general] [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Ramachandra K
                   ` (3 preceding siblings ...)
  2008-04-30 17:17 ` [ofa-general] [PATCH 04/13] QLogic VNIC: Implementation of Control path of communication protocol Ramachandra K
@ 2008-04-30 17:18 ` Ramachandra K
  2008-04-30 17:18 ` [ofa-general] [PATCH 06/13] QLogic VNIC: IB core stack interaction Ramachandra K
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 32+ messages in thread
From: Ramachandra K @ 2008-04-30 17:18 UTC (permalink / raw)
  To: rdreier, general, netdev; +Cc: poornima.kamath, amar.mudrankit

From: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>

This patch implements the actual data transfer part of the communication
protocol with the EVIC/VEx. RDMA of ethernet packets is implemented in
here.

Signed-off-by: Poornima Kamath <poornima.kamath@qlogic.com>
Signed-off-by: Amar Mudrankit <amar.mudrankit@qlogic.com>
---

 drivers/infiniband/ulp/qlgc_vnic/vnic_data.c    | 1473 +++++++++++++++++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_data.h    |  206 +++
 drivers/infiniband/ulp/qlgc_vnic/vnic_trailer.h |  103 ++
 3 files changed, 1782 insertions(+), 0 deletions(-)
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_data.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_data.h
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_trailer.h

diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_data.c b/drivers/infiniband/ulp/qlgc_vnic/vnic_data.c
new file mode 100644
index 0000000..599e716
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_data.c
@@ -0,0 +1,1473 @@
+/*
+ * Copyright (c) 2006 QLogic, 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 <net/inet_sock.h>
+#include <linux/ip.h>
+#include <linux/if_ether.h>
+#include <linux/vmalloc.h>
+
+#include "vnic_util.h"
+#include "vnic_viport.h"
+#include "vnic_main.h"
+#include "vnic_data.h"
+#include "vnic_trailer.h"
+#include "vnic_stats.h"
+
+static void data_received_kick(struct io *io);
+static void data_xmit_complete(struct io *io);
+
+static void mc_data_recv_routine(struct io *io);
+static void mc_data_post_recvs(struct mc_data *mc_data);
+static void mc_data_recv_to_skbuff(struct viport *viport, struct sk_buff *skb,
+	struct viport_trailer *trailer);
+
+static u32 min_rcv_skb = 60;
+module_param(min_rcv_skb, int, 0444);
+MODULE_PARM_DESC(min_rcv_skb, "Packets of size (in bytes) less than"
+		 " or equal this value will be copied during receive."
+		 " Default 60");
+
+static u32 min_xmt_skb = 60;
+module_param(min_xmt_skb, int, 0444);
+MODULE_PARM_DESC(min_xmit_skb, "Packets of size (in bytes) less than"
+		 " or equal to this value will be copied during transmit."
+		 "Default 60");
+
+int data_init(struct data *data, struct viport *viport,
+	      struct data_config *config, struct ib_pd *pd)
+{
+	DATA_FUNCTION("data_init()\n");
+
+	data->parent = viport;
+	data->config = config;
+	data->ib_conn.viport = viport;
+	data->ib_conn.ib_config = &config->ib_config;
+	data->ib_conn.state = IB_CONN_UNINITTED;
+	data->ib_conn.callback_thread = NULL;
+	data->ib_conn.callback_thread_end = 0;
+
+	if ((min_xmt_skb < 60) || (min_xmt_skb > 9000)) {
+		DATA_ERROR("min_xmt_skb (%d) must be between 60 and 9000\n",
+			   min_xmt_skb);
+		goto failure;
+	}
+	if (vnic_ib_conn_init(&data->ib_conn, viport, pd,
+			      &config->ib_config)) {
+		DATA_ERROR("Data IB connection initialization failed\n");
+		goto failure;
+	}
+	data->mr = ib_get_dma_mr(pd,
+				 IB_ACCESS_LOCAL_WRITE |
+				 IB_ACCESS_REMOTE_READ |
+				 IB_ACCESS_REMOTE_WRITE);
+	if (IS_ERR(data->mr)) {
+		DATA_ERROR("failed to register memory for"
+			   " data connection\n");
+		goto destroy_conn;
+	}
+
+	data->ib_conn.cm_id = ib_create_cm_id(viport->config->ibdev,
+					      vnic_ib_cm_handler,
+					      &data->ib_conn);
+
+	if (IS_ERR(data->ib_conn.cm_id)) {
+		DATA_ERROR("creating data CM ID failed\n");
+		goto dereg_mr;
+	}
+
+	return 0;
+
+dereg_mr:
+	ib_dereg_mr(data->mr);
+destroy_conn:
+	completion_callback_cleanup(&data->ib_conn);
+	ib_destroy_qp(data->ib_conn.qp);
+	ib_destroy_cq(data->ib_conn.cq);
+failure:
+	return -1;
+}
+
+static void data_post_recvs(struct data *data)
+{
+	unsigned long flags;
+	int i = 0;
+
+	DATA_FUNCTION("data_post_recvs()\n");
+	spin_lock_irqsave(&data->recv_ios_lock, flags);
+	while (!list_empty(&data->recv_ios)) {
+		struct io *io = list_entry(data->recv_ios.next,
+					   struct io, list_ptrs);
+		struct recv_io *recv_io = (struct recv_io *)io;
+
+		list_del(&recv_io->io.list_ptrs);
+		spin_unlock_irqrestore(&data->recv_ios_lock, flags);
+		if (vnic_ib_post_recv(&data->ib_conn, &recv_io->io)) {
+			viport_failure(data->parent);
+			return;
+		}
+		i++;
+		spin_lock_irqsave(&data->recv_ios_lock, flags);
+	}
+	spin_unlock_irqrestore(&data->recv_ios_lock, flags);
+	DATA_INFO("data posted %d %p\n", i, &data->recv_ios);
+}
+
+static void data_init_pool_work_reqs(struct data *data,
+				      struct recv_io *recv_io)
+{
+	struct recv_pool	*recv_pool = &data->recv_pool;
+	struct xmit_pool	*xmit_pool = &data->xmit_pool;
+	struct rdma_io		*rdma_io;
+	struct rdma_dest	*rdma_dest;
+	dma_addr_t		xmit_dma;
+	u8			*xmit_data;
+	unsigned int		i;
+
+	INIT_LIST_HEAD(&data->recv_ios);
+	spin_lock_init(&data->recv_ios_lock);
+	spin_lock_init(&data->xmit_buf_lock);
+	for (i = 0; i < data->config->num_recvs; i++) {
+		recv_io[i].io.viport = data->parent;
+		recv_io[i].io.routine = data_received_kick;
+		recv_io[i].list.addr = data->region_data_dma;
+		recv_io[i].list.length = 4;
+		recv_io[i].list.lkey = data->mr->lkey;
+
+		recv_io[i].io.rwr.wr_id = (u64)&recv_io[i].io;
+		recv_io[i].io.rwr.sg_list = &recv_io[i].list;
+		recv_io[i].io.rwr.num_sge = 1;
+
+		list_add(&recv_io[i].io.list_ptrs, &data->recv_ios);
+	}
+
+	INIT_LIST_HEAD(&recv_pool->avail_recv_bufs);
+	for (i = 0; i < recv_pool->pool_sz; i++) {
+		rdma_dest = &recv_pool->recv_bufs[i];
+		list_add(&rdma_dest->list_ptrs,
+			 &recv_pool->avail_recv_bufs);
+	}
+
+	xmit_dma = xmit_pool->xmitdata_dma;
+	xmit_data = xmit_pool->xmit_data;
+
+	for (i = 0; i < xmit_pool->num_xmit_bufs; i++) {
+		rdma_io = &xmit_pool->xmit_bufs[i];
+		rdma_io->index = i;
+		rdma_io->io.viport = data->parent;
+		rdma_io->io.routine = data_xmit_complete;
+
+		rdma_io->list[0].lkey = data->mr->lkey;
+		rdma_io->list[1].lkey = data->mr->lkey;
+		rdma_io->io.swr.wr_id = (u64)rdma_io;
+		rdma_io->io.swr.sg_list = rdma_io->list;
+		rdma_io->io.swr.num_sge = 2;
+		rdma_io->io.swr.opcode = IB_WR_RDMA_WRITE;
+		rdma_io->io.swr.send_flags = IB_SEND_SIGNALED;
+		rdma_io->io.type = RDMA;
+
+		rdma_io->data = xmit_data;
+		rdma_io->data_dma = xmit_dma;
+
+		xmit_data += ALIGN(min_xmt_skb, VIPORT_TRAILER_ALIGNMENT);
+		xmit_dma += ALIGN(min_xmt_skb, VIPORT_TRAILER_ALIGNMENT);
+		rdma_io->trailer = (struct viport_trailer *)xmit_data;
+		rdma_io->trailer_dma = xmit_dma;
+		xmit_data += sizeof(struct viport_trailer);
+		xmit_dma += sizeof(struct viport_trailer);
+	}
+
+	xmit_pool->rdma_rkey = data->mr->rkey;
+	xmit_pool->rdma_addr = xmit_pool->buf_pool_dma;
+}
+
+static void data_init_free_bufs_swrs(struct data *data)
+{
+	struct rdma_io		*rdma_io;
+	struct send_io		*send_io;
+
+	rdma_io = &data->free_bufs_io;
+	rdma_io->io.viport = data->parent;
+	rdma_io->io.routine = NULL;
+
+	rdma_io->list[0].lkey = data->mr->lkey;
+
+	rdma_io->io.swr.wr_id = (u64)rdma_io;
+	rdma_io->io.swr.sg_list = rdma_io->list;
+	rdma_io->io.swr.num_sge = 1;
+	rdma_io->io.swr.opcode = IB_WR_RDMA_WRITE;
+	rdma_io->io.swr.send_flags = IB_SEND_SIGNALED;
+	rdma_io->io.type = RDMA;
+
+	send_io = &data->kick_io;
+	send_io->io.viport = data->parent;
+	send_io->io.routine = NULL;
+
+	send_io->list.addr = data->region_data_dma;
+	send_io->list.length = 0;
+	send_io->list.lkey = data->mr->lkey;
+
+	send_io->io.swr.wr_id = (u64)send_io;
+	send_io->io.swr.sg_list = &send_io->list;
+	send_io->io.swr.num_sge = 1;
+	send_io->io.swr.opcode = IB_WR_SEND;
+	send_io->io.swr.send_flags = IB_SEND_SIGNALED;
+	send_io->io.type = SEND;
+}
+
+static int data_init_buf_pools(struct data *data)
+{
+	struct recv_pool	*recv_pool = &data->recv_pool;
+	struct xmit_pool	*xmit_pool = &data->xmit_pool;
+	struct viport		*viport = data->parent;
+
+	recv_pool->buf_pool_len =
+	    sizeof(struct buff_pool_entry) * recv_pool->eioc_pool_sz;
+
+	recv_pool->buf_pool = kzalloc(recv_pool->buf_pool_len, GFP_KERNEL);
+
+	if (!recv_pool->buf_pool) {
+		DATA_ERROR("failed allocating %d bytes"
+			   " for recv pool bufpool\n",
+			   recv_pool->buf_pool_len);
+		goto failure;
+	}
+
+	recv_pool->buf_pool_dma =
+	    ib_dma_map_single(viport->config->ibdev,
+			      recv_pool->buf_pool, recv_pool->buf_pool_len,
+			      DMA_TO_DEVICE);
+
+	if (ib_dma_mapping_error(viport->config->ibdev, recv_pool->buf_pool_dma)) {
+		DATA_ERROR("xmit buf_pool dma map error\n");
+		goto free_recv_pool;
+	}
+
+	xmit_pool->buf_pool_len =
+	    sizeof(struct buff_pool_entry) * xmit_pool->pool_sz;
+	xmit_pool->buf_pool = kzalloc(xmit_pool->buf_pool_len, GFP_KERNEL);
+
+	if (!xmit_pool->buf_pool) {
+		DATA_ERROR("failed allocating %d bytes"
+			   " for xmit pool bufpool\n",
+			   xmit_pool->buf_pool_len);
+		goto unmap_recv_pool;
+	}
+
+	xmit_pool->buf_pool_dma =
+	    ib_dma_map_single(viport->config->ibdev,
+			      xmit_pool->buf_pool, xmit_pool->buf_pool_len,
+			      DMA_FROM_DEVICE);
+
+	if (ib_dma_mapping_error(viport->config->ibdev, xmit_pool->buf_pool_dma)) {
+		DATA_ERROR("xmit buf_pool dma map error\n");
+		goto free_xmit_pool;
+	}
+
+	xmit_pool->xmit_data = kzalloc(xmit_pool->xmitdata_len, GFP_KERNEL);
+
+	if (!xmit_pool->xmit_data) {
+		DATA_ERROR("failed allocating %d bytes for xmit data\n",
+			   xmit_pool->xmitdata_len);
+		goto unmap_xmit_pool;
+	}
+
+	xmit_pool->xmitdata_dma =
+	    ib_dma_map_single(viport->config->ibdev,
+			      xmit_pool->xmit_data, xmit_pool->xmitdata_len,
+			      DMA_TO_DEVICE);
+
+	if (ib_dma_mapping_error(viport->config->ibdev, xmit_pool->xmitdata_dma)) {
+		DATA_ERROR("xmit data dma map error\n");
+		goto free_xmit_data;
+	}
+
+	return 0;
+
+free_xmit_data:
+	kfree(xmit_pool->xmit_data);
+unmap_xmit_pool:
+	ib_dma_unmap_single(data->parent->config->ibdev,
+			    xmit_pool->buf_pool_dma,
+			    xmit_pool->buf_pool_len, DMA_FROM_DEVICE);
+free_xmit_pool:
+	kfree(xmit_pool->buf_pool);
+unmap_recv_pool:
+	ib_dma_unmap_single(data->parent->config->ibdev,
+			    recv_pool->buf_pool_dma,
+			    recv_pool->buf_pool_len, DMA_TO_DEVICE);
+free_recv_pool:
+	kfree(recv_pool->buf_pool);
+failure:
+	return -1;
+}
+
+static void data_init_xmit_pool(struct data *data)
+{
+	struct xmit_pool	*xmit_pool = &data->xmit_pool;
+
+	xmit_pool->pool_sz =
+		be32_to_cpu(data->eioc_pool_parms.num_recv_pool_entries);
+	xmit_pool->buffer_sz =
+		be32_to_cpu(data->eioc_pool_parms.size_recv_pool_entry);
+
+	xmit_pool->notify_count = 0;
+	xmit_pool->notify_bundle = data->config->notify_bundle;
+	xmit_pool->next_xmit_pool = 0;
+	xmit_pool->num_xmit_bufs = xmit_pool->notify_bundle * 2;
+	xmit_pool->next_xmit_buf = 0;
+	xmit_pool->last_comp_buf = xmit_pool->num_xmit_bufs - 1;
+	/* This assumes that data_init_recv_pool has been called
+	 * before.
+	 */
+	data->max_mtu = MAX_PAYLOAD(min((data)->recv_pool.buffer_sz,
+				   (data)->xmit_pool.buffer_sz)) - VLAN_ETH_HLEN;
+
+	xmit_pool->kick_count = 0;
+	xmit_pool->kick_byte_count = 0;
+
+	xmit_pool->send_kicks =
+	  be32_to_cpu(data->
+			eioc_pool_parms.num_recv_pool_entries_before_kick)
+	  || be32_to_cpu(data->
+			eioc_pool_parms.num_recv_pool_bytes_before_kick);
+	xmit_pool->kick_bundle =
+	    be32_to_cpu(data->
+			eioc_pool_parms.num_recv_pool_entries_before_kick);
+	xmit_pool->kick_byte_bundle =
+	    be32_to_cpu(data->
+			eioc_pool_parms.num_recv_pool_bytes_before_kick);
+
+	xmit_pool->need_buffers = 1;
+
+	xmit_pool->xmitdata_len =
+	    BUFFER_SIZE(min_xmt_skb) * xmit_pool->num_xmit_bufs;
+}
+
+static void data_init_recv_pool(struct data *data)
+{
+	struct recv_pool	*recv_pool = &data->recv_pool;
+
+	recv_pool->pool_sz = data->config->host_recv_pool_entries;
+	recv_pool->eioc_pool_sz =
+		be32_to_cpu(data->host_pool_parms.num_recv_pool_entries);
+	if (recv_pool->pool_sz > recv_pool->eioc_pool_sz)
+		recv_pool->pool_sz =
+		    be32_to_cpu(data->host_pool_parms.num_recv_pool_entries);
+
+	recv_pool->buffer_sz =
+		    be32_to_cpu(data->host_pool_parms.size_recv_pool_entry);
+
+	recv_pool->sz_free_bundle =
+		be32_to_cpu(data->
+			host_pool_parms.free_recv_pool_entries_per_update);
+	recv_pool->num_free_bufs = 0;
+	recv_pool->num_posted_bufs = 0;
+
+	recv_pool->next_full_buf = 0;
+	recv_pool->next_free_buf = 0;
+	recv_pool->kick_on_free  = 0;
+}
+
+int data_connect(struct data *data)
+{
+	struct xmit_pool	*xmit_pool = &data->xmit_pool;
+	struct recv_pool	*recv_pool = &data->recv_pool;
+	struct recv_io		*recv_io;
+	unsigned int		sz;
+	struct viport		*viport = data->parent;
+
+	DATA_FUNCTION("data_connect()\n");
+
+	/* Do not interchange the order of the functions
+	 * called below as this will affect the MAX MTU
+	 * calculation
+	 */
+
+	data_init_recv_pool(data);
+	data_init_xmit_pool(data);
+
+	sz = sizeof(struct rdma_dest) * recv_pool->pool_sz    +
+	     sizeof(struct recv_io) * data->config->num_recvs +
+	     sizeof(struct rdma_io) * xmit_pool->num_xmit_bufs;
+
+	data->local_storage = vmalloc(sz);
+
+	if (!data->local_storage) {
+		DATA_ERROR("failed allocating %d bytes"
+			   " local storage\n", sz);
+		goto out;
+	}
+
+	memset(data->local_storage, 0, sz);
+
+	recv_pool->recv_bufs = (struct rdma_dest *)data->local_storage;
+	sz = sizeof(struct rdma_dest) * recv_pool->pool_sz;
+
+	recv_io = (struct recv_io *)(data->local_storage + sz);
+	sz += sizeof(struct recv_io) * data->config->num_recvs;
+
+	xmit_pool->xmit_bufs = (struct rdma_io *)(data->local_storage + sz);
+	data->region_data = kzalloc(4, GFP_KERNEL);
+
+	if (!data->region_data) {
+		DATA_ERROR("failed to alloc memory for region data\n");
+		goto free_local_storage;
+	}
+
+	data->region_data_dma =
+	    ib_dma_map_single(viport->config->ibdev,
+			      data->region_data, 4, DMA_BIDIRECTIONAL);
+
+	if (ib_dma_mapping_error(viport->config->ibdev, data->region_data_dma)) {
+		DATA_ERROR("region data dma map error\n");
+		goto free_region_data;
+	}
+
+	if (data_init_buf_pools(data))
+		goto unmap_region_data;
+
+	data_init_free_bufs_swrs(data);
+	data_init_pool_work_reqs(data, recv_io);
+
+	data_post_recvs(data);
+
+	if (vnic_ib_cm_connect(&data->ib_conn))
+		goto unmap_region_data;
+
+	return 0;
+
+unmap_region_data:
+	ib_dma_unmap_single(data->parent->config->ibdev,
+			    data->region_data_dma, 4, DMA_BIDIRECTIONAL);
+free_region_data:
+		kfree(data->region_data);
+free_local_storage:
+		vfree(data->local_storage);
+out:
+	return -1;
+}
+
+static void data_add_free_buffer(struct data *data, int index,
+				 struct rdma_dest *rdma_dest)
+{
+	struct recv_pool *pool = &data->recv_pool;
+	struct buff_pool_entry *bpe;
+	dma_addr_t vaddr_dma;
+
+	DATA_FUNCTION("data_add_free_buffer()\n");
+	rdma_dest->trailer->connection_hash_and_valid = 0;
+	ib_dma_sync_single_for_cpu(data->parent->config->ibdev,
+				   pool->buf_pool_dma, pool->buf_pool_len,
+				   DMA_TO_DEVICE);
+
+	bpe = &pool->buf_pool[index];
+	bpe->rkey = cpu_to_be32(data->mr->rkey);
+	vaddr_dma = ib_dma_map_single(data->parent->config->ibdev,
+					rdma_dest->data, pool->buffer_sz,
+					DMA_FROM_DEVICE);
+	if (ib_dma_mapping_error(data->parent->config->ibdev, vaddr_dma)) {
+		DATA_ERROR("rdma_dest->data dma map error\n");
+		goto failure;
+	}
+	bpe->remote_addr = cpu_to_be64(vaddr_dma);
+	bpe->valid = (u32) (rdma_dest - &pool->recv_bufs[0]) + 1;
+	++pool->num_free_bufs;
+failure:
+	ib_dma_sync_single_for_device(data->parent->config->ibdev,
+				      pool->buf_pool_dma, pool->buf_pool_len,
+				      DMA_TO_DEVICE);
+}
+
+/* NOTE: this routine is not reentrant */
+static void data_alloc_buffers(struct data *data, int initial_allocation)
+{
+	struct recv_pool *pool = &data->recv_pool;
+	struct rdma_dest *rdma_dest;
+	struct sk_buff *skb;
+	int index;
+
+	DATA_FUNCTION("data_alloc_buffers()\n");
+	index = ADD(pool->next_free_buf, pool->num_free_bufs,
+		    pool->eioc_pool_sz);
+
+	while (!list_empty(&pool->avail_recv_bufs)) {
+		rdma_dest =
+		    list_entry(pool->avail_recv_bufs.next,
+			       struct rdma_dest, list_ptrs);
+		if (!rdma_dest->skb) {
+			if (initial_allocation)
+				skb = alloc_skb(pool->buffer_sz + 2,
+						GFP_KERNEL);
+			else
+				skb = dev_alloc_skb(pool->buffer_sz + 2);
+			if (!skb)
+				break;
+			skb_reserve(skb, 2);
+			skb_put(skb, pool->buffer_sz);
+			rdma_dest->skb = skb;
+			rdma_dest->data = skb->data;
+			rdma_dest->trailer =
+			  (struct viport_trailer *)(rdma_dest->data +
+						    pool->buffer_sz -
+						    sizeof(struct
+							   viport_trailer));
+		}
+		rdma_dest->trailer->connection_hash_and_valid = 0;
+
+		list_del_init(&rdma_dest->list_ptrs);
+
+		data_add_free_buffer(data, index, rdma_dest);
+		index = NEXT(index, pool->eioc_pool_sz);
+	}
+}
+
+static void data_send_kick_message(struct data *data)
+{
+	struct xmit_pool *pool = &data->xmit_pool;
+	DATA_FUNCTION("data_send_kick_message()\n");
+	/* stop timer for bundle_timeout */
+	if (data->kick_timer_on) {
+		del_timer(&data->kick_timer);
+		data->kick_timer_on = 0;
+	}
+	pool->kick_count = 0;
+	pool->kick_byte_count = 0;
+
+	/* TODO: keep track of when kick is outstanding, and
+	 * don't reuse until complete
+	 */
+	if (vnic_ib_post_send(&data->ib_conn, &data->free_bufs_io.io)) {
+		DATA_ERROR("failed to post send\n");
+		viport_failure(data->parent);
+	}
+}
+
+static void data_send_free_recv_buffers(struct data *data)
+{
+	struct recv_pool *pool = &data->recv_pool;
+	struct ib_send_wr *swr = &data->free_bufs_io.io.swr;
+
+	int bufs_sent = 0;
+	u64 rdma_addr;
+	u32 offset;
+	u32 sz;
+	unsigned int num_to_send, next_increment;
+
+	DATA_FUNCTION("data_send_free_recv_buffers()\n");
+
+	for (num_to_send = pool->sz_free_bundle;
+	     num_to_send <= pool->num_free_bufs;
+	     num_to_send += pool->sz_free_bundle) {
+		/* handle multiple bundles as one when possible. */
+		next_increment = num_to_send + pool->sz_free_bundle;
+		if ((next_increment <= pool->num_free_bufs)
+		    && (pool->next_free_buf + next_increment <=
+			pool->eioc_pool_sz))
+			continue;
+
+		offset = pool->next_free_buf *
+				sizeof(struct buff_pool_entry);
+		sz = num_to_send * sizeof(struct buff_pool_entry);
+		rdma_addr = pool->eioc_rdma_addr + offset;
+		swr->sg_list->length = sz;
+		swr->sg_list->addr = pool->buf_pool_dma + offset;
+		swr->wr.rdma.remote_addr = rdma_addr;
+
+		if (vnic_ib_post_send(&data->ib_conn,
+		    &data->free_bufs_io.io)) {
+			DATA_ERROR("failed to post send\n");
+			viport_failure(data->parent);
+			return;
+		}
+		INC(pool->next_free_buf, num_to_send, pool->eioc_pool_sz);
+		pool->num_free_bufs -= num_to_send;
+		pool->num_posted_bufs += num_to_send;
+		bufs_sent = 1;
+	}
+
+	if (bufs_sent) {
+		if (pool->kick_on_free)
+			data_send_kick_message(data);
+	}
+	if (pool->num_posted_bufs == 0) {
+		struct vnic *vnic = data->parent->vnic;
+
+		if (vnic->current_path == &vnic->primary_path)
+			DATA_ERROR("%s: primary path: "
+					"unable to allocate receive buffers\n",
+					vnic->config->name);
+		else if (vnic->current_path == &vnic->secondary_path)
+			DATA_ERROR("%s: secondary path: "
+					"unable to allocate receive buffers\n",
+					vnic->config->name);
+		data->ib_conn.state = IB_CONN_ERRORED;
+		viport_failure(data->parent);
+	}
+}
+
+void data_connected(struct data *data)
+{
+	DATA_FUNCTION("data_connected()\n");
+	data->free_bufs_io.io.swr.wr.rdma.rkey =
+				data->recv_pool.eioc_rdma_rkey;
+	data_alloc_buffers(data, 1);
+	data_send_free_recv_buffers(data);
+	data->connected = 1;
+}
+
+void data_disconnect(struct data *data)
+{
+	struct xmit_pool *xmit_pool = &data->xmit_pool;
+	struct recv_pool *recv_pool = &data->recv_pool;
+	unsigned int i;
+
+	DATA_FUNCTION("data_disconnect()\n");
+
+	data->connected = 0;
+	if (data->kick_timer_on) {
+		del_timer_sync(&data->kick_timer);
+		data->kick_timer_on = 0;
+	}
+
+	if (ib_send_cm_dreq(data->ib_conn.cm_id, NULL, 0))
+		DATA_ERROR("data CM DREQ sending failed\n");
+	data->ib_conn.state = IB_CONN_DISCONNECTED;
+
+	completion_callback_cleanup(&data->ib_conn);
+
+	for (i = 0; i < xmit_pool->num_xmit_bufs; i++) {
+		if (xmit_pool->xmit_bufs[i].skb)
+			dev_kfree_skb(xmit_pool->xmit_bufs[i].skb);
+		xmit_pool->xmit_bufs[i].skb = NULL;
+
+	}
+	for (i = 0; i < recv_pool->pool_sz; i++) {
+		if (data->recv_pool.recv_bufs[i].skb)
+			dev_kfree_skb(recv_pool->recv_bufs[i].skb);
+		recv_pool->recv_bufs[i].skb = NULL;
+	}
+	vfree(data->local_storage);
+	if (data->region_data) {
+		ib_dma_unmap_single(data->parent->config->ibdev,
+				    data->region_data_dma, 4,
+				    DMA_BIDIRECTIONAL);
+		kfree(data->region_data);
+	}
+
+	if (recv_pool->buf_pool) {
+		ib_dma_unmap_single(data->parent->config->ibdev,
+				    recv_pool->buf_pool_dma,
+				    recv_pool->buf_pool_len, DMA_TO_DEVICE);
+		kfree(recv_pool->buf_pool);
+	}
+
+	if (xmit_pool->buf_pool) {
+		ib_dma_unmap_single(data->parent->config->ibdev,
+				    xmit_pool->buf_pool_dma,
+				    xmit_pool->buf_pool_len, DMA_FROM_DEVICE);
+		kfree(xmit_pool->buf_pool);
+	}
+
+	if (xmit_pool->xmit_data) {
+		ib_dma_unmap_single(data->parent->config->ibdev,
+				    xmit_pool->xmitdata_dma,
+				    xmit_pool->xmitdata_len, DMA_TO_DEVICE);
+		kfree(xmit_pool->xmit_data);
+	}
+}
+
+void data_cleanup(struct data *data)
+{
+	ib_destroy_cm_id(data->ib_conn.cm_id);
+
+	/* Completion callback cleanup called again.
+	 * This is to cleanup the threads in case there is an
+	 * error before state LINK_DATACONNECT due to which
+	 * data_disconnect is not called.
+	 */
+	completion_callback_cleanup(&data->ib_conn);
+	ib_destroy_qp(data->ib_conn.qp);
+	ib_destroy_cq(data->ib_conn.cq);
+	ib_dereg_mr(data->mr);
+
+}
+
+static int data_alloc_xmit_buffer(struct data *data, struct sk_buff *skb,
+				  struct buff_pool_entry **pp_bpe,
+				  struct rdma_io **pp_rdma_io,
+				  int *last)
+{
+	struct xmit_pool	*pool = &data->xmit_pool;
+	unsigned long		flags;
+	int			ret;
+
+	DATA_FUNCTION("data_alloc_xmit_buffer()\n");
+
+	spin_lock_irqsave(&data->xmit_buf_lock, flags);
+	ib_dma_sync_single_for_cpu(data->parent->config->ibdev,
+				   pool->buf_pool_dma, pool->buf_pool_len,
+				   DMA_TO_DEVICE);
+	*last = 0;
+	*pp_rdma_io = &pool->xmit_bufs[pool->next_xmit_buf];
+	*pp_bpe = &pool->buf_pool[pool->next_xmit_pool];
+
+	if ((*pp_bpe)->valid && pool->next_xmit_buf !=
+	     pool->last_comp_buf) {
+		INC(pool->next_xmit_buf, 1, pool->num_xmit_bufs);
+		INC(pool->next_xmit_pool, 1, pool->pool_sz);
+		if (!pool->buf_pool[pool->next_xmit_pool].valid) {
+			DATA_INFO("just used the last EIOU"
+				  " receive buffer\n");
+			*last = 1;
+			pool->need_buffers = 1;
+			vnic_stop_xmit(data->parent->vnic,
+				       data->parent->parent);
+			data_kickreq_stats(data);
+		} else if (pool->next_xmit_buf == pool->last_comp_buf) {
+			DATA_INFO("just used our last xmit buffer\n");
+			pool->need_buffers = 1;
+			vnic_stop_xmit(data->parent->vnic,
+				       data->parent->parent);
+		}
+		(*pp_rdma_io)->skb = skb;
+		(*pp_bpe)->valid = 0;
+		ret = 0;
+	} else {
+		data_no_xmitbuf_stats(data);
+		DATA_ERROR("Out of xmit buffers\n");
+		vnic_stop_xmit(data->parent->vnic,
+			       data->parent->parent);
+		ret = -1;
+	}
+
+	ib_dma_sync_single_for_device(data->parent->config->ibdev,
+				      pool->buf_pool_dma,
+				      pool->buf_pool_len, DMA_TO_DEVICE);
+	spin_unlock_irqrestore(&data->xmit_buf_lock, flags);
+	return ret;
+}
+
+static void data_rdma_packet(struct data *data, struct buff_pool_entry *bpe,
+			     struct rdma_io *rdma_io)
+{
+	struct ib_send_wr	*swr;
+	struct sk_buff		*skb;
+	dma_addr_t		trailer_data_dma;
+	dma_addr_t		skb_data_dma;
+	struct xmit_pool	*xmit_pool = &data->xmit_pool;
+	struct viport		*viport = data->parent;
+	u8			*d;
+	int			len;
+	int			fill_len;
+
+	DATA_FUNCTION("data_rdma_packet()\n");
+	swr = &rdma_io->io.swr;
+	skb = rdma_io->skb;
+	len = ALIGN(rdma_io->len, VIPORT_TRAILER_ALIGNMENT);
+	fill_len = len - skb->len;
+
+	ib_dma_sync_single_for_cpu(data->parent->config->ibdev,
+				   xmit_pool->xmitdata_dma,
+				   xmit_pool->xmitdata_len, DMA_TO_DEVICE);
+
+	d = (u8 *) rdma_io->trailer - fill_len;
+	trailer_data_dma = rdma_io->trailer_dma - fill_len;
+	memset(d, 0, fill_len);
+
+	swr->sg_list[0].length = skb->len;
+	if (skb->len <= min_xmt_skb) {
+		memcpy(rdma_io->data, skb->data, skb->len);
+		swr->sg_list[0].lkey = data->mr->lkey;
+		swr->sg_list[0].addr = rdma_io->data_dma;
+		dev_kfree_skb_any(skb);
+		rdma_io->skb = NULL;
+	} else {
+		swr->sg_list[0].lkey = data->mr->lkey;
+
+		skb_data_dma = ib_dma_map_single(viport->config->ibdev,
+						skb->data, skb->len,
+						DMA_TO_DEVICE);
+
+		if (ib_dma_mapping_error(viport->config->ibdev, skb_data_dma)) {
+			DATA_ERROR("skb data dma map error\n");
+			goto failure;
+		}
+
+		rdma_io->skb_data_dma = skb_data_dma;
+
+		swr->sg_list[0].addr = skb_data_dma;
+		skb_orphan(skb);
+	}
+	ib_dma_sync_single_for_cpu(data->parent->config->ibdev,
+				   xmit_pool->buf_pool_dma,
+				   xmit_pool->buf_pool_len, DMA_TO_DEVICE);
+
+	swr->sg_list[1].addr = trailer_data_dma;
+	swr->sg_list[1].length = fill_len + sizeof(struct viport_trailer);
+	swr->sg_list[0].lkey = data->mr->lkey;
+	swr->wr.rdma.remote_addr = be64_to_cpu(bpe->remote_addr);
+	swr->wr.rdma.remote_addr += data->xmit_pool.buffer_sz;
+	swr->wr.rdma.remote_addr -= (sizeof(struct viport_trailer) + len);
+	swr->wr.rdma.rkey = be32_to_cpu(bpe->rkey);
+
+	ib_dma_sync_single_for_device(data->parent->config->ibdev,
+				      xmit_pool->buf_pool_dma,
+				      xmit_pool->buf_pool_len, DMA_TO_DEVICE);
+
+	/* If VNIC_FEAT_RDMA_IMMED is supported then change the work request
+	 * opcode to IB_WR_RDMA_WRITE_WITH_IMM
+	 */
+
+	if (data->parent->features_supported & VNIC_FEAT_RDMA_IMMED) {
+		swr->ex.imm_data = 0;
+		swr->opcode = IB_WR_RDMA_WRITE_WITH_IMM;
+	}
+
+	data->xmit_pool.notify_count++;
+	if (data->xmit_pool.notify_count >= data->xmit_pool.notify_bundle) {
+		data->xmit_pool.notify_count = 0;
+		swr->send_flags = IB_SEND_SIGNALED;
+	} else {
+		swr->send_flags = 0;
+	}
+	ib_dma_sync_single_for_device(data->parent->config->ibdev,
+				      xmit_pool->xmitdata_dma,
+				      xmit_pool->xmitdata_len, DMA_TO_DEVICE);
+	if (vnic_ib_post_send(&data->ib_conn, &rdma_io->io)) {
+		DATA_ERROR("failed to post send for data RDMA write\n");
+		viport_failure(data->parent);
+		goto failure;
+	}
+
+	data_xmits_stats(data);
+failure:
+	ib_dma_sync_single_for_device(data->parent->config->ibdev,
+				      xmit_pool->xmitdata_dma,
+				      xmit_pool->xmitdata_len, DMA_TO_DEVICE);
+}
+
+static void data_kick_timeout_handler(unsigned long arg)
+{
+	struct data *data = (struct data *)arg;
+
+	DATA_FUNCTION("data_kick_timeout_handler()\n");
+	data->kick_timer_on = 0;
+	data_send_kick_message(data);
+}
+
+int data_xmit_packet(struct data *data, struct sk_buff *skb)
+{
+	struct xmit_pool	*pool = &data->xmit_pool;
+	struct rdma_io		*rdma_io;
+	struct buff_pool_entry	*bpe;
+	struct viport_trailer	*trailer;
+	unsigned int		sz = skb->len;
+	int			last;
+
+	DATA_FUNCTION("data_xmit_packet()\n");
+	if (sz > pool->buffer_sz) {
+		DATA_ERROR("outbound packet too large, size = %d\n", sz);
+		return -1;
+	}
+
+	if (data_alloc_xmit_buffer(data, skb, &bpe, &rdma_io, &last)) {
+		DATA_ERROR("error in allocating data xmit buffer\n");
+		return -1;
+	}
+
+	ib_dma_sync_single_for_cpu(data->parent->config->ibdev,
+				   pool->xmitdata_dma, pool->xmitdata_len,
+				   DMA_TO_DEVICE);
+	trailer = rdma_io->trailer;
+
+	memset(trailer, 0, sizeof *trailer);
+	memcpy(trailer->dest_mac_addr, skb->data, ETH_ALEN);
+
+	if (skb->sk)
+		trailer->connection_hash_and_valid = 0x40 |
+			 ((be16_to_cpu(inet_sk(skb->sk)->sport) +
+			   be16_to_cpu(inet_sk(skb->sk)->dport)) & 0x3f);
+
+	trailer->connection_hash_and_valid |= CHV_VALID;
+
+	if ((sz > 16) && (*(__be16 *) (skb->data + 12) ==
+			   __constant_cpu_to_be16(ETH_P_8021Q))) {
+		trailer->vlan = *(__be16 *) (skb->data + 14);
+		memmove(skb->data + 4, skb->data, 12);
+		skb_pull(skb, 4);
+		sz -= 4;
+		trailer->pkt_flags |= PF_VLAN_INSERT;
+	}
+	if (last)
+		trailer->pkt_flags |= PF_KICK;
+	if (sz < ETH_ZLEN) {
+		/* EIOU requires all packets to be
+		 * of ethernet minimum packet size.
+		 */
+		trailer->data_length = __constant_cpu_to_be16(ETH_ZLEN);
+		rdma_io->len = ETH_ZLEN;
+	} else {
+		trailer->data_length = cpu_to_be16(sz);
+		rdma_io->len = sz;
+	}
+
+	if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		trailer->tx_chksum_flags = TX_CHKSUM_FLAGS_CHECKSUM_V4
+		    | TX_CHKSUM_FLAGS_IP_CHECKSUM
+		    | TX_CHKSUM_FLAGS_TCP_CHECKSUM
+		    | TX_CHKSUM_FLAGS_UDP_CHECKSUM;
+	}
+
+	ib_dma_sync_single_for_device(data->parent->config->ibdev,
+				      pool->xmitdata_dma, pool->xmitdata_len,
+				      DMA_TO_DEVICE);
+	data_rdma_packet(data, bpe, rdma_io);
+
+	if (pool->send_kicks) {
+		/* EIOC needs kicks to inform it of sent packets */
+		pool->kick_count++;
+		pool->kick_byte_count += sz;
+		if ((pool->kick_count >= pool->kick_bundle)
+		    || (pool->kick_byte_count >= pool->kick_byte_bundle)) {
+			data_send_kick_message(data);
+		} else if (pool->kick_count == 1) {
+			init_timer(&data->kick_timer);
+			/* timeout_before_kick is in usec */
+			data->kick_timer.expires =
+			   msecs_to_jiffies(be32_to_cpu(data->
+				eioc_pool_parms.timeout_before_kick) * 1000)
+				+ jiffies;
+			data->kick_timer.data = (unsigned long)data;
+			data->kick_timer.function = data_kick_timeout_handler;
+			add_timer(&data->kick_timer);
+			data->kick_timer_on = 1;
+		}
+	}
+	return 0;
+}
+
+static void data_check_xmit_buffers(struct data *data)
+{
+	struct xmit_pool *pool = &data->xmit_pool;
+	unsigned long flags;
+
+	DATA_FUNCTION("data_check_xmit_buffers()\n");
+	spin_lock_irqsave(&data->xmit_buf_lock, flags);
+	ib_dma_sync_single_for_cpu(data->parent->config->ibdev,
+				   pool->buf_pool_dma, pool->buf_pool_len,
+				   DMA_TO_DEVICE);
+
+	if (data->xmit_pool.need_buffers
+	    && pool->buf_pool[pool->next_xmit_pool].valid
+	    && pool->next_xmit_buf != pool->last_comp_buf) {
+		data->xmit_pool.need_buffers = 0;
+		vnic_restart_xmit(data->parent->vnic,
+				  data->parent->parent);
+		DATA_INFO("there are free xmit buffers\n");
+	}
+	ib_dma_sync_single_for_device(data->parent->config->ibdev,
+				      pool->buf_pool_dma, pool->buf_pool_len,
+				      DMA_TO_DEVICE);
+
+	spin_unlock_irqrestore(&data->xmit_buf_lock, flags);
+}
+
+static struct sk_buff *data_recv_to_skbuff(struct data *data,
+					   struct rdma_dest *rdma_dest)
+{
+	struct viport_trailer *trailer;
+	struct sk_buff *skb = NULL;
+	int start;
+	unsigned int len;
+	u8 rx_chksum_flags;
+
+	DATA_FUNCTION("data_recv_to_skbuff()\n");
+	trailer = rdma_dest->trailer;
+	start = data_offset(data, trailer);
+	len = data_len(data, trailer);
+
+	if (len <= min_rcv_skb)
+		skb = dev_alloc_skb(len + VLAN_HLEN + 2);
+			 /* leave room for VLAN header and alignment */
+	if (skb) {
+		skb_reserve(skb, VLAN_HLEN + 2);
+		memcpy(skb->data, rdma_dest->data + start, len);
+		skb_put(skb, len);
+	} else {
+		skb = rdma_dest->skb;
+		rdma_dest->skb = NULL;
+		rdma_dest->trailer = NULL;
+		rdma_dest->data = NULL;
+		skb_pull(skb, start);
+		skb_trim(skb, len);
+	}
+
+	rx_chksum_flags = trailer->rx_chksum_flags;
+	DATA_INFO("rx_chksum_flags = %d, LOOP = %c, IP = %c,"
+	     " TCP = %c, UDP = %c\n",
+	     rx_chksum_flags,
+	     (rx_chksum_flags & RX_CHKSUM_FLAGS_LOOPBACK) ? 'Y' : 'N',
+	     (rx_chksum_flags & RX_CHKSUM_FLAGS_IP_CHECKSUM_SUCCEEDED) ? 'Y'
+	     : (rx_chksum_flags & RX_CHKSUM_FLAGS_IP_CHECKSUM_FAILED) ? 'N' :
+	     '-',
+	     (rx_chksum_flags & RX_CHKSUM_FLAGS_TCP_CHECKSUM_SUCCEEDED) ? 'Y'
+	     : (rx_chksum_flags & RX_CHKSUM_FLAGS_TCP_CHECKSUM_FAILED) ? 'N' :
+	     '-',
+	     (rx_chksum_flags & RX_CHKSUM_FLAGS_UDP_CHECKSUM_SUCCEEDED) ? 'Y'
+	     : (rx_chksum_flags & RX_CHKSUM_FLAGS_UDP_CHECKSUM_FAILED) ? 'N' :
+	     '-');
+
+	if ((rx_chksum_flags & RX_CHKSUM_FLAGS_LOOPBACK)
+	    || ((rx_chksum_flags & RX_CHKSUM_FLAGS_IP_CHECKSUM_SUCCEEDED)
+		&& ((rx_chksum_flags & RX_CHKSUM_FLAGS_TCP_CHECKSUM_SUCCEEDED)
+		    || (rx_chksum_flags &
+			RX_CHKSUM_FLAGS_UDP_CHECKSUM_SUCCEEDED))))
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	else
+		skb->ip_summed = CHECKSUM_NONE;
+
+	if ((trailer->pkt_flags & PF_VLAN_INSERT) &&
+		!(data->parent->features_supported & VNIC_FEAT_IGNORE_VLAN)) {
+		u8 *rv;
+
+		rv = skb_push(skb, 4);
+		memmove(rv, rv + 4, 12);
+		*(__be16 *) (rv + 12) = __constant_cpu_to_be16(ETH_P_8021Q);
+		if (trailer->pkt_flags & PF_PVID_OVERRIDDEN)
+			*(__be16 *) (rv + 14) = trailer->vlan &
+					__constant_cpu_to_be16(0xF000);
+		else
+			*(__be16 *) (rv + 14) = trailer->vlan;
+	}
+
+	return skb;
+}
+
+static int data_incoming_recv(struct data *data)
+{
+	struct recv_pool *pool = &data->recv_pool;
+	struct rdma_dest *rdma_dest;
+	struct viport_trailer *trailer;
+	struct buff_pool_entry *bpe;
+	struct sk_buff *skb;
+	dma_addr_t vaddr_dma;
+
+	DATA_FUNCTION("data_incoming_recv()\n");
+	if (pool->next_full_buf == pool->next_free_buf)
+		return -1;
+	bpe = &pool->buf_pool[pool->next_full_buf];
+	vaddr_dma = be64_to_cpu(bpe->remote_addr);
+	rdma_dest = &pool->recv_bufs[bpe->valid - 1];
+	trailer = rdma_dest->trailer;
+
+	if (!trailer
+	    || !(trailer->connection_hash_and_valid & CHV_VALID))
+		return -1;
+
+	/* received a packet */
+	if (trailer->pkt_flags & PF_KICK)
+		pool->kick_on_free = 1;
+
+	skb = data_recv_to_skbuff(data, rdma_dest);
+
+	if (skb) {
+		vnic_recv_packet(data->parent->vnic,
+				 data->parent->parent, skb);
+		list_add(&rdma_dest->list_ptrs, &pool->avail_recv_bufs);
+	}
+
+	ib_dma_unmap_single(data->parent->config->ibdev,
+			    vaddr_dma, pool->buffer_sz,
+			    DMA_FROM_DEVICE);
+	ib_dma_sync_single_for_cpu(data->parent->config->ibdev,
+				   pool->buf_pool_dma, pool->buf_pool_len,
+				   DMA_TO_DEVICE);
+
+	bpe->valid = 0;
+	ib_dma_sync_single_for_device(data->parent->config->ibdev,
+					pool->buf_pool_dma, pool->buf_pool_len,
+					DMA_TO_DEVICE);
+
+	INC(pool->next_full_buf, 1, pool->eioc_pool_sz);
+	pool->num_posted_bufs--;
+	data_recvs_stats(data);
+	return 0;
+}
+
+static void data_received_kick(struct io *io)
+{
+	struct data *data = &io->viport->data;
+	unsigned long flags;
+
+	DATA_FUNCTION("data_received_kick()\n");
+	data_note_kickrcv_time();
+	spin_lock_irqsave(&data->recv_ios_lock, flags);
+	list_add(&io->list_ptrs, &data->recv_ios);
+	spin_unlock_irqrestore(&data->recv_ios_lock, flags);
+	data_post_recvs(data);
+	data_rcvkicks_stats(data);
+	data_check_xmit_buffers(data);
+
+	while (!data_incoming_recv(data));
+
+	if (data->connected) {
+		data_alloc_buffers(data, 0);
+		data_send_free_recv_buffers(data);
+	}
+}
+
+static void data_xmit_complete(struct io *io)
+{
+	struct rdma_io *rdma_io = (struct rdma_io *)io;
+	struct data *data = &io->viport->data;
+	struct xmit_pool *pool = &data->xmit_pool;
+	struct sk_buff *skb;
+
+	DATA_FUNCTION("data_xmit_complete()\n");
+
+	if (rdma_io->skb)
+		ib_dma_unmap_single(data->parent->config->ibdev,
+				    rdma_io->skb_data_dma, rdma_io->skb->len,
+				    DMA_TO_DEVICE);
+
+	while (pool->last_comp_buf != rdma_io->index) {
+		INC(pool->last_comp_buf, 1, pool->num_xmit_bufs);
+		skb = pool->xmit_bufs[pool->last_comp_buf].skb;
+		if (skb)
+			dev_kfree_skb_any(skb);
+		pool->xmit_bufs[pool->last_comp_buf].skb = NULL;
+	}
+
+	data_check_xmit_buffers(data);
+}
+
+static int mc_data_alloc_skb(struct ud_recv_io *recv_io, u32 len,
+				int initial_allocation)
+{
+	struct sk_buff *skb;
+	struct mc_data *mc_data = &recv_io->io.viport->mc_data;
+
+	DATA_FUNCTION("mc_data_alloc_skb\n");
+	if (initial_allocation)
+		skb = alloc_skb(len, GFP_KERNEL);
+	else
+		skb = alloc_skb(len, GFP_ATOMIC);
+	if (!skb) {
+		DATA_ERROR("failed to alloc MULTICAST skb\n");
+		return -1;
+	}
+	skb_put(skb, len);
+	recv_io->skb = skb;
+
+	recv_io->skb_data_dma = ib_dma_map_single(
+					recv_io->io.viport->config->ibdev,
+					skb->data, skb->len,
+					DMA_FROM_DEVICE);
+
+	if (ib_dma_mapping_error(recv_io->io.viport->config->ibdev,
+			recv_io->skb_data_dma)) {
+		DATA_ERROR("skb data dma map error\n");
+		dev_kfree_skb(skb);
+		return -1;
+	}
+
+	recv_io->list[0].addr = recv_io->skb_data_dma;
+	recv_io->list[0].length = sizeof(struct ib_grh);
+	recv_io->list[0].lkey = mc_data->mr->lkey;
+
+	recv_io->list[1].addr = recv_io->skb_data_dma + sizeof(struct ib_grh);
+	recv_io->list[1].length = len - sizeof(struct ib_grh);
+	recv_io->list[1].lkey = mc_data->mr->lkey;
+
+	recv_io->io.rwr.wr_id = (u64)&recv_io->io;
+	recv_io->io.rwr.sg_list = recv_io->list;
+	recv_io->io.rwr.num_sge = 2;
+	recv_io->io.rwr.next = NULL;
+
+	return 0;
+}
+
+static int mc_data_alloc_buffers(struct mc_data *mc_data)
+{
+	unsigned int i, num;
+	struct ud_recv_io *bufs = NULL, *recv_io;
+
+	DATA_FUNCTION("mc_data_alloc_buffers\n");
+	if (!mc_data->skb_len) {
+		unsigned int len;
+		/* align multicast msg buffer on viport_trailer boundary */
+		len = (MCAST_MSG_SIZE + VIPORT_TRAILER_ALIGNMENT - 1) &
+				(~((unsigned int)VIPORT_TRAILER_ALIGNMENT - 1));
+		/*
+		 * Add size of grh and trailer -
+		 * note, we don't need a + 4 for vlan because we have room in
+		 * netbuf for grh & trailer and we'll strip them both, so there
+		 * will be room enough to handle the 4 byte insertion for vlan.
+		 */
+		len +=	sizeof(struct ib_grh) +
+				sizeof(struct viport_trailer);
+		mc_data->skb_len = len;
+		DATA_INFO("mc_data->skb_len %d (sizes:%d %d)\n",
+					len, (int)sizeof(struct ib_grh),
+					(int)sizeof(struct viport_trailer));
+	}
+	mc_data->recv_len = sizeof(struct ud_recv_io) * mc_data->num_recvs;
+	bufs = kmalloc(mc_data->recv_len, GFP_KERNEL);
+	if (!bufs) {
+		DATA_ERROR("failed to allocate MULTICAST buffers size:%d\n",
+				mc_data->recv_len);
+		return -1;
+	}
+	DATA_INFO("allocated num_recvs:%d recv_len:%d \n",
+			mc_data->num_recvs, mc_data->recv_len);
+	for (num = 0; num < mc_data->num_recvs; num++) {
+		recv_io = &bufs[num];
+		recv_io->len = mc_data->skb_len;
+		recv_io->io.type = RECV_UD;
+		recv_io->io.viport = mc_data->parent;
+		recv_io->io.routine = mc_data_recv_routine;
+
+		if (mc_data_alloc_skb(recv_io, mc_data->skb_len, 1)) {
+			for (i = 0; i < num; i++) {
+				recv_io = &bufs[i];
+				ib_dma_unmap_single(recv_io->io.viport->config->ibdev,
+						    recv_io->skb_data_dma,
+						    recv_io->skb->len,
+						    DMA_FROM_DEVICE);
+				dev_kfree_skb(recv_io->skb);
+			}
+			kfree(bufs);
+			return -1;
+		}
+		list_add_tail(&recv_io->io.list_ptrs,
+						 &mc_data->avail_recv_ios_list);
+	}
+	mc_data->recv_ios = bufs;
+	return 0;
+}
+
+void mc_data_cleanup(struct mc_data *mc_data)
+{
+	DATA_FUNCTION("mc_data_cleanup\n");
+	completion_callback_cleanup(&mc_data->ib_conn);
+	if (!IS_ERR(mc_data->ib_conn.qp)) {
+		ib_destroy_qp(mc_data->ib_conn.qp);
+		mc_data->ib_conn.qp = (struct ib_qp *)ERR_PTR(-EINVAL);
+	}
+	if (!IS_ERR(mc_data->ib_conn.cq)) {
+		ib_destroy_cq(mc_data->ib_conn.cq);
+		mc_data->ib_conn.cq = (struct ib_cq *)ERR_PTR(-EINVAL);
+	}
+	kfree(mc_data->recv_ios);
+	mc_data->recv_ios = (struct ud_recv_io *)NULL;
+	if (mc_data->mr) {
+		ib_dereg_mr(mc_data->mr);
+		mc_data->mr = (struct ib_mr *)NULL;
+	}
+	DATA_FUNCTION("mc_data_cleanup done\n");
+
+}
+
+int mc_data_init(struct mc_data *mc_data, struct viport *viport,
+	      struct data_config *config, struct ib_pd *pd)
+{
+	DATA_FUNCTION("mc_data_init()\n");
+
+	mc_data->num_recvs = viport->data.config->num_recvs;
+
+	INIT_LIST_HEAD(&mc_data->avail_recv_ios_list);
+	spin_lock_init(&mc_data->recv_lock);
+
+	mc_data->parent = viport;
+	mc_data->config = config;
+
+	mc_data->ib_conn.cm_id = NULL;
+	mc_data->ib_conn.viport = viport;
+	mc_data->ib_conn.ib_config = &config->ib_config;
+	mc_data->ib_conn.state = IB_CONN_UNINITTED;
+	mc_data->ib_conn.callback_thread = NULL;
+	mc_data->ib_conn.callback_thread_end = 0;
+
+	if (vnic_ib_mc_init(mc_data, viport, pd,
+			      &config->ib_config)) {
+		DATA_ERROR("vnic_ib_mc_init failed\n");
+		goto failure;
+	}
+	mc_data->mr = ib_get_dma_mr(pd,
+				 IB_ACCESS_LOCAL_WRITE |
+				 IB_ACCESS_REMOTE_WRITE);
+	if (IS_ERR(mc_data->mr)) {
+		DATA_ERROR("failed to register memory for"
+			   " mc_data connection\n");
+		goto destroy_conn;
+	}
+
+	if (mc_data_alloc_buffers(mc_data))
+		goto dereg_mr;
+
+	mc_data_post_recvs(mc_data);
+	if (vnic_ib_mc_mod_qp_to_rts(mc_data->ib_conn.qp))
+		goto dereg_mr;
+
+	return 0;
+
+dereg_mr:
+	ib_dereg_mr(mc_data->mr);
+	mc_data->mr = (struct ib_mr *)NULL;
+destroy_conn:
+	completion_callback_cleanup(&mc_data->ib_conn);
+	ib_destroy_qp(mc_data->ib_conn.qp);
+	mc_data->ib_conn.qp = (struct ib_qp *)ERR_PTR(-EINVAL);
+	ib_destroy_cq(mc_data->ib_conn.cq);
+	mc_data->ib_conn.cq = (struct ib_cq *)ERR_PTR(-EINVAL);
+failure:
+	return -1;
+}
+
+static void mc_data_post_recvs(struct mc_data *mc_data)
+{
+	unsigned long flags;
+	int i = 0;
+	DATA_FUNCTION("mc_data_post_recvs\n");
+	spin_lock_irqsave(&mc_data->recv_lock, flags);
+	while (!list_empty(&mc_data->avail_recv_ios_list)) {
+		struct io *io = list_entry(mc_data->avail_recv_ios_list.next,
+				struct io, list_ptrs);
+		struct ud_recv_io *recv_io =
+					container_of(io, struct ud_recv_io, io);
+		list_del(&recv_io->io.list_ptrs);
+		spin_unlock_irqrestore(&mc_data->recv_lock, flags);
+		if (vnic_ib_mc_post_recv(mc_data, &recv_io->io)) {
+			viport_failure(mc_data->parent);
+			return;
+		}
+		spin_lock_irqsave(&mc_data->recv_lock, flags);
+		i++;
+	}
+	DATA_INFO("mcdata posted %d %p\n", i, &mc_data->avail_recv_ios_list);
+	spin_unlock_irqrestore(&mc_data->recv_lock, flags);
+}
+
+static void mc_data_recv_routine(struct io *io)
+{
+	struct sk_buff *skb;
+	struct ib_grh *grh;
+	struct viport_trailer *trailer;
+	struct mc_data *mc_data;
+	unsigned long flags;
+	struct ud_recv_io *recv_io = container_of(io, struct ud_recv_io, io);
+	union ib_gid_cpu sgid;
+
+	DATA_FUNCTION("mc_data_recv_routine\n");
+	skb = recv_io->skb;
+	grh = (struct ib_grh *)skb->data;
+	mc_data = &recv_io->io.viport->mc_data;
+
+	ib_dma_unmap_single(recv_io->io.viport->config->ibdev,
+			    recv_io->skb_data_dma, recv_io->skb->len,
+			    DMA_FROM_DEVICE);
+
+	/* first - check if we've got our own mc packet  */
+	/* convert sgid from host to cpu form before comparing */
+	bswap_ib_gid(&grh->sgid, &sgid);
+	if (cpu_to_be64(sgid.global.interface_id) ==
+		io->viport->config->path_info.path.sgid.global.interface_id) {
+		DATA_ERROR("dropping - our mc packet\n");
+		dev_kfree_skb(skb);
+	} else {
+		/* GRH is at head and trailer at end. Remove GRH from head.  */
+		trailer = (struct viport_trailer *)
+				(skb->data + recv_io->len -
+				 sizeof(struct viport_trailer));
+		skb_pull(skb, sizeof(struct ib_grh));
+		if (trailer->connection_hash_and_valid & CHV_VALID) {
+			mc_data_recv_to_skbuff(io->viport, skb, trailer);
+			vnic_recv_packet(io->viport->vnic, io->viport->parent,
+					skb);
+			vnic_multicast_recv_pkt_stats(io->viport->vnic);
+		} else {
+			DATA_ERROR("dropping - no CHV_VALID in HashAndValid\n");
+			dev_kfree_skb(skb);
+		}
+	}
+	recv_io->skb = NULL;
+	if (mc_data_alloc_skb(recv_io, mc_data->skb_len, 0))
+		return;
+
+	spin_lock_irqsave(&mc_data->recv_lock, flags);
+	list_add_tail(&recv_io->io.list_ptrs, &mc_data->avail_recv_ios_list);
+	spin_unlock_irqrestore(&mc_data->recv_lock, flags);
+	mc_data_post_recvs(mc_data);
+	return;
+}
+
+static void mc_data_recv_to_skbuff(struct viport *viport, struct sk_buff *skb,
+				   struct viport_trailer *trailer)
+{
+	u8 rx_chksum_flags = trailer->rx_chksum_flags;
+
+	/* drop alignment bytes at start */
+	skb_pull(skb, trailer->data_alignment_offset);
+	/* drop excess from end */
+	skb_trim(skb, __be16_to_cpu(trailer->data_length));
+
+	if ((rx_chksum_flags & RX_CHKSUM_FLAGS_LOOPBACK)
+	    || ((rx_chksum_flags & RX_CHKSUM_FLAGS_IP_CHECKSUM_SUCCEEDED)
+		&& ((rx_chksum_flags & RX_CHKSUM_FLAGS_TCP_CHECKSUM_SUCCEEDED)
+		    || (rx_chksum_flags &
+			RX_CHKSUM_FLAGS_UDP_CHECKSUM_SUCCEEDED))))
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	else
+		skb->ip_summed = CHECKSUM_NONE;
+
+	if ((trailer->pkt_flags & PF_VLAN_INSERT) &&
+	    !(viport->features_supported & VNIC_FEAT_IGNORE_VLAN)) {
+		u8 *rv;
+
+		/* insert VLAN id between source & length */
+		DATA_INFO("VLAN adjustment\n");
+		rv = skb_push(skb, 4);
+		memmove(rv, rv + 4, 12);
+		*(__be16 *) (rv + 12) = __constant_cpu_to_be16(ETH_P_8021Q);
+		if (trailer->pkt_flags & PF_PVID_OVERRIDDEN)
+		/*
+		 *  Indicates VLAN is 0 but we keep the protocol id.
+		 */
+			*(__be16 *) (rv + 14) = trailer->vlan &
+					__constant_cpu_to_be16(0xF000);
+		else
+			*(__be16 *) (rv + 14) = trailer->vlan;
+		DATA_INFO("vlan:%x\n", *(int *)(rv+14));
+	}
+
+    return;
+}
diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_data.h b/drivers/infiniband/ulp/qlgc_vnic/vnic_data.h
new file mode 100644
index 0000000..ad77aa9
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_data.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2006 QLogic, 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 VNIC_DATA_H_INCLUDED
+#define VNIC_DATA_H_INCLUDED
+
+#include <linux/if_vlan.h>
+
+#ifdef CONFIG_INFINIBAND_QLGC_VNIC_STATS
+#include <linux/timex.h>
+#endif	/* CONFIG_INFINIBAND_QLGC_VNIC_STATS */
+
+#include "vnic_ib.h"
+#include "vnic_control_pkt.h"
+#include "vnic_trailer.h"
+
+struct rdma_dest {
+	struct list_head	list_ptrs;
+	struct sk_buff		*skb;
+	u8			*data;
+	struct viport_trailer	*trailer __attribute__((aligned(32)));
+};
+
+struct buff_pool_entry {
+	__be64	remote_addr;
+	__be32	rkey;
+	u32	valid;
+};
+
+struct recv_pool {
+	u32			buffer_sz;
+	u32			pool_sz;
+	u32			eioc_pool_sz;
+	u32	 		eioc_rdma_rkey;
+	u64 			eioc_rdma_addr;
+	u32 			next_full_buf;
+	u32 			next_free_buf;
+	u32 			num_free_bufs;
+	u32 			num_posted_bufs;
+	u32 			sz_free_bundle;
+	int			kick_on_free;
+	struct buff_pool_entry	*buf_pool;
+	dma_addr_t		buf_pool_dma;
+	int			buf_pool_len;
+	struct rdma_dest	*recv_bufs;
+	struct list_head	avail_recv_bufs;
+};
+
+struct xmit_pool {
+	u32			buffer_sz;
+	u32 			pool_sz;
+	u32 			notify_count;
+	u32 			notify_bundle;
+	u32 			next_xmit_buf;
+	u32 			last_comp_buf;
+	u32 			num_xmit_bufs;
+	u32 			next_xmit_pool;
+	u32 			kick_count;
+	u32 			kick_byte_count;
+	u32 			kick_bundle;
+	u32 			kick_byte_bundle;
+	int			need_buffers;
+	int			send_kicks;
+	uint32_t 		rdma_rkey;
+	u64 			rdma_addr;
+	struct buff_pool_entry	*buf_pool;
+	dma_addr_t		buf_pool_dma;
+	int			buf_pool_len;
+	struct rdma_io		*xmit_bufs;
+	u8			*xmit_data;
+	dma_addr_t		xmitdata_dma;
+	int			xmitdata_len;
+};
+
+struct data {
+	struct viport			*parent;
+	struct data_config		*config;
+	struct ib_mr			*mr;
+	struct vnic_ib_conn		ib_conn;
+	u8				*local_storage;
+	struct vnic_recv_pool_config	host_pool_parms;
+	struct vnic_recv_pool_config	eioc_pool_parms;
+	struct recv_pool		recv_pool;
+	struct xmit_pool		xmit_pool;
+	u8				*region_data;
+	dma_addr_t			region_data_dma;
+	struct rdma_io			free_bufs_io;
+	struct send_io			kick_io;
+	struct list_head		recv_ios;
+	spinlock_t			recv_ios_lock;
+	spinlock_t			xmit_buf_lock;
+	int				kick_timer_on;
+	int				connected;
+	u16				max_mtu;
+	struct timer_list		kick_timer;
+	struct completion		done;
+#ifdef CONFIG_INFINIBAND_QLGC_VNIC_STATS
+	struct {
+		u32		xmit_num;
+		u32		recv_num;
+		u32		free_buf_sends;
+		u32		free_buf_num;
+		u32		free_buf_min;
+		u32		kick_recvs;
+		u32		kick_reqs;
+		u32		no_xmit_bufs;
+		cycles_t	no_xmit_buf_time;
+	} statistics;
+#endif	/* CONFIG_INFINIBAND_QLGC_VNIC_STATS */
+};
+
+struct mc_data {
+    struct viport           *parent;
+    struct data_config      *config;
+    struct ib_mr            *mr;
+    struct vnic_ib_conn     ib_conn;
+
+    u32                     num_recvs;
+    u32                     skb_len;
+    spinlock_t              recv_lock;
+    int                     recv_len;
+    struct ud_recv_io      *recv_ios;
+    struct list_head        avail_recv_ios_list;
+};
+
+int data_init(struct data *data, struct viport *viport,
+	      struct data_config *config, struct ib_pd *pd);
+
+int  data_connect(struct data *data);
+void data_connected(struct data *data);
+void data_disconnect(struct data *data);
+
+int data_xmit_packet(struct data *data, struct sk_buff *skb);
+
+void data_cleanup(struct data *data);
+
+#define data_is_connected(data)		\
+	(vnic_ib_conn_connected(&((data)->ib_conn)))
+#define data_path_id(data)		(data)->config->path_id
+#define data_eioc_pool(data)		&(data)->eioc_pool_parms
+#define data_host_pool(data)		&(data)->host_pool_parms
+#define data_eioc_pool_min(data)	&(data)->config->eioc_min
+#define data_host_pool_min(data)	&(data)->config->host_min
+#define data_eioc_pool_max(data)	&(data)->config->eioc_max
+#define data_host_pool_max(data)	&(data)->config->host_max
+#define data_local_pool_addr(data)	(data)->xmit_pool.rdma_addr
+#define data_local_pool_rkey(data)	(data)->xmit_pool.rdma_rkey
+#define data_remote_pool_addr(data)	&(data)->recv_pool.eioc_rdma_addr
+#define data_remote_pool_rkey(data)	&(data)->recv_pool.eioc_rdma_rkey
+
+#define data_max_mtu(data)		(data)->max_mtu
+
+
+#define data_len(data, trailer)		be16_to_cpu(trailer->data_length)
+#define data_offset(data, trailer)					\
+	((data)->recv_pool.buffer_sz - sizeof(struct viport_trailer)	\
+	- ALIGN(data_len((data), (trailer)), VIPORT_TRAILER_ALIGNMENT)	\
+	+ (trailer->data_alignment_offset))
+
+/* the following macros manipulate ring buffer indexes.
+ * the ring buffer size must be a power of 2.
+ */
+#define ADD(index, increment, size)	(((index) + (increment))&((size) - 1))
+#define NEXT(index, size)		ADD(index, 1, size)
+#define INC(index, increment, size)	(index) = ADD(index, increment, size)
+
+/* this is max multicast msg embedded will send */
+#define MCAST_MSG_SIZE \
+		(2048 - sizeof(struct ib_grh) - sizeof(struct viport_trailer))
+
+int mc_data_init(struct mc_data *mc_data, struct viport *viport,
+	struct data_config *config,
+	struct ib_pd *pd);
+
+void mc_data_cleanup(struct mc_data *mc_data);
+
+#endif	/* VNIC_DATA_H_INCLUDED */
diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_trailer.h b/drivers/infiniband/ulp/qlgc_vnic/vnic_trailer.h
new file mode 100644
index 0000000..dd8a073
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_trailer.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2006 QLogic, 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 VNIC_TRAILER_H_INCLUDED
+#define VNIC_TRAILER_H_INCLUDED
+
+/* pkt_flags values */
+enum {
+	PF_CHASH_VALID		= 0x01,
+	PF_IPSEC_VALID		= 0x02,
+	PF_TCP_SEGMENT		= 0x04,
+	PF_KICK			= 0x08,
+	PF_VLAN_INSERT		= 0x10,
+	PF_PVID_OVERRIDDEN 	= 0x20,
+	PF_FCS_INCLUDED 	= 0x40,
+	PF_FORCE_ROUTE		= 0x80
+};
+
+/* tx_chksum_flags values */
+enum {
+	TX_CHKSUM_FLAGS_CHECKSUM_V4	= 0x01,
+	TX_CHKSUM_FLAGS_CHECKSUM_V6	= 0x02,
+	TX_CHKSUM_FLAGS_TCP_CHECKSUM	= 0x04,
+	TX_CHKSUM_FLAGS_UDP_CHECKSUM	= 0x08,
+	TX_CHKSUM_FLAGS_IP_CHECKSUM	= 0x10
+};
+
+/* rx_chksum_flags values */
+enum {
+	RX_CHKSUM_FLAGS_TCP_CHECKSUM_FAILED	= 0x01,
+	RX_CHKSUM_FLAGS_UDP_CHECKSUM_FAILED	= 0x02,
+	RX_CHKSUM_FLAGS_IP_CHECKSUM_FAILED	= 0x04,
+	RX_CHKSUM_FLAGS_TCP_CHECKSUM_SUCCEEDED	= 0x08,
+	RX_CHKSUM_FLAGS_UDP_CHECKSUM_SUCCEEDED	= 0x10,
+	RX_CHKSUM_FLAGS_IP_CHECKSUM_SUCCEEDED	= 0x20,
+	RX_CHKSUM_FLAGS_LOOPBACK		= 0x40,
+	RX_CHKSUM_FLAGS_RESERVED		= 0x80
+};
+
+/* connection_hash_and_valid values */
+enum {
+	CHV_VALID	= 0x80,
+	CHV_HASH_MASH	= 0x7f
+};
+
+struct viport_trailer {
+	s8	data_alignment_offset;
+	u8	rndis_header_length;	/* reserved for use by edp */
+	__be16	data_length;
+	u8	pkt_flags;
+	u8	tx_chksum_flags;
+	u8	rx_chksum_flags;
+	u8	ip_sec_flags;
+	u32	tcp_seq_no;
+	u32	ip_sec_offload_handle;
+	u32	ip_sec_next_offload_handle;
+	u8	dest_mac_addr[6];
+	__be16	vlan;
+	u16	time_stamp;
+	u8	origin;
+	u8	connection_hash_and_valid;
+};
+
+#define VIPORT_TRAILER_ALIGNMENT	32
+
+#define BUFFER_SIZE(len)					\
+	(sizeof(struct viport_trailer) +			\
+	 ALIGN((len), VIPORT_TRAILER_ALIGNMENT))
+
+#define MAX_PAYLOAD(len)					\
+	ALIGN_DOWN((len) - sizeof(struct viport_trailer),	\
+		   VIPORT_TRAILER_ALIGNMENT)
+
+#endif	/* VNIC_TRAILER_H_INCLUDED */

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [ofa-general] [PATCH 06/13] QLogic VNIC: IB core stack interaction
  2008-04-30 17:15 [ofa-general] [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Ramachandra K
                   ` (4 preceding siblings ...)
  2008-04-30 17:18 ` [ofa-general] [PATCH 05/13] QLogic VNIC: Implementation of Data " Ramachandra K
@ 2008-04-30 17:18 ` Ramachandra K
  2008-05-13 20:40   ` [ofa-general] " Roland Dreier
  2008-04-30 17:19 ` [ofa-general] [PATCH 07/13] QLogic VNIC: Handling configurable parameters of the driver Ramachandra K
                   ` (7 subsequent siblings)
  13 siblings, 1 reply; 32+ messages in thread
From: Ramachandra K @ 2008-04-30 17:18 UTC (permalink / raw)
  To: rdreier, general, netdev; +Cc: poornima.kamath, amar.mudrankit

From: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>

The patch implements the interaction of the QLogic VNIC driver with
the underlying core infiniband stack.

Signed-off-by: Poornima Kamath <poornima.kamath@qlogic.com>
Signed-off-by: Amar Mudrankit <amar.mudrankit@qlogic.com>
---

 drivers/infiniband/ulp/qlgc_vnic/vnic_ib.c | 1046 ++++++++++++++++++++++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_ib.h |  206 ++++++
 2 files changed, 1252 insertions(+), 0 deletions(-)
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_ib.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_ib.h

diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_ib.c b/drivers/infiniband/ulp/qlgc_vnic/vnic_ib.c
new file mode 100644
index 0000000..3bf6455
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_ib.c
@@ -0,0 +1,1046 @@
+/*
+ * Copyright (c) 2006 QLogic, 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/string.h>
+#include <linux/random.h>
+#include <linux/netdevice.h>
+#include <linux/list.h>
+#include <rdma/ib_cache.h>
+
+#include "vnic_util.h"
+#include "vnic_data.h"
+#include "vnic_config.h"
+#include "vnic_ib.h"
+#include "vnic_viport.h"
+#include "vnic_sys.h"
+#include "vnic_main.h"
+#include "vnic_stats.h"
+
+static int vnic_ib_inited;
+static void vnic_add_one(struct ib_device *device);
+static void vnic_remove_one(struct ib_device *device);
+static int vnic_defer_completion(void *ptr);
+
+static int vnic_ib_mc_init_qp(struct mc_data *mc_data,
+		struct vnic_ib_config *config,
+		struct ib_pd *pd,
+		struct viport_config *viport_config);
+
+static struct ib_client vnic_client = {
+	.name = "vnic",
+	.add = vnic_add_one,
+	.remove = vnic_remove_one
+};
+
+struct ib_sa_client vnic_sa_client;
+
+int vnic_ib_init(void)
+{
+	int ret = -1;
+
+	IB_FUNCTION("vnic_ib_init()\n");
+
+	/* class has to be registered before
+	 * calling ib_register_client() because, that call
+	 * will trigger vnic_add_port() which will register
+	 * class_device for the port with the parent class
+	 * as vnic_class
+	 */
+	ret = class_register(&vnic_class);
+	if (ret) {
+		printk(KERN_ERR PFX "couldn't register class"
+		       " infiniband_qlgc_vnic; error %d", ret);
+		goto out;
+	}
+
+	ib_sa_register_client(&vnic_sa_client);
+	ret = ib_register_client(&vnic_client);
+	if (ret) {
+		printk(KERN_ERR PFX "couldn't register IB client;"
+		       " error %d", ret);
+		goto err_ib_reg;
+	}
+
+	interface_dev.dev.class = &vnic_class;
+	interface_dev.dev.release = vnic_release_dev;
+	snprintf(interface_dev.dev.bus_id,
+		 BUS_ID_SIZE, "interfaces");
+	init_completion(&interface_dev.released);
+	ret = device_register(&interface_dev.dev);
+	if (ret) {
+		printk(KERN_ERR PFX "couldn't register class interfaces;"
+		       " error %d", ret);
+		goto err_class_dev;
+	}
+	ret = device_create_file(&interface_dev.dev,
+				       &dev_attr_delete_vnic);
+	if (ret) {
+		printk(KERN_ERR PFX "couldn't create class file"
+		       " 'delete_vnic'; error %d", ret);
+		goto err_class_file;
+	}
+
+	vnic_ib_inited = 1;
+
+	return ret;
+err_class_file:
+	device_unregister(&interface_dev.dev);
+err_class_dev:
+	ib_unregister_client(&vnic_client);
+err_ib_reg:
+	ib_sa_unregister_client(&vnic_sa_client);
+	class_unregister(&vnic_class);
+out:
+	return ret;
+}
+
+static struct vnic_ib_port *vnic_add_port(struct vnic_ib_device *device,
+					  u8 port_num)
+{
+	struct vnic_ib_port *port;
+
+	port = kzalloc(sizeof *port, GFP_KERNEL);
+	if (!port)
+		return NULL;
+
+	init_completion(&port->pdev_info.released);
+	port->dev = device;
+	port->port_num = port_num;
+
+	port->pdev_info.dev.class = &vnic_class;
+	port->pdev_info.dev.parent = NULL;
+	port->pdev_info.dev.release = vnic_release_dev;
+	snprintf(port->pdev_info.dev.bus_id, BUS_ID_SIZE,
+		 "vnic-%s-%d", device->dev->name, port_num);
+
+	if (device_register(&port->pdev_info.dev))
+		goto free_port;
+
+	if (device_create_file(&port->pdev_info.dev,
+				     &dev_attr_create_primary))
+		goto err_class;
+	if (device_create_file(&port->pdev_info.dev,
+				     &dev_attr_create_secondary))
+		goto err_class;
+
+	return port;
+err_class:
+	device_unregister(&port->pdev_info.dev);
+free_port:
+	kfree(port);
+
+	return NULL;
+}
+
+static void vnic_add_one(struct ib_device *device)
+{
+	struct vnic_ib_device *vnic_dev;
+	struct vnic_ib_port *port;
+	int s, e, p;
+
+	vnic_dev = kmalloc(sizeof *vnic_dev, GFP_KERNEL);
+	if (!vnic_dev)
+		return;
+
+	vnic_dev->dev = device;
+	INIT_LIST_HEAD(&vnic_dev->port_list);
+
+	if (device->node_type == RDMA_NODE_IB_SWITCH) {
+		s = 0;
+		e = 0;
+
+	} else {
+		s = 1;
+		e = device->phys_port_cnt;
+
+	}
+
+	for (p = s; p <= e; p++) {
+		port = vnic_add_port(vnic_dev, p);
+		if (port)
+			list_add_tail(&port->list, &vnic_dev->port_list);
+	}
+
+	ib_set_client_data(device, &vnic_client, vnic_dev);
+
+}
+
+static void vnic_remove_one(struct ib_device *device)
+{
+	struct vnic_ib_device *vnic_dev;
+	struct vnic_ib_port *port, *tmp_port;
+
+	vnic_dev = ib_get_client_data(device, &vnic_client);
+	list_for_each_entry_safe(port, tmp_port,
+				 &vnic_dev->port_list, list) {
+		device_unregister(&port->pdev_info.dev);
+		/*
+		 * wait for sysfs entries to go away, so that no new vnics
+		 * are created
+		 */
+		wait_for_completion(&port->pdev_info.released);
+		kfree(port);
+
+	}
+	kfree(vnic_dev);
+
+	/* TODO Only those vnic interfaces associated with
+	 * the HCA whose remove event is called should be freed
+	 * Currently all the vnic interfaces are freed
+	 */
+
+	while (!list_empty(&vnic_list)) {
+		struct vnic *vnic =
+		    list_entry(vnic_list.next, struct vnic, list_ptrs);
+		vnic_free(vnic);
+	}
+
+	vnic_npevent_cleanup();
+	viport_cleanup();
+
+}
+
+void vnic_ib_cleanup(void)
+{
+	IB_FUNCTION("vnic_ib_cleanup()\n");
+
+	if (!vnic_ib_inited)
+		return;
+
+	device_unregister(&interface_dev.dev);
+	wait_for_completion(&interface_dev.released);
+
+	ib_unregister_client(&vnic_client);
+	ib_sa_unregister_client(&vnic_sa_client);
+	class_unregister(&vnic_class);
+}
+
+static void vnic_path_rec_completion(int status,
+				     struct ib_sa_path_rec *pathrec,
+				     void *context)
+{
+	struct vnic_ib_path_info *p = context;
+	p->status = status;
+	if (!status)
+		p->path = *pathrec;
+
+	complete(&p->done);
+}
+
+int vnic_ib_get_path(struct netpath *netpath, struct vnic *vnic)
+{
+	struct viport_config *config = netpath->viport->config;
+	int ret = 0;
+
+	init_completion(&config->path_info.done);
+	IB_INFO("Using SA path rec get time out value of %d\n",
+	       config->sa_path_rec_get_timeout);
+	config->path_info.path_query_id =
+			 ib_sa_path_rec_get(&vnic_sa_client,
+					    config->ibdev,
+					    config->port,
+					    &config->path_info.path,
+					    IB_SA_PATH_REC_DGID      |
+					    IB_SA_PATH_REC_SGID      |
+					    IB_SA_PATH_REC_NUMB_PATH |
+					    IB_SA_PATH_REC_PKEY,
+					    config->sa_path_rec_get_timeout,
+					    GFP_KERNEL,
+					    vnic_path_rec_completion,
+					    &config->path_info,
+					    &config->path_info.path_query);
+
+	if (config->path_info.path_query_id < 0) {
+		IB_ERROR("SA path record query failed; error %d\n",
+			 config->path_info.path_query_id);
+		ret = config->path_info.path_query_id;
+		goto out;
+	}
+
+	wait_for_completion(&config->path_info.done);
+
+	if (config->path_info.status < 0) {
+		printk(KERN_WARNING PFX "connection not available to dgid "
+		       "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
+		       (int)be16_to_cpu(*(__be16 *) &config->path_info.path.
+					dgid.raw[0]),
+		       (int)be16_to_cpu(*(__be16 *) &config->path_info.path.
+					dgid.raw[2]),
+		       (int)be16_to_cpu(*(__be16 *) &config->path_info.path.
+					dgid.raw[4]),
+		       (int)be16_to_cpu(*(__be16 *) &config->path_info.path.
+					dgid.raw[6]),
+		       (int)be16_to_cpu(*(__be16 *) &config->path_info.path.
+					dgid.raw[8]),
+		       (int)be16_to_cpu(*(__be16 *) &config->path_info.path.
+					dgid.raw[10]),
+		       (int)be16_to_cpu(*(__be16 *) &config->path_info.path.
+					dgid.raw[12]),
+		       (int)be16_to_cpu(*(__be16 *) &config->path_info.path.
+					dgid.raw[14]));
+
+		if (config->path_info.status == -ETIMEDOUT)
+			printk(KERN_INFO " path query timed out\n");
+		else if (config->path_info.status == -EIO)
+			printk(KERN_INFO " path query sending error\n");
+		else
+			printk(KERN_INFO " error %d\n",
+			       config->path_info.status);
+
+		ret = config->path_info.status;
+	}
+out:
+	if (ret)
+		netpath_timer(netpath, vnic->config->no_path_timeout);
+
+	return ret;
+}
+
+static inline void vnic_ib_handle_completions(struct ib_wc *wc,
+					      struct vnic_ib_conn *ib_conn,
+					      u32 *comp_num,
+					      cycles_t *comp_time)
+{
+	struct io *io;
+
+	io = (struct io *)(wc->wr_id);
+	vnic_ib_comp_stats(ib_conn, comp_num);
+	if (wc->status) {
+		IB_INFO("completion error  wc.status %d"
+			 " wc.opcode %d vendor err 0x%x\n",
+			  wc->status, wc->opcode, wc->vendor_err);
+	} else if (io) {
+		vnic_ib_io_stats(io, ib_conn, *comp_time);
+		if (io->type == RECV_UD) {
+			struct ud_recv_io *recv_io =
+				container_of(io, struct ud_recv_io, io);
+			recv_io->len = wc->byte_len;
+		}
+		if (io->routine)
+			(*io->routine) (io);
+	}
+}
+
+static void ib_qp_event(struct ib_event *event, void *context)
+{
+	IB_ERROR("QP event %d\n", event->event);
+}
+
+static void vnic_ib_completion(struct ib_cq *cq, void *ptr)
+{
+	struct vnic_ib_conn *ib_conn = ptr;
+	unsigned long	 flags;
+	int compl_received;
+	struct ib_wc wc;
+	cycles_t  comp_time;
+	u32  comp_num = 0;
+
+	/* for multicast, cm_id is NULL, so skip that test */
+	if (ib_conn->cm_id &&
+	    (ib_conn->state != IB_CONN_CONNECTED))
+		return;
+
+	/* Check if completion processing is taking place in thread
+	 * If not then process completions in this handler,
+	 * else set compl_received if not set, to indicate that
+	 * there are more completions to process in thread.
+	 */
+
+	spin_lock_irqsave(&ib_conn->compl_received_lock, flags);
+	compl_received = ib_conn->compl_received;
+	spin_unlock_irqrestore(&ib_conn->compl_received_lock, flags);
+
+	if (ib_conn->in_thread || compl_received) {
+		if (!compl_received) {
+			spin_lock_irqsave(&ib_conn->compl_received_lock, flags);
+			ib_conn->compl_received = 1;
+			spin_unlock_irqrestore(&ib_conn->compl_received_lock,
+									flags);
+		}
+		wake_up(&(ib_conn->callback_wait_queue));
+	} else {
+		vnic_ib_note_comptime_stats(&comp_time);
+		vnic_ib_callback_stats(ib_conn);
+		ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
+		while (ib_poll_cq(cq, 1, &wc) > 0) {
+			vnic_ib_handle_completions(&wc, ib_conn, &comp_num,
+								 &comp_time);
+			if (ib_conn->cm_id &&
+				 ib_conn->state != IB_CONN_CONNECTED)
+				break;
+
+			/* If we get more completions than the completion limit
+			 * defer completion to the thread
+			 */
+			if ((!ib_conn->in_thread) &&
+			    (comp_num >= ib_conn->ib_config->completion_limit)) {
+				ib_conn->in_thread = 1;
+				spin_lock_irqsave(
+					&ib_conn->compl_received_lock, flags);
+				ib_conn->compl_received = 1;
+				spin_unlock_irqrestore(
+					&ib_conn->compl_received_lock, flags);
+				wake_up(&(ib_conn->callback_wait_queue));
+				break;
+			}
+
+		}
+		vnic_ib_maxio_stats(ib_conn, comp_num);
+	}
+}
+
+static int vnic_ib_mod_qp_to_rts(struct ib_cm_id *cm_id,
+			     struct vnic_ib_conn *ib_conn)
+{
+	int attr_mask = 0;
+	int ret;
+	struct ib_qp_attr *qp_attr = NULL;
+
+	qp_attr = kmalloc(sizeof *qp_attr, GFP_KERNEL);
+	if (!qp_attr)
+		return -ENOMEM;
+
+	qp_attr->qp_state = IB_QPS_RTR;
+
+	ret = ib_cm_init_qp_attr(cm_id, qp_attr, &attr_mask);
+	if (ret)
+		goto out;
+
+	ret = ib_modify_qp(ib_conn->qp, qp_attr, attr_mask);
+	if (ret)
+		goto out;
+
+	IB_INFO("QP RTR\n");
+
+	qp_attr->qp_state = IB_QPS_RTS;
+
+	ret = ib_cm_init_qp_attr(cm_id, qp_attr, &attr_mask);
+	if (ret)
+		goto out;
+
+	ret = ib_modify_qp(ib_conn->qp, qp_attr, attr_mask);
+	if (ret)
+		goto out;
+
+	IB_INFO("QP RTS\n");
+
+	ret = ib_send_cm_rtu(cm_id, NULL, 0);
+	if (ret)
+		goto out;
+out:
+	kfree(qp_attr);
+	return ret;
+}
+
+int vnic_ib_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
+{
+	struct vnic_ib_conn *ib_conn = cm_id->context;
+	struct viport *viport = ib_conn->viport;
+	int err = 0;
+
+	switch (event->event) {
+	case IB_CM_REQ_ERROR:
+		IB_ERROR("sending CM REQ failed\n");
+		err = 1;
+		viport->retry = 1;
+		break;
+	case IB_CM_REP_RECEIVED:
+		IB_INFO("CM REP recvd\n");
+		if (vnic_ib_mod_qp_to_rts(cm_id, ib_conn))
+			err = 1;
+		else {
+			ib_conn->state = IB_CONN_CONNECTED;
+			vnic_ib_connected_time_stats(ib_conn);
+			IB_INFO("RTU SENT\n");
+		}
+		break;
+	case IB_CM_REJ_RECEIVED:
+		printk(KERN_ERR PFX " CM rejected control connection\n");
+		if (event->param.rej_rcvd.reason ==
+		    IB_CM_REJ_INVALID_SERVICE_ID)
+			printk(KERN_ERR "reason: invalid service ID. "
+			       "IOCGUID value specified may be incorrect\n");
+		else
+			printk(KERN_ERR "reason code : 0x%x\n",
+			       event->param.rej_rcvd.reason);
+
+		err = 1;
+		viport->retry = 1;
+		break;
+	case IB_CM_MRA_RECEIVED:
+		IB_INFO("CM MRA received\n");
+		break;
+
+	case IB_CM_DREP_RECEIVED:
+		IB_INFO("CM DREP recvd\n");
+		ib_conn->state = IB_CONN_DISCONNECTED;
+		break;
+
+	case IB_CM_TIMEWAIT_EXIT:
+		IB_ERROR("CM timewait exit\n");
+		err = 1;
+		break;
+
+	default:
+		IB_INFO("unhandled CM event %d\n", event->event);
+		break;
+
+	}
+
+	if (err) {
+		ib_conn->state = IB_CONN_DISCONNECTED;
+		viport_failure(viport);
+	}
+
+	viport_kick(viport);
+	return 0;
+}
+
+
+int vnic_ib_cm_connect(struct vnic_ib_conn *ib_conn)
+{
+	struct ib_cm_req_param	*req = NULL;
+	struct viport		*viport;
+	int 			ret = -1;
+
+	if (!vnic_ib_conn_initted(ib_conn)) {
+		IB_ERROR("IB Connection out of state for CM connect (%d)\n",
+			 ib_conn->state);
+		return -EINVAL;
+	}
+
+	vnic_ib_conntime_stats(ib_conn);
+	req = kzalloc(sizeof *req, GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	viport	= ib_conn->viport;
+
+	req->primary_path	= &viport->config->path_info.path;
+	req->alternate_path	= NULL;
+	req->qp_num		= ib_conn->qp->qp_num;
+	req->qp_type		= ib_conn->qp->qp_type;
+	req->service_id 	= ib_conn->ib_config->service_id;
+	req->private_data	= &ib_conn->ib_config->conn_data;
+	req->private_data_len	= sizeof(struct vnic_connection_data);
+	req->flow_control	= 1;
+
+	get_random_bytes(&req->starting_psn, 4);
+	req->starting_psn &= 0xffffff;
+
+	/*
+	 * Both responder_resources and initiator_depth are set to zero
+	 * as we do not need RDMA read.
+	 *
+	 * They also must be set to zero, otherwise data connections
+	 * are rejected by VEx.
+	 */
+	req->responder_resources 	= 0;
+	req->initiator_depth		= 0;
+	req->remote_cm_response_timeout = 20;
+	req->local_cm_response_timeout  = 20;
+	req->retry_count		= ib_conn->ib_config->retry_count;
+	req->rnr_retry_count		= ib_conn->ib_config->rnr_retry_count;
+	req->max_cm_retries		= 15;
+
+	ib_conn->state = IB_CONN_CONNECTING;
+
+	ret = ib_send_cm_req(ib_conn->cm_id, req);
+
+	kfree(req);
+
+	if (ret) {
+		IB_ERROR("CM REQ sending failed; error %d \n", ret);
+		ib_conn->state = IB_CONN_DISCONNECTED;
+	}
+
+	return ret;
+}
+
+static int vnic_ib_init_qp(struct vnic_ib_conn *ib_conn,
+			   struct vnic_ib_config *config,
+			   struct ib_pd	*pd,
+			   struct viport_config *viport_config)
+{
+	struct ib_qp_init_attr	*init_attr;
+	struct ib_qp_attr	*attr;
+	int			ret;
+
+	init_attr = kzalloc(sizeof *init_attr, GFP_KERNEL);
+	if (!init_attr)
+		return -ENOMEM;
+
+	init_attr->event_handler	= ib_qp_event;
+	init_attr->cap.max_send_wr	= config->num_sends;
+	init_attr->cap.max_recv_wr	= config->num_recvs;
+	init_attr->cap.max_recv_sge	= config->recv_scatter;
+	init_attr->cap.max_send_sge	= config->send_gather;
+	init_attr->sq_sig_type		= IB_SIGNAL_ALL_WR;
+	init_attr->qp_type		= IB_QPT_RC;
+	init_attr->send_cq		= ib_conn->cq;
+	init_attr->recv_cq		= ib_conn->cq;
+
+	ib_conn->qp = ib_create_qp(pd, init_attr);
+
+	if (IS_ERR(ib_conn->qp)) {
+		ret = -1;
+		IB_ERROR("could not create QP\n");
+		goto free_init_attr;
+	}
+
+	attr = kmalloc(sizeof *attr, GFP_KERNEL);
+	if (!attr) {
+		ret = -ENOMEM;
+		goto destroy_qp;
+	}
+
+	ret = ib_find_cached_pkey(viport_config->ibdev,
+				  viport_config->port,
+				  be16_to_cpu(viport_config->path_info.path.
+					      pkey),
+				  &attr->pkey_index);
+	if (ret) {
+		printk(KERN_WARNING PFX "ib_find_cached_pkey() failed; "
+		       "error %d\n", ret);
+		goto freeattr;
+	}
+
+	attr->qp_state		= IB_QPS_INIT;
+	attr->qp_access_flags	= IB_ACCESS_REMOTE_WRITE;
+	attr->port_num		= viport_config->port;
+
+	ret = ib_modify_qp(ib_conn->qp, attr,
+			   IB_QP_STATE |
+			   IB_QP_PKEY_INDEX |
+			   IB_QP_ACCESS_FLAGS | IB_QP_PORT);
+	if (ret) {
+		printk(KERN_WARNING PFX "could not modify QP; error %d \n",
+		       ret);
+		goto freeattr;
+	}
+
+	kfree(attr);
+	kfree(init_attr);
+	return ret;
+
+freeattr:
+	kfree(attr);
+destroy_qp:
+	ib_destroy_qp(ib_conn->qp);
+free_init_attr:
+	kfree(init_attr);
+	return ret;
+}
+
+int vnic_ib_conn_init(struct vnic_ib_conn *ib_conn, struct viport *viport,
+		      struct ib_pd *pd, struct vnic_ib_config *config)
+{
+	struct viport_config	*viport_config = viport->config;
+	int		ret = -1;
+	unsigned int	cq_size = config->num_sends + config->num_recvs;
+
+
+	if (!vnic_ib_conn_uninitted(ib_conn)) {
+		IB_ERROR("IB Connection out of state for init (%d)\n",
+			 ib_conn->state);
+		return -EINVAL;
+	}
+
+	ib_conn->cq = ib_create_cq(viport_config->ibdev, vnic_ib_completion,
+#ifdef BUILD_FOR_OFED_1_2
+				   NULL, ib_conn, cq_size);
+#else
+				   NULL, ib_conn, cq_size, 0);
+#endif
+	if (IS_ERR(ib_conn->cq)) {
+		IB_ERROR("could not create CQ\n");
+		goto out;
+	}
+
+	IB_INFO("cq created %p %d\n", ib_conn->cq, cq_size);
+	ib_req_notify_cq(ib_conn->cq, IB_CQ_NEXT_COMP);
+	init_waitqueue_head(&(ib_conn->callback_wait_queue));
+	init_completion(&(ib_conn->callback_thread_exit));
+
+	spin_lock_init(&ib_conn->compl_received_lock);
+
+	ib_conn->callback_thread = kthread_run(vnic_defer_completion, ib_conn,
+						"qlgc_vnic_def_compl");
+	if (IS_ERR(ib_conn->callback_thread)) {
+		IB_ERROR("Could not create vnic_callback_thread;"
+			" error %d\n", (int) PTR_ERR(ib_conn->callback_thread));
+		ib_conn->callback_thread = NULL;
+		goto destroy_cq;
+	}
+
+	ret = vnic_ib_init_qp(ib_conn, config, pd, viport_config);
+
+	if (ret)
+		goto destroy_thread;
+
+	spin_lock_init(&ib_conn->conn_lock);
+	ib_conn->state = IB_CONN_INITTED;
+
+	return ret;
+
+destroy_thread:
+	completion_callback_cleanup(ib_conn);
+destroy_cq:
+	ib_destroy_cq(ib_conn->cq);
+out:
+	return ret;
+}
+
+int vnic_ib_post_recv(struct vnic_ib_conn *ib_conn, struct io *io)
+{
+	cycles_t		post_time;
+	struct ib_recv_wr	*bad_wr;
+	int			ret = -1;
+	unsigned long		flags;
+
+	IB_FUNCTION("vnic_ib_post_recv()\n");
+
+	spin_lock_irqsave(&ib_conn->conn_lock, flags);
+
+	if (!vnic_ib_conn_initted(ib_conn) &&
+	    !vnic_ib_conn_connected(ib_conn)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	vnic_ib_pre_rcvpost_stats(ib_conn, io, &post_time);
+	io->type = RECV;
+	ret = ib_post_recv(ib_conn->qp, &io->rwr, &bad_wr);
+	if (ret) {
+		IB_ERROR("error in posting rcv wr; error %d\n", ret);
+		ib_conn->state = IB_CONN_ERRORED;
+		goto out;
+	}
+
+	vnic_ib_post_rcvpost_stats(ib_conn, post_time);
+out:
+	spin_unlock_irqrestore(&ib_conn->conn_lock, flags);
+	return ret;
+
+}
+
+int vnic_ib_post_send(struct vnic_ib_conn *ib_conn, struct io *io)
+{
+	cycles_t		post_time;
+	unsigned long		flags;
+	struct ib_send_wr	*bad_wr;
+	int			ret = -1;
+
+	IB_FUNCTION("vnic_ib_post_send()\n");
+
+	spin_lock_irqsave(&ib_conn->conn_lock, flags);
+	if (!vnic_ib_conn_connected(ib_conn)) {
+		IB_ERROR("IB Connection out of state for"
+			 " posting sends (%d)\n", ib_conn->state);
+		goto out;
+	}
+
+	vnic_ib_pre_sendpost_stats(io, &post_time);
+	if (io->swr.opcode == IB_WR_RDMA_WRITE)
+		io->type = RDMA;
+	else
+		io->type = SEND;
+
+	ret = ib_post_send(ib_conn->qp, &io->swr, &bad_wr);
+	if (ret) {
+		IB_ERROR("error in posting send wr; error %d\n", ret);
+		ib_conn->state = IB_CONN_ERRORED;
+		goto out;
+	}
+
+	vnic_ib_post_sendpost_stats(ib_conn, io, post_time);
+out:
+	spin_unlock_irqrestore(&ib_conn->conn_lock, flags);
+	return ret;
+}
+
+static int vnic_defer_completion(void *ptr)
+{
+	struct vnic_ib_conn *ib_conn = ptr;
+	struct ib_wc wc;
+	struct ib_cq *cq = ib_conn->cq;
+	cycles_t 	 comp_time;
+	u32              comp_num = 0;
+	unsigned long	flags;
+
+	while (!ib_conn->callback_thread_end) {
+		wait_event_interruptible(ib_conn->callback_wait_queue,
+					 ib_conn->compl_received ||
+					 ib_conn->callback_thread_end);
+		ib_conn->in_thread = 1;
+		spin_lock_irqsave(&ib_conn->compl_received_lock, flags);
+		ib_conn->compl_received = 0;
+		spin_unlock_irqrestore(&ib_conn->compl_received_lock, flags);
+		if (ib_conn->cm_id &&
+		    ib_conn->state != IB_CONN_CONNECTED)
+			goto out_thread;
+
+		vnic_ib_note_comptime_stats(&comp_time);
+		vnic_ib_callback_stats(ib_conn);
+		ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
+		while (ib_poll_cq(cq, 1, &wc) > 0) {
+			vnic_ib_handle_completions(&wc, ib_conn, &comp_num,
+								 &comp_time);
+			if (ib_conn->cm_id &&
+				 ib_conn->state != IB_CONN_CONNECTED)
+				break;
+		}
+		vnic_ib_maxio_stats(ib_conn, comp_num);
+out_thread:
+		ib_conn->in_thread = 0;
+	}
+	complete_and_exit(&(ib_conn->callback_thread_exit), 0);
+	return 0;
+}
+
+void completion_callback_cleanup(struct vnic_ib_conn *ib_conn)
+{
+	if (ib_conn->callback_thread) {
+		ib_conn->callback_thread_end = 1;
+		wake_up(&(ib_conn->callback_wait_queue));
+		wait_for_completion(&(ib_conn->callback_thread_exit));
+		ib_conn->callback_thread = NULL;
+	}
+}
+
+int vnic_ib_mc_init(struct mc_data *mc_data, struct viport *viport,
+		      struct ib_pd *pd, struct vnic_ib_config *config)
+{
+	struct viport_config	*viport_config = viport->config;
+	int		ret = -1;
+	unsigned int	cq_size = config->num_recvs; /* recvs only */
+
+	IB_FUNCTION("vnic_ib_mc_init\n");
+
+	mc_data->ib_conn.cq = ib_create_cq(viport_config->ibdev, vnic_ib_completion,
+#ifdef BUILD_FOR_OFED_1_2
+				   NULL, &mc_data->ib_conn, cq_size);
+#else
+				   NULL, &mc_data->ib_conn, cq_size, 0);
+#endif
+	if (IS_ERR(mc_data->ib_conn.cq)) {
+		IB_ERROR("ib_create_cq failed\n");
+		goto out;
+	}
+	IB_INFO("mc cq created %p %d\n", mc_data->ib_conn.cq, cq_size);
+
+	ret = ib_req_notify_cq(mc_data->ib_conn.cq, IB_CQ_NEXT_COMP);
+	if (ret) {
+		IB_ERROR("ib_req_notify_cq failed %x \n", ret);
+		goto destroy_cq;
+	}
+
+	init_waitqueue_head(&(mc_data->ib_conn.callback_wait_queue));
+	init_completion(&(mc_data->ib_conn.callback_thread_exit));
+
+	spin_lock_init(&mc_data->ib_conn.compl_received_lock);
+	mc_data->ib_conn.callback_thread = kthread_run(vnic_defer_completion,
+							&mc_data->ib_conn,
+							"qlgc_vnic_mc_def_compl");
+	if (IS_ERR(mc_data->ib_conn.callback_thread)) {
+		IB_ERROR("Could not create vnic_callback_thread for MULTICAST;"
+			" error %d\n",
+			(int) PTR_ERR(mc_data->ib_conn.callback_thread));
+		mc_data->ib_conn.callback_thread = NULL;
+		goto destroy_cq;
+	}
+	IB_INFO("callback_thread created\n");
+
+	ret = vnic_ib_mc_init_qp(mc_data, config, pd, viport_config);
+	if (ret)
+		goto destroy_thread;
+
+	spin_lock_init(&mc_data->ib_conn.conn_lock);
+	mc_data->ib_conn.state = IB_CONN_INITTED; /* stays in this state */
+
+	return ret;
+
+destroy_thread:
+	completion_callback_cleanup(&mc_data->ib_conn);
+destroy_cq:
+	ib_destroy_cq(mc_data->ib_conn.cq);
+	mc_data->ib_conn.cq = (struct ib_cq *)ERR_PTR(-EINVAL);
+out:
+	return ret;
+}
+
+static int vnic_ib_mc_init_qp(struct mc_data *mc_data,
+			   struct vnic_ib_config *config,
+			   struct ib_pd	*pd,
+			   struct viport_config *viport_config)
+{
+	struct ib_qp_init_attr	*init_attr;
+	struct ib_qp_attr	*qp_attr;
+	int			ret;
+
+	IB_FUNCTION("vnic_ib_mc_init_qp\n");
+
+	if (!mc_data->ib_conn.cq) {
+		IB_ERROR("cq is null\n");
+		return -ENOMEM;
+	}
+
+	init_attr = kzalloc(sizeof *init_attr, GFP_KERNEL);
+	if (!init_attr) {
+		IB_ERROR("failed to alloc init_attr\n");
+		return -ENOMEM;
+	}
+
+	init_attr->cap.max_recv_wr	= config->num_recvs;
+	init_attr->cap.max_send_wr	= 1;
+	init_attr->cap.max_recv_sge	= 2;
+	init_attr->cap.max_send_sge	= 1;
+
+	/* Completion for all work requests. */
+	init_attr->sq_sig_type		= IB_SIGNAL_ALL_WR;
+
+	init_attr->qp_type		= IB_QPT_UD;
+
+	init_attr->send_cq		= mc_data->ib_conn.cq;
+	init_attr->recv_cq		= mc_data->ib_conn.cq;
+
+	IB_INFO("creating qp %d \n", config->num_recvs);
+
+	mc_data->ib_conn.qp = ib_create_qp(pd, init_attr);
+
+	if (IS_ERR(mc_data->ib_conn.qp)) {
+		ret = -1;
+		IB_ERROR("could not create QP\n");
+		goto free_init_attr;
+	}
+
+	qp_attr = kzalloc(sizeof *qp_attr, GFP_KERNEL);
+	if (!qp_attr) {
+		ret = -ENOMEM;
+		goto destroy_qp;
+	}
+
+	qp_attr->qp_state	= IB_QPS_INIT;
+	qp_attr->port_num	= viport_config->port;
+	qp_attr->qkey 		= IOC_NUMBER(be64_to_cpu(viport_config->ioc_guid));
+	qp_attr->pkey_index	= 0;
+	/* cannot set access flags for UD qp
+	qp_attr->qp_access_flags	= IB_ACCESS_REMOTE_WRITE; */
+
+	IB_INFO("port_num:%d qkey:%d pkey:%d\n", qp_attr->port_num,
+			qp_attr->qkey, qp_attr->pkey_index);
+	ret = ib_modify_qp(mc_data->ib_conn.qp, qp_attr,
+			   IB_QP_STATE |
+			   IB_QP_PKEY_INDEX |
+			   IB_QP_QKEY |
+
+			/* cannot set this for UD
+			   IB_QP_ACCESS_FLAGS | */
+
+			   IB_QP_PORT);
+	if (ret) {
+		IB_ERROR("ib_modify_qp to INIT failed %d \n", ret);
+		goto free_qp_attr;
+	}
+
+	kfree(qp_attr);
+	kfree(init_attr);
+	return ret;
+
+free_qp_attr:
+	kfree(qp_attr);
+destroy_qp:
+	ib_destroy_qp(mc_data->ib_conn.qp);
+	mc_data->ib_conn.qp = ERR_PTR(-EINVAL);
+free_init_attr:
+	kfree(init_attr);
+	return ret;
+}
+
+int vnic_ib_mc_mod_qp_to_rts(struct ib_qp *qp)
+{
+	int ret;
+	struct ib_qp_attr *qp_attr = NULL;
+
+	IB_FUNCTION("vnic_ib_mc_mod_qp_to_rts\n");
+	qp_attr = kmalloc(sizeof *qp_attr, GFP_KERNEL);
+	if (!qp_attr)
+		return -ENOMEM;
+
+	memset(qp_attr, 0, sizeof *qp_attr);
+	qp_attr->qp_state = IB_QPS_RTR;
+
+	ret = ib_modify_qp(qp, qp_attr, IB_QP_STATE);
+	if (ret) {
+		IB_ERROR("ib_modify_qp to RTR failed %d\n", ret);
+		goto out;
+	}
+	IB_INFO("MC QP RTR\n");
+
+	memset(qp_attr, 0, sizeof *qp_attr);
+	qp_attr->qp_state = IB_QPS_RTS;
+	qp_attr->sq_psn = 0;
+
+	ret = ib_modify_qp(qp, qp_attr, IB_QP_STATE | IB_QP_SQ_PSN);
+	if (ret) {
+		IB_ERROR("ib_modify_qp to RTS failed %d\n", ret);
+		goto out;
+	}
+	IB_INFO("MC QP RTS\n");
+
+	return 0;
+
+out:
+	kfree(qp_attr);
+	return -1;
+}
+
+int vnic_ib_mc_post_recv(struct mc_data *mc_data, struct io *io)
+{
+	cycles_t		post_time;
+	struct ib_recv_wr	*bad_wr;
+	int			ret = -1;
+
+	IB_FUNCTION("vnic_ib_mc_post_recv()\n");
+
+	vnic_ib_pre_rcvpost_stats(&mc_data->ib_conn, io, &post_time);
+	io->type = RECV_UD;
+	ret = ib_post_recv(mc_data->ib_conn.qp, &io->rwr, &bad_wr);
+	if (ret) {
+		IB_ERROR("error in posting rcv wr; error %d\n", ret);
+		goto out;
+	}
+	vnic_ib_post_rcvpost_stats(&mc_data->ib_conn, post_time);
+
+out:
+	return ret;
+}
diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_ib.h b/drivers/infiniband/ulp/qlgc_vnic/vnic_ib.h
new file mode 100644
index 0000000..ebf9ef5
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_ib.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2006 QLogic, 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 VNIC_IB_H_INCLUDED
+#define VNIC_IB_H_INCLUDED
+
+#include <linux/timex.h>
+#include <linux/completion.h>
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_pack.h>
+#include <rdma/ib_sa.h>
+#include <rdma/ib_cm.h>
+
+#include "vnic_sys.h"
+#include "vnic_netpath.h"
+#define PFX	"qlgc_vnic: "
+
+struct io;
+typedef void (comp_routine_t) (struct io *io);
+
+enum vnic_ib_conn_state {
+	IB_CONN_UNINITTED	= 0,
+	IB_CONN_INITTED		= 1,
+	IB_CONN_CONNECTING	= 2,
+	IB_CONN_CONNECTED	= 3,
+	IB_CONN_DISCONNECTED	= 4,
+	IB_CONN_ERRORED		= 5
+};
+
+struct vnic_ib_conn {
+	struct viport		*viport;
+	struct vnic_ib_config	*ib_config;
+	spinlock_t		conn_lock;
+	enum vnic_ib_conn_state	state;
+	struct ib_qp		*qp;
+	struct ib_cq		*cq;
+	struct ib_cm_id		*cm_id;
+	int 			callback_thread_end;
+	struct task_struct	*callback_thread;
+	wait_queue_head_t	callback_wait_queue;
+	u32 			in_thread;
+	u32 			compl_received;
+	struct completion 	callback_thread_exit;
+	spinlock_t		compl_received_lock;
+#ifdef CONFIG_INFINIBAND_QLGC_VNIC_STATS
+	struct {
+		cycles_t	connection_time;
+		cycles_t	rdma_post_time;
+		u32		rdma_post_ios;
+		cycles_t	rdma_comp_time;
+		u32		rdma_comp_ios;
+		cycles_t	send_post_time;
+		u32		send_post_ios;
+		cycles_t	send_comp_time;
+		u32		send_comp_ios;
+		cycles_t	recv_post_time;
+		u32		recv_post_ios;
+		cycles_t	recv_comp_time;
+		u32		recv_comp_ios;
+		u32		num_ios;
+		u32		num_callbacks;
+		u32		max_ios;
+	} statistics;
+#endif	/* CONFIG_INFINIBAND_QLGC_VNIC_STATS */
+};
+
+struct vnic_ib_path_info {
+	struct ib_sa_path_rec	path;
+	struct ib_sa_query	*path_query;
+	int			path_query_id;
+	int			status;
+	struct			completion done;
+};
+
+struct vnic_ib_device {
+	struct ib_device	*dev;
+	struct list_head	port_list;
+};
+
+struct vnic_ib_port {
+	struct vnic_ib_device	*dev;
+	u8			port_num;
+	struct dev_info		pdev_info;
+	struct list_head	list;
+};
+
+struct io {
+	struct list_head	list_ptrs;
+	struct viport		*viport;
+	comp_routine_t		*routine;
+	struct ib_recv_wr	rwr;
+	struct ib_send_wr	swr;
+#ifdef CONFIG_INFINIBAND_QLGC_VNIC_STATS
+	cycles_t		time;
+#endif	/* CONFIG_INFINIBAND_QLGC_VNIC_STATS */
+	enum {RECV, RDMA, SEND, RECV_UD}	type;
+};
+
+struct rdma_io {
+	struct io		io;
+	struct ib_sge		list[2];
+	u16			index;
+	u16			len;
+	u8			*data;
+	dma_addr_t		data_dma;
+	struct sk_buff		*skb;
+	dma_addr_t		skb_data_dma;
+	struct viport_trailer 	*trailer;
+	dma_addr_t 		trailer_dma;
+};
+
+struct send_io {
+	struct io	io;
+	struct ib_sge	list;
+	u8		*virtual_addr;
+};
+
+struct recv_io {
+	struct io	io;
+	struct ib_sge	list;
+	u8		*virtual_addr;
+};
+
+struct ud_recv_io {
+	struct io	io;
+	u16 	len;
+	dma_addr_t		skb_data_dma;
+	struct ib_sge	list[2]; /* one for grh and other for rest of pkt. */
+	struct sk_buff 	*skb;
+};
+
+int	vnic_ib_init(void);
+void	vnic_ib_cleanup(void);
+
+struct vnic;
+int vnic_ib_get_path(struct netpath *netpath, struct vnic *vnic);
+int vnic_ib_conn_init(struct vnic_ib_conn *ib_conn, struct viport *viport,
+		      struct ib_pd *pd, struct vnic_ib_config *config);
+
+int vnic_ib_post_recv(struct vnic_ib_conn *ib_conn, struct io *io);
+int vnic_ib_post_send(struct vnic_ib_conn *ib_conn, struct io *io);
+int vnic_ib_cm_connect(struct vnic_ib_conn *ib_conn);
+int vnic_ib_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event);
+
+#define	vnic_ib_conn_uninitted(ib_conn)			\
+	((ib_conn)->state == IB_CONN_UNINITTED)
+#define	vnic_ib_conn_initted(ib_conn)			\
+	((ib_conn)->state == IB_CONN_INITTED)
+#define	vnic_ib_conn_connecting(ib_conn)		\
+	((ib_conn)->state == IB_CONN_CONNECTING)
+#define	vnic_ib_conn_connected(ib_conn)			\
+	((ib_conn)->state == IB_CONN_CONNECTED)
+#define	vnic_ib_conn_disconnected(ib_conn)		\
+	((ib_conn)->state == IB_CONN_DISCONNECTED)
+
+#define MCAST_GROUP_INVALID 0x00 /* viport failed to join or left mc group */
+#define MCAST_GROUP_JOINING 0x01 /* wait for completion */
+#define MCAST_GROUP_JOINED  0x02 /* join process completed successfully */
+
+/* vnic_sa_client is used to register with sa once. It is needed to join and
+ * leave multicast groups.
+ */
+extern struct ib_sa_client vnic_sa_client;
+
+/* The following functions are using initialize and handle multicast
+ * components.
+ */
+struct mc_data; /* forward declaration */
+/* Initialize all necessary mc components */
+int vnic_ib_mc_init(struct mc_data *mc_data, struct viport *viport,
+			struct ib_pd *pd, struct vnic_ib_config *config);
+/* Put multicast qp in RTS */
+int vnic_ib_mc_mod_qp_to_rts(struct ib_qp *qp);
+/* Post multicast receive buffers */
+int vnic_ib_mc_post_recv(struct mc_data *mc_data, struct io *io);
+
+#endif	/* VNIC_IB_H_INCLUDED */

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [ofa-general] [PATCH 07/13] QLogic VNIC: Handling configurable parameters of the driver
  2008-04-30 17:15 [ofa-general] [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Ramachandra K
                   ` (5 preceding siblings ...)
  2008-04-30 17:18 ` [ofa-general] [PATCH 06/13] QLogic VNIC: IB core stack interaction Ramachandra K
@ 2008-04-30 17:19 ` Ramachandra K
  2008-05-13 20:41   ` Roland Dreier
  2008-04-30 17:19 ` [ofa-general] [PATCH 08/13] QLogic VNIC: sysfs interface implementation for " Ramachandra K
                   ` (6 subsequent siblings)
  13 siblings, 1 reply; 32+ messages in thread
From: Ramachandra K @ 2008-04-30 17:19 UTC (permalink / raw)
  To: rdreier, general, netdev; +Cc: poornima.kamath, amar.mudrankit

From: Poornima Kamath <poornima.kamath@qlogic.com>

This patch adds the files that handle various configurable parameters
of the VNIC driver ---- configuration of virtual NIC, control, data 
connections to the EVIC and general IB connection parameters.

Signed-off-by: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>
Signed-off-by: Amar Mudrankit <amar.mudrankit@qlogic.com>
---

 drivers/infiniband/ulp/qlgc_vnic/vnic_config.c |  380 ++++++++++++++++++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_config.h |  242 +++++++++++++++
 2 files changed, 622 insertions(+), 0 deletions(-)
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_config.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_config.h

diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_config.c b/drivers/infiniband/ulp/qlgc_vnic/vnic_config.c
new file mode 100644
index 0000000..86d99b6
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_config.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2006 QLogic, 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/netdevice.h>
+#include <linux/string.h>
+#include <linux/utsname.h>
+#include <linux/if_vlan.h>
+
+#include <rdma/ib_cache.h>
+
+#include "vnic_util.h"
+#include "vnic_config.h"
+#include "vnic_trailer.h"
+#include "vnic_main.h"
+
+u16 vnic_max_mtu = MAX_MTU;
+
+static u32 default_no_path_timeout = DEFAULT_NO_PATH_TIMEOUT;
+static u32 sa_path_rec_get_timeout = SA_PATH_REC_GET_TIMEOUT;
+static u32 default_primary_reconnect_timeout =
+				    DEFAULT_PRIMARY_RECONNECT_TIMEOUT;
+static u32 default_primary_switch_timeout = DEFAULT_PRIMARY_SWITCH_TIMEOUT;
+static int default_prefer_primary         = DEFAULT_PREFER_PRIMARY;
+
+static int use_rx_csum = VNIC_USE_RX_CSUM;
+static int use_tx_csum = VNIC_USE_TX_CSUM;
+
+static u32 control_response_timeout = CONTROL_RSP_TIMEOUT;
+static u32 completion_limit = DEFAULT_COMPLETION_LIMIT;
+
+module_param(vnic_max_mtu, ushort, 0444);
+MODULE_PARM_DESC(vnic_max_mtu, "Maximum MTU size (1500-9500). Default is 9500");
+
+module_param(default_prefer_primary, bool, 0444);
+MODULE_PARM_DESC(default_prefer_primary, "Determines if primary path is"
+		 " preferred (1) or not (0). Defaults to 0");
+module_param(use_rx_csum, bool, 0444);
+MODULE_PARM_DESC(use_rx_csum, "Determines if RX checksum is done on VEx (1)"
+		 " or not (0). Defaults to 1");
+module_param(use_tx_csum, bool, 0444);
+MODULE_PARM_DESC(use_tx_csum, "Determines if TX checksum is done on VEx (1)"
+		 " or not (0). Defaults to 1");
+module_param(default_no_path_timeout, uint, 0444);
+MODULE_PARM_DESC(default_no_path_timeout, "Time to wait in milliseconds"
+		 " before reconnecting to VEx after connection loss");
+module_param(default_primary_reconnect_timeout, uint, 0444);
+MODULE_PARM_DESC(default_primary_reconnect_timeout,  "Time to wait in"
+		 " milliseconds before reconnecting the"
+		 " primary path to VEx");
+module_param(default_primary_switch_timeout, uint, 0444);
+MODULE_PARM_DESC(default_primary_switch_timeout, "Time to wait before"
+		 " switching back to primary path if"
+		 " primary path is preferred");
+module_param(sa_path_rec_get_timeout, uint, 0444);
+MODULE_PARM_DESC(sa_path_rec_get_timeout, "Time out value in milliseconds"
+		 " for SA path record get queries");
+
+module_param(control_response_timeout, uint, 0444);
+MODULE_PARM_DESC(control_response_timeout, "Time out value in milliseconds"
+		 " to wait for response to control requests");
+
+module_param(completion_limit, uint, 0444);
+MODULE_PARM_DESC(completion_limit, "Maximum completions to process"
+		" in a single completion callback invocation. Default is 100"
+		" Minimum value is 10");
+
+static void config_control_defaults(struct control_config *control_config,
+				    struct path_param *params)
+{
+	int len;
+	char *dot;
+	u64 sid;
+
+	sid = (SST_AGN << 56) | (SST_OUI << 32) | (CONTROL_PATH_ID << 8)
+	      |	IOC_NUMBER(be64_to_cpu(params->ioc_guid));
+
+	control_config->ib_config.service_id = cpu_to_be64(sid);
+	control_config->ib_config.conn_data.path_id = 0;
+	control_config->ib_config.conn_data.vnic_instance = params->instance;
+	control_config->ib_config.conn_data.path_num = 0;
+	control_config->ib_config.conn_data.features_supported =
+			__constant_cpu_to_be32((u32) (VNIC_FEAT_IGNORE_VLAN |
+						      VNIC_FEAT_RDMA_IMMED));
+	dot = strchr(init_utsname()->nodename, '.');
+
+	if (dot)
+		len = dot - init_utsname()->nodename;
+	else
+		len = strlen(init_utsname()->nodename);
+
+	if (len > VNIC_MAX_NODENAME_LEN)
+		len = VNIC_MAX_NODENAME_LEN;
+
+	memcpy(control_config->ib_config.conn_data.nodename,
+					init_utsname()->nodename, len);
+
+	if (params->ib_multicast == 1)
+		control_config->ib_multicast = 1;
+	else if (params->ib_multicast == 0)
+		control_config->ib_multicast = 0;
+	else {
+		/* parameter is not set - enable it by default */
+		control_config->ib_multicast = 1;
+		CONFIG_ERROR("IOCGUID=%llx INSTANCE=%d IB_MULTICAST defaulted"
+					" to TRUE\n", params->ioc_guid,
+					(char)params->instance);
+	}
+
+	if (control_config->ib_multicast)
+		control_config->ib_config.conn_data.features_supported |=
+			__constant_cpu_to_be32(VNIC_FEAT_INBOUND_IB_MC);
+
+	control_config->ib_config.retry_count = RETRY_COUNT;
+	control_config->ib_config.rnr_retry_count = RETRY_COUNT;
+	control_config->ib_config.min_rnr_timer = MIN_RNR_TIMER;
+
+	/* These values are not configurable*/
+	control_config->ib_config.num_recvs    = 5;
+	control_config->ib_config.num_sends    = 1;
+	control_config->ib_config.recv_scatter = 1;
+	control_config->ib_config.send_gather  = 1;
+	control_config->ib_config.completion_limit = completion_limit;
+
+	control_config->num_recvs = control_config->ib_config.num_recvs;
+
+	control_config->vnic_instance = params->instance;
+	control_config->max_address_entries = MAX_ADDRESS_ENTRIES;
+	control_config->min_address_entries = MIN_ADDRESS_ENTRIES;
+	control_config->rsp_timeout = msecs_to_jiffies(control_response_timeout);
+}
+
+static void config_data_defaults(struct data_config *data_config,
+				 struct path_param *params)
+{
+	u64 sid;
+
+	sid = (SST_AGN << 56) | (SST_OUI << 32) | (DATA_PATH_ID << 8)
+	      |	IOC_NUMBER(be64_to_cpu(params->ioc_guid));
+
+	data_config->ib_config.service_id = cpu_to_be64(sid);
+	data_config->ib_config.conn_data.path_id = jiffies; /* random */
+	data_config->ib_config.conn_data.vnic_instance = params->instance;
+	data_config->ib_config.conn_data.path_num = 0;
+
+	data_config->ib_config.retry_count = RETRY_COUNT;
+	data_config->ib_config.rnr_retry_count = RETRY_COUNT;
+	data_config->ib_config.min_rnr_timer = MIN_RNR_TIMER;
+
+	/*
+	 * NOTE: the num_recvs size assumes that the EIOC could
+	 * RDMA enough packets to fill all of the host recv
+	 * pool entries, plus send a kick message after each
+	 * packet, plus RDMA new buffers for the size of
+	 * the EIOC recv buffer pool, plus send kick messages
+	 * after each min_host_update_sz of new buffers all
+	 * before the host can even pull off the first completed
+	 * receive off the completion queue, and repost the
+	 * receive. NOT LIKELY!
+	 */
+	data_config->ib_config.num_recvs = HOST_RECV_POOL_ENTRIES +
+	    (MAX_EIOC_POOL_SZ / MIN_HOST_UPDATE_SZ);
+
+	data_config->ib_config.num_sends = (2 * NOTIFY_BUNDLE_SZ) +
+	    (HOST_RECV_POOL_ENTRIES / MIN_EIOC_UPDATE_SZ) + 1;
+
+	data_config->ib_config.recv_scatter = 1; /* not configurable */
+	data_config->ib_config.send_gather = 2;	 /* not configurable */
+	data_config->ib_config.completion_limit = completion_limit;
+
+	data_config->num_recvs = data_config->ib_config.num_recvs;
+	data_config->path_id = data_config->ib_config.conn_data.path_id;
+
+
+	data_config->host_recv_pool_entries = HOST_RECV_POOL_ENTRIES;
+
+	data_config->host_min.size_recv_pool_entry =
+			cpu_to_be32(BUFFER_SIZE(VLAN_ETH_HLEN + MIN_MTU));
+	data_config->host_max.size_recv_pool_entry =
+			cpu_to_be32(BUFFER_SIZE(VLAN_ETH_HLEN + vnic_max_mtu));
+	data_config->eioc_min.size_recv_pool_entry =
+			cpu_to_be32(BUFFER_SIZE(VLAN_ETH_HLEN + MIN_MTU));
+	data_config->eioc_max.size_recv_pool_entry =
+			__constant_cpu_to_be32(MAX_PARAM_VALUE);
+
+	data_config->host_min.num_recv_pool_entries =
+				__constant_cpu_to_be32(MIN_HOST_POOL_SZ);
+	data_config->host_max.num_recv_pool_entries =
+				__constant_cpu_to_be32(MAX_PARAM_VALUE);
+	data_config->eioc_min.num_recv_pool_entries =
+				__constant_cpu_to_be32(MIN_EIOC_POOL_SZ);
+	data_config->eioc_max.num_recv_pool_entries =
+				__constant_cpu_to_be32(MAX_EIOC_POOL_SZ);
+
+	data_config->host_min.timeout_before_kick =
+			__constant_cpu_to_be32(MIN_HOST_KICK_TIMEOUT);
+	data_config->host_max.timeout_before_kick =
+			__constant_cpu_to_be32(MAX_HOST_KICK_TIMEOUT);
+	data_config->eioc_min.timeout_before_kick = 0;
+	data_config->eioc_max.timeout_before_kick =
+			__constant_cpu_to_be32(MAX_PARAM_VALUE);
+
+	data_config->host_min.num_recv_pool_entries_before_kick =
+			__constant_cpu_to_be32(MIN_HOST_KICK_ENTRIES);
+	data_config->host_max.num_recv_pool_entries_before_kick =
+			__constant_cpu_to_be32(MAX_HOST_KICK_ENTRIES);
+	data_config->eioc_min.num_recv_pool_entries_before_kick = 0;
+	data_config->eioc_max.num_recv_pool_entries_before_kick =
+				__constant_cpu_to_be32(MAX_PARAM_VALUE);
+
+	data_config->host_min.num_recv_pool_bytes_before_kick =
+			__constant_cpu_to_be32(MIN_HOST_KICK_BYTES);
+	data_config->host_max.num_recv_pool_bytes_before_kick =
+			__constant_cpu_to_be32(MAX_HOST_KICK_BYTES);
+	data_config->eioc_min.num_recv_pool_bytes_before_kick = 0;
+	data_config->eioc_max.num_recv_pool_bytes_before_kick =
+				__constant_cpu_to_be32(MAX_PARAM_VALUE);
+
+	data_config->host_min.free_recv_pool_entries_per_update =
+				__constant_cpu_to_be32(MIN_HOST_UPDATE_SZ);
+	data_config->host_max.free_recv_pool_entries_per_update =
+				__constant_cpu_to_be32(MAX_HOST_UPDATE_SZ);
+	data_config->eioc_min.free_recv_pool_entries_per_update =
+				__constant_cpu_to_be32(MIN_EIOC_UPDATE_SZ);
+	data_config->eioc_max.free_recv_pool_entries_per_update =
+				__constant_cpu_to_be32(MAX_EIOC_UPDATE_SZ);
+
+	data_config->notify_bundle = NOTIFY_BUNDLE_SZ;
+}
+
+static void config_path_info_defaults(struct viport_config *config,
+				      struct path_param *params)
+{
+	int i;
+	ib_get_cached_gid(config->ibdev, config->port, 0,
+			  &config->path_info.path.sgid);
+	for (i = 0; i < 16; i++)
+		config->path_info.path.dgid.raw[i] = params->dgid[i];
+
+	config->path_info.path.pkey = params->pkey;
+	config->path_info.path.numb_path = 1;
+	config->sa_path_rec_get_timeout = sa_path_rec_get_timeout;
+
+}
+
+static void config_viport_defaults(struct viport_config *config,
+				      struct path_param *params)
+{
+	config->ibdev = params->ibdev;
+	config->port = params->port;
+	config->ioc_guid = params->ioc_guid;
+	config->stats_interval = msecs_to_jiffies(VIPORT_STATS_INTERVAL);
+	config->hb_interval = msecs_to_jiffies(VIPORT_HEARTBEAT_INTERVAL);
+	config->hb_timeout = VIPORT_HEARTBEAT_TIMEOUT * 1000;
+				/*hb_timeout needs to be in usec*/
+	strcpy(config->ioc_string, params->ioc_string);
+	config_path_info_defaults(config, params);
+
+	config_control_defaults(&config->control_config, params);
+	config_data_defaults(&config->data_config, params);
+}
+
+static void config_vnic_defaults(struct vnic_config *config)
+{
+	config->no_path_timeout = msecs_to_jiffies(default_no_path_timeout);
+	config->primary_connect_timeout =
+	    msecs_to_jiffies(DEFAULT_PRIMARY_CONNECT_TIMEOUT);
+	config->primary_reconnect_timeout =
+	    msecs_to_jiffies(default_primary_reconnect_timeout);
+	config->primary_switch_timeout =
+	    msecs_to_jiffies(default_primary_switch_timeout);
+	config->prefer_primary = default_prefer_primary;
+	config->use_rx_csum = use_rx_csum;
+	config->use_tx_csum = use_tx_csum;
+}
+
+struct viport_config *config_alloc_viport(struct path_param *params)
+{
+	struct viport_config *config;
+
+	config = kzalloc(sizeof *config, GFP_KERNEL);
+	if (!config) {
+		CONFIG_ERROR("could not allocate memory for"
+			     " struct viport_config\n");
+		return NULL;
+	}
+
+	config_viport_defaults(config, params);
+
+	return config;
+}
+
+struct vnic_config *config_alloc_vnic(void)
+{
+	struct vnic_config *config;
+
+	config = kzalloc(sizeof *config, GFP_KERNEL);
+	if (!config) {
+		CONFIG_ERROR("couldn't allocate memory for"
+			     " struct vnic_config\n");
+
+		return NULL;
+	}
+
+	config_vnic_defaults(config);
+	return config;
+}
+
+char *config_viport_name(struct viport_config *config)
+{
+	/* function only called by one thread, can return a static string */
+	static char str[64];
+
+	sprintf(str, "GUID %llx instance %d",
+		be64_to_cpu(config->ioc_guid),
+		config->control_config.vnic_instance);
+	return str;
+}
+
+int config_start(void)
+{
+	vnic_max_mtu = min_t(u16, vnic_max_mtu, MAX_MTU);
+	vnic_max_mtu = max_t(u16, vnic_max_mtu, MIN_MTU);
+
+	sa_path_rec_get_timeout = min_t(u32, sa_path_rec_get_timeout,
+					MAX_SA_TIMEOUT);
+	sa_path_rec_get_timeout = max_t(u32, sa_path_rec_get_timeout,
+					MIN_SA_TIMEOUT);
+
+	control_response_timeout = min_t(u32, control_response_timeout,
+					 MAX_CONTROL_RSP_TIMEOUT);
+
+	control_response_timeout = max_t(u32, control_response_timeout,
+					 MIN_CONTROL_RSP_TIMEOUT);
+
+	completion_limit	 = max_t(u32, completion_limit,
+					 MIN_COMPLETION_LIMIT);
+
+	if (!default_no_path_timeout)
+		default_no_path_timeout = DEFAULT_NO_PATH_TIMEOUT;
+
+	if (!default_primary_reconnect_timeout)
+		default_primary_reconnect_timeout =
+					 DEFAULT_PRIMARY_RECONNECT_TIMEOUT;
+
+	if (!default_primary_switch_timeout)
+		default_primary_switch_timeout =
+					DEFAULT_PRIMARY_SWITCH_TIMEOUT;
+
+	return 0;
+
+}
diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_config.h b/drivers/infiniband/ulp/qlgc_vnic/vnic_config.h
new file mode 100644
index 0000000..c5b00b9
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_config.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2006 QLogic, 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 VNIC_CONFIG_H_INCLUDED
+#define VNIC_CONFIG_H_INCLUDED
+
+#include <rdma/ib_verbs.h>
+#include <linux/types.h>
+#include <linux/if.h>
+
+#include "vnic_control.h"
+#include "vnic_ib.h"
+
+#define SST_AGN         0x10ULL
+#define SST_OUI         0x00066AULL
+
+enum {
+	CONTROL_PATH_ID = 0x0,
+	DATA_PATH_ID    = 0x1
+};
+
+#define IOC_NUMBER(GUID)        (((GUID) >> 32) & 0xFF)
+
+enum {
+	VNIC_CLASS_SUBCLASS	= 0x2000066A,
+	VNIC_PROTOCOL		= 0,
+	VNIC_PROT_VERSION	= 1
+};
+
+enum {
+	MIN_MTU	= 1500,	/* minimum negotiated MTU size */
+	MAX_MTU	= 9500	/* jumbo frame */
+};
+
+/*
+ * TODO: tune the pool parameter values
+ */
+enum {
+	MIN_ADDRESS_ENTRIES = 16,
+	MAX_ADDRESS_ENTRIES = 64
+};
+
+enum {
+	HOST_RECV_POOL_ENTRIES	= 512,
+	MIN_HOST_POOL_SZ	= 64,
+	MIN_EIOC_POOL_SZ	= 64,
+	MAX_EIOC_POOL_SZ	= 256,
+	MIN_HOST_UPDATE_SZ	= 8,
+	MAX_HOST_UPDATE_SZ	= 32,
+	MIN_EIOC_UPDATE_SZ	= 8,
+	MAX_EIOC_UPDATE_SZ	= 32,
+	NOTIFY_BUNDLE_SZ	= 32
+};
+
+enum {
+	MIN_HOST_KICK_TIMEOUT = 10,	/* in usec */
+	MAX_HOST_KICK_TIMEOUT = 100	/* in usec */
+};
+
+enum {
+	MIN_HOST_KICK_ENTRIES = 1,
+	MAX_HOST_KICK_ENTRIES = 128
+};
+
+enum {
+	MIN_HOST_KICK_BYTES = 0,
+	MAX_HOST_KICK_BYTES = 5000
+};
+
+enum {
+	DEFAULT_NO_PATH_TIMEOUT			= 10000,
+	DEFAULT_PRIMARY_CONNECT_TIMEOUT		= 10000,
+	DEFAULT_PRIMARY_RECONNECT_TIMEOUT	= 10000,
+	DEFAULT_PRIMARY_SWITCH_TIMEOUT		= 10000
+};
+
+enum {
+	VIPORT_STATS_INTERVAL		= 500,	/* .5 sec */
+	VIPORT_HEARTBEAT_INTERVAL	= 1000,	/* 1 second */
+	VIPORT_HEARTBEAT_TIMEOUT	= 64000	/* 64 sec */
+};
+
+enum {
+	/* 5 sec increased for EVIC support for large number of
+	 * host connections
+	 */
+	CONTROL_RSP_TIMEOUT		= 5000,
+	MIN_CONTROL_RSP_TIMEOUT		= 1000,	/* 1  sec */
+	MAX_CONTROL_RSP_TIMEOUT		= 60000	/* 60 sec */
+};
+
+/* Maximum number of completions to be processed
+ * during a single completion callback invocation
+ */
+enum {
+	DEFAULT_COMPLETION_LIMIT 	= 100,
+	MIN_COMPLETION_LIMIT		= 10
+};
+
+/* infiniband connection parameters */
+enum {
+	RETRY_COUNT		= 3,
+	MIN_RNR_TIMER		= 22,	/* 20 ms */
+	DEFAULT_PKEY		= 0	/* pkey table index */
+};
+
+enum {
+	SA_PATH_REC_GET_TIMEOUT	= 1000,	/* 1000 ms */
+	MIN_SA_TIMEOUT		= 100,	/* 100 ms */
+	MAX_SA_TIMEOUT		= 20000	/* 20s */
+};
+
+#define MAX_PARAM_VALUE                 0x40000000
+#define VNIC_USE_RX_CSUM		1
+#define VNIC_USE_TX_CSUM		1
+#define	DEFAULT_PREFER_PRIMARY		0
+
+/* As per IBTA specification, IOCString Maximum length can be 512 bits. */
+#define MAX_IOC_STRING_LEN 		(512/8)
+
+struct path_param {
+	__be64			ioc_guid;
+	u8			ioc_string[MAX_IOC_STRING_LEN+1];
+	u8			port;
+	u8			instance;
+	struct ib_device	*ibdev;
+	struct vnic_ib_port	*ibport;
+	char			name[IFNAMSIZ];
+	u8			dgid[16];
+	__be16			pkey;
+	int			rx_csum;
+	int			tx_csum;
+	int			heartbeat;
+	int			ib_multicast;
+};
+
+struct vnic_ib_config {
+	__be64				service_id;
+	struct vnic_connection_data	conn_data;
+	u32				retry_count;
+	u32				rnr_retry_count;
+	u8				min_rnr_timer;
+	u32				num_sends;
+	u32				num_recvs;
+	u32				recv_scatter;	/* 1 */
+	u32				send_gather;	/* 1 or 2 */
+	u32				completion_limit;
+};
+
+struct control_config {
+	struct vnic_ib_config	ib_config;
+	u32			num_recvs;
+	u8			vnic_instance;
+	u16			max_address_entries;
+	u16			min_address_entries;
+	u32			rsp_timeout;
+	u32			ib_multicast;
+};
+
+struct data_config {
+	struct vnic_ib_config		ib_config;
+	u64				path_id;
+	u32				num_recvs;
+	u32				host_recv_pool_entries;
+	struct vnic_recv_pool_config	host_min;
+	struct vnic_recv_pool_config	host_max;
+	struct vnic_recv_pool_config	eioc_min;
+	struct vnic_recv_pool_config	eioc_max;
+	u32				notify_bundle;
+};
+
+struct viport_config {
+	struct viport			*viport;
+	struct control_config		control_config;
+	struct data_config		data_config;
+	struct vnic_ib_path_info	path_info;
+	u32				sa_path_rec_get_timeout;
+	struct ib_device		*ibdev;
+	u32				port;
+	u32				stats_interval;
+	u32				hb_interval;
+	u32				hb_timeout;
+	__be64				ioc_guid;
+	u8				ioc_string[MAX_IOC_STRING_LEN+1];
+	size_t				path_idx;
+};
+
+/*
+ * primary_connect_timeout   - if the secondary connects first,
+ *                             how long do we give the primary?
+ * primary_reconnect_timeout - same as above, but used when recovering
+ *                             from the case where both paths fail
+ * primary_switch_timeout -    how long do we wait before switching to the
+ *                             primary when it comes back?
+ */
+struct vnic_config {
+	struct vnic	*vnic;
+	char		name[IFNAMSIZ];
+	u32		no_path_timeout;
+	u32 		primary_connect_timeout;
+	u32		primary_reconnect_timeout;
+	u32		primary_switch_timeout;
+	int		prefer_primary;
+	int		use_rx_csum;
+	int		use_tx_csum;
+};
+
+int config_start(void);
+struct viport_config *config_alloc_viport(struct path_param *params);
+struct vnic_config   *config_alloc_vnic(void);
+char *config_viport_name(struct viport_config *config);
+
+#endif	/* VNIC_CONFIG_H_INCLUDED */

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [ofa-general] [PATCH 08/13] QLogic VNIC: sysfs interface implementation for the driver
  2008-04-30 17:15 [ofa-general] [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Ramachandra K
                   ` (6 preceding siblings ...)
  2008-04-30 17:19 ` [ofa-general] [PATCH 07/13] QLogic VNIC: Handling configurable parameters of the driver Ramachandra K
@ 2008-04-30 17:19 ` Ramachandra K
  2008-05-01 14:56   ` Stephen Hemminger
  2008-04-30 17:20 ` [ofa-general] [PATCH 09/13] QLogic VNIC: IB Multicast for Ethernet broadcast/multicast Ramachandra K
                   ` (5 subsequent siblings)
  13 siblings, 1 reply; 32+ messages in thread
From: Ramachandra K @ 2008-04-30 17:19 UTC (permalink / raw)
  To: rdreier, general, netdev; +Cc: poornima.kamath, amar.mudrankit

From: Amar Mudrankit <amar.mudrankit@qlogic.com>

The sysfs interface for the QLogic VNIC driver is implemented through
this patch.

Signed-off-by: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>
Signed-off-by: Poornima Kamath <poornima.kamath@qlogic.com>
---

 drivers/infiniband/ulp/qlgc_vnic/vnic_sys.c | 1127 +++++++++++++++++++++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_sys.h |   62 +
 2 files changed, 1189 insertions(+), 0 deletions(-)
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_sys.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_sys.h

diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_sys.c b/drivers/infiniband/ulp/qlgc_vnic/vnic_sys.c
new file mode 100644
index 0000000..7e70b0c
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_sys.c
@@ -0,0 +1,1127 @@
+/*
+ * Copyright (c) 2006 QLogic, 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/parser.h>
+#include <linux/netdevice.h>
+#include <linux/if.h>
+
+#include "vnic_util.h"
+#include "vnic_config.h"
+#include "vnic_ib.h"
+#include "vnic_viport.h"
+#include "vnic_main.h"
+#include "vnic_stats.h"
+
+/*
+ * target eiocs are added by writing
+ *
+ * ioc_guid=<EIOC GUID>,dgid=<dest GID>,pkey=<P_key>,name=<interface_name>
+ * to the create_primary  sysfs attribute.
+ */
+enum {
+	VNIC_OPT_ERR = 0,
+	VNIC_OPT_IOC_GUID = 1 << 0,
+	VNIC_OPT_DGID = 1 << 1,
+	VNIC_OPT_PKEY = 1 << 2,
+	VNIC_OPT_NAME = 1 << 3,
+	VNIC_OPT_INSTANCE = 1 << 4,
+	VNIC_OPT_RXCSUM = 1 << 5,
+	VNIC_OPT_TXCSUM = 1 << 6,
+	VNIC_OPT_HEARTBEAT = 1 << 7,
+	VNIC_OPT_IOC_STRING = 1 << 8,
+	VNIC_OPT_IB_MULTICAST = 1 << 9,
+	VNIC_OPT_ALL = (VNIC_OPT_IOC_GUID |
+			VNIC_OPT_DGID | VNIC_OPT_NAME | VNIC_OPT_PKEY),
+};
+
+static match_table_t vnic_opt_tokens = {
+	{VNIC_OPT_IOC_GUID, "ioc_guid=%s"},
+	{VNIC_OPT_DGID, "dgid=%s"},
+	{VNIC_OPT_PKEY, "pkey=%x"},
+	{VNIC_OPT_NAME, "name=%s"},
+	{VNIC_OPT_INSTANCE, "instance=%d"},
+	{VNIC_OPT_RXCSUM, "rx_csum=%s"},
+	{VNIC_OPT_TXCSUM, "tx_csum=%s"},
+	{VNIC_OPT_HEARTBEAT, "heartbeat=%d"},
+	{VNIC_OPT_IOC_STRING, "ioc_string=\"%s"},
+	{VNIC_OPT_IB_MULTICAST, "ib_multicast=%s"},
+	{VNIC_OPT_ERR, NULL}
+};
+
+void vnic_release_dev(struct device *dev)
+{
+	struct dev_info *dev_info =
+	    container_of(dev, struct dev_info, dev);
+
+	complete(&dev_info->released);
+
+}
+
+struct class vnic_class = {
+	.name = "infiniband_qlgc_vnic",
+	.dev_release = vnic_release_dev
+};
+
+struct dev_info interface_dev;
+
+DEVICE_ATTR(create_primary, S_IWUSR, NULL, vnic_create_primary);
+DEVICE_ATTR(create_secondary, S_IWUSR, NULL, vnic_create_secondary);
+DEVICE_ATTR(delete_vnic, S_IWUSR, NULL, vnic_delete);
+
+static int vnic_parse_options(const char *buf, struct path_param *param)
+{
+	char *options, *sep_opt;
+	char *p;
+	char dgid[3];
+	substring_t args[MAX_OPT_ARGS];
+	int opt_mask = 0;
+	int token;
+	int ret = -EINVAL;
+	int i, len;
+
+	options = kstrdup(buf, GFP_KERNEL);
+	if (!options)
+		return -ENOMEM;
+
+	sep_opt = options;
+	while ((p = strsep(&sep_opt, ",")) != NULL) {
+		if (!*p)
+			continue;
+
+		token = match_token(p, vnic_opt_tokens, args);
+		opt_mask |= token;
+
+		switch (token) {
+		case VNIC_OPT_IOC_GUID:
+			p = match_strdup(args);
+			param->ioc_guid = cpu_to_be64(simple_strtoull(p, NULL,
+								      16));
+			kfree(p);
+			break;
+
+		case VNIC_OPT_DGID:
+			p = match_strdup(args);
+			if (strlen(p) != 32) {
+				printk(KERN_WARNING PFX
+				       "bad dest GID parameter '%s'\n", p);
+				kfree(p);
+				goto out;
+			}
+
+			for (i = 0; i < 16; ++i) {
+				strlcpy(dgid, p + i * 2, 3);
+				param->dgid[i] = simple_strtoul(dgid, NULL,
+								16);
+
+			}
+			kfree(p);
+			break;
+
+		case VNIC_OPT_PKEY:
+			if (match_hex(args, &token)) {
+				printk(KERN_WARNING PFX
+				       "bad P_key parameter '%s'\n", p);
+				goto out;
+			}
+			param->pkey = cpu_to_be16(token);
+			break;
+
+		case VNIC_OPT_NAME:
+			p = match_strdup(args);
+			if (strlen(p) >= IFNAMSIZ) {
+				printk(KERN_WARNING PFX
+				       "interface name parameter too long\n");
+				kfree(p);
+				goto out;
+			}
+			strcpy(param->name, p);
+			kfree(p);
+			break;
+		case VNIC_OPT_INSTANCE:
+			if (match_int(args, &token)) {
+				printk(KERN_WARNING PFX
+				       "bad instance parameter '%s'\n", p);
+				goto out;
+			}
+
+			if (token > 255 || token < 0) {
+				printk(KERN_WARNING PFX
+				       "instance parameter must be"
+				       " >= 0 and <= 255\n");
+				goto out;
+			}
+
+			param->instance = token;
+			break;
+		case VNIC_OPT_RXCSUM:
+			p = match_strdup(args);
+			if (!strncmp(p, "true", 4))
+				param->rx_csum = 1;
+			else if (!strncmp(p, "false", 5))
+				param->rx_csum = 0;
+			else {
+				printk(KERN_WARNING PFX
+				       "bad rx_csum parameter."
+				       " must be 'true' or 'false'\n");
+				kfree(p);
+				goto out;
+			}
+			kfree(p);
+			break;
+		case VNIC_OPT_TXCSUM:
+			p = match_strdup(args);
+			if (!strncmp(p, "true", 4))
+				param->tx_csum = 1;
+			else if (!strncmp(p, "false", 5))
+				param->tx_csum = 0;
+			else {
+				printk(KERN_WARNING PFX
+				       "bad tx_csum parameter."
+				       " must be 'true' or 'false'\n");
+				kfree(p);
+				goto out;
+			}
+			kfree(p);
+			break;
+		case VNIC_OPT_HEARTBEAT:
+			if (match_int(args, &token)) {
+				printk(KERN_WARNING PFX
+				       "bad instance parameter '%s'\n", p);
+				goto out;
+			}
+
+			if (token > 6000 || token <= 0) {
+				printk(KERN_WARNING PFX
+				       "heartbeat parameter must be"
+				       " > 0 and <= 6000\n");
+				goto out;
+			}
+			param->heartbeat = token;
+			break;
+		case VNIC_OPT_IOC_STRING:
+			p = match_strdup(args);
+			len = strlen(p);
+			if (len > MAX_IOC_STRING_LEN) {
+				printk(KERN_WARNING PFX
+				       "ioc string parameter too long\n");
+				kfree(p);
+				goto out;
+			}
+			strcpy(param->ioc_string, p);
+			if (*(p + len - 1) != '\"') {
+				strcat(param->ioc_string, ",");
+				kfree(p);
+				p = strsep(&sep_opt, "\"");
+				strcat(param->ioc_string, p);
+				sep_opt++;
+			} else {
+				*(param->ioc_string + len - 1) = '\0';
+				kfree(p);
+			}
+			break;
+		case VNIC_OPT_IB_MULTICAST:
+			p = match_strdup(args);
+			if (!strncmp(p, "true", 4))
+				param->ib_multicast = 1;
+			else if (!strncmp(p, "false", 5))
+				param->ib_multicast = 0;
+			else {
+					printk(KERN_WARNING PFX
+					"bad ib_multicast parameter."
+					" must be 'true' or 'false'\n");
+				kfree(p);
+				goto out;
+			}
+			kfree(p);
+			break;
+		default:
+			printk(KERN_WARNING PFX
+			       "unknown parameter or missing value "
+			       "'%s' in target creation request\n", p);
+			goto out;
+		}
+
+	}
+
+	if ((opt_mask & VNIC_OPT_ALL) == VNIC_OPT_ALL)
+		ret = 0;
+	else
+		for (i = 0; i < ARRAY_SIZE(vnic_opt_tokens); ++i)
+			if ((vnic_opt_tokens[i].token & VNIC_OPT_ALL) &&
+			    !(vnic_opt_tokens[i].token & opt_mask))
+				printk(KERN_WARNING PFX
+				       "target creation request is "
+				       "missing parameter '%s'\n",
+				       vnic_opt_tokens[i].pattern);
+
+out:
+	kfree(options);
+	return ret;
+
+}
+
+static ssize_t show_vnic_state(struct device *dev,
+			       struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info = container_of(dev, struct dev_info, dev);
+	struct vnic *vnic = container_of(info, struct vnic, dev_info);
+	switch (vnic->state) {
+	case VNIC_UNINITIALIZED:
+		return sprintf(buf, "VNIC_UNINITIALIZED\n");
+	case VNIC_REGISTERED:
+		return sprintf(buf, "VNIC_REGISTERED\n");
+	default:
+		return sprintf(buf, "INVALID STATE\n");
+	}
+
+}
+
+static DEVICE_ATTR(vnic_state, S_IRUGO, show_vnic_state, NULL);
+
+static ssize_t show_rx_csum(struct device *dev,
+			    struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info = container_of(dev, struct dev_info, dev);
+	struct vnic *vnic = container_of(info, struct vnic, dev_info);
+
+	if (vnic->config->use_rx_csum)
+		return sprintf(buf, "true\n");
+	else
+		return sprintf(buf, "false\n");
+}
+
+static DEVICE_ATTR(rx_csum, S_IRUGO, show_rx_csum, NULL);
+
+static ssize_t show_tx_csum(struct device *dev,
+			    struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info = container_of(dev, struct dev_info, dev);
+	struct vnic *vnic = container_of(info, struct vnic, dev_info);
+
+	if (vnic->config->use_tx_csum)
+		return sprintf(buf, "true\n");
+	else
+		return sprintf(buf, "false\n");
+}
+
+static DEVICE_ATTR(tx_csum, S_IRUGO, show_tx_csum, NULL);
+
+static ssize_t show_current_path(struct device *dev,
+				 struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info = container_of(dev, struct dev_info, dev);
+	struct vnic *vnic = container_of(info, struct vnic, dev_info);
+
+	if (vnic->current_path == &vnic->primary_path)
+		return sprintf(buf, "primary path\n");
+	else if (vnic->current_path == &vnic->secondary_path)
+		return sprintf(buf, "secondary path\n");
+	else
+		return sprintf(buf, "none\n");
+
+}
+
+static DEVICE_ATTR(current_path, S_IRUGO, show_current_path, NULL);
+
+static struct attribute *vnic_dev_attrs[] = {
+	&dev_attr_vnic_state.attr,
+	&dev_attr_rx_csum.attr,
+	&dev_attr_tx_csum.attr,
+	&dev_attr_current_path.attr,
+	NULL
+};
+
+struct attribute_group vnic_dev_attr_group = {
+	.attrs = vnic_dev_attrs,
+};
+
+static inline void print_dgid(u8 *dgid)
+{
+	int i;
+
+	for (i = 0; i < 16; i += 2)
+		printk("%04x", be16_to_cpu(*(__be16 *)&dgid[i]));
+}
+
+static inline int is_dgid_zero(u8 *dgid)
+{
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		if (dgid[i] != 0)
+			return 1;
+	}
+	return 0;
+}
+
+static int create_netpath(struct netpath *npdest,
+			  struct path_param *p_params)
+{
+	struct viport_config	*viport_config;
+	struct viport		*viport;
+	struct vnic		*vnic;
+	struct list_head	*ptr;
+	int			ret = 0;
+
+	list_for_each(ptr, &vnic_list) {
+		vnic = list_entry(ptr, struct vnic, list_ptrs);
+		if (vnic->primary_path.viport) {
+			viport_config = vnic->primary_path.viport->config;
+			if ((viport_config->ioc_guid == p_params->ioc_guid)
+			    && (viport_config->control_config.vnic_instance
+				== p_params->instance)
+			    && (be64_to_cpu(p_params->ioc_guid))) {
+				SYS_ERROR("GUID %llx,"
+					  " INSTANCE %d already in use\n",
+					  be64_to_cpu(p_params->ioc_guid),
+					  p_params->instance);
+				ret = -EINVAL;
+				goto out;
+			}
+		}
+
+		if (vnic->secondary_path.viport) {
+			viport_config = vnic->secondary_path.viport->config;
+			if ((viport_config->ioc_guid == p_params->ioc_guid)
+			    && (viport_config->control_config.vnic_instance
+				== p_params->instance)
+			    && (be64_to_cpu(p_params->ioc_guid))) {
+				SYS_ERROR("GUID %llx,"
+					  " INSTANCE %d already in use\n",
+					  be64_to_cpu(p_params->ioc_guid),
+					  p_params->instance);
+				ret = -EINVAL;
+				goto out;
+			}
+		}
+	}
+
+	if (npdest->viport) {
+		SYS_ERROR("create_netpath: path already exists\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	viport_config = config_alloc_viport(p_params);
+	if (!viport_config) {
+		SYS_ERROR("create_netpath: failed creating viport config\n");
+		ret = -1;
+		goto out;
+	}
+
+	/*User specified heartbeat value is in 1/100s of a sec*/
+	if (p_params->heartbeat != -1) {
+		viport_config->hb_interval =
+			msecs_to_jiffies(p_params->heartbeat * 10);
+		viport_config->hb_timeout =
+			(p_params->heartbeat << 6) * 10000; /* usec */
+	}
+
+	viport_config->path_idx = 0;
+
+	viport = viport_allocate(viport_config);
+	if (!viport) {
+		SYS_ERROR("create_netpath: failed creating viport\n");
+		kfree(viport_config);
+		ret = -1;
+		goto out;
+	}
+
+	npdest->viport = viport;
+	viport->parent = npdest;
+	viport->vnic = npdest->parent;
+
+	if (is_dgid_zero(p_params->dgid) &&  p_params->ioc_guid != 0
+	   &&  p_params->pkey != 0) {
+		viport_kick(viport);
+		vnic_disconnected(npdest->parent, npdest);
+	} else {
+		printk(KERN_WARNING "Specified parameters IOCGUID=%llx, "
+			"P_Key=%x, DGID=", be64_to_cpu(p_params->ioc_guid),
+			p_params->pkey);
+		print_dgid(p_params->dgid);
+		printk(" insufficient for establishing %s path for interface "
+			"%s. Hence, path will not be established.\n",
+			(npdest->second_bias ? "secondary" : "primary"),
+			p_params->name);
+	}
+out:
+	return ret;
+}
+
+static struct vnic *create_vnic(struct path_param *param)
+{
+	struct vnic_config *vnic_config;
+	struct vnic *vnic;
+	struct list_head *ptr;
+
+	SYS_INFO("create_vnic: name = %s\n", param->name);
+	list_for_each(ptr, &vnic_list) {
+		vnic = list_entry(ptr, struct vnic, list_ptrs);
+		if (!strcmp(vnic->config->name, param->name)) {
+			SYS_ERROR("vnic %s already exists\n",
+				   param->name);
+			return NULL;
+		}
+	}
+
+	vnic_config = config_alloc_vnic();
+	if (!vnic_config) {
+		SYS_ERROR("create_vnic: failed creating vnic config\n");
+		return NULL;
+	}
+
+	if (param->rx_csum != -1)
+		vnic_config->use_rx_csum = param->rx_csum;
+
+	if (param->tx_csum != -1)
+		vnic_config->use_tx_csum = param->tx_csum;
+
+	strcpy(vnic_config->name, param->name);
+	vnic = vnic_allocate(vnic_config);
+	if (!vnic) {
+		SYS_ERROR("create_vnic: failed allocating vnic\n");
+		goto free_vnic_config;
+	}
+
+	init_completion(&vnic->dev_info.released);
+
+	vnic->dev_info.dev.class = NULL;
+	vnic->dev_info.dev.parent = &interface_dev.dev;
+	vnic->dev_info.dev.release = vnic_release_dev;
+	snprintf(vnic->dev_info.dev.bus_id, BUS_ID_SIZE,
+		 vnic_config->name);
+
+	if (device_register(&vnic->dev_info.dev)) {
+		SYS_ERROR("create_vnic: error in registering"
+			  " vnic class dev\n");
+		goto free_vnic;
+	}
+
+	if (sysfs_create_group(&vnic->dev_info.dev.kobj,
+			       &vnic_dev_attr_group)) {
+		SYS_ERROR("create_vnic: error in creating"
+			  "vnic attr group\n");
+		goto err_attr;
+
+	}
+
+	if (vnic_setup_stats_files(vnic))
+		goto err_stats;
+
+	return vnic;
+err_stats:
+	sysfs_remove_group(&vnic->dev_info.dev.kobj,
+			   &vnic_dev_attr_group);
+err_attr:
+	device_unregister(&vnic->dev_info.dev);
+	wait_for_completion(&vnic->dev_info.released);
+free_vnic:
+	list_del(&vnic->list_ptrs);
+	kfree(vnic);
+free_vnic_config:
+	kfree(vnic_config);
+	return NULL;
+}
+
+ssize_t vnic_delete(struct device *dev, struct device_attribute *dev_attr,
+		    const char *buf, size_t count)
+{
+	struct vnic *vnic;
+	struct list_head *ptr;
+	int ret = -EINVAL;
+
+	if (count > IFNAMSIZ) {
+		printk(KERN_WARNING PFX "invalid vnic interface name\n");
+		return ret;
+	}
+
+	SYS_INFO("vnic_delete: name = %s\n", buf);
+	list_for_each(ptr, &vnic_list) {
+		vnic = list_entry(ptr, struct vnic, list_ptrs);
+		if (!strcmp(vnic->config->name, buf)) {
+			vnic_free(vnic);
+			return count;
+		}
+	}
+
+	printk(KERN_WARNING PFX "vnic interface '%s' does not exist\n", buf);
+	return ret;
+}
+
+static ssize_t show_viport_state(struct device *dev,
+				 struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info = container_of(dev, struct dev_info, dev);
+	struct netpath *path = container_of(info, struct netpath, dev_info);
+	switch (path->viport->state) {
+	case VIPORT_DISCONNECTED:
+		return sprintf(buf, "VIPORT_DISCONNECTED\n");
+	case VIPORT_CONNECTED:
+		return sprintf(buf, "VIPORT_CONNECTED\n");
+	default:
+		return sprintf(buf, "INVALID STATE\n");
+	}
+
+}
+
+static DEVICE_ATTR(viport_state, S_IRUGO, show_viport_state, NULL);
+
+static ssize_t show_link_state(struct device *dev,
+			       struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info = container_of(dev, struct dev_info, dev);
+	struct netpath *path = container_of(info, struct netpath, dev_info);
+
+	switch (path->viport->link_state) {
+	case LINK_UNINITIALIZED:
+		return sprintf(buf, "LINK_UNINITIALIZED\n");
+	case LINK_INITIALIZE:
+		return sprintf(buf, "LINK_INITIALIZE\n");
+	case LINK_INITIALIZECONTROL:
+		return sprintf(buf, "LINK_INITIALIZECONTROL\n");
+	case LINK_INITIALIZEDATA:
+		return sprintf(buf, "LINK_INITIALIZEDATA\n");
+	case LINK_CONTROLCONNECT:
+		return sprintf(buf, "LINK_CONTROLCONNECT\n");
+	case LINK_CONTROLCONNECTWAIT:
+		return sprintf(buf, "LINK_CONTROLCONNECTWAIT\n");
+	case LINK_INITVNICREQ:
+		return sprintf(buf, "LINK_INITVNICREQ\n");
+	case LINK_INITVNICRSP:
+		return sprintf(buf, "LINK_INITVNICRSP\n");
+	case LINK_BEGINDATAPATH:
+		return sprintf(buf, "LINK_BEGINDATAPATH\n");
+	case LINK_CONFIGDATAPATHREQ:
+		return sprintf(buf, "LINK_CONFIGDATAPATHREQ\n");
+	case LINK_CONFIGDATAPATHRSP:
+		return sprintf(buf, "LINK_CONFIGDATAPATHRSP\n");
+	case LINK_DATACONNECT:
+		return sprintf(buf, "LINK_DATACONNECT\n");
+	case LINK_DATACONNECTWAIT:
+		return sprintf(buf, "LINK_DATACONNECTWAIT\n");
+	case LINK_XCHGPOOLREQ:
+		return sprintf(buf, "LINK_XCHGPOOLREQ\n");
+	case LINK_XCHGPOOLRSP:
+		return sprintf(buf, "LINK_XCHGPOOLRSP\n");
+	case LINK_INITIALIZED:
+		return sprintf(buf, "LINK_INITIALIZED\n");
+	case LINK_IDLE:
+		return sprintf(buf, "LINK_IDLE\n");
+	case LINK_IDLING:
+		return sprintf(buf, "LINK_IDLING\n");
+	case LINK_CONFIGLINKREQ:
+		return sprintf(buf, "LINK_CONFIGLINKREQ\n");
+	case LINK_CONFIGLINKRSP:
+		return sprintf(buf, "LINK_CONFIGLINKRSP\n");
+	case LINK_CONFIGADDRSREQ:
+		return sprintf(buf, "LINK_CONFIGADDRSREQ\n");
+	case LINK_CONFIGADDRSRSP:
+		return sprintf(buf, "LINK_CONFIGADDRSRSP\n");
+	case LINK_REPORTSTATREQ:
+		return sprintf(buf, "LINK_REPORTSTATREQ\n");
+	case LINK_REPORTSTATRSP:
+		return sprintf(buf, "LINK_REPORTSTATRSP\n");
+	case LINK_HEARTBEATREQ:
+		return sprintf(buf, "LINK_HEARTBEATREQ\n");
+	case LINK_HEARTBEATRSP:
+		return sprintf(buf, "LINK_HEARTBEATRSP\n");
+	case LINK_RESET:
+		return sprintf(buf, "LINK_RESET\n");
+	case LINK_RESETRSP:
+		return sprintf(buf, "LINK_RESETRSP\n");
+	case LINK_RESETCONTROL:
+		return sprintf(buf, "LINK_RESETCONTROL\n");
+	case LINK_RESETCONTROLRSP:
+		return sprintf(buf, "LINK_RESETCONTROLRSP\n");
+	case LINK_DATADISCONNECT:
+		return sprintf(buf, "LINK_DATADISCONNECT\n");
+	case LINK_CONTROLDISCONNECT:
+		return sprintf(buf, "LINK_CONTROLDISCONNECT\n");
+	case LINK_CLEANUPDATA:
+		return sprintf(buf, "LINK_CLEANUPDATA\n");
+	case LINK_CLEANUPCONTROL:
+		return sprintf(buf, "LINK_CLEANUPCONTROL\n");
+	case LINK_DISCONNECTED:
+		return sprintf(buf, "LINK_DISCONNECTED\n");
+	case LINK_RETRYWAIT:
+		return sprintf(buf, "LINK_RETRYWAIT\n");
+	default:
+		return sprintf(buf, "INVALID STATE\n");
+
+	}
+
+}
+static DEVICE_ATTR(link_state, S_IRUGO, show_link_state, NULL);
+
+static ssize_t show_heartbeat(struct device *dev,
+			      struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info = container_of(dev, struct dev_info, dev);
+
+	struct netpath *path = container_of(info, struct netpath, dev_info);
+
+	/* hb_inteval is in jiffies, convert it back to
+	 * 1/100ths of a second
+	 */
+	return sprintf(buf, "%d\n",
+		(jiffies_to_msecs(path->viport->config->hb_interval)/10));
+}
+
+static DEVICE_ATTR(heartbeat, S_IRUGO, show_heartbeat, NULL);
+
+static ssize_t show_ioc_guid(struct device *dev,
+			     struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info = container_of(dev, struct dev_info, dev);
+
+	struct netpath *path = container_of(info, struct netpath, dev_info);
+
+	return sprintf(buf, "%llx\n",
+				__be64_to_cpu(path->viport->config->ioc_guid));
+}
+
+static DEVICE_ATTR(ioc_guid, S_IRUGO, show_ioc_guid, NULL);
+
+static inline void get_dgid_string(u8 *dgid, char *buf)
+{
+	int i;
+	char holder[5];
+
+	for (i = 0; i < 16; i += 2) {
+		sprintf(holder, "%04x", be16_to_cpu(*(__be16 *)&dgid[i]));
+		strcat(buf, holder);
+	}
+
+	strcat(buf, "\n");
+}
+
+static ssize_t show_dgid(struct device *dev,
+			 struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info =	container_of(dev, struct dev_info, dev);
+
+	struct netpath *path = container_of(info, struct netpath, dev_info);
+
+	get_dgid_string(path->viport->config->path_info.path.dgid.raw, buf);
+
+	return strlen(buf);
+}
+
+static DEVICE_ATTR(dgid, S_IRUGO, show_dgid, NULL);
+
+static ssize_t show_pkey(struct device *dev,
+			 struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info =	container_of(dev, struct dev_info, dev);
+
+	struct netpath *path = container_of(info, struct netpath, dev_info);
+
+	return sprintf(buf, "%x\n", path->viport->config->path_info.path.pkey);
+}
+
+static DEVICE_ATTR(pkey, S_IRUGO, show_pkey, NULL);
+
+static ssize_t show_hca_info(struct device *dev,
+			     struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info =	container_of(dev, struct dev_info, dev);
+
+	struct netpath *path = container_of(info, struct netpath, dev_info);
+
+	return sprintf(buf, "vnic-%s-%d\n", path->viport->config->ibdev->name,
+						path->viport->config->port);
+}
+
+static DEVICE_ATTR(hca_info, S_IRUGO, show_hca_info, NULL);
+
+static ssize_t show_ioc_string(struct device *dev,
+			       struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info =	container_of(dev, struct dev_info, dev);
+
+	struct netpath *path = container_of(info, struct netpath, dev_info);
+
+	return sprintf(buf, "%s\n", path->viport->config->ioc_string);
+}
+
+static  DEVICE_ATTR(ioc_string, S_IRUGO, show_ioc_string, NULL);
+
+static ssize_t show_multicast_state(struct device *dev,
+				    struct device_attribute *dev_attr,
+				    char *buf)
+{
+	struct dev_info *info =	container_of(dev, struct dev_info, dev);
+
+	struct netpath *path = container_of(info, struct netpath, dev_info);
+
+	if (!(path->viport->features_supported & VNIC_FEAT_INBOUND_IB_MC))
+		return sprintf(buf, "feature not enabled\n");
+
+	switch (path->viport->mc_info.state) {
+	case MCAST_STATE_INVALID:
+		return sprintf(buf, "state=Invalid\n");
+	case MCAST_STATE_JOINING:
+		return sprintf(buf, "state=Joining MGID:" VNIC_GID_FMT "\n",
+			VNIC_GID_RAW_ARG(path->viport->mc_info.mgid.raw));
+	case MCAST_STATE_ATTACHING:
+		return sprintf(buf, "state=Attaching MGID:" VNIC_GID_FMT
+			" MLID:%X\n",
+			VNIC_GID_RAW_ARG(path->viport->mc_info.mgid.raw),
+			path->viport->mc_info.mlid);
+	case MCAST_STATE_JOINED_ATTACHED:
+		return sprintf(buf,
+			"state=Joined & Attached MGID:" VNIC_GID_FMT
+			" MLID:%X\n",
+			VNIC_GID_RAW_ARG(path->viport->mc_info.mgid.raw),
+			path->viport->mc_info.mlid);
+	case MCAST_STATE_DETACHING:
+		return sprintf(buf, "state=Detaching MGID: " VNIC_GID_FMT "\n",
+			VNIC_GID_RAW_ARG(path->viport->mc_info.mgid.raw));
+	case MCAST_STATE_RETRIED:
+		return sprintf(buf, "state=Retries Exceeded\n");
+	}
+	return sprintf(buf, "invalid state\n");
+}
+
+static  DEVICE_ATTR(multicast_state, S_IRUGO, show_multicast_state, NULL);
+
+static struct attribute *vnic_path_attrs[] = {
+	&dev_attr_viport_state.attr,
+	&dev_attr_link_state.attr,
+	&dev_attr_heartbeat.attr,
+	&dev_attr_ioc_guid.attr,
+	&dev_attr_dgid.attr,
+	&dev_attr_pkey.attr,
+	&dev_attr_hca_info.attr,
+	&dev_attr_ioc_string.attr,
+	&dev_attr_multicast_state.attr,
+	NULL
+};
+
+struct attribute_group vnic_path_attr_group = {
+	.attrs = vnic_path_attrs,
+};
+
+
+static int setup_path_class_files(struct netpath *path, char *name)
+{
+	init_completion(&path->dev_info.released);
+
+	path->dev_info.dev.class = NULL;
+	path->dev_info.dev.parent = &path->parent->dev_info.dev;
+	path->dev_info.dev.release = vnic_release_dev;
+	snprintf(path->dev_info.dev.bus_id, BUS_ID_SIZE, name);
+
+	if (device_register(&path->dev_info.dev)) {
+		SYS_ERROR("error in registering path class dev\n");
+		goto out;
+	}
+
+	if (sysfs_create_group(&path->dev_info.dev.kobj,
+			       &vnic_path_attr_group)) {
+		SYS_ERROR("error in creating vnic path group attrs");
+		goto err_path;
+	}
+
+	return 0;
+
+err_path:
+	device_unregister(&path->dev_info.dev);
+	wait_for_completion(&path->dev_info.released);
+out:
+	return -1;
+
+}
+
+static inline void update_dgids(u8 *old, u8 *new, char *vnic_name,
+				char *path_name)
+{
+	int i;
+
+	if (!memcmp(old, new, 16))
+		return;
+
+	printk(KERN_INFO PFX "Changing dgid from 0x");
+	print_dgid(old);
+	printk(" to 0x");
+	print_dgid(new);
+	printk(" for %s path of %s\n", path_name, vnic_name);
+	for (i = 0; i < 16; i++)
+		old[i] = new[i];
+}
+
+static inline void update_ioc_guids(struct path_param *params,
+				    struct netpath *path,
+				    char *vnic_name, char *path_name)
+{
+	u64 sid;
+
+	if (path->viport->config->ioc_guid == params->ioc_guid)
+		return;
+
+	printk(KERN_INFO PFX "Changing IOC GUID from 0x%llx to 0x%llx "
+			 "for %s path of %s\n",
+			 __be64_to_cpu(path->viport->config->ioc_guid),
+			 __be64_to_cpu(params->ioc_guid), path_name, vnic_name);
+
+	path->viport->config->ioc_guid = params->ioc_guid;
+
+	sid = (SST_AGN << 56) | (SST_OUI << 32) | (CONTROL_PATH_ID << 8)
+				| IOC_NUMBER(be64_to_cpu(params->ioc_guid));
+
+	path->viport->config->control_config.ib_config.service_id =
+							 cpu_to_be64(sid);
+
+	sid = (SST_AGN << 56) | (SST_OUI << 32) | (DATA_PATH_ID << 8)
+				| IOC_NUMBER(be64_to_cpu(params->ioc_guid));
+
+	path->viport->config->data_config.ib_config.service_id =
+							 cpu_to_be64(sid);
+}
+
+static inline void update_pkeys(__be16 *old, __be16 *new, char *vnic_name,
+				char *path_name)
+{
+	if (*old == *new)
+		return;
+
+	printk(KERN_INFO PFX "Changing P_Key from 0x%x to 0x%x "
+			 "for %s path of %s\n", *old, *new,
+			 path_name, vnic_name);
+	*old = *new;
+}
+
+static void update_ioc_strings(struct path_param *params, struct netpath *path,
+								char *path_name)
+{
+	if (!strcmp(params->ioc_string, path->viport->config->ioc_string))
+		return;
+
+	printk(KERN_INFO PFX "Changing ioc_string to %s for %s path of %s\n",
+				params->ioc_string, path_name, params->name);
+
+	strcpy(path->viport->config->ioc_string, params->ioc_string);
+}
+
+static void update_path_parameters(struct path_param *params,
+				   struct netpath *path)
+{
+	update_dgids(path->viport->config->path_info.path.dgid.raw,
+		params->dgid, params->name,
+		(path->second_bias ? "secondary" : "primary"));
+
+	update_ioc_guids(params, path, params->name,
+		(path->second_bias ? "secondary" : "primary"));
+
+	update_pkeys(&path->viport->config->path_info.path.pkey,
+		&params->pkey, params->name,
+		(path->second_bias ? "secondary" : "primary"));
+
+	update_ioc_strings(params, path,
+		(path->second_bias ? "secondary" : "primary"));
+}
+
+static ssize_t update_params_and_connect(struct path_param *params,
+					 struct netpath *path, size_t count)
+{
+	if (is_dgid_zero(params->dgid) && params->ioc_guid != 0 &&
+	    params->pkey != 0) {
+
+		if (!memcmp(path->viport->config->path_info.path.dgid.raw,
+			params->dgid, 16) &&
+		    params->ioc_guid == path->viport->config->ioc_guid &&
+		    params->pkey     == path->viport->config->path_info.path.pkey) {
+
+			printk(KERN_WARNING PFX "All of the dgid, ioc_guid and "
+						"pkeys are same as the existing"
+						" one. Not updating values.\n");
+			return -EINVAL;
+		} else {
+			if (path->viport->state == VIPORT_CONNECTED) {
+				printk(KERN_WARNING PFX "%s path of %s "
+					"interface is already in connected "
+					"state. Not updating values.\n",
+				(path->second_bias ? "Secondary" : "Primary"),
+				path->parent->config->name);
+				return -EINVAL;
+			} else {
+				update_path_parameters(params, path);
+				viport_kick(path->viport);
+				vnic_disconnected(path->parent, path);
+				return count;
+			}
+		}
+	} else {
+		printk(KERN_WARNING PFX "Either dgid, iocguid, pkey is zero. "
+					"No update.\n");
+		return -EINVAL;
+	}
+}
+
+ssize_t vnic_create_primary(struct device *dev,
+			    struct device_attribute *dev_attr, const char *buf,
+			    size_t count)
+{
+	struct dev_info *info = container_of(dev, struct dev_info, dev);
+	struct vnic_ib_port *target =
+	    container_of(info, struct vnic_ib_port, pdev_info);
+
+	struct path_param param;
+	int ret = -EINVAL;
+	struct vnic *vnic;
+	struct list_head    *ptr;
+
+	param.instance = 0;
+	param.rx_csum = -1;
+	param.tx_csum = -1;
+	param.heartbeat = -1;
+	param.ib_multicast = -1;
+	*param.ioc_string = '\0';
+
+	ret = vnic_parse_options(buf, &param);
+
+	if (ret)
+		goto out;
+
+	list_for_each(ptr, &vnic_list) {
+		vnic = list_entry(ptr, struct vnic, list_ptrs);
+		if (!strcmp(vnic->config->name, param.name)) {
+			ret = update_params_and_connect(&param,
+							&vnic->primary_path,
+							count);
+			goto out;
+		}
+	 }
+
+	param.ibdev = target->dev->dev;
+	param.ibport = target;
+	param.port = target->port_num;
+
+	vnic = create_vnic(&param);
+	if (!vnic) {
+		printk(KERN_ERR PFX "creating vnic failed\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (create_netpath(&vnic->primary_path, &param)) {
+		printk(KERN_ERR PFX "creating primary netpath failed\n");
+		goto free_vnic;
+	}
+
+	if (setup_path_class_files(&vnic->primary_path, "primary_path"))
+		goto free_vnic;
+
+	if (vnic && !vnic->primary_path.viport) {
+		printk(KERN_ERR PFX "no valid netpaths\n");
+		goto free_vnic;
+	}
+
+	return count;
+
+free_vnic:
+	vnic_free(vnic);
+	ret = -EINVAL;
+out:
+	return ret;
+}
+
+ssize_t vnic_create_secondary(struct device *dev,
+			      struct device_attribute *dev_attr,
+			      const char *buf, size_t count)
+{
+	struct dev_info *info = container_of(dev, struct dev_info, dev);
+	struct vnic_ib_port *target =
+	    container_of(info, struct vnic_ib_port, pdev_info);
+
+	struct path_param param;
+	struct vnic *vnic = NULL;
+	int ret = -EINVAL;
+	struct list_head *ptr;
+	int found = 0;
+
+	param.instance = 0;
+	param.rx_csum = -1;
+	param.tx_csum = -1;
+	param.heartbeat = -1;
+	param.ib_multicast = -1;
+	*param.ioc_string = '\0';
+
+	ret = vnic_parse_options(buf, &param);
+
+	if (ret)
+		goto out;
+
+	list_for_each(ptr, &vnic_list) {
+		vnic = list_entry(ptr, struct vnic, list_ptrs);
+		if (!strncmp(vnic->config->name, param.name, IFNAMSIZ)) {
+			if (vnic->secondary_path.viport) {
+				ret = update_params_and_connect(&param,
+								&vnic->secondary_path,
+								count);
+				goto out;
+			}
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found) {
+		printk(KERN_ERR PFX
+		       "primary connection with name '%s' does not exist\n",
+		       param.name);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	param.ibdev = target->dev->dev;
+	param.ibport = target;
+	param.port = target->port_num;
+
+	if (create_netpath(&vnic->secondary_path, &param)) {
+		printk(KERN_ERR PFX "creating secondary netpath failed\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (setup_path_class_files(&vnic->secondary_path, "secondary_path"))
+		goto free_vnic;
+
+	return count;
+
+free_vnic:
+	vnic_free(vnic);
+	ret = -EINVAL;
+out:
+	return ret;
+}
diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_sys.h b/drivers/infiniband/ulp/qlgc_vnic/vnic_sys.h
new file mode 100644
index 0000000..b41e770
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_sys.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2006 QLogic, 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 VNIC_SYS_H_INCLUDED
+#define VNIC_SYS_H_INCLUDED
+
+struct dev_info {
+	struct device		dev;
+	struct completion	released;
+};
+
+extern struct class vnic_class;
+extern struct dev_info interface_dev;
+extern struct attribute_group vnic_dev_attr_group;
+extern struct attribute_group vnic_path_attr_group;
+extern struct device_attribute dev_attr_create_primary;
+extern struct device_attribute dev_attr_create_secondary;
+extern struct device_attribute dev_attr_delete_vnic;
+
+extern void vnic_release_dev(struct device *dev);
+
+extern ssize_t vnic_create_primary(struct device *dev,
+				   struct device_attribute *dev_attr,
+				   const char *buf, size_t count);
+
+extern ssize_t vnic_create_secondary(struct device *dev,
+				     struct device_attribute *dev_attr,
+				     const char *buf, size_t count);
+
+extern ssize_t vnic_delete(struct device *dev,
+			   struct device_attribute *dev_attr,
+			   const char *buf, size_t count);
+#endif	/*VNIC_SYS_H_INCLUDED*/

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [ofa-general] [PATCH 09/13] QLogic VNIC: IB Multicast for Ethernet broadcast/multicast
  2008-04-30 17:15 [ofa-general] [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Ramachandra K
                   ` (7 preceding siblings ...)
  2008-04-30 17:19 ` [ofa-general] [PATCH 08/13] QLogic VNIC: sysfs interface implementation for " Ramachandra K
@ 2008-04-30 17:20 ` Ramachandra K
  2008-05-15 22:38   ` Roland Dreier
  2008-04-30 17:20 ` [ofa-general] [PATCH 10/13] QLogic VNIC: Driver Statistics collection Ramachandra K
                   ` (4 subsequent siblings)
  13 siblings, 1 reply; 32+ messages in thread
From: Ramachandra K @ 2008-04-30 17:20 UTC (permalink / raw)
  To: rdreier, general, netdev; +Cc: poornima.kamath, amar.mudrankit

From: Usha Srinivasan <usha.srinivasan@qlogic.com>

Implementation of ethernet broadcasting and multicasting for QLogic
VNIC interface by making use of underlying IB multicasting. 

Signed-off-by: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>
Signed-off-by: Poornima Kamath <poornima.kamath@qlogic.com>
Signed-off-by: Amar Mudrankit <amar.mudrankit@qlogic.com>
---

 drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.c |  332 +++++++++++++++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.h |   76 +++++
 2 files changed, 408 insertions(+), 0 deletions(-)
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.h

diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.c b/drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.c
new file mode 100644
index 0000000..044d447
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2008 QLogic, 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/net.h>
+#include <linux/netdevice.h>
+#include <linux/jiffies.h>
+#include <rdma/ib_sa.h>
+#include "vnic_viport.h"
+#include "vnic_netpath.h"
+#include "vnic_main.h"
+#include "vnic_config.h"
+#include "vnic_control.h"
+#include "vnic_util.h"
+#include "vnic_ib.h"
+
+#define control_ifcfg_name(p)\
+   ((p)->parent->parent->parent->config->name)
+
+#define SET_MCAST_STATE_INVALID \
+do { \
+	viport->mc_info.state = MCAST_STATE_INVALID; \
+	viport->mc_info.mc = NULL; \
+	memset(&viport->mc_info.mgid, 0, sizeof(union ib_gid)); \
+} while (0);
+
+int vnic_mc_init(struct viport *viport)
+{
+	MCAST_FUNCTION("vnic_mc_init %p\n", viport);
+	SET_MCAST_STATE_INVALID;
+	viport->mc_info.retries = 0;
+	spin_lock_init(&viport->mc_info.lock);
+
+	return 0;
+}
+
+void vnic_mc_uninit(struct viport *viport)
+{
+	unsigned long flags;
+	MCAST_FUNCTION("vnic_mc_uninit %p\n", viport);
+
+	spin_lock_irqsave(&viport->mc_info.lock, flags);
+	if ((viport->mc_info.state != MCAST_STATE_INVALID) &&
+	    (viport->mc_info.state != MCAST_STATE_RETRIED)) {
+		MCAST_ERROR("%s mcast state is not INVALID or RETRIED %d\n",
+				control_ifcfg_name(&viport->control),
+				viport->mc_info.state);
+	}
+	spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+	MCAST_FUNCTION("vnic_mc_uninit done\n");
+}
+
+
+/* This function is called when NEED_MCAST_COMPLETION is set.
+ * It finishes off the join multicast work.
+ */
+int vnic_mc_join_handle_completion(struct viport *viport)
+{
+	unsigned long flags;
+	unsigned int ret = 0;
+
+	MCAST_FUNCTION("in vnic_mc_join_handle_completion\n");
+	spin_lock_irqsave(&viport->mc_info.lock, flags);
+	if (viport->mc_info.state != MCAST_STATE_JOINING) {
+		MCAST_ERROR("%s unexpected mcast state in handle_completion: "
+				" %d\n", control_ifcfg_name(&viport->control),
+				viport->mc_info.state);
+		spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+		return -1;
+	}
+	viport->mc_info.state = MCAST_STATE_ATTACHING;
+	spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+	MCAST_INFO("%s calling ib_attach_mcast %lx mgid:"
+			VNIC_GID_FMT " mlid:%x\n",
+			control_ifcfg_name(&viport->control), jiffies,
+			VNIC_GID_RAW_ARG(viport->mc_info.mgid.raw),
+					 viport->mc_info.mlid);
+	ret = ib_attach_mcast(viport->mc_data.ib_conn.qp, &viport->mc_info.mgid,
+			viport->mc_info.mlid);
+	if (ret) {
+		MCAST_ERROR("%s attach mcast qp failed %d\n",
+				control_ifcfg_name(&viport->control), ret);
+		return -1;
+	}
+	MCAST_INFO("%s attached\n",
+			control_ifcfg_name(&viport->control));
+	spin_lock_irqsave(&viport->mc_info.lock, flags);
+	viport->mc_info.state = MCAST_STATE_JOINED_ATTACHED;
+	MCAST_INFO("%s qp attached to mcast group\n",
+			control_ifcfg_name(&viport->control));
+	spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+	return 0;
+}
+
+/* NOTE: ib_sa.h says "returning a non-zero value from this callback will
+ * result in destroying the multicast tracking structure.
+ */
+static int vnic_mc_join_complete(int status,
+				struct ib_sa_multicast *multicast)
+{
+	struct viport *viport = (struct viport *)multicast->context;
+	unsigned long flags;
+
+	MCAST_FUNCTION("in vnic_mc_join_complete status:%x\n", status);
+	if (status) {
+		spin_lock_irqsave(&viport->mc_info.lock, flags);
+		if (status == -ENETRESET) {
+			SET_MCAST_STATE_INVALID;
+			viport->mc_info.retries = 0;
+			spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+			MCAST_ERROR("%s got ENETRESET what's the right thing "
+					"to do?\n",
+					control_ifcfg_name(&viport->control));
+			return status;
+		}
+		/* perhaps the mcgroup hasn't yet been created - retry */
+		viport->mc_info.retries++;
+		viport->mc_info.mc = NULL;
+		if (viport->mc_info.retries > MAX_MCAST_JOIN_RETRIES) {
+			viport->mc_info.state = MCAST_STATE_RETRIED;
+			spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+			MCAST_ERROR("%s join failed 0x%x - max retries:%d "
+					"exceeded\n",
+					control_ifcfg_name(&viport->control),
+					status, viport->mc_info.retries);
+		} else {
+			viport->mc_info.state = MCAST_STATE_INVALID;
+			spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+			spin_lock_irqsave(&viport->lock, flags);
+			viport->updates |= NEED_MCAST_JOIN;
+			spin_unlock_irqrestore(&viport->lock, flags);
+			viport_kick(viport);
+			MCAST_ERROR("%s join failed 0x%x - retrying; "
+					"retries:%d\n",
+					control_ifcfg_name(&viport->control),
+					status, viport->mc_info.retries);
+		}
+		return status;
+	}
+
+	/* finish join work from main state loop for viport - in case
+	 * the work itself cannot be done in a callback environment */
+	spin_lock_irqsave(&viport->lock, flags);
+	viport->mc_info.mlid = be16_to_cpu(multicast->rec.mlid);
+	viport->updates |= NEED_MCAST_COMPLETION;
+	spin_unlock_irqrestore(&viport->lock, flags);
+	viport_kick(viport);
+	MCAST_INFO("%s set NEED_MCAST_COMPLETION %x %x\n",
+			control_ifcfg_name(&viport->control),
+			multicast->rec.mlid, viport->mc_info.mlid);
+	return 0;
+}
+
+void vnic_mc_join_setup(struct viport *viport, union ib_gid *mgid)
+{
+	unsigned long flags;
+
+	MCAST_FUNCTION("in vnic_mc_join_setup\n");
+	spin_lock_irqsave(&viport->mc_info.lock, flags);
+	if (viport->mc_info.state != MCAST_STATE_INVALID) {
+		if (viport->mc_info.state == MCAST_STATE_DETACHING) {
+			MCAST_ERROR("%s detach in progress\n",
+					control_ifcfg_name(&viport->control));
+		} else if (viport->mc_info.state == MCAST_STATE_RETRIED) {
+			MCAST_ERROR("%s max join retries exceeded\n",
+					control_ifcfg_name(&viport->control));
+		} else {
+			/* join/attach in progress or done */
+			/* verify that the current mgid is same as prev mgid */
+			if (memcmp(mgid, &viport->mc_info.mgid, sizeof(union ib_gid)) != 0) {
+				/* Separate MGID for each IOC */
+				MCAST_ERROR("%s Multicast Group MGIDs not "
+					"unique; mgids: " VNIC_GID_FMT
+					 " " VNIC_GID_FMT "\n",
+					control_ifcfg_name(&viport->control),
+					VNIC_GID_RAW_ARG(mgid->raw),
+					VNIC_GID_RAW_ARG(viport->mc_info.mgid.raw));
+			} else
+				MCAST_INFO("%s join already issued: %d\n",
+					control_ifcfg_name(&viport->control),
+					viport->mc_info.state);
+
+		}
+		spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+		return;
+	}
+	viport->mc_info.mgid = *mgid;
+	spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+	spin_lock_irqsave(&viport->lock, flags);
+	viport->updates |= NEED_MCAST_JOIN;
+	spin_unlock_irqrestore(&viport->lock, flags);
+	viport_kick(viport);
+	MCAST_INFO("%s set NEED_MCAST_JOIN \n",
+			control_ifcfg_name(&viport->control));
+}
+
+int vnic_mc_join(struct viport *viport)
+{
+	struct ib_sa_mcmember_rec rec;
+	ib_sa_comp_mask comp_mask;
+	unsigned long flags;
+
+	MCAST_FUNCTION("in vnic_mc_join\n");
+	if (!viport->mc_data.ib_conn.qp) {
+		MCAST_ERROR("%s qp is NULL\n",
+				control_ifcfg_name(&viport->control));
+		return -1;
+	}
+	spin_lock_irqsave(&viport->mc_info.lock, flags);
+	if (viport->mc_info.state != MCAST_STATE_INVALID) {
+		MCAST_INFO("%s join already issued: %d\n",
+				control_ifcfg_name(&viport->control),
+				viport->mc_info.state);
+		spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+		return 0;
+	}
+	viport->mc_info.state = MCAST_STATE_JOINING;
+	spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+
+	memset(&rec, 0, sizeof(rec));
+	rec.join_state = 2; /* bit 1 is Nonmember */
+	rec.mgid = viport->mc_info.mgid;
+	rec.port_gid = viport->config->path_info.path.sgid;
+
+	comp_mask = 	IB_SA_MCMEMBER_REC_MGID     |
+			IB_SA_MCMEMBER_REC_PORT_GID |
+			IB_SA_MCMEMBER_REC_JOIN_STATE;
+
+	MCAST_INFO("%s calling ib_sa_join_multicast %lx mgid:"
+			VNIC_GID_FMT " port_gid: " VNIC_GID_FMT "\n",
+			control_ifcfg_name(&viport->control), jiffies,
+			VNIC_GID_RAW_ARG(rec.mgid.raw),
+			VNIC_GID_RAW_ARG(rec.port_gid.raw));
+
+	viport->mc_info.mc = ib_sa_join_multicast(&vnic_sa_client,
+			viport->config->ibdev, viport->config->port,
+			&rec, comp_mask, GFP_KERNEL,
+			vnic_mc_join_complete, viport);
+
+	if (IS_ERR(viport->mc_info.mc)) {
+		MCAST_ERROR("%s ib_sa_join_multicast failed " VNIC_GID_FMT
+				".\n",
+				control_ifcfg_name(&viport->control),
+				VNIC_GID_RAW_ARG(rec.mgid.raw));
+		spin_lock_irqsave(&viport->mc_info.lock, flags);
+		viport->mc_info.state = MCAST_STATE_INVALID;
+		spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+		return -1;
+	}
+	MCAST_INFO("%s join issued ib_sa_join_multicast mgid:"
+			VNIC_GID_FMT " port_gid: " VNIC_GID_FMT "\n",
+			control_ifcfg_name(&viport->control),
+			VNIC_GID_RAW_ARG(rec.mgid.raw),
+			VNIC_GID_RAW_ARG(rec.port_gid.raw));
+
+	return 0;
+}
+
+void vnic_mc_leave(struct viport *viport)
+{
+	unsigned long flags;
+	unsigned int ret;
+	struct ib_sa_multicast *mc;
+
+	MCAST_FUNCTION("vnic_mc_leave \n");
+
+	spin_lock_irqsave(&viport->mc_info.lock, flags);
+	if ((viport->mc_info.state == MCAST_STATE_INVALID) ||
+	    (viport->mc_info.state == MCAST_STATE_RETRIED)) {
+		spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+		return;
+	}
+
+	if (viport->mc_info.state == MCAST_STATE_JOINED_ATTACHED) {
+
+		viport->mc_info.state = MCAST_STATE_DETACHING;
+		spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+		ret = ib_detach_mcast(viport->mc_data.ib_conn.qp,
+					 &viport->mc_info.mgid,
+					viport->mc_info.mlid);
+		if (ret) {
+			MCAST_ERROR("%s detach failed %d\n",
+				control_ifcfg_name(&viport->control), ret);
+			return;
+		}
+		MCAST_INFO("%s detached succesfully\n",
+				control_ifcfg_name(&viport->control));
+		spin_lock_irqsave(&viport->mc_info.lock, flags);
+	}
+	mc = viport->mc_info.mc;
+	SET_MCAST_STATE_INVALID;
+	viport->mc_info.retries = 0;
+	spin_unlock_irqrestore(&viport->mc_info.lock, flags);
+
+	if (mc) {
+		MCAST_INFO("%s calling ib_sa_free_multicast\n",
+				control_ifcfg_name(&viport->control));
+		ib_sa_free_multicast(mc);
+	}
+	MCAST_FUNCTION("vnic_mc_leave done\n");
+	return;
+}
+
+
diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.h b/drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.h
new file mode 100644
index 0000000..0e5499d
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_multicast.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2008 QLogic, 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 __VNIC_MULTICAST_H__
+#define __VNIC_MULTTCAST_H__
+
+enum {
+	MCAST_STATE_INVALID         = 0x00, /* join not attempted or failed */
+	MCAST_STATE_JOINING         = 0x01, /* join mcgroup in progress */
+	MCAST_STATE_ATTACHING       = 0x02, /* join completed with success,
+					     * attach qp to mcgroup in progress
+					     */
+	MCAST_STATE_JOINED_ATTACHED = 0x03, /* join completed with success */
+	MCAST_STATE_DETACHING       = 0x04, /* detach qp in progress */
+	MCAST_STATE_RETRIED         = 0x05, /* retried join and failed */
+};
+
+#define MAX_MCAST_JOIN_RETRIES 	       5 /* used to retry join */
+
+struct mc_info {
+	u8  			state;
+	spinlock_t 		lock;
+	union ib_gid 		mgid;
+	u16 			mlid;
+	struct ib_sa_multicast 	*mc;
+	u8 			retries;
+};
+
+
+int vnic_mc_init(struct viport *viport);
+void vnic_mc_uninit(struct viport *viport);
+
+/* This function is called when a viport gets a multicast mgid from EVIC
+   and must join the multicast group. It sets up NEED_MCAST_JOIN flag, which
+   results in vnic_mc_join being called later. */
+void vnic_mc_join_setup(struct viport *viport, union ib_gid *mgid);
+
+/* This function is called when NEED_MCAST_JOIN flag is set. */
+int vnic_mc_join(struct viport *viport);
+
+/* This function is called when NEED_MCAST_COMPLETION is set.
+   It finishes off the join multicast work. */
+int vnic_mc_join_handle_completion(struct viport *viport);
+
+void vnic_mc_leave(struct viport *viport);
+
+#endif /* __VNIC_MULTICAST_H__ */

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [ofa-general] [PATCH 10/13] QLogic VNIC: Driver Statistics collection
  2008-04-30 17:15 [ofa-general] [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Ramachandra K
                   ` (8 preceding siblings ...)
  2008-04-30 17:20 ` [ofa-general] [PATCH 09/13] QLogic VNIC: IB Multicast for Ethernet broadcast/multicast Ramachandra K
@ 2008-04-30 17:20 ` Ramachandra K
  2008-05-15 22:33   ` Roland Dreier
  2008-04-30 17:21 ` [PATCH 11/13] QLogic VNIC: Driver utility file - implements various utility macros Ramachandra K
                   ` (3 subsequent siblings)
  13 siblings, 1 reply; 32+ messages in thread
From: Ramachandra K @ 2008-04-30 17:20 UTC (permalink / raw)
  To: rdreier, general, netdev; +Cc: poornima.kamath, amar.mudrankit

From: Amar Mudrankit <amar.mudrankit@qlogic.com>

Collection of statistics about QLogic VNIC interfaces is implemented
in this patch.

Signed-off-by: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>
Signed-off-by: Poornima Kamath <poornima.kamath@qlogic.com>
---

 drivers/infiniband/ulp/qlgc_vnic/vnic_stats.c |  234 ++++++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_stats.h |  497 +++++++++++++++++++++++++
 2 files changed, 731 insertions(+), 0 deletions(-)
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_stats.c
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_stats.h

diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_stats.c b/drivers/infiniband/ulp/qlgc_vnic/vnic_stats.c
new file mode 100644
index 0000000..cebcc26
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_stats.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2006 QLogic, 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/types.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+
+#include "vnic_main.h"
+
+cycles_t recv_ref;
+
+/*
+ * TODO: Statistics reporting for control path, data path,
+ *       RDMA times, IOs etc
+ *
+ */
+static ssize_t show_lifetime(struct device *dev,
+			     struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info =	container_of(dev, struct dev_info, dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+	cycles_t time = get_cycles() - vnic->statistics.start_time;
+
+	return sprintf(buf, "%llu\n", (unsigned long long)time);
+}
+
+static DEVICE_ATTR(lifetime, S_IRUGO, show_lifetime, NULL);
+
+static ssize_t show_conntime(struct device *dev,
+			     struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info =	container_of(dev, struct dev_info, dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+
+	if (vnic->statistics.conn_time)
+		return sprintf(buf, "%llu\n",
+			   (unsigned long long)vnic->statistics.conn_time);
+	return 0;
+}
+
+static DEVICE_ATTR(connection_time, S_IRUGO, show_conntime, NULL);
+
+static ssize_t show_disconnects(struct device *dev,
+				struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info =	container_of(dev, struct dev_info, dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+	u32 num;
+
+	if (vnic->statistics.disconn_ref)
+		num = vnic->statistics.disconn_num + 1;
+	else
+		num = vnic->statistics.disconn_num;
+
+	return sprintf(buf, "%d\n", num);
+}
+
+static DEVICE_ATTR(disconnects, S_IRUGO, show_disconnects, NULL);
+
+static ssize_t show_total_disconn_time(struct device *dev,
+				       struct device_attribute *dev_attr,
+				       char *buf)
+{
+	struct dev_info *info = container_of(dev, struct dev_info, dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+	cycles_t time;
+
+	if (vnic->statistics.disconn_ref)
+		time = vnic->statistics.disconn_time +
+		       get_cycles() - vnic->statistics.disconn_ref;
+	else
+		time = vnic->statistics.disconn_time;
+
+	return sprintf(buf, "%llu\n", (unsigned long long)time);
+}
+
+static DEVICE_ATTR(total_disconn_time, S_IRUGO, show_total_disconn_time, NULL);
+
+static ssize_t show_carrier_losses(struct device *dev,
+				   struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info =	container_of(dev, struct dev_info, dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+	u32 num;
+
+	if (vnic->statistics.carrier_ref)
+		num = vnic->statistics.carrier_off_num + 1;
+	else
+		num = vnic->statistics.carrier_off_num;
+
+	return sprintf(buf, "%d\n", num);
+}
+
+static DEVICE_ATTR(carrier_losses, S_IRUGO, show_carrier_losses, NULL);
+
+static ssize_t show_total_carr_loss_time(struct device *dev,
+					 struct device_attribute *dev_attr,
+					 char *buf)
+{
+	struct dev_info *info =	container_of(dev, struct dev_info, dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+	cycles_t time;
+
+	if (vnic->statistics.carrier_ref)
+		time = vnic->statistics.carrier_off_time +
+		       get_cycles() - vnic->statistics.carrier_ref;
+	else
+		time = vnic->statistics.carrier_off_time;
+
+	return sprintf(buf, "%llu\n", (unsigned long long)time);
+}
+
+static DEVICE_ATTR(total_carrier_loss_time, S_IRUGO,
+			 show_total_carr_loss_time, NULL);
+
+static ssize_t show_total_recv_time(struct device *dev,
+				    struct device_attribute *dev_attr,
+				    char *buf)
+{
+	struct dev_info *info =	container_of(dev, struct dev_info, dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+
+	return sprintf(buf, "%llu\n",
+		       (unsigned long long)vnic->statistics.recv_time);
+}
+
+static DEVICE_ATTR(total_recv_time, S_IRUGO, show_total_recv_time, NULL);
+
+static ssize_t show_recvs(struct device *dev,
+			  struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info =	container_of(dev, struct dev_info, dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+
+	return sprintf(buf, "%d\n", vnic->statistics.recv_num);
+}
+
+static DEVICE_ATTR(recvs, S_IRUGO, show_recvs, NULL);
+
+static ssize_t show_multicast_recvs(struct device *dev,
+				    struct device_attribute *dev_attr,
+				    char *buf)
+{
+	struct dev_info *info =	container_of(dev, struct dev_info, dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+
+	return sprintf(buf, "%d\n", vnic->statistics.multicast_recv_num);
+}
+
+static DEVICE_ATTR(multicast_recvs, S_IRUGO, show_multicast_recvs, NULL);
+
+static ssize_t show_total_xmit_time(struct device *dev,
+				    struct device_attribute *dev_attr,
+				    char *buf)
+{
+	struct dev_info *info =	container_of(dev, struct dev_info, dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+
+	return sprintf(buf, "%llu\n",
+		       (unsigned long long)vnic->statistics.xmit_time);
+}
+
+static DEVICE_ATTR(total_xmit_time, S_IRUGO, show_total_xmit_time, NULL);
+
+static ssize_t show_xmits(struct device *dev,
+			  struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info =	container_of(dev, struct dev_info, dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+
+	return sprintf(buf, "%d\n", vnic->statistics.xmit_num);
+}
+
+static DEVICE_ATTR(xmits, S_IRUGO, show_xmits, NULL);
+
+static ssize_t show_failed_xmits(struct device *dev,
+				 struct device_attribute *dev_attr, char *buf)
+{
+	struct dev_info *info =	container_of(dev, struct dev_info, dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+
+	return sprintf(buf, "%d\n", vnic->statistics.xmit_fail);
+}
+
+static DEVICE_ATTR(failed_xmits, S_IRUGO, show_failed_xmits, NULL);
+
+static struct attribute *vnic_stats_attrs[] = {
+	&dev_attr_lifetime.attr,
+	&dev_attr_xmits.attr,
+	&dev_attr_total_xmit_time.attr,
+	&dev_attr_failed_xmits.attr,
+	&dev_attr_recvs.attr,
+	&dev_attr_multicast_recvs.attr,
+	&dev_attr_total_recv_time.attr,
+	&dev_attr_connection_time.attr,
+	&dev_attr_disconnects.attr,
+	&dev_attr_total_disconn_time.attr,
+	&dev_attr_carrier_losses.attr,
+	&dev_attr_total_carrier_loss_time.attr,
+	NULL
+};
+
+struct attribute_group vnic_stats_attr_group = {
+	.attrs = vnic_stats_attrs,
+};
diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_stats.h b/drivers/infiniband/ulp/qlgc_vnic/vnic_stats.h
new file mode 100644
index 0000000..af77794
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_stats.h
@@ -0,0 +1,497 @@
+/*
+ * Copyright (c) 2006 QLogic, 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 VNIC_STATS_H_INCLUDED
+#define VNIC_STATS_H_INCLUDED
+
+#include "vnic_main.h"
+#include "vnic_ib.h"
+#include "vnic_sys.h"
+
+#ifdef CONFIG_INFINIBAND_QLGC_VNIC_STATS
+
+static inline void vnic_connected_stats(struct vnic *vnic)
+{
+	if (vnic->statistics.conn_time == 0) {
+		vnic->statistics.conn_time =
+		    get_cycles() - vnic->statistics.start_time;
+	}
+
+	if (vnic->statistics.disconn_ref != 0) {
+		vnic->statistics.disconn_time +=
+		    get_cycles() - vnic->statistics.disconn_ref;
+		vnic->statistics.disconn_num++;
+		vnic->statistics.disconn_ref = 0;
+	}
+
+}
+
+static inline void vnic_stop_xmit_stats(struct vnic *vnic)
+{
+	if (vnic->statistics.xmit_ref == 0)
+		vnic->statistics.xmit_ref = get_cycles();
+}
+
+static inline void vnic_restart_xmit_stats(struct vnic *vnic)
+{
+	if (vnic->statistics.xmit_ref != 0) {
+		vnic->statistics.xmit_off_time +=
+		    get_cycles() - vnic->statistics.xmit_ref;
+		vnic->statistics.xmit_off_num++;
+		vnic->statistics.xmit_ref = 0;
+	}
+}
+
+static inline void vnic_recv_pkt_stats(struct vnic *vnic)
+{
+	vnic->statistics.recv_time += get_cycles() - recv_ref;
+	vnic->statistics.recv_num++;
+}
+
+static inline void vnic_multicast_recv_pkt_stats(struct vnic *vnic)
+{
+	vnic->statistics.multicast_recv_num++;
+}
+
+static inline void vnic_pre_pkt_xmit_stats(cycles_t *time)
+{
+	*time = get_cycles();
+}
+
+static inline void vnic_post_pkt_xmit_stats(struct vnic *vnic,
+					    cycles_t time)
+{
+	vnic->statistics.xmit_time += get_cycles() - time;
+	vnic->statistics.xmit_num++;
+
+}
+
+static inline void vnic_xmit_fail_stats(struct vnic *vnic)
+{
+	vnic->statistics.xmit_fail++;
+}
+
+static inline void vnic_carrier_loss_stats(struct vnic *vnic)
+{
+	if (vnic->statistics.carrier_ref != 0) {
+		vnic->statistics.carrier_off_time +=
+			get_cycles() -  vnic->statistics.carrier_ref;
+		vnic->statistics.carrier_off_num++;
+		vnic->statistics.carrier_ref = 0;
+	}
+}
+
+static inline int vnic_setup_stats_files(struct vnic *vnic)
+{
+	init_completion(&vnic->stat_info.released);
+	vnic->stat_info.dev.class = NULL;
+	vnic->stat_info.dev.parent = &vnic->dev_info.dev;
+	vnic->stat_info.dev.release = vnic_release_dev;
+	snprintf(vnic->stat_info.dev.bus_id, BUS_ID_SIZE,
+		 "stats");
+
+	if (device_register(&vnic->stat_info.dev)) {
+		SYS_ERROR("create_vnic: error in registering"
+			  " stat class dev\n");
+		goto stats_out;
+	}
+
+	if (sysfs_create_group(&vnic->stat_info.dev.kobj,
+			       &vnic_stats_attr_group))
+		goto err_stats_file;
+
+	return 0;
+err_stats_file:
+	device_unregister(&vnic->stat_info.dev);
+	wait_for_completion(&vnic->stat_info.released);
+stats_out:
+	return -1;
+}
+
+static inline void vnic_cleanup_stats_files(struct vnic *vnic)
+{
+	sysfs_remove_group(&vnic->dev_info.dev.kobj,
+			   &vnic_stats_attr_group);
+	device_unregister(&vnic->stat_info.dev);
+	wait_for_completion(&vnic->stat_info.released);
+}
+
+static inline void vnic_disconn_stats(struct vnic *vnic)
+{
+	if (!vnic->statistics.disconn_ref)
+		vnic->statistics.disconn_ref = get_cycles();
+
+	if (vnic->statistics.carrier_ref == 0)
+		vnic->statistics.carrier_ref = get_cycles();
+}
+
+static inline void vnic_alloc_stats(struct vnic *vnic)
+{
+	vnic->statistics.start_time = get_cycles();
+}
+
+static inline void control_note_rsptime_stats(cycles_t *time)
+{
+	*time = get_cycles();
+}
+
+static inline void control_update_rsptime_stats(struct control *control,
+						cycles_t response_time)
+{
+	response_time -= control->statistics.request_time;
+	control->statistics.response_time += response_time;
+	control->statistics.response_num++;
+	if (control->statistics.response_max < response_time)
+		control->statistics.response_max = response_time;
+	if ((control->statistics.response_min == 0) ||
+	    (control->statistics.response_min > response_time))
+		control->statistics.response_min =  response_time;
+
+}
+
+static inline void control_note_reqtime_stats(struct control *control)
+{
+	control->statistics.request_time = get_cycles();
+}
+
+static inline void control_timeout_stats(struct control *control)
+{
+	control->statistics.timeout_num++;
+}
+
+static inline void data_kickreq_stats(struct data *data)
+{
+	data->statistics.kick_reqs++;
+}
+
+static inline void data_no_xmitbuf_stats(struct data *data)
+{
+	data->statistics.no_xmit_bufs++;
+}
+
+static inline void data_xmits_stats(struct data *data)
+{
+	data->statistics.xmit_num++;
+}
+
+static inline void data_recvs_stats(struct data *data)
+{
+	data->statistics.recv_num++;
+}
+
+static inline void data_note_kickrcv_time(void)
+{
+	recv_ref = get_cycles();
+}
+
+static inline void data_rcvkicks_stats(struct data *data)
+{
+	data->statistics.kick_recvs++;
+}
+
+
+static inline void vnic_ib_conntime_stats(struct vnic_ib_conn *ib_conn)
+{
+	ib_conn->statistics.connection_time = get_cycles();
+}
+
+static inline void vnic_ib_note_comptime_stats(cycles_t *time)
+{
+	*time = get_cycles();
+}
+
+static inline void vnic_ib_callback_stats(struct vnic_ib_conn *ib_conn)
+{
+	ib_conn->statistics.num_callbacks++;
+}
+
+static inline void vnic_ib_comp_stats(struct vnic_ib_conn *ib_conn,
+				      u32 *comp_num)
+{
+	ib_conn->statistics.num_ios++;
+	*comp_num = *comp_num + 1;
+
+}
+
+static inline void vnic_ib_io_stats(struct io *io,
+				    struct vnic_ib_conn *ib_conn,
+				    cycles_t comp_time)
+{
+	if ((io->type == RECV) || (io->type == RECV_UD))
+		io->time = comp_time;
+	else if (io->type == RDMA) {
+		ib_conn->statistics.rdma_comp_time += comp_time - io->time;
+		ib_conn->statistics.rdma_comp_ios++;
+	} else if (io->type == SEND) {
+		ib_conn->statistics.send_comp_time += comp_time - io->time;
+		ib_conn->statistics.send_comp_ios++;
+	}
+}
+
+static inline void vnic_ib_maxio_stats(struct vnic_ib_conn *ib_conn,
+				       u32 comp_num)
+{
+	if (comp_num > ib_conn->statistics.max_ios)
+		ib_conn->statistics.max_ios = comp_num;
+}
+
+static inline void vnic_ib_connected_time_stats(struct vnic_ib_conn *ib_conn)
+{
+	ib_conn->statistics.connection_time =
+			 get_cycles() - ib_conn->statistics.connection_time;
+
+}
+
+static inline void vnic_ib_pre_rcvpost_stats(struct vnic_ib_conn *ib_conn,
+					     struct io *io,
+					     cycles_t *time)
+{
+	*time = get_cycles();
+	if (io->time != 0) {
+		ib_conn->statistics.recv_comp_time += *time - io->time;
+		ib_conn->statistics.recv_comp_ios++;
+	}
+
+}
+
+static inline void vnic_ib_post_rcvpost_stats(struct vnic_ib_conn *ib_conn,
+					      cycles_t time)
+{
+	ib_conn->statistics.recv_post_time += get_cycles() - time;
+	ib_conn->statistics.recv_post_ios++;
+}
+
+static inline void vnic_ib_pre_sendpost_stats(struct io *io,
+					      cycles_t *time)
+{
+	io->time = *time = get_cycles();
+}
+
+static inline void vnic_ib_post_sendpost_stats(struct vnic_ib_conn *ib_conn,
+					       struct io *io,
+					       cycles_t time)
+{
+	time = get_cycles() - time;
+	if (io->swr.opcode == IB_WR_RDMA_WRITE) {
+		ib_conn->statistics.rdma_post_time += time;
+		ib_conn->statistics.rdma_post_ios++;
+	} else {
+		ib_conn->statistics.send_post_time += time;
+		ib_conn->statistics.send_post_ios++;
+	}
+}
+#else	/*CONFIG_INIFINIBAND_VNIC_STATS*/
+
+static inline void vnic_connected_stats(struct vnic *vnic)
+{
+	;
+}
+
+static inline void vnic_stop_xmit_stats(struct vnic *vnic)
+{
+	;
+}
+
+static inline void vnic_restart_xmit_stats(struct vnic *vnic)
+{
+	;
+}
+
+static inline void vnic_recv_pkt_stats(struct vnic *vnic)
+{
+	;
+}
+
+static inline void vnic_multicast_recv_pkt_stats(struct vnic *vnic)
+{
+	;
+}
+
+static inline void vnic_pre_pkt_xmit_stats(cycles_t *time)
+{
+	;
+}
+
+static inline void vnic_post_pkt_xmit_stats(struct vnic *vnic,
+					    cycles_t time)
+{
+	;
+}
+
+static inline void vnic_xmit_fail_stats(struct vnic *vnic)
+{
+	;
+}
+
+static inline int vnic_setup_stats_files(struct vnic *vnic)
+{
+	return 0;
+}
+
+static inline void vnic_cleanup_stats_files(struct vnic *vnic)
+{
+	;
+}
+
+static inline void vnic_carrier_loss_stats(struct vnic *vnic)
+{
+	;
+}
+
+static inline void vnic_disconn_stats(struct vnic *vnic)
+{
+	;
+}
+
+static inline void vnic_alloc_stats(struct vnic *vnic)
+{
+	;
+}
+
+static inline void control_note_rsptime_stats(cycles_t *time)
+{
+	;
+}
+
+static inline void control_update_rsptime_stats(struct control *control,
+						cycles_t response_time)
+{
+	;
+}
+
+static inline void control_note_reqtime_stats(struct control *control)
+{
+	;
+}
+
+static inline void control_timeout_stats(struct control *control)
+{
+	;
+}
+
+static inline void data_kickreq_stats(struct data *data)
+{
+	;
+}
+
+static inline void data_no_xmitbuf_stats(struct data *data)
+{
+	;
+}
+
+static inline void data_xmits_stats(struct data *data)
+{
+	;
+}
+
+static inline void data_recvs_stats(struct data *data)
+{
+	;
+}
+
+static inline void data_note_kickrcv_time(void)
+{
+	;
+}
+
+static inline void data_rcvkicks_stats(struct data *data)
+{
+	;
+}
+
+static inline void vnic_ib_conntime_stats(struct vnic_ib_conn *ib_conn)
+{
+	;
+}
+
+static inline void vnic_ib_note_comptime_stats(cycles_t *time)
+{
+	;
+}
+
+static inline void vnic_ib_callback_stats(struct vnic_ib_conn *ib_conn)
+
+{
+	;
+}
+static inline void vnic_ib_comp_stats(struct vnic_ib_conn *ib_conn,
+				      u32 *comp_num)
+{
+	;
+}
+
+static inline void vnic_ib_io_stats(struct io *io,
+				    struct vnic_ib_conn *ib_conn,
+				    cycles_t comp_time)
+{
+	;
+}
+
+static inline void vnic_ib_maxio_stats(struct vnic_ib_conn *ib_conn,
+				       u32 comp_num)
+{
+	;
+}
+
+static inline void vnic_ib_connected_time_stats(struct vnic_ib_conn *ib_conn)
+{
+	;
+}
+
+static inline void vnic_ib_pre_rcvpost_stats(struct vnic_ib_conn *ib_conn,
+					     struct io *io,
+					     cycles_t *time)
+{
+	;
+}
+
+static inline void vnic_ib_post_rcvpost_stats(struct vnic_ib_conn *ib_conn,
+					      cycles_t time)
+{
+	;
+}
+
+static inline void vnic_ib_pre_sendpost_stats(struct io *io,
+					      cycles_t *time)
+{
+	;
+}
+
+static inline void vnic_ib_post_sendpost_stats(struct vnic_ib_conn *ib_conn,
+					       struct io *io,
+					       cycles_t time)
+{
+	;
+}
+#endif	/*CONFIG_INIFINIBAND_VNIC_STATS*/
+
+#endif	/*VNIC_STATS_H_INCLUDED*/

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH 11/13] QLogic VNIC: Driver utility file - implements various utility macros
  2008-04-30 17:15 [ofa-general] [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Ramachandra K
                   ` (9 preceding siblings ...)
  2008-04-30 17:20 ` [ofa-general] [PATCH 10/13] QLogic VNIC: Driver Statistics collection Ramachandra K
@ 2008-04-30 17:21 ` Ramachandra K
  2008-05-01 14:58   ` [ofa-general] " Stephen Hemminger
  2008-04-30 17:21 ` [ofa-general] [PATCH 12/13] QLogic VNIC: Driver Kconfig and Makefile Ramachandra K
                   ` (2 subsequent siblings)
  13 siblings, 1 reply; 32+ messages in thread
From: Ramachandra K @ 2008-04-30 17:21 UTC (permalink / raw)
  To: rdreier, general, netdev; +Cc: amar.mudrankit, poornima.kamath

From: Poornima Kamath <poornima.kamath@qlogic.com>

This patch adds the driver utility file which mainly contains utility
macros for debugging of QLogic VNIC driver.

Signed-off-by: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>
Signed-off-by: Amar Mudrankit <amar.mudrankit@qlogic.com>
---

 drivers/infiniband/ulp/qlgc_vnic/vnic_util.h |  251 ++++++++++++++++++++++++++
 1 files changed, 251 insertions(+), 0 deletions(-)
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_util.h

diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_util.h b/drivers/infiniband/ulp/qlgc_vnic/vnic_util.h
new file mode 100644
index 0000000..4d7d540
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_util.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2006 QLogic, 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 VNIC_UTIL_H_INCLUDED
+#define VNIC_UTIL_H_INCLUDED
+
+#define MODULE_NAME "QLGC_VNIC"
+
+#define VNIC_MAJORVERSION	1
+#define VNIC_MINORVERSION	1
+
+#define is_power_of2(value)	(((value) & ((value - 1))) == 0)
+#define ALIGN_DOWN(x, a)	((x)&(~((a)-1)))
+
+extern u32 vnic_debug;
+
+enum {
+	DEBUG_IB_INFO			= 0x00000001,
+	DEBUG_IB_FUNCTION		= 0x00000002,
+	DEBUG_IB_FSTATUS		= 0x00000004,
+	DEBUG_IB_ASSERTS		= 0x00000008,
+	DEBUG_CONTROL_INFO		= 0x00000010,
+	DEBUG_CONTROL_FUNCTION	= 0x00000020,
+	DEBUG_CONTROL_PACKET	= 0x00000040,
+	DEBUG_CONFIG_INFO		= 0x00000100,
+	DEBUG_DATA_INFO 		= 0x00001000,
+	DEBUG_DATA_FUNCTION		= 0x00002000,
+	DEBUG_NETPATH_INFO		= 0x00010000,
+	DEBUG_VIPORT_INFO		= 0x00100000,
+	DEBUG_VIPORT_FUNCTION	= 0x00200000,
+	DEBUG_LINK_STATE		= 0x00400000,
+	DEBUG_VNIC_INFO 		= 0x01000000,
+	DEBUG_VNIC_FUNCTION		= 0x02000000,
+	DEBUG_MCAST_INFO		= 0x04000000,
+	DEBUG_MCAST_FUNCTION	= 0x08000000,
+	DEBUG_SYS_INFO			= 0x10000000,
+	DEBUG_SYS_VERBOSE		= 0x40000000
+};
+
+#ifdef CONFIG_INFINIBAND_QLGC_VNIC_DEBUG
+#define PRINT(level, x, fmt, arg...)					\
+	printk(level "%s: %s: %s, line %d: " fmt,			\
+	       MODULE_NAME, x, __FILE__, __LINE__, ##arg)
+
+#define PRINT_CONDITIONAL(level, x, condition, fmt, arg...)		\
+	do {								\
+		if (condition)						\
+			printk(level "%s: %s: %s, line %d: " fmt,	\
+			       MODULE_NAME, x, __FILE__, __LINE__,	\
+			       ##arg);					\
+	} while (0)
+#else
+#define PRINT(level, x, fmt, arg...)					\
+	printk(level "%s: " fmt, MODULE_NAME, ##arg)
+
+#define PRINT_CONDITIONAL(level, x, condition, fmt, arg...)		\
+	do {								\
+		 if (condition)						\
+			printk(level "%s: %s: " fmt,			\
+			       MODULE_NAME, x, ##arg);			\
+	} while (0)
+#endif	/*CONFIG_INFINIBAND_QLGC_VNIC_DEBUG*/
+
+#define IB_PRINT(fmt, arg...)			\
+	PRINT(KERN_INFO, "IB", fmt, ##arg)
+#define IB_ERROR(fmt, arg...)			\
+	PRINT(KERN_ERR, "IB", fmt, ##arg)
+
+#define IB_FUNCTION(fmt, arg...) 				\
+	PRINT_CONDITIONAL(KERN_INFO, 				\
+			  "IB", 				\
+			  (vnic_debug & DEBUG_IB_FUNCTION), 	\
+			  fmt, ##arg)
+
+#define IB_INFO(fmt, arg...)					\
+	PRINT_CONDITIONAL(KERN_INFO,				\
+			  "IB",					\
+			  (vnic_debug & DEBUG_IB_INFO),		\
+			  fmt, ##arg)
+
+#define IB_ASSERT(x)							\
+	do {								\
+		 if ((vnic_debug & DEBUG_IB_ASSERTS) && !(x))		\
+			panic("%s assertion failed, file:  %s,"		\
+				" line %d: ",				\
+				MODULE_NAME, __FILE__, __LINE__)	\
+	} while (0)
+
+#define CONTROL_PRINT(fmt, arg...)			\
+	PRINT(KERN_INFO, "CONTROL", fmt, ##arg)
+#define CONTROL_ERROR(fmt, arg...)			\
+	PRINT(KERN_ERR, "CONTROL", fmt, ##arg)
+
+#define CONTROL_INFO(fmt, arg...)					\
+	PRINT_CONDITIONAL(KERN_INFO,					\
+			  "CONTROL",					\
+			  (vnic_debug & DEBUG_CONTROL_INFO),		\
+			  fmt, ##arg)
+
+#define CONTROL_FUNCTION(fmt, arg...)					\
+	PRINT_CONDITIONAL(KERN_INFO,					\
+			"CONTROL",					\
+			(vnic_debug & DEBUG_CONTROL_FUNCTION),		\
+			fmt, ##arg)
+
+#define CONTROL_PACKET(pkt)					\
+	do {							\
+		 if (vnic_debug & DEBUG_CONTROL_PACKET)		\
+			control_log_control_packet(pkt);	\
+	} while (0)
+
+#define CONFIG_PRINT(fmt, arg...)		\
+	PRINT(KERN_INFO, "CONFIG", fmt, ##arg)
+#define CONFIG_ERROR(fmt, arg...)		\
+	PRINT(KERN_ERR, "CONFIG", fmt, ##arg)
+
+#define CONFIG_INFO(fmt, arg...)				\
+	PRINT_CONDITIONAL(KERN_INFO,				\
+			  "CONFIG",				\
+			  (vnic_debug & DEBUG_CONFIG_INFO),	\
+			  fmt, ##arg)
+
+#define DATA_PRINT(fmt, arg...)			\
+	PRINT(KERN_INFO, "DATA", fmt, ##arg)
+#define DATA_ERROR(fmt, arg...)			\
+	PRINT(KERN_ERR, "DATA", fmt, ##arg)
+
+#define DATA_INFO(fmt, arg...)					\
+	PRINT_CONDITIONAL(KERN_INFO,				\
+			  "DATA",				\
+			  (vnic_debug & DEBUG_DATA_INFO),	\
+			  fmt, ##arg)
+
+#define DATA_FUNCTION(fmt, arg...)				\
+	PRINT_CONDITIONAL(KERN_INFO,				\
+			  "DATA",				\
+			  (vnic_debug & DEBUG_DATA_FUNCTION),	\
+			  fmt, ##arg)
+
+
+#define MCAST_PRINT(fmt, arg...)        \
+    PRINT(KERN_INFO, "MCAST", fmt, ##arg)
+#define MCAST_ERROR(fmt, arg...)        \
+    PRINT(KERN_ERR, "MCAST", fmt, ##arg)
+
+#define MCAST_INFO(fmt, arg...)   	              		\
+	PRINT_CONDITIONAL(KERN_INFO,     			\
+			"MCAST",   				\
+			(vnic_debug & DEBUG_MCAST_INFO),	\
+			fmt, ##arg)
+
+#define MCAST_FUNCTION(fmt, arg...)				\
+	PRINT_CONDITIONAL(KERN_INFO,				\
+			"MCAST",				\
+			(vnic_debug & DEBUG_MCAST_FUNCTION), 	\
+			fmt, ##arg)
+
+#define NETPATH_PRINT(fmt, arg...)		\
+	PRINT(KERN_INFO, "NETPATH", fmt, ##arg)
+#define NETPATH_ERROR(fmt, arg...)		\
+	PRINT(KERN_ERR, "NETPATH", fmt, ##arg)
+
+#define NETPATH_INFO(fmt, arg...)				\
+	PRINT_CONDITIONAL(KERN_INFO,				\
+			  "NETPATH",				\
+			  (vnic_debug & DEBUG_NETPATH_INFO),	\
+			  fmt, ##arg)
+
+#define VIPORT_PRINT(fmt, arg...)		\
+	PRINT(KERN_INFO, "VIPORT", fmt, ##arg)
+#define VIPORT_ERROR(fmt, arg...)		\
+	PRINT(KERN_ERR, "VIPORT", fmt, ##arg)
+
+#define VIPORT_INFO(fmt, arg...) 				\
+	PRINT_CONDITIONAL(KERN_INFO,				\
+			  "VIPORT",				\
+			  (vnic_debug & DEBUG_VIPORT_INFO),	\
+			  fmt, ##arg)
+
+#define VIPORT_FUNCTION(fmt, arg...)				\
+	PRINT_CONDITIONAL(KERN_INFO,				\
+			  "VIPORT",				\
+			  (vnic_debug & DEBUG_VIPORT_FUNCTION),	\
+			  fmt, ##arg)
+
+#define LINK_STATE(fmt, arg...) 				\
+	PRINT_CONDITIONAL(KERN_INFO,				\
+			  "LINK",				\
+			  (vnic_debug & DEBUG_LINK_STATE),	\
+			  fmt, ##arg)
+
+#define VNIC_PRINT(fmt, arg...)			\
+	PRINT(KERN_INFO, "NIC", fmt, ##arg)
+#define VNIC_ERROR(fmt, arg...)			\
+	PRINT(KERN_ERR, "NIC", fmt, ##arg)
+#define VNIC_INIT(fmt, arg...)			\
+	PRINT(KERN_INFO, "NIC", fmt, ##arg)
+
+#define VNIC_INFO(fmt, arg...)					\
+	 PRINT_CONDITIONAL(KERN_INFO,				\
+			   "NIC",				\
+			   (vnic_debug & DEBUG_VNIC_INFO),	\
+			   fmt, ##arg)
+
+#define VNIC_FUNCTION(fmt, arg...)				\
+	 PRINT_CONDITIONAL(KERN_INFO,				\
+			   "NIC",				\
+			   (vnic_debug & DEBUG_VNIC_FUNCTION),	\
+			   fmt, ##arg)
+
+#define SYS_PRINT(fmt, arg...)			\
+	PRINT(KERN_INFO, "SYS", fmt, ##arg)
+#define SYS_ERROR(fmt, arg...)			\
+	PRINT(KERN_ERR, "SYS", fmt, ##arg)
+
+#define SYS_INFO(fmt, arg...)					\
+	 PRINT_CONDITIONAL(KERN_INFO,				\
+			   "SYS",				\
+			   (vnic_debug & DEBUG_SYS_INFO),	\
+			   fmt, ##arg)
+
+#endif	/* VNIC_UTIL_H_INCLUDED */


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [ofa-general] [PATCH 12/13] QLogic VNIC: Driver Kconfig and Makefile.
  2008-04-30 17:15 [ofa-general] [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Ramachandra K
                   ` (10 preceding siblings ...)
  2008-04-30 17:21 ` [PATCH 11/13] QLogic VNIC: Driver utility file - implements various utility macros Ramachandra K
@ 2008-04-30 17:21 ` Ramachandra K
  2008-05-15 22:31   ` Roland Dreier
  2008-04-30 17:22 ` [ofa-general] [PATCH 13/13] QLogic VNIC: Modifications to IB " Ramachandra K
  2008-04-30 22:25 ` [ofa-general] Re: [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Roland Dreier
  13 siblings, 1 reply; 32+ messages in thread
From: Ramachandra K @ 2008-04-30 17:21 UTC (permalink / raw)
  To: rdreier, general, netdev; +Cc: poornima.kamath, amar.mudrankit

From: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>

Kconfig and Makefile for the QLogic VNIC driver.

Signed-off-by: Poornima Kamath <poornima.kamath@qlogic.com>
Signed-off-by: Amar Mudrankit <amar.mudrankit@qlogic.com>
---

 drivers/infiniband/ulp/qlgc_vnic/Kconfig  |   28 ++++++++++++++++++++++++++++
 drivers/infiniband/ulp/qlgc_vnic/Makefile |   13 +++++++++++++
 2 files changed, 41 insertions(+), 0 deletions(-)
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/Kconfig
 create mode 100644 drivers/infiniband/ulp/qlgc_vnic/Makefile

diff --git a/drivers/infiniband/ulp/qlgc_vnic/Kconfig b/drivers/infiniband/ulp/qlgc_vnic/Kconfig
new file mode 100644
index 0000000..6a08770
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/Kconfig
@@ -0,0 +1,28 @@
+config INFINIBAND_QLGC_VNIC
+	tristate "QLogic VNIC - Support for QLogic Ethernet Virtual I/O Controller"
+	depends on INFINIBAND && NETDEVICES && INET
+	---help---
+	  Support for the QLogic Ethernet Virtual I/O Controller
+	  (EVIC). In conjunction with the EVIC, this provides virtual
+	  ethernet interfaces and transports ethernet packets over
+	  InfiniBand so that you can communicate with Ethernet networks
+	  using your IB device.
+
+config INFINIBAND_QLGC_VNIC_DEBUG
+	bool "QLogic VNIC Verbose debugging"
+	depends on INFINIBAND_QLGC_VNIC
+	default n
+	---help---
+	  This option causes verbose debugging code to be compiled
+	  into the QLogic VNIC driver.  The output can be turned on via the
+	  vnic_debug module parameter.
+
+config INFINIBAND_QLGC_VNIC_STATS
+	bool "QLogic VNIC Statistics"
+	depends on INFINIBAND_QLGC_VNIC
+	default n
+	---help---
+	  This option compiles statistics collecting code into the
+	  data path of the QLogic VNIC driver to help in profiling and fine
+	  tuning. This adds some overhead in the interest of gathering
+	  data.
diff --git a/drivers/infiniband/ulp/qlgc_vnic/Makefile b/drivers/infiniband/ulp/qlgc_vnic/Makefile
new file mode 100644
index 0000000..509dd67
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/Makefile
@@ -0,0 +1,13 @@
+obj-$(CONFIG_INFINIBAND_QLGC_VNIC)		+= qlgc_vnic.o
+
+qlgc_vnic-y					:= vnic_main.o \
+						   vnic_ib.o \
+						   vnic_viport.o \
+						   vnic_control.o \
+						   vnic_data.o \
+						   vnic_netpath.o \
+						   vnic_config.o \
+						   vnic_sys.o \
+						   vnic_multicast.o
+
+qlgc_vnic-$(CONFIG_INFINIBAND_QLGC_VNIC_STATS)	+= vnic_stats.o

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [ofa-general] [PATCH 13/13] QLogic VNIC: Modifications to IB Kconfig and Makefile
  2008-04-30 17:15 [ofa-general] [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Ramachandra K
                   ` (11 preceding siblings ...)
  2008-04-30 17:21 ` [ofa-general] [PATCH 12/13] QLogic VNIC: Driver Kconfig and Makefile Ramachandra K
@ 2008-04-30 17:22 ` Ramachandra K
  2008-04-30 22:25 ` [ofa-general] Re: [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Roland Dreier
  13 siblings, 0 replies; 32+ messages in thread
From: Ramachandra K @ 2008-04-30 17:22 UTC (permalink / raw)
  To: rdreier, general, netdev; +Cc: poornima.kamath, amar.mudrankit

From: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>

This patch modifies the toplevel Infiniband Kconfig and Makefile
to include QLogic VNIC as new ULP.

Signed-off-by: Poornima Kamath <poornima.kamath@qlogic.com>
Signed-off-by: Amar Mudrankit <amar.mudrankit@qlogic.com>
---

 drivers/infiniband/Kconfig  |    2 ++
 drivers/infiniband/Makefile |    1 +
 2 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig
index a5dc78a..0775df5 100644
--- a/drivers/infiniband/Kconfig
+++ b/drivers/infiniband/Kconfig
@@ -53,4 +53,6 @@ source "drivers/infiniband/ulp/srp/Kconfig"
 
 source "drivers/infiniband/ulp/iser/Kconfig"
 
+source "drivers/infiniband/ulp/qlgc_vnic/Kconfig"
+
 endif # INFINIBAND
diff --git a/drivers/infiniband/Makefile b/drivers/infiniband/Makefile
index ed35e44..845271e 100644
--- a/drivers/infiniband/Makefile
+++ b/drivers/infiniband/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_INFINIBAND_NES)		+= hw/nes/
 obj-$(CONFIG_INFINIBAND_IPOIB)		+= ulp/ipoib/
 obj-$(CONFIG_INFINIBAND_SRP)		+= ulp/srp/
 obj-$(CONFIG_INFINIBAND_ISER)		+= ulp/iser/
+obj-$(CONFIG_INFINIBAND_QLGC_VNIC)	+= ulp/qlgc_vnic/

^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [ofa-general] Re: [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver
  2008-04-30 17:15 [ofa-general] [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Ramachandra K
                   ` (12 preceding siblings ...)
  2008-04-30 17:22 ` [ofa-general] [PATCH 13/13] QLogic VNIC: Modifications to IB " Ramachandra K
@ 2008-04-30 22:25 ` Roland Dreier
  13 siblings, 0 replies; 32+ messages in thread
From: Roland Dreier @ 2008-04-30 22:25 UTC (permalink / raw)
  To: Ramachandra K; +Cc: netdev, poornima.kamath, general, amar.mudrankit

 > This is the QLogic Virtual NIC driver patch series which has been tested
 > against your for-2.6.26 and for-2.6.27 branches. We intended these patches to
 > make it to the 2.6.26 kernel, but if it is too late for the 2.6.26 merge window
 > please consider them for 2.6.27.

Yes, *WAY* too late for 2.6.26, given that today is the last day of the
merge window, and that things that get merged need to be ready before
the merge window opens.

 > The driver compiles cleanly with sparse endianness checking enabled. We have
 > also tested the driver with lockdep checking enabled.
 > 
 > We have run these patches through checkpatch.pl and the only warnings are
 > related to lines slightly longer than 80 columns in some of the statements.

All good news.

Will review and I hope get this into 2.6.27.

 - R.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 08/13] QLogic VNIC: sysfs interface implementation for the driver
  2008-04-30 17:19 ` [ofa-general] [PATCH 08/13] QLogic VNIC: sysfs interface implementation for " Ramachandra K
@ 2008-05-01 14:56   ` Stephen Hemminger
  2008-05-01 16:02     ` [ofa-general] " Kuchimanchi, Ramachandra (Contractor - )
  0 siblings, 1 reply; 32+ messages in thread
From: Stephen Hemminger @ 2008-05-01 14:56 UTC (permalink / raw)
  To: Ramachandra K; +Cc: rdreier, general, netdev, amar.mudrankit, poornima.kamath

On Wed, 30 Apr 2008 22:49:55 +0530
Ramachandra K <ramachandra.kuchimanchi@qlogic.com> wrote:

> From: Amar Mudrankit <amar.mudrankit@qlogic.com>
> 
> The sysfs interface for the QLogic VNIC driver is implemented through
> this patch.
> 
> Signed-off-by: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>
> Signed-off-by: Poornima Kamath <poornima.kamath@qlogic.com>
> ---
> 
>  drivers/infiniband/ulp/qlgc_vnic/vnic_sys.c | 1127 +++++++++++++++++++++++++++
>  drivers/infiniband/ulp/qlgc_vnic/vnic_sys.h |   62 +
>  2 files changed, 1189 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_sys.c
>  create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_sys.h
> 
> diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_sys.c b/drivers/infiniband/ulp/qlgc_vnic/vnic_sys.c
> new file mode 100644
> index 0000000..7e70b0c
> --- /dev/null
> +++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_sys.c
> @@ -0,0 +1,1127 @@
> +/*
> + * Copyright (c) 2006 QLogic, 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/parser.h>
> +#include <linux/netdevice.h>
> +#include <linux/if.h>
> +
> +#include "vnic_util.h"
> +#include "vnic_config.h"
> +#include "vnic_ib.h"
> +#include "vnic_viport.h"
> +#include "vnic_main.h"
> +#include "vnic_stats.h"
> +
> +/*
> + * target eiocs are added by writing
> + *
> + * ioc_guid=<EIOC GUID>,dgid=<dest GID>,pkey=<P_key>,name=<interface_name>
> + * to the create_primary  sysfs attribute.
> + */
> +enum {
> +	VNIC_OPT_ERR = 0,
> +	VNIC_OPT_IOC_GUID = 1 << 0,
> +	VNIC_OPT_DGID = 1 << 1,
> +	VNIC_OPT_PKEY = 1 << 2,
> +	VNIC_OPT_NAME = 1 << 3,
> +	VNIC_OPT_INSTANCE = 1 << 4,
> +	VNIC_OPT_RXCSUM = 1 << 5,
> +	VNIC_OPT_TXCSUM = 1 << 6,
> +	VNIC_OPT_HEARTBEAT = 1 << 7,
> +	VNIC_OPT_IOC_STRING = 1 << 8,
> +	VNIC_OPT_IB_MULTICAST = 1 << 9,
> +	VNIC_OPT_ALL = (VNIC_OPT_IOC_GUID |
> +			VNIC_OPT_DGID | VNIC_OPT_NAME | VNIC_OPT_PKEY),
> +};
> +
> +static match_table_t vnic_opt_tokens = {
> +	{VNIC_OPT_IOC_GUID, "ioc_guid=%s"},
> +	{VNIC_OPT_DGID, "dgid=%s"},
> +	{VNIC_OPT_PKEY, "pkey=%x"},
> +	{VNIC_OPT_NAME, "name=%s"},
> +	{VNIC_OPT_INSTANCE, "instance=%d"},
> +	{VNIC_OPT_RXCSUM, "rx_csum=%s"},
> +	{VNIC_OPT_TXCSUM, "tx_csum=%s"},
> +	{VNIC_OPT_HEARTBEAT, "heartbeat=%d"},
> +	{VNIC_OPT_IOC_STRING, "ioc_string=\"%s"},
> +	{VNIC_OPT_IB_MULTICAST, "ib_multicast=%s"},
> +	{VNIC_OPT_ERR, NULL}
> +};
> 

NO

1. Most of this shouldn't be done via sysfs (rx_csum, tx_csum, ...)

2. Sysfs is one value per file not name=value


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [ofa-general] Re: [PATCH 11/13] QLogic VNIC: Driver utility file - implements various utility macros
  2008-04-30 17:21 ` [PATCH 11/13] QLogic VNIC: Driver utility file - implements various utility macros Ramachandra K
@ 2008-05-01 14:58   ` Stephen Hemminger
  2008-05-01 16:18     ` [ofa-general] " Kuchimanchi, Ramachandra (Contractor - )
  0 siblings, 1 reply; 32+ messages in thread
From: Stephen Hemminger @ 2008-05-01 14:58 UTC (permalink / raw)
  To: Ramachandra K; +Cc: netdev, rdreier, poornima.kamath, general, amar.mudrankit

On Wed, 30 Apr 2008 22:51:26 +0530
Ramachandra K <ramachandra.kuchimanchi@qlogic.com> wrote:

> From: Poornima Kamath <poornima.kamath@qlogic.com>
> 
> This patch adds the driver utility file which mainly contains utility
> macros for debugging of QLogic VNIC driver.
> 
> Signed-off-by: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>
> Signed-off-by: Amar Mudrankit <amar.mudrankit@qlogic.com>
> ---
> 
>  drivers/infiniband/ulp/qlgc_vnic/vnic_util.h |  251 ++++++++++++++++++++++++++
>  1 files changed, 251 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/infiniband/ulp/qlgc_vnic/vnic_util.h
> 
> diff --git a/drivers/infiniband/ulp/qlgc_vnic/vnic_util.h b/drivers/infiniband/ulp/qlgc_vnic/vnic_util.h
> new file mode 100644
> index 0000000..4d7d540
> --- /dev/null
> +++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_util.h
> @@ -0,0 +1,251 @@
> +/*
> + * Copyright (c) 2006 QLogic, 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 VNIC_UTIL_H_INCLUDED
> +#define VNIC_UTIL_H_INCLUDED
> +
> +#define MODULE_NAME "QLGC_VNIC"
> +
> +#define VNIC_MAJORVERSION	1
> +#define VNIC_MINORVERSION	1
> +
> +#define is_power_of2(value)	(((value) & ((value - 1))) == 0)
> +#define ALIGN_DOWN(x, a)	((x)&(~((a)-1)))

In kernel.h already

> +extern u32 vnic_debug;

Use msg level macros instead?

> +
> +enum {
> +	DEBUG_IB_INFO			= 0x00000001,
> +	DEBUG_IB_FUNCTION		= 0x00000002,
> +	DEBUG_IB_FSTATUS		= 0x00000004,
> +	DEBUG_IB_ASSERTS		= 0x00000008,
> +	DEBUG_CONTROL_INFO		= 0x00000010,
> +	DEBUG_CONTROL_FUNCTION	= 0x00000020,
> +	DEBUG_CONTROL_PACKET	= 0x00000040,
> +	DEBUG_CONFIG_INFO		= 0x00000100,
> +	DEBUG_DATA_INFO 		= 0x00001000,
> +	DEBUG_DATA_FUNCTION		= 0x00002000,
> +	DEBUG_NETPATH_INFO		= 0x00010000,
> +	DEBUG_VIPORT_INFO		= 0x00100000,
> +	DEBUG_VIPORT_FUNCTION	= 0x00200000,
> +	DEBUG_LINK_STATE		= 0x00400000,
> +	DEBUG_VNIC_INFO 		= 0x01000000,
> +	DEBUG_VNIC_FUNCTION		= 0x02000000,
> +	DEBUG_MCAST_INFO		= 0x04000000,
> +	DEBUG_MCAST_FUNCTION	= 0x08000000,
> +	DEBUG_SYS_INFO			= 0x10000000,
> +	DEBUG_SYS_VERBOSE		= 0x40000000
> +};
> +
> +#ifdef CONFIG_INFINIBAND_QLGC_VNIC_DEBUG
> +#define PRINT(level, x, fmt, arg...)					\
> +	printk(level "%s: %s: %s, line %d: " fmt,			\
> +	       MODULE_NAME, x, __FILE__, __LINE__, ##arg)
> +
> +#define PRINT_CONDITIONAL(level, x, condition, fmt, arg...)		\
> +	do {								\
> +		if (condition)						\
> +			printk(level "%s: %s: %s, line %d: " fmt,	\
> +			       MODULE_NAME, x, __FILE__, __LINE__,	\
> +			       ##arg);					\
> +	} while (0)
> +#else
> +#define PRINT(level, x, fmt, arg...)					\
> +	printk(level "%s: " fmt, MODULE_NAME, ##arg)
> +
> +#define PRINT_CONDITIONAL(level, x, condition, fmt, arg...)		\
> +	do {								\
> +		 if (condition)						\
> +			printk(level "%s: %s: " fmt,			\
> +			       MODULE_NAME, x, ##arg);			\
> +	} while (0)
> +#endif	/*CONFIG_INFINIBAND_QLGC_VNIC_DEBUG*/
> +
> +#define IB_PRINT(fmt, arg...)			\
> +	PRINT(KERN_INFO, "IB", fmt, ##arg)
> +#define IB_ERROR(fmt, arg...)			\
> +	PRINT(KERN_ERR, "IB", fmt, ##arg)
> +
> +#define IB_FUNCTION(fmt, arg...) 				\
> +	PRINT_CONDITIONAL(KERN_INFO, 				\
> +			  "IB", 				\
> +			  (vnic_debug & DEBUG_IB_FUNCTION), 	\
> +			  fmt, ##arg)
> +
> +#define IB_INFO(fmt, arg...)					\
> +	PRINT_CONDITIONAL(KERN_INFO,				\
> +			  "IB",					\
> +			  (vnic_debug & DEBUG_IB_INFO),		\
> +			  fmt, ##arg)
> +
> +#define IB_ASSERT(x)							\
> +	do {								\
> +		 if ((vnic_debug & DEBUG_IB_ASSERTS) && !(x))		\
> +			panic("%s assertion failed, file:  %s,"		\
> +				" line %d: ",				\
> +				MODULE_NAME, __FILE__, __LINE__)	\
> +	} while (0)
> +
> +#define CONTROL_PRINT(fmt, arg...)			\
> +	PRINT(KERN_INFO, "CONTROL", fmt, ##arg)
> +#define CONTROL_ERROR(fmt, arg...)			\
> +	PRINT(KERN_ERR, "CONTROL", fmt, ##arg)
> +
> +#define CONTROL_INFO(fmt, arg...)					\
> +	PRINT_CONDITIONAL(KERN_INFO,					\
> +			  "CONTROL",					\
> +			  (vnic_debug & DEBUG_CONTROL_INFO),		\
> +			  fmt, ##arg)
> +
> +#define CONTROL_FUNCTION(fmt, arg...)					\
> +	PRINT_CONDITIONAL(KERN_INFO,					\
> +			"CONTROL",					\
> +			(vnic_debug & DEBUG_CONTROL_FUNCTION),		\
> +			fmt, ##arg)
> +
> +#define CONTROL_PACKET(pkt)					\
> +	do {							\
> +		 if (vnic_debug & DEBUG_CONTROL_PACKET)		\
> +			control_log_control_packet(pkt);	\
> +	} while (0)
> +
> +#define CONFIG_PRINT(fmt, arg...)		\
> +	PRINT(KERN_INFO, "CONFIG", fmt, ##arg)
> +#define CONFIG_ERROR(fmt, arg...)		\
> +	PRINT(KERN_ERR, "CONFIG", fmt, ##arg)
> +
> +#define CONFIG_INFO(fmt, arg...)				\
> +	PRINT_CONDITIONAL(KERN_INFO,				\
> +			  "CONFIG",				\
> +			  (vnic_debug & DEBUG_CONFIG_INFO),	\
> +			  fmt, ##arg)
> +
> +#define DATA_PRINT(fmt, arg...)			\
> +	PRINT(KERN_INFO, "DATA", fmt, ##arg)
> +#define DATA_ERROR(fmt, arg...)			\
> +	PRINT(KERN_ERR, "DATA", fmt, ##arg)
> +
> +#define DATA_INFO(fmt, arg...)					\
> +	PRINT_CONDITIONAL(KERN_INFO,				\
> +			  "DATA",				\
> +			  (vnic_debug & DEBUG_DATA_INFO),	\
> +			  fmt, ##arg)
> +
> +#define DATA_FUNCTION(fmt, arg...)				\
> +	PRINT_CONDITIONAL(KERN_INFO,				\
> +			  "DATA",				\
> +			  (vnic_debug & DEBUG_DATA_FUNCTION),	\
> +			  fmt, ##arg)
> +
> +
> +#define MCAST_PRINT(fmt, arg...)        \
> +    PRINT(KERN_INFO, "MCAST", fmt, ##arg)
> +#define MCAST_ERROR(fmt, arg...)        \
> +    PRINT(KERN_ERR, "MCAST", fmt, ##arg)
> +
> +#define MCAST_INFO(fmt, arg...)   	              		\
> +	PRINT_CONDITIONAL(KERN_INFO,     			\
> +			"MCAST",   				\
> +			(vnic_debug & DEBUG_MCAST_INFO),	\
> +			fmt, ##arg)
> +
> +#define MCAST_FUNCTION(fmt, arg...)				\
> +	PRINT_CONDITIONAL(KERN_INFO,				\
> +			"MCAST",				\
> +			(vnic_debug & DEBUG_MCAST_FUNCTION), 	\
> +			fmt, ##arg)
> +
> +#define NETPATH_PRINT(fmt, arg...)		\
> +	PRINT(KERN_INFO, "NETPATH", fmt, ##arg)
> +#define NETPATH_ERROR(fmt, arg...)		\
> +	PRINT(KERN_ERR, "NETPATH", fmt, ##arg)
> +
> +#define NETPATH_INFO(fmt, arg...)				\
> +	PRINT_CONDITIONAL(KERN_INFO,				\
> +			  "NETPATH",				\
> +			  (vnic_debug & DEBUG_NETPATH_INFO),	\
> +			  fmt, ##arg)
> +
> +#define VIPORT_PRINT(fmt, arg...)		\
> +	PRINT(KERN_INFO, "VIPORT", fmt, ##arg)
> +#define VIPORT_ERROR(fmt, arg...)		\
> +	PRINT(KERN_ERR, "VIPORT", fmt, ##arg)
> +
> +#define VIPORT_INFO(fmt, arg...) 				\
> +	PRINT_CONDITIONAL(KERN_INFO,				\
> +			  "VIPORT",				\
> +			  (vnic_debug & DEBUG_VIPORT_INFO),	\
> +			  fmt, ##arg)
> +
> +#define VIPORT_FUNCTION(fmt, arg...)				\
> +	PRINT_CONDITIONAL(KERN_INFO,				\
> +			  "VIPORT",				\
> +			  (vnic_debug & DEBUG_VIPORT_FUNCTION),	\
> +			  fmt, ##arg)
> +
> +#define LINK_STATE(fmt, arg...) 				\
> +	PRINT_CONDITIONAL(KERN_INFO,				\
> +			  "LINK",				\
> +			  (vnic_debug & DEBUG_LINK_STATE),	\
> +			  fmt, ##arg)
> +
> +#define VNIC_PRINT(fmt, arg...)			\
> +	PRINT(KERN_INFO, "NIC", fmt, ##arg)
> +#define VNIC_ERROR(fmt, arg...)			\
> +	PRINT(KERN_ERR, "NIC", fmt, ##arg)
> +#define VNIC_INIT(fmt, arg...)			\
> +	PRINT(KERN_INFO, "NIC", fmt, ##arg)
> +
> +#define VNIC_INFO(fmt, arg...)					\
> +	 PRINT_CONDITIONAL(KERN_INFO,				\
> +			   "NIC",				\
> +			   (vnic_debug & DEBUG_VNIC_INFO),	\
> +			   fmt, ##arg)
> +
> +#define VNIC_FUNCTION(fmt, arg...)				\
> +	 PRINT_CONDITIONAL(KERN_INFO,				\
> +			   "NIC",				\
> +			   (vnic_debug & DEBUG_VNIC_FUNCTION),	\
> +			   fmt, ##arg)
> +
> +#define SYS_PRINT(fmt, arg...)			\
> +	PRINT(KERN_INFO, "SYS", fmt, ##arg)
> +#define SYS_ERROR(fmt, arg...)			\
> +	PRINT(KERN_ERR, "SYS", fmt, ##arg)
> +
> +#define SYS_INFO(fmt, arg...)					\
> +	 PRINT_CONDITIONAL(KERN_INFO,				\
> +			   "SYS",				\
> +			   (vnic_debug & DEBUG_SYS_INFO),	\
> +			   fmt, ##arg)
> +
> +#endif	/* VNIC_UTIL_H_INCLUDED */


Many of these are already in standard macros pr_info, pr_err etc.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [ofa-general] RE: [PATCH 08/13] QLogic VNIC: sysfs interface implementation for the driver
  2008-05-01 14:56   ` Stephen Hemminger
@ 2008-05-01 16:02     ` Kuchimanchi, Ramachandra (Contractor - )
  2008-05-01 16:43       ` [ofa-general] [RESEND] " Ramachandra K
  0 siblings, 1 reply; 32+ messages in thread
From: Kuchimanchi, Ramachandra (Contractor - ) @ 2008-05-01 16:02 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: netdev, rdreier, Poornima Kamath (Contractor - ), general,
	Amar Mudrankit (Contractor - )


[-- Attachment #1.1: Type: text/plain, Size: 1171 bytes --]

Stephen,

Stephen Hemminger [mailto:shemminger@vyatta.com] wrote:

>> On Wed, 30 Apr 2008 22:49:55 +0530
>> Ramachandra K <ramachandra.kuchimanchi@qlogic.com> wrote:

<snip>

>> +static match_table_t vnic_opt_tokens = {
>> +	{VNIC_OPT_IOC_GUID, "ioc_guid=%s"},
>> +	{VNIC_OPT_DGID, "dgid=%s"},
>> +	{VNIC_OPT_PKEY, "pkey=%x"},
>> +	{VNIC_OPT_NAME, "name=%s"},
>> +	{VNIC_OPT_INSTANCE, "instance=%d"},
>> +	{VNIC_OPT_RXCSUM, "rx_csum=%s"},
>> +	{VNIC_OPT_TXCSUM, "tx_csum=%s"},
>> +	{VNIC_OPT_HEARTBEAT, "heartbeat=%d"},
>> +	{VNIC_OPT_IOC_STRING, "ioc_string=\"%s"},
>> +	{VNIC_OPT_IB_MULTICAST, "ib_multicast=%s"},
>> +	{VNIC_OPT_ERR, NULL}
>> +};
>> 

> NO
> 1. Most of this shouldn't be done via sysfs (rx_csum, tx_csum, ...)
> 2. Sysfs is one value per file not name=value

The VNIC driver needs multiple parameters (IOCGUID, DGID etc) from user space
to connect to the EVIC. For this the "name=value" mechanism is used for
a write-only sysfs file as an input method to the driver.

The driver follows the one value per file sysfs rule when it returns any data
with each readable file returning only a single value.

Regards,
Ram

[-- Attachment #1.2: Type: text/html, Size: 2204 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 32+ messages in thread

* [ofa-general] RE: [PATCH 11/13] QLogic VNIC: Driver utility file - implements various utility macros
  2008-05-01 14:58   ` [ofa-general] " Stephen Hemminger
@ 2008-05-01 16:18     ` Kuchimanchi, Ramachandra (Contractor - )
  2008-05-01 17:01       ` Ramachandra K
  0 siblings, 1 reply; 32+ messages in thread
From: Kuchimanchi, Ramachandra (Contractor - ) @ 2008-05-01 16:18 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: netdev, rdreier, Poornima Kamath (Contractor - ), general,
	Amar Mudrankit (Contractor - )


[-- Attachment #1.1: Type: text/plain, Size: 828 bytes --]

Stephen,

Stephen Hemminger [mailto:shemminger@vyatta.com] wrote:

> Ramachandra K <ramachandra.kuchimanchi@qlogic.com> wrote:

>> +#define is_power_of2(value)	(((value) & ((value - 1))) == 0)
>> +#define ALIGN_DOWN(x, a)	((x)&(~((a)-1)))

> In kernel.h already

Will fix this. Thanks.

>> +extern u32 vnic_debug;

> Use msg level macros instead?

I am sorry, I did not understand this comment.

> +
> +#define SYS_INFO(fmt, arg...)					\
> +	 PRINT_CONDITIONAL(KERN_INFO,				\
> +			   "SYS",				\
> +			   (vnic_debug & DEBUG_SYS_INFO),	\
> +			   fmt, ##arg)
> +
> +#endif	/* VNIC_UTIL_H_INCLUDED */


> Many of these are already in standard macros pr_info, pr_err etc.

These macros are for providing a debug log level functionality through
the vnic_debug module parameter.

Regards,
Ram

[-- Attachment #1.2: Type: text/html, Size: 2503 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 32+ messages in thread

* [ofa-general] [RESEND] RE: [PATCH 08/13] QLogic VNIC: sysfs interface implementation for the driver
  2008-05-01 16:02     ` [ofa-general] " Kuchimanchi, Ramachandra (Contractor - )
@ 2008-05-01 16:43       ` Ramachandra K
  2008-05-01 18:22         ` [ofa-general] " Stephen Hemminger
  0 siblings, 1 reply; 32+ messages in thread
From: Ramachandra K @ 2008-05-01 16:43 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: netdev, rdreier, Amar Mudrankit (Contractor - ), general,
	Poornima Kamath (Contractor - )

Sorry for the resend. Original mail got bounced from netdev.

On Thu, May 1, 2008 at 9:32 PM,  <ramachandra.kuchimanchi@qlogic.com> wrote:
>
> Stephen,
>
>
>  Stephen Hemminger [mailto:shemminger@vyatta.com] wrote:
>
>  >> On Wed, 30 Apr 2008 22:49:55 +0530
>  >> Ramachandra K <ramachandra.kuchimanchi@qlogic.com> wrote:
>
>  <snip>
>
>
>  >> +static match_table_t vnic_opt_tokens = {
>  >> +    {VNIC_OPT_IOC_GUID, "ioc_guid=%s"},
>  >> +    {VNIC_OPT_DGID, "dgid=%s"},
>  >> +    {VNIC_OPT_PKEY, "pkey=%x"},
>  >> +    {VNIC_OPT_NAME, "name=%s"},
>  >> +    {VNIC_OPT_INSTANCE, "instance=%d"},
>  >> +    {VNIC_OPT_RXCSUM, "rx_csum=%s"},
>  >> +    {VNIC_OPT_TXCSUM, "tx_csum=%s"},
>  >> +    {VNIC_OPT_HEARTBEAT, "heartbeat=%d"},
>  >> +    {VNIC_OPT_IOC_STRING, "ioc_string=\"%s"},
>  >> +    {VNIC_OPT_IB_MULTICAST, "ib_multicast=%s"},
>  >> +    {VNIC_OPT_ERR, NULL}
>  >> +};
>  >>
>
>  > NO
>  > 1. Most of this shouldn't be done via sysfs (rx_csum, tx_csum, ...)
>  > 2. Sysfs is one value per file not name=value
>
>  The VNIC driver needs multiple parameters (IOCGUID, DGID etc) from user space
>  to connect to the EVIC. For this the "name=value" mechanism is used for
>  a write-only sysfs file as an input method to the driver.
>
>  The driver follows the one value per file sysfs rule when it returns any data
>  with each readable file returning only a single value.
>
>  Regards,
>  Ram

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [ofa-general] RE: [PATCH 11/13] QLogic VNIC: Driver utility file - implements various utility macros
  2008-05-01 16:18     ` [ofa-general] " Kuchimanchi, Ramachandra (Contractor - )
@ 2008-05-01 17:01       ` Ramachandra K
  2008-05-01 18:26         ` Stephen Hemminger
  0 siblings, 1 reply; 32+ messages in thread
From: Ramachandra K @ 2008-05-01 17:01 UTC (permalink / raw)
  To: Stephen Hemminger
  Cc: netdev, rdreier, Amar Mudrankit (Contractor - ), general,
	Poornima Kamath (Contractor - )

Sorry for the resend. Original mail bounced from netdev.

On Thu, May 1, 2008 at 9:48 PM <ramachandra.kuchimanchi@qlogic.com> wrote:
> Stephen,
>
>
>  Stephen Hemminger [mailto:shemminger@vyatta.com] wrote:
>
>  > Ramachandra K <ramachandra.kuchimanchi@qlogic.com> wrote:
>
>  >> +#define is_power_of2(value) (((value) & ((value - 1))) == 0)
>  >> +#define ALIGN_DOWN(x, a)    ((x)&(~((a)-1)))
>
>  > In kernel.h already
>
>  Will fix this. Thanks.
>
>
>  >> +extern u32 vnic_debug;
>
>  > Use msg level macros instead?
>
>  I am sorry, I did not understand this comment.
>
>
>  > +
>  > +#define SYS_INFO(fmt, arg...)                                        \
>  > +      PRINT_CONDITIONAL(KERN_INFO,                           \
>  > +                        "SYS",                               \
>  > +                        (vnic_debug & DEBUG_SYS_INFO),       \
>  > +                        fmt, ##arg)
>  > +
>  > +#endif       /* VNIC_UTIL_H_INCLUDED */
>
>
>  > Many of these are already in standard macros pr_info, pr_err etc.
>
>  These macros are for providing a debug log level functionality through
>  the vnic_debug module parameter.
>
>  Regards,
>  Ram

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [ofa-general] Re: [RESEND] RE: [PATCH 08/13] QLogic VNIC: sysfs interface implementation for the driver
  2008-05-01 16:43       ` [ofa-general] [RESEND] " Ramachandra K
@ 2008-05-01 18:22         ` Stephen Hemminger
  0 siblings, 0 replies; 32+ messages in thread
From: Stephen Hemminger @ 2008-05-01 18:22 UTC (permalink / raw)
  To: Ramachandra K
  Cc: netdev, rdreier, Amar Mudrankit (Contractor - ), general,
	Poornima Kamath (Contractor - )

On Thu, 1 May 2008 22:13:08 +0530
"Ramachandra K" <ramachandra.kuchimanchi@qlogic.com> wrote:

> Sorry for the resend. Original mail got bounced from netdev.
> 
> On Thu, May 1, 2008 at 9:32 PM,  <ramachandra.kuchimanchi@qlogic.com> wrote:
> >
> > Stephen,
> >
> >
> >  Stephen Hemminger [mailto:shemminger@vyatta.com] wrote:
> >
> >  >> On Wed, 30 Apr 2008 22:49:55 +0530
> >  >> Ramachandra K <ramachandra.kuchimanchi@qlogic.com> wrote:
> >
> >  <snip>
> >
> >
> >  >> +static match_table_t vnic_opt_tokens = {
> >  >> +    {VNIC_OPT_IOC_GUID, "ioc_guid=%s"},
> >  >> +    {VNIC_OPT_DGID, "dgid=%s"},
> >  >> +    {VNIC_OPT_PKEY, "pkey=%x"},
> >  >> +    {VNIC_OPT_NAME, "name=%s"},
> >  >> +    {VNIC_OPT_INSTANCE, "instance=%d"},
> >  >> +    {VNIC_OPT_RXCSUM, "rx_csum=%s"},
> >  >> +    {VNIC_OPT_TXCSUM, "tx_csum=%s"},
> >  >> +    {VNIC_OPT_HEARTBEAT, "heartbeat=%d"},
> >  >> +    {VNIC_OPT_IOC_STRING, "ioc_string=\"%s"},
> >  >> +    {VNIC_OPT_IB_MULTICAST, "ib_multicast=%s"},
> >  >> +    {VNIC_OPT_ERR, NULL}
> >  >> +};
> >  >>
> >
> >  > NO
> >  > 1. Most of this shouldn't be done via sysfs (rx_csum, tx_csum, ...)
> >  > 2. Sysfs is one value per file not name=value
> >
> >  The VNIC driver needs multiple parameters (IOCGUID, DGID etc) from user space
> >  to connect to the EVIC. For this the "name=value" mechanism is used for
> >  a write-only sysfs file as an input method to the driver.
> >
> >  The driver follows the one value per file sysfs rule when it returns any data
> >  with each readable file returning only a single value.
> >
> >  Regards,
> >  Ram

The undocumented style rule of sysfs is one value (ascii) per file.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [ofa-general] RE: [PATCH 11/13] QLogic VNIC: Driver utility file - implements various utility macros
  2008-05-01 17:01       ` Ramachandra K
@ 2008-05-01 18:26         ` Stephen Hemminger
  0 siblings, 0 replies; 32+ messages in thread
From: Stephen Hemminger @ 2008-05-01 18:26 UTC (permalink / raw)
  To: Ramachandra K
  Cc: netdev, rdreier, Amar Mudrankit (Contractor - ), general,
	Poornima Kamath (Contractor - )

On Thu, 1 May 2008 22:31:10 +0530
"Ramachandra K" <ramachandra.kuchimanchi@qlogic.com> wrote:

> Sorry for the resend. Original mail bounced from netdev.
> 
> On Thu, May 1, 2008 at 9:48 PM <ramachandra.kuchimanchi@qlogic.com> wrote:
> > Stephen,
> >
> >
> >  Stephen Hemminger [mailto:shemminger@vyatta.com] wrote:
> >
> >  > Ramachandra K <ramachandra.kuchimanchi@qlogic.com> wrote:
> >
> >  >> +#define is_power_of2(value) (((value) & ((value - 1))) == 0)
> >  >> +#define ALIGN_DOWN(x, a)    ((x)&(~((a)-1)))
> >
> >  > In kernel.h already
> >
> >  Will fix this. Thanks.
> >
> >
> >  >> +extern u32 vnic_debug;
> >
> >  > Use msg level macros instead?

There is a ethtool mechanism to set message level for debug method, read other
network drivers and look at netif_msg_timer(x), netif_msg_probe(x), etc in
include/linux/netdevice.h

The goal here is to not have any special configuration for each different type
of hardware.  It is bad user design to have each hardware vendor choosing different
mechanism and values to enable debugging. You can argue that the existing infrastructure
is inadequate, in which case extend the infrastructure for all devices.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 01/13] QLogic VNIC: Driver - netdev implementation
  2008-04-30 17:16 ` [ofa-general] [PATCH 01/13] QLogic VNIC: Driver - netdev implementation Ramachandra K
@ 2008-05-02 18:15   ` Roland Dreier
  2008-05-05 15:36     ` [ofa-general] " Ramachandra K
  0 siblings, 1 reply; 32+ messages in thread
From: Roland Dreier @ 2008-05-02 18:15 UTC (permalink / raw)
  To: Ramachandra K; +Cc: general, netdev, amar.mudrankit, poornima.kamath

 > From: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>
 > 
 > QLogic Virtual NIC Driver. This patch implements netdev registration,
 > netdev functions and state maintenance of the QLogic Virtual NIC
 > corresponding to the various events associated with the QLogic Ethernet 
 > Virtual I/O Controller (EVIC/VEx) connection.
 > 
 > Signed-off-by: Poornima Kamath <poornima.kamath@qlogic.com>
 > Signed-off-by: Amar Mudrankit <amar.mudrankit@qlogic.com>

For the next submission please clean up the From and Signed-off-by
lines.  As it stands now you are saying that you (Ramachandra K) are the
author of the patch, and that Poornima and Amar signed off on it (ie
forwarded it), but you as the person sending the email did not sign off
on it.

 > +#include <rdma/ib_cache.h>

I would like to kill off the caching support in the IB core, so adding
new users of the API is not desirable.  However your code doesn't seem
to call any functions from this header anyway, so I guess you can just
delete the include.

 > +void vnic_stop_xmit(struct vnic *vnic, struct netpath *netpath)
 > +{
 > +	VNIC_FUNCTION("vnic_stop_xmit()\n");
 > +	if (netpath == vnic->current_path) {
 > +		if (vnic->xmit_started) {
 > +			netif_stop_queue(vnic->netdevice);
 > +			vnic->xmit_started = 0;
 > +		}
 > +
 > +		vnic_stop_xmit_stats(vnic);
 > +	}
 > +}

Do you have sufficient locking here?  Could vnic->current_path or
vnic->xmit_started change after they are tested, leading to bad results?
Also do you get anything from having a xmit_started flag that you
couldn't get just by testing with netif_queue_stopped()?

 > +	vnic = (struct vnic *)device->priv;

All this device->priv should probably be using netdev_priv() instead,
and without a cast (since a cast from void * is not needed).

 > +	if (jiffies > netpath->connect_time +
 > +		      vnic->config->no_path_timeout) {

want to use time_after() for jiffies comparison to avoid problems with
jiffies wrap.

 > +	vnic->netdevice = alloc_netdev((int) 0, config->name, vnic_setup);
 > +	vnic->netdevice->priv = (void *)vnic;

Not sure this is even kosher to do any more.  Anyway I think it's much
cleaner if you just allocate everything with alloc_netdev instead of
trying to stick your own structure in the priv pointer.

 > +extern cycles_t recv_ref;

seems like too generic a name to make global.  What the heck are you
using cycle_t to keep track of anyway?

 > +/* This array should be kept next to enum above since a change to npevent_type
 > +   enum affects this array. */
 > +static const char *const vnic_npevent_str[] = {
 > +    "PRIMARY CONNECTED",
 > +    "PRIMARY DISCONNECTED",
 > +    "PRIMARY CARRIER",

putting this in a header means every file that uses it gets a private copy/

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [ofa-general] Re: [PATCH 01/13] QLogic VNIC: Driver - netdev implementation
  2008-05-02 18:15   ` Roland Dreier
@ 2008-05-05 15:36     ` Ramachandra K
  2008-05-05 20:42       ` Roland Dreier
  0 siblings, 1 reply; 32+ messages in thread
From: Ramachandra K @ 2008-05-05 15:36 UTC (permalink / raw)
  To: Roland Dreier; +Cc: netdev, poornima.kamath, general, amar.mudrankit

Roland,

Thanks for the review. Your comments make sense and we will fix the
things you pointed out. Please see some clarifications in-line.

On Fri, May 2, 2008 at 11:45 PM, Roland Dreier <rdreier@cisco.com> wrote:
>  > From: Ramachandra K <ramachandra.kuchimanchi@qlogic.com>
>   >
>   > Signed-off-by: Poornima Kamath <poornima.kamath@qlogic.com>
>   > Signed-off-by: Amar Mudrankit <amar.mudrankit@qlogic.com>
>
>  For the next submission please clean up the From and Signed-off-by
>  lines.  As it stands now you are saying that you (Ramachandra K) are the
>  author of the patch, and that Poornima and Amar signed off on it (ie
>  forwarded it), but you as the person sending the email did not sign off
>  on it.
>

I will make sure to sign off on all patches. Should I also drop the From line
for the patches which I developed, since I am mailing them myself ?

I am using the Signed-off-by line to indicate the people who were
involved in the development of the patches at some stage.

>
>
>   > +void vnic_stop_xmit(struct vnic *vnic, struct netpath *netpath)
>   > +{
>   > +    VNIC_FUNCTION("vnic_stop_xmit()\n");
>   > +    if (netpath == vnic->current_path) {
>   > +            if (vnic->xmit_started) {
>   > +                    netif_stop_queue(vnic->netdevice);
>   > +                    vnic->xmit_started = 0;
>   > +            }
>   > +
>   > +            vnic_stop_xmit_stats(vnic);
>   > +    }
>   > +}
>
>  Do you have sufficient locking here?  Could vnic->current_path or
>  vnic->xmit_started change after they are tested, leading to bad results?
>  Also do you get anything from having a xmit_started flag that you
>  couldn't get just by testing with netif_queue_stopped()?
>

You are right, xmit_started might not be required and we will look at
the locking
issue too.

>
>
>   > +extern cycles_t recv_ref;
>
>  seems like too generic a name to make global.  What the heck are you
>  using cycle_t to keep track of anyway?
>

This is being used as part of the driver internal statistics
collection to keep track of the time
elapsed between a message arriving from the EVIC indicating that it
has done an RDMA write of
an Ethernet packet to the driver memory and the driver giving the packet
to the network stack.  Will fix the variable name.

Regards,
Ram

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [ofa-general] Re: [PATCH 01/13] QLogic VNIC: Driver - netdev implementation
  2008-05-05 15:36     ` [ofa-general] " Ramachandra K
@ 2008-05-05 20:42       ` Roland Dreier
  0 siblings, 0 replies; 32+ messages in thread
From: Roland Dreier @ 2008-05-05 20:42 UTC (permalink / raw)
  To: Ramachandra K; +Cc: netdev, poornima.kamath, general, amar.mudrankit

 > I will make sure to sign off on all patches. Should I also drop the From line
 > for the patches which I developed, since I am mailing them myself ?

It doesn't hurt to include a From: line if it is the same as the one for
the email itself, but it isn't necessary.  When I import a patch the
last From: line will be used.

 > I am using the Signed-off-by line to indicate the people who were
 > involved in the development of the patches at some stage.

That's fine.  You can read Documentation/SubmittingPatches to see the
precise legal meaning of Signed-off-by, and make sure that it applies to
everyone whose signoff you are including.  You can also add less formal
text like "X <z@foo> helped develop this patch" in the changelog entry.

 > >   > +extern cycles_t recv_ref;
 > >
 > >  seems like too generic a name to make global.  What the heck are you
 > >  using cycle_t to keep track of anyway?
 > >
 > 
 > This is being used as part of the driver internal statistics
 > collection to keep track of the time
 > elapsed between a message arriving from the EVIC indicating that it
 > has done an RDMA write of
 > an Ethernet packet to the driver memory and the driver giving the packet
 > to the network stack.

cycles don't track time (eg x86 TSC might stop for a while).  Do you
*really* need to use cycles, or are jiffies a better replacement?

 - R.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* [ofa-general] Re: [PATCH 06/13] QLogic VNIC: IB core stack interaction
  2008-04-30 17:18 ` [ofa-general] [PATCH 06/13] QLogic VNIC: IB core stack interaction Ramachandra K
@ 2008-05-13 20:40   ` Roland Dreier
  0 siblings, 0 replies; 32+ messages in thread
From: Roland Dreier @ 2008-05-13 20:40 UTC (permalink / raw)
  To: Ramachandra K; +Cc: netdev, poornima.kamath, general, amar.mudrankit

 > +#include <rdma/ib_cache.h>

 > +	ret = ib_find_cached_pkey(viport_config->ibdev,
 > +				  viport_config->port,
 > +				  be16_to_cpu(viport_config->path_info.path.
 > +					      pkey),
 > +				  &attr->pkey_index);

I think this can just be replaced with ib_find_pkey()... there is a call
to kmalloc(... GFP_KERNEL) just a couple of lines about, so you are in a
context where sleeping is allowed.  As I said before we want to get rid
of the caching infrastructure so please don't add new users.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 07/13] QLogic VNIC: Handling configurable parameters of the driver
  2008-04-30 17:19 ` [ofa-general] [PATCH 07/13] QLogic VNIC: Handling configurable parameters of the driver Ramachandra K
@ 2008-05-13 20:41   ` Roland Dreier
  0 siblings, 0 replies; 32+ messages in thread
From: Roland Dreier @ 2008-05-13 20:41 UTC (permalink / raw)
  To: Ramachandra K; +Cc: general, netdev, amar.mudrankit, poornima.kamath

 > +	ib_get_cached_gid(config->ibdev, config->port, 0,
 > +			  &config->path_info.path.sgid);

Again, looks like a sleepable context so please use ib_query_gid()
instead.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 12/13] QLogic VNIC: Driver Kconfig and Makefile.
  2008-04-30 17:21 ` [ofa-general] [PATCH 12/13] QLogic VNIC: Driver Kconfig and Makefile Ramachandra K
@ 2008-05-15 22:31   ` Roland Dreier
  0 siblings, 0 replies; 32+ messages in thread
From: Roland Dreier @ 2008-05-15 22:31 UTC (permalink / raw)
  To: Ramachandra K; +Cc: general, netdev, amar.mudrankit, poornima.kamath

 > +config INFINIBAND_QLGC_VNIC_DEBUG
 > +	bool "QLogic VNIC Verbose debugging"
 > +	depends on INFINIBAND_QLGC_VNIC
 > +	default n
 > +	---help---
 > +	  This option causes verbose debugging code to be compiled
 > +	  into the QLogic VNIC driver.  The output can be turned on via the
 > +	  vnic_debug module parameter.

If you have runtime control of this, I suggest making it default to on,
like mthca does with:

	config INFINIBAND_MTHCA_DEBUG
		bool "Verbose debugging output" if EMBEDDED
		depends on INFINIBAND_MTHCA
		default y

otherwise distros will leave the option off and it becomes a pain to
debug problems because you force users to do compiles.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH 10/13] QLogic VNIC: Driver Statistics collection
  2008-04-30 17:20 ` [ofa-general] [PATCH 10/13] QLogic VNIC: Driver Statistics collection Ramachandra K
@ 2008-05-15 22:33   ` Roland Dreier
  2008-05-20 16:52     ` [ofa-general] " Ramachandra K
  0 siblings, 1 reply; 32+ messages in thread
From: Roland Dreier @ 2008-05-15 22:33 UTC (permalink / raw)
  To: Ramachandra K; +Cc: general, netdev, amar.mudrankit, poornima.kamath

 > +#else	/*CONFIG_INIFINIBAND_VNIC_STATS*/
 > +
 > +static inline void vnic_connected_stats(struct vnic *vnic)
 > +{
 > +	;
 > +}

there are an awful lot of stubs here.  Do you really expect anyone to
set CONFIG_INFINIBAND_QLGC_VNIC_STATS=n?

 - R.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [ofa-general] [PATCH 09/13] QLogic VNIC: IB Multicast for Ethernet broadcast/multicast
  2008-04-30 17:20 ` [ofa-general] [PATCH 09/13] QLogic VNIC: IB Multicast for Ethernet broadcast/multicast Ramachandra K
@ 2008-05-15 22:38   ` Roland Dreier
  0 siblings, 0 replies; 32+ messages in thread
From: Roland Dreier @ 2008-05-15 22:38 UTC (permalink / raw)
  To: Ramachandra K; +Cc: netdev, amar.mudrankit, general, poornima.kamath

 > +#define SET_MCAST_STATE_INVALID \
 > +do { \
 > +	viport->mc_info.state = MCAST_STATE_INVALID; \
 > +	viport->mc_info.mc = NULL; \
 > +	memset(&viport->mc_info.mgid, 0, sizeof(union ib_gid)); \
 > +} while (0);

Seems like this could be profitably implemented in C instead of CPP.

 > +		spin_lock_irqsave(&viport->mc_info.lock, flags);
 > +		viport->mc_info.state = MCAST_STATE_INVALID;
 > +		spin_unlock_irqrestore(&viport->mc_info.lock, flags);

This pattern makes me uneasy about the locking... setting the state
member will already be atomic, so what do you think you're protecting
against here by taking the lock?

 - R.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [ofa-general] Re: [PATCH 10/13] QLogic VNIC: Driver Statistics collection
  2008-05-15 22:33   ` Roland Dreier
@ 2008-05-20 16:52     ` Ramachandra K
  0 siblings, 0 replies; 32+ messages in thread
From: Ramachandra K @ 2008-05-20 16:52 UTC (permalink / raw)
  To: Roland Dreier; +Cc: netdev, amar.mudrankit, general, poornima.kamath

Roland,

On Fri, May 16, 2008 at 4:03 AM, Roland Dreier <rdreier@cisco.com> wrote:
>  > +#else       /*CONFIG_INIFINIBAND_VNIC_STATS*/
>  > +
>  > +static inline void vnic_connected_stats(struct vnic *vnic)
>  > +{
>  > +    ;
>  > +}
>
> there are an awful lot of stubs here.  Do you really expect anyone to
> set CONFIG_INFINIBAND_QLGC_VNIC_STATS=n?

Sorry, missed this comment from the first round of review. Yes, the default
behavior we want is to disable the statistics collection as some of the
statistics collection is in the data transfer path and hence this code
can add overheads. These statistics are more for performance debugging/tuning.

Regards,
Ram

^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2008-05-20 16:52 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-04-30 17:15 [ofa-general] [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Ramachandra K
2008-04-30 17:16 ` [ofa-general] [PATCH 01/13] QLogic VNIC: Driver - netdev implementation Ramachandra K
2008-05-02 18:15   ` Roland Dreier
2008-05-05 15:36     ` [ofa-general] " Ramachandra K
2008-05-05 20:42       ` Roland Dreier
2008-04-30 17:16 ` [ofa-general] [PATCH 02/13] QLogic VNIC: Netpath - abstraction of connection to EVIC/VEx Ramachandra K
2008-04-30 17:17 ` [ofa-general] [PATCH 03/13] QLogic VNIC: Implementation of communication protocol with EVIC/VEx Ramachandra K
2008-04-30 17:17 ` [ofa-general] [PATCH 04/13] QLogic VNIC: Implementation of Control path of communication protocol Ramachandra K
2008-04-30 17:18 ` [ofa-general] [PATCH 05/13] QLogic VNIC: Implementation of Data " Ramachandra K
2008-04-30 17:18 ` [ofa-general] [PATCH 06/13] QLogic VNIC: IB core stack interaction Ramachandra K
2008-05-13 20:40   ` [ofa-general] " Roland Dreier
2008-04-30 17:19 ` [ofa-general] [PATCH 07/13] QLogic VNIC: Handling configurable parameters of the driver Ramachandra K
2008-05-13 20:41   ` Roland Dreier
2008-04-30 17:19 ` [ofa-general] [PATCH 08/13] QLogic VNIC: sysfs interface implementation for " Ramachandra K
2008-05-01 14:56   ` Stephen Hemminger
2008-05-01 16:02     ` [ofa-general] " Kuchimanchi, Ramachandra (Contractor - )
2008-05-01 16:43       ` [ofa-general] [RESEND] " Ramachandra K
2008-05-01 18:22         ` [ofa-general] " Stephen Hemminger
2008-04-30 17:20 ` [ofa-general] [PATCH 09/13] QLogic VNIC: IB Multicast for Ethernet broadcast/multicast Ramachandra K
2008-05-15 22:38   ` Roland Dreier
2008-04-30 17:20 ` [ofa-general] [PATCH 10/13] QLogic VNIC: Driver Statistics collection Ramachandra K
2008-05-15 22:33   ` Roland Dreier
2008-05-20 16:52     ` [ofa-general] " Ramachandra K
2008-04-30 17:21 ` [PATCH 11/13] QLogic VNIC: Driver utility file - implements various utility macros Ramachandra K
2008-05-01 14:58   ` [ofa-general] " Stephen Hemminger
2008-05-01 16:18     ` [ofa-general] " Kuchimanchi, Ramachandra (Contractor - )
2008-05-01 17:01       ` Ramachandra K
2008-05-01 18:26         ` Stephen Hemminger
2008-04-30 17:21 ` [ofa-general] [PATCH 12/13] QLogic VNIC: Driver Kconfig and Makefile Ramachandra K
2008-05-15 22:31   ` Roland Dreier
2008-04-30 17:22 ` [ofa-general] [PATCH 13/13] QLogic VNIC: Modifications to IB " Ramachandra K
2008-04-30 22:25 ` [ofa-general] Re: [PATCH 00/13] QLogic Virtual NIC (VNIC) Driver Roland Dreier

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).