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
========
1. Query the currently customized list of the device

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

2. Tune
$ ethtool -C ethx rx-eqe-profile 1,1,0_2,2,0_3,3,0_4,4,0_5,5,0
$ ethtool -c ethx
...
rx-eqe-profile:
{.usec =   1, .pkts =   1, .comps =   0,},
{.usec =   2, .pkts =   2, .comps =   0,},
{.usec =   3, .pkts =   3, .comps =   0,},
{.usec =   4, .pkts =   4, .comps =   0,},
{.usec =   5, .pkts =   5, .comps =   0,}
rx-cqe-profile:   n/a
tx-eqe-profile:   n/a
tx-cqe-profile:   n/a

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

Signed-off-by: Heng Qi <hen...@linux.alibaba.com>
---
 Documentation/netlink/specs/ethtool.yaml     |  33 +++++
 Documentation/networking/ethtool-netlink.rst |   8 ++
 include/linux/ethtool.h                      |  12 +-
 include/linux/netdevice.h                    |  15 +++
 include/uapi/linux/ethtool_netlink.h         |  24 ++++
 net/core/dev.c                               |  63 +++++++++
 net/ethtool/coalesce.c                       | 184 ++++++++++++++++++++++++++-
 7 files changed, 336 insertions(+), 3 deletions(-)

diff --git a/Documentation/netlink/specs/ethtool.yaml 
b/Documentation/netlink/specs/ethtool.yaml
index 87ae7b3..1a560ff 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -413,6 +413,18 @@ attribute-sets:
       -
         name: combined-count
         type: u32
+  -
+    name: moderation
+    attributes:
+      -
+        name: usec
+        type: u16
+      -
+        name: pkts
+        type: u16
+      -
+        name: comps
+        type: u16
 
   -
     name: coalesce
@@ -502,6 +514,23 @@ attribute-sets:
       -
         name: tx-aggr-time-usecs
         type: u32
+      -
+        name: rx-eqe-profile
+        type: nest
+        nested-attributes: moderation
+      -
+        name: rx-cqe-profile
+        type: nest
+        nested-attributes: moderation
+      -
+        name: tx-eqe-profile
+        type: nest
+        nested-attributes: moderation
+      -
+        name: tx-cqe-profile
+        type: nest
+        nested-attributes: moderation
+
   -
     name: pause-stat
     attributes:
@@ -1313,6 +1342,10 @@ operations:
             - tx-aggr-max-bytes
             - tx-aggr-max-frames
             - tx-aggr-time-usecs
+            - rx-eqe-profile
+            - rx-cqe-profile
+            - tx-eqe-profile
+            - tx-cqe-profile
       dump: *coalesce-get-op
     -
       name: coalesce-set
diff --git a/Documentation/networking/ethtool-netlink.rst 
b/Documentation/networking/ethtool-netlink.rst
index 5dc42f7..4d9eecf 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -1040,6 +1040,10 @@ 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_EQE_PROFILE``        nested  profile of DIM EQE, Rx
+  ``ETHTOOL_A_COALESCE_RX_CQE_PROFILE``        nested  profile of DIM CQE, Rx
+  ``ETHTOOL_A_COALESCE_TX_EQE_PROFILE``        nested  profile of DIM EQE, Tx
+  ``ETHTOOL_A_COALESCE_TX_CQE_PROFILE``        nested  profile of DIM CQE, Tx
   ===========================================  ======  =======================
 
 Attributes are only included in reply if their value is not zero or the
@@ -1105,6 +1109,10 @@ 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_EQE_PROFILE``        nested  profile of DIM EQE, Rx
+  ``ETHTOOL_A_COALESCE_RX_CQE_PROFILE``        nested  profile of DIM CQE, Rx
+  ``ETHTOOL_A_COALESCE_TX_EQE_PROFILE``        nested  profile of DIM EQE, Tx
+  ``ETHTOOL_A_COALESCE_TX_CQE_PROFILE``        nested  profile of DIM CQE, Tx
   ===========================================  ======  =======================
 
 Request is rejected if it attributes declared as unsupported by driver (i.e.
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 6fd9107..1dcfbe5 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -18,6 +18,7 @@
 #include <linux/if_ether.h>
 #include <linux/netlink.h>
 #include <uapi/linux/ethtool.h>
+#include <linux/dim.h>
 
 struct compat_ethtool_rx_flow_spec {
        u32             flow_type;
@@ -284,7 +285,11 @@ 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_EQE_PROFILE         BIT(27)
+#define ETHTOOL_COALESCE_RX_CQE_PROFILE         BIT(28)
+#define ETHTOOL_COALESCE_TX_EQE_PROFILE         BIT(29)
+#define ETHTOOL_COALESCE_TX_CQE_PROFILE         BIT(30)
+#define ETHTOOL_COALESCE_ALL_PARAMS            GENMASK(30, 0)
 
 #define ETHTOOL_COALESCE_USECS                                         \
        (ETHTOOL_COALESCE_RX_USECS | ETHTOOL_COALESCE_TX_USECS)
@@ -316,6 +321,11 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 
*legacy_u32,
        (ETHTOOL_COALESCE_TX_AGGR_MAX_BYTES |   \
         ETHTOOL_COALESCE_TX_AGGR_MAX_FRAMES |  \
         ETHTOOL_COALESCE_TX_AGGR_TIME_USECS)
+#define ETHTOOL_COALESCE_PROFILE               \
+       (ETHTOOL_COALESCE_RX_EQE_PROFILE |      \
+        ETHTOOL_COALESCE_RX_CQE_PROFILE |      \
+        ETHTOOL_COALESCE_TX_EQE_PROFILE |      \
+        ETHTOOL_COALESCE_TX_CQE_PROFILE)
 
 #define ETHTOOL_STAT_NOT_SET   (~0ULL)
 
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index d45f330..d2f499a 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -48,6 +48,7 @@
 #include <uapi/linux/netdev.h>
 #include <linux/hashtable.h>
 #include <linux/rbtree.h>
+#include <linux/dim.h>
 #include <net/net_trackers.h>
 #include <net/net_debug.h>
 #include <net/dropreason-core.h>
@@ -1649,6 +1650,9 @@ struct net_device_ops {
  * @IFF_SEE_ALL_HWTSTAMP_REQUESTS: device wants to see calls to
  *     ndo_hwtstamp_set() for all timestamp requests regardless of source,
  *     even if those aren't HWTSTAMP_SOURCE_NETDEV.
+ * @IFF_PROFILE_USEC: device supports adjusting the DIM profile's usec field
+ * @IFF_PROFILE_PKTS: device supports adjusting the DIM profile's pkts field
+ * @IFF_PROFILE_COMPS: device supports adjusting the DIM profile's comps field
  */
 enum netdev_priv_flags {
        IFF_802_1Q_VLAN                 = 1<<0,
@@ -1685,6 +1689,9 @@ enum netdev_priv_flags {
        IFF_TX_SKB_NO_LINEAR            = BIT_ULL(31),
        IFF_CHANGE_PROTO_DOWN           = BIT_ULL(32),
        IFF_SEE_ALL_HWTSTAMP_REQUESTS   = BIT_ULL(33),
+       IFF_PROFILE_USEC                = BIT_ULL(34),
+       IFF_PROFILE_PKTS                = BIT_ULL(35),
+       IFF_PROFILE_COMPS               = BIT_ULL(36),
 };
 
 #define IFF_802_1Q_VLAN                        IFF_802_1Q_VLAN
@@ -2400,6 +2407,14 @@ struct net_device {
        /** @page_pools: page pools created for this netdevice */
        struct hlist_head       page_pools;
 #endif
+
+#if IS_ENABLED(CONFIG_DIMLIB)
+       /* DIM profile lists for different dim cq modes */
+       struct dim_cq_moder *rx_eqe_profile;
+       struct dim_cq_moder *rx_cqe_profile;
+       struct dim_cq_moder *tx_eqe_profile;
+       struct dim_cq_moder *tx_cqe_profile;
+#endif
 };
 #define to_net_dev(d) container_of(d, struct net_device, dev)
 
diff --git a/include/uapi/linux/ethtool_netlink.h 
b/include/uapi/linux/ethtool_netlink.h
index 23e225f..81c6d9e 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -416,12 +416,36 @@ 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 */
+       ETHTOOL_A_COALESCE_RX_EQE_PROFILE,              /* nest - 
_A_MODERATIONS_MODERATION */
+       ETHTOOL_A_COALESCE_RX_CQE_PROFILE,              /* nest - 
_A_MODERATIONS_MODERATION */
+       ETHTOOL_A_COALESCE_TX_EQE_PROFILE,              /* nest - 
_A_MODERATIONS_MODERATION */
+       ETHTOOL_A_COALESCE_TX_CQE_PROFILE,              /* nest - 
_A_MODERATIONS_MODERATION */
 
        /* add new constants above here */
        __ETHTOOL_A_COALESCE_CNT,
        ETHTOOL_A_COALESCE_MAX = (__ETHTOOL_A_COALESCE_CNT - 1)
 };
 
+enum {
+       ETHTOOL_A_MODERATIONS_UNSPEC,
+       ETHTOOL_A_MODERATIONS_MODERATION,               /* nest, 
_A_MODERATION_* */
+
+       /* add new constants above here */
+       __ETHTOOL_A_MODERATIONS_CNT,
+       ETHTOOL_A_MODERATIONS_MAX = (__ETHTOOL_A_MODERATIONS_CNT - 1)
+};
+
+enum {
+       ETHTOOL_A_MODERATION_UNSPEC,
+       ETHTOOL_A_MODERATION_USEC,                      /* u16 */
+       ETHTOOL_A_MODERATION_PKTS,                      /* u16 */
+       ETHTOOL_A_MODERATION_COMPS,                     /* u16 */
+
+       /* add new constants above here */
+       __ETHTOOL_A_MODERATION_CNT,
+       ETHTOOL_A_MODERATION_MAX = (__ETHTOOL_A_MODERATION_CNT - 1)
+};
+
 /* PAUSE */
 
 enum {
diff --git a/net/core/dev.c b/net/core/dev.c
index 854a3a2..bc38f33 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -154,6 +154,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/prandom.h>
 #include <linux/once_lite.h>
+#include <linux/ethtool.h>
 #include <net/netdev_rx_queue.h>
 #include <net/page_pool/types.h>
 #include <net/page_pool/helpers.h>
@@ -10229,6 +10230,42 @@ static void netdev_do_free_pcpu_stats(struct 
net_device *dev)
        }
 }
 
+static int dev_dim_profile_init(struct net_device *dev)
+{
+       int length = NET_DIM_PARAMS_NUM_PROFILES * sizeof(*dev->rx_eqe_profile);
+       u32 supported = dev->ethtool_ops->supported_coalesce_params;
+
+       if (!(dev->priv_flags & (IFF_PROFILE_USEC | IFF_PROFILE_PKTS | 
IFF_PROFILE_COMPS)))
+               return 0;
+
+       if (supported & ETHTOOL_COALESCE_RX_EQE_PROFILE) {
+               dev->rx_eqe_profile = kzalloc(length, GFP_KERNEL);
+               if (!dev->rx_eqe_profile)
+                       return -ENOMEM;
+               memcpy(dev->rx_eqe_profile, rx_profile[0], length);
+       }
+       if (supported & ETHTOOL_COALESCE_RX_CQE_PROFILE) {
+               dev->rx_cqe_profile = kzalloc(length, GFP_KERNEL);
+               if (!dev->rx_cqe_profile)
+                       return -ENOMEM;
+               memcpy(dev->rx_cqe_profile, rx_profile[1], length);
+       }
+       if (supported & ETHTOOL_COALESCE_TX_EQE_PROFILE) {
+               dev->tx_eqe_profile = kzalloc(length, GFP_KERNEL);
+               if (!dev->tx_eqe_profile)
+                       return -ENOMEM;
+               memcpy(dev->tx_eqe_profile, tx_profile[0], length);
+       }
+       if (supported & ETHTOOL_COALESCE_TX_CQE_PROFILE) {
+               dev->tx_cqe_profile = kzalloc(length, GFP_KERNEL);
+               if (!dev->tx_cqe_profile)
+                       return -ENOMEM;
+               memcpy(dev->tx_cqe_profile, tx_profile[1], length);
+       }
+
+       return 0;
+}
+
 /**
  * register_netdevice() - register a network device
  * @dev: device to register
@@ -10258,6 +10295,10 @@ int register_netdevice(struct net_device *dev)
        if (ret)
                return ret;
 
+       ret = dev_dim_profile_init(dev);
+       if (ret)
+               return ret;
+
        spin_lock_init(&dev->addr_list_lock);
        netdev_set_addr_lockdep_class(dev);
 
@@ -11011,6 +11052,26 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, 
const char *name,
 }
 EXPORT_SYMBOL(alloc_netdev_mqs);
 
+static void netif_free_profile(struct net_device *dev)
+{
+       u32 supported = dev->ethtool_ops->supported_coalesce_params;
+
+       if (!(dev->priv_flags & (IFF_PROFILE_USEC | IFF_PROFILE_PKTS | 
IFF_PROFILE_COMPS)))
+               return;
+
+       if (supported & ETHTOOL_COALESCE_RX_EQE_PROFILE)
+               kfree(dev->rx_eqe_profile);
+
+       if (supported & ETHTOOL_COALESCE_RX_CQE_PROFILE)
+               kfree(dev->rx_cqe_profile);
+
+       if (supported & ETHTOOL_COALESCE_TX_EQE_PROFILE)
+               kfree(dev->tx_eqe_profile);
+
+       if (supported & ETHTOOL_COALESCE_TX_CQE_PROFILE)
+               kfree(dev->tx_cqe_profile);
+}
+
 /**
  * free_netdev - free network device
  * @dev: device
@@ -11036,6 +11097,8 @@ void free_netdev(struct net_device *dev)
                return;
        }
 
+       netif_free_profile(dev);
+
        netif_free_tx_queues(dev);
        netif_free_rx_queues(dev);
 
diff --git a/net/ethtool/coalesce.c b/net/ethtool/coalesce.c
index 83112c1..7b542c3e 100644
--- a/net/ethtool/coalesce.c
+++ b/net/ethtool/coalesce.c
@@ -51,6 +51,10 @@ static u32 attr_to_mask(unsigned int attr_type)
 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_HIGH);
 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_HIGH);
 __CHECK_SUPPORTED_OFFSET(COALESCE_RATE_SAMPLE_INTERVAL);
+__CHECK_SUPPORTED_OFFSET(COALESCE_RX_EQE_PROFILE);
+__CHECK_SUPPORTED_OFFSET(COALESCE_RX_CQE_PROFILE);
+__CHECK_SUPPORTED_OFFSET(COALESCE_TX_EQE_PROFILE);
+__CHECK_SUPPORTED_OFFSET(COALESCE_TX_CQE_PROFILE);
 
 const struct nla_policy ethnl_coalesce_get_policy[] = {
        [ETHTOOL_A_COALESCE_HEADER]             =
@@ -82,6 +86,13 @@ 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) + /* _MODERATIONS_MODERATION, nest */
+                     nla_total_size(sizeof(u16)) + /* _MODERATION_USEC */
+                     nla_total_size(sizeof(u16)) + /* _MODERATION_PKTS */
+                     nla_total_size(sizeof(u16));  /* _MODERATION_COMPS */
+       int total_modersz = nla_total_size(0) +  /* _{R,T}X_{E,C}QE_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 +119,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 * 4;               /* _{R,T}X_{E,C}QE_PROFILE */
 }
 
 static bool coalesce_put_u32(struct sk_buff *skb, u16 attr_type, u32 val,
@@ -127,6 +139,67 @@ 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_*QE_PROFILE
+ * @profile: data passed to userspace
+ * @supported_params: modifiable parameters supported by the driver
+ *
+ * Put a dim profile nest attribute. Refer to ETHTOOL_A_MODERATIONS_MODERATION.
+ *
+ * Returns false to indicate successful placement or no placement, and
+ * returns true to pass the -EMSGSIZE error to the wrapper.
+ */
+static bool coalesce_put_profile(struct sk_buff *skb, u16 attr_type,
+                                const struct dim_cq_moder *profile,
+                                u32 supported_params)
+{
+       struct nlattr *profile_attr, *moder_attr;
+       bool valid = false, emsg = !!-EMSGSIZE;
+       int i;
+
+       if (!profile)
+               return false;
+
+       for (i = 0; i < NET_DIM_PARAMS_NUM_PROFILES; i++) {
+               if (profile[i].usec || profile[i].pkts || profile[i].comps) {
+                       valid = true;
+                       break;
+               }
+       }
+
+       if (!valid || !(supported_params & attr_to_mask(attr_type)))
+               return false;
+
+       profile_attr = nla_nest_start(skb, attr_type);
+       if (!profile_attr)
+               return emsg;
+
+       for (i = 0; i < NET_DIM_PARAMS_NUM_PROFILES; i++) {
+               moder_attr = nla_nest_start(skb, 
ETHTOOL_A_MODERATIONS_MODERATION);
+               if (!moder_attr)
+                       goto nla_cancel_profile;
+
+               if (nla_put_u16(skb, ETHTOOL_A_MODERATION_USEC, 
profile[i].usec) ||
+                   nla_put_u16(skb, ETHTOOL_A_MODERATION_PKTS, 
profile[i].pkts) ||
+                   nla_put_u16(skb, ETHTOOL_A_MODERATION_COMPS, 
profile[i].comps))
+                       goto nla_cancel_moder;
+
+               nla_nest_end(skb, moder_attr);
+       }
+
+       nla_nest_end(skb, profile_attr);
+
+       return 0;
+
+nla_cancel_moder:
+       nla_nest_cancel(skb, moder_attr);
+nla_cancel_profile:
+       nla_nest_cancel(skb, profile_attr);
+       return emsg;
+}
+
 static int coalesce_fill_reply(struct sk_buff *skb,
                               const struct ethnl_req_info *req_base,
                               const struct ethnl_reply_data *reply_base)
@@ -134,6 +207,7 @@ static int coalesce_fill_reply(struct sk_buff *skb,
        const struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base);
        const struct kernel_ethtool_coalesce *kcoal = &data->kernel_coalesce;
        const struct ethtool_coalesce *coal = &data->coalesce;
+       struct net_device *dev = req_base->dev;
        u32 supported = data->supported_params;
 
        if (coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS,
@@ -189,7 +263,15 @@ static int coalesce_fill_reply(struct sk_buff *skb,
            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES,
                             kcoal->tx_aggr_max_frames, supported) ||
            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS,
-                            kcoal->tx_aggr_time_usecs, supported))
+                            kcoal->tx_aggr_time_usecs, supported) ||
+           coalesce_put_profile(skb, ETHTOOL_A_COALESCE_RX_EQE_PROFILE,
+                                dev->rx_eqe_profile, supported) ||
+           coalesce_put_profile(skb, ETHTOOL_A_COALESCE_RX_CQE_PROFILE,
+                                dev->rx_cqe_profile, supported) ||
+           coalesce_put_profile(skb, ETHTOOL_A_COALESCE_TX_EQE_PROFILE,
+                                dev->tx_eqe_profile, supported) ||
+           coalesce_put_profile(skb, ETHTOOL_A_COALESCE_TX_CQE_PROFILE,
+                                dev->tx_cqe_profile, supported))
                return -EMSGSIZE;
 
        return 0;
@@ -227,6 +309,16 @@ static int coalesce_fill_reply(struct sk_buff *skb,
        [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_EQE_PROFILE]     = { .type = NLA_NESTED },
+       [ETHTOOL_A_COALESCE_RX_CQE_PROFILE]     = { .type = NLA_NESTED },
+       [ETHTOOL_A_COALESCE_TX_EQE_PROFILE]     = { .type = NLA_NESTED },
+       [ETHTOOL_A_COALESCE_TX_CQE_PROFILE]     = { .type = NLA_NESTED },
+};
+
+static const struct nla_policy coalesce_set_profile_policy[] = {
+       [ETHTOOL_A_MODERATION_USEC]     = {.type = NLA_U16},
+       [ETHTOOL_A_MODERATION_PKTS]     = {.type = NLA_U16},
+       [ETHTOOL_A_MODERATION_COMPS]    = {.type = NLA_U16},
 };
 
 static int
@@ -253,6 +345,73 @@ static int coalesce_fill_reply(struct sk_buff *skb,
        return 1;
 }
 
+/**
+ * ethnl_update_profile - get a nla nest with four child nla nests from 
userspace.
+ * @dst: data get from the driver and modified by ethnl_update_profile.
+ * @nests: nest attr ETHTOOL_A_COALESCE_*X_*QE_PROFILE to set driver's profile.
+ * @mod: whether the data is modified
+ * @extack: Netlink extended ack
+ *
+ * Layout of nests:
+ *   Nested ETHTOOL_A_COALESCE_*X_*QE_PROFILE attr
+ *     Nested ETHTOOL_A_MODERATIONS_MODERATION attr
+ *       ETHTOOL_A_MODERATION_USEC attr
+ *       ETHTOOL_A_MODERATION_PKTS attr
+ *       ETHTOOL_A_MODERATION_COMPS attr
+ *     ...
+ *     Nested ETHTOOL_A_MODERATIONS_MODERATION attr
+ *       ETHTOOL_A_MODERATION_USEC attr
+ *       ETHTOOL_A_MODERATION_PKTS attr
+ *       ETHTOOL_A_MODERATION_COMPS attr
+ *
+ * Returns 0 on success or a negative error code.
+ */
+static inline int ethnl_update_profile(struct net_device *dev,
+                                      struct dim_cq_moder *dst,
+                                      const struct nlattr *nests,
+                                      struct netlink_ext_ack *extack)
+{
+       struct nlattr *tb_moder[ARRAY_SIZE(coalesce_set_profile_policy)];
+       struct dim_cq_moder profile[NET_DIM_PARAMS_NUM_PROFILES];
+       struct nlattr *nest;
+       int ret, rem, i = 0;
+
+       if (!nests)
+               return 0;
+
+       if (!dst)
+               return -EOPNOTSUPP;
+
+       nla_for_each_nested_type(nest, ETHTOOL_A_MODERATIONS_MODERATION, nests, 
rem) {
+               ret = nla_parse_nested(tb_moder,
+                                      ARRAY_SIZE(coalesce_set_profile_policy) 
- 1,
+                                      nest, coalesce_set_profile_policy,
+                                      extack);
+               if (ret)
+                       return ret;
+
+               if (NL_REQ_ATTR_CHECK(extack, nest, tb_moder, 
ETHTOOL_A_MODERATION_USEC) ||
+                   NL_REQ_ATTR_CHECK(extack, nest, tb_moder, 
ETHTOOL_A_MODERATION_PKTS) ||
+                   NL_REQ_ATTR_CHECK(extack, nest, tb_moder, 
ETHTOOL_A_MODERATION_COMPS))
+                       return -EINVAL;
+
+               profile[i].usec = 
nla_get_u16(tb_moder[ETHTOOL_A_MODERATION_USEC]);
+               profile[i].pkts = 
nla_get_u16(tb_moder[ETHTOOL_A_MODERATION_PKTS]);
+               profile[i].comps = 
nla_get_u16(tb_moder[ETHTOOL_A_MODERATION_COMPS]);
+
+               if ((dst[i].usec != profile[i].usec && !(dev->priv_flags & 
IFF_PROFILE_USEC)) ||
+                   (dst[i].pkts != profile[i].pkts && !(dev->priv_flags & 
IFF_PROFILE_PKTS)) ||
+                   (dst[i].comps != profile[i].comps && !(dev->priv_flags & 
IFF_PROFILE_COMPS)))
+                       return -EOPNOTSUPP;
+
+               i++;
+       }
+
+       memcpy(dst, profile, sizeof(profile));
+
+       return 0;
+}
+
 static int
 __ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info,
                     bool *dual_change)
@@ -317,6 +476,27 @@ static int coalesce_fill_reply(struct sk_buff *skb,
        ethnl_update_u32(&kernel_coalesce.tx_aggr_time_usecs,
                         tb[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS], &mod);
 
+       ret = ethnl_update_profile(dev, dev->rx_eqe_profile,
+                                  tb[ETHTOOL_A_COALESCE_RX_EQE_PROFILE],
+                                  info->extack);
+       if (ret < 0)
+               return ret;
+       ret = ethnl_update_profile(dev, dev->rx_cqe_profile,
+                                  tb[ETHTOOL_A_COALESCE_RX_CQE_PROFILE],
+                                  info->extack);
+       if (ret < 0)
+               return ret;
+       ret = ethnl_update_profile(dev, dev->tx_eqe_profile,
+                                  tb[ETHTOOL_A_COALESCE_TX_EQE_PROFILE],
+                                  info->extack);
+       if (ret < 0)
+               return ret;
+       ret = ethnl_update_profile(dev, dev->tx_cqe_profile,
+                                  tb[ETHTOOL_A_COALESCE_TX_CQE_PROFILE],
+                                  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);
-- 
1.8.3.1


Reply via email to