/** * __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.
*/ staticint __devlink_snapshot_id_increment(struct devlink *devlink, u32 id)
{ unsignedlong 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;
}
/** * __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.
*/ staticvoid __devlink_snapshot_id_decrement(struct devlink *devlink, u32 id)
{ unsignedlong 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.
*/ staticint __devlink_snapshot_id_insert(struct devlink *devlink, u32 id)
{ int 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.
*/ staticint __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
*/ staticint
__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(®ion->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;
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); if (!msg) return -ENOMEM;
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(info->extack, "The requested region does not exist"); return -EINVAL;
}
if (!region->ops->snapshot) {
NL_SET_ERR_MSG(info->extack, "The requested region does not support taking an immediate snapshot"); return -EOPNOTSUPP;
}
mutex_lock(®ion->snapshot_lock);
if (region->cur_snapshots == region->max_snapshots) {
NL_SET_ERR_MSG(info->extack, "The region has reached the maximum number of stored snapshots");
err = -ENOSPC; goto unlock;
}
snapshot_id_attr = info->attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]; if (snapshot_id_attr) {
snapshot_id = nla_get_u32(snapshot_id_attr);
if (devlink_region_snapshot_get_by_id(region, snapshot_id)) {
NL_SET_ERR_MSG(info->extack, "The requested snapshot id is already in use");
err = -EEXIST; goto unlock;
}
err = __devlink_snapshot_id_insert(devlink, snapshot_id); if (err) goto unlock;
} else {
err = __devlink_region_snapshot_id_get(devlink, &snapshot_id); if (err) {
NL_SET_ERR_MSG(info->extack, "Failed to allocate a new snapshot id"); goto unlock;
}
}
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;
err = __devlink_region_snapshot_create(region, data, snapshot_id); if (err) goto err_snapshot_create;
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_ATTR(cb->extack, region_attr, "Requested region does not exist");
err = -EINVAL; goto out_unlock;
}
snapshot_attr = attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]; if (!snapshot_attr) { if (!nla_get_flag(attrs[DEVLINK_ATTR_REGION_DIRECT])) {
NL_SET_ERR_MSG(cb->extack, "No snapshot id provided");
err = -EINVAL; goto out_unlock;
}
if (!region->ops->read) {
NL_SET_ERR_MSG(cb->extack, "Requested region does not support direct read");
err = -EOPNOTSUPP; goto out_unlock;
}
if (nla_get_flag(attrs[DEVLINK_ATTR_REGION_DIRECT])) {
NL_SET_ERR_MSG_ATTR(cb->extack, snapshot_attr, "Direct region read does not use snapshot");
err = -EINVAL; goto out_unlock;
}
snapshot_id = nla_get_u32(snapshot_attr);
snapshot = devlink_region_snapshot_get_by_id(region, snapshot_id); if (!snapshot) {
NL_SET_ERR_MSG_ATTR(cb->extack, snapshot_attr, "Requested snapshot does not exist");
err = -EINVAL; goto out_unlock;
}
region_cb = &devlink_region_snapshot_fill;
region_cb_priv = snapshot;
}
if (attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR] &&
attrs[DEVLINK_ATTR_REGION_CHUNK_LEN]) { if (!start_offset)
start_offset =
nla_get_u64(attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR]);
/** * devl_region_create - create a new address region * * @devlink: devlink * @ops: region operations and name * @region_max_snapshots: Maximum supported number of snapshots for region * @region_size: size of region
*/ struct devlink_region *devl_region_create(struct devlink *devlink, conststruct devlink_region_ops *ops,
u32 region_max_snapshots,
u64 region_size)
{ struct devlink_region *region;
devl_assert_locked(devlink);
if (WARN_ON(!ops) || WARN_ON(!ops->destructor)) return ERR_PTR(-EINVAL);
if (devlink_region_get_by_name(devlink, ops->name)) return ERR_PTR(-EEXIST);
region = kzalloc(sizeof(*region), GFP_KERNEL); if (!region) return ERR_PTR(-ENOMEM);
/** * devlink_region_create - create a new address region * * @devlink: devlink * @ops: region operations and name * @region_max_snapshots: Maximum supported number of snapshots for region * @region_size: size of region * * Context: Takes and release devlink->lock <mutex>.
*/ struct devlink_region *
devlink_region_create(struct devlink *devlink, conststruct devlink_region_ops *ops,
u32 region_max_snapshots, u64 region_size)
{ struct devlink_region *region;
/** * 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 * * Context: Takes and release devlink->lock <mutex>.
*/ struct devlink_region *
devlink_port_region_create(struct devlink_port *port, conststruct devlink_port_region_ops *ops,
u32 region_max_snapshots, u64 region_size)
{ struct devlink *devlink = port->devlink; struct devlink_region *region; int err = 0;
ASSERT_DEVLINK_PORT_INITIALIZED(port);
if (WARN_ON(!ops) || WARN_ON(!ops->destructor)) return ERR_PTR(-EINVAL);
devl_lock(devlink);
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;
}
/** * devl_region_destroy - destroy address region * * @region: devlink region to destroy
*/ void devl_region_destroy(struct devlink_region *region)
{ struct devlink *devlink = region->devlink; struct devlink_snapshot *snapshot, *ts;
devl_assert_locked(devlink);
/* Free all snapshots of region */
mutex_lock(®ion->snapshot_lock);
list_for_each_entry_safe(snapshot, ts, ®ion->snapshot_list, list)
devlink_region_snapshot_del(region, snapshot);
mutex_unlock(®ion->snapshot_lock);
/** * devlink_region_snapshot_id_get - get snapshot ID * * This callback should be called when adding a new snapshot, * Driver should use the same id for multiple snapshots taken * on multiple regions at the same time/by the same trigger. * * The caller of this function must use devlink_region_snapshot_id_put * when finished creating regions using this id. * * Returns zero on success, or a negative error code on failure. * * @devlink: devlink * @id: storage to return id
*/ int devlink_region_snapshot_id_get(struct devlink *devlink, u32 *id)
{ return __devlink_region_snapshot_id_get(devlink, id);
}
EXPORT_SYMBOL_GPL(devlink_region_snapshot_id_get);
/** * devlink_region_snapshot_id_put - put snapshot ID reference * * This should be called by a driver after finishing creating snapshots * with an id. Doing so ensures that the ID can later be released in the * event that all snapshots using it have been destroyed. * * @devlink: devlink * @id: id to release reference on
*/ void devlink_region_snapshot_id_put(struct devlink *devlink, u32 id)
{
__devlink_snapshot_id_decrement(devlink, id);
}
EXPORT_SYMBOL_GPL(devlink_region_snapshot_id_put);
/** * 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. * * @region: devlink region of the snapshot * @data: snapshot data * @snapshot_id: snapshot id to be created
*/ int devlink_region_snapshot_create(struct devlink_region *region,
u8 *data, u32 snapshot_id)
{ int err;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.