1258 lines · 31.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Copyright (c) 2016 Mellanox Technologies. All rights reserved. * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> */ #include "devl_internal.h" struct devlink_region {	struct devlink *devlink;	struct devlink_port *port;	struct list_head list;	union {		const struct devlink_region_ops *ops;		const struct devlink_port_region_ops *port_ops;	};	struct mutex snapshot_lock; /* protects snapshot_list,				     * max_snapshots and cur_snapshots				     * consistency.				     */	struct list_head snapshot_list;	u32 max_snapshots;	u32 cur_snapshots;	u64 size;}; struct devlink_snapshot {	struct list_head list;	struct devlink_region *region;	u8 *data;	u32 id;}; static struct devlink_region *devlink_region_get_by_name(struct devlink *devlink, const char *region_name){	struct devlink_region *region; 	list_for_each_entry(region, &devlink->region_list, list)		if (!strcmp(region->ops->name, region_name))			return region; 	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){	struct devlink_snapshot *snapshot; 	list_for_each_entry(snapshot, &region->snapshot_list, list)		if (snapshot->id == id)			return snapshot; 	return NULL;} static int devlink_nl_region_snapshot_id_put(struct sk_buff *msg,					     struct devlink *devlink,					     struct devlink_snapshot *snapshot){	struct nlattr *snap_attr;	int err; 	snap_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_REGION_SNAPSHOT);	if (!snap_attr)		return -EMSGSIZE; 	err = nla_put_u32(msg, DEVLINK_ATTR_REGION_SNAPSHOT_ID, snapshot->id);	if (err)		goto nla_put_failure; 	nla_nest_end(msg, snap_attr);	return 0; nla_put_failure:	nla_nest_cancel(msg, snap_attr);	return err;} static int devlink_nl_region_snapshots_id_put(struct sk_buff *msg,					      struct devlink *devlink,					      struct devlink_region *region){	struct devlink_snapshot *snapshot;	struct nlattr *snapshots_attr;	int err; 	snapshots_attr = nla_nest_start_noflag(msg,					       DEVLINK_ATTR_REGION_SNAPSHOTS);	if (!snapshots_attr)		return -EMSGSIZE; 	list_for_each_entry(snapshot, &region->snapshot_list, list) {		err = devlink_nl_region_snapshot_id_put(msg, devlink, snapshot);		if (err)			goto nla_put_failure;	} 	nla_nest_end(msg, snapshots_attr);	return 0; nla_put_failure:	nla_nest_cancel(msg, snapshots_attr);	return err;} static int devlink_nl_region_fill(struct sk_buff *msg, struct devlink *devlink,				  enum devlink_command cmd, u32 portid,				  u32 seq, int flags,				  struct devlink_region *region){	void *hdr;	int err; 	hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);	if (!hdr)		return -EMSGSIZE; 	err = devlink_nl_put_handle(msg, devlink);	if (err)		goto nla_put_failure; 	if (region->port) {		err = nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX,				  region->port->index);		if (err)			goto nla_put_failure;	} 	err = nla_put_string(msg, DEVLINK_ATTR_REGION_NAME, region->ops->name);	if (err)		goto nla_put_failure; 	err = devlink_nl_put_u64(msg, DEVLINK_ATTR_REGION_SIZE, region->size);	if (err)		goto nla_put_failure; 	err = nla_put_u32(msg, DEVLINK_ATTR_REGION_MAX_SNAPSHOTS,			  region->max_snapshots);	if (err)		goto nla_put_failure; 	err = devlink_nl_region_snapshots_id_put(msg, devlink, region);	if (err)		goto nla_put_failure; 	genlmsg_end(msg, hdr);	return 0; nla_put_failure:	genlmsg_cancel(msg, hdr);	return err;} static struct sk_buff *devlink_nl_region_notify_build(struct devlink_region *region,			       struct devlink_snapshot *snapshot,			       enum devlink_command cmd, u32 portid, u32 seq){	struct devlink *devlink = region->devlink;	struct sk_buff *msg;	void *hdr;	int err; 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);	if (!msg)		return ERR_PTR(-ENOMEM); 	hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, 0, cmd);	if (!hdr) {		err = -EMSGSIZE;		goto out_free_msg;	} 	err = devlink_nl_put_handle(msg, devlink);	if (err)		goto out_cancel_msg; 	if (region->port) {		err = nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX,				  region->port->index);		if (err)			goto out_cancel_msg;	} 	err = nla_put_string(msg, DEVLINK_ATTR_REGION_NAME,			     region->ops->name);	if (err)		goto out_cancel_msg; 	if (snapshot) {		err = nla_put_u32(msg, DEVLINK_ATTR_REGION_SNAPSHOT_ID,				  snapshot->id);		if (err)			goto out_cancel_msg;	} else {		err = devlink_nl_put_u64(msg, DEVLINK_ATTR_REGION_SIZE,					 region->size);		if (err)			goto out_cancel_msg;	}	genlmsg_end(msg, hdr); 	return msg; out_cancel_msg:	genlmsg_cancel(msg, hdr);out_free_msg:	nlmsg_free(msg);	return ERR_PTR(err);} static void devlink_nl_region_notify(struct devlink_region *region,				     struct devlink_snapshot *snapshot,				     enum devlink_command cmd){	struct devlink *devlink = region->devlink;	struct sk_buff *msg; 	WARN_ON(cmd != DEVLINK_CMD_REGION_NEW && cmd != DEVLINK_CMD_REGION_DEL); 	if (!__devl_is_registered(devlink) || !devlink_nl_notify_need(devlink))		return; 	msg = devlink_nl_region_notify_build(region, snapshot, cmd, 0, 0);	if (IS_ERR(msg))		return; 	devlink_nl_notify_send(devlink, msg);} void devlink_regions_notify_register(struct devlink *devlink){	struct devlink_region *region; 	list_for_each_entry(region, &devlink->region_list, list)		devlink_nl_region_notify(region, NULL, DEVLINK_CMD_REGION_NEW);} void devlink_regions_notify_unregister(struct devlink *devlink){	struct devlink_region *region; 	list_for_each_entry_reverse(region, &devlink->region_list, list)		devlink_nl_region_notify(region, NULL, DEVLINK_CMD_REGION_DEL);} /** * __devlink_snapshot_id_increment - Increment number of snapshots using an id *	@devlink: devlink instance *	@id: the snapshot id * *	Track when a new snapshot begins using an id. Load the count for the *	given id from the snapshot xarray, increment it, and store it back. * *	Called when a new snapshot is created with the given id. * *	The id *must* have been previously allocated by *	devlink_region_snapshot_id_get(). * *	Returns 0 on success, or an error on failure. */static int __devlink_snapshot_id_increment(struct devlink *devlink, u32 id){	unsigned long count;	void *p;	int err; 	xa_lock(&devlink->snapshot_ids);	p = xa_load(&devlink->snapshot_ids, id);	if (WARN_ON(!p)) {		err = -EINVAL;		goto unlock;	} 	if (WARN_ON(!xa_is_value(p))) {		err = -EINVAL;		goto unlock;	} 	count = xa_to_value(p);	count++; 	err = xa_err(__xa_store(&devlink->snapshot_ids, id, xa_mk_value(count),				GFP_ATOMIC));unlock:	xa_unlock(&devlink->snapshot_ids);	return err;} /** * __devlink_snapshot_id_decrement - Decrease number of snapshots using an id *	@devlink: devlink instance *	@id: the snapshot id * *	Track when a snapshot is deleted and stops using an id. Load the count *	for the given id from the snapshot xarray, decrement it, and store it *	back. * *	If the count reaches zero, erase this id from the xarray, freeing it *	up for future re-use by devlink_region_snapshot_id_get(). * *	Called when a snapshot using the given id is deleted, and when the *	initial allocator of the id is finished using it. */static void __devlink_snapshot_id_decrement(struct devlink *devlink, u32 id){	unsigned long count;	void *p; 	xa_lock(&devlink->snapshot_ids);	p = xa_load(&devlink->snapshot_ids, id);	if (WARN_ON(!p))		goto unlock; 	if (WARN_ON(!xa_is_value(p)))		goto unlock; 	count = xa_to_value(p); 	if (count > 1) {		count--;		__xa_store(&devlink->snapshot_ids, id, xa_mk_value(count),			   GFP_ATOMIC);	} else {		/* If this was the last user, we can erase this id */		__xa_erase(&devlink->snapshot_ids, id);	}unlock:	xa_unlock(&devlink->snapshot_ids);} /** *	__devlink_snapshot_id_insert - Insert a specific snapshot ID *	@devlink: devlink instance *	@id: the snapshot id * *	Mark the given snapshot id as used by inserting a zero value into the *	snapshot xarray. * *	This must be called while holding the devlink instance lock. Unlike *	devlink_snapshot_id_get, the initial reference count is zero, not one. *	It is expected that the id will immediately be used before *	releasing the devlink instance lock. * *	Returns zero on success, or an error code if the snapshot id could not *	be inserted. */static int __devlink_snapshot_id_insert(struct devlink *devlink, u32 id){	int err; 	xa_lock(&devlink->snapshot_ids);	if (xa_load(&devlink->snapshot_ids, id)) {		xa_unlock(&devlink->snapshot_ids);		return -EEXIST;	}	err = xa_err(__xa_store(&devlink->snapshot_ids, id, xa_mk_value(0),				GFP_ATOMIC));	xa_unlock(&devlink->snapshot_ids);	return err;} /** *	__devlink_region_snapshot_id_get - get snapshot ID *	@devlink: devlink instance *	@id: storage to return snapshot id * *	Allocates a new snapshot id. Returns zero on success, or a negative *	error on failure. Must be called while holding the devlink instance *	lock. * *	Snapshot IDs are tracked using an xarray which stores the number of *	users of the snapshot id. * *	Note that the caller of this function counts as a 'user', in order to *	avoid race conditions. The caller must release its hold on the *	snapshot by using devlink_region_snapshot_id_put. */static int __devlink_region_snapshot_id_get(struct devlink *devlink, u32 *id){	return xa_alloc(&devlink->snapshot_ids, id, xa_mk_value(1),			xa_limit_32b, GFP_KERNEL);} /** *	__devlink_region_snapshot_create - create a new snapshot *	This will add a new snapshot of a region. The snapshot *	will be stored on the region struct and can be accessed *	from devlink. This is useful for future analyses of snapshots. *	Multiple snapshots can be created on a region. *	The @snapshot_id should be obtained using the getter function. * *	Must be called only while holding the region snapshot lock. * *	@region: devlink region of the snapshot *	@data: snapshot data *	@snapshot_id: snapshot id to be created */static int__devlink_region_snapshot_create(struct devlink_region *region,				 u8 *data, u32 snapshot_id){	struct devlink *devlink = region->devlink;	struct devlink_snapshot *snapshot;	int err; 	lockdep_assert_held(&region->snapshot_lock); 	/* check if region can hold one more snapshot */	if (region->cur_snapshots == region->max_snapshots)		return -ENOSPC; 	if (devlink_region_snapshot_get_by_id(region, snapshot_id))		return -EEXIST; 	snapshot = kzalloc(sizeof(*snapshot), GFP_KERNEL);	if (!snapshot)		return -ENOMEM; 	err = __devlink_snapshot_id_increment(devlink, snapshot_id);	if (err)		goto err_snapshot_id_increment; 	snapshot->id = snapshot_id;	snapshot->region = region;	snapshot->data = data; 	list_add_tail(&snapshot->list, &region->snapshot_list); 	region->cur_snapshots++; 	devlink_nl_region_notify(region, snapshot, DEVLINK_CMD_REGION_NEW);	return 0; err_snapshot_id_increment:	kfree(snapshot);	return err;} static void devlink_region_snapshot_del(struct devlink_region *region,					struct devlink_snapshot *snapshot){	struct devlink *devlink = region->devlink; 	lockdep_assert_held(&region->snapshot_lock); 	devlink_nl_region_notify(region, snapshot, DEVLINK_CMD_REGION_DEL);	region->cur_snapshots--;	list_del(&snapshot->list);	region->ops->destructor(snapshot->data);	__devlink_snapshot_id_decrement(devlink, snapshot->id);	kfree(snapshot);} int devlink_nl_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 (GENL_REQ_ATTR_CHECK(info, 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]);	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; 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);

Lines 1–500 of 1258.