[ewg] [PATCH 5/9] [RFC] Add support for Xsigo configuration protocol manager (XCPM)
Hal Rosenstock
hrosenstock at xsigo.com
Fri Apr 4 06:18:56 PDT 2008
Add support for the Xsigo configuration protocol manager (XCPM).
The Xsigo Configuration Protocol Manager (XCPM) manages sessions between
a host and the control plane of the Xsigo chassis. It is also the "hub"
for all control information flow between the host V* drivers and the
Xsigo chassis.
Signed-off-by: Hal Rosenstock <hal at xsigo.com>
---
drivers/infiniband/ulp/xsigo/xscore/xcpm.c | 1161 ++++++++++++++++++++
drivers/infiniband/ulp/xsigo/xscore/xcpm_export.h | 81 ++
.../infiniband/ulp/xsigo/xscore/xcpm_interface.h | 151 +++
drivers/infiniband/ulp/xsigo/xscore/xcpm_priv.h | 161 +++
4 files changed, 1554 insertions(+), 0 deletions(-)
create mode 100644 drivers/infiniband/ulp/xsigo/xscore/xcpm.c
create mode 100644 drivers/infiniband/ulp/xsigo/xscore/xcpm_export.h
create mode 100644 drivers/infiniband/ulp/xsigo/xscore/xcpm_interface.h
create mode 100644 drivers/infiniband/ulp/xsigo/xscore/xcpm_priv.h
diff --git a/drivers/infiniband/ulp/xsigo/xscore/xcpm.c b/drivers/infiniband/ulp/xsigo/xscore/xcpm.c
new file mode 100644
index 0000000..5d3cbe3
--- /dev/null
+++ b/drivers/infiniband/ulp/xsigo/xscore/xcpm.c
@@ -0,0 +1,1161 @@
+/*
+ * Copyright (c) 2006-2008 Xsigo Systems 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.
+ *
+ */
+
+/*
+ * The Xsigo Configuration Protocol Manager (XCPM) manages sessions between
+ * a host and the control plane of the Xsigo chassis. It is also the "hub"
+ * for all control information flow between the host V* drivers and the
+ * Xsigo chassis.
+ */
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <rdma/ib_cache.h>
+
+#include "ib_if.h"
+#include "xsmp.h"
+#include "xcpm_priv.h"
+#include "xcpm_export.h"
+#include "xcpm_stats.h"
+
+#define XCPM_VERSION "0.21"
+
+#define XCPM_STACK_DELAY (2 * HZ)
+
+static int check_recv_seq_number(int link, u32 recv_seq_number);
+static void seq_number_action(int link, u32 recv_seq_number);
+
+/* Delay the start of the init sequence, allow the stack to stabilize */
+static void xcpm_startup_work_handler(struct work_struct *work);
+
+/* The XCPM data structure */
+struct xcpm_info *xcpm = NULL;
+
+/* Support timeouts on links */
+static int noconntimeout = 0;
+module_param(noconntimeout, int, 0);
+
+/* Disable recovery/reconnect to the peer */
+static int norecovery = 0;
+module_param(norecovery, int, 0);
+
+#ifdef CONFIG_XSCORE_DEBUG
+int xcpm_debug_level = 0;
+module_param(xcpm_debug_level, int, 0);
+#endif
+
+static int stats_frequency = 20;
+module_param(stats_frequency, int, 0);
+
+static int port_sweep_timeout = XCPM_PORT_SWEEP_INTERVAL_SECS;
+module_param(port_sweep_timeout, int, 0);
+
+int boot_flag = 0;
+module_param(boot_flag, int, 0);
+
+wait_queue_head_t xcpm_wait;
+
+struct workqueue_struct *xcpm_wq = NULL;
+
+extern struct kmem_cache *xsmp_cachep;
+
+
+/* Send out a message on the send queue of the 'link' */
+int transmit_to_xcm(int link, u8 *data, int length)
+{
+ struct link_info *plink = &xcpm->links[link];
+ unsigned long flags;
+ int ret;
+
+ if (length <= 0) {
+ xcpm_debug(KERN_ERR, "Invalid length: %d\n", length);
+ log_link_error(link, XSMP_MESSAGE_LENGTH_INVALID);
+ ret = -EINVAL;
+ goto transmit_exit;
+ }
+
+ spin_lock_irqsave(&xcpm->link_lock, flags);
+
+ /* Allow only in LINK_INIT and LINK_UP states */
+ if (plink->link_state != LINK_UP &&
+ plink->link_state != LINK_INIT) {
+ spin_unlock_irqrestore(&xcpm->link_lock, flags);
+ xcpm_debug(KERN_DEBUG,
+ "Attempting to transmit, link <0x%x> is down or "
+ "non-existent\n", link);
+ ret = -ENOLINK;
+ goto transmit_exit;
+ }
+
+ /* Add a sequence number to the message */
+ xsmp_set_seq_number(data, plink->send_seq_number);
+ plink->send_seq_number++;
+
+ /* Set the source and the destination IDs (GUIDs) */
+ xsmp_set_source_dest_ids(link, data);
+
+ /* Send the message onto the send queue */
+ ret = ib_if_send_msg(&plink->ib_link, data, length);
+
+ spin_unlock_irqrestore(&xcpm->link_lock, flags);
+
+transmit_exit:
+ /* Free the buffer from the cache if the transmit did not succeed */
+ if (ret && xsmp_is_local_msg(data))
+ kmem_cache_free(xsmp_cachep, data);
+
+ return ret;
+}
+
+void log_link_error(int link, int error_type)
+{
+ xcpm->links[link].error_counts[error_type]++;
+}
+
+void send_service_status(link)
+{
+ if (xcpm->links[link].link_state == LINK_UP)
+ xsmp_send_resource_list(link, xcpm->resource_flags);
+}
+
+/* Tell all XCMs what all services (V* drivers) are active */
+void broadcast_service_status(void)
+{
+ int count;
+
+ for (count = 0; count < MAX_NUM_LINKS; count++)
+ if (xcpm->links[count].link_state == LINK_UP)
+ xsmp_send_resource_list(count, xcpm->resource_flags);
+}
+
+int send_error_counts(int link)
+{
+ xcpm_debug(KERN_INFO, "Sending error counts for link %d\n", link);
+
+ return xsmp_send_error_counts(link, xcpm->links[link].error_counts);
+}
+
+/*
+ * Interface functions that the service drivers
+ * use to communicate to the XCPM
+ */
+int xcpm_register_service(struct service_type_info *s_info)
+{
+ int ret = 0;
+ int count;
+ int index;
+ unsigned long flags;
+
+ if (!s_info) {
+ ret = -EINVAL;
+ goto register_service_exit;
+ }
+
+ /* Check for duplicates */
+ for (count = 0; count < MAX_NUM_SVCS; count++)
+ if (xcpm->svcs[count].ctrl_message_type == s_info->ctrl_message_type &&
+ xcpm->svcs[count].svc_state == SERVICE_UP) {
+ ret = -EALREADY;
+ goto register_service_exit;
+ }
+
+ spin_lock_irqsave(&xcpm->interface_lock, flags);
+
+ /* Find an empty slot */
+ for (index = 0; index < MAX_NUM_SVCS; index++)
+ if (xcpm->svcs[index].svc_state != SERVICE_UP)
+ break;
+
+ if (index == MAX_NUM_SVCS) {
+ spin_unlock_irqrestore(&xcpm->interface_lock, flags);
+ ret = -ENOMEM;
+ goto register_service_exit;
+ }
+
+ xcpm->svcs[index].ctrl_message_type = s_info->ctrl_message_type;
+ xcpm->svcs[index].resource_flag_index = s_info->resource_flag_index;
+ xcpm->svcs[index].receive_handler = s_info->receive_handler;
+ xcpm->svcs[index].abort_handler = s_info->abort_handler;
+ xcpm->svcs[index].svc_state = SERVICE_UP;
+ spin_lock_init(&xcpm->svcs[index].callup_lock);
+
+ /*
+ * On the next XSMP register, the presence of
+ * the new service gets reflected
+ */
+ xcpm->resource_flags |= (1 << xcpm->svcs[index].resource_flag_index);
+
+ spin_unlock_irqrestore(&xcpm->interface_lock, flags);
+
+ ret = index;
+
+ /* Tell all connected XCFMs that we have a new service up */
+ broadcast_service_status();
+register_service_exit:
+ return ret;
+}
+EXPORT_SYMBOL(xcpm_register_service);
+
+int xcpm_unregister_service(int service_id)
+{
+ int ret;
+
+ if (service_id < 0 || service_id >= MAX_NUM_SVCS) {
+ ret = -EINVAL;
+ goto unregister_service_exit;
+ }
+
+ if (xcpm->svcs[service_id].svc_state != SERVICE_UP) {
+ ret = -EHOSTDOWN;
+ goto unregister_service_exit;
+ }
+
+ /* Service no longer available */
+ xcpm->svcs[service_id].svc_state = SERVICE_DOWN;
+
+ /* Show the service as down on the next 'register' */
+ xcpm->resource_flags &= ~(1 << xcpm->svcs[service_id].resource_flag_index);
+
+ /* Tell all XCFMs that a service is down */
+ broadcast_service_status();
+
+ ret = 0;
+
+unregister_service_exit:
+ return ret;
+}
+EXPORT_SYMBOL(xcpm_unregister_service);
+
+int xcpm_send_message(int link, int service_id, u8 *data, int length)
+{
+ int ret;
+
+ if (link < 0 || link >= MAX_NUM_LINKS) {
+ ret = -EINVAL;
+ goto send_message_exit;
+ }
+
+ if (service_id < 0 || service_id >= MAX_NUM_SVCS) {
+ ret = -EINVAL;
+ goto send_message_exit;
+ }
+
+ if (xcpm->svcs[service_id].svc_state != SERVICE_UP) {
+ ret = -EHOSTDOWN;
+ goto send_message_exit;
+ }
+
+ /* Transmit the message over the link specified */
+ ret = transmit_to_xcm(link, data, length);
+send_message_exit:
+ return ret;
+}
+EXPORT_SYMBOL(xcpm_send_message);
+
+int xcpm_query_link(int link, struct query_link_info *q_info)
+{
+ int ret;
+ union ib_gid gid;
+
+ if (link < 0 || link >= MAX_NUM_LINKS || !q_info) {
+ ret = -EINVAL;
+ goto query_link_exit;
+ }
+ if (xcpm->links[link].link_state == LINK_DEAD) {
+ ret = -ENOLINK;
+ goto query_link_exit;
+ }
+
+ q_info->device = xcpm->links[link].ib_link.port->device;
+ q_info->port = xcpm->links[link].ib_link.port->port_num;
+ q_info->pd = xcpm->links[link].ib_link.port->pd;
+ q_info->mr = xcpm->links[link].ib_link.port->mr;
+
+ ret = ib_get_cached_gid(xcpm->links[link].ib_link.port->device,
+ xcpm->links[link].ib_link.port->port_num,
+ 0, &gid);
+ if (ret)
+ q_info->dgid.global.subnet_prefix = cpu_to_be64(DEFAULT_SUBNET_PREFIX);
+ else
+ q_info->dgid.global.subnet_prefix = gid.global.subnet_prefix;
+
+ q_info->dgid.global.interface_id = xcpm->links[link].ib_link.link_xcm.port_id;
+ q_info->dlid = xcpm->links[link].ib_link.link_xcm.xcm_lid;
+ q_info->sgid = xcpm->links[link].ib_link.port->gid;
+ q_info->slid = xcpm->links[link].ib_link.port->lid;
+
+query_link_exit:
+ return ret;
+}
+EXPORT_SYMBOL(xcpm_query_link);
+
+/*
+ * Handler that executes when there has been no communication from the XCM
+ * for a certain time period. This indicates that the link is down
+ */
+static void linkstate_timer_handler(struct work_struct *work)
+{
+ struct link_info *plink = container_of(work, struct link_info,
+ linkstate_timer.work);
+ int link = (int)(plink - &xcpm->links[0]);
+ struct timeval tv;
+ unsigned long flags;
+
+ /* Don't do anything if the link already got deleted */
+ if (plink->link_state == LINK_DEAD)
+ return;
+
+ spin_lock_irqsave(&xcpm->link_lock, flags);
+ plink->link_state = LINK_DOWN;
+ spin_unlock_irqrestore(&xcpm->link_lock, flags);
+
+ do_gettimeofday(&tv);
+ xcpm_debug(KERN_WARNING, "Session Timeout, link %d [secs: %d]\n",
+ link, (int) tv.tv_sec);
+ plink->session_timeouts++;
+
+ if (!atomic_read(&xcpm->xcpm_down)) {
+ /* Disconnect the link */
+ bring_down_link(link);
+
+ /* Restart the link timer */
+ queue_delayed_work(xcpm_wq, &plink->linkstate_timer,
+ plink->linkstate_timeout * HZ);
+
+ /* Start the datapath timer if it isn't already */
+ if (!plink->datapath_timer_on &&
+ !plink->datapath_timeout_disabled) {
+ plink->datapath_timer_on = 1;
+ queue_delayed_work(xcpm_wq, &plink->datapath_timer,
+ plink->datapath_timeout * HZ);
+ }
+
+ /* We need this for messages to go out and be received back */
+ spin_lock_irqsave(&xcpm->link_lock, flags);
+ plink->link_state = LINK_INIT;
+ spin_unlock_irqrestore(&xcpm->link_lock, flags);
+
+ plink->send_seq_number = plink->recv_seq_number = 0;
+ plink->hellos_received = plink->hellos_sent = 0;
+ plink->confirmed = 0;
+
+ /* Try connecting again */
+ ib_if_link_connect(link, &xcpm->links[link].ib_link);
+ }
+}
+
+static void set_linkstate(int link, int state)
+{
+ struct link_info *plink = &xcpm->links[link];
+ unsigned long flags;
+
+ spin_lock_irqsave(&xcpm->link_lock, flags);
+ plink->link_state = state;
+ spin_unlock_irqrestore(&xcpm->link_lock, flags);
+}
+
+static void datapath_timer_handler(struct work_struct *work)
+{
+ struct link_info *plink = container_of(work, struct link_info,
+ datapath_timer.work);
+ int link = (int)(plink - &xcpm->links[0]);
+
+ xcpm_debug(KERN_WARNING, "Datapath Timeout, link %d\n", link);
+
+ flush_workqueue(xcpm_wq);
+
+ cancel_delayed_work(&plink->linkstate_timer);
+ cancel_delayed_work(&plink->datapath_timer);
+
+ abort_datapaths_on_link(link);
+ bring_down_link(link);
+
+ /*
+ * The link is disabled
+ * may be restarted next time we hear from the SM/SA
+ */
+ set_linkstate(link, LINK_DISABLED);
+}
+
+/*
+ * Process an incoming message
+ * Either dispatch it to a service driver or use it locally
+ */
+void process_incoming_msg(int link, u8 *buf, int length)
+{
+ int count;
+ int delivered = 0;
+
+ if (length == 0 || buf == 0) {
+ xcpm_debug(KERN_ERR, "Empty message received\n");
+ if (length == 0)
+ log_link_error(link, XSMP_MESSAGE_LENGTH_INVALID);
+ goto process_incoming_msg_exit;
+ }
+
+ /* Make sure that the arriving message has a sequence number in order */
+ if (!check_recv_seq_number(link, xsmp_get_seq_number(buf)))
+ seq_number_action(link, xsmp_get_seq_number(buf));
+
+ if (xsmp_is_local_msg(buf)) {
+ xsmp_process_local_msg(link, buf, length);
+ delivered = 1;
+ goto process_incoming_msg_exit;
+ }
+
+ /*
+ * The message belongs to a service driver
+ * Find the service and deliver the message
+ */
+ for (count = 0; count < MAX_NUM_SVCS; count++) {
+ struct svc_info *service = &xcpm->svcs[count];
+
+ /* If the service is active and the msg belongs to this service */
+ if (service->svc_state == SERVICE_UP &&
+ xsmp_msg_belongs(service->ctrl_message_type, buf)) {
+ if (xcpm->links[link].link_state == LINK_UP &&
+ xcpm->svcs[count].receive_handler)
+ xcpm->svcs[count].receive_handler(link, buf, length);
+ delivered = 1;
+ break;
+ }
+ }
+
+ if (!delivered) {
+ xcpm_debug(KERN_WARNING, "Undeliverable message, dropping...\n");
+ log_link_error(link, XSMP_UNDELIVERABLE_MSG_ERROR);
+ }
+
+process_incoming_msg_exit:
+ return;
+}
+
+/*
+ * Executed by the IB completion handler to process the messages
+ * Runs in the context of a work queue
+ */
+static void receive_and_process_msgs(struct work_struct *work)
+{
+ struct link_info *link_s = container_of(work, struct link_info,
+ msg_dispatch_work);
+ int link = (int)(link_s - &xcpm->links[0]);
+ struct ib_cq *cq;
+
+ /* Ignore messages if the link is down */
+ if (link_s->link_state == LINK_DOWN || link_s->link_state == LINK_DEAD)
+ return;
+
+ cq = ib_if_get_recv_cq(&link_s->ib_link);
+
+ ib_if_recv_comp_handler(&xcpm->links[link].ib_link, cq);
+}
+
+int is_link_alive(int link)
+{
+ if (link >= MAX_NUM_LINKS || link < 0) {
+ xcpm_debug(KERN_WARNING,
+ "Checking state of invalid link index %d\n", link);
+ return 0;
+ }
+
+ return (xcpm->links[link].link_state == LINK_UP);
+}
+
+/* Check if the link got a confirmation */
+int is_link_confirmed(int link)
+{
+ if (link >= MAX_NUM_LINKS || link < 0) {
+ xcpm_debug(KERN_WARNING,
+ "Checking state of invalid link index %d\n", link);
+ return 0;
+ }
+
+ return xcpm->links[link].confirmed;
+}
+
+/* Update the expiry time of the timer */
+void update_linkstate(int link)
+{
+ struct link_info *plink = &xcpm->links[link];
+ struct delayed_work *work = &plink->linkstate_timer;
+ unsigned long flags;
+
+ if (!cancel_delayed_work(work))
+ flush_workqueue(xcpm_wq);
+
+ if (plink->link_state == LINK_DOWN) {
+ xcpm_debug(KERN_WARNING,
+ "Link %d is down, cannot restart the timer\n", link);
+ return;
+ }
+
+ spin_lock_irqsave(&xcpm->link_lock, flags);
+ plink->link_state = LINK_UP;
+ spin_unlock_irqrestore(&xcpm->link_lock, flags);
+
+ if (!atomic_read(&xcpm->xcpm_down))
+ queue_delayed_work(xcpm_wq, work,
+ plink->linkstate_timeout * HZ);
+}
+
+/* Allocate and initialize a link */
+static void link_init(int link_number)
+{
+ struct link_info *link = &xcpm->links[link_number];
+
+ link->link_index = link_number;
+
+ INIT_WORK(&link->msg_dispatch_work, &receive_and_process_msgs);
+ INIT_DELAYED_WORK(&link->linkstate_timer, &linkstate_timer_handler);
+ INIT_DELAYED_WORK(&link->datapath_timer, &datapath_timer_handler);
+
+ link->datapath_timer_on = 0;
+ link->send_seq_number = link->recv_seq_number = 0;
+ link->hellos_received = link->hellos_sent = 0;
+ link->confirmed = 0;
+ link->linkstate_timeout = 3 * XCPM_DEFAULT_HELLO_INTERVAL;
+ link->datapath_timeout = XCPM_LINK_DATAPATH_TIMEOUT_SECS;
+};
+
+/*
+ * Check to see if the recv sequence number is in order
+ * Increment it as a side effect
+ */
+static int check_recv_seq_number(int link, u32 recv_seq_number)
+{
+ int ret;
+ struct link_info *plink = &xcpm->links[link];
+
+ ret = (recv_seq_number >= plink->recv_seq_number);
+
+ plink->recv_seq_number = recv_seq_number + 1;
+
+ return ret;
+}
+
+/*
+ * Action taken when the receive sequence number are detected
+ * out of order on a link
+ */
+static void seq_number_action(int link, u32 recv_seq_number)
+{
+ xcpm_debug(KERN_ERR,
+ "Receive sequence number %d out of order on link: %d...\n",
+ recv_seq_number, link);
+ if (xcpm->links[link].link_state != LINK_UP)
+ printk(KERN_WARNING PFX "link %d not up, so ignoring\n", link);
+ else {
+ printk(KERN_DEBUG PFX "link %d is up\n", link);
+ printk(KERN_DEBUG PFX
+ "issuing a shutdown and aborting datapaths\n");
+
+ xsmp_send_shutdown(link);
+ abort_datapaths_on_link(link);
+ bring_down_link(link);
+ }
+}
+
+/*
+ * Obtain the 'guid' for the port that the logical link resides on
+ * In network byte order
+ */
+u64 get_link_guid(int link)
+{
+ struct link_info *plink = &xcpm->links[link];
+
+ return cpu_to_be64(plink->ib_link.port->guid);
+}
+
+/*
+ * Return the GUID for the destination
+ * In network byte order
+ */
+u64 get_dest_guid(int link)
+{
+ struct link_info *plink = &xcpm->links[link];
+
+ return plink->ib_link.link_xcm.port_id;
+}
+
+/*
+ * Abort the datapaths that were installed via this link
+ * Needs to be used before the link goes down
+ */
+void abort_datapaths_on_link(int link)
+{
+ int count;
+
+ for (count = 0; count < MAX_NUM_SVCS; count++) {
+ if (xcpm->svcs[count].svc_state != SERVICE_UP)
+ continue;
+ if (xcpm->svcs[count].abort_handler)
+ xcpm->svcs[count].abort_handler(link);
+ }
+}
+
+void schedule_port_sweep(struct ib_port_info *port, int fast_poll)
+{
+ unsigned long delay;
+
+ /* Need to query the SA for this port */
+ port->queried = 0;
+
+ /*
+ * Schedule the port sweep operation to restart the link later
+ * If recovery is not disabled
+ */
+ if (!port->port_down && !norecovery) {
+ if (fast_poll)
+ delay = 5 * HZ;
+ else
+ delay = port_sweep_timeout * HZ;
+ queue_delayed_work(xcpm_wq, &port->port_sweep_work, delay);
+ }
+}
+
+/*
+ * Bring down the link: link state will be down
+ * and the link will be unconnected
+ */
+void bring_down_link(int link)
+{
+ struct link_info *plink = &xcpm->links[link];
+ unsigned long flags;
+
+ spin_lock_irqsave(&xcpm->link_lock, flags);
+ if (plink->link_state != LINK_DEAD)
+ plink->link_state = LINK_DOWN;
+ spin_unlock_irqrestore(&xcpm->link_lock, flags);
+
+ plink->send_seq_number = 0;
+ plink->recv_seq_number = 0;
+
+ ib_if_link_exit(&plink->ib_link);
+}
+
+/* Bring down, disconnect, and remove the sysfs entries */
+void delete_link(int link)
+{
+ struct link_info *plink = &xcpm->links[link];
+ unsigned long flags;
+
+ if (plink->link_state == LINK_DEAD)
+ return;
+
+ spin_lock_irqsave(&xcpm->link_lock, flags);
+ plink->link_state = LINK_DEAD;
+ spin_unlock_irqrestore(&xcpm->link_lock, flags);
+
+ abort_datapaths_on_link(link);
+
+ bring_down_link(link);
+
+ /*
+ * The flush is unconditional to make sure that non-delayed
+ * queued work for the link is also completed
+ */
+ cancel_delayed_work(&plink->linkstate_timer);
+ cancel_delayed_work(&plink->datapath_timer);
+
+ flush_workqueue(xcpm_wq);
+
+ cancel_delayed_work(&plink->linkstate_timer);
+ cancel_delayed_work(&plink->datapath_timer);
+
+ xcpm_link_remove_sysfs(link);
+
+ atomic_dec(&xcpm->num_links);
+ xcpm_debug(KERN_INFO, "Number of links now: %d\n",
+ atomic_read(&xcpm->num_links));
+
+ /* Mark as an unused slot */
+ memset(plink, 0, sizeof(*plink));
+ plink->used = 0;
+}
+
+void set_link_ready(int link, int hello_interval, int datapath_timeout)
+{
+ struct link_info *plink = &xcpm->links[link];
+ unsigned long flags;
+
+ spin_lock_irqsave(&xcpm->link_lock, flags);
+ plink->link_state = LINK_UP;
+ spin_unlock_irqrestore(&xcpm->link_lock, flags);
+
+ xcpm_debug(KERN_INFO, "Received a REG_CONFIRM, link %d is up\n",
+ link);
+
+ if (hello_interval < XCPM_MIN_HELLO_INTERVAL)
+ hello_interval = XCPM_MIN_HELLO_INTERVAL;
+ plink->linkstate_timeout = hello_interval * 3;
+
+ xcpm_debug(KERN_INFO, "Hello interval for link %d is %d seconds\n",
+ link, hello_interval);
+
+ if (datapath_timeout == -1) {
+ plink->datapath_timeout_disabled = 1;
+
+ xcpm_debug(KERN_INFO, "Datapath timeout disabled for link %d\n",
+ link);
+ } else {
+ if (datapath_timeout < 2 * plink->linkstate_timeout)
+ datapath_timeout = 2 * plink->linkstate_timeout;
+
+ xcpm_debug(KERN_INFO,
+ "Datapath timeout for link %d is %d seconds\n", link,
+ datapath_timeout);
+ }
+
+ plink->datapath_timeout = datapath_timeout;
+
+ plink->confirmed = 1; /* Got a CONFIRM */
+
+ flush_workqueue(xcpm_wq);
+ cancel_delayed_work(&plink->datapath_timer);
+ plink->datapath_timer_on = 0;
+}
+
+struct ib_port_info *get_ib_port(int port_index)
+{
+ return &xcpm->ports[port_index];
+}
+
+static void xcpm_add_one(struct ib_device *device)
+{
+ int port_num, start_port, end_port;
+
+ xcpm->pd = ib_alloc_pd(device);
+ if (IS_ERR(xcpm->pd)) {
+ printk(KERN_ERR PFX "PD allocation failed %d\n",
+ (int) PTR_ERR(xcpm->pd));
+ return;
+ }
+
+ xcpm->mr = ib_get_dma_mr(xcpm->pd, IB_ACCESS_LOCAL_WRITE |
+ IB_ACCESS_REMOTE_READ |
+ IB_ACCESS_REMOTE_WRITE);
+ if (IS_ERR(xcpm->mr)) {
+ printk(KERN_ERR PFX "MR allocation failed %d\n",
+ (int) PTR_ERR(xcpm->mr));
+ ib_dealloc_pd(xcpm->pd);
+ xcpm->pd = 0;
+ return;
+ }
+
+ if (device->node_type == RDMA_NODE_IB_SWITCH) {
+ start_port = 0;
+ end_port = 0;
+ } else {
+ start_port = 1;
+ end_port = device->phys_port_cnt;
+ }
+ for (port_num = start_port; port_num <= end_port; port_num++) {
+ int port_index;
+ struct ib_port_info *ib_port = 0;
+
+ for (port_index = 0; port_index < MAX_NUM_PORTS; port_index++)
+ if (!xcpm->ports[port_index].used)
+ break;
+
+ if (port_index == MAX_NUM_PORTS) {
+ printk(KERN_WARNING PFX "MAX_NUM_PORTS exceeded\n");
+ return;
+ }
+
+ ib_port = &xcpm->ports[port_index];
+ ib_port->used = 1;
+
+ ib_if_port_init(device, port_num, xcpm->pd, xcpm->mr, ib_port,
+ &ib_port->xds_handle);
+
+ /* Attempt to retrieve all the assigned XCMs */
+ ib_if_sa_query_xds(ib_port);
+
+ atomic_inc(&xcpm->num_ports);
+ }
+}
+
+void increment_hellos_received(int link)
+{
+ xcpm->links[link].hellos_received++;
+ if (xcpm->links[link].hellos_received % stats_frequency ==
+ stats_frequency - 1)
+ send_error_counts(link);
+}
+
+void increment_hellos_sent(int link)
+{
+ xcpm->links[link].hellos_sent++;
+}
+
+int initialize_and_connect_link(int index, struct xcfm_record *xcfm_record,
+ struct ib_port_info *ib_port,
+ struct ib_link_info *ib_link)
+{
+ int ret;
+ struct link_info *plink = &xcpm->links[index];
+
+ ib_if_link_init(index, xcfm_record, ib_port, ib_link);
+
+ /*
+ * Setup a new link, increment the link count,
+ * set link state to 'LINK_INIT'
+ */
+ link_init(index);
+
+ set_linkstate(index, LINK_INIT);
+
+ if (!atomic_read(&xcpm->xcpm_down)) {
+ if (!noconntimeout)
+ queue_delayed_work(xcpm_wq,
+ &xcpm->links[index].
+ linkstate_timer,
+ plink->linkstate_timeout * HZ);
+ }
+
+ /* Connect to the XCM using the CM and register with the XCM */
+ if ((ret = ib_if_link_connect(index, &xcpm->links[index].ib_link))) {
+ bring_down_link(index);
+ printk(KERN_WARNING PFX "link connect error %d\n", ret);
+ }
+
+ return ret;
+}
+
+int add_link(int index, struct xcfm_record *xcfm_record,
+ struct ib_port_info *ib_port, struct ib_link_info *ib_link)
+{
+ int ret = initialize_and_connect_link(index, xcfm_record, ib_port,
+ ib_link);
+ if (!ret) {
+ xcpm_link_add_sysfs(index);
+ atomic_inc(&xcpm->num_links);
+ }
+ return ret;
+}
+
+/*
+ * For a given device and port, we have received a list of XCMs
+ * so we allocate the port and the links here
+ */
+void allocate_port_and_links(struct ib_port_info *ib_port,
+ struct xcm_list *list)
+{
+ int count, index;
+ u8 link_seen[MAX_NUM_LINKS];
+ unsigned long flags;
+
+ memset(link_seen, 0, MAX_NUM_LINKS);
+
+ /* For all the XCMs for this port */
+ for (count = 0; count < list->count; count++) {
+ int exists = 0;
+
+ /* Let's see if a link for this record already exists */
+
+ /* For all existing links */
+ for (index = 0; index < MAX_NUM_LINKS; index++) {
+
+ /*
+ * Don't even look at the unused slots
+ * If the info comes again, a new link will be
+ * created
+ */
+ if (!xcpm->links[index].used)
+ continue;
+
+ /* A different port */
+ if (xcpm->links[index].ib_link.port != ib_port)
+ continue;
+
+ /* No match */
+ if (!(ib_if_link_match(&list->xcms[count],
+ &xcpm->links[index].ib_link)))
+ continue;
+
+ exists = 1;
+
+ /* Mark the existing matched link as seen */
+ link_seen[index] = 1;
+
+ /* Match, but the link is in use */
+ if (xcpm->links[index].link_state != LINK_DISABLED)
+ continue;
+
+ /* Match and the link is down */
+
+ /* Restart the link */
+ if (initialize_and_connect_link(index,
+ &list->xcms[count],
+ ib_port,
+ &xcpm->links[index].
+ ib_link))
+ continue;
+ }
+
+ /* We had a match earlier */
+ if (exists)
+ continue;
+
+ spin_lock_irqsave(&xcpm->link_lock, flags);
+
+ /* Find an unused slot */
+ for (index = 0; index < MAX_NUM_LINKS; index++) {
+ if (!xcpm->links[index].used) {
+ xcpm->links[index].used = 1;
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&xcpm->link_lock, flags);
+
+ if (index == MAX_NUM_LINKS) {
+ xcpm_debug(KERN_ERR,
+ "Limit reached for the number of links: %d\n",
+ MAX_NUM_LINKS);
+ break;
+ }
+
+ /* Mark the new link as seen */
+ link_seen[index] = 1;
+
+ /* Add the newly discovered link */
+ if (add_link(index, &list->xcms[count], ib_port,
+ &xcpm->links[index].ib_link))
+ continue;
+
+ xcpm_debug(KERN_INFO, "Number of links now: %d\n",
+ atomic_read(&xcpm->num_links));
+ }
+
+ /* Delete all the stale links that don't match the list */
+ for (index = 0; index < MAX_NUM_LINKS; index++) {
+ if (!link_seen[index] &&
+ xcpm->links[index].ib_link.port &&
+ xcpm->links[index].ib_link.port == ib_port &&
+ xcpm->links[index].link_state != LINK_DEAD) {
+ xcpm_debug(KERN_INFO, "Deleting stale link: %d\n", index);
+ delete_link(index);
+ }
+ }
+
+}
+
+void startup_link(int index, u64 fw_ver, u32 hw_ver, u32 vendor_part_id)
+{
+ xsmp_link_connect_send(index, xcpm->resource_flags, fw_ver, hw_ver,
+ vendor_part_id);
+}
+
+static void xcpm_remove_one(struct ib_device *device)
+{
+ int port_num, link_index, port_index, start_port, end_port;
+
+ if (device->node_type == RDMA_NODE_IB_SWITCH) {
+ start_port = 0;
+ end_port = 0;
+ } else {
+ start_port = 1;
+ end_port = device->phys_port_cnt;
+ }
+
+ for (port_num = start_port; port_num <= end_port; port_num++) {
+ struct ib_port_info *ib_port = 0;
+
+ xcpm_debug(KERN_INFO, "Number of links: %d\n",
+ atomic_read(&xcpm->num_links));
+
+ /* Find the port */
+ for (port_index = 0; port_index < MAX_NUM_PORTS; port_index++)
+ /* If there is a port match */
+ if (xcpm->ports[port_index].device == device &&
+ xcpm->ports[port_index].port_num == port_num &&
+ xcpm->ports[port_index].used)
+ ib_port = &xcpm->ports[port_index];
+
+ if (!ib_port)
+ continue;
+
+ ib_port->port_down = 1;
+
+ if (!wait_event_timeout(xcpm_wait,
+ !atomic_read(&ib_port->refcount),
+ 10 * HZ))
+ xcpm_debug(KERN_WARNING,
+ "Warning: Timed out waiting for the reference count\n");
+
+ /* All the links */
+ for (link_index = 0; link_index < MAX_NUM_LINKS; link_index++)
+ /* If the link goes on this port */
+ if (xcpm->links[link_index].link_state != LINK_DEAD &&
+ xcpm->links[link_index].ib_link.port->device == device &&
+ xcpm->links[link_index].ib_link.port->port_num == port_num)
+ delete_link(link_index);
+
+ ib_if_port_exit(ib_port);
+ atomic_dec(&xcpm->num_ports);
+ ib_port->used = 0;
+ }
+
+ /* Don't exit in the middle of executing work functions */
+ flush_workqueue(xcpm_wq);
+
+ if (xcpm->mr) {
+ ib_dereg_mr(xcpm->mr);
+ xcpm->mr = 0;
+ }
+ if (xcpm->pd) {
+ ib_dealloc_pd(xcpm->pd);
+ xcpm->pd = 0;
+ }
+}
+
+static struct ib_client xcpm_client = {
+ .name = "xcpm",
+ .add = xcpm_add_one,
+ .remove = xcpm_remove_one
+};
+
+static void xcpm_startup_work_handler(struct work_struct *work)
+{
+ ib_if_init(&xcpm_client);
+}
+
+int xcpm_init(void)
+{
+ int ret = -ENOMEM;
+ int count;
+
+ xcpm_debug(KERN_ERR, "XCPM version %s\n", XCPM_VERSION);
+
+ /* Allocate the xcpm data structure */
+ xcpm = kmalloc(sizeof(*xcpm), GFP_KERNEL);
+ if (!xcpm) {
+ xcpm_debug(KERN_ERR, "xcpm_info struct allocation failed\n");
+ goto init_done;
+ }
+
+ memset(xcpm, 0, sizeof(*xcpm));
+
+ spin_lock_init(&xcpm->interface_lock);
+
+ /* Lock to serialize link slot allocation */
+ spin_lock_init(&xcpm->link_lock);
+
+ /* All services and links are unusable to begin with */
+ for (count = 0; count < MAX_NUM_SVCS; count++)
+ xcpm->svcs[count].svc_state = SERVICE_DOWN;
+
+ for (count = 0; count < MAX_NUM_LINKS; count++) {
+ xcpm->links[count].link_state = LINK_DEAD;
+ xcpm->links[count].used = 0;
+ }
+
+ atomic_set(&xcpm->num_links, 0);
+ atomic_set(&xcpm->num_ports, 0);
+
+ xcpm->resource_flags = 0;
+
+ init_waitqueue_head(&xcpm_wait);
+
+ xcpm_wq = create_singlethread_workqueue("xscorcpmwq");
+ if (!xcpm_wq)
+ goto leave_err;
+
+ INIT_DELAYED_WORK(&xcpm->startup_work, xcpm_startup_work_handler);
+
+ atomic_set(&xcpm->xcpm_down, 0);
+
+ ret = xcpm_register_sysfs();
+ if (ret)
+ goto leave_err2;
+
+ if (alloc_xsmp_mem_pool())
+ goto leave_err3;
+
+ if (alloc_ib_if_mem_pool()) {
+ dealloc_xsmp_mem_pool();
+ goto leave_err3;
+ }
+
+ queue_delayed_work(xcpm_wq, &xcpm->startup_work, XCPM_STACK_DELAY);
+
+ ret = 0;
+ goto init_done;
+
+leave_err3:
+ xcpm_unregister_sysfs();
+leave_err2:
+ destroy_workqueue(xcpm_wq);
+leave_err:
+ kfree(xcpm);
+
+init_done:
+ return ret;
+}
+
+void xcpm_exit(void)
+{
+ /* Kill the scheduled startup_work, if any */
+ int startup_work_stopped = cancel_delayed_work(&xcpm->startup_work);
+
+ /* The module is going down, no more scheduling */
+ atomic_set(&xcpm->xcpm_down, 1);
+
+ /* If we completely initialized (startup_work executed), then exit */
+ if (!startup_work_stopped)
+ ib_if_exit(&xcpm_client);
+
+ flush_workqueue(xcpm_wq);
+ destroy_workqueue(xcpm_wq);
+
+ dealloc_ib_if_mem_pool();
+
+ dealloc_xsmp_mem_pool();
+
+ xcpm_unregister_sysfs();
+
+ kfree(xcpm);
+
+ xcpm_debug(KERN_INFO, "exit complete\n");
+}
diff --git a/drivers/infiniband/ulp/xsigo/xscore/xcpm_export.h b/drivers/infiniband/ulp/xsigo/xscore/xcpm_export.h
new file mode 100644
index 0000000..5ac8810
--- /dev/null
+++ b/drivers/infiniband/ulp/xsigo/xscore/xcpm_export.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2006-2008 Xsigo Systems 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 __XCPM_EXPORT_H__
+#define __XCPM_EXPORT_H__
+
+#define PFX "xscore/xcpm: "
+
+struct ib_cq;
+struct xcm_list;
+struct ib_port_info;
+
+extern wait_queue_head_t xcpm_wait;
+extern int boot_flag;
+
+#ifdef CONFIG_XSCORE_DEBUG
+extern int xcpm_debug_level;
+
+#define xcpm_debug(level, fmt, args...) \
+ do { \
+ if (xcpm_debug_level > 0) \
+ printk(level "<xscore:xcpm> %s: " fmt, __FUNCTION__ , ## args); \
+ } while (0)
+#else
+#define xcpm_debug(level, fmt, args...)
+#endif
+
+int transmit_to_xcm(int link, u8 *data, int length);
+void update_linkstate(int link);
+void abort_datapaths_on_link(int link);
+void bring_down_link(int link);
+void allocate_port_and_links(struct ib_port_info *ib_port,
+ struct xcm_list *list);
+struct ib_port_info *get_ib_port(int port_index);
+void set_link_ready(int link, int hello_interval, int datapath_timeout);
+void startup_link(int index, u64 fw_ver, u32 hw_ver, u32 vendor_part_id);
+void process_incoming_msg(int link, u8 * buf, int length);
+int is_link_alive(int link);
+int is_link_confirmed(int link);
+void broadcast_service_status(void);
+u64 get_link_guid(int link);
+u64 get_dest_guid(int link);
+void send_service_status(int link);
+int send_error_counts(int link);
+void schedule_port_sweep(struct ib_port_info *port, int fast_poll);
+void increment_hellos_received(int link);
+void increment_hellos_sent(int link);
+void log_link_error(int link, int error_type);
+void delete_link(int link);
+
+#endif /* __XCPM_EXPORT_H__ */
diff --git a/drivers/infiniband/ulp/xsigo/xscore/xcpm_interface.h b/drivers/infiniband/ulp/xsigo/xscore/xcpm_interface.h
new file mode 100644
index 0000000..45db24c
--- /dev/null
+++ b/drivers/infiniband/ulp/xsigo/xscore/xcpm_interface.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2006-2008 Xsigo Systems 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 __XCPM_INTERFACE_H__
+#define __XCPM_INTERFACE_H__
+
+/* The interface functions of the XCPM, used by the services */
+
+/*
+ * Semantics of the interface commands from the XCPM to a service driver
+ * using callback functions
+ * Exported by the service drivers, used by the XCPM
+ */
+
+/**
+ * xcpm_receive_message_handler - callback handler for received XCPM message
+ * @link: the link on which the message was received
+ * @data: received message data (in network byte order)
+ * Data is owned by a receive buffer.
+ * Data is NOT to be freed by the service driver.
+ * @length: length of the received data
+ */
+typedef void (*xcpm_receive_message_handler)(int link, u8 *data, int length);
+
+/**
+ * xcpm_abort_link_handler - callback handler for aborted XCPM link
+ * @link: the link that is being aborted
+ */
+typedef void (*xcpm_abort_link_handler)(int link);
+
+/**
+ * service_type_info - Passed by the service driver to XCPM
+ * to register a new service
+ * @receive_handler: service driver receive message callback handler
+ * (may be NULL)
+ * @abort_handler: service driver abort callback handler (may be NULL)
+ * @ctrl_message_type: XSMP message type (see xsmp_common.h)
+ * @resource_flag_index: XSMP resource flag index (see xsmp_common.h)
+ */
+struct service_type_info {
+ xcpm_receive_message_handler receive_handler;
+ xcpm_abort_link_handler abort_handler;
+ u16 ctrl_message_type;
+ u16 resource_flag_index;
+};
+
+/* Semantics of the interface commands from a service driver to the XCPM: */
+
+/**
+ * xcpm_register_service - Register service with XCPM
+ * @s_info: service type info for service driver being registered with XCPM
+ *
+ * Return value: a new service id allocated to the service driver.
+ * -EALREADY if the service is already up.
+ * -ENOMEM if a new service id cannot be allocated.
+ * -EINVAL if the input parameter is invalid.
+ */
+int xcpm_register_service(struct service_type_info *s_info);
+
+/**
+ * xcpm_unregister_service - Unregister service with XCPM
+ * @service_id: service_id allocated earlier to the service driver
+ * when service was registered.
+ *
+ * Return value: 0 on success.
+ * -EHOSTDOWN if the service is not up
+ * (as seen by XCPM).
+ * -EINVAL if the input parameter is invalid.
+ */
+int xcpm_unregister_service(int service_id);
+
+/**
+ * xcpm_send_message - Send message to XCPM
+ * @link: the logical link on which to send the message.
+ * (on which the corresponding Vdevice was created).
+ * @service_id: service_id allocated earlier to the service driver
+ * when service was registered.
+ * @data: message data to send (network byte order).
+ * Data is freed by the XCPM.
+ * @length: length of the data to send.
+ *
+ * Return value: 0 on success.
+ * -ENOLINK if the link is down or non-existent.
+ * -EINVAL if the input parameters are invalid.
+ * other errors from ib_post_send on transmission
+ * failures on active links.
+ */
+int xcpm_send_message(int link, int service_id, u8 *data, int length);
+
+/**
+ * query_link_info - Passed to a service driver as information related
+ * to an XCPM control link
+ */
+struct query_link_info {
+ struct ib_device *device;
+ int port;
+ struct ib_pd *pd;
+ struct ib_mr *mr;
+
+ /* Local address parameters */
+ u16 slid;
+ union ib_gid sgid;
+
+ /* Remote address parameters */
+ u16 dlid;
+ union ib_gid dgid;
+};
+
+/**
+ * xcpm_query_link - Query XCPM link
+ * @link: the XCPM link index for which the query is made.
+ * @q_info: pointer to 'struct query_link_info' where queried link info
+ * is returned.
+ *
+ * Return value: 0 on success.
+ * -EINVAL if the input parameters are invalid.
+ * -ENOLINK if the link does not exist.
+ */
+int xcpm_query_link(int link, struct query_link_info *q_info);
+
+#endif /* __XCPM_INTERFACE_H__ */
diff --git a/drivers/infiniband/ulp/xsigo/xscore/xcpm_priv.h b/drivers/infiniband/ulp/xsigo/xscore/xcpm_priv.h
new file mode 100644
index 0000000..14783dc
--- /dev/null
+++ b/drivers/infiniband/ulp/xsigo/xscore/xcpm_priv.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2006-2008 Xsigo Systems 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 __XCPM_PRIV_H__
+#define __XCPM_PRIV_H__
+
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/if_infiniband.h>
+
+#include <rdma/ib_verbs.h>
+
+#include "ib_if.h"
+#include "xcpm_interface.h"
+#include "xsmp_session.h"
+
+#define MAX_NUM_SVCS 32
+#define MAX_NUM_LINKS 32
+#define MAX_NUM_PORTS 32
+
+#define XCPM_LINK_TIMEOUT_SECS 9
+#define XCPM_DEFAULT_HELLO_INTERVAL 5
+#define XCPM_MIN_HELLO_INTERVAL 3
+#define XCPM_LINK_DATAPATH_TIMEOUT_SECS 45
+#define XCPM_PORT_SWEEP_INTERVAL_SECS 15
+
+enum xcpm_link_state {
+ LINK_DEAD = 0,
+ LINK_INIT,
+ LINK_UP,
+ LINK_DISABLED,
+ LINK_DOWN
+};
+
+enum xcpm_service_state {
+ SERVICE_DOWN = 10,
+ SERVICE_UP
+};
+
+/* Describes a particular service */
+struct svc_info {
+ enum xcpm_service_state svc_state; /* Service state: active or not */
+
+ /* The callbacks for the control messages */
+ xcpm_receive_message_handler receive_handler;
+ xcpm_abort_link_handler abort_handler;
+
+ u16 ctrl_message_type; /* This is to identify control messages that belong to this service */
+
+ /*
+ * Index into the resource flag field passed to the XCFM
+ * indicating which services are up
+ */
+ u16 resource_flag_index;
+
+ /*
+ * Spinlock to serialize access to the service
+ * driver's interface function
+ */
+ spinlock_t callup_lock;
+};
+
+/* One logical link from the host to the XCM */
+struct link_info {
+ int link_index;
+
+ struct ib_link_info ib_link; /* Local details of the logical link */
+
+ struct class_device link_class_dev;
+
+ /*
+ * Spinlock to synchronize access to the transmit path
+ * of the link (the queue pair)
+ */
+ spinlock_t tx_lock;
+
+ enum xcpm_link_state link_state; /* The state of the link */
+
+ struct delayed_work linkstate_timer; /* Timer to track the link state */
+
+ struct delayed_work datapath_timer; /* Timer to track the datapath expiry time */
+
+ int datapath_timer_on; /* Tells us whether the timer is currently active or not */
+ int linkstate_timeout; /* Timeout after which a link is declared inactive */
+ int datapath_timeout; /* Timeout after which datapaths established over the link are aborted */
+ int datapath_timeout_disabled; /* Whether we want to have a datapath timeout at all */
+
+ struct work_struct msg_dispatch_work; /* Work queue to offload message processing from the interrupt handler */
+
+ u64 send_seq_number, recv_seq_number; /* Sequence numbers for the messages */
+ int confirmed; /* Flag to indicate whether we got a confirm back */
+ u32 error_counts[XSMP_MAX_ERROR_TYPES]; /* Error counts on the link */
+
+ /* Count of the number of HELLOs that went across */
+ int hellos_received;
+ int hellos_sent;
+
+ int used; /* Whether the slot is in use */
+ int session_timeouts;
+};
+
+/* The xcpm data structure */
+struct xcpm_info {
+ struct svc_info svcs[MAX_NUM_SVCS]; /* All the services supported on this server, one entry per type */
+
+ struct link_info links[MAX_NUM_LINKS]; /* All logical links with the XCMs */
+
+ /*
+ * Count of the links to the XCMs
+ * 0 to num_links - 1 are always valid links (whether up or down)
+ * To the contrary, the service list can have 'holes'
+ */
+ atomic_t num_links;
+
+ struct ib_port_info ports[MAX_NUM_PORTS]; /* The physical ports on the various HCAs */
+
+ atomic_t num_ports; /* Number of ports */
+ spinlock_t interface_lock; /* Synchronize calls to the interface function exported by the XCPM */
+ u32 resource_flags; /* Track which services are up */
+
+ struct delayed_work startup_work;
+
+ atomic_t xcpm_down; /* A global flag indicating that the module is going down */
+ spinlock_t link_lock;
+ struct ib_pd *pd;
+ struct ib_mr *mr;
+};
+
+#endif /* __XCPM_PRIV_H__ */
--
1.5.2
More information about the ewg
mailing list