[openib-general] [PATCH 3/10] Driver viport files - implementation of communication protocol with VEx

Ramachandra K rkuchimanchi at silverstorm.com
Mon Oct 2 13:05:00 PDT 2006


Adds the driver viport files. These files implement the state machine
for the communication protocol with the VEx.

Signed-off-by: Ramachandra K <rkuchimanchi at silverstorm.com>
---

 drivers/infiniband/ulp/vnic/vnic_viport.c |  936 +++++++++++++++++++++++++++++
 drivers/infiniband/ulp/vnic/vnic_viport.h |  175 +++++
 2 files changed, 1111 insertions(+), 0 deletions(-)

diff --git a/drivers/infiniband/ulp/vnic/vnic_viport.c b/drivers/infiniband/ulp/vnic/vnic_viport.c
new file mode 100644
index 0000000..516e802
--- /dev/null
+++ b/drivers/infiniband/ulp/vnic/vnic_viport.c
@@ -0,0 +1,936 @@
+/*
+ * Copyright (c) 2006 SilverStorm Technologies Inc.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/netdevice.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+
+#include "vnic_util.h"
+#include "vnic_main.h"
+#include "vnic_viport.h"
+#include "vnic_netpath.h"
+#include "vnic_control.h"
+#include "vnic_data.h"
+#include "vnic_config.h"
+#include "vnic_control_pkt.h"
+
+DECLARE_WAIT_QUEUE_HEAD(viport_queue);
+LIST_HEAD(viport_list);
+DECLARE_COMPLETION(viport_thread_exit);
+spinlock_t viport_list_lock = SPIN_LOCK_UNLOCKED;
+
+int viport_thread = -1;
+int viport_thread_end = 0;
+
+struct viport *viport_allocate(struct viport_config *config)
+{
+	struct viport *viport;
+
+	VIPORT_FUNCTION("viport_allocate()\n");
+	viport = (struct viport *)kmalloc(sizeof(struct viport), GFP_KERNEL);
+	if (!viport) {
+		VIPORT_ERROR("failed allocating viport structure\n");
+		config_free_viport(viport->config);
+		return NULL;
+	}
+	memset(viport, 0, sizeof(struct viport));
+
+	viport->state = VIPORT_DISCONNECTED;
+	viport->link_state = LINK_RETRYWAIT;
+	viport->connect = WAIT;
+	viport->new_mtu = 1500;
+	viport->new_flags = 0;
+	viport->config = config;
+
+	spin_lock_init(&viport->lock);
+	init_waitqueue_head(&viport->stats_queue);
+	init_waitqueue_head(&viport->disconnect_queue);
+	INIT_LIST_HEAD(&viport->list_ptrs);
+
+	viport_kick(viport);
+
+	return viport;
+}
+
+BOOLEAN viport_connect(struct viport * viport, BOOLEAN delay)
+{
+	VIPORT_FUNCTION("viport_connect()\n");
+	if (viport->parent == NULL) {
+		return FALSE;
+	}
+	if (delay)
+		viport->connect = DELAY;
+	else
+		viport->connect = NOW;
+	viport_kick(viport);
+	return TRUE;
+}
+
+BOOLEAN viport_set_parent(struct viport *viport, struct netpath *netpath)
+{
+	VIPORT_FUNCTION("viport_set_parent()\n");
+	if (viport->parent != NULL) {
+		return FALSE;
+	}
+
+	viport->parent = netpath;
+	viport_kick(viport);
+	return TRUE;
+}
+
+BOOLEAN viport_unset_parent(struct viport * viport, struct netpath * netpath)
+{
+	VIPORT_FUNCTION("viport_unset_parent()\n");
+	if (viport->parent != netpath) {
+		return FALSE;
+	}
+	viport_free(viport);
+	return TRUE;
+}
+
+void viport_free(struct viport *viport)
+{
+	VIPORT_FUNCTION("viport_free()\n");
+	viport_disconnect(viport);	/* NOTE: this can sleep */
+	config_free_viport(viport->config);
+	kfree(viport);
+	return;
+}
+
+void viport_disconnect(struct viport *viport)
+{
+	VIPORT_FUNCTION("viport_disconnect()\n");
+	viport->disconnect = 1;
+	viport_failure(viport);
+	wait_event(viport->disconnect_queue, viport->disconnect == 0);
+	return;
+}
+
+BOOLEAN viport_set_link(struct viport * viport, u16 flags, u16 mtu)
+{
+	unsigned long localflags;
+
+	VIPORT_FUNCTION("viport_set_link()\n");
+	if (mtu > data_max_mtu(&viport->data)) {
+		VIPORT_ERROR("configuration error."
+			     " mtu of %d unsupported by %s\n", mtu,
+			     config_viport_name(viport->config));
+		viport_failure(viport);
+		return FALSE;
+	}
+
+	spin_lock_irqsave(&viport->lock, localflags);
+	flags &= IFF_UP | IFF_ALLMULTI | IFF_PROMISC;
+	if ((viport->new_flags != flags)
+	    || (viport->new_mtu != mtu)) {
+		viport->new_flags = flags;
+		viport->new_mtu = mtu;
+		viport->updates |= NEED_LINK_CONFIG;
+		viport_kick(viport);
+	}
+
+	spin_unlock_irqrestore(&viport->lock, localflags);
+	return TRUE;
+}
+
+BOOLEAN viport_set_unicast(struct viport * viport, u8 * address)
+{
+	unsigned long flags;
+
+	VIPORT_FUNCTION("viport_set_unicast()\n");
+	spin_lock_irqsave(&viport->lock, flags);
+	if (viport->mac_addresses == NULL) {
+		spin_unlock_irqrestore(&viport->lock, flags);
+		return FALSE;
+	}
+	if (memcmp(viport->mac_addresses[UNICAST_ADDR].address,
+		   address, MAC_ADDR_LEN)) {
+		memcpy(viport->mac_addresses[UNICAST_ADDR].address,
+		       address, MAC_ADDR_LEN);
+		viport->mac_addresses[UNICAST_ADDR].operation
+		    = VNIC_OP_SET_ENTRY;
+		viport->updates |= NEED_ADDRESS_CONFIG;
+		viport_kick(viport);
+	}
+	spin_unlock_irqrestore(&viport->lock, flags);
+	return TRUE;
+}
+
+BOOLEAN viport_set_multicast(struct viport * viport,
+			     struct dev_mc_list * mc_list, int mc_count)
+{
+	u32 old_update_list;
+	int i;
+	unsigned long flags;
+
+	VIPORT_FUNCTION("viport_set_multicast()\n");
+	spin_lock_irqsave(&viport->lock, flags);
+	if (viport->mac_addresses == NULL) {
+		spin_unlock_irqrestore(&viport->lock, flags);
+		return FALSE;
+	}
+	old_update_list = viport->updates;
+	if (mc_count > viport->num_mac_addresses - MCAST_ADDR_START) {
+		viport->updates |= NEED_LINK_CONFIG | MCAST_OVERFLOW;
+	} else {
+		if (viport->updates & MCAST_OVERFLOW) {
+			viport->updates &= ~MCAST_OVERFLOW;
+			viport->updates |= NEED_LINK_CONFIG;
+		}
+		/* brute force algorithm */
+		for (i = MCAST_ADDR_START;
+		     i < mc_count + MCAST_ADDR_START;
+		     i++, mc_list = mc_list->next) {
+			if (viport->mac_addresses[i].valid &&
+			    !memcmp(viport->mac_addresses[i].address,
+				    mc_list->dmi_addr, MAC_ADDR_LEN))
+				continue;
+			memcpy(viport->mac_addresses[i].address,
+			       mc_list->dmi_addr, MAC_ADDR_LEN);
+			viport->mac_addresses[i].valid = 1;
+			viport->mac_addresses[i].operation = VNIC_OP_SET_ENTRY;
+		}
+		for (; i < viport->num_mac_addresses; i++) {
+			if (!viport->mac_addresses[i].valid)
+				continue;
+			viport->mac_addresses[i].valid = 0;
+			viport->mac_addresses[i].operation = VNIC_OP_SET_ENTRY;
+		}
+		if (mc_count)
+			viport->updates |= NEED_ADDRESS_CONFIG;
+	}
+
+	if (viport->updates != old_update_list)
+		viport_kick(viport);
+	spin_unlock_irqrestore(&viport->lock, flags);
+	return TRUE;
+}
+
+BOOLEAN viport_get_stats(struct viport * viport,
+			 struct net_device_stats * stats)
+{
+	unsigned long flags;
+
+	VIPORT_FUNCTION("viport_get_stats()\n");
+	if (jiffies > viport->last_stats_time +
+				 viport->config->stats_interval) {
+		spin_lock_irqsave(&viport->lock, flags);
+		viport->updates |= NEED_STATS;
+		spin_unlock_irqrestore(&viport->lock, flags);
+		viport_kick(viport);
+		wait_event(viport->stats_queue,
+			   !(viport->updates & NEED_STATS));
+
+		if (viport->stats.ethernet_status) {
+			viport_link_up(viport);
+		} else {
+			viport_link_down(viport);
+		}
+	}
+	stats->rx_packets = viport->stats.if_in_ok;
+	stats->tx_packets = viport->stats.if_out_ok;
+	stats->rx_bytes = viport->stats.if_in_octets;
+	stats->tx_bytes = viport->stats.if_out_octets;
+	stats->rx_errors = viport->stats.if_in_errors;
+	stats->tx_errors = viport->stats.if_out_errors;
+	stats->rx_dropped = 0;	/* EIOC doesn't track */
+	stats->tx_dropped = 0;	/* EIOC doesn't track */
+	stats->multicast = viport->stats.if_in_nucast_pkts;
+	stats->collisions = 0;	/* EIOC doesn't track */
+
+	return TRUE;
+}
+
+BOOLEAN viport_xmit_packet(struct viport * viport, struct sk_buff * skb)
+{
+	BOOLEAN status = FALSE;
+	unsigned long flags;
+
+	VIPORT_FUNCTION("viport_xmit_packet()\n");
+	spin_lock_irqsave(&viport->lock, flags);
+	if (viport->state == VIPORT_CONNECTED)
+		status = data_xmit_packet(&viport->data, skb);
+	spin_unlock_irqrestore(&viport->lock, flags);
+	return status;
+}
+
+void viport_link_up(struct viport *viport)
+{
+	VIPORT_FUNCTION("viport_link_up()\n");
+	netpath_link_up(viport->parent, viport);
+	return;
+}
+
+void viport_link_down(struct viport *viport)
+{
+	VIPORT_FUNCTION("viport_link_down()\n");
+	netpath_link_down(viport->parent, viport);
+	return;
+}
+
+void viport_stop_xmit(struct viport *viport)
+{
+	VIPORT_FUNCTION("viport_stop_xmit()\n");
+	netpath_stop_xmit(viport->parent, viport);
+	return;
+}
+
+void viport_restart_xmit(struct viport *viport)
+{
+	VIPORT_FUNCTION("viport_restart_xmit()\n");
+	netpath_restart_xmit(viport->parent, viport);
+	return;
+}
+
+void viport_recv_packet(struct viport *viport, struct sk_buff *skb)
+{
+	VIPORT_FUNCTION("viport_recv_packet()\n");
+	netpath_recv_packet(viport->parent, skb);
+	return;
+}
+
+void viport_kick(struct viport *viport)
+{
+	unsigned long flags;
+
+	VIPORT_FUNCTION("viport_kick()\n");
+	spin_lock_irqsave(&viport_list_lock, flags);
+	if (list_empty(&viport->list_ptrs)) {
+		list_add_tail(&viport->list_ptrs, &viport_list);
+		wake_up(&viport_queue);
+	}
+	spin_unlock_irqrestore(&viport_list_lock, flags);
+	return;
+}
+
+void viport_failure(struct viport *viport)
+{
+	unsigned long flags;
+
+	VIPORT_FUNCTION("viport_failure()\n");
+	spin_lock_irqsave(&viport_list_lock, flags);
+	viport->errored = 1;
+	if (list_empty(&viport->list_ptrs)) {
+		list_add_tail(&viport->list_ptrs, &viport_list);
+		wake_up(&viport_queue);
+	}
+	spin_unlock_irqrestore(&viport_list_lock, flags);
+	return;
+}
+
+static void viport_timeout(unsigned long data)
+{
+	struct viport *viport;
+
+	VIPORT_FUNCTION("viport_timeout()\n");
+	viport = (struct viport *)data;
+	viport->timer_active = FALSE;
+	viport_kick(viport);
+	return;
+}
+
+static void viport_timer(struct viport *viport, int timeout)
+{
+	VIPORT_FUNCTION("viport_timer()\n");
+	if (viport->timer_active) {
+		del_timer(&viport->timer);
+	}
+	init_timer(&viport->timer);
+	viport->timer.expires = jiffies + timeout;
+	viport->timer.data = (unsigned long)viport;
+	viport->timer.function = viport_timeout;
+	viport->timer_active = TRUE;
+	add_timer(&viport->timer);
+	return;
+}
+
+static void viport_timer_stop(struct viport *viport)
+{
+	VIPORT_FUNCTION("viport_timer_stop()\n");
+	if (viport->timer_active) {
+		del_timer(&viport->timer);
+	}
+	viport->timer_active = FALSE;
+	return;
+}
+
+static BOOLEAN viport_init_mac_addresses(struct viport *viport)
+{
+	int i;
+	struct vnic_address_op *temp;
+	unsigned long flags;
+
+	VIPORT_FUNCTION("viport_init_mac_addresses()\n");
+	i = viport->num_mac_addresses * sizeof(struct vnic_address_op);
+	temp = (struct vnic_address_op *)kmalloc(i, GFP_KERNEL);
+	spin_lock_irqsave(&viport->lock, flags);
+	viport->mac_addresses = temp;
+	if (!viport->mac_addresses) {
+		VIPORT_ERROR("failed allocating MAC address table\n");
+		goto failure;
+	}
+	memset(viport->mac_addresses, '\0', i);
+	for (i = 0; i < viport->num_mac_addresses; i++) {
+		viport->mac_addresses[i].index = i;
+		viport->mac_addresses[i].vlan = viport->default_vlan;
+	}
+	memset(viport->mac_addresses[BROADCAST_ADDR].address,
+	       0xFF, MAC_ADDR_LEN);
+	viport->mac_addresses[BROADCAST_ADDR].valid = TRUE;
+	memcpy(viport->mac_addresses[UNICAST_ADDR].address,
+	       viport->hw_mac_address, MAC_ADDR_LEN);
+	viport->mac_addresses[UNICAST_ADDR].valid = TRUE;
+
+	spin_unlock_irqrestore(&viport->lock, flags);
+	return TRUE;
+failure:
+	spin_unlock_irqrestore(&viport->lock, flags);
+	return FALSE;
+}
+
+static int viport_statemachine(void *context)
+{
+	struct viport *viport;
+	enum link_state old_link_state;
+	int res;
+
+	VIPORT_FUNCTION("viport_statemachine()\n");
+	daemonize("vnic_viport");
+	while (!viport_thread_end || !list_empty(&viport_list)) {
+		wait_event_interruptible(viport_queue, !list_empty(&viport_list)
+					 || viport_thread_end);
+		spin_lock_irq(&viport_list_lock);
+		if (list_empty(&viport_list)) {
+			spin_unlock_irq(&viport_list_lock);
+			continue;
+		}
+		viport = list_entry(viport_list.next, struct viport, list_ptrs);
+		list_del_init(&viport->list_ptrs);
+		spin_unlock_irq(&viport_list_lock);
+repeat:
+		switch (old_link_state = viport->link_state) {
+		case LINK_UNINITIALIZED:
+			LINK_STATE("state LINK_UNINITIALIZED\n");
+			viport->updates = 0;
+			wake_up(&viport->stats_queue);
+			/* in case of going to
+			 * uninitialized put this viport
+			 * back on the serviceQ, delete
+			 * it off again.
+			 */
+			spin_lock_irq(&viport_list_lock);
+			list_del_init(&viport->list_ptrs);
+			spin_unlock_irq(&viport_list_lock);
+			viport->disconnect = 0;
+			wake_up(&viport->disconnect_queue);
+			break;
+		case LINK_INITIALIZE:
+			LINK_STATE("state LINK_INITIALIZE\n");
+			viport->errored = 0;
+			viport->connect = WAIT;
+			viport->last_stats_time = 0;
+			if (viport->disconnect) {
+				viport->link_state = LINK_UNINITIALIZED;
+			} else {
+				viport->link_state = LINK_INITIALIZECONTROL;
+			}
+			break;
+		case LINK_INITIALIZECONTROL:
+			LINK_STATE("state LINK_INITIALIZECONTROL\n");
+			viport->pd = ib_alloc_pd(viport->config->ibdev);
+			if (IS_ERR(viport->pd))
+				viport->link_state = LINK_DISCONNECTED;
+			else if (control_init(&viport->control, viport,
+					      &viport->config->control_config,
+					      viport->pd, viport->port_guid)) {
+				viport->link_state = LINK_INITIALIZEDATA;
+			} else {
+				ib_dealloc_pd(viport->pd);
+				viport->link_state = LINK_DISCONNECTED;
+			}
+			break;
+		case LINK_INITIALIZEDATA:
+			LINK_STATE("state LINK_INITIALIZEDATA\n");
+			if (data_init(&viport->data, viport,
+				      &viport->config->data_config,
+				      viport->pd, viport->port_guid)) {
+				viport->link_state = LINK_CONTROLCONNECT;
+			} else {
+				viport->link_state = LINK_CLEANUPCONTROL;
+			}
+
+			break;
+		case LINK_CONTROLCONNECT:
+			init_completion(&(viport->control.ib_conn.done));
+			if (vnic_ib_cm_connect(&viport->control.ib_conn)) {
+				viport->link_state = LINK_CONTROLCONNECTWAIT;
+			} else {
+				viport->link_state = LINK_CLEANUPDATA;
+			}
+			break;
+		case LINK_CONTROLCONNECTWAIT:
+			LINK_STATE("state LINK_CONTROLCONNECTWAIT\n");
+			wait_for_completion(&(viport->control.ib_conn.done));
+
+			if (control_is_connected(&viport->control)) {
+				viport->link_state = LINK_INITVNICREQ;
+			}
+
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_CONTROLDISCONNECT;
+			}
+			break;
+
+		case LINK_INITVNICREQ:
+			LINK_STATE("state LINK_INITVNICREQ\n");
+			if (control_init_vnic_req(&viport->control)) {
+				viport->link_state = LINK_INITVNICRSP;
+			} else {
+				viport->link_state = LINK_RESETCONTROL;
+			}
+			break;
+		case LINK_INITVNICRSP:
+			LINK_STATE("state LINK_INITVNICRSP\n");
+
+			control_process_async(&viport->control);
+
+			if (control_init_vnic_rsp(&viport->control,
+						  &viport->features_supported,
+						  viport->hw_mac_address,
+						  &viport->num_mac_addresses,
+						  &viport->default_vlan)) {
+				if (viport_init_mac_addresses(viport)) {
+					viport->link_state = LINK_BEGINDATAPATH;
+				} else {
+					viport->link_state = LINK_RESETCONTROL;
+				}
+			}
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_RESETCONTROL;
+			}
+			break;
+		case LINK_BEGINDATAPATH:
+			LINK_STATE("state LINK_BEGINDATAPATH\n");
+			viport->link_state = LINK_CONFIGDATAPATHREQ;
+			break;
+		case LINK_CONFIGDATAPATHREQ:
+			LINK_STATE("state LINK_CONFIGDATAPATHREQ\n");
+			if (control_config_data_path_req(&viport->control,
+							 data_path_id(&viport->
+								      data),
+							 data_host_pool_max
+							 (&viport->data),
+							 data_eioc_pool_max
+							 (&viport->data))) {
+				viport->link_state = LINK_CONFIGDATAPATHRSP;
+			} else {
+				viport->link_state = LINK_RESETCONTROL;
+			}
+			break;
+		case LINK_CONFIGDATAPATHRSP:
+			LINK_STATE("state LINK_CONFIGDATAPATHRSP\n");
+			control_process_async(&viport->control);
+
+			if (control_config_data_path_rsp(&viport->control,
+							 data_host_pool
+							 (&viport->data),
+							 data_eioc_pool
+							 (&viport->data),
+							 data_host_pool_max
+							 (&viport->data),
+							 data_eioc_pool_max
+							 (&viport->data),
+							 data_host_pool_min
+							 (&viport->data),
+							 data_eioc_pool_min
+							 (&viport->data))) {
+				viport->link_state = LINK_DATACONNECT;
+			}
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_RESETCONTROL;
+			}
+			break;
+		case LINK_DATACONNECT:
+			LINK_STATE("state LINK_DATACONNECT\n");
+			init_completion(&viport->data.ib_conn.done);
+			if (data_connect(&viport->data)) {
+				viport->link_state = LINK_DATACONNECTWAIT;
+			} else {
+				viport->link_state = LINK_RESETCONTROL;
+			}
+			break;
+		case LINK_DATACONNECTWAIT:
+			LINK_STATE("state LINK_DATACONNECTWAIT\n");
+			wait_for_completion(&viport->data.ib_conn.done);
+
+			control_process_async(&viport->control);
+
+			if (data_is_connected(&viport->data)) {
+				viport->link_state = LINK_XCHGPOOLREQ;
+			}
+
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_RESET;
+			}
+			break;
+		case LINK_XCHGPOOLREQ:
+			LINK_STATE("state LINK_XCHGPOOLREQ\n");
+			if (control_exchange_pools_req(&viport->control,
+						       data_local_pool_addr
+						       (&viport->data),
+						       data_local_pool_rkey
+						       (&viport->data))) {
+				viport->link_state = LINK_XCHGPOOLRSP;
+			} else {
+				viport->link_state = LINK_RESET;
+			}
+			break;
+		case LINK_XCHGPOOLRSP:
+			LINK_STATE("state LINK_XCHGPOOLRSP\n");
+			control_process_async(&viport->control);
+
+			if (control_exchange_pools_rsp(&viport->control,
+						       data_remote_pool_addr
+						       (&viport->data),
+						       data_remote_pool_rkey
+						       (&viport->data))) {
+				viport->link_state = LINK_INITIALIZED;
+			}
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_RESET;
+			}
+			break;
+		case LINK_INITIALIZED:
+			LINK_STATE("state LINK_INITIALIZED\n");
+			viport->link_state = LINK_IDLE;
+			viport->state = VIPORT_CONNECTED;
+			printk(KERN_INFO PFX
+			       "%s: connection established\n",
+			       config_viport_name(viport->config));
+			data_connected(&viport->data);
+			netpath_connected(viport->parent, viport);
+			spin_lock_irq(&viport->lock);
+			viport->mtu = 1500;
+			viport->flags = 0;
+			if ((viport->mtu != viport->new_mtu)
+			    || (viport->flags != viport->new_flags)) {
+				viport->updates |= NEED_LINK_CONFIG;
+			}
+			spin_unlock_irq(&viport->lock);
+			viport->link_state = LINK_IDLE;
+			break;
+		case LINK_IDLE:
+			LINK_STATE("state LINK_IDLE\n");
+			if (viport->config->hb_interval) {
+				viport_timer(viport,
+					     viport->config->hb_interval);
+			}
+			viport->link_state = LINK_IDLING;
+			break;
+		case LINK_IDLING:
+			LINK_STATE("state LINK_IDLING\n");
+			control_process_async(&viport->control);
+
+			if (viport->errored) {
+				viport_timer_stop(viport);
+				viport->errored = 0;
+				viport->link_state = LINK_RESET;
+				break;
+			}
+			spin_lock_irq(&viport->lock);
+			if (viport->updates & NEED_LINK_CONFIG) {
+				viport_timer_stop(viport);
+				viport->link_state = LINK_CONFIGLINKREQ;
+			} else if (viport->updates & NEED_ADDRESS_CONFIG) {
+				viport_timer_stop(viport);
+				viport->link_state = LINK_CONFIGADDRSREQ;
+			} else if (viport->updates & NEED_STATS) {
+				viport_timer_stop(viport);
+				viport->link_state = LINK_REPORTSTATREQ;
+			} else if (viport->config->hb_interval) {
+				if (!viport->timer_active) {
+					viport->link_state = LINK_HEARTBEATREQ;
+				}
+			}
+			spin_unlock_irq(&viport->lock);
+			break;
+		case LINK_CONFIGLINKREQ:
+			LINK_STATE("state LINK_CONFIGLINKREQ\n");
+			spin_lock_irq(&viport->lock);
+			viport->updates &= ~NEED_LINK_CONFIG;
+			viport->flags = viport->new_flags;
+			if (viport->updates & MCAST_OVERFLOW)
+				viport->flags |= IFF_ALLMULTI;
+			viport->mtu = viport->new_mtu;
+			spin_unlock_irq(&viport->lock);
+			if (control_config_link_req(&viport->control,
+						    viport->flags,
+						    viport->mtu)) {
+				viport->link_state = LINK_CONFIGLINKRSP;
+			} else {
+				viport->link_state = LINK_RESET;
+			}
+			break;
+		case LINK_CONFIGLINKRSP:
+			LINK_STATE("state LINK_CONFIGLINKRSP\n");
+			control_process_async(&viport->control);
+
+			if (control_config_link_rsp(&viport->control,
+						    &viport->flags,
+						    &viport->mtu)) {
+				viport->link_state = LINK_IDLE;
+			}
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_RESET;
+			}
+			break;
+		case LINK_CONFIGADDRSREQ:
+			LINK_STATE("state LINK_CONFIGADDRSREQ\n");
+
+			spin_lock_irq(&viport->lock);
+			res = control_config_addrs_req(&viport->control,
+						       viport->mac_addresses,
+						       viport->
+						       num_mac_addresses);
+
+			if (res > 0) {
+				viport->updates &= ~NEED_ADDRESS_CONFIG;
+				viport->link_state = LINK_CONFIGADDRSRSP;
+			} else if (res == 0) {
+				viport->link_state = LINK_CONFIGADDRSRSP;
+			} else {
+				viport->link_state = LINK_RESET;
+			}
+			spin_unlock_irq(&viport->lock);
+			break;
+		case LINK_CONFIGADDRSRSP:
+			LINK_STATE("state LINK_CONFIGADDRSRSP\n");
+			control_process_async(&viport->control);
+
+			if (control_config_addrs_rsp(&viport->control)) {
+				viport->link_state = LINK_IDLE;
+			}
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_RESET;
+			}
+			break;
+		case LINK_REPORTSTATREQ:
+			LINK_STATE("state LINK_REPORTSTATREQ\n");
+			if (control_report_statistics_req(&viport->control)) {
+				viport->link_state = LINK_REPORTSTATRSP;
+			} else {
+				viport->link_state = LINK_RESET;
+			}
+			break;
+		case LINK_REPORTSTATRSP:
+			LINK_STATE("state LINK_REPORTSTATRSP\n");
+			control_process_async(&viport->control);
+
+			spin_lock_irq(&viport->lock);
+			if (control_report_statistics_rsp(&viport->control,
+							  &viport->stats)) {
+				viport->updates &= ~NEED_STATS;
+				viport->last_stats_time = jiffies;
+				spin_unlock_irq(&viport->lock);
+				wake_up(&viport->stats_queue);
+				viport->link_state = LINK_IDLE;
+			} else {
+				spin_unlock_irq(&viport->lock);
+			}
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_RESET;
+			}
+			break;
+		case LINK_HEARTBEATREQ:
+			LINK_STATE("state LINK_HEARTBEATREQ\n");
+			if (control_heartbeat_req(&viport->control,
+						  viport->config->hb_timeout)) {
+				viport->link_state = LINK_HEARTBEATRSP;
+			} else {
+				viport->link_state = LINK_RESET;
+			}
+			break;
+		case LINK_HEARTBEATRSP:
+			LINK_STATE("state LINK_HEARTBEATRSP\n");
+			control_process_async(&viport->control);
+
+			if (control_heartbeat_rsp(&viport->control)) {
+				viport->link_state = LINK_IDLE;
+			}
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_RESET;
+			}
+			break;
+		case LINK_RESET:
+			LINK_STATE("state LINK_RESET\n");
+			viport->errored = 0;
+			spin_lock_irq(&viport->lock);
+			viport->state = VIPORT_DISCONNECTED;
+			spin_unlock_irq(&viport->lock);
+			viport_link_down(viport);
+			printk(KERN_INFO PFX
+			       "%s: connection lost\n",
+			       config_viport_name(viport->config));
+			if (control_reset_req(&viport->control)) {
+				viport->link_state = LINK_RESETRSP;
+			} else {
+				viport->link_state = LINK_DATADISCONNECT;
+			}
+			break;
+		case LINK_RESETRSP:
+			LINK_STATE("state LINK_RESETRSP\n");
+			control_process_async(&viport->control);
+
+			if (control_reset_rsp(&viport->control)) {
+				viport->link_state = LINK_DATADISCONNECT;
+			}
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_DATADISCONNECT;
+			}
+			break;
+		case LINK_RESETCONTROL:
+			LINK_STATE("state LINK_RESETCONTROL\n");
+			if (control_reset_req(&viport->control)) {
+				viport->link_state = LINK_RESETCONTROLRSP;
+			} else {
+				viport->link_state = LINK_CONTROLDISCONNECT;
+			}
+			break;
+		case LINK_RESETCONTROLRSP:
+			LINK_STATE("state LINK_RESETCONTROLRSP\n");
+			control_process_async(&viport->control);
+
+			if (control_reset_rsp(&viport->control)) {
+				viport->link_state = LINK_CONTROLDISCONNECT;
+			}
+			if (viport->errored) {
+				viport->errored = 0;
+				viport->link_state = LINK_CONTROLDISCONNECT;
+			}
+			break;
+		case LINK_DATADISCONNECT:
+			LINK_STATE("state LINK_DATADISCONNECT\n");
+			data_disconnect(&viport->data);
+			viport->link_state = LINK_CONTROLDISCONNECT;
+			break;
+		case LINK_CONTROLDISCONNECT:
+			LINK_STATE("state LINK_CONTROLDISCONNECT\n");
+			viport->link_state = LINK_CLEANUPDATA;
+			break;
+		case LINK_CLEANUPDATA:
+			LINK_STATE("state LINK_CLEANUPDATA\n");
+			data_cleanup(&viport->data);
+			viport->link_state = LINK_CLEANUPCONTROL;
+			break;
+		case LINK_CLEANUPCONTROL:
+			LINK_STATE("state LINK_CLEANUPCONTROL\n");
+			spin_lock_irq(&viport->lock);
+			if (viport->mac_addresses != NULL) {
+				kfree(viport->mac_addresses);
+				viport->mac_addresses = NULL;
+			}
+			spin_unlock_irq(&viport->lock);
+			control_cleanup(&viport->control);
+			ib_dealloc_pd(viport->pd);
+			viport->link_state = LINK_DISCONNECTED;
+			break;
+		case LINK_DISCONNECTED:
+			LINK_STATE("state LINK_DISCONNECTED\n");
+			netpath_disconnected(viport->parent, viport);
+			if (viport->disconnect != 0) {
+				viport->link_state = LINK_UNINITIALIZED;
+			} else {
+				viport_timer(viport, CONV2JIFFIES(1000));
+				viport->link_state = LINK_RETRYWAIT;
+			}
+			break;
+		case LINK_RETRYWAIT:
+			LINK_STATE("state LINK_RETRYWAIT\n");
+			viport->stats.ethernet_status = 0;
+			viport->updates = 0;
+			wake_up(&viport->stats_queue);
+			if (viport->disconnect != 0) {
+				viport_timer_stop(viport);
+				viport->link_state = LINK_UNINITIALIZED;
+			} else if (viport->connect == DELAY) {
+				if (!viport->timer_active) {
+					viport->link_state = LINK_INITIALIZE;
+				}
+			} else if (viport->connect == NOW) {
+				viport_timer_stop(viport);
+				viport->link_state = LINK_INITIALIZE;
+			}
+			break;
+		}
+
+		/* if state has changed, run through state machine again */
+		if (viport->link_state != old_link_state) {
+			goto repeat;
+		}
+
+	}
+
+	complete_and_exit(&viport_thread_exit, 0);
+}
+
+BOOLEAN viport_start()
+{
+	VIPORT_FUNCTION("viport_start()\n");
+	if ((viport_thread =
+			kernel_thread(viport_statemachine, NULL, 0)) < 0) {
+		return FALSE;
+	}
+	return TRUE;
+}
+
+void viport_cleanup()
+{
+	VIPORT_FUNCTION("viport_cleanup()\n");
+	if (viport_thread > 0) {
+		viport_thread_end = 1;
+		wake_up(&viport_queue);
+		wait_for_completion(&viport_thread_exit);
+	}
+	return;
+}
diff --git a/drivers/infiniband/ulp/vnic/vnic_viport.h b/drivers/infiniband/ulp/vnic/vnic_viport.h
new file mode 100644
index 0000000..2332dbe
--- /dev/null
+++ b/drivers/infiniband/ulp/vnic/vnic_viport.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2006 SilverStorm Technologies Inc.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef VNIC_VIPORT_H_INCLUDED
+#define VNIC_VIPORT_H_INCLUDED
+
+#include "vnic_control.h"
+#include "vnic_data.h"
+
+enum viport_state {
+	VIPORT_DISCONNECTED,
+	VIPORT_CONNECTED
+};
+
+enum link_state {
+	LINK_UNINITIALIZED,
+	LINK_INITIALIZE,
+	LINK_INITIALIZECONTROL,
+	LINK_INITIALIZEDATA,
+	LINK_CONTROLCONNECT,
+	LINK_CONTROLCONNECTWAIT,
+	LINK_INITVNICREQ,
+	LINK_INITVNICRSP,
+	LINK_BEGINDATAPATH,
+	LINK_CONFIGDATAPATHREQ,
+	LINK_CONFIGDATAPATHRSP,
+	LINK_DATACONNECT,
+	LINK_DATACONNECTWAIT,
+	LINK_XCHGPOOLREQ,
+	LINK_XCHGPOOLRSP,
+	LINK_INITIALIZED,
+	LINK_IDLE,
+	LINK_IDLING,
+	LINK_CONFIGLINKREQ,
+	LINK_CONFIGLINKRSP,
+	LINK_CONFIGADDRSREQ,
+	LINK_CONFIGADDRSRSP,
+	LINK_REPORTSTATREQ,
+	LINK_REPORTSTATRSP,
+	LINK_HEARTBEATREQ,
+	LINK_HEARTBEATRSP,
+	LINK_RESET,
+	LINK_RESETRSP,
+	LINK_RESETCONTROL,
+	LINK_RESETCONTROLRSP,
+	LINK_DATADISCONNECT,
+	LINK_CONTROLDISCONNECT,
+	LINK_CLEANUPDATA,
+	LINK_CLEANUPCONTROL,
+	LINK_DISCONNECTED,
+	LINK_RETRYWAIT
+};
+
+#define BROADCAST_ADDR		0
+#define UNICAST_ADDR		1
+#define MCAST_ADDR_START	2
+#define current_mac_address	mac_addresses[UNICAST_ADDR].address
+
+#define NEED_STATS           0x00000001
+#define NEED_ADDRESS_CONFIG  0x00000002
+#define NEED_LINK_CONFIG     0x00000004
+#define MCAST_OVERFLOW       0x00000008
+
+struct viport {
+	struct list_head		list_ptrs;
+	struct netpath			*parent;
+	struct viport_config		*config;
+	struct control			control;
+	struct data			data;
+	u64				ioc_guid;
+	u64				port_guid;
+	spinlock_t			lock;
+	struct ib_pd			*pd;
+	enum viport_state		state;
+	enum link_state			link_state;
+	struct vnic_cmd_report_stats_rsp stats;
+	wait_queue_head_t		stats_queue;
+	u32				last_stats_time;
+	u32				features_supported;
+	u8				hw_mac_address[MAC_ADDR_LEN];
+	u16				default_vlan;
+	u16				num_mac_addresses;
+	struct vnic_address_op		*mac_addresses;
+	u32				updates;
+	u16				flags;
+	u16				new_flags;
+	u16				mtu;
+	u16				new_mtu;
+	u32				errored;
+	enum { WAIT, DELAY, NOW }	connect;
+	u32				disconnect;
+	wait_queue_head_t		disconnect_queue;
+	BOOLEAN				timer_active;
+	struct timer_list		timer;
+};
+
+BOOLEAN viport_start(void);
+void viport_cleanup(void);
+
+struct viport *viport_allocate(struct viport_config *config);
+
+BOOLEAN viport_connect(struct viport *viport, BOOLEAN delay);
+
+BOOLEAN viport_set_parent(struct viport *viport, struct netpath *netpath);
+BOOLEAN viport_unset_parent(struct viport *viport, struct netpath *netpath);
+
+void viport_free(struct viport *viport);
+void viport_disconnect(struct viport *viport);
+
+BOOLEAN viport_set_link(struct viport *viport, u16 flags, u16 mtu);
+
+BOOLEAN viport_get_stats(struct viport *viport, struct net_device_stats *stats);
+
+BOOLEAN viport_xmit_packet(struct viport *viport, struct sk_buff *skb);
+
+void viport_link_up(struct viport *viport);
+void viport_link_down(struct viport *viport);
+
+void viport_stop_xmit(struct viport *viport);
+void viport_restart_xmit(struct viport *viport);
+
+void viport_recv_packet(struct viport *viport, struct sk_buff *skb);
+
+void viport_kick(struct viport *viport);
+
+void viport_failure(struct viport *viport);
+
+BOOLEAN viport_set_unicast(struct viport *viport, u8 * address);
+BOOLEAN viport_set_multicast(struct viport *viport, struct dev_mc_list *mc_list,
+			     int mc_count);
+
+#define viport_port_guid(viport)	((viport)->port_guid)
+#define viport_max_mtu(viport)		data_max_mtu(&(viport)->data)
+
+#define viport_get_hw_addr(viport,address)				\
+	memcpy(address, (viport)->hw_mac_address, MAC_ADDR_LEN)
+
+#define viport_features(viport) ((viport)->features_supported)
+
+#define viport_can_tx_csum(viport)					\
+	(((viport)->features_supported & 				\
+	(VNIC_FEAT_IPV4_CSUM_TX | VNIC_FEAT_TCP_CSUM_TX |		\
+	VNIC_FEAT_UDP_CSUM_TX)) == (VNIC_FEAT_IPV4_CSUM_TX |	\
+	VNIC_FEAT_TCP_CSUM_TX | VNIC_FEAT_UDP_CSUM_TX))
+
+#endif /* VNIC_VIPORT_H_INCLUDED */





More information about the general mailing list