[openib-general] [PATCH] More sysfs support

Roland Dreier roland at topspin.com
Sun Sep 5 14:22:35 PDT 2004


I implemented some attributes to fill out the sysfs directory.
There's still a fair number more attributes to expose but I think this
is the complete framework.  It should be pretty quick to finish up so
that the current /proc/infiniband/core tree can be killed.

Greg, is it cool to create kobjects the way I do so that we can get
the hierarchy of ports in sysfs (like mthca0/ports/1)?  Also is there
any better way to dynamically create the GID table attribute group?


The code creates a class tree that looks like:

    /sys/class/infiniband/
    `-- mthca0
        |-- device -> ../../../devices/pci0000:00/0000:00:1f.0/0000:01:01.0/0000:02:00.0
        |-- driver -> ../../../bus/pci/drivers/ib_mthca
        |-- node_guid
        `-- ports
            |-- 1
            |   |-- gids
            |   |   |-- 0
            |   |   |-- 1
            ... 29 more entries 2...30 snipped ...
            |   |   `-- 31
            |   |-- lid
            |   `-- state
            `-- 2
                |-- gids
                |   |-- 0
                |   |-- 1
            ... 29 more entries 2...30 snipped ...
                |   `-- 31
                |-- lid
                `-- state

And the contents of the files look like:

    # cat /sys/class/infiniband/mthca0/node_guid
    0005:ad00:0001:8211

    # cat /sys/class/infiniband/mthca0/ports/1/state
    4: ACTIVE

    # cat /sys/class/infiniband/mthca0/ports/1/gids/0
    fe80:0000:0000:0000:0005:ad00:0001:8212

By the way, I chose the plurals "ports" and "gids" to match other
sysfs usage like "devices" and "drivers."  However I'm wondering if it
might not make more sense to use "port" and "gid" so that one could
have a path like mthca0/port/1/gid/0.  Any opinions?

Thanks,
  Roland

Index: infiniband/include/ib_verbs.h
===================================================================
--- infiniband/include/ib_verbs.h	(revision 727)
+++ infiniband/include/ib_verbs.h	(working copy)
@@ -689,6 +689,8 @@
 	ib_mad_process_func          mad_process;
 
 	struct class_device          class_dev;
+	struct kobject               ports_parent;
+	struct list_head             port_list;
 
 	enum {
 		IB_DEV_UNINITIALIZED,
Index: infiniband/core/ib_sysfs.c
===================================================================
--- infiniband/core/ib_sysfs.c	(revision 727)
+++ infiniband/core/ib_sysfs.c	(working copy)
@@ -21,6 +21,139 @@
 
 #include "core_priv.h"
 
+struct ib_port {
+	struct kobject         kobj;
+	struct ib_device      *ibdev;
+	struct attribute_group gid_group;
+	struct attribute     **gid_attr;
+	u8                     port_num;
+};
+
+struct port_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct ib_port *, struct port_attribute *, char *buf);
+	ssize_t (*store)(struct ib_port *, struct port_attribute *, const char *buf, size_t count);
+};
+
+#define PORT_ATTR(_name, _mode, _show, _store) 		\
+struct port_attribute port_attr_##_name = { 		\
+	.attr  = { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \
+	.show  = _show, 				\
+	.store = _store					\
+}
+
+struct port_table_attribute {
+	struct port_attribute attr;
+	int                   index;
+};
+
+static ssize_t port_attr_show(struct kobject *kobj,
+			      struct attribute *attr, char *buf)
+{
+	struct port_attribute *port_attr =
+		container_of(attr, struct port_attribute, attr);
+	struct ib_port *p = container_of(kobj, struct ib_port, kobj);
+
+	if (!port_attr->show)
+		return 0;
+
+	return port_attr->show(p, port_attr, buf);
+}
+
+static struct sysfs_ops port_sysfs_ops = {
+	.show = port_attr_show
+};
+
+static ssize_t show_port_state(struct ib_port *p, struct port_attribute *unused,
+			       char *buf)
+{
+	struct ib_port_attr attr;
+	ssize_t ret;
+
+	static const char *state_name[] = {
+		[IB_PORT_NOP]		= "NOP",
+		[IB_PORT_DOWN]		= "DOWN",
+		[IB_PORT_INIT]		= "INIT",
+		[IB_PORT_ARMED]		= "ARMED",
+		[IB_PORT_ACTIVE]	= "ACTIVE",
+		[IB_PORT_ACTIVE_DEFER]	= "ACTIVE_DEFER"
+	};
+
+	ret = ib_query_port(p->ibdev, p->port_num, &attr);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%d: %s\n", attr.state,
+		       attr.state >= 0 && attr.state <= ARRAY_SIZE(state_name) ?
+		       state_name[attr.state] : "UNKNOWN");
+}
+
+static ssize_t show_port_lid(struct ib_port *p, struct port_attribute *unused,
+			     char *buf)
+{
+	struct ib_port_attr attr;
+	ssize_t ret;
+
+	ret = ib_query_port(p->ibdev, p->port_num, &attr);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "0x%x\n", attr.lid);
+}
+
+PORT_ATTR(state, S_IRUGO, show_port_state, NULL);
+PORT_ATTR(lid,   S_IRUGO, show_port_lid,   NULL);
+
+static struct attribute *port_default_attrs[] = {
+	&port_attr_state.attr,
+	&port_attr_lid.attr,
+	NULL
+};
+
+static ssize_t show_port_gid(struct ib_port *p, struct port_attribute *attr,
+			     char *buf)
+{
+	struct port_table_attribute *tab_attr =
+		container_of(attr, struct port_table_attribute, attr);
+	union ib_gid gid;
+	ssize_t ret;
+
+	ret = ib_query_gid(p->ibdev, p->port_num, tab_attr->index, &gid);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+		       be16_to_cpu(((u16 *) gid.raw)[0]),
+		       be16_to_cpu(((u16 *) gid.raw)[1]),
+		       be16_to_cpu(((u16 *) gid.raw)[2]),
+		       be16_to_cpu(((u16 *) gid.raw)[3]),
+		       be16_to_cpu(((u16 *) gid.raw)[4]),
+		       be16_to_cpu(((u16 *) gid.raw)[5]),
+		       be16_to_cpu(((u16 *) gid.raw)[6]),
+		       be16_to_cpu(((u16 *) gid.raw)[7]));
+}
+
+static void ib_port_release(struct kobject *kobj)
+{
+	struct ib_port *p = container_of(kobj, struct ib_port, kobj);
+	struct attribute *a;
+	int i;
+
+	for (i = 0; (a = p->gid_attr[i]); ++i) {
+		kfree(a->name);
+		kfree(a);
+	}
+
+	kfree(p->gid_attr);
+	kfree(p);
+}
+
+static struct kobj_type port_type = {
+	.release       = ib_port_release,
+	.sysfs_ops     = &port_sysfs_ops,
+	.default_attrs = port_default_attrs
+};
+
 static void ib_device_release(struct class_device *cdev)
 {
 	struct ib_device *dev = container_of(cdev, struct ib_device, class_dev);
@@ -35,6 +168,126 @@
 	return 0;
 }
 
+static int add_port(struct ib_device *device, int port_num)
+{
+	struct ib_port *p;
+	struct ib_port_attr attr;
+	struct port_table_attribute **gid_attr;
+	int i;
+	int ret;
+
+	ret = ib_query_port(device, port_num, &attr);
+	if (ret)
+		return ret;
+
+	p = kmalloc(sizeof *p, GFP_KERNEL);
+	if (!p)
+		return -ENOMEM;
+	memset(p, 0, sizeof *p);
+
+	p->ibdev      = device;
+	p->port_num   = port_num;
+	p->kobj.ktype = &port_type;
+
+	p->kobj.parent = kobject_get(&device->ports_parent);
+	if (!p->kobj.parent) {
+		ret = -EBUSY;
+		goto err;
+	}
+
+	ret = kobject_set_name(&p->kobj, "%d", port_num);
+	if (ret)
+		goto err_put;
+
+	ret = kobject_register(&p->kobj);
+	if (ret)
+		goto err_put;
+
+	p->gid_attr = kmalloc((1 + attr.gid_tbl_len) * sizeof *p->gid_attr,
+			      GFP_KERNEL);
+	if (!p->gid_attr) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	memset(p->gid_attr, 0, (1 + attr.gid_tbl_len) * sizeof *p->gid_attr);
+
+	p->gid_group.name  = "gids";
+	p->gid_group.attrs = p->gid_attr;
+
+	gid_attr = (struct port_table_attribute **) p->gid_attr;
+
+	for (i = 0; i < attr.gid_tbl_len; ++i) {
+		gid_attr[i] = kmalloc(sizeof *gid_attr[i], GFP_KERNEL);
+		if (!gid_attr[i]) {
+			ret = -ENOMEM;
+			goto err_free;
+		}
+		memset(gid_attr[i], 0, sizeof *gid_attr[i]);
+		gid_attr[i]->attr.attr.name = kmalloc(8, GFP_KERNEL);
+		if (!gid_attr[i]->attr.attr.name) {
+			ret = -ENOMEM;
+			goto err_free;
+		}
+
+		if (snprintf(gid_attr[i]->attr.attr.name, 8, "%d", i) >= 8) {
+			ret = -ENOMEM;
+			goto err_free;
+		}
+
+		gid_attr[i]->attr.attr.mode  = S_IRUGO;
+		gid_attr[i]->attr.attr.owner = THIS_MODULE;
+		gid_attr[i]->attr.show       = show_port_gid;
+		gid_attr[i]->index           = i;
+	}	
+
+	ret = sysfs_create_group(&p->kobj, &p->gid_group);
+	if (ret)
+		goto err_free;
+
+	list_add_tail(&p->kobj.entry, &device->port_list);
+
+	return 0;
+
+err_free:
+	for (i = 0; i < attr.gid_tbl_len; ++i) {
+		if (p->gid_attr[i])
+			kfree(p->gid_attr[i]->name);
+		kfree(p->gid_attr[i]);
+	}
+
+	kfree(p->gid_attr);
+
+err_put:
+	kobject_put(&device->ports_parent);
+
+err:
+	kfree(p);
+	return ret;
+}
+
+static ssize_t show_node_guid(struct class_device *cdev, char *buf)
+{
+	struct ib_device *dev = container_of(cdev, struct ib_device, class_dev);
+	struct ib_device_attr attr;
+	ssize_t ret;
+
+	ret = ib_query_device(dev, &attr);
+	if (ret)
+		return ret;
+
+	return sprintf(buf, "%04x:%04x:%04x:%04x\n",
+		       be16_to_cpu(((u16 *) &attr.node_guid)[0]),
+		       be16_to_cpu(((u16 *) &attr.node_guid)[1]),
+		       be16_to_cpu(((u16 *) &attr.node_guid)[2]),
+		       be16_to_cpu(((u16 *) &attr.node_guid)[3]));
+}
+
+CLASS_DEVICE_ATTR(node_guid, S_IRUGO, show_node_guid, NULL);
+
+static struct class_device_attribute *ib_class_attributes[] = {
+	&class_device_attr_node_guid
+};
+
 static struct class ib_class = {
 	.name    = "infiniband",
 	.release = ib_device_release,
@@ -44,18 +297,93 @@
 int ib_device_register_sysfs(struct ib_device *device)
 {
 	struct class_device *class_dev = &device->class_dev;
+	int ret;
+	int i;
 
 	class_dev->class      = &ib_class;
 	class_dev->class_data = device;
 	strlcpy(class_dev->class_id, device->name, BUS_ID_SIZE);
 
-	return class_device_register(class_dev);
+	INIT_LIST_HEAD(&device->port_list);
 
+	ret = class_device_register(class_dev);
+	if (ret)
+		goto err;
 
+	for (i = 0; i < ARRAY_SIZE(ib_class_attributes); ++i) {
+		ret = class_device_create_file(class_dev, ib_class_attributes[i]);
+		if (ret)
+			goto err_unregister;
+	}
+
+	device->ports_parent.parent = kobject_get(&class_dev->kobj);
+	if (!device->ports_parent.parent) {
+		ret = -EBUSY;
+		goto err_unregister;
+	}
+	ret = kobject_set_name(&device->ports_parent, "ports");
+	if (ret)
+		goto err_put;
+	ret = kobject_register(&device->ports_parent);
+	if (ret)
+		goto err_put;
+
+	if (device->node_type == IB_NODE_SWITCH) {
+		ret = add_port(device, 0);
+		if (ret)
+			goto err_put;
+	} else {
+		struct ib_device_attr attr;
+		int i;
+
+		ret = ib_query_device(device, &attr);
+		if (ret)
+			goto err_put;
+
+		for (i = 1; i <= attr.phys_port_cnt; ++i) {
+			ret = add_port(device, i);
+			if (ret)
+				goto err_put;
+		}
+	}
+
+	return 0;
+
+err_put:
+	{
+		struct kobject *p, *t;
+		struct ib_port *port;
+
+		list_for_each_entry_safe(p, t, &device->port_list, entry) {
+			list_del(&p->entry);
+			port = container_of(p, struct ib_port, kobj);
+			sysfs_remove_group(p, &port->gid_group);
+			kobject_unregister(p);
+		}
+	}
+
+	kobject_put(&class_dev->kobj);
+
+err_unregister:
+	class_device_unregister(class_dev);
+
+err:
+	return ret;
 }
 
 void ib_device_deregister_sysfs(struct ib_device *device)
 {
+	struct kobject *p, *t;
+	struct ib_port *port;
+
+	list_for_each_entry_safe(p, t, &device->port_list, entry) {
+		list_del(&p->entry);
+		port = container_of(p, struct ib_port, kobj);
+		sysfs_remove_group(p,&port->gid_group);
+		kobject_unregister(p);
+	}
+
+	kobject_unregister(&device->ports_parent);
 	class_device_unregister(&device->class_dev);
 }
 



More information about the general mailing list