[openib-general] [PATCH 1/10] Driver Main files - netdev functions and corresponding state maintenance

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


Adds the driver main files. These files implement netdev
registration, netdev functions and state maintenance of the
virtual NIC corresponding to the various events associated
with the Virtual Ethernet IOC (VEx) connection.

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

 drivers/infiniband/ulp/vnic/vnic_main.c | 1040 +++++++++++++++++++++++++++++++
 drivers/infiniband/ulp/vnic/vnic_main.h |  152 +++++
 2 files changed, 1192 insertions(+), 0 deletions(-)

diff --git a/drivers/infiniband/ulp/vnic/vnic_main.c b/drivers/infiniband/ulp/vnic/vnic_main.c
new file mode 100644
index 0000000..b87e00b
--- /dev/null
+++ b/drivers/infiniband/ulp/vnic/vnic_main.c
@@ -0,0 +1,1040 @@
+/*
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/completion.h>
+
+#include <rdma/ib_cache.h>
+
+#include "vnic_util.h"
+#include "vnic_main.h"
+#include "vnic_netpath.h"
+#include "vnic_viport.h"
+#include "vnic_ib.h"
+
+#define MODULEVERSION "0.1"
+#define MODULEDETAILS "Virtual NIC driver version " MODULEVERSION
+
+MODULE_AUTHOR("SilverStorm Technologies Inc.");
+MODULE_DESCRIPTION(MODULEDETAILS);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_SUPPORTED_DEVICE("SilverStorm Ethernet Virtual I/O Controller");
+
+u32 vnic_debug = 0x0;
+
+module_param(vnic_debug, uint, 0444);
+
+LIST_HEAD(vnic_list);
+
+const char driver[] = "vnic";
+
+void vnic_connected(struct vnic *vnic, struct netpath *netpath)
+{
+	VNIC_FUNCTION("vnic_connected()\n");
+	vnic_npevent_queue_evt(netpath, VNICNP_CONNECTED);
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+	if (vnic->statistics.conn_time == 0) {
+		vnic->statistics.conn_time =
+		    get_cycles() - vnic->statistics.start_time;
+	}
+	if (vnic->statistics.disconn_ref != 0) {
+		vnic->statistics.disconn_time +=
+		    get_cycles() - vnic->statistics.disconn_ref;
+		vnic->statistics.disconn_num++;
+		vnic->statistics.disconn_ref = 0;
+	}
+#endif	/* CONFIG_INFINIBAND_VNIC_STATS */
+}
+
+void vnic_disconnected(struct vnic *vnic, struct netpath *netpath)
+{
+	VNIC_FUNCTION("vnic_disconnected()\n");
+	vnic_npevent_queue_evt(netpath, VNICNP_DISCONNECTED);
+}
+
+void vnic_link_up(struct vnic *vnic, struct netpath *netpath)
+{
+	VNIC_FUNCTION("vnic_link_up()\n");
+	vnic_npevent_queue_evt(netpath, VNICNP_LINKUP);
+}
+
+void vnic_link_down(struct vnic *vnic, struct netpath *netpath)
+{
+	VNIC_FUNCTION("vnic_link_down()\n");
+	vnic_npevent_queue_evt(netpath, VNICNP_LINKDOWN);
+}
+
+void vnic_stop_xmit(struct vnic *vnic, struct netpath *netpath)
+{
+	VNIC_FUNCTION("vnic_stop_xmit()\n");
+	if (netpath == vnic->current_path) {
+		if (vnic->xmit_started) {
+			netif_stop_queue(&vnic->netdevice);
+			vnic->xmit_started = 0;
+		}
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+		if (vnic->statistics.xmit_ref == 0) {
+			vnic->statistics.xmit_ref = get_cycles();
+		}
+#endif	/* CONFIG_INFINIBAND_VNIC_STATS */
+	}
+	return;
+}
+
+void vnic_restart_xmit(struct vnic *vnic, struct netpath *netpath)
+{
+	VNIC_FUNCTION("vnic_restart_xmit()\n");
+	if (netpath == vnic->current_path) {
+		if (!vnic->xmit_started) {
+			netif_wake_queue(&vnic->netdevice);
+			vnic->xmit_started = 1;
+		}
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+		if (vnic->statistics.xmit_ref != 0) {
+			vnic->statistics.xmit_off_time +=
+			    get_cycles() - vnic->statistics.xmit_ref;
+			vnic->statistics.xmit_off_num++;
+			vnic->statistics.xmit_ref = 0;
+		}
+#endif	/* CONFIG_INFINIBAND_VNIC_STATS */
+	}
+	return;
+}
+
+void vnic_recv_packet(struct vnic *vnic, struct netpath *netpath,
+		      struct sk_buff *skb)
+{
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+	extern cycles_t recv_ref;
+#endif				/* CONFIG_INFINIBAND_VNIC_STATS */
+
+	VNIC_FUNCTION("vnic_recv_packet()\n");
+	if ((netpath != vnic->current_path) || !vnic->open) {
+		VNIC_INFO("tossing packet\n");
+		dev_kfree_skb(skb);
+		return;
+	}
+
+	vnic->netdevice.last_rx = jiffies;
+
+	skb->dev = &vnic->netdevice;
+	skb->protocol = eth_type_trans(skb, skb->dev);
+	if (!vnic->config->use_rx_csum) {
+		skb->ip_summed = CHECKSUM_NONE;
+	}
+
+	netif_rx(skb);
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+	vnic->statistics.recv_time += get_cycles() - recv_ref;
+	vnic->statistics.recv_num++;
+#endif	/* CONFIG_INFINIBAND_VNIC_STATS */
+
+	return;
+}
+
+static struct net_device_stats *vnic_get_stats(struct net_device *device)
+{
+	struct vnic *vnic;
+	int ret = 0;
+	struct netpath *np;
+
+	VNIC_FUNCTION("vnic_get_stats()\n");
+	vnic = (struct vnic *)device->priv;
+
+	np = vnic->current_path;
+	if (!np || !netpath_get_stats(np, &vnic->stats)) {
+		ret = -ENODEV;
+	}
+
+	return &vnic->stats;
+}
+
+static int vnic_open(struct net_device *device)
+{
+	struct vnic *vnic;
+	int ret = 0;
+	struct netpath *np;
+
+	VNIC_FUNCTION("vnic_open()\n");
+	vnic = (struct vnic *)device->priv;
+	np = vnic->current_path;
+
+	if (vnic->state != VNIC_REGISTERED) {
+		ret = -ENODEV;
+	}
+
+	vnic->open++;
+	vnic_npevent_queue_evt(&vnic->primary_path, VNIC_NP_SETLINK);
+	vnic->xmit_started = 1;
+	netif_start_queue(&vnic->netdevice);
+
+	return ret;
+}
+
+static int vnic_stop(struct net_device *device)
+{
+	struct vnic *vnic;
+	int ret = 0;
+	struct netpath *np;
+
+	VNIC_FUNCTION("vnic_stop()\n");
+	vnic = (struct vnic *)device->priv;
+	np = vnic->current_path;
+	netif_stop_queue(device);
+	vnic->xmit_started = 0;
+	vnic->open--;
+	vnic_npevent_queue_evt(&vnic->primary_path, VNIC_NP_SETLINK);
+
+	return ret;
+}
+
+static int vnic_hard_start_xmit(struct sk_buff *skb, struct net_device *device)
+{
+	struct vnic *vnic;
+	struct netpath *np;
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+	cycles_t xmit_time;
+#endif	/* CONFIG_INFINIBAND_VNIC_STATS */
+
+	VNIC_FUNCTION("vnic_hard_start_xmit()\n");
+	vnic = (struct vnic *)device->priv;
+	np = vnic->current_path;
+
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+	xmit_time = get_cycles();
+#endif	/* CONFIG_INFINIBAND_VNIC_STATS */
+	if (np && netpath_xmit_packet(np, skb)) {
+		device->trans_start = jiffies;
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+		vnic->statistics.xmit_time += get_cycles() - xmit_time;
+		vnic->statistics.xmit_num++;
+#endif	/* CONFIG_INFINIBAND_VNIC_STATS */
+		return 0;
+	}
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+	vnic->statistics.xmit_fail++;
+#endif	/* CONFIG_INFINIBAND_VNIC_STATS */
+	dev_kfree_skb(skb);
+	return 0;	/* TBD: what should I return? */
+}
+
+static void vnic_tx_timeout(struct net_device *device)
+{
+	struct vnic *vnic;
+
+	VNIC_FUNCTION("vnic_tx_timeout()\n");
+	vnic = (struct vnic *)device->priv;
+	device->trans_start = jiffies;
+
+	/* netpath_tx_timeout(vnic->current_path); */
+	VNIC_ERROR("vnic_tx_timeout\n");
+
+	return;
+}
+
+static void vnic_set_multicast_list(struct net_device *device)
+{
+	struct vnic *vnic;
+	unsigned long flags;
+
+	VNIC_FUNCTION("vnic_set_multicast_list()\n");
+	vnic = (struct vnic *)device->priv;
+
+	spin_lock_irqsave(&vnic->lock, flags);
+	/* the vnic_link_evt thread also needs to be able to access
+	 * mc_list. it is only safe to access the mc_list
+	 * in the netdevice from this call, so make a local
+	 * copy of it in the vnic. the mc_list is a linked
+	 * list, but my copy is an array where each element's
+	 * next pointer points to the next element. when I
+	 * reallocate the list, I always size it with 10
+	 * extra elements so I don't have to resize it as
+	 * often. I only downsize the list when it goes empty.
+	 */
+	if (device->mc_count == 0) {
+		if (vnic->mc_list_len) {
+			vnic->mc_list_len = vnic->mc_count = 0;
+			kfree(vnic->mc_list);
+		}
+	} else {
+		struct dev_mc_list *mc_list = device->mc_list;
+		int i;
+
+		if (device->mc_count > vnic->mc_list_len) {
+			if (vnic->mc_list_len)
+				kfree(vnic->mc_list);
+			vnic->mc_list_len = device->mc_count + 10;
+			vnic->mc_list = (struct dev_mc_list *)
+			    kmalloc(sizeof(struct dev_mc_list) *
+				    vnic->mc_list_len, GFP_ATOMIC);
+			if (!vnic->mc_list) {
+				vnic->mc_list_len = vnic->mc_count = 0;
+				spin_unlock_irqrestore(&vnic->lock, flags);
+				VNIC_ERROR("failed allocating mc_list\n");
+				return;
+			}
+		}
+		vnic->mc_count = device->mc_count;
+		for (i = 0; i < device->mc_count; i++) {
+			vnic->mc_list[i] = *mc_list;
+			vnic->mc_list[i].next = &vnic->mc_list[i + 1];
+			mc_list = mc_list->next;
+		}
+	}
+	spin_unlock_irqrestore(&vnic->lock, flags);
+	netpath_set_multicast(&vnic->primary_path,
+			      vnic->mc_list, vnic->mc_count);
+
+	netpath_set_multicast(&vnic->secondary_path,
+			      vnic->mc_list, vnic->mc_count);
+	vnic_npevent_queue_evt(&vnic->primary_path, VNIC_NP_SETLINK);
+	return;
+}
+
+static int vnic_set_mac_address(struct net_device *device, void *addr)
+{
+	struct vnic *vnic;
+	struct sockaddr *sockaddr = addr;
+
+	VNIC_FUNCTION("vnic_set_mac_address()\n");
+	vnic = (struct vnic *)device->priv;
+
+	if (netif_running(device))
+		return -EBUSY;
+	memcpy(device->dev_addr, sockaddr->sa_data, MAC_ADDR_LEN);
+	netpath_set_unicast(&vnic->primary_path, sockaddr->sa_data);
+	netpath_set_unicast(&vnic->secondary_path, sockaddr->sa_data);
+	vnic->mac_set = 1;
+	/* I'm assuming that this should work even if nothing is connected
+	 * at the moment.  note that this might return before the address has
+	 * actually been changed.
+	 */
+	return 0;
+}
+
+static int vnic_change_mtu(struct net_device *device, int mtu)
+{
+	struct vnic *vnic;
+	int ret = 0;
+
+	VNIC_FUNCTION("vnic_change_mtu()\n");
+	vnic = (struct vnic *)device->priv;
+
+	if ((mtu < netpath_max_mtu(&vnic->primary_path))
+	    && (mtu < netpath_max_mtu(&vnic->secondary_path))) {
+		device->mtu = mtu;
+		vnic_npevent_queue_evt(&vnic->primary_path, VNIC_NP_SETLINK);
+	}
+
+	return ret;
+}
+
+static int vnic_do_ioctl(struct net_device *device, struct ifreq *ifr, int cmd)
+{
+	struct vnic *vnic;
+	int ret = 0;
+
+	VNIC_FUNCTION("vnic_do_ioctl()\n");
+	vnic = (struct vnic *)device->priv;
+
+	/* TBD */
+
+	return ret;
+}
+
+static int vnic_set_config(struct net_device *device, struct ifmap *map)
+{
+	struct vnic *vnic;
+	int ret = 0;
+
+	VNIC_FUNCTION("vnic_set_config()\n");
+	vnic = (struct vnic *)device->priv;
+
+	/* TBD */
+
+	return ret;
+}
+
+DECLARE_WAIT_QUEUE_HEAD(vnic_npevent_queue);
+LIST_HEAD(vnic_npevent_list);
+DECLARE_COMPLETION(vnic_npevent_thread_exit);
+spinlock_t vnic_npevent_list_lock = SPIN_LOCK_UNLOCKED;
+int vnic_npevent_thread = -1;
+int vnic_npevent_thread_end = 0;
+
+void vnic_npevent_init(struct vnic *vnic)
+{
+	int i;
+
+	for (i = 0; i < VNICNP_NUM_EVENTS; i++) {
+		INIT_LIST_HEAD(&(vnic->npevents[i].list_ptrs));
+		vnic->npevents[i].vnic = vnic;
+	}
+}
+
+static BOOLEAN vnic_npevent_register(struct vnic *vnic, struct netpath *netpath)
+{
+	if (!vnic->mac_set) {
+		/* if netpath == secondary_path, then the primary path isn't
+		 * connected.  MAC address will be set when the primary
+		 * connects.
+		 */
+		netpath_get_hw_addr(netpath, vnic->netdevice.dev_addr);
+		netpath_set_unicast(&vnic->secondary_path,
+				    vnic->netdevice.dev_addr);
+		vnic->mac_set = 1;
+	}
+	if (register_netdev(&vnic->netdevice) != 0) {
+		VNIC_ERROR("failed registering netdev\n");
+		return FALSE;
+	}
+	vnic->state = VNIC_REGISTERED;
+	vnic->carrier = 2;	/* special value to force netif_carrier_(on|off) */
+	return TRUE;
+}
+
+static const char *const vnic_npevent_str[] = {
+	"PRIMARY CONNECTED",
+	"PRIMARY DISCONNECTED",
+	"PRIMARY CARRIER",
+	"PRIMARY NO CARRIER",
+	"PRIMARY TIMER EXPIRED",
+	"SETLINK",
+	"SECONDARY CONNECTED",
+	"SECONDARY DISCONNECTED",
+	"SECONDARY CARRIER",
+	"SECONDARY NO CARRIER",
+	"SECONDARY TIMER EXPIRED",
+	"FREE VNIC",
+};
+
+static void update_path_and_reconnect(struct netpath *netpath,
+				      struct vnic *vnic)
+{
+	struct viport_config *config = netpath->viport->config;
+	BOOLEAN delay = TRUE;
+
+	if (!vnic_ib_get_path(netpath, vnic)) {
+		return;
+	}
+
+	/*
+	 * tell viport_connect to wait 10 seconds before connecting if
+	 * we are retrying the same path index within 10 seconds.
+	 * This prevents flooding connect requests to a path (or set
+	 * of paths) that aren't successfully connecting for some reason.
+	 */
+	if (jiffies > netpath->connect_time + vnic->config->no_path_timeout) {
+		netpath->path_idx = config->path_idx;
+		netpath->connect_time = jiffies;
+		delay = FALSE;
+	} else if (config->path_idx != netpath->path_idx) {
+		delay = FALSE;
+	}
+
+	viport_connect(netpath->viport, delay);
+
+	return;
+}
+
+static int vnic_npevent_statemachine(void *context)
+{
+	struct vnic_npevent *vnic_link_evt;
+	int operation;
+	int is_secondary;
+	struct vnic *vnic;
+	struct netpath *netpath;
+	int last_carrier;
+	struct netpath *last_path;
+	int i;
+	BOOLEAN other_path_ok;
+
+	daemonize("vnic_link_evt");
+
+	while (!vnic_npevent_thread_end || !list_empty(&vnic_npevent_list)) {
+		unsigned long flags;
+
+		wait_event_interruptible(vnic_npevent_queue,
+					 !list_empty(&vnic_npevent_list)
+					 || vnic_npevent_thread_end);
+		spin_lock_irqsave(&vnic_npevent_list_lock, flags);
+		if (list_empty(&vnic_npevent_list)) {
+			spin_unlock_irqrestore(&vnic_npevent_list_lock, flags);
+			VNIC_INFO("netpath statemachine wake on empty list\n");
+			continue;
+		}
+		vnic_link_evt =
+		    list_entry(vnic_npevent_list.next, struct vnic_npevent,
+			       list_ptrs);
+		list_del_init(&vnic_link_evt->list_ptrs);
+		spin_unlock_irqrestore(&vnic_npevent_list_lock, flags);
+
+		vnic = vnic_link_evt->vnic;
+		operation = vnic_link_evt - vnic_link_evt->vnic->npevents;
+
+		VNIC_INFO("%s: processing %s, netpath=%s, carrier=%d\n",
+			  vnic->config->name,
+			  vnic_npevent_str[operation],
+			  netpath_to_string(vnic, vnic->current_path),
+			  vnic->carrier);
+
+		is_secondary = (operation >= VNICNP_SECONDARYOFFSET);
+		if (is_secondary) {
+			netpath = &vnic->secondary_path;
+		} else {
+			netpath = &vnic->primary_path;
+		}
+
+		if (vnic->current_path == &vnic->secondary_path)
+			other_path_ok = vnic->primary_path.carrier;
+		else if (vnic->current_path == &vnic->primary_path)
+			other_path_ok = vnic->secondary_path.carrier;
+		else
+			other_path_ok = FALSE;
+
+		switch (operation) {
+		case VNIC_PRINP_CONNECTED:
+			if (vnic->state == VNIC_UNINITIALIZED) {
+				if (!vnic_npevent_register(vnic, netpath))
+					break;
+			}
+			/* FALLTHROUGH : we may need to set MAC address, etc. */
+
+		case VNIC_SECNP_CONNECTED:
+			if (vnic->mac_set) {
+				netpath_set_unicast(netpath,
+						    vnic->netdevice.dev_addr);
+			}
+			spin_lock_irqsave(&vnic->lock, flags);
+			if (vnic->mc_list) {
+				netpath_set_multicast(netpath,
+						      vnic->mc_list,
+						      vnic->mc_count);
+			}
+			spin_unlock_irqrestore(&vnic->lock, flags);
+			if (vnic->state == VNIC_REGISTERED) {
+				netpath_set_link(netpath,
+						 vnic->netdevice.
+						 flags & ~IFF_UP,
+						 vnic->netdevice.mtu);
+			}
+			break;
+
+		case VNIC_PRINP_TIMEREXPIRED:
+			netpath->timer_state = NETPATH_TS_EXPIRED;
+			if (!netpath->carrier) {
+				update_path_and_reconnect(netpath, vnic);
+			}
+			break;
+
+		case VNIC_SECNP_TIMEREXPIRED:
+			netpath->timer_state = NETPATH_TS_EXPIRED;
+			if (netpath->carrier) {
+				if (vnic->state == VNIC_UNINITIALIZED) {
+					vnic_npevent_register(vnic, netpath);
+				}
+			} else {
+				update_path_and_reconnect(netpath, vnic);
+			}
+			break;
+
+		case VNIC_PRINP_LINKUP:
+			netpath->carrier = 1;
+			break;
+
+		case VNIC_SECNP_LINKUP:
+			netpath->carrier = 1;
+			if (!vnic->carrier) {
+				switch (netpath->timer_state) {
+				case NETPATH_TS_IDLE:
+					netpath->timer_state =
+					    NETPATH_TS_ACTIVE;
+					if (vnic->state == VNIC_UNINITIALIZED)
+						netpath_timer(netpath,
+							      vnic->config->
+							      primary_connect_timeout);
+					else
+						netpath_timer(netpath,
+							      vnic->config->
+							      primary_reconnect_timeout);
+					break;
+				case NETPATH_TS_ACTIVE:
+					/* do nothing */
+					break;
+				case NETPATH_TS_EXPIRED:
+					if (vnic->state == VNIC_UNINITIALIZED) {
+						vnic_npevent_register(vnic,
+								      netpath);
+					}
+					break;
+				}
+			}
+			break;
+
+		case VNIC_PRINP_LINKDOWN:
+			netpath->carrier = 0;
+			break;
+		case VNIC_SECNP_LINKDOWN:
+			if (vnic->state == VNIC_UNINITIALIZED)
+				netpath_timer_stop(netpath);
+			netpath->carrier = 0;
+			break;
+		case VNIC_PRINP_DISCONNECTED:
+		case VNIC_SECNP_DISCONNECTED:
+			netpath_timer_stop(netpath);
+			netpath->carrier = 0;
+			update_path_and_reconnect(netpath, vnic);
+			break;
+		case VNIC_NP_FREEVNIC:
+			netpath_timer_stop(&vnic->primary_path);
+			netpath_timer_stop(&vnic->secondary_path);
+			vnic->current_path = NULL;
+			netpath_free(&vnic->primary_path);
+			netpath_free(&vnic->secondary_path);
+			if (vnic->state == VNIC_REGISTERED) {
+				unregister_netdev(&vnic->netdevice);
+			}
+			for (i = 0; i < VNICNP_NUM_EVENTS; i++) {
+				list_del_init(&vnic->npevents[i].list_ptrs);
+			}
+			config_free_vnic(vnic->config);
+			if (vnic->mc_list_len) {
+				vnic->mc_list_len = vnic->mc_count = 0;
+				kfree(vnic->mc_list);
+			}
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+			class_device_unregister(&vnic->stat_info.class_dev);
+			wait_for_completion(&vnic->stat_info.released);
+#endif	/*CONFIG_INFINIBAND_VNIC_STATS*/
+			class_device_unregister(&vnic->class_dev_info.
+						class_dev);
+			wait_for_completion(&vnic->class_dev_info.released);
+
+			kfree(vnic);
+			vnic = NULL;
+			break;
+		case VNIC_NP_SETLINK:
+			if (vnic->current_path) {
+				netpath_set_link(vnic->current_path,
+						 vnic->netdevice.flags,
+						 vnic->netdevice.mtu);
+			}
+			break;
+		}
+
+		if (!vnic)
+			continue;
+
+		last_carrier = vnic->carrier;
+		last_path = vnic->current_path;
+
+		if (!(vnic->current_path) || !vnic->current_path->carrier) {
+			vnic->carrier = 0;
+			vnic->current_path = NULL;
+			vnic->netdevice.features &= ~NETIF_F_IP_CSUM;
+		}
+
+		if (!vnic->carrier) {
+			if (vnic->primary_path.carrier) {
+				vnic->carrier = 1;
+				vnic->current_path = &vnic->primary_path;
+				if (last_path
+				    && last_path != vnic->current_path)
+					printk(KERN_INFO PFX
+					       "%s: failing over to"
+					       " primary path\n",
+					       vnic->config->name);
+				else if (!last_path)
+					printk(KERN_INFO PFX
+					       "%s: using primary path\n",
+					       vnic->config->name);
+
+				if (vnic->config->use_tx_csum
+				    && netpath_can_tx_csum(vnic->
+							   current_path)) {
+					vnic->netdevice.features |=
+					    NETIF_F_IP_CSUM;
+				}
+			} else if ((vnic->secondary_path.carrier) &&
+				   (vnic->secondary_path.timer_state !=
+				    NETPATH_TS_ACTIVE)) {
+				vnic->carrier = 1;
+				vnic->current_path = &vnic->secondary_path;
+				if (last_path
+				    && last_path != vnic->current_path)
+					printk(KERN_INFO PFX
+					       "%s: failing over to"
+					       " secondary path\n",
+					       vnic->config->name);
+
+				else if (!last_path)
+					printk(KERN_INFO PFX
+					       "%s: using secondary path\n",
+					       vnic->config->name);
+
+				if (vnic->config->use_tx_csum
+				    && netpath_can_tx_csum(vnic->
+							   current_path)) {
+					vnic->netdevice.features |=
+					    NETIF_F_IP_CSUM;
+				}
+			}
+		} else if ((vnic->current_path != &vnic->primary_path) &&
+			   (vnic->config->prefer_primary) &&
+			   (vnic->primary_path.carrier)) {
+			switch (vnic->primary_path.timer_state) {
+			case NETPATH_TS_ACTIVE:
+				/* nothing to do. just wait */
+				break;
+			case NETPATH_TS_IDLE:
+				netpath_timer(&vnic->primary_path,
+					      vnic->config->
+					      primary_switch_timeout);
+				break;
+			case NETPATH_TS_EXPIRED:
+				printk(KERN_INFO PFX
+				       "%s: switching to primary path\n",
+				       vnic->config->name);
+
+				vnic->current_path = &vnic->primary_path;
+				if (vnic->config->use_tx_csum
+				    && netpath_can_tx_csum(vnic->
+							   current_path)) {
+					vnic->netdevice.features |=
+					    NETIF_F_IP_CSUM;
+				}
+				break;
+			}
+		}
+		if (last_path) {
+			if (!vnic->current_path) {
+				if (last_path == &vnic->primary_path)
+					printk(KERN_INFO PFX
+					       "%s: primary path lost, "
+					       "no failover path available\n",
+					       vnic->config->name);
+
+				else
+					printk(KERN_INFO PFX
+					       "%s: secondary path lost, "
+					       "no failover path available\n",
+					       vnic->config->name);
+			} else if (last_path == vnic->current_path) {
+				if (vnic->current_path == &vnic->secondary_path) {
+					if (other_path_ok !=
+					    vnic->primary_path.carrier) {
+						if (other_path_ok)
+							printk(KERN_INFO PFX
+							       "%s: primary "
+							       "path no longer"
+							       " available for"
+							       " failover\n",
+							       vnic->config->
+							       name);
+						else
+							printk(KERN_INFO PFX
+							       "%s: primary "
+							       "path now"
+							       " available for"
+							       " failover\n",
+							       vnic->config->
+							       name);
+					}
+				} else {
+					if (other_path_ok !=
+					    vnic->secondary_path.carrier) {
+						if (other_path_ok)
+							printk(KERN_INFO PFX
+							       "%s: secondary "
+							       "path no longer"
+							       " available for"
+							       " failover\n",
+							       vnic->config->
+							       name);
+						else
+							printk(KERN_INFO PFX
+							       "%s: secondary "
+							       "path now"
+							       " available for"
+							       " failover\n",
+							       vnic->config->
+							       name);
+					}
+				}
+			}
+		}
+
+		VNIC_INFO("new netpath=%s, carrier=%d\n",
+			  netpath_to_string(vnic, vnic->current_path),
+			  vnic->carrier);
+
+		if (vnic->current_path != last_path) {
+			if (last_path == NULL) {
+				if (vnic->current_path == &vnic->primary_path) {
+					last_path = &vnic->secondary_path;
+				} else {
+					last_path = &vnic->primary_path;
+				}
+			}
+			if (vnic->current_path) {
+				netpath_set_link(vnic->current_path,
+						 vnic->netdevice.flags,
+						 vnic->netdevice.mtu);
+			}
+			netpath_set_link(last_path,
+					 vnic->netdevice.flags & ~IFF_UP,
+					 vnic->netdevice.mtu);
+			vnic_restart_xmit(vnic, vnic->current_path);
+		}
+		if (vnic->carrier != last_carrier) {
+			if (vnic->carrier) {
+				VNIC_INFO("netif_carrier_on\n");
+				netif_carrier_on(&vnic->netdevice);
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+				if (vnic->statistics.carrier_ref != 0) {
+					vnic->statistics.carrier_off_time +=
+					    get_cycles() -
+					    vnic->statistics.carrier_ref;
+					vnic->statistics.carrier_off_num++;
+					vnic->statistics.carrier_ref = 0;
+				}
+#endif	/* CONFIG_INFINIBAND_VNIC_STATS */
+			} else {
+				VNIC_INFO("netif_carrier_off\n");
+				netif_carrier_off(&vnic->netdevice);
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+				if (!vnic->statistics.disconn_ref) {
+					vnic->statistics.disconn_ref =
+					    get_cycles();
+				}
+				if (vnic->statistics.carrier_ref == 0) {
+					vnic->statistics.carrier_ref =
+					    get_cycles();
+				}
+#endif	/* CONFIG_INFINIBAND_VNIC_STATS */
+			}
+
+		}
+	}
+	complete_and_exit(&vnic_npevent_thread_exit, 0);
+	return 0;
+}
+
+void vnic_npevent_queue_evt(struct netpath *netpath, int evt_num)
+{
+	struct vnic *vnic = netpath->parent;
+	struct list_head *l =
+	    &vnic->npevents[evt_num + netpath->second_bias].list_ptrs;
+	unsigned long flags;
+
+	spin_lock_irqsave(&vnic_npevent_list_lock, flags);
+	list_del_init(l);
+	list_add_tail(l, &vnic_npevent_list);
+	spin_unlock_irqrestore(&vnic_npevent_list_lock, flags);
+	wake_up(&vnic_npevent_queue);
+}
+
+void vnic_npevent_dequeue_evt(struct netpath *netpath, int evt_num)
+{
+	struct vnic *vnic = netpath->parent;
+	struct list_head *l =
+	    &vnic->npevents[evt_num + netpath->second_bias].list_ptrs;
+	unsigned long flags;
+
+	spin_lock_irqsave(&vnic_npevent_list_lock, flags);
+	if (!list_empty(l)) {
+		list_del_init(l);
+	}
+	spin_unlock_irqrestore(&vnic_npevent_list_lock, flags);
+}
+
+BOOLEAN vnic_npevent_start()
+{
+	VNIC_FUNCTION("vnic_npevent_start()\n");
+
+	if ((vnic_npevent_thread =
+	     kernel_thread(vnic_npevent_statemachine, NULL, 0)) < 0) {
+		return FALSE;
+	}
+	return TRUE;
+}
+
+void vnic_npevent_cleanup()
+{
+	if (vnic_npevent_thread >= 0) {
+		vnic_npevent_thread_end = 1;
+		wake_up(&vnic_npevent_queue);
+		wait_for_completion(&vnic_npevent_thread_exit);
+	}
+	return;
+}
+
+struct vnic *vnic_allocate(struct vnic_config *config)
+{
+	struct vnic *vnic = NULL;
+	struct net_device *device;
+
+	VNIC_FUNCTION("vnic_allocate()\n");
+	vnic = (struct vnic *)kmalloc(sizeof(struct vnic), GFP_KERNEL);
+	if (!vnic) {
+		VNIC_ERROR("failed allocating vnic structure\n");
+		goto failure;
+	}
+	memset(vnic, 0, sizeof(struct vnic));
+	vnic->lock = SPIN_LOCK_UNLOCKED;
+
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+	vnic->statistics.start_time = get_cycles();
+#endif	/* CONFIG_INFINIBAND_VNIC_STATS */
+	vnic->state = VNIC_UNINITIALIZED;
+	vnic->config = config;
+	device = &vnic->netdevice;
+
+	strcpy(device->name, config->name);
+
+	ether_setup(device);
+
+	/* FUTURE:
+	 * ether_setup sets the following values. these
+	 * may need to be overridden in the future.
+	 */
+	/* device->hard_header_len set to 14 for ethernet */
+	/* device->mtu set to 1500 for ethernet */
+	/* device->tx_queue_len defaults to 100 for ethernet */
+	/* device->type defaults to ARPHRD_ETHER for ethernet */
+	/* device->addr_len set to 6 octets for ethernet */
+	/* device->broadcast set to 0xffffffffffff for ethernet */
+	/* device->family defaults to AF_INET for ethernet */
+	/* device->pa_alen length of family address len (4) */
+	/* device->pa_addr set by ifconfig, do not modify */
+	/* device->pa_brdaddr set by ifconfig, do not modify */
+	/* device->pa_mask set by ifconfig, do not modify */
+	/* device->pa_dstaddr dest for p-to-p, set by ifconfig, do not modify */
+	/* device->flags use default flags for now */
+
+	device->priv			= (void *)vnic;
+	device->get_stats		= vnic_get_stats;
+	device->open			= vnic_open;
+	device->stop			= vnic_stop;
+	device->hard_start_xmit		= vnic_hard_start_xmit;
+	device->tx_timeout		= vnic_tx_timeout;
+	device->set_multicast_list	= vnic_set_multicast_list;
+	device->set_mac_address		= vnic_set_mac_address;
+	device->change_mtu		= vnic_change_mtu;
+	device->do_ioctl		= vnic_do_ioctl;
+	device->set_config		= vnic_set_config;
+	device->watchdog_timeo 		= HZ;	/* 1 second */
+	/* TBD: do I want the NETIF_F_DYNALLOC feature? */
+	device->features		= 0;
+
+	netpath_init(&vnic->primary_path, vnic, 0);
+	netpath_init(&vnic->secondary_path, vnic, VNICNP_SECONDARYOFFSET);
+
+	vnic->current_path = NULL;
+
+	vnic_npevent_init(vnic);
+
+	list_add_tail(&vnic->list_ptrs, &vnic_list);
+
+	return vnic;
+failure:
+	config_free_vnic(vnic->config);
+	return NULL;
+}
+
+void vnic_free(struct vnic *vnic)
+{
+	VNIC_FUNCTION("vnic_free()\n");
+	list_del(&vnic->list_ptrs);
+	vnic_npevent_queue_evt(&vnic->primary_path, VNIC_NP_FREEVNIC);
+	return;
+}
+
+static void __exit vnic_cleanup(void)
+{
+	VNIC_FUNCTION("vnic_cleanup()\n");
+
+	VNIC_INIT("unloading %s\n", MODULEDETAILS);
+
+	while (!list_empty(&vnic_list)) {
+		struct vnic *vnic =
+		    list_entry(vnic_list.next, struct vnic, list_ptrs);
+		vnic_free(vnic);
+	}
+
+	vnic_npevent_cleanup();
+	viport_cleanup();
+	vnic_ib_cleanup();
+	config_cleanup();
+
+	return;
+}
+
+static int __init vnic_init(void)
+{
+	VNIC_FUNCTION("vnic_init()\n");
+	VNIC_INIT("Initializing %s\n", MODULEDETAILS);
+
+	if (config_start() == FALSE) {
+		VNIC_ERROR("config_start failed\n");
+		goto failure;
+	}
+	if (vnic_ib_init() == FALSE) {
+		VNIC_ERROR("ib_start failed\n");
+		goto failure;
+	}
+	if (viport_start() == FALSE) {
+		VNIC_ERROR("viport_start failed\n");
+		goto failure;
+	}
+	if (vnic_npevent_start() == FALSE) {
+		VNIC_ERROR("vnic_npevent_start failed\n");
+		goto failure;
+	}
+
+	return 0;
+failure:
+	vnic_cleanup();
+	return -ENODEV;
+}
+
+module_init(vnic_init);
+module_exit(vnic_cleanup);
diff --git a/drivers/infiniband/ulp/vnic/vnic_main.h b/drivers/infiniband/ulp/vnic/vnic_main.h
new file mode 100644
index 0000000..b48c2cf
--- /dev/null
+++ b/drivers/infiniband/ulp/vnic/vnic_main.h
@@ -0,0 +1,152 @@
+/*
+ * 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_MAIN_H_INCLUDED
+#define VNIC_MAIN_H_INCLUDED
+
+#include <linux/timex.h>
+
+#include "vnic_config.h"
+#include "vnic_netpath.h"
+
+#define stringize(x)	#x
+#define add_quotes(x)	stringize(x)
+
+
+/* keep in sync with names in vnic_main.c vnic_npevent_str[] */
+
+enum vnic_npevent_pos {
+	VNICNP_CONNECTED = 0,
+	VNICNP_DISCONNECTED,
+	VNICNP_LINKUP,
+	VNICNP_LINKDOWN,
+	VNICNP_TIMEREXPIRED,
+	VNICNP_UNIVERSAL1,
+	/* SECONDARYOFFSET MUST ALWAYS COME AT THE END */
+	VNICNP_SECONDARYOFFSET
+};
+
+#define VNICNP_NUM_EVENTS	(2 * VNICNP_SECONDARYOFFSET)
+
+#define VNIC_PRINP_CONNECTED	VNICNP_CONNECTED
+#define VNIC_PRINP_DISCONNECTED	VNICNP_DISCONNECTED
+#define VNIC_PRINP_LINKUP 	VNICNP_LINKUP
+#define VNIC_PRINP_LINKDOWN 	VNICNP_LINKDOWN
+#define VNIC_PRINP_TIMEREXPIRED	VNICNP_TIMEREXPIRED
+#define VNIC_NP_SETLINK		VNICNP_UNIVERSAL1
+
+#define VNIC_SECNP_CONNECTED	(VNICNP_CONNECTED + VNICNP_SECONDARYOFFSET)
+#define VNIC_SECNP_DISCONNECTED	(VNICNP_DISCONNECTED + VNICNP_SECONDARYOFFSET)
+#define VNIC_SECNP_LINKUP 	(VNICNP_LINKUP + VNICNP_SECONDARYOFFSET)
+#define VNIC_SECNP_LINKDOWN 	(VNICNP_LINKDOWN + VNICNP_SECONDARYOFFSET)
+#define VNIC_SECNP_TIMEREXPIRED	(VNICNP_TIMEREXPIRED + VNICNP_SECONDARYOFFSET)
+#define VNIC_NP_FREEVNIC	(VNICNP_UNIVERSAL1 + VNICNP_SECONDARYOFFSET)
+
+struct vnic_npevent {
+	struct list_head	list_ptrs;
+	struct vnic		*vnic;
+};
+
+void vnic_npevent_init(struct vnic *);
+
+BOOLEAN vnic_npevent_start(void);
+
+void vnic_npevent_cleanup(void);
+void vnic_npevent_queue_evt(struct netpath *netpath, int evt_num);
+void vnic_npevent_dequeue_evt(struct netpath *netpath, int evt_num);
+
+enum vnic_state {
+	VNIC_UNINITIALIZED,
+	VNIC_REGISTERED,
+};
+
+struct vnic {
+	struct list_head		list_ptrs;
+	enum vnic_state			state;
+	struct vnic_config		*config;
+	struct netpath			*current_path;
+	struct netpath			primary_path;
+	struct netpath			secondary_path;
+	int				open;
+	int				carrier;
+	int				xmit_started;
+	int				mac_set;
+	struct net_device_stats 	stats;
+	struct net_device		netdevice;
+	struct class_dev_info		class_dev_info;
+	struct dev_mc_list		*mc_list;
+	int				mc_list_len;
+	int				mc_count;
+	spinlock_t			lock;
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+	struct {
+		cycles_t	start_time;
+		cycles_t	conn_time;
+		cycles_t	disconn_ref;	/* intermediate time */
+		cycles_t	disconn_time;
+		u32		disconn_num;
+		cycles_t	xmit_time;
+		u32		xmit_num;
+		u32		xmit_fail;
+		cycles_t	recv_time;
+		u32		recv_num;
+		cycles_t	xmit_ref;	/* intermediate time */
+		cycles_t	xmit_off_time;
+		u32		xmit_off_num;
+		cycles_t	carrier_ref;	/* intermediate time */
+		cycles_t	carrier_off_time;
+		u32		carrier_off_num;
+	} statistics;
+	struct class_dev_info	stat_info;
+#endif	/* CONFIG_INFINIBAND_VNIC_STATS */
+	struct vnic_npevent npevents[VNICNP_NUM_EVENTS];
+};
+
+;
+
+struct vnic *vnic_allocate(struct vnic_config *config);
+
+void vnic_free(struct vnic *vnic);
+
+void vnic_connected(struct vnic *vnic, struct netpath *netpath);
+void vnic_disconnected(struct vnic *vnic, struct netpath *netpath);
+
+void vnic_link_up(struct vnic *vnic, struct netpath *netpath);
+void vnic_link_down(struct vnic *vnic, struct netpath *netpath);
+
+void vnic_stop_xmit(struct vnic *vnic, struct netpath *netpath);
+void vnic_restart_xmit(struct vnic *vnic, struct netpath *netpath);
+
+void vnic_recv_packet(struct vnic *vnic, struct netpath *netpath,
+		      struct sk_buff *skb);
+
+#endif	/* VNIC_MAIN_H_INCLUDED */





More information about the general mailing list