[openib-general] [PATCH v2 3/11] Implementation of communication protocol with VEx
Ramachandra K
ramachandra.kuchimanchi at qlogic.com
Tue Nov 14 06:54:21 PST 2006
Adds the driver viport files. These files implement the state machine
for the communication protocol with the VEx.
Signed-off-by: Ramachandra K <ramachandra.kuchimanchi at qlogic.com>
---
drivers/infiniband/ulp/vnic/vnic_viport.c | 1023 +++++++++++++++++++++++++++++
drivers/infiniband/ulp/vnic/vnic_viport.h | 165 +++++
2 files changed, 1188 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..722a18e
--- /dev/null
+++ b/drivers/infiniband/ulp/vnic/vnic_viport.c
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (c) 2006 QLogic, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/netdevice.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+
+#include "vnic_util.h"
+#include "vnic_main.h"
+#include "vnic_viport.h"
+#include "vnic_netpath.h"
+#include "vnic_control.h"
+#include "vnic_data.h"
+#include "vnic_config.h"
+#include "vnic_control_pkt.h"
+
+#define VIPORT_DISCONN_TIMER 10000 /*in ms*/
+
+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 = kzalloc(sizeof *viport, GFP_KERNEL);
+ if (!viport) {
+ VIPORT_ERROR("failed allocating viport structure\n");
+ return NULL;
+ }
+
+ 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;
+}
+
+void viport_connect(struct viport * viport, int delay)
+{
+ VIPORT_FUNCTION("viport_connect()\n");
+
+ if (delay)
+ viport->connect = DELAY;
+ else
+ viport->connect = NOW;
+
+ viport_kick(viport);
+}
+
+void viport_disconnect(struct viport *viport)
+{
+ VIPORT_FUNCTION("viport_disconnect()\n");
+ viport->disconnect = 1;
+ viport_failure(viport);
+ wait_event(viport->disconnect_queue, viport->disconnect == 0);
+}
+
+void viport_free(struct viport *viport)
+{
+ VIPORT_FUNCTION("viport_free()\n");
+ viport_disconnect(viport); /* NOTE: this can sleep */
+ kfree(viport->config);
+ kfree(viport);
+}
+
+void viport_set_link(struct viport * viport, u16 flags, u16 mtu)
+{
+ unsigned long localflags;
+
+ VIPORT_FUNCTION("viport_set_link()\n");
+ if (mtu > data_max_mtu(&viport->data)) {
+ VIPORT_ERROR("configuration error."
+ " mtu of %d unsupported by %s\n", mtu,
+ config_viport_name(viport->config));
+ goto failure;
+ }
+
+ spin_lock_irqsave(&viport->lock, localflags);
+ flags &= IFF_UP | IFF_ALLMULTI | IFF_PROMISC;
+ if ((viport->new_flags != flags)
+ || (viport->new_mtu != mtu)) {
+ viport->new_flags = flags;
+ viport->new_mtu = mtu;
+ viport->updates |= NEED_LINK_CONFIG;
+ viport_kick(viport);
+ }
+
+ spin_unlock_irqrestore(&viport->lock, localflags);
+ return;
+failure:
+ viport_failure(viport);
+}
+
+int viport_set_unicast(struct viport * viport, u8 * address)
+{
+ unsigned long flags;
+ int ret = -1;
+ VIPORT_FUNCTION("viport_set_unicast()\n");
+ spin_lock_irqsave(&viport->lock, flags);
+
+ if (!viport->mac_addresses)
+ goto out;
+
+ if (memcmp(viport->mac_addresses[UNICAST_ADDR].address,
+ address, ETH_ALEN)) {
+ memcpy(viport->mac_addresses[UNICAST_ADDR].address,
+ address, ETH_ALEN);
+ viport->mac_addresses[UNICAST_ADDR].operation
+ = VNIC_OP_SET_ENTRY;
+ viport->updates |= NEED_ADDRESS_CONFIG;
+ viport_kick(viport);
+ }
+ ret = 0;
+out:
+ spin_unlock_irqrestore(&viport->lock, flags);
+ return ret;
+}
+
+int viport_set_multicast(struct viport * viport,
+ struct dev_mc_list * mc_list, int mc_count)
+{
+ u32 old_update_list;
+ int i;
+ int ret = -1;
+ unsigned long flags;
+
+ VIPORT_FUNCTION("viport_set_multicast()\n");
+ spin_lock_irqsave(&viport->lock, flags);
+
+ if (!viport->mac_addresses)
+ goto out;
+
+ old_update_list = viport->updates;
+ if (mc_count > viport->num_mac_addresses - MCAST_ADDR_START)
+ viport->updates |= NEED_LINK_CONFIG | MCAST_OVERFLOW;
+ else {
+ if (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, ETH_ALEN))
+ continue;
+ memcpy(viport->mac_addresses[i].address,
+ mc_list->dmi_addr, ETH_ALEN);
+ viport->mac_addresses[i].valid = 1;
+ viport->mac_addresses[i].operation =
+ VNIC_OP_SET_ENTRY;
+ }
+ for (; i < viport->num_mac_addresses; i++) {
+ if (!viport->mac_addresses[i].valid)
+ continue;
+ viport->mac_addresses[i].valid = 0;
+ viport->mac_addresses[i].operation =
+ VNIC_OP_SET_ENTRY;
+ }
+ if (mc_count)
+ viport->updates |= NEED_ADDRESS_CONFIG;
+ }
+
+ if (viport->updates != old_update_list)
+ viport_kick(viport);
+ ret = 0;
+out:
+ spin_unlock_irqrestore(&viport->lock, flags);
+ return ret;
+}
+
+void viport_get_stats(struct viport * viport,
+ struct net_device_stats * stats)
+{
+ unsigned long flags;
+
+ VIPORT_FUNCTION("viport_get_stats()\n");
+ if (jiffies > viport->last_stats_time +
+ viport->config->stats_interval) {
+ spin_lock_irqsave(&viport->lock, flags);
+ viport->updates |= NEED_STATS;
+ spin_unlock_irqrestore(&viport->lock, flags);
+ viport_kick(viport);
+ wait_event(viport->stats_queue,
+ !(viport->updates & NEED_STATS));
+
+ if (viport->stats.ethernet_status)
+ vnic_link_up(viport->vnic, viport->parent);
+ else
+ vnic_link_down(viport->vnic, viport->parent);
+ }
+
+ stats->rx_packets = be64_to_cpu(viport->stats.if_in_ok);
+ stats->tx_packets = be64_to_cpu(viport->stats.if_out_ok);
+ stats->rx_bytes = be64_to_cpu(viport->stats.if_in_octets);
+ stats->tx_bytes = be64_to_cpu(viport->stats.if_out_octets);
+ stats->rx_errors = be64_to_cpu(viport->stats.if_in_errors);
+ stats->tx_errors = be64_to_cpu(viport->stats.if_out_errors);
+ stats->rx_dropped = 0; /* EIOC doesn't track */
+ stats->tx_dropped = 0; /* EIOC doesn't track */
+ stats->multicast = be64_to_cpu(viport->stats.if_in_nucast_pkts);
+ stats->collisions = 0; /* EIOC doesn't track */
+}
+
+int viport_xmit_packet(struct viport * viport, struct sk_buff * skb)
+{
+ int status = -1;
+ unsigned long flags;
+
+ VIPORT_FUNCTION("viport_xmit_packet()\n");
+ spin_lock_irqsave(&viport->lock, flags);
+ if (viport->state == VIPORT_CONNECTED)
+ status = data_xmit_packet(&viport->data, skb);
+ spin_unlock_irqrestore(&viport->lock, flags);
+
+ return status;
+}
+
+void viport_kick(struct viport *viport)
+{
+ unsigned long flags;
+
+ VIPORT_FUNCTION("viport_kick()\n");
+ spin_lock_irqsave(&viport_list_lock, flags);
+ if (list_empty(&viport->list_ptrs)) {
+ list_add_tail(&viport->list_ptrs, &viport_list);
+ wake_up(&viport_queue);
+ }
+ spin_unlock_irqrestore(&viport_list_lock, flags);
+}
+
+void viport_failure(struct viport *viport)
+{
+ unsigned long flags;
+
+ VIPORT_FUNCTION("viport_failure()\n");
+ spin_lock_irqsave(&viport_list_lock, flags);
+ viport->errored = 1;
+ if (list_empty(&viport->list_ptrs)) {
+ list_add_tail(&viport->list_ptrs, &viport_list);
+ wake_up(&viport_queue);
+ }
+ spin_unlock_irqrestore(&viport_list_lock, flags);
+}
+
+static void viport_timeout(unsigned long data)
+{
+ struct viport *viport;
+
+ VIPORT_FUNCTION("viport_timeout()\n");
+ viport = (struct viport *)data;
+ viport->timer_active = 0;
+ viport_kick(viport);
+}
+
+static void viport_timer(struct viport *viport, int timeout)
+{
+ VIPORT_FUNCTION("viport_timer()\n");
+ if (viport->timer_active)
+ del_timer(&viport->timer);
+ init_timer(&viport->timer);
+ viport->timer.expires = jiffies + timeout;
+ viport->timer.data = (unsigned long)viport;
+ viport->timer.function = viport_timeout;
+ viport->timer_active = 1;
+ add_timer(&viport->timer);
+}
+
+static void viport_timer_stop(struct viport *viport)
+{
+ VIPORT_FUNCTION("viport_timer_stop()\n");
+ if (viport->timer_active)
+ del_timer(&viport->timer);
+ viport->timer_active = 0;
+}
+
+static int viport_init_mac_addresses(struct viport *viport)
+{
+ struct vnic_address_op *temp;
+ unsigned long flags;
+ int i;
+
+ VIPORT_FUNCTION("viport_init_mac_addresses()\n");
+ i = viport->num_mac_addresses * sizeof *temp;
+ temp = kzalloc(viport->num_mac_addresses * sizeof *temp,
+ GFP_KERNEL);
+ if (!temp) {
+ VIPORT_ERROR("failed allocating MAC address table\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_irqsave(&viport->lock, flags);
+ viport->mac_addresses = temp;
+ for (i = 0; i < viport->num_mac_addresses; i++) {
+ viport->mac_addresses[i].index = cpu_to_be16(i);
+ viport->mac_addresses[i].vlan =
+ cpu_to_be16(viport->default_vlan);
+ }
+ memset(viport->mac_addresses[BROADCAST_ADDR].address,
+ 0xFF, ETH_ALEN);
+ viport->mac_addresses[BROADCAST_ADDR].valid = 1;
+ memcpy(viport->mac_addresses[UNICAST_ADDR].address,
+ viport->hw_mac_address, ETH_ALEN);
+ viport->mac_addresses[UNICAST_ADDR].valid = 1;
+
+ spin_unlock_irqrestore(&viport->lock, flags);
+
+ return 0;
+}
+
+static int viport_handle_init_states(struct viport *viport)
+{
+ enum link_state old_state;
+
+ do {
+ switch(old_state = viport->link_state) {
+ case LINK_UNINITIALIZED:
+ LINK_STATE("state LINK_UNINITIALIZED\n");
+ viport->updates = 0;
+ 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)) {
+ ib_dealloc_pd(viport->pd);
+ viport->link_state = LINK_DISCONNECTED;
+
+ }
+ else
+ viport->link_state = LINK_INITIALIZEDATA;
+ break;
+ case LINK_INITIALIZEDATA:
+ LINK_STATE("state LINK_INITIALIZEDATA\n");
+ if (data_init(&viport->data, viport,
+ &viport->config->data_config,
+ viport->pd))
+ viport->link_state = LINK_CLEANUPCONTROL;
+ else
+ viport->link_state = LINK_CONTROLCONNECT;
+ break;
+ default:
+ return -1;
+ }
+ } while (viport->link_state != old_state);
+
+ return 0;
+}
+
+static int viport_handle_control_states(struct viport *viport)
+{
+ enum link_state old_state;
+
+ do {
+ switch(old_state = viport->link_state) {
+ case LINK_CONTROLCONNECT:
+ init_completion(&(viport->control.ib_conn.done));
+ if (vnic_ib_cm_connect(&viport->control.ib_conn))
+ viport->link_state = LINK_CLEANUPDATA;
+ else
+ viport->link_state = LINK_CONTROLCONNECTWAIT;
+ break;
+ case LINK_CONTROLCONNECTWAIT:
+ LINK_STATE("state LINK_CONTROLCONNECTWAIT\n");
+ 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_RESETCONTROL;
+ else
+ viport->link_state = LINK_INITVNICRSP;
+ break;
+ case LINK_INITVNICRSP:
+ LINK_STATE("state LINK_INITVNICRSP\n");
+ control_process_async(&viport->control);
+
+ if (!control_init_vnic_rsp(&viport->control,
+ &viport->features_supported,
+ viport->hw_mac_address,
+ &viport->num_mac_addresses,
+ &viport->default_vlan)) {
+ if (viport_init_mac_addresses(viport))
+ viport->link_state =
+ LINK_RESETCONTROL;
+ else
+ viport->link_state =
+ LINK_BEGINDATAPATH;
+ }
+
+ if (viport->errored) {
+ viport->errored = 0;
+ viport->link_state = LINK_RESETCONTROL;
+ }
+ break;
+ default:
+ return -1;
+ }
+ } while(viport->link_state != old_state);
+
+ return 0;
+}
+
+static int viport_handle_data_states(struct viport *viport)
+{
+ enum link_state old_state;
+
+ do {
+ switch(old_state = viport->link_state) {
+ case LINK_BEGINDATAPATH:
+ LINK_STATE("state LINK_BEGINDATAPATH\n");
+ viport->link_state = LINK_CONFIGDATAPATHREQ;
+ break;
+ case LINK_CONFIGDATAPATHREQ:
+ LINK_STATE("state LINK_CONFIGDATAPATHREQ\n");
+ if (control_config_data_path_req(&viport->control,
+ data_path_id(&viport->
+ data),
+ data_host_pool_max
+ (&viport->data),
+ data_eioc_pool_max
+ (&viport->data)))
+ viport->link_state = LINK_RESETCONTROL;
+ else
+ viport->link_state = LINK_CONFIGDATAPATHRSP;
+ break;
+ case LINK_CONFIGDATAPATHRSP:
+ LINK_STATE("state LINK_CONFIGDATAPATHRSP\n");
+ control_process_async(&viport->control);
+
+ if (!control_config_data_path_rsp(&viport->control,
+ data_host_pool
+ (&viport->data),
+ data_eioc_pool
+ (&viport->data),
+ data_host_pool_max
+ (&viport->data),
+ data_eioc_pool_max
+ (&viport->data),
+ data_host_pool_min
+ (&viport->data),
+ data_eioc_pool_min
+ (&viport->data)))
+ viport->link_state = LINK_DATACONNECT;
+
+ if (viport->errored) {
+ viport->errored = 0;
+ viport->link_state = LINK_RESETCONTROL;
+ }
+ break;
+ case LINK_DATACONNECT:
+ LINK_STATE("state LINK_DATACONNECT\n");
+ init_completion(&viport->data.ib_conn.done);
+ if (data_connect(&viport->data))
+ viport->link_state = LINK_RESETCONTROL;
+ else
+ viport->link_state = LINK_DATACONNECTWAIT;
+ break;
+ case LINK_DATACONNECTWAIT:
+ LINK_STATE("state LINK_DATACONNECTWAIT\n");
+ 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;
+ default:
+ return -1;
+ }
+ } while(viport->link_state != old_state);
+
+ return 0;
+}
+
+static int viport_handle_xchgpool_states(struct viport *viport)
+{
+ enum link_state old_state;
+
+ do {
+ switch(old_state = viport->link_state) {
+ case LINK_XCHGPOOLREQ:
+ LINK_STATE("state LINK_XCHGPOOLREQ\n");
+ if (control_exchange_pools_req(&viport->control,
+ data_local_pool_addr
+ (&viport->data),
+ data_local_pool_rkey
+ (&viport->data)))
+ viport->link_state = LINK_RESET;
+ else
+ viport->link_state = LINK_XCHGPOOLRSP;
+ break;
+ case LINK_XCHGPOOLRSP:
+ LINK_STATE("state LINK_XCHGPOOLRSP\n");
+ control_process_async(&viport->control);
+
+ if (!control_exchange_pools_rsp(&viport->control,
+ data_remote_pool_addr
+ (&viport->data),
+ data_remote_pool_rkey
+ (&viport->data)))
+ viport->link_state = LINK_INITIALIZED;
+
+ if (viport->errored) {
+ viport->errored = 0;
+ viport->link_state = LINK_RESET;
+ }
+ break;
+ case LINK_INITIALIZED:
+ LINK_STATE("state LINK_INITIALIZED\n");
+ viport->state = VIPORT_CONNECTED;
+ printk(KERN_INFO PFX
+ "%s: connection established\n",
+ config_viport_name(viport->config));
+ data_connected(&viport->data);
+ vnic_connected(viport->parent->parent,
+ viport->parent);
+ 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;
+ default:
+ return -1;
+ }
+ } while(viport->link_state != old_state);
+
+ return 0;
+}
+
+static int viport_handle_idle_states(struct viport *viport)
+{
+ enum link_state old_state;
+
+ do {
+ switch(old_state = viport->link_state) {
+ case LINK_IDLE:
+ LINK_STATE("state LINK_IDLE\n");
+ if (viport->config->hb_interval)
+ viport_timer(viport,
+ viport->config->hb_interval);
+ viport->link_state = LINK_IDLING;
+ break;
+ case LINK_IDLING:
+ LINK_STATE("state LINK_IDLING\n");
+ control_process_async(&viport->control);
+ if (viport->errored) {
+ viport_timer_stop(viport);
+ viport->errored = 0;
+ viport->link_state = LINK_RESET;
+ break;
+ }
+
+ spin_lock_irq(&viport->lock);
+ 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;
+ default:
+ return -1;
+ }
+ } while(viport->link_state != old_state);
+
+ return 0;
+}
+
+static int viport_handle_config_states(struct viport *viport)
+{
+ enum link_state old_state;
+ int res;
+
+ do {
+ switch(old_state = viport->link_state) {
+ case LINK_CONFIGLINKREQ:
+ LINK_STATE("state LINK_CONFIGLINKREQ\n");
+ spin_lock_irq(&viport->lock);
+ viport->updates &= ~NEED_LINK_CONFIG;
+ viport->flags = viport->new_flags;
+ if (viport->updates & MCAST_OVERFLOW)
+ viport->flags |= IFF_ALLMULTI;
+ viport->mtu = viport->new_mtu;
+ spin_unlock_irq(&viport->lock);
+ if (control_config_link_req(&viport->control,
+ viport->flags,
+ viport->mtu))
+ viport->link_state = LINK_RESET;
+ else
+ viport->link_state = LINK_CONFIGLINKRSP;
+ break;
+ case LINK_CONFIGLINKRSP:
+ LINK_STATE("state LINK_CONFIGLINKRSP\n");
+ control_process_async(&viport->control);
+
+ if (!control_config_link_rsp(&viport->control,
+ &viport->flags,
+ &viport->mtu))
+ viport->link_state = LINK_IDLE;
+
+ if (viport->errored) {
+ viport->errored = 0;
+ viport->link_state = LINK_RESET;
+ }
+ break;
+ case LINK_CONFIGADDRSREQ:
+ LINK_STATE("state LINK_CONFIGADDRSREQ\n");
+
+ spin_lock_irq(&viport->lock);
+ res = control_config_addrs_req(&viport->control,
+ viport->mac_addresses,
+ viport->
+ num_mac_addresses);
+
+ if (res > 0) {
+ viport->updates &= ~NEED_ADDRESS_CONFIG;
+ viport->link_state = LINK_CONFIGADDRSRSP;
+ } else if (res == 0)
+ viport->link_state = LINK_CONFIGADDRSRSP;
+ else
+ viport->link_state = LINK_RESET;
+ spin_unlock_irq(&viport->lock);
+ break;
+ case LINK_CONFIGADDRSRSP:
+ LINK_STATE("state LINK_CONFIGADDRSRSP\n");
+ control_process_async(&viport->control);
+
+ if (!control_config_addrs_rsp(&viport->control))
+ viport->link_state = LINK_IDLE;
+
+ if (viport->errored) {
+ viport->errored = 0;
+ viport->link_state = LINK_RESET;
+ }
+ break;
+ default:
+ return -1;
+ }
+ } while(viport->link_state != old_state);
+
+ return 0;
+}
+
+static int viport_handle_stat_states(struct viport *viport)
+{
+ enum link_state old_state;
+
+ do {
+ switch(old_state = viport->link_state) {
+ case LINK_REPORTSTATREQ:
+ LINK_STATE("state LINK_REPORTSTATREQ\n");
+ if (control_report_statistics_req(&viport->control))
+ viport->link_state = LINK_RESET;
+ else
+ viport->link_state = LINK_REPORTSTATRSP;
+ break;
+ case LINK_REPORTSTATRSP:
+ LINK_STATE("state LINK_REPORTSTATRSP\n");
+ control_process_async(&viport->control);
+
+ spin_lock_irq(&viport->lock);
+ if (control_report_statistics_rsp(&viport->control,
+ &viport->stats) == 0) {
+ viport->updates &= ~NEED_STATS;
+ viport->last_stats_time = jiffies;
+ wake_up(&viport->stats_queue);
+ viport->link_state = LINK_IDLE;
+ }
+
+ spin_unlock_irq(&viport->lock);
+
+ if (viport->errored) {
+ viport->errored = 0;
+ viport->link_state = LINK_RESET;
+ }
+ break;
+ default:
+ return -1;
+ }
+ } while(viport->link_state != old_state);
+
+ return 0;
+}
+
+static int viport_handle_heartbeat_states(struct viport *viport)
+{
+ enum link_state old_state;
+
+ do {
+ switch(old_state = viport->link_state) {
+ case LINK_HEARTBEATREQ:
+ LINK_STATE("state LINK_HEARTBEATREQ\n");
+ if (control_heartbeat_req(&viport->control,
+ viport->config->hb_timeout))
+ viport->link_state = LINK_RESET;
+ else
+ viport->link_state = LINK_HEARTBEATRSP;
+ break;
+ case LINK_HEARTBEATRSP:
+ LINK_STATE("state LINK_HEARTBEATRSP\n");
+ control_process_async(&viport->control);
+
+ if (!control_heartbeat_rsp(&viport->control))
+ viport->link_state = LINK_IDLE;
+
+ if (viport->errored) {
+ viport->errored = 0;
+ viport->link_state = LINK_RESET;
+ }
+ break;
+ default:
+ return -1;
+ }
+ } while(viport->link_state != old_state);
+
+ return 0;
+}
+
+static int viport_handle_reset_states(struct viport *viport)
+{
+ enum link_state old_state;
+
+ do {
+ switch(old_state = viport->link_state) {
+ case LINK_RESET:
+ LINK_STATE("state LINK_RESET\n");
+ viport->errored = 0;
+ spin_lock_irq(&viport->lock);
+ viport->state = VIPORT_DISCONNECTED;
+ spin_unlock_irq(&viport->lock);
+ vnic_link_down(viport->vnic, viport->parent);
+ printk(KERN_INFO PFX
+ "%s: connection lost\n",
+ config_viport_name(viport->config));
+ if (control_reset_req(&viport->control))
+ viport->link_state = LINK_DATADISCONNECT;
+ else
+ viport->link_state = LINK_RESETRSP;
+ break;
+ case LINK_RESETRSP:
+ LINK_STATE("state LINK_RESETRSP\n");
+ control_process_async(&viport->control);
+
+ if (!control_reset_rsp(&viport->control))
+ viport->link_state = LINK_DATADISCONNECT;
+
+ if (viport->errored) {
+ viport->errored = 0;
+ viport->link_state = LINK_DATADISCONNECT;
+ }
+ break;
+ case LINK_RESETCONTROL:
+ LINK_STATE("state LINK_RESETCONTROL\n");
+ if (control_reset_req(&viport->control))
+ viport->link_state = LINK_CONTROLDISCONNECT;
+ else
+ viport->link_state = LINK_RESETCONTROLRSP;
+ break;
+ case LINK_RESETCONTROLRSP:
+ LINK_STATE("state LINK_RESETCONTROLRSP\n");
+ control_process_async(&viport->control);
+
+ if (!control_reset_rsp(&viport->control))
+ viport->link_state = LINK_CONTROLDISCONNECT;
+
+ if (viport->errored) {
+ viport->errored = 0;
+ viport->link_state = LINK_CONTROLDISCONNECT;
+ }
+ break;
+ default:
+ return -1;
+ }
+ } while(viport->link_state != old_state);
+
+ return 0;
+}
+
+static int viport_handle_disconn_states(struct viport *viport)
+{
+ enum link_state old_state;
+
+ do {
+ switch(old_state = viport->link_state) {
+ case LINK_DATADISCONNECT:
+ LINK_STATE("state LINK_DATADISCONNECT\n");
+ data_disconnect(&viport->data);
+ viport->link_state = LINK_CONTROLDISCONNECT;
+ break;
+ case LINK_CONTROLDISCONNECT:
+ LINK_STATE("state LINK_CONTROLDISCONNECT\n");
+ viport->link_state = LINK_CLEANUPDATA;
+ break;
+ case LINK_CLEANUPDATA:
+ LINK_STATE("state LINK_CLEANUPDATA\n");
+ data_cleanup(&viport->data);
+ viport->link_state = LINK_CLEANUPCONTROL;
+ break;
+ case LINK_CLEANUPCONTROL:
+ LINK_STATE("state LINK_CLEANUPCONTROL\n");
+ spin_lock_irq(&viport->lock);
+ if (viport->mac_addresses) {
+ kfree(viport->mac_addresses);
+ viport->mac_addresses = NULL;
+ }
+ spin_unlock_irq(&viport->lock);
+ control_cleanup(&viport->control);
+ ib_dealloc_pd(viport->pd);
+ viport->link_state = LINK_DISCONNECTED;
+ break;
+ case LINK_DISCONNECTED:
+ LINK_STATE("state LINK_DISCONNECTED\n");
+ vnic_disconnected(viport->parent->parent,
+ viport->parent);
+ if (viport->disconnect != 0)
+ viport->link_state = LINK_UNINITIALIZED;
+ else {
+ viport_timer(viport,
+ msecs_to_jiffies(VIPORT_DISCONN_TIMER));
+ 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;
+ default:
+ return -1;
+ }
+ } while(viport->link_state != old_state);
+
+ return 0;
+}
+
+static int viport_statemachine(void *context)
+{
+ struct viport *viport;
+ enum link_state old_link_state;
+
+ VIPORT_FUNCTION("viport_statemachine()\n");
+ 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);
+
+ do {
+ old_link_state = viport->link_state;
+
+ /*
+ * Optimize for the state machine steady state
+ * by checking for the most common states first.
+ *
+ */
+ if (viport_handle_idle_states(viport) == 0)
+ break;
+ if (viport_handle_heartbeat_states(viport) == 0)
+ break;
+ if (viport_handle_stat_states(viport) == 0)
+ break;
+ if (viport_handle_config_states(viport) == 0)
+ break;
+
+ if (viport_handle_init_states(viport) == 0)
+ break;
+ if (viport_handle_control_states(viport) == 0)
+ break;
+ if (viport_handle_data_states(viport) == 0)
+ break;
+ if (viport_handle_xchgpool_states(viport) == 0)
+ break;
+ if (viport_handle_reset_states(viport) == 0)
+ break;
+ if (viport_handle_disconn_states(viport) == 0)
+ break;
+ } while (viport->link_state != old_link_state);
+ }
+
+ complete_and_exit(&viport_thread_exit, 0);
+}
+
+int viport_start(void)
+{
+ VIPORT_FUNCTION("viport_start()\n");
+
+ viport_thread = kernel_thread(viport_statemachine, NULL, 0);
+ if (viport_thread < 0) {
+ printk(KERN_WARNING PFX "Could not create viport_thread;"
+ " error %d\n", viport_thread);
+ return viport_thread;
+ }
+
+ return 0;
+}
+
+void viport_cleanup(void)
+{
+ VIPORT_FUNCTION("viport_cleanup()\n");
+ if (viport_thread > 0) {
+ viport_thread_end = 1;
+ wake_up(&viport_queue);
+ wait_for_completion(&viport_thread_exit);
+ }
+}
diff --git a/drivers/infiniband/ulp/vnic/vnic_viport.h b/drivers/infiniband/ulp/vnic/vnic_viport.h
new file mode 100644
index 0000000..4a3060a
--- /dev/null
+++ b/drivers/infiniband/ulp/vnic/vnic_viport.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2006 QLogic, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef VNIC_VIPORT_H_INCLUDED
+#define VNIC_VIPORT_H_INCLUDED
+
+#include "vnic_control.h"
+#include "vnic_data.h"
+
+enum viport_state {
+ VIPORT_DISCONNECTED = 0,
+ VIPORT_CONNECTED = 1
+};
+
+enum link_state {
+ LINK_UNINITIALIZED = 0,
+ LINK_INITIALIZE = 1,
+ LINK_INITIALIZECONTROL = 2,
+ LINK_INITIALIZEDATA = 3,
+ LINK_CONTROLCONNECT = 4,
+ LINK_CONTROLCONNECTWAIT = 5,
+ LINK_INITVNICREQ = 6,
+ LINK_INITVNICRSP = 7,
+ LINK_BEGINDATAPATH = 8,
+ LINK_CONFIGDATAPATHREQ = 9,
+ LINK_CONFIGDATAPATHRSP = 10,
+ LINK_DATACONNECT = 11,
+ LINK_DATACONNECTWAIT = 12,
+ LINK_XCHGPOOLREQ = 13,
+ LINK_XCHGPOOLRSP = 14,
+ LINK_INITIALIZED = 15,
+ LINK_IDLE = 16,
+ LINK_IDLING = 17,
+ LINK_CONFIGLINKREQ = 18,
+ LINK_CONFIGLINKRSP = 19,
+ LINK_CONFIGADDRSREQ = 20,
+ LINK_CONFIGADDRSRSP = 21,
+ LINK_REPORTSTATREQ = 22,
+ LINK_REPORTSTATRSP = 23,
+ LINK_HEARTBEATREQ = 24,
+ LINK_HEARTBEATRSP = 25,
+ LINK_RESET = 26,
+ LINK_RESETRSP = 27,
+ LINK_RESETCONTROL = 28,
+ LINK_RESETCONTROLRSP = 29,
+ LINK_DATADISCONNECT = 30,
+ LINK_CONTROLDISCONNECT = 31,
+ LINK_CLEANUPDATA = 32,
+ LINK_CLEANUPCONTROL = 33,
+ LINK_DISCONNECTED = 34,
+ LINK_RETRYWAIT = 35
+};
+
+enum {
+ BROADCAST_ADDR = 0,
+ UNICAST_ADDR = 1,
+ MCAST_ADDR_START = 2
+};
+
+#define current_mac_address mac_addresses[UNICAST_ADDR].address
+
+enum {
+ NEED_STATS = 0x00000001,
+ NEED_ADDRESS_CONFIG = 0x00000002,
+ NEED_LINK_CONFIG = 0x00000004,
+ MCAST_OVERFLOW = 0x00000008
+};
+
+struct viport {
+ struct list_head list_ptrs;
+ struct netpath *parent;
+ struct vnic *vnic;
+ struct viport_config *config;
+ struct control control;
+ struct data data;
+ spinlock_t lock;
+ struct ib_pd *pd;
+ enum viport_state state;
+ enum link_state link_state;
+ struct vnic_cmd_report_stats_rsp stats;
+ wait_queue_head_t stats_queue;
+ u32 last_stats_time;
+ u32 features_supported;
+ u8 hw_mac_address[ETH_ALEN];
+ u16 default_vlan;
+ u16 num_mac_addresses;
+ struct vnic_address_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;
+ int timer_active;
+ struct timer_list timer;
+};
+
+int viport_start(void);
+void viport_cleanup(void);
+
+struct viport *viport_allocate(struct viport_config *config);
+void viport_free(struct viport *viport);
+
+void viport_connect(struct viport *viport, int delay);
+void viport_disconnect(struct viport *viport);
+
+void viport_set_link(struct viport *viport, u16 flags, u16 mtu);
+void viport_get_stats(struct viport *viport,
+ struct net_device_stats *stats);
+int viport_xmit_packet(struct viport *viport, struct sk_buff *skb);
+void viport_kick(struct viport *viport);
+
+void viport_failure(struct viport *viport);
+
+int viport_set_unicast(struct viport *viport, u8 * address);
+int viport_set_multicast(struct viport *viport,
+ struct dev_mc_list *mc_list,
+ int mc_count);
+
+#define viport_max_mtu(viport) data_max_mtu(&(viport)->data)
+
+#define viport_get_hw_addr(viport, address) \
+ memcpy(address, (viport)->hw_mac_address, ETH_ALEN)
+
+#define viport_features(viport) ((viport)->features_supported)
+
+#define viport_can_tx_csum(viport) \
+ (((viport)->features_supported & \
+ (VNIC_FEAT_IPV4_CSUM_TX | VNIC_FEAT_TCP_CSUM_TX | \
+ VNIC_FEAT_UDP_CSUM_TX)) == (VNIC_FEAT_IPV4_CSUM_TX | \
+ VNIC_FEAT_TCP_CSUM_TX | VNIC_FEAT_UDP_CSUM_TX))
+
+#endif /* VNIC_VIPORT_H_INCLUDED */
More information about the general
mailing list