net: devlink: Add support for port regions
Allow regions to be registered to a devlink port. The same netlink API is used, but the port index is provided to indicate when a region is a port region as opposed to a device region. Reviewed-by: Vladimir Oltean <olteanv@gmail.com> Tested-by: Vladimir Oltean <olteanv@gmail.com> Signed-off-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									3122433eb5
								
							
						
					
					
						commit
						544e7c33ec
					
				| @ -110,6 +110,7 @@ struct devlink_port_attrs { | ||||
| struct devlink_port { | ||||
| 	struct list_head list; | ||||
| 	struct list_head param_list; | ||||
| 	struct list_head region_list; | ||||
| 	struct devlink *devlink; | ||||
| 	unsigned int index; | ||||
| 	bool registered; | ||||
| @ -591,6 +592,26 @@ struct devlink_region_ops { | ||||
| 	void *priv; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct devlink_port_region_ops - Region operations for a port | ||||
|  * @name: region name | ||||
|  * @destructor: callback used to free snapshot memory when deleting | ||||
|  * @snapshot: callback to request an immediate snapshot. On success, | ||||
|  *            the data variable must be updated to point to the snapshot data. | ||||
|  *            The function will be called while the devlink instance lock is | ||||
|  *            held. | ||||
|  * @priv: Pointer to driver private data for the region operation | ||||
|  */ | ||||
| struct devlink_port_region_ops { | ||||
| 	const char *name; | ||||
| 	void (*destructor)(const void *data); | ||||
| 	int (*snapshot)(struct devlink_port *port, | ||||
| 			const struct devlink_port_region_ops *ops, | ||||
| 			struct netlink_ext_ack *extack, | ||||
| 			u8 **data); | ||||
| 	void *priv; | ||||
| }; | ||||
| 
 | ||||
| struct devlink_fmsg; | ||||
| struct devlink_health_reporter; | ||||
| 
 | ||||
| @ -1445,7 +1466,13 @@ struct devlink_region * | ||||
| devlink_region_create(struct devlink *devlink, | ||||
| 		      const struct devlink_region_ops *ops, | ||||
| 		      u32 region_max_snapshots, u64 region_size); | ||||
| struct devlink_region * | ||||
| devlink_port_region_create(struct devlink_port *port, | ||||
| 			   const struct devlink_port_region_ops *ops, | ||||
| 			   u32 region_max_snapshots, u64 region_size); | ||||
| void devlink_region_destroy(struct devlink_region *region); | ||||
| void devlink_port_region_destroy(struct devlink_region *region); | ||||
| 
 | ||||
| int devlink_region_snapshot_id_get(struct devlink *devlink, u32 *id); | ||||
| void devlink_region_snapshot_id_put(struct devlink *devlink, u32 id); | ||||
| int devlink_region_snapshot_create(struct devlink_region *region, | ||||
|  | ||||
| @ -347,8 +347,12 @@ devlink_sb_tc_index_get_from_info(struct devlink_sb *devlink_sb, | ||||
| 
 | ||||
| struct devlink_region { | ||||
| 	struct devlink *devlink; | ||||
| 	struct devlink_port *port; | ||||
| 	struct list_head list; | ||||
| 	const struct devlink_region_ops *ops; | ||||
| 	union { | ||||
| 		const struct devlink_region_ops *ops; | ||||
| 		const struct devlink_port_region_ops *port_ops; | ||||
| 	}; | ||||
| 	struct list_head snapshot_list; | ||||
| 	u32 max_snapshots; | ||||
| 	u32 cur_snapshots; | ||||
| @ -374,6 +378,19 @@ devlink_region_get_by_name(struct devlink *devlink, const char *region_name) | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static struct devlink_region * | ||||
| devlink_port_region_get_by_name(struct devlink_port *port, | ||||
| 				const char *region_name) | ||||
| { | ||||
| 	struct devlink_region *region; | ||||
| 
 | ||||
| 	list_for_each_entry(region, &port->region_list, list) | ||||
| 		if (!strcmp(region->ops->name, region_name)) | ||||
| 			return region; | ||||
| 
 | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| static struct devlink_snapshot * | ||||
| devlink_region_snapshot_get_by_id(struct devlink_region *region, u32 id) | ||||
| { | ||||
| @ -3926,6 +3943,11 @@ static int devlink_nl_region_fill(struct sk_buff *msg, struct devlink *devlink, | ||||
| 	if (err) | ||||
| 		goto nla_put_failure; | ||||
| 
 | ||||
| 	if (region->port) | ||||
| 		if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, | ||||
| 				region->port->index)) | ||||
| 			goto nla_put_failure; | ||||
| 
 | ||||
| 	err = nla_put_string(msg, DEVLINK_ATTR_REGION_NAME, region->ops->name); | ||||
| 	if (err) | ||||
| 		goto nla_put_failure; | ||||
| @ -3973,6 +3995,11 @@ devlink_nl_region_notify_build(struct devlink_region *region, | ||||
| 	if (err) | ||||
| 		goto out_cancel_msg; | ||||
| 
 | ||||
| 	if (region->port) | ||||
| 		if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, | ||||
| 				region->port->index)) | ||||
| 			goto out_cancel_msg; | ||||
| 
 | ||||
| 	err = nla_put_string(msg, DEVLINK_ATTR_REGION_NAME, | ||||
| 			     region->ops->name); | ||||
| 	if (err) | ||||
| @ -4219,16 +4246,30 @@ static int devlink_nl_cmd_region_get_doit(struct sk_buff *skb, | ||||
| 					  struct genl_info *info) | ||||
| { | ||||
| 	struct devlink *devlink = info->user_ptr[0]; | ||||
| 	struct devlink_port *port = NULL; | ||||
| 	struct devlink_region *region; | ||||
| 	const char *region_name; | ||||
| 	struct sk_buff *msg; | ||||
| 	unsigned int index; | ||||
| 	int err; | ||||
| 
 | ||||
| 	if (!info->attrs[DEVLINK_ATTR_REGION_NAME]) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) { | ||||
| 		index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]); | ||||
| 
 | ||||
| 		port = devlink_port_get_by_index(devlink, index); | ||||
| 		if (!port) | ||||
| 			return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	region_name = nla_data(info->attrs[DEVLINK_ATTR_REGION_NAME]); | ||||
| 	region = devlink_region_get_by_name(devlink, region_name); | ||||
| 	if (port) | ||||
| 		region = devlink_port_region_get_by_name(port, region_name); | ||||
| 	else | ||||
| 		region = devlink_region_get_by_name(devlink, region_name); | ||||
| 
 | ||||
| 	if (!region) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| @ -4247,10 +4288,75 @@ static int devlink_nl_cmd_region_get_doit(struct sk_buff *skb, | ||||
| 	return genlmsg_reply(msg, info); | ||||
| } | ||||
| 
 | ||||
| static int devlink_nl_cmd_region_get_port_dumpit(struct sk_buff *msg, | ||||
| 						 struct netlink_callback *cb, | ||||
| 						 struct devlink_port *port, | ||||
| 						 int *idx, | ||||
| 						 int start) | ||||
| { | ||||
| 	struct devlink_region *region; | ||||
| 	int err = 0; | ||||
| 
 | ||||
| 	list_for_each_entry(region, &port->region_list, list) { | ||||
| 		if (*idx < start) { | ||||
| 			(*idx)++; | ||||
| 			continue; | ||||
| 		} | ||||
| 		err = devlink_nl_region_fill(msg, port->devlink, | ||||
| 					     DEVLINK_CMD_REGION_GET, | ||||
| 					     NETLINK_CB(cb->skb).portid, | ||||
| 					     cb->nlh->nlmsg_seq, | ||||
| 					     NLM_F_MULTI, region); | ||||
| 		if (err) | ||||
| 			goto out; | ||||
| 		(*idx)++; | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int devlink_nl_cmd_region_get_devlink_dumpit(struct sk_buff *msg, | ||||
| 						    struct netlink_callback *cb, | ||||
| 						    struct devlink *devlink, | ||||
| 						    int *idx, | ||||
| 						    int start) | ||||
| { | ||||
| 	struct devlink_region *region; | ||||
| 	struct devlink_port *port; | ||||
| 	int err = 0; | ||||
| 
 | ||||
| 	mutex_lock(&devlink->lock); | ||||
| 	list_for_each_entry(region, &devlink->region_list, list) { | ||||
| 		if (*idx < start) { | ||||
| 			(*idx)++; | ||||
| 			continue; | ||||
| 		} | ||||
| 		err = devlink_nl_region_fill(msg, devlink, | ||||
| 					     DEVLINK_CMD_REGION_GET, | ||||
| 					     NETLINK_CB(cb->skb).portid, | ||||
| 					     cb->nlh->nlmsg_seq, | ||||
| 					     NLM_F_MULTI, region); | ||||
| 		if (err) | ||||
| 			goto out; | ||||
| 		(*idx)++; | ||||
| 	} | ||||
| 
 | ||||
| 	list_for_each_entry(port, &devlink->port_list, list) { | ||||
| 		err = devlink_nl_cmd_region_get_port_dumpit(msg, cb, port, idx, | ||||
| 							    start); | ||||
| 		if (err) | ||||
| 			goto out; | ||||
| 	} | ||||
| 
 | ||||
| out: | ||||
| 	mutex_unlock(&devlink->lock); | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static int devlink_nl_cmd_region_get_dumpit(struct sk_buff *msg, | ||||
| 					    struct netlink_callback *cb) | ||||
| { | ||||
| 	struct devlink_region *region; | ||||
| 	struct devlink *devlink; | ||||
| 	int start = cb->args[0]; | ||||
| 	int idx = 0; | ||||
| @ -4260,25 +4366,10 @@ static int devlink_nl_cmd_region_get_dumpit(struct sk_buff *msg, | ||||
| 	list_for_each_entry(devlink, &devlink_list, list) { | ||||
| 		if (!net_eq(devlink_net(devlink), sock_net(msg->sk))) | ||||
| 			continue; | ||||
| 
 | ||||
| 		mutex_lock(&devlink->lock); | ||||
| 		list_for_each_entry(region, &devlink->region_list, list) { | ||||
| 			if (idx < start) { | ||||
| 				idx++; | ||||
| 				continue; | ||||
| 			} | ||||
| 			err = devlink_nl_region_fill(msg, devlink, | ||||
| 						     DEVLINK_CMD_REGION_GET, | ||||
| 						     NETLINK_CB(cb->skb).portid, | ||||
| 						     cb->nlh->nlmsg_seq, | ||||
| 						     NLM_F_MULTI, region); | ||||
| 			if (err) { | ||||
| 				mutex_unlock(&devlink->lock); | ||||
| 				goto out; | ||||
| 			} | ||||
| 			idx++; | ||||
| 		} | ||||
| 		mutex_unlock(&devlink->lock); | ||||
| 		err = devlink_nl_cmd_region_get_devlink_dumpit(msg, cb, devlink, | ||||
| 							       &idx, start); | ||||
| 		if (err) | ||||
| 			goto out; | ||||
| 	} | ||||
| out: | ||||
| 	mutex_unlock(&devlink_mutex); | ||||
| @ -4291,8 +4382,10 @@ static int devlink_nl_cmd_region_del(struct sk_buff *skb, | ||||
| { | ||||
| 	struct devlink *devlink = info->user_ptr[0]; | ||||
| 	struct devlink_snapshot *snapshot; | ||||
| 	struct devlink_port *port = NULL; | ||||
| 	struct devlink_region *region; | ||||
| 	const char *region_name; | ||||
| 	unsigned int index; | ||||
| 	u32 snapshot_id; | ||||
| 
 | ||||
| 	if (!info->attrs[DEVLINK_ATTR_REGION_NAME] || | ||||
| @ -4302,7 +4395,19 @@ static int devlink_nl_cmd_region_del(struct sk_buff *skb, | ||||
| 	region_name = nla_data(info->attrs[DEVLINK_ATTR_REGION_NAME]); | ||||
| 	snapshot_id = nla_get_u32(info->attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]); | ||||
| 
 | ||||
| 	region = devlink_region_get_by_name(devlink, region_name); | ||||
| 	if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) { | ||||
| 		index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]); | ||||
| 
 | ||||
| 		port = devlink_port_get_by_index(devlink, index); | ||||
| 		if (!port) | ||||
| 			return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	if (port) | ||||
| 		region = devlink_port_region_get_by_name(port, region_name); | ||||
| 	else | ||||
| 		region = devlink_region_get_by_name(devlink, region_name); | ||||
| 
 | ||||
| 	if (!region) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| @ -4319,9 +4424,11 @@ devlink_nl_cmd_region_new(struct sk_buff *skb, struct genl_info *info) | ||||
| { | ||||
| 	struct devlink *devlink = info->user_ptr[0]; | ||||
| 	struct devlink_snapshot *snapshot; | ||||
| 	struct devlink_port *port = NULL; | ||||
| 	struct nlattr *snapshot_id_attr; | ||||
| 	struct devlink_region *region; | ||||
| 	const char *region_name; | ||||
| 	unsigned int index; | ||||
| 	u32 snapshot_id; | ||||
| 	u8 *data; | ||||
| 	int err; | ||||
| @ -4332,7 +4439,20 @@ devlink_nl_cmd_region_new(struct sk_buff *skb, struct genl_info *info) | ||||
| 	} | ||||
| 
 | ||||
| 	region_name = nla_data(info->attrs[DEVLINK_ATTR_REGION_NAME]); | ||||
| 	region = devlink_region_get_by_name(devlink, region_name); | ||||
| 
 | ||||
| 	if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) { | ||||
| 		index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]); | ||||
| 
 | ||||
| 		port = devlink_port_get_by_index(devlink, index); | ||||
| 		if (!port) | ||||
| 			return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	if (port) | ||||
| 		region = devlink_port_region_get_by_name(port, region_name); | ||||
| 	else | ||||
| 		region = devlink_region_get_by_name(devlink, region_name); | ||||
| 
 | ||||
| 	if (!region) { | ||||
| 		NL_SET_ERR_MSG_MOD(info->extack, "The requested region does not exist"); | ||||
| 		return -EINVAL; | ||||
| @ -4368,7 +4488,12 @@ devlink_nl_cmd_region_new(struct sk_buff *skb, struct genl_info *info) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	err = region->ops->snapshot(devlink, region->ops, info->extack, &data); | ||||
| 	if (port) | ||||
| 		err = region->port_ops->snapshot(port, region->port_ops, | ||||
| 						 info->extack, &data); | ||||
| 	else | ||||
| 		err = region->ops->snapshot(devlink, region->ops, | ||||
| 					    info->extack, &data); | ||||
| 	if (err) | ||||
| 		goto err_snapshot_capture; | ||||
| 
 | ||||
| @ -4490,10 +4615,12 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, | ||||
| 	const struct genl_dumpit_info *info = genl_dumpit_info(cb); | ||||
| 	u64 ret_offset, start_offset, end_offset = U64_MAX; | ||||
| 	struct nlattr **attrs = info->attrs; | ||||
| 	struct devlink_port *port = NULL; | ||||
| 	struct devlink_region *region; | ||||
| 	struct nlattr *chunks_attr; | ||||
| 	const char *region_name; | ||||
| 	struct devlink *devlink; | ||||
| 	unsigned int index; | ||||
| 	void *hdr; | ||||
| 	int err; | ||||
| 
 | ||||
| @ -4514,8 +4641,21 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, | ||||
| 		goto out_unlock; | ||||
| 	} | ||||
| 
 | ||||
| 	if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) { | ||||
| 		index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]); | ||||
| 
 | ||||
| 		port = devlink_port_get_by_index(devlink, index); | ||||
| 		if (!port) | ||||
| 			return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	region_name = nla_data(attrs[DEVLINK_ATTR_REGION_NAME]); | ||||
| 	region = devlink_region_get_by_name(devlink, region_name); | ||||
| 
 | ||||
| 	if (port) | ||||
| 		region = devlink_port_region_get_by_name(port, region_name); | ||||
| 	else | ||||
| 		region = devlink_region_get_by_name(devlink, region_name); | ||||
| 
 | ||||
| 	if (!region) { | ||||
| 		err = -EINVAL; | ||||
| 		goto out_unlock; | ||||
| @ -4552,6 +4692,11 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb, | ||||
| 	if (err) | ||||
| 		goto nla_put_failure; | ||||
| 
 | ||||
| 	if (region->port) | ||||
| 		if (nla_put_u32(skb, DEVLINK_ATTR_PORT_INDEX, | ||||
| 				region->port->index)) | ||||
| 			goto nla_put_failure; | ||||
| 
 | ||||
| 	err = nla_put_string(skb, DEVLINK_ATTR_REGION_NAME, region_name); | ||||
| 	if (err) | ||||
| 		goto nla_put_failure; | ||||
| @ -7666,6 +7811,7 @@ int devlink_port_register(struct devlink *devlink, | ||||
| 	mutex_init(&devlink_port->reporters_lock); | ||||
| 	list_add_tail(&devlink_port->list, &devlink->port_list); | ||||
| 	INIT_LIST_HEAD(&devlink_port->param_list); | ||||
| 	INIT_LIST_HEAD(&devlink_port->region_list); | ||||
| 	mutex_unlock(&devlink->lock); | ||||
| 	INIT_DELAYED_WORK(&devlink_port->type_warn_dw, &devlink_port_type_warn); | ||||
| 	devlink_port_type_warn_schedule(devlink_port); | ||||
| @ -7689,6 +7835,7 @@ void devlink_port_unregister(struct devlink_port *devlink_port) | ||||
| 	list_del(&devlink_port->list); | ||||
| 	mutex_unlock(&devlink->lock); | ||||
| 	WARN_ON(!list_empty(&devlink_port->reporter_list)); | ||||
| 	WARN_ON(!list_empty(&devlink_port->region_list)); | ||||
| 	mutex_destroy(&devlink_port->reporters_lock); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(devlink_port_unregister); | ||||
| @ -8768,6 +8915,57 @@ unlock: | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(devlink_region_create); | ||||
| 
 | ||||
| /**
 | ||||
|  *	devlink_port_region_create - create a new address region for a port | ||||
|  * | ||||
|  *	@port: devlink port | ||||
|  *	@ops: region operations and name | ||||
|  *	@region_max_snapshots: Maximum supported number of snapshots for region | ||||
|  *	@region_size: size of region | ||||
|  */ | ||||
| struct devlink_region * | ||||
| devlink_port_region_create(struct devlink_port *port, | ||||
| 			   const struct devlink_port_region_ops *ops, | ||||
| 			   u32 region_max_snapshots, u64 region_size) | ||||
| { | ||||
| 	struct devlink *devlink = port->devlink; | ||||
| 	struct devlink_region *region; | ||||
| 	int err = 0; | ||||
| 
 | ||||
| 	if (WARN_ON(!ops) || WARN_ON(!ops->destructor)) | ||||
| 		return ERR_PTR(-EINVAL); | ||||
| 
 | ||||
| 	mutex_lock(&devlink->lock); | ||||
| 
 | ||||
| 	if (devlink_port_region_get_by_name(port, ops->name)) { | ||||
| 		err = -EEXIST; | ||||
| 		goto unlock; | ||||
| 	} | ||||
| 
 | ||||
| 	region = kzalloc(sizeof(*region), GFP_KERNEL); | ||||
| 	if (!region) { | ||||
| 		err = -ENOMEM; | ||||
| 		goto unlock; | ||||
| 	} | ||||
| 
 | ||||
| 	region->devlink = devlink; | ||||
| 	region->port = port; | ||||
| 	region->max_snapshots = region_max_snapshots; | ||||
| 	region->port_ops = ops; | ||||
| 	region->size = region_size; | ||||
| 	INIT_LIST_HEAD(®ion->snapshot_list); | ||||
| 	list_add_tail(®ion->list, &port->region_list); | ||||
| 	devlink_nl_region_notify(region, NULL, DEVLINK_CMD_REGION_NEW); | ||||
| 
 | ||||
| 	mutex_unlock(&devlink->lock); | ||||
| 	return region; | ||||
| 
 | ||||
| unlock: | ||||
| 	mutex_unlock(&devlink->lock); | ||||
| 	return ERR_PTR(err); | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(devlink_port_region_create); | ||||
| 
 | ||||
| /**
 | ||||
|  *	devlink_region_destroy - destroy address region | ||||
|  * | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user