diff options
Diffstat (limited to 'drivers/infiniband/core/sysfs.c')
| -rw-r--r-- | drivers/infiniband/core/sysfs.c | 374 | 
1 files changed, 250 insertions, 124 deletions
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index 14606afbfaa8..a5793c8f1590 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -56,8 +56,10 @@ struct ib_port {  	struct gid_attr_group *gid_attr_group;  	struct attribute_group gid_group;  	struct attribute_group pkey_group; -	u8                     port_num;  	struct attribute_group *pma_table; +	struct attribute_group *hw_stats_ag; +	struct rdma_hw_stats   *hw_stats; +	u8                     port_num;  };  struct port_attribute { @@ -80,6 +82,18 @@ struct port_table_attribute {  	__be16			attr_id;  }; +struct hw_stats_attribute { +	struct attribute	attr; +	ssize_t			(*show)(struct kobject *kobj, +					struct attribute *attr, char *buf); +	ssize_t			(*store)(struct kobject *kobj, +					 struct attribute *attr, +					 const char *buf, +					 size_t count); +	int			index; +	u8			port_num; +}; +  static ssize_t port_attr_show(struct kobject *kobj,  			      struct attribute *attr, char *buf)  { @@ -733,6 +747,220 @@ static struct attribute_group *get_counter_table(struct ib_device *dev,  	return &pma_group;  } +static int update_hw_stats(struct ib_device *dev, struct rdma_hw_stats *stats, +			   u8 port_num, int index) +{ +	int ret; + +	if (time_is_after_eq_jiffies(stats->timestamp + stats->lifespan)) +		return 0; +	ret = dev->get_hw_stats(dev, stats, port_num, index); +	if (ret < 0) +		return ret; +	if (ret == stats->num_counters) +		stats->timestamp = jiffies; + +	return 0; +} + +static ssize_t print_hw_stat(struct rdma_hw_stats *stats, int index, char *buf) +{ +	return sprintf(buf, "%llu\n", stats->value[index]); +} + +static ssize_t show_hw_stats(struct kobject *kobj, struct attribute *attr, +			     char *buf) +{ +	struct ib_device *dev; +	struct ib_port *port; +	struct hw_stats_attribute *hsa; +	struct rdma_hw_stats *stats; +	int ret; + +	hsa = container_of(attr, struct hw_stats_attribute, attr); +	if (!hsa->port_num) { +		dev = container_of((struct device *)kobj, +				   struct ib_device, dev); +		stats = dev->hw_stats; +	} else { +		port = container_of(kobj, struct ib_port, kobj); +		dev = port->ibdev; +		stats = port->hw_stats; +	} +	ret = update_hw_stats(dev, stats, hsa->port_num, hsa->index); +	if (ret) +		return ret; +	return print_hw_stat(stats, hsa->index, buf); +} + +static ssize_t show_stats_lifespan(struct kobject *kobj, +				   struct attribute *attr, +				   char *buf) +{ +	struct hw_stats_attribute *hsa; +	int msecs; + +	hsa = container_of(attr, struct hw_stats_attribute, attr); +	if (!hsa->port_num) { +		struct ib_device *dev = container_of((struct device *)kobj, +						     struct ib_device, dev); +		msecs = jiffies_to_msecs(dev->hw_stats->lifespan); +	} else { +		struct ib_port *p = container_of(kobj, struct ib_port, kobj); +		msecs = jiffies_to_msecs(p->hw_stats->lifespan); +	} +	return sprintf(buf, "%d\n", msecs); +} + +static ssize_t set_stats_lifespan(struct kobject *kobj, +				  struct attribute *attr, +				  const char *buf, size_t count) +{ +	struct hw_stats_attribute *hsa; +	int msecs; +	int jiffies; +	int ret; + +	ret = kstrtoint(buf, 10, &msecs); +	if (ret) +		return ret; +	if (msecs < 0 || msecs > 10000) +		return -EINVAL; +	jiffies = msecs_to_jiffies(msecs); +	hsa = container_of(attr, struct hw_stats_attribute, attr); +	if (!hsa->port_num) { +		struct ib_device *dev = container_of((struct device *)kobj, +						     struct ib_device, dev); +		dev->hw_stats->lifespan = jiffies; +	} else { +		struct ib_port *p = container_of(kobj, struct ib_port, kobj); +		p->hw_stats->lifespan = jiffies; +	} +	return count; +} + +static void free_hsag(struct kobject *kobj, struct attribute_group *attr_group) +{ +	struct attribute **attr; + +	sysfs_remove_group(kobj, attr_group); + +	for (attr = attr_group->attrs; *attr; attr++) +		kfree(*attr); +	kfree(attr_group); +} + +static struct attribute *alloc_hsa(int index, u8 port_num, const char *name) +{ +	struct hw_stats_attribute *hsa; + +	hsa = kmalloc(sizeof(*hsa), GFP_KERNEL); +	if (!hsa) +		return NULL; + +	hsa->attr.name = (char *)name; +	hsa->attr.mode = S_IRUGO; +	hsa->show = show_hw_stats; +	hsa->store = NULL; +	hsa->index = index; +	hsa->port_num = port_num; + +	return &hsa->attr; +} + +static struct attribute *alloc_hsa_lifespan(char *name, u8 port_num) +{ +	struct hw_stats_attribute *hsa; + +	hsa = kmalloc(sizeof(*hsa), GFP_KERNEL); +	if (!hsa) +		return NULL; + +	hsa->attr.name = name; +	hsa->attr.mode = S_IWUSR | S_IRUGO; +	hsa->show = show_stats_lifespan; +	hsa->store = set_stats_lifespan; +	hsa->index = 0; +	hsa->port_num = port_num; + +	return &hsa->attr; +} + +static void setup_hw_stats(struct ib_device *device, struct ib_port *port, +			   u8 port_num) +{ +	struct attribute_group *hsag; +	struct rdma_hw_stats *stats; +	int i, ret; + +	stats = device->alloc_hw_stats(device, port_num); + +	if (!stats) +		return; + +	if (!stats->names || stats->num_counters <= 0) +		goto err_free_stats; + +	/* +	 * Two extra attribue elements here, one for the lifespan entry and +	 * one to NULL terminate the list for the sysfs core code +	 */ +	hsag = kzalloc(sizeof(*hsag) + +		       sizeof(void *) * (stats->num_counters + 2), +		       GFP_KERNEL); +	if (!hsag) +		goto err_free_stats; + +	ret = device->get_hw_stats(device, stats, port_num, +				   stats->num_counters); +	if (ret != stats->num_counters) +		goto err_free_hsag; + +	stats->timestamp = jiffies; + +	hsag->name = "hw_counters"; +	hsag->attrs = (void *)hsag + sizeof(*hsag); + +	for (i = 0; i < stats->num_counters; i++) { +		hsag->attrs[i] = alloc_hsa(i, port_num, stats->names[i]); +		if (!hsag->attrs[i]) +			goto err; +		sysfs_attr_init(hsag->attrs[i]); +	} + +	/* treat an error here as non-fatal */ +	hsag->attrs[i] = alloc_hsa_lifespan("lifespan", port_num); +	if (hsag->attrs[i]) +		sysfs_attr_init(hsag->attrs[i]); + +	if (port) { +		struct kobject *kobj = &port->kobj; +		ret = sysfs_create_group(kobj, hsag); +		if (ret) +			goto err; +		port->hw_stats_ag = hsag; +		port->hw_stats = stats; +	} else { +		struct kobject *kobj = &device->dev.kobj; +		ret = sysfs_create_group(kobj, hsag); +		if (ret) +			goto err; +		device->hw_stats_ag = hsag; +		device->hw_stats = stats; +	} + +	return; + +err: +	for (; i >= 0; i--) +		kfree(hsag->attrs[i]); +err_free_hsag: +	kfree(hsag); +err_free_stats: +	kfree(stats); +	return; +} +  static int add_port(struct ib_device *device, int port_num,  		    int (*port_callback)(struct ib_device *,  					 u8, struct kobject *)) @@ -835,6 +1063,14 @@ static int add_port(struct ib_device *device, int port_num,  			goto err_remove_pkey;  	} +	/* +	 * If port == 0, it means we have only one port and the parent +	 * device, not this port device, should be the holder of the +	 * hw_counters +	 */ +	if (device->alloc_hw_stats && port_num) +		setup_hw_stats(device, p, port_num); +  	list_add_tail(&p->kobj.entry, &device->port_list);  	kobject_uevent(&p->kobj, KOBJ_ADD); @@ -972,120 +1208,6 @@ static struct device_attribute *ib_class_attributes[] = {  	&dev_attr_node_desc  }; -/* Show a given an attribute in the statistics group */ -static ssize_t show_protocol_stat(const struct device *device, -			    struct device_attribute *attr, char *buf, -			    unsigned offset) -{ -	struct ib_device *dev = container_of(device, struct ib_device, dev); -	union rdma_protocol_stats stats; -	ssize_t ret; - -	ret = dev->get_protocol_stats(dev, &stats); -	if (ret) -		return ret; - -	return sprintf(buf, "%llu\n", -		       (unsigned long long) ((u64 *) &stats)[offset]); -} - -/* generate a read-only iwarp statistics attribute */ -#define IW_STATS_ENTRY(name)						\ -static ssize_t show_##name(struct device *device,			\ -			   struct device_attribute *attr, char *buf)	\ -{									\ -	return show_protocol_stat(device, attr, buf,			\ -				  offsetof(struct iw_protocol_stats, name) / \ -				  sizeof (u64));			\ -}									\ -static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) - -IW_STATS_ENTRY(ipInReceives); -IW_STATS_ENTRY(ipInHdrErrors); -IW_STATS_ENTRY(ipInTooBigErrors); -IW_STATS_ENTRY(ipInNoRoutes); -IW_STATS_ENTRY(ipInAddrErrors); -IW_STATS_ENTRY(ipInUnknownProtos); -IW_STATS_ENTRY(ipInTruncatedPkts); -IW_STATS_ENTRY(ipInDiscards); -IW_STATS_ENTRY(ipInDelivers); -IW_STATS_ENTRY(ipOutForwDatagrams); -IW_STATS_ENTRY(ipOutRequests); -IW_STATS_ENTRY(ipOutDiscards); -IW_STATS_ENTRY(ipOutNoRoutes); -IW_STATS_ENTRY(ipReasmTimeout); -IW_STATS_ENTRY(ipReasmReqds); -IW_STATS_ENTRY(ipReasmOKs); -IW_STATS_ENTRY(ipReasmFails); -IW_STATS_ENTRY(ipFragOKs); -IW_STATS_ENTRY(ipFragFails); -IW_STATS_ENTRY(ipFragCreates); -IW_STATS_ENTRY(ipInMcastPkts); -IW_STATS_ENTRY(ipOutMcastPkts); -IW_STATS_ENTRY(ipInBcastPkts); -IW_STATS_ENTRY(ipOutBcastPkts); -IW_STATS_ENTRY(tcpRtoAlgorithm); -IW_STATS_ENTRY(tcpRtoMin); -IW_STATS_ENTRY(tcpRtoMax); -IW_STATS_ENTRY(tcpMaxConn); -IW_STATS_ENTRY(tcpActiveOpens); -IW_STATS_ENTRY(tcpPassiveOpens); -IW_STATS_ENTRY(tcpAttemptFails); -IW_STATS_ENTRY(tcpEstabResets); -IW_STATS_ENTRY(tcpCurrEstab); -IW_STATS_ENTRY(tcpInSegs); -IW_STATS_ENTRY(tcpOutSegs); -IW_STATS_ENTRY(tcpRetransSegs); -IW_STATS_ENTRY(tcpInErrs); -IW_STATS_ENTRY(tcpOutRsts); - -static struct attribute *iw_proto_stats_attrs[] = { -	&dev_attr_ipInReceives.attr, -	&dev_attr_ipInHdrErrors.attr, -	&dev_attr_ipInTooBigErrors.attr, -	&dev_attr_ipInNoRoutes.attr, -	&dev_attr_ipInAddrErrors.attr, -	&dev_attr_ipInUnknownProtos.attr, -	&dev_attr_ipInTruncatedPkts.attr, -	&dev_attr_ipInDiscards.attr, -	&dev_attr_ipInDelivers.attr, -	&dev_attr_ipOutForwDatagrams.attr, -	&dev_attr_ipOutRequests.attr, -	&dev_attr_ipOutDiscards.attr, -	&dev_attr_ipOutNoRoutes.attr, -	&dev_attr_ipReasmTimeout.attr, -	&dev_attr_ipReasmReqds.attr, -	&dev_attr_ipReasmOKs.attr, -	&dev_attr_ipReasmFails.attr, -	&dev_attr_ipFragOKs.attr, -	&dev_attr_ipFragFails.attr, -	&dev_attr_ipFragCreates.attr, -	&dev_attr_ipInMcastPkts.attr, -	&dev_attr_ipOutMcastPkts.attr, -	&dev_attr_ipInBcastPkts.attr, -	&dev_attr_ipOutBcastPkts.attr, -	&dev_attr_tcpRtoAlgorithm.attr, -	&dev_attr_tcpRtoMin.attr, -	&dev_attr_tcpRtoMax.attr, -	&dev_attr_tcpMaxConn.attr, -	&dev_attr_tcpActiveOpens.attr, -	&dev_attr_tcpPassiveOpens.attr, -	&dev_attr_tcpAttemptFails.attr, -	&dev_attr_tcpEstabResets.attr, -	&dev_attr_tcpCurrEstab.attr, -	&dev_attr_tcpInSegs.attr, -	&dev_attr_tcpOutSegs.attr, -	&dev_attr_tcpRetransSegs.attr, -	&dev_attr_tcpInErrs.attr, -	&dev_attr_tcpOutRsts.attr, -	NULL -}; - -static struct attribute_group iw_stats_group = { -	.name	= "proto_stats", -	.attrs	= iw_proto_stats_attrs, -}; -  static void free_port_list_attributes(struct ib_device *device)  {  	struct kobject *p, *t; @@ -1093,6 +1215,10 @@ static void free_port_list_attributes(struct ib_device *device)  	list_for_each_entry_safe(p, t, &device->port_list, entry) {  		struct ib_port *port = container_of(p, struct ib_port, kobj);  		list_del(&p->entry); +		if (port->hw_stats) { +			kfree(port->hw_stats); +			free_hsag(&port->kobj, port->hw_stats_ag); +		}  		sysfs_remove_group(p, port->pma_table);  		sysfs_remove_group(p, &port->pkey_group);  		sysfs_remove_group(p, &port->gid_group); @@ -1149,11 +1275,8 @@ int ib_device_register_sysfs(struct ib_device *device,  		}  	} -	if (device->node_type == RDMA_NODE_RNIC && device->get_protocol_stats) { -		ret = sysfs_create_group(&class_dev->kobj, &iw_stats_group); -		if (ret) -			goto err_put; -	} +	if (device->alloc_hw_stats) +		setup_hw_stats(device, NULL, 0);  	return 0; @@ -1169,15 +1292,18 @@ err:  void ib_device_unregister_sysfs(struct ib_device *device)  { -	/* Hold kobject until ib_dealloc_device() */ -	struct kobject *kobj_dev = kobject_get(&device->dev.kobj);  	int i; -	if (device->node_type == RDMA_NODE_RNIC && device->get_protocol_stats) -		sysfs_remove_group(kobj_dev, &iw_stats_group); +	/* Hold kobject until ib_dealloc_device() */ +	kobject_get(&device->dev.kobj);  	free_port_list_attributes(device); +	if (device->hw_stats) { +		kfree(device->hw_stats); +		free_hsag(&device->dev.kobj, device->hw_stats_ag); +	} +  	for (i = 0; i < ARRAY_SIZE(ib_class_attributes); ++i)  		device_remove_file(&device->dev, ib_class_attributes[i]);  |