Subject: [ofa-general] [PATCH v4 04/14] QLogic VNIC: Implementation of Control path of communication protocol

From: Ramachandra K <[email-masked]>
Date: Tue, 10 Jun 2008 17:04:15 -0400

From: Poornima Kamath <[email-masked]>

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: Poornima Kamath <[email-masked]>
Signed-off-by: Ramachandra K <[email-masked]>
Signed-off-by: Amar Mudrankit <[email-masked]>
---

 drivers/infiniband/ulp/qlgc_vnic/vnic_control.c    | 2286 ++++++++++++++++++++
 drivers/infiniband/ulp/qlgc_vnic/vnic_control.h    |  180 ++
 .../infiniband/ulp/qlgc_vnic/vnic_control_pkt.h    |  368 +++
 3 files changed, 2834 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..df4d2a8
--- /dev/null
+++ b/drivers/infiniband/ulp/qlgc_vnic/vnic_control.c
@@ -0,0 +1,2286 @@
+/*
+ * 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_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);
+
+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_of_2(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_of_2(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))
+		CONTROL_ERROR("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;
+	vnic_completion_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..77307da
--- /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 */