mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 22:14:03 +02:00
Merge branch 'devlink-and-mlx5-support-cross-function-rate-scheduling'
Tariq Toukan says: ==================== devlink and mlx5: Support cross-function rate scheduling This series by Cosmin adds support for cross-function rate scheduling in devlink and mlx5. See detailed explanation by Cosmin below [0]. [0] devlink objects support rate management for TX scheduling, which involves maintaining a tree of rate nodes that corresponds to TX schedulers in hardware. 'man devlink-rate' has the full details. The tree of rate nodes is maintained per devlink object, protected by the devlink lock. There exists hardware capable of instantiating TX scheduling trees spanning multiple functions of the same physical device (and thus devlink objects) and therefore the current API and locking scheme is insufficient. This patch series changes the devlink rate implementation and API to allow supporting such hardware and managing TX scheduling trees across multiple functions of a physical device. Modeling this requires having devlink rate nodes with parents in other devlink objects. A naive approach that relies on the current one-lock-per-devlink model is impossible, as it would require in some cases acquiring multiple devlink locks in the correct order. The solution proposed in this patch series makes use of the recently introduced shared devlink instance [1] to manage rate hierarchy changes across multiple functions. V1 of this patch series was sent a long time ago [2], using a different approach of storing rates in a shared rate domain with special locking rules. This new approach uses standard devlink instances and nesting. The first part of the series adds support to devlink rates for maintaining the rate tree across multiple functions. The second part changes the mlx5 implementation to make use of this (and cleans up remnants of the previous approach, involving rate domains). The neat part about using the shared devlink object is that it works for SFs as well, which are already nested in their parent PF instances. So with this series, complex scheduling trees spanning multiple SFs across multiple PFs of the same NIC can now be supported. ==================== Link: https://patch.msgid.link/20260701073254.754518-1-tariqt@nvidia.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
commit
8436f31673
|
|
@ -895,6 +895,16 @@ attribute-sets:
|
|||
resource-dump response. Bit 0 (dev) selects device-level
|
||||
resources; bit 1 (port) selects port-level resources.
|
||||
When absent all classes are returned.
|
||||
-
|
||||
name: parent-dev
|
||||
type: nest
|
||||
nested-attributes: dl-parent-dev
|
||||
doc: |
|
||||
Identifies the devlink instance which owns the parent rate node.
|
||||
Used with rate-set and rate-new to parent a rate object to a node on
|
||||
a different devlink instance, enabling cross-device rate scheduling.
|
||||
When absent, the parent node is resolved on the same instance.
|
||||
|
||||
-
|
||||
name: dl-dev-stats
|
||||
subset-of: devlink
|
||||
|
|
@ -1317,6 +1327,16 @@ attribute-sets:
|
|||
Specifies the bandwidth share assigned to the Traffic Class.
|
||||
The bandwidth for the traffic class is determined
|
||||
in proportion to the sum of the shares of all configured classes.
|
||||
-
|
||||
name: dl-parent-dev
|
||||
subset-of: devlink
|
||||
attributes:
|
||||
-
|
||||
name: bus-name
|
||||
-
|
||||
name: dev-name
|
||||
-
|
||||
name: index
|
||||
|
||||
operations:
|
||||
enum-model: directional
|
||||
|
|
@ -2289,8 +2309,8 @@ operations:
|
|||
dont-validate: [strict]
|
||||
flags: [admin-perm]
|
||||
do:
|
||||
pre: devlink-nl-pre-doit
|
||||
post: devlink-nl-post-doit
|
||||
pre: devlink-nl-pre-doit-parent-dev-optional
|
||||
post: devlink-nl-post-doit-parent-dev-optional
|
||||
request:
|
||||
attributes:
|
||||
- bus-name
|
||||
|
|
@ -2303,6 +2323,7 @@ operations:
|
|||
- rate-tx-weight
|
||||
- rate-parent-node-name
|
||||
- rate-tc-bws
|
||||
- parent-dev
|
||||
|
||||
-
|
||||
name: rate-new
|
||||
|
|
@ -2311,8 +2332,8 @@ operations:
|
|||
dont-validate: [strict]
|
||||
flags: [admin-perm]
|
||||
do:
|
||||
pre: devlink-nl-pre-doit
|
||||
post: devlink-nl-post-doit
|
||||
pre: devlink-nl-pre-doit-parent-dev-optional
|
||||
post: devlink-nl-post-doit-parent-dev-optional
|
||||
request:
|
||||
attributes:
|
||||
- bus-name
|
||||
|
|
@ -2325,6 +2346,7 @@ operations:
|
|||
- rate-tx-weight
|
||||
- rate-parent-node-name
|
||||
- rate-tc-bws
|
||||
- parent-dev
|
||||
|
||||
-
|
||||
name: rate-del
|
||||
|
|
|
|||
|
|
@ -420,6 +420,8 @@ API allows to configure following rate object's parameters:
|
|||
Parent node name. Parent node rate limits are considered as additional limits
|
||||
to all node children limits. ``tx_max`` is an upper limit for children.
|
||||
``tx_share`` is a total bandwidth distributed among children.
|
||||
If the device supports cross-function scheduling, the parent can be from a
|
||||
different function of the same underlying device.
|
||||
|
||||
``tc_bw``
|
||||
Allow users to set the bandwidth allocation per traffic class on rate
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@ sure to respect following rules:
|
|||
|
||||
- Lock ordering should be maintained. If driver needs to take instance
|
||||
lock of both nested and parent instances at the same time, devlink
|
||||
instance lock of the parent instance should be taken first, only then
|
||||
instance lock of the nested instance could be taken.
|
||||
- Driver should use object-specific helpers to setup the nested relationship
|
||||
before registering the nested devlink instance:
|
||||
instance lock of the nested instance should be taken first, only then
|
||||
instance lock of the parent instance could be taken.
|
||||
- Driver should use object-specific helpers to setup the
|
||||
nested relationship:
|
||||
|
||||
- ``devl_nested_devlink_set()`` - called to setup devlink -> nested
|
||||
devlink relationship (could be used for multiple nested instances).
|
||||
|
|
|
|||
|
|
@ -419,3 +419,36 @@ User commands examples:
|
|||
|
||||
.. note::
|
||||
This command can run over all interfaces such as PF/VF and representor ports.
|
||||
|
||||
Rates
|
||||
=====
|
||||
|
||||
mlx5 devices can limit transmission of individual VFs or a group of them via
|
||||
the devlink-rate API in switchdev mode.
|
||||
|
||||
User commands examples:
|
||||
|
||||
- Print the existing rates::
|
||||
|
||||
$ devlink port function rate show
|
||||
|
||||
- Set a max tx limit on traffic from VF0::
|
||||
|
||||
$ devlink port function rate set pci/0000:82:00.0/1 tx_max 10Gbit
|
||||
|
||||
- Create a rate group with a max tx limit and add two VFs to it::
|
||||
|
||||
$ devlink port function rate add pci/0000:82:00.0/group1 tx_max 10Gbit
|
||||
$ devlink port function rate set pci/0000:82:00.0/1 parent group1
|
||||
$ devlink port function rate set pci/0000:82:00.0/2 parent group1
|
||||
|
||||
- Same scenario, with a min guarantee of 20% of the bandwidth for the first VF::
|
||||
|
||||
$ devlink port function rate add pci/0000:82:00.0/group1 tx_max 10Gbit
|
||||
$ devlink port function rate set pci/0000:82:00.0/1 parent group1 tx_share 2Gbit
|
||||
$ devlink port function rate set pci/0000:82:00.0/2 parent group1
|
||||
|
||||
- Cross-device scheduling::
|
||||
|
||||
$ devlink port function rate add pci/0000:82:00.0/group1 tx_max 10Gbit
|
||||
$ devlink port function rate set pci/0000:82:00.1/32769 parent pci/0000:82:00.0/group1
|
||||
|
|
|
|||
|
|
@ -383,6 +383,7 @@ static const struct devlink_ops mlx5_devlink_ops = {
|
|||
.rate_node_del = mlx5_esw_devlink_rate_node_del,
|
||||
.rate_leaf_parent_set = mlx5_esw_devlink_rate_leaf_parent_set,
|
||||
.rate_node_parent_set = mlx5_esw_devlink_rate_node_parent_set,
|
||||
.supported_cross_device_rate_nodes = true,
|
||||
#endif
|
||||
#ifdef CONFIG_MLX5_SF_MANAGER
|
||||
.port_new = mlx5_devlink_sf_port_new,
|
||||
|
|
|
|||
|
|
@ -268,7 +268,6 @@ void mlx5_esw_offloads_devlink_port_unregister(struct mlx5_vport *vport)
|
|||
dl_port = vport->dl_port;
|
||||
mlx5_esw_devlink_port_res_unregister(&dl_port->dl_port);
|
||||
|
||||
mlx5_esw_qos_vport_update_parent(vport, NULL, NULL);
|
||||
devl_rate_leaf_destroy(&dl_port->dl_port);
|
||||
|
||||
devl_port_unregister(&dl_port->dl_port);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -6,9 +6,6 @@
|
|||
|
||||
#ifdef CONFIG_MLX5_ESWITCH
|
||||
|
||||
int mlx5_esw_qos_init(struct mlx5_eswitch *esw);
|
||||
void mlx5_esw_qos_cleanup(struct mlx5_eswitch *esw);
|
||||
|
||||
int mlx5_esw_qos_set_vport_rate(struct mlx5_vport *evport, u32 max_rate, u32 min_rate);
|
||||
bool mlx5_esw_qos_get_vport_rate(struct mlx5_vport *vport, u32 *max_rate, u32 *min_rate);
|
||||
void mlx5_esw_qos_vport_disable(struct mlx5_vport *vport);
|
||||
|
|
|
|||
|
|
@ -1885,10 +1885,6 @@ int mlx5_eswitch_enable_locked(struct mlx5_eswitch *esw, int num_vfs)
|
|||
MLX5_NB_INIT(&esw->nb, eswitch_vport_event, NIC_VPORT_CHANGE);
|
||||
mlx5_eq_notifier_register(esw->dev, &esw->nb);
|
||||
|
||||
err = mlx5_esw_qos_init(esw);
|
||||
if (err)
|
||||
goto err_esw_init;
|
||||
|
||||
if (esw->mode == MLX5_ESWITCH_LEGACY) {
|
||||
err = esw_legacy_enable(esw);
|
||||
} else {
|
||||
|
|
@ -1990,6 +1986,13 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw, bool clear_vf)
|
|||
esw->esw_funcs.num_vfs, esw->esw_funcs.num_ec_vfs, esw->enabled_vports);
|
||||
|
||||
mlx5_eswitch_invalidate_wq(esw);
|
||||
|
||||
if (esw->mode == MLX5_ESWITCH_OFFLOADS) {
|
||||
struct devlink *devlink = priv_to_devlink(esw->dev);
|
||||
|
||||
devl_rate_nodes_destroy(devlink);
|
||||
}
|
||||
|
||||
mlx5_esw_reps_block(esw);
|
||||
|
||||
if (!mlx5_core_is_ecpf(esw->dev)) {
|
||||
|
|
@ -2003,12 +2006,6 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw, bool clear_vf)
|
|||
}
|
||||
|
||||
mlx5_esw_reps_unblock(esw);
|
||||
|
||||
if (esw->mode == MLX5_ESWITCH_OFFLOADS) {
|
||||
struct devlink *devlink = priv_to_devlink(esw->dev);
|
||||
|
||||
devl_rate_nodes_destroy(devlink);
|
||||
}
|
||||
/* Destroy legacy fdb when disabling sriov in legacy mode. */
|
||||
if (esw->mode == MLX5_ESWITCH_LEGACY)
|
||||
mlx5_eswitch_disable_locked(esw);
|
||||
|
|
@ -2039,6 +2036,9 @@ void mlx5_eswitch_disable_locked(struct mlx5_eswitch *esw)
|
|||
esw->mode == MLX5_ESWITCH_LEGACY ? "LEGACY" : "OFFLOADS",
|
||||
esw->esw_funcs.num_vfs, esw->esw_funcs.num_ec_vfs, esw->enabled_vports);
|
||||
|
||||
if (esw->mode == MLX5_ESWITCH_OFFLOADS)
|
||||
devl_rate_nodes_destroy(devlink);
|
||||
|
||||
if (esw->fdb_table.flags & MLX5_ESW_FDB_CREATED) {
|
||||
esw->fdb_table.flags &= ~MLX5_ESW_FDB_CREATED;
|
||||
if (esw->mode == MLX5_ESWITCH_OFFLOADS)
|
||||
|
|
@ -2047,9 +2047,6 @@ void mlx5_eswitch_disable_locked(struct mlx5_eswitch *esw)
|
|||
esw_legacy_disable(esw);
|
||||
mlx5_esw_acls_ns_cleanup(esw);
|
||||
}
|
||||
|
||||
if (esw->mode == MLX5_ESWITCH_OFFLOADS)
|
||||
devl_rate_nodes_destroy(devlink);
|
||||
}
|
||||
|
||||
void mlx5_eswitch_disable(struct mlx5_eswitch *esw)
|
||||
|
|
@ -2554,9 +2551,6 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev)
|
|||
goto reps_err;
|
||||
|
||||
esw->mode = MLX5_ESWITCH_LEGACY;
|
||||
err = mlx5_esw_qos_init(esw);
|
||||
if (err)
|
||||
goto reps_err;
|
||||
|
||||
mutex_init(&esw->offloads.encap_tbl_lock);
|
||||
hash_init(esw->offloads.encap_tbl);
|
||||
|
|
@ -2611,7 +2605,6 @@ void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw)
|
|||
|
||||
mlx5_eswitch_invalidate_wq(esw);
|
||||
destroy_workqueue(esw->work_queue);
|
||||
mlx5_esw_qos_cleanup(esw);
|
||||
WARN_ON(refcount_read(&esw->qos.refcnt));
|
||||
mutex_destroy(&esw->state_lock);
|
||||
WARN_ON(!xa_empty(&esw->offloads.vhca_map));
|
||||
|
|
|
|||
|
|
@ -234,8 +234,10 @@ struct mlx5_vport {
|
|||
|
||||
struct mlx5_vport_info info;
|
||||
|
||||
/* Protected with the E-Switch qos domain lock. The Vport QoS can
|
||||
* either be disabled (sched_node is NULL) or in one of three states:
|
||||
/* Protected by either the shared devlink (dev->shd) lock or by
|
||||
* esw->state_lock. See esw_assert_qos_lock_held() for more details.
|
||||
* The Vport QoS can either be disabled (sched_node is NULL) or in one
|
||||
* of three states:
|
||||
* 1. Regular QoS (sched_node is a vport node).
|
||||
* 2. TC QoS enabled on the vport (sched_node is a TC arbiter).
|
||||
* 3. TC QoS enabled on the vport's parent node
|
||||
|
|
@ -382,7 +384,6 @@ enum {
|
|||
};
|
||||
|
||||
struct dentry;
|
||||
struct mlx5_qos_domain;
|
||||
|
||||
struct mlx5_eswitch {
|
||||
struct mlx5_core_dev *dev;
|
||||
|
|
@ -411,12 +412,15 @@ struct mlx5_eswitch {
|
|||
atomic64_t user_count;
|
||||
wait_queue_head_t work_queue_wait;
|
||||
|
||||
/* Protected with the E-Switch qos domain lock. */
|
||||
/* QoS changes are serialized by either the shared devlink (dev->shd)
|
||||
* lock or by esw->state_lock. See esw_assert_qos_lock_held() for more
|
||||
* details.
|
||||
*/
|
||||
struct {
|
||||
/* Initially 0, meaning no QoS users and QoS is disabled. */
|
||||
refcount_t refcnt;
|
||||
u32 root_tsar_ix;
|
||||
struct mlx5_qos_domain *domain;
|
||||
/* The root node of the hierarchy. */
|
||||
struct mlx5_esw_sched_node *root;
|
||||
} qos;
|
||||
|
||||
struct mlx5_esw_bridge_offloads *br_offloads;
|
||||
|
|
@ -482,8 +486,6 @@ int mlx5_eswitch_set_vport_trust(struct mlx5_eswitch *esw,
|
|||
u16 vport_num, bool setting);
|
||||
int mlx5_eswitch_set_vport_rate(struct mlx5_eswitch *esw, u16 vport,
|
||||
u32 max_rate, u32 min_rate);
|
||||
int mlx5_esw_qos_vport_update_parent(struct mlx5_vport *vport, struct mlx5_esw_sched_node *node,
|
||||
struct netlink_ext_ack *extack);
|
||||
int mlx5_eswitch_set_vepa(struct mlx5_eswitch *esw, u8 setting);
|
||||
int mlx5_eswitch_get_vepa(struct mlx5_eswitch *esw, u8 *setting);
|
||||
int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
|
||||
|
|
|
|||
|
|
@ -1594,6 +1594,15 @@ struct devlink_ops {
|
|||
struct devlink_rate *parent,
|
||||
void *priv_child, void *priv_parent,
|
||||
struct netlink_ext_ack *extack);
|
||||
/* Indicates if cross-device rate nodes are supported.
|
||||
* This also requires a shared common ancestor object all devices that
|
||||
* could share rate nodes are nested in.
|
||||
* If enabled, rate operations may be called on an instance with only
|
||||
* the common ancestor lock held and *without that instance lock held*.
|
||||
* It is the driver's responsibility to ensure proper serialization
|
||||
* with other operations.
|
||||
*/
|
||||
bool supported_cross_device_rate_nodes;
|
||||
/**
|
||||
* selftests_check() - queries if selftest is supported
|
||||
* @devlink: devlink instance
|
||||
|
|
|
|||
|
|
@ -648,6 +648,8 @@ enum devlink_attr {
|
|||
DEVLINK_ATTR_INDEX, /* uint */
|
||||
DEVLINK_ATTR_RESOURCE_SCOPE_MASK, /* u32 */
|
||||
|
||||
DEVLINK_ATTR_PARENT_DEV, /* nested */
|
||||
|
||||
/* Add new attributes above here, update the spec in
|
||||
* Documentation/netlink/specs/devlink.yaml and re-generate
|
||||
* net/devlink/netlink_gen.c.
|
||||
|
|
|
|||
|
|
@ -67,6 +67,22 @@ static void __devlink_rel_put(struct devlink_rel *rel)
|
|||
devlink_rel_free(rel);
|
||||
}
|
||||
|
||||
struct devlink *__must_check devlink_nested_in_get_lock(struct devlink *devlink)
|
||||
{
|
||||
devl_assert_locked(devlink);
|
||||
if (!devlink->rel)
|
||||
return NULL;
|
||||
devlink = devlinks_xa_get(devlink->rel->nested_in.devlink_index);
|
||||
if (!devlink)
|
||||
return NULL;
|
||||
devl_lock(devlink);
|
||||
if (devl_is_registered(devlink))
|
||||
return devlink;
|
||||
devl_unlock(devlink);
|
||||
devlink_put(devlink);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void devlink_rel_nested_in_notify_work(struct work_struct *work)
|
||||
{
|
||||
struct devlink_rel *rel = container_of(work, struct devlink_rel,
|
||||
|
|
@ -518,6 +534,9 @@ void devlink_free(struct devlink *devlink)
|
|||
{
|
||||
ASSERT_DEVLINK_NOT_REGISTERED(devlink);
|
||||
|
||||
devl_lock(devlink);
|
||||
WARN_ON(devlink_rates_check(devlink, NULL, NULL));
|
||||
devl_unlock(devlink);
|
||||
devlink_rel_put(devlink);
|
||||
|
||||
WARN_ON(!list_empty(&devlink->trap_policer_list));
|
||||
|
|
@ -528,7 +547,6 @@ void devlink_free(struct devlink *devlink)
|
|||
WARN_ON(!list_empty(&devlink->resource_list));
|
||||
WARN_ON(!list_empty(&devlink->dpipe_table_list));
|
||||
WARN_ON(!list_empty(&devlink->sb_list));
|
||||
WARN_ON(devlink_rates_check(devlink, NULL, NULL));
|
||||
WARN_ON(!list_empty(&devlink->linecard_list));
|
||||
WARN_ON(!xa_empty(&devlink->ports));
|
||||
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ static void devlink_notify(struct devlink *devlink, enum devlink_command cmd)
|
|||
|
||||
int devlink_nl_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
||||
|
|
@ -519,7 +519,7 @@ devlink_nl_reload_actions_performed_snd(struct devlink *devlink, u32 actions_per
|
|||
|
||||
int devlink_nl_reload_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
enum devlink_reload_action action;
|
||||
enum devlink_reload_limit limit;
|
||||
struct net *dest_net = NULL;
|
||||
|
|
@ -683,7 +683,7 @@ static int devlink_nl_eswitch_fill(struct sk_buff *msg, struct devlink *devlink,
|
|||
|
||||
int devlink_nl_eswitch_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
||||
|
|
@ -704,7 +704,7 @@ int devlink_nl_eswitch_get_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
|
||||
int devlink_nl_eswitch_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
const struct devlink_ops *ops = devlink->ops;
|
||||
enum devlink_eswitch_encap_mode encap_mode;
|
||||
u8 inline_mode;
|
||||
|
|
@ -906,7 +906,7 @@ devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink,
|
|||
|
||||
int devlink_nl_info_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
||||
|
|
@ -1134,7 +1134,7 @@ int devlink_nl_flash_update_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
{
|
||||
struct nlattr *nla_overwrite_mask, *nla_file_name;
|
||||
struct devlink_flash_update_params params = {};
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
const char *file_name;
|
||||
u32 supported_params;
|
||||
int ret;
|
||||
|
|
@ -1302,7 +1302,7 @@ devlink_nl_selftests_fill(struct sk_buff *msg, struct devlink *devlink,
|
|||
|
||||
int devlink_nl_selftests_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
||||
|
|
@ -1372,7 +1372,7 @@ static const struct nla_policy devlink_selftest_nl_policy[DEVLINK_ATTR_SELFTEST_
|
|||
int devlink_nl_selftests_run_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct nlattr *tb[DEVLINK_ATTR_SELFTEST_ID_MAX + 1];
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct nlattr *attrs, *selftests;
|
||||
struct sk_buff *msg;
|
||||
void *hdr;
|
||||
|
|
|
|||
|
|
@ -136,6 +136,10 @@ typedef void devlink_rel_notify_cb_t(struct devlink *devlink, u32 obj_index);
|
|||
typedef void devlink_rel_cleanup_cb_t(struct devlink *devlink, u32 obj_index,
|
||||
u32 rel_index);
|
||||
|
||||
/* Returns the locked+referenced nested-in instance or NULL. */
|
||||
struct devlink *__must_check
|
||||
devlink_nested_in_get_lock(struct devlink *devlink);
|
||||
|
||||
void devlink_rel_nested_in_clear(u32 rel_index);
|
||||
int devlink_rel_nested_in_add(u32 *rel_index, u32 devlink_index,
|
||||
u32 obj_index, devlink_rel_notify_cb_t *notify_cb,
|
||||
|
|
@ -147,6 +151,20 @@ int devlink_rel_devlink_handle_put(struct sk_buff *msg, struct devlink *devlink,
|
|||
bool *msg_updated);
|
||||
|
||||
/* Netlink */
|
||||
struct devlink_nl_ctx {
|
||||
struct devlink *devlink;
|
||||
struct devlink_port *devlink_port;
|
||||
struct devlink *parent_devlink;
|
||||
};
|
||||
|
||||
static inline struct devlink_nl_ctx *
|
||||
devlink_nl_ctx(struct genl_info *info)
|
||||
{
|
||||
BUILD_BUG_ON(sizeof(struct devlink_nl_ctx) >
|
||||
sizeof_field(struct genl_info, ctx));
|
||||
return (struct devlink_nl_ctx *)info->ctx;
|
||||
}
|
||||
|
||||
enum devlink_multicast_groups {
|
||||
DEVLINK_MCGRP_CONFIG,
|
||||
};
|
||||
|
|
@ -180,6 +198,8 @@ typedef int devlink_nl_dump_one_func_t(struct sk_buff *msg,
|
|||
struct devlink *
|
||||
devlink_get_from_attrs_lock(struct net *net, struct nlattr **attrs,
|
||||
bool dev_lock);
|
||||
struct devlink *
|
||||
devlink_get_parent_from_attrs_lock(struct net *net, struct nlattr **attrs);
|
||||
|
||||
int devlink_nl_dumpit(struct sk_buff *msg, struct netlink_callback *cb,
|
||||
devlink_nl_dump_one_func_t *dump_one);
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ static int devlink_dpipe_tables_fill(struct genl_info *info,
|
|||
struct list_head *dpipe_tables,
|
||||
const char *table_name)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_dpipe_table *table;
|
||||
struct nlattr *tables_attr;
|
||||
struct sk_buff *skb = NULL;
|
||||
|
|
@ -290,7 +290,7 @@ static int devlink_dpipe_tables_fill(struct genl_info *info,
|
|||
|
||||
int devlink_nl_dpipe_table_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
const char *table_name = NULL;
|
||||
|
||||
if (info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME])
|
||||
|
|
@ -478,7 +478,7 @@ int devlink_dpipe_entry_ctx_prepare(struct devlink_dpipe_dump_ctx *dump_ctx)
|
|||
if (!dump_ctx->hdr)
|
||||
goto nla_put_failure;
|
||||
|
||||
devlink = dump_ctx->info->user_ptr[0];
|
||||
devlink = devlink_nl_ctx(dump_ctx->info)->devlink;
|
||||
if (devlink_nl_put_handle(dump_ctx->skb, devlink))
|
||||
goto nla_put_failure;
|
||||
dump_ctx->nest = nla_nest_start_noflag(dump_ctx->skb,
|
||||
|
|
@ -563,7 +563,7 @@ static int devlink_dpipe_entries_fill(struct genl_info *info,
|
|||
int devlink_nl_dpipe_entries_get_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_dpipe_table *table;
|
||||
const char *table_name;
|
||||
|
||||
|
|
@ -650,7 +650,7 @@ static int devlink_dpipe_headers_fill(struct genl_info *info,
|
|||
struct devlink_dpipe_headers *
|
||||
dpipe_headers)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct nlattr *headers_attr;
|
||||
struct sk_buff *skb = NULL;
|
||||
struct nlmsghdr *nlh;
|
||||
|
|
@ -713,7 +713,7 @@ static int devlink_dpipe_headers_fill(struct genl_info *info,
|
|||
int devlink_nl_dpipe_headers_get_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
|
||||
if (!devlink->dpipe_headers)
|
||||
return -EOPNOTSUPP;
|
||||
|
|
@ -747,7 +747,7 @@ static int devlink_dpipe_table_counters_set(struct devlink *devlink,
|
|||
int devlink_nl_dpipe_table_counters_set_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
const char *table_name;
|
||||
bool counters_enable;
|
||||
|
||||
|
|
|
|||
|
|
@ -358,7 +358,7 @@ devlink_health_reporter_get_from_info(struct devlink *devlink,
|
|||
int devlink_nl_health_reporter_get_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_health_reporter *reporter;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
|
@ -456,7 +456,7 @@ int devlink_nl_health_reporter_get_dumpit(struct sk_buff *skb,
|
|||
int devlink_nl_health_reporter_set_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_health_reporter *reporter;
|
||||
|
||||
reporter = devlink_health_reporter_get_from_info(devlink, info);
|
||||
|
|
@ -715,7 +715,7 @@ EXPORT_SYMBOL_GPL(devlink_health_reporter_state_update);
|
|||
int devlink_nl_health_reporter_recover_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_health_reporter *reporter;
|
||||
|
||||
reporter = devlink_health_reporter_get_from_info(devlink, info);
|
||||
|
|
@ -1157,7 +1157,7 @@ static int devlink_fmsg_dumpit(struct devlink_fmsg *fmsg, struct sk_buff *skb,
|
|||
int devlink_nl_health_reporter_diagnose_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_health_reporter *reporter;
|
||||
struct devlink_fmsg *fmsg;
|
||||
int err;
|
||||
|
|
@ -1252,7 +1252,7 @@ int devlink_nl_health_reporter_dump_get_dumpit(struct sk_buff *skb,
|
|||
int devlink_nl_health_reporter_dump_clear_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_health_reporter *reporter;
|
||||
|
||||
reporter = devlink_health_reporter_get_from_info(devlink, info);
|
||||
|
|
@ -1269,7 +1269,7 @@ int devlink_nl_health_reporter_dump_clear_doit(struct sk_buff *skb,
|
|||
int devlink_nl_health_reporter_test_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_health_reporter *reporter;
|
||||
|
||||
reporter = devlink_health_reporter_get_from_info(devlink, info);
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ void devlink_linecards_notify_unregister(struct devlink *devlink)
|
|||
|
||||
int devlink_nl_linecard_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_linecard *linecard;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
|
@ -371,7 +371,7 @@ static int devlink_linecard_type_unset(struct devlink_linecard *linecard,
|
|||
int devlink_nl_linecard_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct netlink_ext_ack *extack = info->extack;
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_linecard *linecard;
|
||||
int err;
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#define DEVLINK_NL_FLAG_NEED_PORT BIT(0)
|
||||
#define DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT BIT(1)
|
||||
#define DEVLINK_NL_FLAG_NEED_DEV_LOCK BIT(2)
|
||||
#define DEVLINK_NL_FLAG_OPTIONAL_PARENT_DEV BIT(3)
|
||||
|
||||
static const struct genl_multicast_group devlink_nl_mcgrps[] = {
|
||||
[DEVLINK_MCGRP_CONFIG] = { .name = DEVLINK_GENL_MCGRP_CONFIG_NAME },
|
||||
|
|
@ -239,37 +240,82 @@ devlink_get_from_attrs_lock(struct net *net, struct nlattr **attrs,
|
|||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
struct devlink *
|
||||
devlink_get_parent_from_attrs_lock(struct net *net, struct nlattr **attrs)
|
||||
{
|
||||
unsigned int maxtype = ARRAY_SIZE(devlink_dl_parent_dev_nl_policy) - 1;
|
||||
struct devlink *devlink;
|
||||
struct nlattr **tb;
|
||||
int err;
|
||||
|
||||
if (!attrs[DEVLINK_ATTR_PARENT_DEV])
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
tb = kcalloc(maxtype + 1, sizeof(*tb), GFP_KERNEL);
|
||||
if (!tb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = nla_parse_nested(tb, maxtype, attrs[DEVLINK_ATTR_PARENT_DEV],
|
||||
devlink_dl_parent_dev_nl_policy, NULL);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
devlink = devlink_get_from_attrs_lock(net, tb, false);
|
||||
kfree(tb);
|
||||
return devlink;
|
||||
out:
|
||||
kfree(tb);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int __devlink_nl_pre_doit(struct sk_buff *skb, struct genl_info *info,
|
||||
u8 flags)
|
||||
{
|
||||
bool parent_dev = flags & DEVLINK_NL_FLAG_OPTIONAL_PARENT_DEV;
|
||||
bool dev_lock = flags & DEVLINK_NL_FLAG_NEED_DEV_LOCK;
|
||||
struct devlink *devlink, *parent_devlink = NULL;
|
||||
struct net *net = genl_info_net(info);
|
||||
struct nlattr **attrs = info->attrs;
|
||||
struct devlink_port *devlink_port;
|
||||
struct devlink *devlink;
|
||||
int err;
|
||||
|
||||
devlink = devlink_get_from_attrs_lock(genl_info_net(info), info->attrs,
|
||||
dev_lock);
|
||||
if (IS_ERR(devlink))
|
||||
return PTR_ERR(devlink);
|
||||
if (parent_dev && attrs[DEVLINK_ATTR_PARENT_DEV]) {
|
||||
parent_devlink = devlink_get_parent_from_attrs_lock(net, attrs);
|
||||
if (IS_ERR(parent_devlink))
|
||||
return PTR_ERR(parent_devlink);
|
||||
devlink_nl_ctx(info)->parent_devlink = parent_devlink;
|
||||
/* Drop the parent devlink lock but don't release the reference.
|
||||
* This will keep it alive until the end of the request.
|
||||
*/
|
||||
devl_unlock(parent_devlink);
|
||||
}
|
||||
|
||||
info->user_ptr[0] = devlink;
|
||||
devlink = devlink_get_from_attrs_lock(net, attrs, dev_lock);
|
||||
if (IS_ERR(devlink)) {
|
||||
err = PTR_ERR(devlink);
|
||||
goto parent_put;
|
||||
}
|
||||
devlink_nl_ctx(info)->devlink = devlink;
|
||||
if (flags & DEVLINK_NL_FLAG_NEED_PORT) {
|
||||
devlink_port = devlink_port_get_from_info(devlink, info);
|
||||
if (IS_ERR(devlink_port)) {
|
||||
err = PTR_ERR(devlink_port);
|
||||
goto unlock;
|
||||
}
|
||||
info->user_ptr[1] = devlink_port;
|
||||
devlink_nl_ctx(info)->devlink_port = devlink_port;
|
||||
} else if (flags & DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT) {
|
||||
devlink_port = devlink_port_get_from_info(devlink, info);
|
||||
if (!IS_ERR(devlink_port))
|
||||
info->user_ptr[1] = devlink_port;
|
||||
devlink_nl_ctx(info)->devlink_port = devlink_port;
|
||||
}
|
||||
return 0;
|
||||
|
||||
unlock:
|
||||
devl_dev_unlock(devlink, dev_lock);
|
||||
devlink_put(devlink);
|
||||
parent_put:
|
||||
if (parent_dev && parent_devlink)
|
||||
devlink_put(parent_devlink);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -298,15 +344,25 @@ int devlink_nl_pre_doit_port_optional(const struct genl_split_ops *ops,
|
|||
return __devlink_nl_pre_doit(skb, info, DEVLINK_NL_FLAG_NEED_DEVLINK_OR_PORT);
|
||||
}
|
||||
|
||||
int devlink_nl_pre_doit_parent_dev_optional(const struct genl_split_ops *ops,
|
||||
struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
return __devlink_nl_pre_doit(skb, info,
|
||||
DEVLINK_NL_FLAG_OPTIONAL_PARENT_DEV);
|
||||
}
|
||||
|
||||
static void __devlink_nl_post_doit(struct sk_buff *skb, struct genl_info *info,
|
||||
u8 flags)
|
||||
{
|
||||
bool dev_lock = flags & DEVLINK_NL_FLAG_NEED_DEV_LOCK;
|
||||
struct devlink *devlink;
|
||||
|
||||
devlink = info->user_ptr[0];
|
||||
devlink = devlink_nl_ctx(info)->devlink;
|
||||
devl_dev_unlock(devlink, dev_lock);
|
||||
devlink_put(devlink);
|
||||
if (devlink_nl_ctx(info)->parent_devlink)
|
||||
devlink_put(devlink_nl_ctx(info)->parent_devlink);
|
||||
}
|
||||
|
||||
void devlink_nl_post_doit(const struct genl_split_ops *ops,
|
||||
|
|
@ -322,6 +378,14 @@ devlink_nl_post_doit_dev_lock(const struct genl_split_ops *ops,
|
|||
__devlink_nl_post_doit(skb, info, DEVLINK_NL_FLAG_NEED_DEV_LOCK);
|
||||
}
|
||||
|
||||
void
|
||||
devlink_nl_post_doit_parent_dev_optional(const struct genl_split_ops *ops,
|
||||
struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
__devlink_nl_post_doit(skb, info, DEVLINK_NL_FLAG_OPTIONAL_PARENT_DEV);
|
||||
}
|
||||
|
||||
static int devlink_nl_inst_single_dumpit(struct sk_buff *msg,
|
||||
struct netlink_callback *cb, int flags,
|
||||
devlink_nl_dump_one_func_t *dump_one,
|
||||
|
|
|
|||
|
|
@ -46,6 +46,12 @@ devlink_attr_param_type_validate(const struct nlattr *attr,
|
|||
}
|
||||
|
||||
/* Common nested types */
|
||||
const struct nla_policy devlink_dl_parent_dev_nl_policy[DEVLINK_ATTR_INDEX + 1] = {
|
||||
[DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING, },
|
||||
[DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING, },
|
||||
[DEVLINK_ATTR_INDEX] = NLA_POLICY_FULL_RANGE(NLA_UINT, &devlink_attr_index_range),
|
||||
};
|
||||
|
||||
const struct nla_policy devlink_dl_port_function_nl_policy[DEVLINK_PORT_FN_ATTR_CAPS + 1] = {
|
||||
[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR] = { .type = NLA_BINARY, },
|
||||
[DEVLINK_PORT_FN_ATTR_STATE] = NLA_POLICY_MAX(NLA_U8, 1),
|
||||
|
|
@ -608,7 +614,7 @@ static const struct nla_policy devlink_rate_get_dump_nl_policy[DEVLINK_ATTR_INDE
|
|||
};
|
||||
|
||||
/* DEVLINK_CMD_RATE_SET - do */
|
||||
static const struct nla_policy devlink_rate_set_nl_policy[DEVLINK_ATTR_INDEX + 1] = {
|
||||
static const struct nla_policy devlink_rate_set_nl_policy[DEVLINK_ATTR_PARENT_DEV + 1] = {
|
||||
[DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING, },
|
||||
[DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING, },
|
||||
[DEVLINK_ATTR_INDEX] = NLA_POLICY_FULL_RANGE(NLA_UINT, &devlink_attr_index_range),
|
||||
|
|
@ -619,10 +625,11 @@ static const struct nla_policy devlink_rate_set_nl_policy[DEVLINK_ATTR_INDEX + 1
|
|||
[DEVLINK_ATTR_RATE_TX_WEIGHT] = { .type = NLA_U32, },
|
||||
[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] = { .type = NLA_NUL_STRING, },
|
||||
[DEVLINK_ATTR_RATE_TC_BWS] = NLA_POLICY_NESTED(devlink_dl_rate_tc_bws_nl_policy),
|
||||
[DEVLINK_ATTR_PARENT_DEV] = NLA_POLICY_NESTED(devlink_dl_parent_dev_nl_policy),
|
||||
};
|
||||
|
||||
/* DEVLINK_CMD_RATE_NEW - do */
|
||||
static const struct nla_policy devlink_rate_new_nl_policy[DEVLINK_ATTR_INDEX + 1] = {
|
||||
static const struct nla_policy devlink_rate_new_nl_policy[DEVLINK_ATTR_PARENT_DEV + 1] = {
|
||||
[DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING, },
|
||||
[DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING, },
|
||||
[DEVLINK_ATTR_INDEX] = NLA_POLICY_FULL_RANGE(NLA_UINT, &devlink_attr_index_range),
|
||||
|
|
@ -633,6 +640,7 @@ static const struct nla_policy devlink_rate_new_nl_policy[DEVLINK_ATTR_INDEX + 1
|
|||
[DEVLINK_ATTR_RATE_TX_WEIGHT] = { .type = NLA_U32, },
|
||||
[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] = { .type = NLA_NUL_STRING, },
|
||||
[DEVLINK_ATTR_RATE_TC_BWS] = NLA_POLICY_NESTED(devlink_dl_rate_tc_bws_nl_policy),
|
||||
[DEVLINK_ATTR_PARENT_DEV] = NLA_POLICY_NESTED(devlink_dl_parent_dev_nl_policy),
|
||||
};
|
||||
|
||||
/* DEVLINK_CMD_RATE_DEL - do */
|
||||
|
|
@ -1290,21 +1298,21 @@ const struct genl_split_ops devlink_nl_ops[75] = {
|
|||
{
|
||||
.cmd = DEVLINK_CMD_RATE_SET,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT,
|
||||
.pre_doit = devlink_nl_pre_doit,
|
||||
.pre_doit = devlink_nl_pre_doit_parent_dev_optional,
|
||||
.doit = devlink_nl_rate_set_doit,
|
||||
.post_doit = devlink_nl_post_doit,
|
||||
.post_doit = devlink_nl_post_doit_parent_dev_optional,
|
||||
.policy = devlink_rate_set_nl_policy,
|
||||
.maxattr = DEVLINK_ATTR_INDEX,
|
||||
.maxattr = DEVLINK_ATTR_PARENT_DEV,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
.cmd = DEVLINK_CMD_RATE_NEW,
|
||||
.validate = GENL_DONT_VALIDATE_STRICT,
|
||||
.pre_doit = devlink_nl_pre_doit,
|
||||
.pre_doit = devlink_nl_pre_doit_parent_dev_optional,
|
||||
.doit = devlink_nl_rate_new_doit,
|
||||
.post_doit = devlink_nl_post_doit,
|
||||
.post_doit = devlink_nl_post_doit_parent_dev_optional,
|
||||
.policy = devlink_rate_new_nl_policy,
|
||||
.maxattr = DEVLINK_ATTR_INDEX,
|
||||
.maxattr = DEVLINK_ATTR_PARENT_DEV,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <uapi/linux/devlink.h>
|
||||
|
||||
/* Common nested types */
|
||||
extern const struct nla_policy devlink_dl_parent_dev_nl_policy[DEVLINK_ATTR_INDEX + 1];
|
||||
extern const struct nla_policy devlink_dl_port_function_nl_policy[DEVLINK_PORT_FN_ATTR_CAPS + 1];
|
||||
extern const struct nla_policy devlink_dl_rate_tc_bws_nl_policy[DEVLINK_RATE_TC_ATTR_BW + 1];
|
||||
extern const struct nla_policy devlink_dl_selftest_id_nl_policy[DEVLINK_ATTR_SELFTEST_ID_FLASH + 1];
|
||||
|
|
@ -29,12 +30,19 @@ int devlink_nl_pre_doit_port_optional(const struct genl_split_ops *ops,
|
|||
struct genl_info *info);
|
||||
int devlink_nl_pre_doit_dev_lock(const struct genl_split_ops *ops,
|
||||
struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_pre_doit_parent_dev_optional(const struct genl_split_ops *ops,
|
||||
struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
void
|
||||
devlink_nl_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
void
|
||||
devlink_nl_post_doit_dev_lock(const struct genl_split_ops *ops,
|
||||
struct sk_buff *skb, struct genl_info *info);
|
||||
void
|
||||
devlink_nl_post_doit_parent_dev_optional(const struct genl_split_ops *ops,
|
||||
struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
|
||||
int devlink_nl_get_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int devlink_nl_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
|
||||
|
|
|
|||
|
|
@ -627,7 +627,7 @@ devlink_param_get_from_info(struct xarray *params, struct genl_info *info)
|
|||
int devlink_nl_param_get_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_param_item *param_item;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
|
@ -728,7 +728,7 @@ static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
|
|||
|
||||
int devlink_nl_param_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
|
||||
return __devlink_nl_cmd_param_set_doit(devlink, 0, &devlink->params,
|
||||
info, DEVLINK_CMD_PARAM_NEW);
|
||||
|
|
|
|||
|
|
@ -594,7 +594,7 @@ void devlink_ports_notify_unregister(struct devlink *devlink)
|
|||
|
||||
int devlink_nl_port_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
||||
|
|
@ -830,7 +830,7 @@ static int devlink_port_function_set(struct devlink_port *port,
|
|||
|
||||
int devlink_nl_port_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
|
||||
int err;
|
||||
|
||||
if (info->attrs[DEVLINK_ATTR_PORT_TYPE]) {
|
||||
|
|
@ -856,8 +856,8 @@ int devlink_nl_port_set_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
|
||||
int devlink_nl_port_split_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
u32 count;
|
||||
|
||||
if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_PORT_SPLIT_COUNT))
|
||||
|
|
@ -887,8 +887,8 @@ int devlink_nl_port_split_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
|
||||
int devlink_nl_port_unsplit_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
|
||||
if (!devlink_port->ops->port_unsplit)
|
||||
return -EOPNOTSUPP;
|
||||
|
|
@ -899,7 +899,7 @@ int devlink_nl_port_new_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
{
|
||||
struct netlink_ext_ack *extack = info->extack;
|
||||
struct devlink_port_new_attrs new_attrs = {};
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_port *devlink_port;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
|
@ -961,9 +961,9 @@ int devlink_nl_port_new_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
|
||||
int devlink_nl_port_del_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
|
||||
struct netlink_ext_ack *extack = info->extack;
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
|
||||
if (!devlink_port->ops->port_del)
|
||||
return -EOPNOTSUPP;
|
||||
|
|
|
|||
|
|
@ -30,13 +30,53 @@ devlink_rate_leaf_get_from_info(struct devlink *devlink, struct genl_info *info)
|
|||
return devlink_rate ?: ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
/* Repeatedly walks the nested devlink chain while cross device rate nodes are
|
||||
* supported and finds the topmost instance where rates should be stored.
|
||||
* That instance is locked, referenced and returned.
|
||||
* When cross device rate nodes aren't supported the original devlink instance
|
||||
* is returned.
|
||||
*/
|
||||
static struct devlink *devl_rate_lock(struct devlink *devlink)
|
||||
{
|
||||
struct devlink *rate_devlink = devlink, *parent;
|
||||
|
||||
devl_assert_locked(devlink);
|
||||
|
||||
while (rate_devlink->ops &&
|
||||
rate_devlink->ops->supported_cross_device_rate_nodes) {
|
||||
parent = devlink_nested_in_get_lock(rate_devlink);
|
||||
if (!parent)
|
||||
break;
|
||||
if (rate_devlink != devlink) {
|
||||
/* Unlock intermediate instances. */
|
||||
devl_unlock(rate_devlink);
|
||||
devlink_put(rate_devlink);
|
||||
}
|
||||
rate_devlink = parent;
|
||||
}
|
||||
return rate_devlink;
|
||||
}
|
||||
|
||||
/* Unlocks and puts 'rate devlink' if different than 'devlink'. */
|
||||
static void devl_rate_unlock(struct devlink *devlink,
|
||||
struct devlink *rate_devlink)
|
||||
{
|
||||
if (devlink == rate_devlink)
|
||||
return;
|
||||
|
||||
devl_unlock(rate_devlink);
|
||||
devlink_put(rate_devlink);
|
||||
}
|
||||
|
||||
static struct devlink_rate *
|
||||
devlink_rate_node_get_by_name(struct devlink *devlink, const char *node_name)
|
||||
devlink_rate_node_get_by_name(struct devlink *rate_devlink,
|
||||
struct devlink *devlink, const char *node_name)
|
||||
{
|
||||
struct devlink_rate *devlink_rate;
|
||||
|
||||
list_for_each_entry(devlink_rate, &devlink->rate_list, list) {
|
||||
if (devlink_rate_is_node(devlink_rate) &&
|
||||
list_for_each_entry(devlink_rate, &rate_devlink->rate_list, list) {
|
||||
if (devlink_rate->devlink == devlink &&
|
||||
devlink_rate_is_node(devlink_rate) &&
|
||||
!strcmp(node_name, devlink_rate->name))
|
||||
return devlink_rate;
|
||||
}
|
||||
|
|
@ -44,7 +84,8 @@ devlink_rate_node_get_by_name(struct devlink *devlink, const char *node_name)
|
|||
}
|
||||
|
||||
static struct devlink_rate *
|
||||
devlink_rate_node_get_from_attrs(struct devlink *devlink, struct nlattr **attrs)
|
||||
devlink_rate_node_get_from_attrs(struct devlink *rate_devlink,
|
||||
struct devlink *devlink, struct nlattr **attrs)
|
||||
{
|
||||
const char *rate_node_name;
|
||||
size_t len;
|
||||
|
|
@ -57,24 +98,30 @@ devlink_rate_node_get_from_attrs(struct devlink *devlink, struct nlattr **attrs)
|
|||
if (!len || strspn(rate_node_name, "0123456789") == len)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return devlink_rate_node_get_by_name(devlink, rate_node_name);
|
||||
return devlink_rate_node_get_by_name(rate_devlink, devlink,
|
||||
rate_node_name);
|
||||
}
|
||||
|
||||
static struct devlink_rate *
|
||||
devlink_rate_node_get_from_info(struct devlink *devlink, struct genl_info *info)
|
||||
devlink_rate_node_get_from_info(struct devlink *rate_devlink,
|
||||
struct devlink *devlink,
|
||||
struct genl_info *info)
|
||||
{
|
||||
return devlink_rate_node_get_from_attrs(devlink, info->attrs);
|
||||
return devlink_rate_node_get_from_attrs(rate_devlink, devlink,
|
||||
info->attrs);
|
||||
}
|
||||
|
||||
static struct devlink_rate *
|
||||
devlink_rate_get_from_info(struct devlink *devlink, struct genl_info *info)
|
||||
devlink_rate_get_from_info(struct devlink *rate_devlink,
|
||||
struct devlink *devlink, struct genl_info *info)
|
||||
{
|
||||
struct nlattr **attrs = info->attrs;
|
||||
|
||||
if (attrs[DEVLINK_ATTR_PORT_INDEX])
|
||||
return devlink_rate_leaf_get_from_info(devlink, info);
|
||||
else if (attrs[DEVLINK_ATTR_RATE_NODE_NAME])
|
||||
return devlink_rate_node_get_from_info(devlink, info);
|
||||
return devlink_rate_node_get_from_info(rate_devlink, devlink,
|
||||
info);
|
||||
else
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
|
@ -102,6 +149,25 @@ static int devlink_rate_put_tc_bws(struct sk_buff *msg, u32 *tc_bw)
|
|||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static int devlink_nl_rate_parent_fill(struct sk_buff *msg,
|
||||
struct devlink_rate *devlink_rate)
|
||||
{
|
||||
struct devlink_rate *parent = devlink_rate->parent;
|
||||
struct devlink *devlink = parent->devlink;
|
||||
|
||||
if (nla_put_string(msg, DEVLINK_ATTR_RATE_PARENT_NODE_NAME,
|
||||
parent->name))
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (devlink != devlink_rate->devlink &&
|
||||
devlink_nl_put_nested_handle(msg,
|
||||
devlink_net(devlink_rate->devlink),
|
||||
devlink, DEVLINK_ATTR_PARENT_DEV))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int devlink_nl_rate_fill(struct sk_buff *msg,
|
||||
struct devlink_rate *devlink_rate,
|
||||
enum devlink_command cmd, u32 portid, u32 seq,
|
||||
|
|
@ -146,10 +212,9 @@ static int devlink_nl_rate_fill(struct sk_buff *msg,
|
|||
devlink_rate->tx_weight))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (devlink_rate->parent)
|
||||
if (nla_put_string(msg, DEVLINK_ATTR_RATE_PARENT_NODE_NAME,
|
||||
devlink_rate->parent->name))
|
||||
goto nla_put_failure;
|
||||
if (devlink_rate->parent &&
|
||||
devlink_nl_rate_parent_fill(msg, devlink_rate))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (devlink_rate_put_tc_bws(msg, devlink_rate->tc_bw))
|
||||
goto nla_put_failure;
|
||||
|
|
@ -190,17 +255,25 @@ static void devlink_rate_notify(struct devlink_rate *devlink_rate,
|
|||
void devlink_rates_notify_register(struct devlink *devlink)
|
||||
{
|
||||
struct devlink_rate *rate_node;
|
||||
struct devlink *rate_devlink;
|
||||
|
||||
list_for_each_entry(rate_node, &devlink->rate_list, list)
|
||||
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);
|
||||
rate_devlink = devl_rate_lock(devlink);
|
||||
list_for_each_entry(rate_node, &rate_devlink->rate_list, list)
|
||||
if (rate_node->devlink == devlink)
|
||||
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);
|
||||
devl_rate_unlock(devlink, rate_devlink);
|
||||
}
|
||||
|
||||
void devlink_rates_notify_unregister(struct devlink *devlink)
|
||||
{
|
||||
struct devlink_rate *rate_node;
|
||||
struct devlink *rate_devlink;
|
||||
|
||||
list_for_each_entry_reverse(rate_node, &devlink->rate_list, list)
|
||||
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_DEL);
|
||||
rate_devlink = devl_rate_lock(devlink);
|
||||
list_for_each_entry_reverse(rate_node, &rate_devlink->rate_list, list)
|
||||
if (rate_node->devlink == devlink)
|
||||
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_DEL);
|
||||
devl_rate_unlock(devlink, rate_devlink);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -209,17 +282,20 @@ devlink_nl_rate_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
|
|||
{
|
||||
struct devlink_nl_dump_state *state = devlink_dump_state(cb);
|
||||
struct devlink_rate *devlink_rate;
|
||||
struct devlink *rate_devlink;
|
||||
int idx = 0;
|
||||
int err = 0;
|
||||
|
||||
list_for_each_entry(devlink_rate, &devlink->rate_list, list) {
|
||||
rate_devlink = devl_rate_lock(devlink);
|
||||
list_for_each_entry(devlink_rate, &rate_devlink->rate_list, list) {
|
||||
enum devlink_command cmd = DEVLINK_CMD_RATE_NEW;
|
||||
u32 id = NETLINK_CB(cb->skb).portid;
|
||||
|
||||
if (idx < state->idx) {
|
||||
if (idx < state->idx || devlink_rate->devlink != devlink) {
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
err = devlink_nl_rate_fill(msg, devlink_rate, cmd, id,
|
||||
cb->nlh->nlmsg_seq, flags, NULL);
|
||||
if (err) {
|
||||
|
|
@ -228,6 +304,7 @@ devlink_nl_rate_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
|
|||
}
|
||||
idx++;
|
||||
}
|
||||
devl_rate_unlock(devlink, rate_devlink);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -239,28 +316,38 @@ int devlink_nl_rate_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
|
|||
|
||||
int devlink_nl_rate_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *rate_devlink, *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_rate *devlink_rate;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
||||
devlink_rate = devlink_rate_get_from_info(devlink, info);
|
||||
if (IS_ERR(devlink_rate))
|
||||
return PTR_ERR(devlink_rate);
|
||||
rate_devlink = devl_rate_lock(devlink);
|
||||
devlink_rate = devlink_rate_get_from_info(rate_devlink, devlink, info);
|
||||
if (IS_ERR(devlink_rate)) {
|
||||
err = PTR_ERR(devlink_rate);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
if (!msg) {
|
||||
err = -ENOMEM;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = devlink_nl_rate_fill(msg, devlink_rate, DEVLINK_CMD_RATE_NEW,
|
||||
info->snd_portid, info->snd_seq, 0,
|
||||
info->extack);
|
||||
if (err) {
|
||||
nlmsg_free(msg);
|
||||
return err;
|
||||
}
|
||||
if (err)
|
||||
goto err_fill;
|
||||
|
||||
devl_rate_unlock(devlink, rate_devlink);
|
||||
return genlmsg_reply(msg, info);
|
||||
|
||||
err_fill:
|
||||
nlmsg_free(msg);
|
||||
unlock:
|
||||
devl_rate_unlock(devlink, rate_devlink);
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -277,16 +364,18 @@ devlink_rate_is_parent_node(struct devlink_rate *devlink_rate,
|
|||
|
||||
static int
|
||||
devlink_nl_rate_parent_node_set(struct devlink_rate *devlink_rate,
|
||||
struct devlink *rate_devlink,
|
||||
struct genl_info *info,
|
||||
struct nlattr *nla_parent)
|
||||
{
|
||||
struct devlink *devlink = devlink_rate->devlink;
|
||||
struct devlink *devlink = devlink_rate->devlink, *parent_devlink;
|
||||
const char *parent_name = nla_data(nla_parent);
|
||||
const struct devlink_ops *ops = devlink->ops;
|
||||
size_t len = strlen(parent_name);
|
||||
struct devlink_rate *parent;
|
||||
int err = -EOPNOTSUPP;
|
||||
|
||||
parent_devlink = devlink_nl_ctx(info)->parent_devlink ? : devlink;
|
||||
parent = devlink_rate->parent;
|
||||
|
||||
if (parent && !len) {
|
||||
|
|
@ -304,7 +393,14 @@ devlink_nl_rate_parent_node_set(struct devlink_rate *devlink_rate,
|
|||
refcount_dec(&parent->refcnt);
|
||||
devlink_rate->parent = NULL;
|
||||
} else if (len) {
|
||||
parent = devlink_rate_node_get_by_name(devlink, parent_name);
|
||||
/* parent_devlink (when different than devlink) isn't locked,
|
||||
* but the rate node devlink instance is, so nobody from the
|
||||
* same group of devices sharing rates could change the used
|
||||
* fields or unregister the parent.
|
||||
*/
|
||||
parent = devlink_rate_node_get_by_name(rate_devlink,
|
||||
parent_devlink,
|
||||
parent_name);
|
||||
if (IS_ERR(parent))
|
||||
return -ENODEV;
|
||||
|
||||
|
|
@ -423,6 +519,7 @@ static int devlink_nl_rate_tc_bw_set(struct devlink_rate *devlink_rate,
|
|||
}
|
||||
|
||||
static int devlink_nl_rate_set(struct devlink_rate *devlink_rate,
|
||||
struct devlink *rate_devlink,
|
||||
const struct devlink_ops *ops,
|
||||
struct genl_info *info)
|
||||
{
|
||||
|
|
@ -497,7 +594,8 @@ static int devlink_nl_rate_set(struct devlink_rate *devlink_rate,
|
|||
*/
|
||||
nla_parent = attrs[DEVLINK_ATTR_RATE_PARENT_NODE_NAME];
|
||||
if (nla_parent) {
|
||||
err = devlink_nl_rate_parent_node_set(devlink_rate, info,
|
||||
err = devlink_nl_rate_parent_node_set(devlink_rate,
|
||||
rate_devlink, info,
|
||||
nla_parent);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
@ -588,31 +686,51 @@ static bool devlink_rate_set_ops_supported(const struct devlink_ops *ops,
|
|||
|
||||
int devlink_nl_rate_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_nl_ctx *ctx = devlink_nl_ctx(info);
|
||||
struct devlink *devlink = ctx->devlink;
|
||||
struct devlink_rate *devlink_rate;
|
||||
const struct devlink_ops *ops;
|
||||
struct devlink *rate_devlink;
|
||||
int err;
|
||||
|
||||
devlink_rate = devlink_rate_get_from_info(devlink, info);
|
||||
if (IS_ERR(devlink_rate))
|
||||
return PTR_ERR(devlink_rate);
|
||||
rate_devlink = devl_rate_lock(devlink);
|
||||
devlink_rate = devlink_rate_get_from_info(rate_devlink, devlink, info);
|
||||
if (IS_ERR(devlink_rate)) {
|
||||
err = PTR_ERR(devlink_rate);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ops = devlink->ops;
|
||||
if (!ops || !devlink_rate_set_ops_supported(ops, info, devlink_rate->type))
|
||||
return -EOPNOTSUPP;
|
||||
if (!ops ||
|
||||
!devlink_rate_set_ops_supported(ops, info, devlink_rate->type)) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = devlink_nl_rate_set(devlink_rate, ops, info);
|
||||
if (ctx->parent_devlink && ctx->parent_devlink != devlink &&
|
||||
!ops->supported_cross_device_rate_nodes) {
|
||||
NL_SET_ERR_MSG(info->extack,
|
||||
"Cross-device rate parents aren't supported");
|
||||
err = -EOPNOTSUPP;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = devlink_nl_rate_set(devlink_rate, rate_devlink, ops, info);
|
||||
|
||||
if (!err)
|
||||
devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_NEW);
|
||||
unlock:
|
||||
devl_rate_unlock(devlink, rate_devlink);
|
||||
return err;
|
||||
}
|
||||
|
||||
int devlink_nl_rate_new_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_nl_ctx *ctx = devlink_nl_ctx(info);
|
||||
struct devlink *devlink = ctx->devlink;
|
||||
struct devlink_rate *rate_node;
|
||||
const struct devlink_ops *ops;
|
||||
struct devlink *rate_devlink;
|
||||
int err;
|
||||
|
||||
ops = devlink->ops;
|
||||
|
|
@ -624,15 +742,29 @@ int devlink_nl_rate_new_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
if (!devlink_rate_set_ops_supported(ops, info, DEVLINK_RATE_TYPE_NODE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
rate_node = devlink_rate_node_get_from_attrs(devlink, info->attrs);
|
||||
if (!IS_ERR(rate_node))
|
||||
return -EEXIST;
|
||||
else if (rate_node == ERR_PTR(-EINVAL))
|
||||
return -EINVAL;
|
||||
if (ctx->parent_devlink && ctx->parent_devlink != devlink &&
|
||||
!ops->supported_cross_device_rate_nodes) {
|
||||
NL_SET_ERR_MSG(info->extack,
|
||||
"Cross-device rate parents aren't supported");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
rate_devlink = devl_rate_lock(devlink);
|
||||
rate_node = devlink_rate_node_get_from_attrs(rate_devlink, devlink,
|
||||
info->attrs);
|
||||
if (!IS_ERR(rate_node)) {
|
||||
err = -EEXIST;
|
||||
goto unlock;
|
||||
} else if (rate_node == ERR_PTR(-EINVAL)) {
|
||||
err = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
rate_node = kzalloc_obj(*rate_node);
|
||||
if (!rate_node)
|
||||
return -ENOMEM;
|
||||
if (!rate_node) {
|
||||
err = -ENOMEM;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
rate_node->devlink = devlink;
|
||||
rate_node->type = DEVLINK_RATE_TYPE_NODE;
|
||||
|
|
@ -646,13 +778,14 @@ int devlink_nl_rate_new_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
if (err)
|
||||
goto err_node_new;
|
||||
|
||||
err = devlink_nl_rate_set(rate_node, ops, info);
|
||||
err = devlink_nl_rate_set(rate_node, rate_devlink, ops, info);
|
||||
if (err)
|
||||
goto err_rate_set;
|
||||
|
||||
refcount_set(&rate_node->refcnt, 1);
|
||||
list_add(&rate_node->list, &devlink->rate_list);
|
||||
list_add(&rate_node->list, &rate_devlink->rate_list);
|
||||
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);
|
||||
devl_rate_unlock(devlink, rate_devlink);
|
||||
return 0;
|
||||
|
||||
err_rate_set:
|
||||
|
|
@ -661,22 +794,29 @@ int devlink_nl_rate_new_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
kfree(rate_node->name);
|
||||
err_strdup:
|
||||
kfree(rate_node);
|
||||
unlock:
|
||||
devl_rate_unlock(devlink, rate_devlink);
|
||||
return err;
|
||||
}
|
||||
|
||||
int devlink_nl_rate_del_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *rate_devlink, *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_rate *rate_node;
|
||||
int err;
|
||||
|
||||
rate_node = devlink_rate_node_get_from_info(devlink, info);
|
||||
if (IS_ERR(rate_node))
|
||||
return PTR_ERR(rate_node);
|
||||
rate_devlink = devl_rate_lock(devlink);
|
||||
rate_node = devlink_rate_node_get_from_info(rate_devlink, devlink,
|
||||
info);
|
||||
if (IS_ERR(rate_node)) {
|
||||
err = PTR_ERR(rate_node);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (refcount_read(&rate_node->refcnt) > 1) {
|
||||
NL_SET_ERR_MSG(info->extack, "Node has children. Cannot delete node.");
|
||||
return -EBUSY;
|
||||
err = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_DEL);
|
||||
|
|
@ -687,6 +827,8 @@ int devlink_nl_rate_del_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
list_del(&rate_node->list);
|
||||
kfree(rate_node->name);
|
||||
kfree(rate_node);
|
||||
unlock:
|
||||
devl_rate_unlock(devlink, rate_devlink);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -695,14 +837,20 @@ int devlink_rates_check(struct devlink *devlink,
|
|||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct devlink_rate *devlink_rate;
|
||||
struct devlink *rate_devlink;
|
||||
int err = 0;
|
||||
|
||||
list_for_each_entry(devlink_rate, &devlink->rate_list, list)
|
||||
if (!rate_filter || rate_filter(devlink_rate)) {
|
||||
rate_devlink = devl_rate_lock(devlink);
|
||||
list_for_each_entry(devlink_rate, &rate_devlink->rate_list, list)
|
||||
if (devlink_rate->devlink == devlink &&
|
||||
(!rate_filter || rate_filter(devlink_rate))) {
|
||||
if (extack)
|
||||
NL_SET_ERR_MSG(extack, "Rate node(s) exists.");
|
||||
return -EBUSY;
|
||||
err = -EBUSY;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
devl_rate_unlock(devlink, rate_devlink);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -719,14 +867,21 @@ devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name,
|
|||
struct devlink_rate *parent)
|
||||
{
|
||||
struct devlink_rate *rate_node;
|
||||
struct devlink *rate_devlink;
|
||||
|
||||
rate_node = devlink_rate_node_get_by_name(devlink, node_name);
|
||||
if (!IS_ERR(rate_node))
|
||||
return ERR_PTR(-EEXIST);
|
||||
rate_devlink = devl_rate_lock(devlink);
|
||||
rate_node = devlink_rate_node_get_by_name(rate_devlink, devlink,
|
||||
node_name);
|
||||
if (!IS_ERR(rate_node)) {
|
||||
rate_node = ERR_PTR(-EEXIST);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
rate_node = kzalloc_obj(*rate_node);
|
||||
if (!rate_node)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (!rate_node) {
|
||||
rate_node = ERR_PTR(-ENOMEM);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
rate_node->type = DEVLINK_RATE_TYPE_NODE;
|
||||
rate_node->devlink = devlink;
|
||||
|
|
@ -735,7 +890,8 @@ devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name,
|
|||
rate_node->name = kstrdup(node_name, GFP_KERNEL);
|
||||
if (!rate_node->name) {
|
||||
kfree(rate_node);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
rate_node = ERR_PTR(-ENOMEM);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
|
|
@ -744,8 +900,10 @@ devl_rate_node_create(struct devlink *devlink, void *priv, char *node_name,
|
|||
}
|
||||
|
||||
refcount_set(&rate_node->refcnt, 1);
|
||||
list_add(&rate_node->list, &devlink->rate_list);
|
||||
list_add(&rate_node->list, &rate_devlink->rate_list);
|
||||
devlink_rate_notify(rate_node, DEVLINK_CMD_RATE_NEW);
|
||||
unlock:
|
||||
devl_rate_unlock(devlink, rate_devlink);
|
||||
return rate_node;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_rate_node_create);
|
||||
|
|
@ -761,10 +919,10 @@ EXPORT_SYMBOL_GPL(devl_rate_node_create);
|
|||
int devl_rate_leaf_create(struct devlink_port *devlink_port, void *priv,
|
||||
struct devlink_rate *parent)
|
||||
{
|
||||
struct devlink *devlink = devlink_port->devlink;
|
||||
struct devlink *rate_devlink, *devlink = devlink_port->devlink;
|
||||
struct devlink_rate *devlink_rate;
|
||||
|
||||
devl_assert_locked(devlink_port->devlink);
|
||||
devl_assert_locked(devlink);
|
||||
|
||||
if (WARN_ON(devlink_port->devlink_rate))
|
||||
return -EBUSY;
|
||||
|
|
@ -773,6 +931,7 @@ int devl_rate_leaf_create(struct devlink_port *devlink_port, void *priv,
|
|||
if (!devlink_rate)
|
||||
return -ENOMEM;
|
||||
|
||||
rate_devlink = devl_rate_lock(devlink);
|
||||
if (parent) {
|
||||
devlink_rate->parent = parent;
|
||||
refcount_inc(&devlink_rate->parent->refcnt);
|
||||
|
|
@ -782,9 +941,10 @@ int devl_rate_leaf_create(struct devlink_port *devlink_port, void *priv,
|
|||
devlink_rate->devlink = devlink;
|
||||
devlink_rate->devlink_port = devlink_port;
|
||||
devlink_rate->priv = priv;
|
||||
list_add_tail(&devlink_rate->list, &devlink->rate_list);
|
||||
list_add_tail(&devlink_rate->list, &rate_devlink->rate_list);
|
||||
devlink_port->devlink_rate = devlink_rate;
|
||||
devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_NEW);
|
||||
devl_rate_unlock(devlink, rate_devlink);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -800,16 +960,19 @@ EXPORT_SYMBOL_GPL(devl_rate_leaf_create);
|
|||
void devl_rate_leaf_destroy(struct devlink_port *devlink_port)
|
||||
{
|
||||
struct devlink_rate *devlink_rate = devlink_port->devlink_rate;
|
||||
struct devlink *rate_devlink, *devlink = devlink_port->devlink;
|
||||
|
||||
devl_assert_locked(devlink_port->devlink);
|
||||
devl_assert_locked(devlink);
|
||||
if (!devlink_rate)
|
||||
return;
|
||||
|
||||
rate_devlink = devl_rate_lock(devlink);
|
||||
devlink_rate_notify(devlink_rate, DEVLINK_CMD_RATE_DEL);
|
||||
if (devlink_rate->parent)
|
||||
refcount_dec(&devlink_rate->parent->refcnt);
|
||||
list_del(&devlink_rate->list);
|
||||
devlink_port->devlink_rate = NULL;
|
||||
devl_rate_unlock(devlink, rate_devlink);
|
||||
kfree(devlink_rate);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_rate_leaf_destroy);
|
||||
|
|
@ -818,20 +981,30 @@ EXPORT_SYMBOL_GPL(devl_rate_leaf_destroy);
|
|||
* devl_rate_nodes_destroy - destroy all devlink rate nodes on device
|
||||
* @devlink: devlink instance
|
||||
*
|
||||
* Unset parent for all rate objects and destroy all rate nodes
|
||||
* on specified device.
|
||||
* Unset parent for all rate objects involving this device and destroy all rate
|
||||
* nodes on it.
|
||||
*/
|
||||
void devl_rate_nodes_destroy(struct devlink *devlink)
|
||||
{
|
||||
const struct devlink_ops *ops = devlink->ops;
|
||||
struct devlink_rate *devlink_rate, *tmp;
|
||||
const struct devlink_ops *ops;
|
||||
struct devlink *rate_devlink;
|
||||
|
||||
devl_assert_locked(devlink);
|
||||
rate_devlink = devl_rate_lock(devlink);
|
||||
|
||||
list_for_each_entry(devlink_rate, &devlink->rate_list, list) {
|
||||
if (!devlink_rate->parent)
|
||||
list_for_each_entry(devlink_rate, &rate_devlink->rate_list, list) {
|
||||
if (!devlink_rate->parent ||
|
||||
(devlink_rate->devlink != devlink &&
|
||||
devlink_rate->parent->devlink != devlink))
|
||||
continue;
|
||||
|
||||
/* This could destroy rate objects on other devlinks in the
|
||||
* same hierarchy under 'rate_devlink'. This is safe because
|
||||
* the shared common ancestor is locked so there can be no
|
||||
* other concurrent rate operations on devlink_rate->devlink.
|
||||
*/
|
||||
ops = devlink_rate->devlink->ops;
|
||||
if (devlink_rate_is_leaf(devlink_rate))
|
||||
ops->rate_leaf_parent_set(devlink_rate, NULL, devlink_rate->priv,
|
||||
NULL, NULL);
|
||||
|
|
@ -842,13 +1015,17 @@ void devl_rate_nodes_destroy(struct devlink *devlink)
|
|||
refcount_dec(&devlink_rate->parent->refcnt);
|
||||
devlink_rate->parent = NULL;
|
||||
}
|
||||
list_for_each_entry_safe(devlink_rate, tmp, &devlink->rate_list, list) {
|
||||
if (devlink_rate_is_node(devlink_rate)) {
|
||||
ops = devlink->ops;
|
||||
list_for_each_entry_safe(devlink_rate, tmp, &rate_devlink->rate_list,
|
||||
list) {
|
||||
if (devlink_rate->devlink == devlink &&
|
||||
devlink_rate_is_node(devlink_rate)) {
|
||||
ops->rate_node_del(devlink_rate, devlink_rate->priv, NULL);
|
||||
list_del(&devlink_rate->list);
|
||||
kfree(devlink_rate->name);
|
||||
kfree(devlink_rate);
|
||||
}
|
||||
}
|
||||
devl_rate_unlock(devlink, rate_devlink);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devl_rate_nodes_destroy);
|
||||
|
|
|
|||
|
|
@ -469,7 +469,7 @@ static void devlink_region_snapshot_del(struct devlink_region *region,
|
|||
|
||||
int devlink_nl_region_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_port *port = NULL;
|
||||
struct devlink_region *region;
|
||||
const char *region_name;
|
||||
|
|
@ -588,7 +588,7 @@ int devlink_nl_region_get_dumpit(struct sk_buff *skb,
|
|||
|
||||
int devlink_nl_region_del_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_snapshot *snapshot;
|
||||
struct devlink_port *port = NULL;
|
||||
struct devlink_region *region;
|
||||
|
|
@ -633,7 +633,7 @@ int devlink_nl_region_del_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
|
||||
int devlink_nl_region_new_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_snapshot *snapshot;
|
||||
struct devlink_port *port = NULL;
|
||||
struct nlattr *snapshot_id_attr;
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ devlink_resource_validate_size(struct devlink_resource *resource, u64 size,
|
|||
|
||||
int devlink_nl_resource_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_resource *resource;
|
||||
u64 resource_id;
|
||||
u64 size;
|
||||
|
|
@ -251,8 +251,9 @@ static int devlink_resource_list_fill(struct sk_buff *skb,
|
|||
static int devlink_resource_fill(struct genl_info *info,
|
||||
enum devlink_command cmd, int flags)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_nl_ctx *ctx = devlink_nl_ctx(info);
|
||||
struct devlink *devlink = ctx->devlink;
|
||||
struct devlink_port *devlink_port;
|
||||
struct devlink_resource *resource;
|
||||
struct list_head *resource_list;
|
||||
struct nlattr *resources_attr;
|
||||
|
|
@ -263,6 +264,7 @@ static int devlink_resource_fill(struct genl_info *info,
|
|||
int i;
|
||||
int err;
|
||||
|
||||
devlink_port = ctx->devlink_port;
|
||||
resource_list = devlink_port ?
|
||||
&devlink_port->resource_list : &devlink->resource_list;
|
||||
resource = list_first_entry(resource_list,
|
||||
|
|
@ -326,10 +328,12 @@ static int devlink_resource_fill(struct genl_info *info,
|
|||
|
||||
int devlink_nl_resource_dump_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_nl_ctx *ctx = devlink_nl_ctx(info);
|
||||
struct devlink *devlink = ctx->devlink;
|
||||
struct devlink_port *devlink_port;
|
||||
struct list_head *resource_list;
|
||||
|
||||
devlink_port = ctx->devlink_port;
|
||||
if (info->attrs[DEVLINK_ATTR_PORT_INDEX] && !devlink_port)
|
||||
return -ENODEV;
|
||||
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ static int devlink_nl_sb_fill(struct sk_buff *msg, struct devlink *devlink,
|
|||
|
||||
int devlink_nl_sb_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_sb *devlink_sb;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
|
@ -306,7 +306,7 @@ static int devlink_nl_sb_pool_fill(struct sk_buff *msg, struct devlink *devlink,
|
|||
|
||||
int devlink_nl_sb_pool_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_sb *devlink_sb;
|
||||
struct sk_buff *msg;
|
||||
u16 pool_index;
|
||||
|
|
@ -415,7 +415,7 @@ static int devlink_sb_pool_set(struct devlink *devlink, unsigned int sb_index,
|
|||
|
||||
int devlink_nl_sb_pool_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
enum devlink_sb_threshold_type threshold_type;
|
||||
struct devlink_sb *devlink_sb;
|
||||
u16 pool_index;
|
||||
|
|
@ -506,7 +506,7 @@ static int devlink_nl_sb_port_pool_fill(struct sk_buff *msg,
|
|||
int devlink_nl_sb_port_pool_get_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
|
||||
struct devlink *devlink = devlink_port->devlink;
|
||||
struct devlink_sb *devlink_sb;
|
||||
struct sk_buff *msg;
|
||||
|
|
@ -624,8 +624,8 @@ static int devlink_sb_port_pool_set(struct devlink_port *devlink_port,
|
|||
int devlink_nl_sb_port_pool_set_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_sb *devlink_sb;
|
||||
u16 pool_index;
|
||||
u32 threshold;
|
||||
|
|
@ -716,7 +716,7 @@ devlink_nl_sb_tc_pool_bind_fill(struct sk_buff *msg, struct devlink *devlink,
|
|||
int devlink_nl_sb_tc_pool_bind_get_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
|
||||
struct devlink *devlink = devlink_port->devlink;
|
||||
struct devlink_sb *devlink_sb;
|
||||
struct sk_buff *msg;
|
||||
|
|
@ -864,8 +864,8 @@ static int devlink_sb_tc_pool_bind_set(struct devlink_port *devlink_port,
|
|||
int devlink_nl_sb_tc_pool_bind_set_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink_port *devlink_port = info->user_ptr[1];
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink_port *devlink_port = devlink_nl_ctx(info)->devlink_port;
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
enum devlink_sb_pool_type pool_type;
|
||||
struct devlink_sb *devlink_sb;
|
||||
u16 tc_index;
|
||||
|
|
@ -902,7 +902,7 @@ int devlink_nl_sb_tc_pool_bind_set_doit(struct sk_buff *skb,
|
|||
|
||||
int devlink_nl_sb_occ_snapshot_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
const struct devlink_ops *ops = devlink->ops;
|
||||
struct devlink_sb *devlink_sb;
|
||||
|
||||
|
|
@ -918,7 +918,7 @@ int devlink_nl_sb_occ_snapshot_doit(struct sk_buff *skb, struct genl_info *info)
|
|||
int devlink_nl_sb_occ_max_clear_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
const struct devlink_ops *ops = devlink->ops;
|
||||
struct devlink_sb *devlink_sb;
|
||||
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ static int devlink_nl_trap_fill(struct sk_buff *msg, struct devlink *devlink,
|
|||
int devlink_nl_trap_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct netlink_ext_ack *extack = info->extack;
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_trap_item *trap_item;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
|
@ -412,7 +412,7 @@ static int devlink_trap_action_set(struct devlink *devlink,
|
|||
int devlink_nl_trap_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct netlink_ext_ack *extack = info->extack;
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_trap_item *trap_item;
|
||||
|
||||
if (list_empty(&devlink->trap_list))
|
||||
|
|
@ -511,7 +511,7 @@ devlink_nl_trap_group_fill(struct sk_buff *msg, struct devlink *devlink,
|
|||
int devlink_nl_trap_group_get_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct netlink_ext_ack *extack = info->extack;
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_trap_group_item *group_item;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
|
@ -682,7 +682,7 @@ static int devlink_trap_group_set(struct devlink *devlink,
|
|||
int devlink_nl_trap_group_set_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct netlink_ext_ack *extack = info->extack;
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct devlink_trap_group_item *group_item;
|
||||
bool modified = false;
|
||||
int err;
|
||||
|
|
@ -804,7 +804,7 @@ int devlink_nl_trap_policer_get_doit(struct sk_buff *skb,
|
|||
{
|
||||
struct devlink_trap_policer_item *policer_item;
|
||||
struct netlink_ext_ack *extack = info->extack;
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
struct sk_buff *msg;
|
||||
int err;
|
||||
|
||||
|
|
@ -924,7 +924,7 @@ int devlink_nl_trap_policer_set_doit(struct sk_buff *skb,
|
|||
{
|
||||
struct devlink_trap_policer_item *policer_item;
|
||||
struct netlink_ext_ack *extack = info->extack;
|
||||
struct devlink *devlink = info->user_ptr[0];
|
||||
struct devlink *devlink = devlink_nl_ctx(info)->devlink;
|
||||
|
||||
if (list_empty(&devlink->trap_policer_list))
|
||||
return -EOPNOTSUPP;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ TEST_GEN_FILES := \
|
|||
TEST_PROGS = \
|
||||
csum.py \
|
||||
devlink_port_split.py \
|
||||
devlink_rate_cross_esw.py \
|
||||
devlink_rate_tc_bw.py \
|
||||
devmem.py \
|
||||
ethtool.sh \
|
||||
|
|
|
|||
296
tools/testing/selftests/drivers/net/hw/devlink_rate_cross_esw.py
Executable file
296
tools/testing/selftests/drivers/net/hw/devlink_rate_cross_esw.py
Executable file
|
|
@ -0,0 +1,296 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
"""
|
||||
Devlink Rate Cross-eswitch Scheduling Test Suite
|
||||
==================================================
|
||||
|
||||
Control-plane tests for cross-eswitch TX scheduling via devlink-rate.
|
||||
Validates that VFs from different PFs on the same chip can share
|
||||
rate groups using the cross-device parent-dev attribute.
|
||||
|
||||
Preconditions:
|
||||
- NETIF points to a bond device with exactly two interfaces.
|
||||
- the interfaces must be two PFs from different devices sharing the same chip.
|
||||
- (for mlx5): the two interfaces are in switchdev mode and configured in a LAG:
|
||||
- devlink dev eswitch set $DEV1 mode switchdev
|
||||
- devlink dev eswitch set $DEV2 mode switchdev
|
||||
- devlink dev param set $DEV1 name esw_multiport value 1 cmode runtime
|
||||
- devlink dev param set $DEV2 name esw_multiport value 1 cmode runtime
|
||||
- test cases will be skipped if:
|
||||
- the number of interfaces in the bond device is != 2.
|
||||
- the kernel doesn't support devlink rates.
|
||||
- the devlink API doesn't support cross-device parents (ENODEV).
|
||||
- cross-esw rate scheduling returns EOPNOTSUPP.
|
||||
"""
|
||||
|
||||
import errno
|
||||
import glob
|
||||
import os
|
||||
import time
|
||||
|
||||
from lib.py import ksft_pr, ksft_eq, ksft_run, ksft_exit
|
||||
from lib.py import KsftSkipEx, KsftFailEx
|
||||
from lib.py import NetDrvEnv, DevlinkFamily
|
||||
from lib.py import NlError
|
||||
from lib.py import cmd, defer, ip, tool
|
||||
|
||||
|
||||
# --- Discovery and setup ---
|
||||
|
||||
|
||||
def get_bond_slaves(bond_ifname):
|
||||
"""Returns sorted list of slave netdev names for a bond."""
|
||||
pattern = f"/sys/class/net/{bond_ifname}/lower_*"
|
||||
lowers = glob.glob(pattern)
|
||||
if not lowers:
|
||||
raise KsftSkipEx(f"No bond slaves for {bond_ifname}")
|
||||
slaves = []
|
||||
for path in sorted(lowers):
|
||||
name = os.path.basename(path)
|
||||
if name.startswith("lower_"):
|
||||
name = name[len("lower_"):]
|
||||
slaves.append(name)
|
||||
return slaves
|
||||
|
||||
|
||||
def discover_pfs(cfg):
|
||||
"""Discovers both PFs from bond slaves."""
|
||||
slaves = get_bond_slaves(cfg.ifname)
|
||||
if len(slaves) != 2:
|
||||
raise KsftSkipEx(f"Need 2 bond slaves, found {len(slaves)}")
|
||||
|
||||
pf0, pf1 = slaves[0], slaves[1]
|
||||
ksft_pr(f"PF0: {pf0} PF1: {pf1}")
|
||||
return pf0, pf1
|
||||
|
||||
|
||||
def get_pci_addr(ifname):
|
||||
"""Resolves PCI address for a network interface."""
|
||||
return os.path.basename(os.path.realpath(f"/sys/class/net/{ifname}/device"))
|
||||
|
||||
|
||||
def get_vf_port_index(pf_pci):
|
||||
"""Finds devlink port-index for vf0 under pf_pci."""
|
||||
ports = tool("devlink", "port show", json=True)["port"]
|
||||
for port_name, props in ports.items():
|
||||
if port_name.startswith(f"pci/{pf_pci}/") and props.get("vfnum") == 0:
|
||||
return int(port_name.split("/")[-1])
|
||||
raise KsftSkipEx(f"VF port not found for {pf_pci}")
|
||||
|
||||
|
||||
def cleanup_esw(pf):
|
||||
"""Removes VFs if created by tests."""
|
||||
cmd(f"echo 0 > /sys/class/net/{pf}/device/sriov_numvfs", shell=True, fail=False)
|
||||
|
||||
|
||||
def setup_esw(pf):
|
||||
"""Creates 1 VF on 'pf'."""
|
||||
path = f"/sys/class/net/{pf}/device/sriov_numvfs"
|
||||
cmd(f"echo 0 > {path}", shell=True)
|
||||
cmd(f"echo 1 > {path}", shell=True)
|
||||
defer(cleanup_esw, pf)
|
||||
time.sleep(2)
|
||||
|
||||
vf_dir = f"/sys/class/net/{pf}/device/virtfn0/net"
|
||||
entries = os.listdir(vf_dir) if os.path.isdir(vf_dir) else []
|
||||
if not entries:
|
||||
raise KsftSkipEx(f"VF not found for {pf}")
|
||||
ip(f"link set dev {entries[0]} up")
|
||||
|
||||
pf_pci = get_pci_addr(pf)
|
||||
vf_idx = get_vf_port_index(pf_pci)
|
||||
ksft_pr(f"Created VF {vf_idx} on PF {pf} ({pf_pci})")
|
||||
return pf_pci, vf_idx
|
||||
|
||||
|
||||
# --- Rate operation helpers ---
|
||||
|
||||
|
||||
def rate_new(devnl, dev_pci, node_name, **kwargs):
|
||||
"""Creates rate node."""
|
||||
params = {
|
||||
"bus-name": "pci",
|
||||
"dev-name": dev_pci,
|
||||
"rate-node-name": node_name,
|
||||
}
|
||||
params.update(kwargs)
|
||||
try:
|
||||
devnl.rate_new(params)
|
||||
except NlError as e:
|
||||
if e.error == errno.EOPNOTSUPP:
|
||||
raise KsftSkipEx("rate_new not supported") from e
|
||||
raise KsftFailEx("rate_new failed") from e
|
||||
|
||||
|
||||
def rate_get(devnl, dev_pci, node_name):
|
||||
"""Gets rate node."""
|
||||
params = {
|
||||
"bus-name": "pci",
|
||||
"dev-name": dev_pci,
|
||||
"rate-node-name": node_name,
|
||||
}
|
||||
return devnl.rate_get(params)
|
||||
|
||||
|
||||
def rate_get_leaf(devnl, dev_pci, port_index):
|
||||
"""Gets rate leaf (VF)."""
|
||||
params = {
|
||||
"bus-name": "pci",
|
||||
"dev-name": dev_pci,
|
||||
"port-index": port_index,
|
||||
}
|
||||
return devnl.rate_get(params)
|
||||
|
||||
|
||||
def rate_del(devnl, dev_pci, node_name):
|
||||
"""Deletes rate node."""
|
||||
devnl.rate_del({
|
||||
"bus-name": "pci",
|
||||
"dev-name": dev_pci,
|
||||
"rate-node-name": node_name,
|
||||
})
|
||||
|
||||
|
||||
def rate_set_leaf(devnl, dev_pci, port_index, **kwargs):
|
||||
"""Sets rate attributes on a leaf (VF)."""
|
||||
params = {
|
||||
"bus-name": "pci",
|
||||
"dev-name": dev_pci,
|
||||
"port-index": port_index,
|
||||
}
|
||||
params.update(kwargs)
|
||||
try:
|
||||
devnl.rate_set(params)
|
||||
except NlError as e:
|
||||
if e.error == errno.EOPNOTSUPP:
|
||||
raise KsftSkipEx("rate_set not supported") from e
|
||||
raise KsftFailEx("rate_set failed") from e
|
||||
|
||||
|
||||
def rate_set_leaf_parent(devnl, dev_pci, port_index,
|
||||
parent_name, parent_dev_pci=None):
|
||||
"""Sets a leaf's parent, optionally cross-esw."""
|
||||
params = {
|
||||
"bus-name": "pci",
|
||||
"dev-name": dev_pci,
|
||||
"port-index": port_index,
|
||||
"rate-parent-node-name": parent_name,
|
||||
}
|
||||
if parent_dev_pci:
|
||||
params["parent-dev"] = {
|
||||
"bus-name": "pci",
|
||||
"dev-name": parent_dev_pci,
|
||||
}
|
||||
try:
|
||||
devnl.rate_set(params)
|
||||
except NlError as e:
|
||||
if e.error == errno.EOPNOTSUPP:
|
||||
raise KsftSkipEx("rate_set not supported") from e
|
||||
if parent_dev_pci and e.error == errno.ENODEV:
|
||||
raise KsftSkipEx("Cross-esw scheduling not supported") from e
|
||||
raise KsftFailEx("rate_set failed") from e
|
||||
|
||||
|
||||
def rate_clear_leaf_parent(devnl, dev_pci, port_index):
|
||||
"""Clears a leaf's parent."""
|
||||
rate_set_leaf_parent(devnl, dev_pci, port_index, "")
|
||||
|
||||
|
||||
def rate_set_node(devnl, dev_pci, node_name, **kwargs):
|
||||
"""Sets rate attributes on a node."""
|
||||
params = {
|
||||
"bus-name": "pci",
|
||||
"dev-name": dev_pci,
|
||||
"rate-node-name": node_name,
|
||||
}
|
||||
params.update(kwargs)
|
||||
devnl.rate_set(params)
|
||||
|
||||
|
||||
# --- Test cases ---
|
||||
|
||||
|
||||
def test_same_esw_parent(cfg):
|
||||
"""Assigns PF0's VF to PF0's group (same esw baseline)."""
|
||||
pf0, _ = discover_pfs(cfg)
|
||||
pf0_pci, vf0_idx = setup_esw(pf0)
|
||||
|
||||
rate_new(cfg.devnl, pf0_pci, "group0")
|
||||
defer(rate_del, cfg.devnl, pf0_pci, "group0")
|
||||
ksft_pr("rate-new succeeded")
|
||||
|
||||
rate_set_leaf_parent(cfg.devnl, pf0_pci, vf0_idx, "group0")
|
||||
defer(rate_clear_leaf_parent, cfg.devnl, pf0_pci, vf0_idx)
|
||||
|
||||
ksft_pr("Same-esw parent assignment succeeded")
|
||||
|
||||
|
||||
def test_cross_esw_parent(cfg):
|
||||
"""Sets cross-esw parent, then clear it."""
|
||||
pf0, pf1 = discover_pfs(cfg)
|
||||
pf0_pci, _ = setup_esw(pf0)
|
||||
pf1_pci, vf1_idx = setup_esw(pf1)
|
||||
|
||||
rate_new(cfg.devnl, pf0_pci, "group1")
|
||||
defer(rate_del, cfg.devnl, pf0_pci, "group1")
|
||||
ksft_pr("rate-new succeeded")
|
||||
|
||||
rate_set_leaf_parent(cfg.devnl, pf1_pci, vf1_idx,
|
||||
"group1", parent_dev_pci=pf0_pci)
|
||||
defer(rate_clear_leaf_parent, cfg.devnl, pf1_pci, vf1_idx)
|
||||
|
||||
ksft_pr("Cross-esw parent set and clear succeeded")
|
||||
|
||||
|
||||
def test_tx_rates_on_cross_esw(cfg):
|
||||
"""Sets tx_max on group and tx_share on leaves in a cross-esw setup."""
|
||||
pf0, pf1 = discover_pfs(cfg)
|
||||
pf0_pci, vf0_idx = setup_esw(pf0)
|
||||
pf1_pci, vf1_idx = setup_esw(pf1)
|
||||
|
||||
rate_new(cfg.devnl, pf0_pci, "group2", **{"rate-tx-max": 10000000})
|
||||
defer(rate_del, cfg.devnl, pf0_pci, "group2")
|
||||
ksft_pr("rate-new succeeded")
|
||||
|
||||
rate_set_leaf_parent(cfg.devnl, pf1_pci, vf1_idx,
|
||||
"group2", parent_dev_pci=pf0_pci)
|
||||
defer(rate_clear_leaf_parent, cfg.devnl, pf1_pci, vf1_idx)
|
||||
ksft_pr("set parent cross-esw succeeded")
|
||||
|
||||
rate_set_leaf_parent(cfg.devnl, pf0_pci, vf0_idx, "group2")
|
||||
defer(rate_clear_leaf_parent, cfg.devnl, pf0_pci, vf0_idx)
|
||||
ksft_pr("set parent same esw succeeded")
|
||||
|
||||
rate_set_leaf(cfg.devnl, pf0_pci, vf0_idx, **{"rate-tx-share": 1000000})
|
||||
rate = rate_get_leaf(cfg.devnl, pf0_pci, vf0_idx)
|
||||
ksft_eq(rate["rate-tx-share"], 1000000)
|
||||
rate_set_leaf(cfg.devnl, pf1_pci, vf1_idx, **{"rate-tx-share": 2000000})
|
||||
rate = rate_get_leaf(cfg.devnl, pf1_pci, vf1_idx)
|
||||
ksft_eq(rate["rate-tx-share"], 2000000)
|
||||
rate_set_node(cfg.devnl, pf0_pci, "group2", **{"rate-tx-max": 250000000})
|
||||
rate = rate_get(cfg.devnl, pf0_pci, "group2")
|
||||
ksft_eq(rate["rate-tx-max"], 250000000)
|
||||
|
||||
ksft_pr("tx_max and tx_share set on cross-esw group")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Main function."""
|
||||
|
||||
with NetDrvEnv(__file__, nsim_test=False) as cfg:
|
||||
cfg.devnl = DevlinkFamily()
|
||||
|
||||
ksft_run(
|
||||
cases=[
|
||||
test_same_esw_parent,
|
||||
test_cross_esw_parent,
|
||||
test_tx_rates_on_cross_esw,
|
||||
],
|
||||
args=(cfg,),
|
||||
)
|
||||
ksft_exit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user