Commit f8f59847 authored by David S. Miller's avatar David S. Miller

Merge branch 'implement-DEVLINK_CMD_REGION_NEW'

Jacob Keller says:

====================
implement DEVLINK_CMD_REGION_NEW

This series adds support for the DEVLINK_CMD_REGION_NEW operation, used to
enable userspace requesting a snapshot of a region on demand.

This can be useful to enable adding regions for a driver for which there is
no trigger to create snapshots. By making this a core part of devlink, there
is no need for the drivers to use a separate channel such as debugfs.

The primary intent for this kind of region is to expose device information
that might be useful for diagnostics and information gathering.

The first few patches refactor regions to support a new ops structure for
extending the available operations that regions can perform. This includes
converting the destructor into an op from a function argument.

Next, patches refactor the snapshot id allocation to use an xarray which
tracks the number of current snapshots using a given id. This is done so
that id lifetime can be determined, and ids can be released when no longer
in use.

Without this change, snapshot ids remain used forever, until the snapshot_id
count rolled over UINT_MAX.

Finally, code to enable the previously unused DEVLINK_CMD_REGION_NEW is
added. This code enforces that the snapshot id is always provided, unlike
previous revisions of this series.

Finally, a patch is added to enable using this new command via the .snapshot
callback in both netdevsim and the ice driver.

For the ice driver, a new "nvm-flash" region is added, which will enable
read access to the NVM flash contents. The intention for this is to allow
diagnostics tools to gather information about the device. By using a
snapshot and gathering the NVM contents all at once, the contents can be
atomic.

Links to previous discussions:
1st RFC - https://lore.kernel.org/netdev/20200130225913.1671982-1-jacob.e.keller@intel.com/
2nd RFC - https://lore.kernel.org/netdev/20200214232223.3442651-1-jacob.e.keller@intel.com/
v1 - https://lore.kernel.org/netdev/20200324223445.2077900-1-jacob.e.keller@intel.com/
v2 - https://lore.kernel.org/netdev/20200326035157.2211090-1-jacob.e.keller@intel.com/

Major changes since RFC:
* use an xarray for tracking snapshot ids, rather than an IDR
* remove support for auto-generated snapshot ids in DEVLINK_CMD_REGION_NEW

See each patch for an individual changelog per-patch
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 6739ce85 dce730f1
......@@ -20,6 +20,11 @@ address regions that are otherwise inaccessible to the user.
Regions may also be used to provide an additional way to debug complex error
states, but see also :doc:`devlink-health`
Regions may optionally support capturing a snapshot on demand via the
``DEVLINK_CMD_REGION_NEW`` netlink message. A driver wishing to allow
requested snapshots must implement the ``.snapshot`` callback for the region
in its ``devlink_region_ops`` structure.
example usage
-------------
......@@ -40,6 +45,9 @@ example usage
# Delete a snapshot using:
$ devlink region del pci/0000:00:05.0/cr-space snapshot 1
# Request an immediate snapshot, if supported by the region
$ devlink region new pci/0000:00:05.0/cr-space snapshot 5
# Dump a snapshot:
$ devlink region dump pci/0000:00:05.0/fw-health snapshot 1
0000000000000000 0014 95dc 0014 9514 0035 1670 0034 db30
......
......@@ -69,3 +69,29 @@ The ``ice`` driver reports the following versions
- The version of the DDP package that is active in the device. Note
that both the name (as reported by ``fw.app.name``) and version are
required to uniquely identify the package.
Regions
=======
The ``ice`` driver enables access to the contents of the Non Volatile Memory
flash chip via the ``nvm-flash`` region.
Users can request an immediate capture of a snapshot via the
``DEVLINK_CMD_REGION_NEW``
.. code:: shell
$ devlink region new pci/0000:01:00.0/nvm-flash snapshot 1
$ devlink region dump pci/0000:01:00.0/nvm-flash snapshot 1
$ devlink region dump pci/0000:01:00.0/nvm-flash snapshot 1
0000000000000000 0014 95dc 0014 9514 0035 1670 0034 db30
0000000000000010 0000 0000 ffff ff04 0029 8c00 0028 8cc8
0000000000000020 0016 0bb8 0016 1720 0000 0000 c00f 3ffc
0000000000000030 bada cce5 bada cce5 bada cce5 bada cce5
$ devlink region read pci/0000:01:00.0/nvm-flash snapshot 1 address 0
length 16
0000000000000000 0014 95dc 0014 9514 0035 1670 0034 db30
$ devlink region delete pci/0000:01:00.0/nvm-flash snapshot 1
......@@ -351,6 +351,8 @@ struct ice_pf {
/* devlink port data */
struct devlink_port devlink_port;
struct devlink_region *nvm_region;
/* OS reserved IRQ details */
struct msix_entry *msix_entries;
struct ice_res_tracker *irq_tracker;
......
......@@ -318,3 +318,99 @@ void ice_devlink_destroy_port(struct ice_pf *pf)
devlink_port_type_clear(&pf->devlink_port);
devlink_port_unregister(&pf->devlink_port);
}
/**
* ice_devlink_nvm_snapshot - Capture a snapshot of the Shadow RAM contents
* @devlink: the devlink instance
* @extack: extended ACK response structure
* @data: on exit points to snapshot data buffer
*
* This function is called in response to the DEVLINK_CMD_REGION_TRIGGER for
* the shadow-ram devlink region. It captures a snapshot of the shadow ram
* contents. This snapshot can later be viewed via the devlink-region
* interface.
*
* @returns zero on success, and updates the data pointer. Returns a non-zero
* error code on failure.
*/
static int ice_devlink_nvm_snapshot(struct devlink *devlink,
struct netlink_ext_ack *extack, u8 **data)
{
struct ice_pf *pf = devlink_priv(devlink);
struct device *dev = ice_pf_to_dev(pf);
struct ice_hw *hw = &pf->hw;
enum ice_status status;
void *nvm_data;
u32 nvm_size;
nvm_size = hw->nvm.flash_size;
nvm_data = vzalloc(nvm_size);
if (!nvm_data)
return -ENOMEM;
status = ice_acquire_nvm(hw, ICE_RES_READ);
if (status) {
dev_dbg(dev, "ice_acquire_nvm failed, err %d aq_err %d\n",
status, hw->adminq.sq_last_status);
NL_SET_ERR_MSG_MOD(extack, "Failed to acquire NVM semaphore");
vfree(nvm_data);
return -EIO;
}
status = ice_read_flat_nvm(hw, 0, &nvm_size, nvm_data, false);
if (status) {
dev_dbg(dev, "ice_read_flat_nvm failed after reading %u bytes, err %d aq_err %d\n",
nvm_size, status, hw->adminq.sq_last_status);
NL_SET_ERR_MSG_MOD(extack, "Failed to read NVM contents");
ice_release_nvm(hw);
vfree(nvm_data);
return -EIO;
}
ice_release_nvm(hw);
*data = nvm_data;
return 0;
}
static const struct devlink_region_ops ice_nvm_region_ops = {
.name = "nvm-flash",
.destructor = vfree,
.snapshot = ice_devlink_nvm_snapshot,
};
/**
* ice_devlink_init_regions - Initialize devlink regions
* @pf: the PF device structure
*
* Create devlink regions used to enable access to dump the contents of the
* flash memory on the device.
*/
void ice_devlink_init_regions(struct ice_pf *pf)
{
struct devlink *devlink = priv_to_devlink(pf);
struct device *dev = ice_pf_to_dev(pf);
u64 nvm_size;
nvm_size = pf->hw.nvm.flash_size;
pf->nvm_region = devlink_region_create(devlink, &ice_nvm_region_ops, 1,
nvm_size);
if (IS_ERR(pf->nvm_region)) {
dev_err(dev, "failed to create NVM devlink region, err %ld\n",
PTR_ERR(pf->nvm_region));
pf->nvm_region = NULL;
}
}
/**
* ice_devlink_destroy_regions - Destroy devlink regions
* @pf: the PF device structure
*
* Remove previously created regions for this PF.
*/
void ice_devlink_destroy_regions(struct ice_pf *pf)
{
if (pf->nvm_region)
devlink_region_destroy(pf->nvm_region);
}
......@@ -11,4 +11,7 @@ void ice_devlink_unregister(struct ice_pf *pf);
int ice_devlink_create_port(struct ice_pf *pf);
void ice_devlink_destroy_port(struct ice_pf *pf);
void ice_devlink_init_regions(struct ice_pf *pf);
void ice_devlink_destroy_regions(struct ice_pf *pf);
#endif /* _ICE_DEVLINK_H_ */
......@@ -3276,6 +3276,8 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
goto err_init_pf_unroll;
}
ice_devlink_init_regions(pf);
pf->num_alloc_vsi = hw->func_caps.guar_num_vsi;
if (!pf->num_alloc_vsi) {
err = -EIO;
......@@ -3385,6 +3387,7 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
devm_kfree(dev, pf->vsi);
err_init_pf_unroll:
ice_deinit_pf(pf);
ice_devlink_destroy_regions(pf);
ice_deinit_hw(hw);
err_exit_unroll:
ice_devlink_unregister(pf);
......@@ -3427,6 +3430,7 @@ static void ice_remove(struct pci_dev *pdev)
ice_vsi_free_q_vectors(pf->vsi[i]);
}
ice_deinit_pf(pf);
ice_devlink_destroy_regions(pf);
ice_deinit_hw(&pf->hw);
ice_devlink_unregister(pf);
......
......@@ -38,8 +38,18 @@
#define CR_ENABLE_BIT_OFFSET 0xF3F04
#define MAX_NUM_OF_DUMPS_TO_STORE (8)
static const char *region_cr_space_str = "cr-space";
static const char *region_fw_health_str = "fw-health";
static const char * const region_cr_space_str = "cr-space";
static const char * const region_fw_health_str = "fw-health";
static const struct devlink_region_ops region_cr_space_ops = {
.name = region_cr_space_str,
.destructor = &kvfree,
};
static const struct devlink_region_ops region_fw_health_ops = {
.name = region_fw_health_str,
.destructor = &kvfree,
};
/* Set to true in case cr enable bit was set to true before crdump */
static bool crdump_enbale_bit_set;
......@@ -99,7 +109,7 @@ static void mlx4_crdump_collect_crspace(struct mlx4_dev *dev,
readl(cr_space + offset);
err = devlink_region_snapshot_create(crdump->region_crspace,
crspace_data, id, &kvfree);
crspace_data, id);
if (err) {
kvfree(crspace_data);
mlx4_warn(dev, "crdump: devlink create %s snapshot id %d err %d\n",
......@@ -138,7 +148,7 @@ static void mlx4_crdump_collect_fw_health(struct mlx4_dev *dev,
readl(health_buf_start + offset);
err = devlink_region_snapshot_create(crdump->region_fw_health,
health_data, id, &kvfree);
health_data, id);
if (err) {
kvfree(health_data);
mlx4_warn(dev, "crdump: devlink create %s snapshot id %d err %d\n",
......@@ -159,6 +169,7 @@ int mlx4_crdump_collect(struct mlx4_dev *dev)
struct pci_dev *pdev = dev->persist->pdev;
unsigned long cr_res_size;
u8 __iomem *cr_space;
int err;
u32 id;
if (!dev->caps.health_buffer_addrs) {
......@@ -179,15 +190,22 @@ int mlx4_crdump_collect(struct mlx4_dev *dev)
return -ENODEV;
}
crdump_enable_crspace_access(dev, cr_space);
/* Get the available snapshot ID for the dumps */
id = devlink_region_snapshot_id_get(devlink);
err = devlink_region_snapshot_id_get(devlink, &id);
if (err) {
mlx4_err(dev, "crdump: devlink get snapshot id err %d\n", err);
return err;
}
crdump_enable_crspace_access(dev, cr_space);
/* Try to capture dumps */
mlx4_crdump_collect_crspace(dev, cr_space, id);
mlx4_crdump_collect_fw_health(dev, cr_space, id);
/* Release reference on the snapshot id */
devlink_region_snapshot_id_put(devlink, id);
crdump_disable_crspace_access(dev, cr_space);
iounmap(cr_space);
......@@ -205,7 +223,7 @@ int mlx4_crdump_init(struct mlx4_dev *dev)
/* Create cr-space region */
crdump->region_crspace =
devlink_region_create(devlink,
region_cr_space_str,
&region_cr_space_ops,
MAX_NUM_OF_DUMPS_TO_STORE,
pci_resource_len(pdev, 0));
if (IS_ERR(crdump->region_crspace))
......@@ -216,7 +234,7 @@ int mlx4_crdump_init(struct mlx4_dev *dev)
/* Create fw-health region */
crdump->region_fw_health =
devlink_region_create(devlink,
region_fw_health_str,
&region_fw_health_ops,
MAX_NUM_OF_DUMPS_TO_STORE,
HEALTH_BUFFER_SIZE);
if (IS_ERR(crdump->region_fw_health))
......
......@@ -39,24 +39,47 @@ static struct dentry *nsim_dev_ddir;
#define NSIM_DEV_DUMMY_REGION_SIZE (1024 * 32)
static int
nsim_dev_take_snapshot(struct devlink *devlink, struct netlink_ext_ack *extack,
u8 **data)
{
void *dummy_data;
dummy_data = kmalloc(NSIM_DEV_DUMMY_REGION_SIZE, GFP_KERNEL);
if (!dummy_data)
return -ENOMEM;
get_random_bytes(dummy_data, NSIM_DEV_DUMMY_REGION_SIZE);
*data = dummy_data;
return 0;
}
static ssize_t nsim_dev_take_snapshot_write(struct file *file,
const char __user *data,
size_t count, loff_t *ppos)
{
struct nsim_dev *nsim_dev = file->private_data;
void *dummy_data;
struct devlink *devlink;
u8 *dummy_data;
int err;
u32 id;
dummy_data = kmalloc(NSIM_DEV_DUMMY_REGION_SIZE, GFP_KERNEL);
if (!dummy_data)
return -ENOMEM;
devlink = priv_to_devlink(nsim_dev);
get_random_bytes(dummy_data, NSIM_DEV_DUMMY_REGION_SIZE);
err = nsim_dev_take_snapshot(devlink, NULL, &dummy_data);
if (err)
return err;
id = devlink_region_snapshot_id_get(priv_to_devlink(nsim_dev));
err = devlink_region_snapshot_id_get(devlink, &id);
if (err) {
pr_err("Failed to get snapshot id\n");
return err;
}
err = devlink_region_snapshot_create(nsim_dev->dummy_region,
dummy_data, id, kfree);
dummy_data, id);
devlink_region_snapshot_id_put(devlink, id);
if (err) {
pr_err("Failed to create region snapshot\n");
kfree(dummy_data);
......@@ -340,11 +363,17 @@ static void nsim_devlink_param_load_driverinit_values(struct devlink *devlink)
#define NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX 16
static const struct devlink_region_ops dummy_region_ops = {
.name = "dummy",
.destructor = &kfree,
.snapshot = nsim_dev_take_snapshot,
};
static int nsim_dev_dummy_region_init(struct nsim_dev *nsim_dev,
struct devlink *devlink)
{
nsim_dev->dummy_region =
devlink_region_create(devlink, "dummy",
devlink_region_create(devlink, &dummy_region_ops,
NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX,
NSIM_DEV_DUMMY_REGION_SIZE);
return PTR_ERR_OR_ZERO(nsim_dev->dummy_region);
......
......@@ -18,6 +18,7 @@
#include <net/net_namespace.h>
#include <net/flow_offload.h>
#include <uapi/linux/devlink.h>
#include <linux/xarray.h>
struct devlink_ops;
......@@ -29,13 +30,13 @@ struct devlink {
struct list_head resource_list;
struct list_head param_list;
struct list_head region_list;
u32 snapshot_id;
struct list_head reporter_list;
struct mutex reporters_lock; /* protects reporter_list */
struct devlink_dpipe_headers *dpipe_headers;
struct list_head trap_list;
struct list_head trap_group_list;
const struct devlink_ops *ops;
struct xarray snapshot_ids;
struct device *dev;
possible_net_t _net;
struct mutex lock;
......@@ -496,7 +497,21 @@ enum devlink_param_generic_id {
struct devlink_region;
struct devlink_info_req;
typedef void devlink_snapshot_data_dest_t(const void *data);
/**
* struct devlink_region_ops - Region operations
* @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.
*/
struct devlink_region_ops {
const char *name;
void (*destructor)(const void *data);
int (*snapshot)(struct devlink *devlink, struct netlink_ext_ack *extack,
u8 **data);
};
struct devlink_fmsg;
struct devlink_health_reporter;
......@@ -963,15 +978,15 @@ void devlink_port_param_value_changed(struct devlink_port *devlink_port,
u32 param_id);
void devlink_param_value_str_fill(union devlink_param_value *dst_val,
const char *src);
struct devlink_region *devlink_region_create(struct devlink *devlink,
const char *region_name,
u32 region_max_snapshots,
u64 region_size);
struct devlink_region *
devlink_region_create(struct devlink *devlink,
const struct devlink_region_ops *ops,
u32 region_max_snapshots, u64 region_size);
void devlink_region_destroy(struct devlink_region *region);
u32 devlink_region_snapshot_id_get(struct devlink *devlink);
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,
u8 *data, u32 snapshot_id,
devlink_snapshot_data_dest_t *data_destructor);
u8 *data, u32 snapshot_id);
int devlink_info_serial_number_put(struct devlink_info_req *req,
const char *sn);
int devlink_info_driver_name_put(struct devlink_info_req *req,
......
......@@ -344,7 +344,7 @@ devlink_sb_tc_index_get_from_info(struct devlink_sb *devlink_sb,
struct devlink_region {
struct devlink *devlink;
struct list_head list;
const char *name;
const struct devlink_region_ops *ops;
struct list_head snapshot_list;
u32 max_snapshots;
u32 cur_snapshots;
......@@ -354,7 +354,6 @@ struct devlink_region {
struct devlink_snapshot {
struct list_head list;
struct devlink_region *region;
devlink_snapshot_data_dest_t *data_destructor;
u8 *data;
u32 id;
};
......@@ -365,7 +364,7 @@ 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->name, region_name))
if (!strcmp(region->ops->name, region_name))
return region;
return NULL;
......@@ -3695,7 +3694,7 @@ static int devlink_nl_region_fill(struct sk_buff *msg, struct devlink *devlink,
if (err)
goto nla_put_failure;
err = nla_put_string(msg, DEVLINK_ATTR_REGION_NAME, region->name);
err = nla_put_string(msg, DEVLINK_ATTR_REGION_NAME, region->ops->name);
if (err)
goto nla_put_failure;
......@@ -3741,7 +3740,7 @@ static void devlink_nl_region_notify(struct devlink_region *region,
goto out_cancel_msg;
err = nla_put_string(msg, DEVLINK_ATTR_REGION_NAME,
region->name);
region->ops->name);
if (err)
goto out_cancel_msg;
......@@ -3769,13 +3768,201 @@ static void devlink_nl_region_notify(struct devlink_region *region,
nlmsg_free(msg);
}
/**
* __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;
lockdep_assert_held(&devlink->lock);
p = xa_load(&devlink->snapshot_ids, id);
if (WARN_ON(!p))
return -EINVAL;
if (WARN_ON(!xa_is_value(p)))
return -EINVAL;
count = xa_to_value(p);
count++;
return xa_err(xa_store(&devlink->snapshot_ids, id, xa_mk_value(count),
GFP_KERNEL));
}
/**
* __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;
lockdep_assert_held(&devlink->lock);
p = xa_load(&devlink->snapshot_ids, id);
if (WARN_ON(!p))
return;
if (WARN_ON(!xa_is_value(p)))
return;
count = xa_to_value(p);
if (count > 1) {
count--;
xa_store(&devlink->snapshot_ids, id, xa_mk_value(count),
GFP_KERNEL);
} else {
/* If this was the last user, we can erase this id */
xa_erase(&devlink->snapshot_ids, id);
}
}
/**
* __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)
{
lockdep_assert_held(&devlink->lock);
if (WARN_ON(xa_load(&devlink->snapshot_ids, id)))
return -EEXIST;
return xa_err(xa_store(&devlink->snapshot_ids, id, xa_mk_value(0),
GFP_KERNEL));
}
/**
* __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)
{
lockdep_assert_held(&devlink->lock);
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 devlink instance 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(&devlink->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(&devlink->lock);
devlink_nl_region_notify(region, snapshot, DEVLINK_CMD_REGION_DEL);
region->cur_snapshots--;
list_del(&snapshot->list);
(*snapshot->data_destructor)(snapshot->data);
region->ops->destructor(snapshot->data);
__devlink_snapshot_id_decrement(devlink, snapshot->id);
kfree(snapshot);
}
......@@ -3878,6 +4065,71 @@ static int devlink_nl_cmd_region_del(struct sk_buff *skb,
return 0;
}
static int
devlink_nl_cmd_region_new(struct sk_buff *skb, struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
struct devlink_region *region;
const char *region_name;
u32 snapshot_id;
u8 *data;
int err;
if (!info->attrs[DEVLINK_ATTR_REGION_NAME]) {
NL_SET_ERR_MSG_MOD(info->extack, "No region name provided");
return -EINVAL;
}
if (!info->attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]) {
NL_SET_ERR_MSG_MOD(info->extack, "No snapshot id provided");
return -EINVAL;
}
region_name = nla_data(info->attrs[DEVLINK_ATTR_REGION_NAME]);
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;
}
if (!region->ops->snapshot) {
NL_SET_ERR_MSG_MOD(info->extack, "The requested region does not support taking an immediate snapshot");
return -EOPNOTSUPP;
}
if (region->cur_snapshots == region->max_snapshots) {
NL_SET_ERR_MSG_MOD(info->extack, "The region has reached the maximum number of stored snapshots");
return -ENOSPC;
}
snapshot_id = nla_get_u32(info->attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]);
if (devlink_region_snapshot_get_by_id(region, snapshot_id)) {
NL_SET_ERR_MSG_MOD(info->extack, "The requested snapshot id is already in use");
return -EEXIST;
}
err = __devlink_snapshot_id_insert(devlink, snapshot_id);
if (err)
return err;
err = region->ops->snapshot(devlink, info->extack, &data);
if (err)
goto err_snapshot_capture;
err = __devlink_region_snapshot_create(region, data, snapshot_id);
if (err)
goto err_snapshot_create;
return 0;
err_snapshot_create:
region->ops->destructor(data);
err_snapshot_capture:
__devlink_snapshot_id_decrement(devlink, snapshot_id);
return err;
}
static int devlink_nl_cmd_region_read_chunk_fill(struct sk_buff *msg,
struct devlink *devlink,
u8 *chunk, u32 chunk_size,
......@@ -6286,6 +6538,13 @@ static const struct genl_ops devlink_nl_ops[] = {
.flags = GENL_ADMIN_PERM,
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
},
{
.cmd = DEVLINK_CMD_REGION_NEW,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.doit = devlink_nl_cmd_region_new,
.flags = GENL_ADMIN_PERM,
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
},
{
.cmd = DEVLINK_CMD_REGION_DEL,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
......@@ -6429,6 +6688,7 @@ struct devlink *devlink_alloc(const struct devlink_ops *ops, size_t priv_size)
if (!devlink)
return NULL;
devlink->ops = ops;
xa_init_flags(&devlink->snapshot_ids, XA_FLAGS_ALLOC);
__devlink_net_set(devlink, &init_net);
INIT_LIST_HEAD(&devlink->port_list);
INIT_LIST_HEAD(&devlink->sb_list);
......@@ -6533,6 +6793,8 @@ void devlink_free(struct devlink *devlink)
WARN_ON(!list_empty(&devlink->sb_list));
WARN_ON(!list_empty(&devlink->port_list));
xa_destroy(&devlink->snapshot_ids);
kfree(devlink);
}
EXPORT_SYMBOL_GPL(devlink_free);
......@@ -7648,21 +7910,24 @@ EXPORT_SYMBOL_GPL(devlink_param_value_str_fill);
* devlink_region_create - create a new address region
*
* @devlink: devlink
* @region_name: region name
* @ops: region operations and name
* @region_max_snapshots: Maximum supported number of snapshots for region
* @region_size: size of region
*/
struct devlink_region *devlink_region_create(struct devlink *devlink,
const char *region_name,
u32 region_max_snapshots,
u64 region_size)
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 *region;
int err = 0;
if (WARN_ON(!ops) || WARN_ON(!ops->destructor))
return ERR_PTR(-EINVAL);
mutex_lock(&devlink->lock);
if (devlink_region_get_by_name(devlink, region_name)) {
if (devlink_region_get_by_name(devlink, ops->name)) {
err = -EEXIST;
goto unlock;
}
......@@ -7675,7 +7940,7 @@ struct devlink_region *devlink_region_create(struct devlink *devlink,
region->devlink = devlink;
region->max_snapshots = region_max_snapshots;
region->name = region_name;
region->ops = ops;
region->size = region_size;
INIT_LIST_HEAD(&region->snapshot_list);
list_add_tail(&region->list, &devlink->region_list);
......@@ -7721,75 +7986,66 @@ EXPORT_SYMBOL_GPL(devlink_region_destroy);
* 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
*/
u32 devlink_region_snapshot_id_get(struct devlink *devlink)
int devlink_region_snapshot_id_get(struct devlink *devlink, u32 *id)
{
u32 id;
int err;
mutex_lock(&devlink->lock);
id = ++devlink->snapshot_id;
err = __devlink_region_snapshot_id_get(devlink, id);
mutex_unlock(&devlink->lock);
return id;
return err;
}
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)
{
mutex_lock(&devlink->lock);
__devlink_snapshot_id_decrement(devlink, id);
mutex_unlock(&devlink->lock);
}
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.
* 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
* @data_destructor: pointer to destructor function to free data
*/
int devlink_region_snapshot_create(struct devlink_region *region,
u8 *data, u32 snapshot_id,
devlink_snapshot_data_dest_t *data_destructor)
u8 *data, u32 snapshot_id)
{
struct devlink *devlink = region->devlink;
struct devlink_snapshot *snapshot;
int err;
mutex_lock(&devlink->lock);
/* check if region can hold one more snapshot */
if (region->cur_snapshots == region->max_snapshots) {
err = -ENOMEM;
goto unlock;
}
if (devlink_region_snapshot_get_by_id(region, snapshot_id)) {
err = -EEXIST;
goto unlock;
}
snapshot = kzalloc(sizeof(*snapshot), GFP_KERNEL);
if (!snapshot) {
err = -ENOMEM;
goto unlock;
}
snapshot->id = snapshot_id;
snapshot->region = region;
snapshot->data = data;
snapshot->data_destructor = data_destructor;
list_add_tail(&snapshot->list, &region->snapshot_list);
region->cur_snapshots++;
devlink_nl_region_notify(region, snapshot, DEVLINK_CMD_REGION_NEW);
err = __devlink_region_snapshot_create(region, data, snapshot_id);
mutex_unlock(&devlink->lock);
return 0;
unlock:
mutex_unlock(&devlink->lock);
return err;
}
EXPORT_SYMBOL_GPL(devlink_region_snapshot_create);
......
......@@ -141,6 +141,16 @@ regions_test()
check_region_snapshot_count dummy post-first-delete 2
devlink region new $DL_HANDLE/dummy snapshot 25
check_err $? "Failed to create a new snapshot with id 25"
check_region_snapshot_count dummy post-first-request 3
devlink region del $DL_HANDLE/dummy snapshot 25
check_err $? "Failed to delete snapshot with id 25"
check_region_snapshot_count dummy post-second-delete 2
log_test "regions test"
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment