[PATCH] iseries_veth: Add sysfs support for connection structs

To aid in field debugging, add sysfs support for iseries_veth's connection
structures. At the moment this is all read-only, however we could think about
adding write support for some attributes in future.

Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c
index b945bf0..25481ea 100644
--- a/drivers/net/iseries_veth.c
+++ b/drivers/net/iseries_veth.c
@@ -182,10 +182,6 @@
 static void veth_timed_ack(unsigned long ptr);
 static void veth_timed_reset(unsigned long ptr);
 
-static struct kobj_type veth_lpar_connection_ktype = {
-	.release	= veth_release_connection
-};
-
 /*
  * Utility functions
  */
@@ -280,6 +276,81 @@
 }
 
 /*
+ * sysfs support
+ */
+
+struct veth_cnx_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct veth_lpar_connection *, char *buf);
+	ssize_t (*store)(struct veth_lpar_connection *, const char *buf);
+};
+
+static ssize_t veth_cnx_attribute_show(struct kobject *kobj,
+		struct attribute *attr, char *buf)
+{
+	struct veth_cnx_attribute *cnx_attr;
+	struct veth_lpar_connection *cnx;
+
+	cnx_attr = container_of(attr, struct veth_cnx_attribute, attr);
+	cnx = container_of(kobj, struct veth_lpar_connection, kobject);
+
+	if (!cnx_attr->show)
+		return -EIO;
+
+	return cnx_attr->show(cnx, buf);
+}
+
+#define CUSTOM_CNX_ATTR(_name, _format, _expression)			\
+static ssize_t _name##_show(struct veth_lpar_connection *cnx, char *buf)\
+{									\
+	return sprintf(buf, _format, _expression);			\
+}									\
+struct veth_cnx_attribute veth_cnx_attr_##_name = __ATTR_RO(_name)
+
+#define SIMPLE_CNX_ATTR(_name)	\
+	CUSTOM_CNX_ATTR(_name, "%lu\n", (unsigned long)cnx->_name)
+
+SIMPLE_CNX_ATTR(outstanding_tx);
+SIMPLE_CNX_ATTR(remote_lp);
+SIMPLE_CNX_ATTR(num_events);
+SIMPLE_CNX_ATTR(src_inst);
+SIMPLE_CNX_ATTR(dst_inst);
+SIMPLE_CNX_ATTR(num_pending_acks);
+SIMPLE_CNX_ATTR(num_ack_events);
+CUSTOM_CNX_ATTR(ack_timeout, "%d\n", jiffies_to_msecs(cnx->ack_timeout));
+CUSTOM_CNX_ATTR(reset_timeout, "%d\n", jiffies_to_msecs(cnx->reset_timeout));
+CUSTOM_CNX_ATTR(state, "0x%.4lX\n", cnx->state);
+CUSTOM_CNX_ATTR(last_contact, "%d\n", cnx->last_contact ?
+		jiffies_to_msecs(jiffies - cnx->last_contact) : 0);
+
+#define GET_CNX_ATTR(_name)	(&veth_cnx_attr_##_name.attr)
+
+static struct attribute *veth_cnx_default_attrs[] = {
+	GET_CNX_ATTR(outstanding_tx),
+	GET_CNX_ATTR(remote_lp),
+	GET_CNX_ATTR(num_events),
+	GET_CNX_ATTR(reset_timeout),
+	GET_CNX_ATTR(last_contact),
+	GET_CNX_ATTR(state),
+	GET_CNX_ATTR(src_inst),
+	GET_CNX_ATTR(dst_inst),
+	GET_CNX_ATTR(num_pending_acks),
+	GET_CNX_ATTR(num_ack_events),
+	GET_CNX_ATTR(ack_timeout),
+	NULL
+};
+
+static struct sysfs_ops veth_cnx_sysfs_ops = {
+		.show = veth_cnx_attribute_show
+};
+
+static struct kobj_type veth_lpar_connection_ktype = {
+	.release	= veth_release_connection,
+	.sysfs_ops	= &veth_cnx_sysfs_ops,
+	.default_attrs	= veth_cnx_default_attrs
+};
+
+/*
  * LPAR connection code
  */
 
@@ -1493,6 +1564,8 @@
 		if (!cnx)
 			continue;
 
+		/* Remove the connection from sysfs */
+		kobject_del(&cnx->kobject);
 		/* Drop the driver's reference to the connection */
 		kobject_put(&cnx->kobject);
 	}
@@ -1523,6 +1596,19 @@
 	if (rc != 0)
 		goto error;
 
+	for (i = 0; i < HVMAXARCHITECTEDLPS; ++i) {
+		struct kobject *kobj;
+
+		if (!veth_cnx[i])
+			continue;
+
+		kobj = &veth_cnx[i]->kobject;
+		kobj->parent = &veth_driver.driver.kobj;
+		/* If the add failes, complain but otherwise continue */
+		if (0 != kobject_add(kobj))
+			veth_error("cnx %d: Failed adding to sysfs.\n", i);
+	}
+
 	return 0;
 
 error: