[openib-general] [PATCH 8/10] sysfs interface implementation

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


Adds the files that implement the sysfs interface of the driver.


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

 drivers/infiniband/ulp/vnic/vnic_sys.c | 1118 ++++++++++++++++++++++++++++++++
 drivers/infiniband/ulp/vnic/vnic_sys.h |   51 +
 2 files changed, 1169 insertions(+), 0 deletions(-)

diff --git a/drivers/infiniband/ulp/vnic/vnic_sys.c b/drivers/infiniband/ulp/vnic/vnic_sys.c
new file mode 100644
index 0000000..052783e
--- /dev/null
+++ b/drivers/infiniband/ulp/vnic/vnic_sys.c
@@ -0,0 +1,1118 @@
+/*
+ * 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/parser.h>
+#include <linux/netdevice.h>
+#include <linux/if.h>
+
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+#include <asm/div64.h>
+#endif
+
+#include "vnic_util.h"
+#include "vnic_config.h"
+#include "vnic_ib.h"
+#include "vnic_viport.h"
+#include "vnic_main.h"
+
+extern struct list_head vnic_list;
+
+/*
+ * target eiocs are added by writing
+ *
+ * ioc_guid=<EIOC GUID>,dgid=<dest GID>,pkey=<P_key>,name=<interface_name>
+ * to the create_primary  sysfs attribute.
+ */
+enum {
+	VNIC_OPT_ERR = 0,
+	VNIC_OPT_IOC_GUID = 1 << 0,
+	VNIC_OPT_DGID = 1 << 1,
+	VNIC_OPT_PKEY = 1 << 2,
+	VNIC_OPT_NAME = 1 << 3,
+	VNIC_OPT_INSTANCE = 1 << 4,
+	VNIC_OPT_RXCSUM = 1 << 5,
+	VNIC_OPT_TXCSUM = 1 << 6,
+	VNIC_OPT_HEARTBEAT = 1 << 7,
+	VNIC_OPT_ALL = (VNIC_OPT_IOC_GUID |
+			VNIC_OPT_DGID | VNIC_OPT_NAME | VNIC_OPT_PKEY),
+};
+
+static match_table_t vnic_opt_tokens = {
+	{VNIC_OPT_IOC_GUID, "ioc_guid=%s"},
+	{VNIC_OPT_DGID, "dgid=%s"},
+	{VNIC_OPT_PKEY, "pkey=%x"},
+	{VNIC_OPT_NAME, "name=%s"},
+	{VNIC_OPT_INSTANCE, "instance=%d"},
+	{VNIC_OPT_RXCSUM, "rx_csum=%s"},
+	{VNIC_OPT_TXCSUM, "tx_csum=%s"},
+	{VNIC_OPT_HEARTBEAT, "heartbeat=%d"},
+	{VNIC_OPT_ERR, NULL}
+};
+
+static void vnic_release_class_dev(struct class_device *class_dev)
+{
+	struct class_dev_info *cdev_info =
+	    container_of(class_dev, struct class_dev_info, class_dev);
+
+	complete(&cdev_info->released);
+
+}
+
+struct class vnic_class = {
+	.name = "infiniband_vnic",
+	.release = vnic_release_class_dev
+};
+
+struct class_dev_info interface_cdev;
+
+static int vnic_parse_options(const char *buf, struct path_param *param)
+{
+	char *options, *sep_opt;
+	char *p;
+	char dgid[3];
+	substring_t args[MAX_OPT_ARGS];
+	int opt_mask = 0;
+	int token;
+	int ret = -EINVAL;
+	int i;
+
+	options = kstrdup(buf, GFP_KERNEL);
+	if (!options)
+		return -ENOMEM;
+
+	sep_opt = options;
+	while ((p = strsep(&sep_opt, ",")) != NULL) {
+		if (!*p)
+			continue;
+
+		token = match_token(p, vnic_opt_tokens, args);
+		opt_mask |= token;
+
+		switch (token) {
+		case VNIC_OPT_IOC_GUID:
+			p = match_strdup(args);
+			param->ioc_guid = cpu_to_be64(simple_strtoull(p, NULL,
+								      16));
+			kfree(p);
+			break;
+
+		case VNIC_OPT_DGID:
+			p = match_strdup(args);
+			if (strlen(p) != 32) {
+				printk(KERN_WARNING PFX
+				       "bad dest GID parameter '%s'\n", p);
+				kfree(p);
+				goto out;
+			}
+
+			for (i = 0; i < 16; ++i) {
+				strlcpy(dgid, p + i * 2, 3);
+				param->dgid[i] = simple_strtoul(dgid, NULL, 16);
+
+			}
+			kfree(p);
+			break;
+
+		case VNIC_OPT_PKEY:
+			if (match_hex(args, &token)) {
+				printk(KERN_WARNING PFX
+				       "bad P_key parameter '%s'\n", p);
+				goto out;
+			}
+			param->pkey = cpu_to_be16(token);
+			break;
+
+		case VNIC_OPT_NAME:
+			p = match_strdup(args);
+			if (strlen(p) >= IFNAMSIZ) {
+				printk(KERN_WARNING PFX
+				       "interface name parameter too long\n");
+				kfree(p);
+				goto out;
+			}
+			strcpy(param->name, p);
+			kfree(p);
+			break;
+		case VNIC_OPT_INSTANCE:
+			if (match_int(args, &token)) {
+				printk(KERN_WARNING PFX
+				       "bad instance parameter '%s'\n", p);
+				goto out;
+			}
+
+			if (token > 255 || token < 0) {
+				printk(KERN_WARNING PFX
+				       "instance parameter must be"
+				       " > 0 and <= 255\n");
+				goto out;
+			}
+
+			param->instance = token;
+			break;
+		case VNIC_OPT_RXCSUM:
+			p = match_strdup(args);
+			if (!strncmp(p, "true", 4))
+				param->rx_csum = 1;
+			else if (!strncmp(p, "false", 5))
+				param->rx_csum = 0;
+			else {
+				printk(KERN_WARNING PFX
+				       "bad rx_csum parameter."
+				       " must be 'true' or 'false'\n");
+				kfree(p);
+				goto out;
+			}
+			kfree(p);
+			break;
+		case VNIC_OPT_TXCSUM:
+			p = match_strdup(args);
+			if (!strncmp(p, "true", 4))
+				param->tx_csum = 1;
+			else if (!strncmp(p, "false", 5))
+				param->tx_csum = 0;
+			else {
+				printk(KERN_WARNING PFX
+				       "bad tx_csum parameter."
+				       " must be 'true' or 'false'\n");
+				kfree(p);
+				goto out;
+			}
+			kfree(p);
+			break;
+		case VNIC_OPT_HEARTBEAT:
+			if (match_int(args, &token)) {
+				printk(KERN_WARNING PFX
+				       "bad instance parameter '%s'\n", p);
+				goto out;
+			}
+
+			if (token > 6000 || token < 0) {
+				printk(KERN_WARNING PFX
+				       "heartbeat parameter must be"
+				       " > 0 and <= 6000\n");
+				goto out;
+			}
+			param->heartbeat = token;
+			break;
+		default:
+			printk(KERN_WARNING PFX
+			       "unknown parameter or missing value "
+			       "'%s' in target creation request\n", p);
+			goto out;
+		}
+
+	}
+
+	if ((opt_mask & VNIC_OPT_ALL) == VNIC_OPT_ALL)
+		ret = 0;
+	else
+		for (i = 0; i < ARRAY_SIZE(vnic_opt_tokens); ++i)
+			if ((vnic_opt_tokens[i].token & VNIC_OPT_ALL) &&
+			    !(vnic_opt_tokens[i].token & opt_mask))
+				printk(KERN_WARNING PFX
+				       "target creation request is "
+				       "missing parameter '%s'\n",
+				       vnic_opt_tokens[i].pattern);
+
+out:
+	kfree(options);
+	return ret;
+
+}
+
+static ssize_t show_vnic_state(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+	    container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic *vnic = container_of(info, struct vnic, class_dev_info);
+	switch (vnic->state) {
+	case VNIC_UNINITIALIZED:
+		return sprintf(buf, "VNIC_UNINITIALIZED\n");
+	case VNIC_REGISTERED:
+		return sprintf(buf, "VNIC_REGISTERED\n");
+	default:
+		return sprintf(buf, "INVALID STATE\n");
+	}
+
+}
+
+static CLASS_DEVICE_ATTR(vnic_state, S_IRUGO, show_vnic_state, NULL);
+
+static ssize_t show_rx_csum(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+	    container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic *vnic = container_of(info, struct vnic, class_dev_info);
+
+	if (vnic->config->use_rx_csum)
+		return sprintf(buf, "true\n");
+	else
+		return sprintf(buf, "false\n");
+}
+
+static CLASS_DEVICE_ATTR(rx_csum, S_IRUGO, show_rx_csum, NULL);
+
+static ssize_t show_tx_csum(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+	    container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic *vnic = container_of(info, struct vnic, class_dev_info);
+
+	if (vnic->config->use_tx_csum)
+		return sprintf(buf, "true\n");
+	else
+		return sprintf(buf, "false\n");
+}
+
+static CLASS_DEVICE_ATTR(tx_csum, S_IRUGO, show_tx_csum, NULL);
+
+static ssize_t show_current_path(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+	    container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic *vnic = container_of(info, struct vnic, class_dev_info);
+
+	if (vnic->current_path == &vnic->primary_path)
+		return sprintf(buf, "primary path\n");
+	else if (vnic->current_path == &vnic->secondary_path)
+		return sprintf(buf, "secondary path\n");
+	else
+		return sprintf(buf, "none\n");
+
+}
+
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+u32 clock_rate = 800;
+module_param(clock_rate, int, 0444);
+
+#define CYCLES_TO_NANOSEC(x)	\
+{				\
+	x *= 1000;		\
+	do_div(x, clock_rate);	\
+}
+
+/*
+ * TODO: Statistics reporting for control path, data path,
+ *       RDMA times, IOs etc
+ *
+ */
+static int avg_ticks_as_time(cycles_t ticks, u32 count, char *buffer)
+{
+	unsigned long long average = ticks;
+	unsigned long long remainder = 0;
+
+	if (count == 0) {
+		return sprintf(buffer, "[NA]\n");
+	} else if (count != 1) {
+		do_div(average, count);
+	}
+
+	CYCLES_TO_NANOSEC(average);
+
+	if (average > 1000000000) {
+		remainder = average;
+		do_div(average, 1000000000);
+		remainder -= average * 1000000000;
+		do_div(remainder, 1000000);
+		if (average > 60) {
+			u32 days, hours, minutes;
+
+			days	 =  (u32)average / ((60 * 60) *24);
+			average	-=  days * ((60 * 60) * 24);
+			hours	 =  (u32)average / (60 * 60);
+			average	-=  hours * (60 * 60);
+			minutes	 =  (u32)average / 60;
+			average	-=  minutes * 60;
+			if (days != 0) {
+				return sprintf(buffer, "%d days, %d:%02d\n",
+					       days, hours, minutes);
+			} else {
+				return sprintf(buffer, "%d:%02d\n",
+					       hours, minutes);
+			}
+		} else  {
+			return sprintf(buffer, "%"PRId64".%03"PRId64" sec\n",
+				       average, remainder);
+		}
+	} else if (average > 1000000) {
+		remainder = average;
+		do_div(average, 1000000);
+		remainder -= average * 1000000;
+		do_div(remainder, 1000);
+		return sprintf(buffer, "%"PRId64".%03"PRId64" msec\n",
+			       average,remainder);
+	} else if (average > 1000) {
+		remainder = average;
+		do_div(average, 1000);
+		remainder -= average * 1000;
+		return sprintf(buffer, "%"PRId64".%03"PRId64" usec\n",
+			average,remainder);
+	} else {
+		return sprintf(buffer, "%"PRId64" nanosec\n",average);
+	}
+
+	return 0;
+}
+
+static ssize_t show_lifetime(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+		container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+	cycles_t time = get_cycles() - vnic->statistics.start_time;
+
+	return avg_ticks_as_time(time, 1, buf);
+}
+
+static CLASS_DEVICE_ATTR(lifetime, S_IRUGO, show_lifetime, NULL);
+
+static ssize_t show_conntime(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+		container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+
+	if (vnic->statistics.conn_time)
+		return avg_ticks_as_time(vnic->statistics.conn_time,
+				 	 1, buf);
+	return 0;
+}
+
+static CLASS_DEVICE_ATTR(connection_time, S_IRUGO, show_conntime, NULL);
+
+static ssize_t show_disconnects(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+		container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+	u32 num;
+
+	if (vnic->statistics.disconn_ref)
+		num = vnic->statistics.disconn_num + 1;
+	else
+		num = vnic->statistics.disconn_num;
+
+	return sprintf(buf, "%d\n", num);
+}
+
+static CLASS_DEVICE_ATTR(disconnects, S_IRUGO, show_disconnects, NULL);
+
+static ssize_t show_avg_disconn_time(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+		container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+	cycles_t time;
+	u32	 num;
+
+	if (vnic->statistics.disconn_ref) {
+		time = vnic->statistics.disconn_time +
+		       get_cycles() - vnic->statistics.disconn_ref;
+		num = vnic->statistics.disconn_num + 1;
+	}
+	else {
+		time = vnic->statistics.disconn_time;
+		num = vnic->statistics.disconn_num;
+	}
+
+	return avg_ticks_as_time(time, num, buf);
+}
+
+static CLASS_DEVICE_ATTR(avg_disconn_time, S_IRUGO, show_avg_disconn_time, NULL);
+
+static ssize_t show_carrier_losses(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+		container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+	u32 num;
+
+	if (vnic->statistics.carrier_ref)
+		num = vnic->statistics.carrier_off_num + 1;
+	else
+		num = vnic->statistics.carrier_off_num;
+
+	return sprintf(buf, "%d\n", num);
+}
+
+static CLASS_DEVICE_ATTR(carrier_losses, S_IRUGO, show_carrier_losses, NULL);
+
+static ssize_t show_avg_carr_loss_time(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+		container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+	cycles_t time;
+	u32	 num;
+
+	if (vnic->statistics.carrier_ref) {
+		time = vnic->statistics.carrier_off_time +
+		       get_cycles() - vnic->statistics.carrier_ref;
+		num = vnic->statistics.disconn_num + 1;
+	}
+	else {
+		time = vnic->statistics.carrier_off_time;
+		num = vnic->statistics.carrier_off_num;
+	}
+
+	return avg_ticks_as_time(time, num, buf);
+}
+
+static CLASS_DEVICE_ATTR(avg_carrier_loss_time, S_IRUGO,
+			 show_avg_carr_loss_time, NULL);
+
+static ssize_t show_avg_recv_time(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+		container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+
+	return avg_ticks_as_time(vnic->statistics.recv_time,
+				 vnic->statistics.recv_num,
+				 buf);
+}
+
+static CLASS_DEVICE_ATTR(avg_recv_time, S_IRUGO, show_avg_recv_time, NULL);
+
+static ssize_t show_recvs(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+		container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+
+	return sprintf(buf, "%d\n", vnic->statistics.recv_num);
+}
+
+static CLASS_DEVICE_ATTR(recvs, S_IRUGO, show_recvs, NULL);
+
+
+static ssize_t show_avg_xmit_time(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+		container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+
+	return avg_ticks_as_time(vnic->statistics.xmit_time,
+				 vnic->statistics.xmit_num,
+				 buf);
+}
+
+static CLASS_DEVICE_ATTR(avg_xmit_time, S_IRUGO, show_avg_xmit_time, NULL);
+
+static ssize_t show_xmits(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+		container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+
+	return sprintf(buf, "%d\n", vnic->statistics.xmit_num);
+}
+
+static CLASS_DEVICE_ATTR(xmits, S_IRUGO, show_xmits, NULL);
+
+static ssize_t show_failed_xmits(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+		container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic *vnic = container_of(info, struct vnic, stat_info);
+
+	return sprintf(buf, "%d\n", vnic->statistics.xmit_fail);
+}
+
+static CLASS_DEVICE_ATTR(failed_xmits, S_IRUGO, show_failed_xmits, NULL);
+
+
+static int setup_vnic_stats_files(struct vnic *vnic)
+{
+
+	init_completion(&vnic->stat_info.released);
+	vnic->stat_info.class_dev.class = &vnic_class;
+	vnic->stat_info.class_dev.parent = &vnic->class_dev_info.class_dev;
+	snprintf(vnic->stat_info.class_dev.class_id, BUS_ID_SIZE,
+		 "stats");
+
+	if (class_device_register(&vnic->stat_info.class_dev)) {
+		SYS_ERROR("create_vnic: error in registering"
+			  " stat class dev\n");
+		goto out;
+	}
+	if (class_device_create_file(&vnic->stat_info.class_dev,
+				     &class_device_attr_lifetime)) {
+		SYS_ERROR("create_vnic: error in creating"
+			  "stats attr file\n");
+		goto err_file;
+	}
+	if (class_device_create_file(&vnic->stat_info.class_dev,
+				     &class_device_attr_xmits)) {
+		SYS_ERROR("create_vnic: error in creating"
+			  "stats attr file\n");
+		goto err_file;
+	}
+	if (class_device_create_file(&vnic->stat_info.class_dev,
+				     &class_device_attr_avg_xmit_time)) {
+		SYS_ERROR("create_vnic: error in creating"
+			  "stats attr file\n");
+		goto err_file;
+	}
+	if (class_device_create_file(&vnic->stat_info.class_dev,
+				     &class_device_attr_failed_xmits)) {
+		SYS_ERROR("create_vnic: error in creating"
+			  "stats attr file\n");
+		goto err_file;
+	}
+	if (class_device_create_file(&vnic->stat_info.class_dev,
+				     &class_device_attr_recvs)) {
+		SYS_ERROR("create_vnic: error in creating"
+			  "stats attr file\n");
+		goto err_file;
+	}
+	if (class_device_create_file(&vnic->stat_info.class_dev,
+				     &class_device_attr_avg_recv_time)) {
+		SYS_ERROR("create_vnic: error in creating"
+			  "stats attr file\n");
+		goto err_file;
+	}
+	if (class_device_create_file(&vnic->stat_info.class_dev,
+				     &class_device_attr_connection_time)) {
+		SYS_ERROR("create_vnic: error in creating"
+			  "stats attr file\n");
+		goto err_file;
+	}
+	if (class_device_create_file(&vnic->stat_info.class_dev,
+				     &class_device_attr_disconnects)) {
+		SYS_ERROR("create_vnic: error in creating"
+			  "stats attr file\n");
+		goto err_file;
+	}
+
+	if (class_device_create_file(&vnic->stat_info.class_dev,
+				     &class_device_attr_avg_disconn_time)) {
+		SYS_ERROR("create_vnic: error in creating"
+			  "stats attr file\n");
+		goto err_file;
+	}
+	if (class_device_create_file(&vnic->stat_info.class_dev,
+				     &class_device_attr_carrier_losses)) {
+		SYS_ERROR("create_vnic: error in creating"
+			  "stats attr file\n");
+		goto err_file;
+	}
+
+	if (class_device_create_file(&vnic->stat_info.class_dev,
+				    &class_device_attr_avg_carrier_loss_time)) {
+		SYS_ERROR("create_vnic: error in creating"
+			  "stats attr file\n");
+		goto err_file;
+	}
+	return 0;
+err_file:
+	class_device_unregister(&vnic->stat_info.class_dev);
+	wait_for_completion(&vnic->stat_info.released);
+out:
+	return -1;
+}
+
+#endif	/*CONFIG_INFINIBAND_VNIC_STATS*/
+
+
+static CLASS_DEVICE_ATTR(current_path, S_IRUGO, show_current_path, NULL);
+
+static int create_netpath(struct netpath *n_pdest, struct path_param *p_params)
+{
+	struct viport_config *viport_config;
+	struct viport *viport;
+	struct vnic *vnic;
+	struct list_head *ptr;
+
+	SYS_INFO(KERN_INFO
+		  "create_netpath: port=%d, instance=%d, heartbeat = %d\n",
+		  (int)p_params->port, (int)p_params->instance,
+		  (int)p_params->heartbeat);
+
+	list_for_each(ptr, &vnic_list) {
+		vnic = list_entry(ptr, struct vnic, list_ptrs);
+		if (vnic->primary_path.viport) {
+			viport_config = vnic->primary_path.viport->config;
+			if ((viport_config->guid == p_params->ioc_guid)
+			    && (viport_config->control_config.vnic_instance ==
+				p_params->instance)) {
+				SYS_ERROR("GUID %llx,"
+					  " INSTANCE %d already in use\n",
+					  p_params->ioc_guid,
+					  p_params->instance);
+				return 0;
+			}
+		}
+		if (vnic->secondary_path.viport) {
+			viport_config = vnic->secondary_path.viport->config;
+			if ((viport_config->guid == p_params->ioc_guid)
+			    && (viport_config->control_config.vnic_instance ==
+				p_params->instance)) {
+				SYS_ERROR("GUID %llx,"
+					  " INSTANCE %d already in use\n",
+					  p_params->ioc_guid,
+					  p_params->instance);
+				return 0;
+			}
+		}
+	}
+
+	viport_config = config_alloc_viport(p_params);
+	if (!viport_config) {
+		SYS_ERROR("create_netpath: failed creating viport config\n");
+		return 0;
+	}
+
+	if (p_params->heartbeat != MAXU64) {
+		viport_config->hb_interval = p_params->heartbeat;
+							/* 1/100s of sec */
+		viport_config->hb_timeout = (p_params->heartbeat << 6)
+		    * 10000;	/* u_sec */
+	}
+
+	viport_config->path_idx = 0;
+
+	viport = viport_allocate(viport_config);
+	if (!viport) {
+		SYS_ERROR("create_netpath: failed creating viport\n");
+		config_free_viport(viport_config);
+		return 0;
+	}
+	if (!netpath_add_path(n_pdest, viport)) {
+		SYS_ERROR("create_netpath: failed associating"
+			  " viport with vnic\n");
+		viport_free(viport);
+		return 0;
+	}
+	netpath_disconnected(n_pdest, viport);
+	return 1;
+}
+
+struct vnic *create_vnic(struct path_param *param)
+{
+	struct vnic_config *vnic_config;
+	struct vnic *vnic;
+	struct list_head *ptr;
+
+	SYS_INFO("create_vnic: name = %s\n", param->name);
+	list_for_each(ptr, &vnic_list) {
+		vnic = list_entry(ptr, struct vnic, list_ptrs);
+		if (!strcmp(vnic->config->name, param->name)) {
+			SYS_ERROR("vnic %s already exists\n",
+				   param->name);
+			return NULL;
+		}
+	}
+
+	vnic_config = config_alloc_vnic();
+	if (!vnic_config) {
+		SYS_ERROR("create_vnic: failed creating vnic config\n");
+		return NULL;
+	}
+	if (param->rx_csum != MAXU64) {
+		vnic_config->overrides |= USE_RX_CSUM_OVERRIDE;
+		vnic_config->use_rx_csum = param->rx_csum ? TRUE : FALSE;
+	}
+	if (param->tx_csum != MAXU64) {
+		vnic_config->overrides |= USE_TX_CSUM_OVERRIDE;
+		vnic_config->use_tx_csum = param->tx_csum ? TRUE : FALSE;
+	}
+	strcpy(vnic_config->name, param->name);
+	vnic = vnic_allocate(vnic_config);
+	if (!vnic) {
+		SYS_ERROR("create_vnic: failed creating vnic\n");
+		config_free_vnic(vnic_config);
+		return NULL;
+	}
+
+	init_completion(&vnic->class_dev_info.released);
+
+	vnic->class_dev_info.class_dev.class = &vnic_class;
+	vnic->class_dev_info.class_dev.parent = &interface_cdev.class_dev;
+	snprintf(vnic->class_dev_info.class_dev.class_id, BUS_ID_SIZE,
+		 vnic_config->name);
+
+	if (class_device_register(&vnic->class_dev_info.class_dev)) {
+		SYS_ERROR("create_vnic: error in registering"
+			  " vnic class dev\n");
+		goto free_vnic;
+	}
+
+	if (class_device_create_file(&vnic->class_dev_info.class_dev,
+				     &class_device_attr_vnic_state)) {
+		SYS_ERROR("create_vnic: error in creating"
+			  "vnic_state attr file\n");
+		goto err_file;
+	}
+
+	if (class_device_create_file(&vnic->class_dev_info.class_dev,
+				     &class_device_attr_rx_csum)) {
+		SYS_ERROR("create_vnic: error in creating"
+			   "rx_csum attr file\n");
+		goto err_file;
+	}
+
+	if (class_device_create_file(&vnic->class_dev_info.class_dev,
+				     &class_device_attr_tx_csum)) {
+		SYS_ERROR("create_vnic: error in creating"
+			   "tx_csum attr file\n");
+		goto err_file;
+	}
+
+	if (class_device_create_file(&vnic->class_dev_info.class_dev,
+				     &class_device_attr_current_path)) {
+		SYS_ERROR("create_vnic: error in creating"
+			   "current_path attr file\n");
+		goto err_file;
+	}
+#ifdef CONFIG_INFINIBAND_VNIC_STATS
+	if (setup_vnic_stats_files(vnic))
+		goto err_file;
+#endif	/*CONFIG_INFINIBAND_VNIC_STATS*/
+	return vnic;
+err_file:
+	class_device_unregister(&vnic->class_dev_info.class_dev);
+	wait_for_completion(&vnic->class_dev_info.released);
+free_vnic:
+	vnic_free(vnic);
+	return NULL;
+}
+
+ssize_t vnic_delete(struct class_device * class_dev,
+		    const char *buf, size_t count)
+{
+	struct vnic *vnic;
+	struct list_head *ptr;
+	int ret = -EINVAL;
+
+	if (count > IFNAMSIZ) {
+		printk(KERN_WARNING PFX "invalid vnic interface name\n");
+		return ret;
+	}
+
+	SYS_INFO("vnic_delete: name = %s\n", buf);
+	list_for_each(ptr, &vnic_list) {
+		vnic = list_entry(ptr, struct vnic, list_ptrs);
+		if (!strcmp(vnic->config->name, buf)) {
+			vnic_free(vnic);
+			return count;
+		}
+	}
+
+	printk(KERN_WARNING PFX "vnic interface '%s' does not exist\n", buf);
+	return ret;
+}
+
+static ssize_t show_viport_state(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+	    container_of(class_dev, struct class_dev_info, class_dev);
+	struct netpath *path =
+	    container_of(info, struct netpath, class_dev_info);
+	switch (path->viport->state) {
+	case VIPORT_DISCONNECTED:
+		return sprintf(buf, "VIPORT_DISCONNECTED\n");
+	case VIPORT_CONNECTED:
+		return sprintf(buf, "VIPORT_CONNECTED\n");
+	default:
+		return sprintf(buf, "INVALID STATE\n");
+	}
+
+}
+
+static CLASS_DEVICE_ATTR(viport_state, S_IRUGO, show_viport_state, NULL);
+
+static ssize_t show_link_state(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+	    container_of(class_dev, struct class_dev_info, class_dev);
+	struct netpath *path =
+	    container_of(info, struct netpath, class_dev_info);
+
+	switch (path->viport->link_state) {
+	case LINK_UNINITIALIZED:
+		return sprintf(buf, "LINK_UNINITIALIZED\n");
+	case LINK_INITIALIZE:
+		return sprintf(buf, "LINK_INITIALIZE\n");
+	case LINK_INITIALIZECONTROL:
+		return sprintf(buf, "LINK_INITIALIZECONTROL\n");
+	case LINK_INITIALIZEDATA:
+		return sprintf(buf, "LINK_INITIALIZEDATA\n");
+	case LINK_CONTROLCONNECT:
+		return sprintf(buf, "LINK_CONTROLCONNECT\n");
+	case LINK_CONTROLCONNECTWAIT:
+		return sprintf(buf, "LINK_CONTROLCONNECTWAIT\n");
+	case LINK_INITVNICREQ:
+		return sprintf(buf, "LINK_INITVNICREQ\n");
+	case LINK_INITVNICRSP:
+		return sprintf(buf, "LINK_INITVNICRSP\n");
+	case LINK_BEGINDATAPATH:
+		return sprintf(buf, "LINK_BEGINDATAPATH\n");
+	case LINK_CONFIGDATAPATHREQ:
+		return sprintf(buf, "LINK_CONFIGDATAPATHREQ\n");
+	case LINK_CONFIGDATAPATHRSP:
+		return sprintf(buf, "LINK_CONFIGDATAPATHRSP\n");
+	case LINK_DATACONNECT:
+		return sprintf(buf, "LINK_DATACONNECT\n");
+	case LINK_DATACONNECTWAIT:
+		return sprintf(buf, "LINK_DATACONNECTWAIT\n");
+	case LINK_XCHGPOOLREQ:
+		return sprintf(buf, "LINK_XCHGPOOLREQ\n");
+	case LINK_XCHGPOOLRSP:
+		return sprintf(buf, "LINK_XCHGPOOLRSP\n");
+	case LINK_INITIALIZED:
+		return sprintf(buf, "LINK_INITIALIZED\n");
+	case LINK_IDLE:
+		return sprintf(buf, "LINK_IDLE\n");
+	case LINK_IDLING:
+		return sprintf(buf, "LINK_IDLING\n");
+	case LINK_CONFIGLINKREQ:
+		return sprintf(buf, "LINK_CONFIGLINKREQ\n");
+	case LINK_CONFIGLINKRSP:
+		return sprintf(buf, "LINK_CONFIGLINKRSP\n");
+	case LINK_CONFIGADDRSREQ:
+		return sprintf(buf, "LINK_CONFIGADDRSREQ\n");
+	case LINK_CONFIGADDRSRSP:
+		return sprintf(buf, "LINK_CONFIGADDRSRSP\n");
+	case LINK_REPORTSTATREQ:
+		return sprintf(buf, "LINK_REPORTSTATREQ\n");
+	case LINK_REPORTSTATRSP:
+		return sprintf(buf, "LINK_REPORTSTATRSP\n");
+	case LINK_HEARTBEATREQ:
+		return sprintf(buf, "LINK_HEARTBEATREQ\n");
+	case LINK_HEARTBEATRSP:
+		return sprintf(buf, "LINK_HEARTBEATRSP\n");
+	case LINK_RESET:
+		return sprintf(buf, "LINK_RESET\n");
+	case LINK_RESETRSP:
+		return sprintf(buf, "LINK_RESETRSP\n");
+	case LINK_RESETCONTROL:
+		return sprintf(buf, "LINK_RESETCONTROL\n");
+	case LINK_RESETCONTROLRSP:
+		return sprintf(buf, "LINK_RESETCONTROLRSP\n");
+	case LINK_DATADISCONNECT:
+		return sprintf(buf, "LINK_DATADISCONNECT\n");
+	case LINK_CONTROLDISCONNECT:
+		return sprintf(buf, "LINK_CONTROLDISCONNECT\n");
+	case LINK_CLEANUPDATA:
+		return sprintf(buf, "LINK_CLEANUPDATA\n");
+	case LINK_CLEANUPCONTROL:
+		return sprintf(buf, "LINK_CLEANUPCONTROL\n");
+	case LINK_DISCONNECTED:
+		return sprintf(buf, "LINK_DISCONNECTED\n");
+	case LINK_RETRYWAIT:
+		return sprintf(buf, "LINK_RETRYWAIT\n");
+	default:
+		return sprintf(buf, "INVALID STATE\n");
+
+	}
+
+}
+static CLASS_DEVICE_ATTR(link_state, S_IRUGO, show_link_state, NULL);
+
+static ssize_t show_heartbeat(struct class_device *class_dev, char *buf)
+{
+	struct class_dev_info *info =
+	    container_of(class_dev, struct class_dev_info, class_dev);
+
+	struct netpath *path =
+	    container_of(info, struct netpath, class_dev_info);
+
+	/*hb_inteval is in jiffies, convert it back to 1/100ths of a second */
+	return sprintf(buf, "%d\n",
+		       (path->viport->config->hb_interval * 100) / HZ);
+}
+
+static CLASS_DEVICE_ATTR(heartbeat, S_IRUGO, show_heartbeat, NULL);
+
+static int setup_path_class_files(struct netpath *path, char *name)
+{
+	init_completion(&path->class_dev_info.released);
+
+	path->class_dev_info.class_dev.class = &vnic_class;
+	path->class_dev_info.class_dev.parent =
+	    &path->parent->class_dev_info.class_dev;
+	snprintf(path->class_dev_info.class_dev.class_id, BUS_ID_SIZE, name);
+
+	if (class_device_register(&path->class_dev_info.class_dev)) {
+		SYS_ERROR("error in registering path class dev\n");
+		goto out;
+	}
+
+	if (class_device_create_file(&path->class_dev_info.class_dev,
+				     &class_device_attr_viport_state)) {
+
+		SYS_ERROR("error in creating viport state file\n");
+		goto err_file;
+	}
+
+	if (class_device_create_file(&path->class_dev_info.class_dev,
+				     &class_device_attr_link_state)) {
+
+		SYS_ERROR("error in creating link state file\n");
+		goto err_file;
+	}
+
+	if (class_device_create_file(&path->class_dev_info.class_dev,
+				     &class_device_attr_heartbeat)) {
+
+		SYS_ERROR("create_vnic: error in creating"
+			   "heartbeat attr file\n");
+		goto err_file;
+	}
+
+	return 0;
+
+err_file:
+	class_device_unregister(&path->class_dev_info.class_dev);
+	wait_for_completion(&path->class_dev_info.released);
+out:
+	return -1;
+
+}
+
+ssize_t vnic_create_primary(struct class_device * class_dev,
+			    const char *buf, size_t count)
+{
+	struct class_dev_info *cdev =
+	    container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic_ib_port *target =
+	    container_of(cdev, struct vnic_ib_port, cdev_info);
+
+	struct path_param param;
+	int ret = -EINVAL;
+	struct vnic *vnic;
+
+	param.instance = 0;
+	param.rx_csum = MAXU64;
+	param.tx_csum = MAXU64;
+	param.heartbeat = MAXU64;
+
+	ret = vnic_parse_options(buf, &param);
+
+	if (ret)
+		goto out;
+
+	param.ibdev = target->dev->dev;
+	param.ibport = target;
+	param.port = target->port_num;
+
+	vnic = create_vnic(&param);
+	if (!vnic) {
+		printk(KERN_ERR PFX "creating vnic failed\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (!create_netpath(&vnic->primary_path, &param)) {
+		printk(KERN_ERR PFX "creating primary netpath failed\n");
+		goto free_vnic;
+	}
+
+	if (setup_path_class_files(&vnic->primary_path, "primary_path"))
+		goto free_vnic;
+
+	if (vnic && !vnic->primary_path.viport) {
+		printk(KERN_ERR PFX "no valid netpaths\n");
+		goto free_vnic;
+	}
+
+	return count;
+
+free_vnic:
+	vnic_free(vnic);
+	ret = -EINVAL;
+out:
+	return ret;
+}
+
+ssize_t vnic_create_secondary(struct class_device * class_dev,
+			      const char *buf, size_t count)
+{
+	struct class_dev_info *cdev =
+	    container_of(class_dev, struct class_dev_info, class_dev);
+	struct vnic_ib_port *target =
+	    container_of(cdev, struct vnic_ib_port, cdev_info);
+
+	struct path_param param;
+	struct vnic *vnic;
+	int ret = -EINVAL;
+	struct list_head *ptr;
+	int found = 0;
+
+	param.instance = 0;
+	param.rx_csum = MAXU64;
+	param.tx_csum = MAXU64;
+	param.heartbeat = MAXU64;
+
+	ret = vnic_parse_options(buf, &param);
+
+	if (ret)
+		goto out;
+
+	list_for_each(ptr, &vnic_list) {
+		vnic = list_entry(ptr, struct vnic, list_ptrs);
+		if (!strncmp(vnic->config->name, param.name, IFNAMSIZ)) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found) {
+		printk(KERN_ERR PFX
+		       "primary connection with name '%s' does not exist\n",
+		       param.name);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	param.ibdev = target->dev->dev;
+	param.ibport = target;
+	param.port = target->port_num;
+
+	if (!create_netpath(&vnic->secondary_path, &param)) {
+		printk(KERN_ERR PFX "creating secondary netpath failed\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (setup_path_class_files(&vnic->secondary_path, "secondary_path"))
+		goto free_vnic;
+
+	return count;
+
+free_vnic:
+	vnic_free(vnic);
+	ret = -EINVAL;
+out:
+	return ret;
+}
diff --git a/drivers/infiniband/ulp/vnic/vnic_sys.h b/drivers/infiniband/ulp/vnic/vnic_sys.h
new file mode 100644
index 0000000..c4c961e
--- /dev/null
+++ b/drivers/infiniband/ulp/vnic/vnic_sys.h
@@ -0,0 +1,51 @@
+/*
+ * 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_SYS_H_INCLUDED
+#define VNIC_SYS_H_INCLUDED
+
+struct class_dev_info {
+	struct class_device	class_dev;
+	struct completion	released;
+};
+
+extern struct class vnic_class;
+extern struct class_dev_info interface_cdev;
+extern ssize_t vnic_create_primary(struct class_device *class_dev,
+ 			   const char *buf, size_t count);
+
+extern ssize_t vnic_create_secondary(struct class_device *class_dev,
+ 			     const char *buf, size_t count);
+
+extern ssize_t vnic_delete(struct class_device *class_dev,
+			   const char *buf, size_t count);
+#endif	/*VNIC_SYS_H_INCLUDED*/





More information about the general mailing list