mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	RDMA/netlink: Implement counter dumpit calback
This patch adds the ability to return all available counters together with their properties and hwstats. Signed-off-by: Mark Zhang <markz@mellanox.com> Reviewed-by: Majd Dibbiny <majd@mellanox.com> Signed-off-by: Leon Romanovsky <leonro@mellanox.com> Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
This commit is contained in:
		
							parent
							
								
									b47ae6f803
								
							
						
					
					
						commit
						c4ffee7c9b
					
				
					 6 changed files with 268 additions and 8 deletions
				
			
		|  | @ -59,7 +59,7 @@ static struct rdma_counter *rdma_counter_alloc(struct ib_device *dev, u8 port, | |||
| { | ||||
| 	struct rdma_counter *counter; | ||||
| 
 | ||||
| 	if (!dev->ops.counter_dealloc) | ||||
| 	if (!dev->ops.counter_dealloc || !dev->ops.counter_alloc_stats) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	counter = kzalloc(sizeof(*counter), GFP_KERNEL); | ||||
|  | @ -69,16 +69,25 @@ static struct rdma_counter *rdma_counter_alloc(struct ib_device *dev, u8 port, | |||
| 	counter->device    = dev; | ||||
| 	counter->port      = port; | ||||
| 	counter->res.type  = RDMA_RESTRACK_COUNTER; | ||||
| 	counter->stats     = dev->ops.counter_alloc_stats(counter); | ||||
| 	if (!counter->stats) | ||||
| 		goto err_stats; | ||||
| 
 | ||||
| 	counter->mode.mode = mode; | ||||
| 	kref_init(&counter->kref); | ||||
| 	mutex_init(&counter->lock); | ||||
| 
 | ||||
| 	return counter; | ||||
| 
 | ||||
| err_stats: | ||||
| 	kfree(counter); | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static void rdma_counter_free(struct rdma_counter *counter) | ||||
| { | ||||
| 	rdma_restrack_del(&counter->res); | ||||
| 	kfree(counter->stats); | ||||
| 	kfree(counter); | ||||
| } | ||||
| 
 | ||||
|  | @ -275,6 +284,21 @@ int rdma_counter_unbind_qp(struct ib_qp *qp, bool force) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int rdma_counter_query_stats(struct rdma_counter *counter) | ||||
| { | ||||
| 	struct ib_device *dev = counter->device; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!dev->ops.counter_update_stats) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	mutex_lock(&counter->lock); | ||||
| 	ret = dev->ops.counter_update_stats(counter); | ||||
| 	mutex_unlock(&counter->lock); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| void rdma_counter_init(struct ib_device *dev) | ||||
| { | ||||
| 	struct rdma_port_counter *port_counter; | ||||
|  |  | |||
|  | @ -2471,9 +2471,11 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) | |||
| 	SET_DEVICE_OP(dev_ops, alloc_xrcd); | ||||
| 	SET_DEVICE_OP(dev_ops, attach_mcast); | ||||
| 	SET_DEVICE_OP(dev_ops, check_mr_status); | ||||
| 	SET_DEVICE_OP(dev_ops, counter_alloc_stats); | ||||
| 	SET_DEVICE_OP(dev_ops, counter_bind_qp); | ||||
| 	SET_DEVICE_OP(dev_ops, counter_dealloc); | ||||
| 	SET_DEVICE_OP(dev_ops, counter_unbind_qp); | ||||
| 	SET_DEVICE_OP(dev_ops, counter_update_stats); | ||||
| 	SET_DEVICE_OP(dev_ops, create_ah); | ||||
| 	SET_DEVICE_OP(dev_ops, create_counters); | ||||
| 	SET_DEVICE_OP(dev_ops, create_cq); | ||||
|  |  | |||
|  | @ -129,6 +129,13 @@ static const struct nla_policy nldev_policy[RDMA_NLDEV_ATTR_MAX] = { | |||
| 	[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK]	= { .type = NLA_U32 }, | ||||
| 	[RDMA_NLDEV_ATTR_STAT_MODE]		= { .type = NLA_U32 }, | ||||
| 	[RDMA_NLDEV_ATTR_STAT_RES]		= { .type = NLA_U32 }, | ||||
| 	[RDMA_NLDEV_ATTR_STAT_COUNTER]		= { .type = NLA_NESTED }, | ||||
| 	[RDMA_NLDEV_ATTR_STAT_COUNTER_ENTRY]	= { .type = NLA_NESTED }, | ||||
| 	[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]       = { .type = NLA_U32 }, | ||||
| 	[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]       = { .type = NLA_NESTED }, | ||||
| 	[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY]  = { .type = NLA_NESTED }, | ||||
| 	[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME] = { .type = NLA_NUL_STRING }, | ||||
| 	[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE] = { .type = NLA_U64 }, | ||||
| 	[RDMA_NLDEV_ATTR_SYS_IMAGE_GUID]	= { .type = NLA_U64 }, | ||||
| 	[RDMA_NLDEV_ATTR_UVERBS_DRIVER_ID]	= { .type = NLA_U32 }, | ||||
| 	[RDMA_NLDEV_NET_NS_FD]			= { .type = NLA_U32 }, | ||||
|  | @ -636,6 +643,152 @@ static int fill_res_pd_entry(struct sk_buff *msg, bool has_cap_net_admin, | |||
| err:	return -EMSGSIZE; | ||||
| } | ||||
| 
 | ||||
| static int fill_stat_counter_mode(struct sk_buff *msg, | ||||
| 				  struct rdma_counter *counter) | ||||
| { | ||||
| 	struct rdma_counter_mode *m = &counter->mode; | ||||
| 
 | ||||
| 	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_STAT_MODE, m->mode)) | ||||
| 		return -EMSGSIZE; | ||||
| 
 | ||||
| 	if (m->mode == RDMA_COUNTER_MODE_AUTO) | ||||
| 		if ((m->mask & RDMA_COUNTER_MASK_QP_TYPE) && | ||||
| 		    nla_put_u8(msg, RDMA_NLDEV_ATTR_RES_TYPE, m->param.qp_type)) | ||||
| 			return -EMSGSIZE; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int fill_stat_counter_qp_entry(struct sk_buff *msg, u32 qpn) | ||||
| { | ||||
| 	struct nlattr *entry_attr; | ||||
| 
 | ||||
| 	entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_QP_ENTRY); | ||||
| 	if (!entry_attr) | ||||
| 		return -EMSGSIZE; | ||||
| 
 | ||||
| 	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_RES_LQPN, qpn)) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	nla_nest_end(msg, entry_attr); | ||||
| 	return 0; | ||||
| 
 | ||||
| err: | ||||
| 	nla_nest_cancel(msg, entry_attr); | ||||
| 	return -EMSGSIZE; | ||||
| } | ||||
| 
 | ||||
| static int fill_stat_counter_qps(struct sk_buff *msg, | ||||
| 				 struct rdma_counter *counter) | ||||
| { | ||||
| 	struct rdma_restrack_entry *res; | ||||
| 	struct rdma_restrack_root *rt; | ||||
| 	struct nlattr *table_attr; | ||||
| 	struct ib_qp *qp = NULL; | ||||
| 	unsigned long id = 0; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	table_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_RES_QP); | ||||
| 
 | ||||
| 	rt = &counter->device->res[RDMA_RESTRACK_QP]; | ||||
| 	xa_lock(&rt->xa); | ||||
| 	xa_for_each(&rt->xa, id, res) { | ||||
| 		if (!rdma_is_visible_in_pid_ns(res)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		qp = container_of(res, struct ib_qp, res); | ||||
| 		if (qp->qp_type == IB_QPT_RAW_PACKET && !capable(CAP_NET_RAW)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (!qp->counter || (qp->counter->id != counter->id)) | ||||
| 			continue; | ||||
| 
 | ||||
| 		ret = fill_stat_counter_qp_entry(msg, qp->qp_num); | ||||
| 		if (ret) | ||||
| 			goto err; | ||||
| 	} | ||||
| 
 | ||||
| 	xa_unlock(&rt->xa); | ||||
| 	nla_nest_end(msg, table_attr); | ||||
| 	return 0; | ||||
| 
 | ||||
| err: | ||||
| 	xa_unlock(&rt->xa); | ||||
| 	nla_nest_cancel(msg, table_attr); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int fill_stat_hwcounter_entry(struct sk_buff *msg, | ||||
| 				     const char *name, u64 value) | ||||
| { | ||||
| 	struct nlattr *entry_attr; | ||||
| 
 | ||||
| 	entry_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY); | ||||
| 	if (!entry_attr) | ||||
| 		return -EMSGSIZE; | ||||
| 
 | ||||
| 	if (nla_put_string(msg, RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME, | ||||
| 			   name)) | ||||
| 		goto err; | ||||
| 	if (nla_put_u64_64bit(msg, RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE, | ||||
| 			      value, RDMA_NLDEV_ATTR_PAD)) | ||||
| 		goto err; | ||||
| 
 | ||||
| 	nla_nest_end(msg, entry_attr); | ||||
| 	return 0; | ||||
| 
 | ||||
| err: | ||||
| 	nla_nest_cancel(msg, entry_attr); | ||||
| 	return -EMSGSIZE; | ||||
| } | ||||
| 
 | ||||
| static int fill_stat_counter_hwcounters(struct sk_buff *msg, | ||||
| 					struct rdma_counter *counter) | ||||
| { | ||||
| 	struct rdma_hw_stats *st = counter->stats; | ||||
| 	struct nlattr *table_attr; | ||||
| 	int i; | ||||
| 
 | ||||
| 	table_attr = nla_nest_start(msg, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS); | ||||
| 	if (!table_attr) | ||||
| 		return -EMSGSIZE; | ||||
| 
 | ||||
| 	for (i = 0; i < st->num_counters; i++) | ||||
| 		if (fill_stat_hwcounter_entry(msg, st->names[i], st->value[i])) | ||||
| 			goto err; | ||||
| 
 | ||||
| 	nla_nest_end(msg, table_attr); | ||||
| 	return 0; | ||||
| 
 | ||||
| err: | ||||
| 	nla_nest_cancel(msg, table_attr); | ||||
| 	return -EMSGSIZE; | ||||
| } | ||||
| 
 | ||||
| static int fill_res_counter_entry(struct sk_buff *msg, bool has_cap_net_admin, | ||||
| 				  struct rdma_restrack_entry *res, | ||||
| 				  uint32_t port) | ||||
| { | ||||
| 	struct rdma_counter *counter = | ||||
| 		container_of(res, struct rdma_counter, res); | ||||
| 
 | ||||
| 	if (port && port != counter->port) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/* Dump it even query failed */ | ||||
| 	rdma_counter_query_stats(counter); | ||||
| 
 | ||||
| 	if (nla_put_u32(msg, RDMA_NLDEV_ATTR_PORT_INDEX, counter->port) || | ||||
| 	    nla_put_u32(msg, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, counter->id) || | ||||
| 	    fill_res_name_pid(msg, &counter->res) || | ||||
| 	    fill_stat_counter_mode(msg, counter) || | ||||
| 	    fill_stat_counter_qps(msg, counter) || | ||||
| 	    fill_stat_counter_hwcounters(msg, counter)) | ||||
| 		return -EMSGSIZE; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int nldev_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh, | ||||
| 			  struct netlink_ext_ack *extack) | ||||
| { | ||||
|  | @ -1003,6 +1156,13 @@ static const struct nldev_fill_res_entry fill_entries[RDMA_RESTRACK_MAX] = { | |||
| 		.entry = RDMA_NLDEV_ATTR_RES_PD_ENTRY, | ||||
| 		.id = RDMA_NLDEV_ATTR_RES_PDN, | ||||
| 	}, | ||||
| 	[RDMA_RESTRACK_COUNTER] = { | ||||
| 		.fill_res_func = fill_res_counter_entry, | ||||
| 		.nldev_cmd = RDMA_NLDEV_CMD_STAT_GET, | ||||
| 		.nldev_attr = RDMA_NLDEV_ATTR_STAT_COUNTER, | ||||
| 		.entry = RDMA_NLDEV_ATTR_STAT_COUNTER_ENTRY, | ||||
| 		.id = RDMA_NLDEV_ATTR_STAT_COUNTER_ID, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static int res_get_common_doit(struct sk_buff *skb, struct nlmsghdr *nlh, | ||||
|  | @ -1239,6 +1399,7 @@ RES_GET_FUNCS(cm_id, RDMA_RESTRACK_CM_ID); | |||
| RES_GET_FUNCS(cq, RDMA_RESTRACK_CQ); | ||||
| RES_GET_FUNCS(pd, RDMA_RESTRACK_PD); | ||||
| RES_GET_FUNCS(mr, RDMA_RESTRACK_MR); | ||||
| RES_GET_FUNCS(counter, RDMA_RESTRACK_COUNTER); | ||||
| 
 | ||||
| static LIST_HEAD(link_ops); | ||||
| static DECLARE_RWSEM(link_ops_rwsem); | ||||
|  | @ -1557,6 +1718,54 @@ static int nldev_stat_set_doit(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int nldev_stat_get_doit(struct sk_buff *skb, struct nlmsghdr *nlh, | ||||
| 			       struct netlink_ext_ack *extack) | ||||
| { | ||||
| 	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = nlmsg_parse(nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, | ||||
| 			  nldev_policy, extack); | ||||
| 	if (ret || !tb[RDMA_NLDEV_ATTR_STAT_RES]) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	switch (nla_get_u32(tb[RDMA_NLDEV_ATTR_STAT_RES])) { | ||||
| 	case RDMA_NLDEV_ATTR_RES_QP: | ||||
| 		ret = nldev_res_get_counter_doit(skb, nlh, extack); | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 		ret = -EINVAL; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int nldev_stat_get_dumpit(struct sk_buff *skb, | ||||
| 				 struct netlink_callback *cb) | ||||
| { | ||||
| 	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX]; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = nlmsg_parse(cb->nlh, 0, tb, RDMA_NLDEV_ATTR_MAX - 1, | ||||
| 			  nldev_policy, NULL); | ||||
| 	if (ret || !tb[RDMA_NLDEV_ATTR_STAT_RES]) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	switch (nla_get_u32(tb[RDMA_NLDEV_ATTR_STAT_RES])) { | ||||
| 	case RDMA_NLDEV_ATTR_RES_QP: | ||||
| 		ret = nldev_res_get_counter_dumpit(skb, cb); | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 		ret = -EINVAL; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = { | ||||
| 	[RDMA_NLDEV_CMD_GET] = { | ||||
| 		.doit = nldev_get_doit, | ||||
|  | @ -1615,6 +1824,10 @@ static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = { | |||
| 		.doit = nldev_stat_set_doit, | ||||
| 		.flags = RDMA_NL_ADMIN_PERM, | ||||
| 	}, | ||||
| 	[RDMA_NLDEV_CMD_STAT_GET] = { | ||||
| 		.doit = nldev_stat_get_doit, | ||||
| 		.dump = nldev_stat_get_dumpit, | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| void __init nldev_init(void) | ||||
|  |  | |||
|  | @ -2503,6 +2503,16 @@ struct ib_device_ops { | |||
| 	 * counter_dealloc -De-allocate the hw counter | ||||
| 	 */ | ||||
| 	int (*counter_dealloc)(struct rdma_counter *counter); | ||||
| 	/**
 | ||||
| 	 * counter_alloc_stats - Allocate a struct rdma_hw_stats and fill in | ||||
| 	 * the driver initialized data. | ||||
| 	 */ | ||||
| 	struct rdma_hw_stats *(*counter_alloc_stats)( | ||||
| 		struct rdma_counter *counter); | ||||
| 	/**
 | ||||
| 	 * counter_update_stats - Query the stats value of this counter | ||||
| 	 */ | ||||
| 	int (*counter_update_stats)(struct rdma_counter *counter); | ||||
| 
 | ||||
| 	DECLARE_RDMA_OBJ_SIZE(ib_ah); | ||||
| 	DECLARE_RDMA_OBJ_SIZE(ib_cq); | ||||
|  |  | |||
|  | @ -37,6 +37,7 @@ struct rdma_counter { | |||
| 	struct kref			kref; | ||||
| 	struct rdma_counter_mode	mode; | ||||
| 	struct mutex			lock; | ||||
| 	struct rdma_hw_stats		*stats; | ||||
| 	u8				port; | ||||
| }; | ||||
| 
 | ||||
|  | @ -47,4 +48,6 @@ int rdma_counter_set_auto_mode(struct ib_device *dev, u8 port, | |||
| int rdma_counter_bind_qp_auto(struct ib_qp *qp, u8 port); | ||||
| int rdma_counter_unbind_qp(struct ib_qp *qp, bool force); | ||||
| 
 | ||||
| int rdma_counter_query_stats(struct rdma_counter *counter); | ||||
| 
 | ||||
| #endif /* _RDMA_COUNTER_H_ */ | ||||
|  |  | |||
|  | @ -283,6 +283,8 @@ enum rdma_nldev_command { | |||
| 
 | ||||
| 	RDMA_NLDEV_CMD_STAT_SET, | ||||
| 
 | ||||
| 	RDMA_NLDEV_CMD_STAT_GET, /* can dump */ | ||||
| 
 | ||||
| 	RDMA_NLDEV_NUM_OPS | ||||
| }; | ||||
| 
 | ||||
|  | @ -490,13 +492,6 @@ enum rdma_nldev_attr { | |||
| 	 * File descriptor handle of the net namespace object | ||||
| 	 */ | ||||
| 	RDMA_NLDEV_NET_NS_FD,			/* u32 */ | ||||
| 	/*
 | ||||
| 	 * Counter-specific attributes. | ||||
| 	 */ | ||||
| 	RDMA_NLDEV_ATTR_STAT_MODE,		/* u32 */ | ||||
| 	RDMA_NLDEV_ATTR_STAT_RES,		/* u32 */ | ||||
| 	RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK,	/* u32 */ | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Information about a chardev. | ||||
| 	 * CHARDEV_TYPE is the name of the chardev ABI (ie uverbs, umad, etc) | ||||
|  | @ -509,6 +504,19 @@ enum rdma_nldev_attr { | |||
| 	RDMA_NLDEV_ATTR_CHARDEV_ABI,		/* u64 */ | ||||
| 	RDMA_NLDEV_ATTR_CHARDEV,		/* u64 */ | ||||
| 	RDMA_NLDEV_ATTR_UVERBS_DRIVER_ID,       /* u64 */ | ||||
| 	/*
 | ||||
| 	 * Counter-specific attributes. | ||||
| 	 */ | ||||
| 	RDMA_NLDEV_ATTR_STAT_MODE,		/* u32 */ | ||||
| 	RDMA_NLDEV_ATTR_STAT_RES,		/* u32 */ | ||||
| 	RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK,	/* u32 */ | ||||
| 	RDMA_NLDEV_ATTR_STAT_COUNTER,		/* nested table */ | ||||
| 	RDMA_NLDEV_ATTR_STAT_COUNTER_ENTRY,	/* nested table */ | ||||
| 	RDMA_NLDEV_ATTR_STAT_COUNTER_ID,	/* u32 */ | ||||
| 	RDMA_NLDEV_ATTR_STAT_HWCOUNTERS,	/* nested table */ | ||||
| 	RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY,	/* nested table */ | ||||
| 	RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME,	/* string */ | ||||
| 	RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE,	/* u64 */ | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Always the end | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Mark Zhang
						Mark Zhang