anolis: ethtool: provide customized dim profile management
ANBZ: #8042 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. ================================================= This patch was not merged into upstream during the upstream window period, so now as a self-developed patch. Later maintainers should avoid merging commits with the same title as upstream. Link: https://lore.kernel.org/all/20240509044747.101237-1-hengqi@linux.alibaba.com/ [Fix conflicts] Signed-off-by: Heng Qi <hengqi@linux.alibaba.com> Reviewed-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com> Link: https://gitee.com/anolis/cloud-kernel/pulls/3227
This commit is contained in:
parent
c89ad9b039
commit
473bef22de
|
@ -928,6 +928,8 @@ Kernel response contents:
|
|||
``ETHTOOL_A_COALESCE_TX_USECS_HIGH`` u32 delay (us), high Tx
|
||||
``ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH`` u32 max packets, high Tx
|
||||
``ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL`` u32 rate sampling interval
|
||||
``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
|
||||
|
@ -966,6 +968,8 @@ Request contents:
|
|||
``ETHTOOL_A_COALESCE_TX_USECS_HIGH`` u32 delay (us), high Tx
|
||||
``ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH`` u32 max packets, high Tx
|
||||
``ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL`` u32 rate sampling interval
|
||||
``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.
|
||||
|
|
|
@ -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
|
||||
==============================================
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -214,7 +214,9 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
|
|||
#define ETHTOOL_COALESCE_TX_USECS_HIGH BIT(19)
|
||||
#define ETHTOOL_COALESCE_TX_MAX_FRAMES_HIGH BIT(20)
|
||||
#define ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL BIT(21)
|
||||
#define ETHTOOL_COALESCE_ALL_PARAMS GENMASK(21, 0)
|
||||
#define ETHTOOL_COALESCE_RX_PROFILE BIT(22)
|
||||
#define ETHTOOL_COALESCE_TX_PROFILE BIT(23)
|
||||
#define ETHTOOL_COALESCE_ALL_PARAMS GENMASK(23, 0)
|
||||
|
||||
#define ETHTOOL_COALESCE_USECS \
|
||||
(ETHTOOL_COALESCE_RX_USECS | ETHTOOL_COALESCE_TX_USECS)
|
||||
|
|
|
@ -2235,6 +2235,8 @@ struct net_device {
|
|||
struct bpf_xdp_entity xdp_state[__MAX_XDP_MODE];
|
||||
|
||||
CK_KABI_USE_SPLIT(1, enum netdev_stat_type pcpu_stat_type:8)
|
||||
/** @irq_moder: dim parameters used if IS_ENABLED(CONFIG_DIMLIB). */
|
||||
CK_KABI_USE(2, struct dim_irq_moder *irq_moder)
|
||||
CK_KABI_RESERVE(2)
|
||||
CK_KABI_RESERVE(3)
|
||||
CK_KABI_RESERVE(4)
|
||||
|
|
|
@ -366,12 +366,32 @@ enum {
|
|||
ETHTOOL_A_COALESCE_TX_USECS_HIGH, /* u32 */
|
||||
ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH, /* u32 */
|
||||
ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL, /* u32 */
|
||||
ETHTOOL_A_COALESCE_RX_PROFILE, /* nest - _A_PROFILE_IRQ_MODERATION */
|
||||
ETHTOOL_A_COALESCE_TX_PROFILE, /* nest - _A_PROFILE_IRQ_MODERATION */
|
||||
|
||||
/* add new constants above here */
|
||||
__ETHTOOL_A_COALESCE_CNT,
|
||||
ETHTOOL_A_COALESCE_MAX = (__ETHTOOL_A_COALESCE_CNT - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
ETHTOOL_A_PROFILE_UNSPEC,
|
||||
ETHTOOL_A_PROFILE_IRQ_MODERATION, /* nest, _A_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 {
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -455,6 +455,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
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/dim.h>
|
||||
#include "netlink.h"
|
||||
#include "common.h"
|
||||
|
||||
|
@ -79,6 +80,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 */
|
||||
|
@ -100,7 +109,8 @@ static int coalesce_reply_size(const struct ethnl_req_info *req_base,
|
|||
nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_HIGH */
|
||||
nla_total_size(sizeof(u32)) + /* _TX_USECS_HIGH */
|
||||
nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_HIGH */
|
||||
nla_total_size(sizeof(u32)); /* _RATE_SAMPLE_INTERVAL */
|
||||
nla_total_size(sizeof(u32)) + /* _RATE_SAMPLE_INTERVAL */
|
||||
total_modersz * 2; /* _{R,T}X_PROFILE */
|
||||
}
|
||||
|
||||
static bool coalesce_put_u32(struct sk_buff *skb, u16 attr_type, u32 val,
|
||||
|
@ -119,13 +129,83 @@ 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);
|
||||
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) ||
|
||||
|
@ -173,7 +253,26 @@ static int coalesce_fill_reply(struct sk_buff *skb,
|
|||
coal->rate_sample_interval, 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;
|
||||
}
|
||||
|
||||
const struct ethnl_request_ops ethnl_coalesce_request_ops = {
|
||||
|
@ -190,6 +289,17 @@ const struct ethnl_request_ops ethnl_coalesce_request_ops = {
|
|||
|
||||
/* 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),
|
||||
|
@ -215,13 +325,144 @@ const struct nla_policy ethnl_coalesce_set_policy[] = {
|
|||
[ETHTOOL_A_COALESCE_TX_USECS_HIGH] = { .type = NLA_U32 },
|
||||
[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH] = { .type = NLA_U32 },
|
||||
[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL] = { .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),
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!tb[attr_type])
|
||||
return 0;
|
||||
|
||||
if (irq_moder->coal_flags & coal_bit) {
|
||||
*irq_field = nla_get_u32(tb[attr_type]);
|
||||
} 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.
|
||||
* @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,
|
||||
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(nest, nests, rem) {
|
||||
if (nla_type(nest) != ETHTOOL_A_PROFILE_IRQ_MODERATION) {
|
||||
ret = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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;
|
||||
}
|
||||
|
||||
int ethnl_set_coalesce(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct ethtool_coalesce coalesce = {};
|
||||
struct ethnl_req_info req_info = {};
|
||||
struct nlattr **tb = info->attrs;
|
||||
struct dim_irq_moder *irq_moder;
|
||||
const struct ethtool_ops *ops;
|
||||
struct net_device *dev;
|
||||
u32 supported_params;
|
||||
|
@ -242,7 +483,14 @@ int ethnl_set_coalesce(struct sk_buff *skb, struct genl_info *info)
|
|||
goto out_dev;
|
||||
|
||||
/* make sure that only supported parameters are present */
|
||||
irq_moder = dev->irq_moder;
|
||||
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))) {
|
||||
ret = -EINVAL;
|
||||
|
@ -303,6 +551,23 @@ int ethnl_set_coalesce(struct sk_buff *skb, struct genl_info *info)
|
|||
tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], &mod);
|
||||
ethnl_update_u32(&coalesce.rate_sample_interval,
|
||||
tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL], &mod);
|
||||
|
||||
if (irq_moder && irq_moder->profile_flags & DIM_PROFILE_RX) {
|
||||
ret = ethnl_update_profile(dev, &irq_moder->rx_profile,
|
||||
tb[ETHTOOL_A_COALESCE_RX_PROFILE],
|
||||
info->extack);
|
||||
if (ret < 0)
|
||||
goto out_ops;
|
||||
}
|
||||
|
||||
if (irq_moder && irq_moder->profile_flags & DIM_PROFILE_TX) {
|
||||
ret = ethnl_update_profile(dev, &irq_moder->tx_profile,
|
||||
tb[ETHTOOL_A_COALESCE_TX_PROFILE],
|
||||
info->extack);
|
||||
if (ret < 0)
|
||||
goto out_ops;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
if (!mod)
|
||||
goto out_ops;
|
||||
|
|
|
@ -369,7 +369,7 @@ extern const struct nla_policy ethnl_rings_set_policy[ETHTOOL_A_RINGS_TX + 1];
|
|||
extern const struct nla_policy ethnl_channels_get_policy[ETHTOOL_A_CHANNELS_HEADER + 1];
|
||||
extern const struct nla_policy ethnl_channels_set_policy[ETHTOOL_A_CHANNELS_COMBINED_COUNT + 1];
|
||||
extern const struct nla_policy ethnl_coalesce_get_policy[ETHTOOL_A_COALESCE_HEADER + 1];
|
||||
extern const struct nla_policy ethnl_coalesce_set_policy[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL + 1];
|
||||
extern const struct nla_policy ethnl_coalesce_set_policy[ETHTOOL_A_COALESCE_MAX + 1];
|
||||
extern const struct nla_policy ethnl_pause_get_policy[ETHTOOL_A_PAUSE_HEADER + 1];
|
||||
extern const struct nla_policy ethnl_pause_set_policy[ETHTOOL_A_PAUSE_TX + 1];
|
||||
extern const struct nla_policy ethnl_eee_get_policy[ETHTOOL_A_EEE_HEADER + 1];
|
||||
|
|
Loading…
Reference in New Issue