ethtool: provide customized dim profile management

The NetDIM library, currently leveraged by an array of NICs, delivers
excellent acceleration benefits. Nevertheless, NICs vary significantly
in their dim profile list prerequisites.

Specifically, virtio-net backends may present diverse sw or hw device
implementation, making a one-size-fits-all parameter list impractical.
On Alibaba Cloud, the virtio DPU's performance under the default DIM
profile falls short of expectations, partly due to a mismatch in
parameter configuration.

I also noticed that ice/idpf/ena and other NICs have customized
profilelist or placed some restrictions on dim capabilities.

Motivated by this, I tried adding new params for "ethtool -C" that provides
a per-device control to modify and access a device's interrupt parameters.

Usage
========
The target NIC is named ethx.

Assume that ethx only declares support for rx profile setting
(with DIM_PROFILE_RX flag set in profile_flags) and supports modification
of usec and pkt fields.

1. Query the currently customized list of the device

$ ethtool -c ethx
...
rx-profile:
{.usec =   1, .pkts = 256, .comps = n/a,},
{.usec =   8, .pkts = 256, .comps = n/a,},
{.usec =  64, .pkts = 256, .comps = n/a,},
{.usec = 128, .pkts = 256, .comps = n/a,},
{.usec = 256, .pkts = 256, .comps = n/a,}
tx-profile:   n/a

2. Tune
$ ethtool -C ethx rx-profile 1,1,n_2,n,n_3,3,n_4,4,n_n,5,n
"n" means do not modify this field.
$ ethtool -c ethx
...
rx-profile:
{.usec =   1, .pkts =   1, .comps = n/a,},
{.usec =   2, .pkts = 256, .comps = n/a,},
{.usec =   3, .pkts =   3, .comps = n/a,},
{.usec =   4, .pkts =   4, .comps = n/a,},
{.usec = 256, .pkts =   5, .comps = n/a,}
tx-profile:   n/a

3. Hint
If the device does not support some type of customized dim profiles,
the corresponding "n/a" will display.

If the "n/a" field is being modified, -EOPNOTSUPP will be reported.

Signed-off-by: Heng Qi <hengqi@linux.alibaba.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20240621101353.107425-4-hengqi@linux.alibaba.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Heng Qi 2024-06-21 18:13:51 +08:00 committed by Jakub Kicinski
parent b65e697a7c
commit f750dfe825
10 changed files with 509 additions and 3 deletions

View File

@ -414,6 +414,26 @@ attribute-sets:
name: combined-count
type: u32
-
name: irq-moderation
attributes:
-
name: usec
type: u32
-
name: pkts
type: u32
-
name: comps
type: u32
-
name: profile
attributes:
-
name: irq-moderation
type: nest
multi-attr: true
nested-attributes: irq-moderation
-
name: coalesce
attributes:
@ -502,6 +522,15 @@ attribute-sets:
-
name: tx-aggr-time-usecs
type: u32
-
name: rx-profile
type: nest
nested-attributes: profile
-
name: tx-profile
type: nest
nested-attributes: profile
-
name: pause-stat
attributes:
@ -1325,6 +1354,8 @@ operations:
- tx-aggr-max-bytes
- tx-aggr-max-frames
- tx-aggr-time-usecs
- rx-profile
- tx-profile
dump: *coalesce-get-op
-
name: coalesce-set

View File

@ -1033,6 +1033,8 @@ Kernel response contents:
``ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES`` u32 max aggr size, Tx
``ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES`` u32 max aggr packets, Tx
``ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS`` u32 time (us), aggr, Tx
``ETHTOOL_A_COALESCE_RX_PROFILE`` nested profile of DIM, Rx
``ETHTOOL_A_COALESCE_TX_PROFILE`` nested profile of DIM, Tx
=========================================== ====== =======================
Attributes are only included in reply if their value is not zero or the
@ -1062,6 +1064,10 @@ block should be sent.
This feature is mainly of interest for specific USB devices which does not cope
well with frequent small-sized URBs transmissions.
``ETHTOOL_A_COALESCE_RX_PROFILE`` and ``ETHTOOL_A_COALESCE_TX_PROFILE`` refer
to DIM parameters, see `Generic Network Dynamic Interrupt Moderation (Net DIM)
<https://www.kernel.org/doc/Documentation/networking/net_dim.rst>`_.
COALESCE_SET
============
@ -1098,6 +1104,8 @@ Request contents:
``ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES`` u32 max aggr size, Tx
``ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES`` u32 max aggr packets, Tx
``ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS`` u32 time (us), aggr, Tx
``ETHTOOL_A_COALESCE_RX_PROFILE`` nested profile of DIM, Rx
``ETHTOOL_A_COALESCE_TX_PROFILE`` nested profile of DIM, Tx
=========================================== ====== =======================
Request is rejected if it attributes declared as unsupported by driver (i.e.

View File

@ -169,6 +169,48 @@ usage is not complete but it should make the outline of the usage clear.
...
}
Tuning DIM
==========
Net DIM serves a range of network devices and delivers excellent acceleration
benefits. Yet, it has been observed that some preset configurations of DIM may
not align seamlessly with the varying specifications of network devices, and
this discrepancy has been identified as a factor to the suboptimal performance
outcomes of DIM-enabled network devices, related to a mismatch in profiles.
To address this issue, Net DIM introduces a per-device control to modify and
access a device's ``rx-profile`` and ``tx-profile`` parameters:
Assume that the target network device is named ethx, and ethx only declares
support for RX profile setting and supports modification of ``usec`` field
and ``pkts`` field (See the data structure:
:c:type:`struct dim_cq_moder <dim_cq_moder>`).
You can use ethtool to modify the current RX DIM profile where all
values are 64::
$ ethtool -C ethx rx-profile 1,1,n_2,2,n_3,n,n_n,4,n_n,n,n
``n`` means do not modify this field, and ``_`` separates structure
elements of the profile array.
Querying the current profiles using::
$ ethtool -c ethx
...
rx-profile:
{.usec = 1, .pkts = 1, .comps = n/a,},
{.usec = 2, .pkts = 2, .comps = n/a,},
{.usec = 3, .pkts = 64, .comps = n/a,},
{.usec = 64, .pkts = 4, .comps = n/a,},
{.usec = 64, .pkts = 64, .comps = n/a,}
tx-profile: n/a
If the network device does not support specific fields of DIM profiles,
the corresponding ``n/a`` will display. If the ``n/a`` field is being
modified, error messages will be reported.
Dynamic Interrupt Moderation (DIM) library API
==============================================

View File

@ -10,6 +10,8 @@
#include <linux/types.h>
#include <linux/workqueue.h>
struct net_device;
/* Number of DIM profiles and period mode. */
#define NET_DIM_PARAMS_NUM_PROFILES 5
#define NET_DIM_DEFAULT_RX_CQ_PKTS_FROM_EQE 256
@ -45,12 +47,45 @@
* @pkts: CQ packet counter suggestion (by DIM)
* @comps: Completion counter
* @cq_period_mode: CQ period count mode (from CQE/EQE)
* @rcu: for asynchronous kfree_rcu
*/
struct dim_cq_moder {
u16 usec;
u16 pkts;
u16 comps;
u8 cq_period_mode;
struct rcu_head rcu;
};
#define DIM_PROFILE_RX BIT(0) /* support rx profile modification */
#define DIM_PROFILE_TX BIT(1) /* support tx profile modification */
#define DIM_COALESCE_USEC BIT(0) /* support usec field modification */
#define DIM_COALESCE_PKTS BIT(1) /* support pkts field modification */
#define DIM_COALESCE_COMPS BIT(2) /* support comps field modification */
/**
* struct dim_irq_moder - Structure for irq moderation information.
* Used to collect irq moderation related information.
*
* @profile_flags: DIM_PROFILE_*
* @coal_flags: DIM_COALESCE_* for Rx and Tx
* @dim_rx_mode: Rx DIM period count mode: CQE or EQE
* @dim_tx_mode: Tx DIM period count mode: CQE or EQE
* @rx_profile: DIM profile list for Rx
* @tx_profile: DIM profile list for Tx
* @rx_dim_work: Rx DIM worker scheduled by net_dim()
* @tx_dim_work: Tx DIM worker scheduled by net_dim()
*/
struct dim_irq_moder {
u8 profile_flags;
u8 coal_flags;
u8 dim_rx_mode;
u8 dim_tx_mode;
struct dim_cq_moder __rcu *rx_profile;
struct dim_cq_moder __rcu *tx_profile;
void (*rx_dim_work)(struct work_struct *work);
void (*tx_dim_work)(struct work_struct *work);
};
/**
@ -198,6 +233,29 @@ enum dim_step_result {
DIM_ON_EDGE,
};
/**
* net_dim_init_irq_moder - collect information to initialize irq moderation
* @dev: target network device
* @profile_flags: Rx or Tx profile modification capability
* @coal_flags: irq moderation params flags
* @rx_mode: CQ period mode for Rx
* @tx_mode: CQ period mode for Tx
* @rx_dim_work: Rx worker called after dim decision
* @tx_dim_work: Tx worker called after dim decision
*
* Return: 0 on success or a negative error code.
*/
int net_dim_init_irq_moder(struct net_device *dev, u8 profile_flags,
u8 coal_flags, u8 rx_mode, u8 tx_mode,
void (*rx_dim_work)(struct work_struct *work),
void (*tx_dim_work)(struct work_struct *work));
/**
* net_dim_free_irq_moder - free fields for irq moderation
* @dev: target network device
*/
void net_dim_free_irq_moder(struct net_device *dev);
/**
* dim_on_top - check if current state is a good place to stop (top location)
* @dim: DIM context

View File

@ -284,7 +284,9 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
#define ETHTOOL_COALESCE_TX_AGGR_MAX_BYTES BIT(24)
#define ETHTOOL_COALESCE_TX_AGGR_MAX_FRAMES BIT(25)
#define ETHTOOL_COALESCE_TX_AGGR_TIME_USECS BIT(26)
#define ETHTOOL_COALESCE_ALL_PARAMS GENMASK(26, 0)
#define ETHTOOL_COALESCE_RX_PROFILE BIT(27)
#define ETHTOOL_COALESCE_TX_PROFILE BIT(28)
#define ETHTOOL_COALESCE_ALL_PARAMS GENMASK(28, 0)
#define ETHTOOL_COALESCE_USECS \
(ETHTOOL_COALESCE_RX_USECS | ETHTOOL_COALESCE_TX_USECS)

View File

@ -2402,6 +2402,9 @@ struct net_device {
/** @page_pools: page pools created for this netdevice */
struct hlist_head page_pools;
#endif
/** @irq_moder: dim parameters used if IS_ENABLED(CONFIG_DIMLIB). */
struct dim_irq_moder *irq_moder;
};
#define to_net_dev(d) container_of(d, struct net_device, dev)

View File

@ -415,12 +415,34 @@ enum {
ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES, /* u32 */
ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES, /* u32 */
ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS, /* u32 */
/* nest - _A_PROFILE_IRQ_MODERATION */
ETHTOOL_A_COALESCE_RX_PROFILE,
/* nest - _A_PROFILE_IRQ_MODERATION */
ETHTOOL_A_COALESCE_TX_PROFILE,
/* add new constants above here */
__ETHTOOL_A_COALESCE_CNT,
ETHTOOL_A_COALESCE_MAX = (__ETHTOOL_A_COALESCE_CNT - 1)
};
enum {
ETHTOOL_A_PROFILE_UNSPEC,
/* nest, _A_IRQ_MODERATION_* */
ETHTOOL_A_PROFILE_IRQ_MODERATION,
__ETHTOOL_A_PROFILE_CNT,
ETHTOOL_A_PROFILE_MAX = (__ETHTOOL_A_PROFILE_CNT - 1)
};
enum {
ETHTOOL_A_IRQ_MODERATION_UNSPEC,
ETHTOOL_A_IRQ_MODERATION_USEC, /* u32 */
ETHTOOL_A_IRQ_MODERATION_PKTS, /* u32 */
ETHTOOL_A_IRQ_MODERATION_COMPS, /* u32 */
__ETHTOOL_A_IRQ_MODERATION_CNT,
ETHTOOL_A_IRQ_MODERATION_MAX = (__ETHTOOL_A_IRQ_MODERATION_CNT - 1)
};
/* PAUSE */
enum {

View File

@ -4,6 +4,7 @@
*/
#include <linux/dim.h>
#include <linux/rtnetlink.h>
/*
* Net DIM profiles:
@ -95,6 +96,75 @@ net_dim_get_def_tx_moderation(u8 cq_period_mode)
}
EXPORT_SYMBOL(net_dim_get_def_tx_moderation);
int net_dim_init_irq_moder(struct net_device *dev, u8 profile_flags,
u8 coal_flags, u8 rx_mode, u8 tx_mode,
void (*rx_dim_work)(struct work_struct *work),
void (*tx_dim_work)(struct work_struct *work))
{
struct dim_cq_moder *rxp = NULL, *txp;
struct dim_irq_moder *moder;
int len;
dev->irq_moder = kzalloc(sizeof(*dev->irq_moder), GFP_KERNEL);
if (!dev->irq_moder)
return -ENOMEM;
moder = dev->irq_moder;
len = NET_DIM_PARAMS_NUM_PROFILES * sizeof(*moder->rx_profile);
moder->coal_flags = coal_flags;
moder->profile_flags = profile_flags;
if (profile_flags & DIM_PROFILE_RX) {
moder->rx_dim_work = rx_dim_work;
moder->dim_rx_mode = rx_mode;
rxp = kmemdup(rx_profile[rx_mode], len, GFP_KERNEL);
if (!rxp)
goto free_moder;
rcu_assign_pointer(moder->rx_profile, rxp);
}
if (profile_flags & DIM_PROFILE_TX) {
moder->tx_dim_work = tx_dim_work;
moder->dim_tx_mode = tx_mode;
txp = kmemdup(tx_profile[tx_mode], len, GFP_KERNEL);
if (!txp)
goto free_rxp;
rcu_assign_pointer(moder->tx_profile, txp);
}
return 0;
free_rxp:
kfree(rxp);
free_moder:
kfree(moder);
return -ENOMEM;
}
EXPORT_SYMBOL(net_dim_init_irq_moder);
/* RTNL lock is held. */
void net_dim_free_irq_moder(struct net_device *dev)
{
struct dim_cq_moder *rxp, *txp;
if (!dev->irq_moder)
return;
rxp = rtnl_dereference(dev->irq_moder->rx_profile);
txp = rtnl_dereference(dev->irq_moder->tx_profile);
rcu_assign_pointer(dev->irq_moder->rx_profile, NULL);
rcu_assign_pointer(dev->irq_moder->tx_profile, NULL);
kfree_rcu(rxp, rcu);
kfree_rcu(txp, rcu);
kfree(dev->irq_moder);
}
EXPORT_SYMBOL(net_dim_free_irq_moder);
static int net_dim_step(struct dim *dim)
{
if (dim->tired == (NET_DIM_PARAMS_NUM_PROFILES * 2))

View File

@ -508,6 +508,7 @@ config FAILOVER
config ETHTOOL_NETLINK
bool "Netlink interface for ethtool"
select DIMLIB
default y
help
An alternative userspace interface for ethtool based on generic

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/dim.h>
#include "netlink.h"
#include "common.h"
@ -82,6 +83,14 @@ static int coalesce_prepare_data(const struct ethnl_req_info *req_base,
static int coalesce_reply_size(const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
int modersz = nla_total_size(0) + /* _PROFILE_IRQ_MODERATION, nest */
nla_total_size(sizeof(u32)) + /* _IRQ_MODERATION_USEC */
nla_total_size(sizeof(u32)) + /* _IRQ_MODERATION_PKTS */
nla_total_size(sizeof(u32)); /* _IRQ_MODERATION_COMPS */
int total_modersz = nla_total_size(0) + /* _{R,T}X_PROFILE, nest */
modersz * NET_DIM_PARAMS_NUM_PROFILES;
return nla_total_size(sizeof(u32)) + /* _RX_USECS */
nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES */
nla_total_size(sizeof(u32)) + /* _RX_USECS_IRQ */
@ -108,7 +117,8 @@ static int coalesce_reply_size(const struct ethnl_req_info *req_base,
nla_total_size(sizeof(u8)) + /* _USE_CQE_MODE_RX */
nla_total_size(sizeof(u32)) + /* _TX_AGGR_MAX_BYTES */
nla_total_size(sizeof(u32)) + /* _TX_AGGR_MAX_FRAMES */
nla_total_size(sizeof(u32)); /* _TX_AGGR_TIME_USECS */
nla_total_size(sizeof(u32)) + /* _TX_AGGR_TIME_USECS */
total_modersz * 2; /* _{R,T}X_PROFILE */
}
static bool coalesce_put_u32(struct sk_buff *skb, u16 attr_type, u32 val,
@ -127,14 +137,84 @@ static bool coalesce_put_bool(struct sk_buff *skb, u16 attr_type, u32 val,
return nla_put_u8(skb, attr_type, !!val);
}
/**
* coalesce_put_profile - fill reply with a nla nest with four child nla nests.
* @skb: socket buffer the message is stored in
* @attr_type: nest attr type ETHTOOL_A_COALESCE_*X_PROFILE
* @profile: data passed to userspace
* @coal_flags: modifiable parameters supported by the driver
*
* Put a dim profile nest attribute. Refer to ETHTOOL_A_PROFILE_IRQ_MODERATION.
*
* Return: 0 on success or a negative error code.
*/
static int coalesce_put_profile(struct sk_buff *skb, u16 attr_type,
const struct dim_cq_moder *profile,
u8 coal_flags)
{
struct nlattr *profile_attr, *moder_attr;
int i, ret;
if (!profile || !coal_flags)
return 0;
profile_attr = nla_nest_start(skb, attr_type);
if (!profile_attr)
return -EMSGSIZE;
for (i = 0; i < NET_DIM_PARAMS_NUM_PROFILES; i++) {
moder_attr = nla_nest_start(skb,
ETHTOOL_A_PROFILE_IRQ_MODERATION);
if (!moder_attr) {
ret = -EMSGSIZE;
goto cancel_profile;
}
if (coal_flags & DIM_COALESCE_USEC) {
ret = nla_put_u32(skb, ETHTOOL_A_IRQ_MODERATION_USEC,
profile[i].usec);
if (ret)
goto cancel_moder;
}
if (coal_flags & DIM_COALESCE_PKTS) {
ret = nla_put_u32(skb, ETHTOOL_A_IRQ_MODERATION_PKTS,
profile[i].pkts);
if (ret)
goto cancel_moder;
}
if (coal_flags & DIM_COALESCE_COMPS) {
ret = nla_put_u32(skb, ETHTOOL_A_IRQ_MODERATION_COMPS,
profile[i].comps);
if (ret)
goto cancel_moder;
}
nla_nest_end(skb, moder_attr);
}
nla_nest_end(skb, profile_attr);
return 0;
cancel_moder:
nla_nest_cancel(skb, moder_attr);
cancel_profile:
nla_nest_cancel(skb, profile_attr);
return ret;
}
static int coalesce_fill_reply(struct sk_buff *skb,
const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
const struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base);
const struct kernel_ethtool_coalesce *kcoal = &data->kernel_coalesce;
struct dim_irq_moder *moder = req_base->dev->irq_moder;
const struct ethtool_coalesce *coal = &data->coalesce;
u32 supported = data->supported_params;
int ret = 0;
if (coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS,
coal->rx_coalesce_usecs, supported) ||
@ -192,11 +272,41 @@ static int coalesce_fill_reply(struct sk_buff *skb,
kcoal->tx_aggr_time_usecs, supported))
return -EMSGSIZE;
return 0;
if (!moder)
return 0;
rcu_read_lock();
if (moder->profile_flags & DIM_PROFILE_RX) {
ret = coalesce_put_profile(skb, ETHTOOL_A_COALESCE_RX_PROFILE,
rcu_dereference(moder->rx_profile),
moder->coal_flags);
if (ret)
goto out;
}
if (moder->profile_flags & DIM_PROFILE_TX)
ret = coalesce_put_profile(skb, ETHTOOL_A_COALESCE_TX_PROFILE,
rcu_dereference(moder->tx_profile),
moder->coal_flags);
out:
rcu_read_unlock();
return ret;
}
/* COALESCE_SET */
static const struct nla_policy coalesce_irq_moderation_policy[] = {
[ETHTOOL_A_IRQ_MODERATION_USEC] = { .type = NLA_U32 },
[ETHTOOL_A_IRQ_MODERATION_PKTS] = { .type = NLA_U32 },
[ETHTOOL_A_IRQ_MODERATION_COMPS] = { .type = NLA_U32 },
};
static const struct nla_policy coalesce_profile_policy[] = {
[ETHTOOL_A_PROFILE_IRQ_MODERATION] =
NLA_POLICY_NESTED(coalesce_irq_moderation_policy),
};
const struct nla_policy ethnl_coalesce_set_policy[] = {
[ETHTOOL_A_COALESCE_HEADER] =
NLA_POLICY_NESTED(ethnl_header_policy),
@ -227,6 +337,10 @@ const struct nla_policy ethnl_coalesce_set_policy[] = {
[ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES] = { .type = NLA_U32 },
[ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES] = { .type = NLA_U32 },
[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS] = { .type = NLA_U32 },
[ETHTOOL_A_COALESCE_RX_PROFILE] =
NLA_POLICY_NESTED(coalesce_profile_policy),
[ETHTOOL_A_COALESCE_TX_PROFILE] =
NLA_POLICY_NESTED(coalesce_profile_policy),
};
static int
@ -234,6 +348,7 @@ ethnl_set_coalesce_validate(struct ethnl_req_info *req_info,
struct genl_info *info)
{
const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
struct dim_irq_moder *irq_moder = req_info->dev->irq_moder;
struct nlattr **tb = info->attrs;
u32 supported_params;
u16 a;
@ -243,6 +358,12 @@ ethnl_set_coalesce_validate(struct ethnl_req_info *req_info,
/* make sure that only supported parameters are present */
supported_params = ops->supported_coalesce_params;
if (irq_moder && irq_moder->profile_flags & DIM_PROFILE_RX)
supported_params |= ETHTOOL_COALESCE_RX_PROFILE;
if (irq_moder && irq_moder->profile_flags & DIM_PROFILE_TX)
supported_params |= ETHTOOL_COALESCE_TX_PROFILE;
for (a = ETHTOOL_A_COALESCE_RX_USECS; a < __ETHTOOL_A_COALESCE_CNT; a++)
if (tb[a] && !(supported_params & attr_to_mask(a))) {
NL_SET_ERR_MSG_ATTR(info->extack, tb[a],
@ -253,6 +374,138 @@ ethnl_set_coalesce_validate(struct ethnl_req_info *req_info,
return 1;
}
/**
* ethnl_update_irq_moder - update a specific field in the given profile
* @irq_moder: place that collects dim related information
* @irq_field: field in profile to modify
* @attr_type: attr type ETHTOOL_A_IRQ_MODERATION_*
* @tb: netlink attribute with new values or null
* @coal_bit: DIM_COALESCE_* bit from coal_flags
* @mod: pointer to bool for modification tracking
* @extack: netlink extended ack
*
* Return: 0 on success or a negative error code.
*/
static int ethnl_update_irq_moder(struct dim_irq_moder *irq_moder,
u16 *irq_field, u16 attr_type,
struct nlattr **tb,
u8 coal_bit, bool *mod,
struct netlink_ext_ack *extack)
{
int ret = 0;
u32 val;
if (!tb[attr_type])
return 0;
if (irq_moder->coal_flags & coal_bit) {
val = nla_get_u32(tb[attr_type]);
if (*irq_field == val)
return 0;
*irq_field = val;
*mod = true;
} else {
NL_SET_BAD_ATTR(extack, tb[attr_type]);
ret = -EOPNOTSUPP;
}
return ret;
}
/**
* ethnl_update_profile - get a profile nest with child nests from userspace.
* @dev: netdevice to update the profile
* @dst: profile get from the driver and modified by ethnl_update_profile.
* @nests: nest attr ETHTOOL_A_COALESCE_*X_PROFILE to set profile.
* @mod: pointer to bool for modification tracking
* @extack: Netlink extended ack
*
* Layout of nests:
* Nested ETHTOOL_A_COALESCE_*X_PROFILE attr
* Nested ETHTOOL_A_PROFILE_IRQ_MODERATION attr
* ETHTOOL_A_IRQ_MODERATION_USEC attr
* ETHTOOL_A_IRQ_MODERATION_PKTS attr
* ETHTOOL_A_IRQ_MODERATION_COMPS attr
* ...
* Nested ETHTOOL_A_PROFILE_IRQ_MODERATION attr
* ETHTOOL_A_IRQ_MODERATION_USEC attr
* ETHTOOL_A_IRQ_MODERATION_PKTS attr
* ETHTOOL_A_IRQ_MODERATION_COMPS attr
*
* Return: 0 on success or a negative error code.
*/
static int ethnl_update_profile(struct net_device *dev,
struct dim_cq_moder __rcu **dst,
const struct nlattr *nests,
bool *mod,
struct netlink_ext_ack *extack)
{
int len_irq_moder = ARRAY_SIZE(coalesce_irq_moderation_policy);
struct nlattr *tb[ARRAY_SIZE(coalesce_irq_moderation_policy)];
struct dim_irq_moder *irq_moder = dev->irq_moder;
struct dim_cq_moder *new_profile, *old_profile;
int ret, rem, i = 0, len;
struct nlattr *nest;
if (!nests)
return 0;
if (!*dst)
return -EOPNOTSUPP;
old_profile = rtnl_dereference(*dst);
len = NET_DIM_PARAMS_NUM_PROFILES * sizeof(*old_profile);
new_profile = kmemdup(old_profile, len, GFP_KERNEL);
if (!new_profile)
return -ENOMEM;
nla_for_each_nested_type(nest, ETHTOOL_A_PROFILE_IRQ_MODERATION,
nests, rem) {
ret = nla_parse_nested(tb, len_irq_moder - 1, nest,
coalesce_irq_moderation_policy,
extack);
if (ret)
goto err_out;
ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].usec,
ETHTOOL_A_IRQ_MODERATION_USEC,
tb, DIM_COALESCE_USEC,
mod, extack);
if (ret)
goto err_out;
ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].pkts,
ETHTOOL_A_IRQ_MODERATION_PKTS,
tb, DIM_COALESCE_PKTS,
mod, extack);
if (ret)
goto err_out;
ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].comps,
ETHTOOL_A_IRQ_MODERATION_COMPS,
tb, DIM_COALESCE_COMPS,
mod, extack);
if (ret)
goto err_out;
i++;
}
/* After the profile is modified, dim itself is a dynamic
* mechanism and will quickly fit to the appropriate
* coalescing parameters according to the new profile.
*/
rcu_assign_pointer(*dst, new_profile);
kfree_rcu(old_profile, rcu);
return 0;
err_out:
kfree(new_profile);
return ret;
}
static int
__ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info,
bool *dual_change)
@ -317,6 +570,22 @@ __ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info,
ethnl_update_u32(&kernel_coalesce.tx_aggr_time_usecs,
tb[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS], &mod);
if (dev->irq_moder && dev->irq_moder->profile_flags & DIM_PROFILE_RX) {
ret = ethnl_update_profile(dev, &dev->irq_moder->rx_profile,
tb[ETHTOOL_A_COALESCE_RX_PROFILE],
&mod, info->extack);
if (ret < 0)
return ret;
}
if (dev->irq_moder && dev->irq_moder->profile_flags & DIM_PROFILE_TX) {
ret = ethnl_update_profile(dev, &dev->irq_moder->tx_profile,
tb[ETHTOOL_A_COALESCE_TX_PROFILE],
&mod, info->extack);
if (ret < 0)
return ret;
}
/* Update operation modes */
ethnl_update_bool32(&coalesce.use_adaptive_rx_coalesce,
tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX], &mod_mode);