linux/net/ethtool/coalesce.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2
   3#include "netlink.h"
   4#include "common.h"
   5
   6struct coalesce_req_info {
   7        struct ethnl_req_info           base;
   8};
   9
  10struct coalesce_reply_data {
  11        struct ethnl_reply_data         base;
  12        struct ethtool_coalesce         coalesce;
  13        u32                             supported_params;
  14};
  15
  16#define COALESCE_REPDATA(__reply_base) \
  17        container_of(__reply_base, struct coalesce_reply_data, base)
  18
  19#define __SUPPORTED_OFFSET ETHTOOL_A_COALESCE_RX_USECS
  20static u32 attr_to_mask(unsigned int attr_type)
  21{
  22        return BIT(attr_type - __SUPPORTED_OFFSET);
  23}
  24
  25/* build time check that indices in ethtool_ops::supported_coalesce_params
  26 * match corresponding attribute types with an offset
  27 */
  28#define __CHECK_SUPPORTED_OFFSET(x) \
  29        static_assert((ETHTOOL_ ## x) == \
  30                      BIT((ETHTOOL_A_ ## x) - __SUPPORTED_OFFSET))
  31__CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS);
  32__CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES);
  33__CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_IRQ);
  34__CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_IRQ);
  35__CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS);
  36__CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES);
  37__CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_IRQ);
  38__CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_IRQ);
  39__CHECK_SUPPORTED_OFFSET(COALESCE_STATS_BLOCK_USECS);
  40__CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_RX);
  41__CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_TX);
  42__CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_LOW);
  43__CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_LOW);
  44__CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_LOW);
  45__CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_LOW);
  46__CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_LOW);
  47__CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_HIGH);
  48__CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_HIGH);
  49__CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_HIGH);
  50__CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_HIGH);
  51__CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_HIGH);
  52__CHECK_SUPPORTED_OFFSET(COALESCE_RATE_SAMPLE_INTERVAL);
  53
  54const struct nla_policy ethnl_coalesce_get_policy[] = {
  55        [ETHTOOL_A_COALESCE_HEADER]             =
  56                NLA_POLICY_NESTED(ethnl_header_policy),
  57};
  58
  59static int coalesce_prepare_data(const struct ethnl_req_info *req_base,
  60                                 struct ethnl_reply_data *reply_base,
  61                                 struct genl_info *info)
  62{
  63        struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base);
  64        struct net_device *dev = reply_base->dev;
  65        int ret;
  66
  67        if (!dev->ethtool_ops->get_coalesce)
  68                return -EOPNOTSUPP;
  69        data->supported_params = dev->ethtool_ops->supported_coalesce_params;
  70        ret = ethnl_ops_begin(dev);
  71        if (ret < 0)
  72                return ret;
  73        ret = dev->ethtool_ops->get_coalesce(dev, &data->coalesce);
  74        ethnl_ops_complete(dev);
  75
  76        return ret;
  77}
  78
  79static int coalesce_reply_size(const struct ethnl_req_info *req_base,
  80                               const struct ethnl_reply_data *reply_base)
  81{
  82        return nla_total_size(sizeof(u32)) +    /* _RX_USECS */
  83               nla_total_size(sizeof(u32)) +    /* _RX_MAX_FRAMES */
  84               nla_total_size(sizeof(u32)) +    /* _RX_USECS_IRQ */
  85               nla_total_size(sizeof(u32)) +    /* _RX_MAX_FRAMES_IRQ */
  86               nla_total_size(sizeof(u32)) +    /* _TX_USECS */
  87               nla_total_size(sizeof(u32)) +    /* _TX_MAX_FRAMES */
  88               nla_total_size(sizeof(u32)) +    /* _TX_USECS_IRQ */
  89               nla_total_size(sizeof(u32)) +    /* _TX_MAX_FRAMES_IRQ */
  90               nla_total_size(sizeof(u32)) +    /* _STATS_BLOCK_USECS */
  91               nla_total_size(sizeof(u8)) +     /* _USE_ADAPTIVE_RX */
  92               nla_total_size(sizeof(u8)) +     /* _USE_ADAPTIVE_TX */
  93               nla_total_size(sizeof(u32)) +    /* _PKT_RATE_LOW */
  94               nla_total_size(sizeof(u32)) +    /* _RX_USECS_LOW */
  95               nla_total_size(sizeof(u32)) +    /* _RX_MAX_FRAMES_LOW */
  96               nla_total_size(sizeof(u32)) +    /* _TX_USECS_LOW */
  97               nla_total_size(sizeof(u32)) +    /* _TX_MAX_FRAMES_LOW */
  98               nla_total_size(sizeof(u32)) +    /* _PKT_RATE_HIGH */
  99               nla_total_size(sizeof(u32)) +    /* _RX_USECS_HIGH */
 100               nla_total_size(sizeof(u32)) +    /* _RX_MAX_FRAMES_HIGH */
 101               nla_total_size(sizeof(u32)) +    /* _TX_USECS_HIGH */
 102               nla_total_size(sizeof(u32)) +    /* _TX_MAX_FRAMES_HIGH */
 103               nla_total_size(sizeof(u32));     /* _RATE_SAMPLE_INTERVAL */
 104}
 105
 106static bool coalesce_put_u32(struct sk_buff *skb, u16 attr_type, u32 val,
 107                             u32 supported_params)
 108{
 109        if (!val && !(supported_params & attr_to_mask(attr_type)))
 110                return false;
 111        return nla_put_u32(skb, attr_type, val);
 112}
 113
 114static bool coalesce_put_bool(struct sk_buff *skb, u16 attr_type, u32 val,
 115                              u32 supported_params)
 116{
 117        if (!val && !(supported_params & attr_to_mask(attr_type)))
 118                return false;
 119        return nla_put_u8(skb, attr_type, !!val);
 120}
 121
 122static int coalesce_fill_reply(struct sk_buff *skb,
 123                               const struct ethnl_req_info *req_base,
 124                               const struct ethnl_reply_data *reply_base)
 125{
 126        const struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base);
 127        const struct ethtool_coalesce *coal = &data->coalesce;
 128        u32 supported = data->supported_params;
 129
 130        if (coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS,
 131                             coal->rx_coalesce_usecs, supported) ||
 132            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES,
 133                             coal->rx_max_coalesced_frames, supported) ||
 134            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_IRQ,
 135                             coal->rx_coalesce_usecs_irq, supported) ||
 136            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ,
 137                             coal->rx_max_coalesced_frames_irq, supported) ||
 138            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS,
 139                             coal->tx_coalesce_usecs, supported) ||
 140            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES,
 141                             coal->tx_max_coalesced_frames, supported) ||
 142            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_IRQ,
 143                             coal->tx_coalesce_usecs_irq, supported) ||
 144            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ,
 145                             coal->tx_max_coalesced_frames_irq, supported) ||
 146            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_STATS_BLOCK_USECS,
 147                             coal->stats_block_coalesce_usecs, supported) ||
 148            coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX,
 149                              coal->use_adaptive_rx_coalesce, supported) ||
 150            coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX,
 151                              coal->use_adaptive_tx_coalesce, supported) ||
 152            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_LOW,
 153                             coal->pkt_rate_low, supported) ||
 154            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_LOW,
 155                             coal->rx_coalesce_usecs_low, supported) ||
 156            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW,
 157                             coal->rx_max_coalesced_frames_low, supported) ||
 158            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_LOW,
 159                             coal->tx_coalesce_usecs_low, supported) ||
 160            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW,
 161                             coal->tx_max_coalesced_frames_low, supported) ||
 162            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_HIGH,
 163                             coal->pkt_rate_high, supported) ||
 164            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_HIGH,
 165                             coal->rx_coalesce_usecs_high, supported) ||
 166            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH,
 167                             coal->rx_max_coalesced_frames_high, supported) ||
 168            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_HIGH,
 169                             coal->tx_coalesce_usecs_high, supported) ||
 170            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH,
 171                             coal->tx_max_coalesced_frames_high, supported) ||
 172            coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL,
 173                             coal->rate_sample_interval, supported))
 174                return -EMSGSIZE;
 175
 176        return 0;
 177}
 178
 179const struct ethnl_request_ops ethnl_coalesce_request_ops = {
 180        .request_cmd            = ETHTOOL_MSG_COALESCE_GET,
 181        .reply_cmd              = ETHTOOL_MSG_COALESCE_GET_REPLY,
 182        .hdr_attr               = ETHTOOL_A_COALESCE_HEADER,
 183        .req_info_size          = sizeof(struct coalesce_req_info),
 184        .reply_data_size        = sizeof(struct coalesce_reply_data),
 185
 186        .prepare_data           = coalesce_prepare_data,
 187        .reply_size             = coalesce_reply_size,
 188        .fill_reply             = coalesce_fill_reply,
 189};
 190
 191/* COALESCE_SET */
 192
 193const struct nla_policy ethnl_coalesce_set_policy[] = {
 194        [ETHTOOL_A_COALESCE_HEADER]             =
 195                NLA_POLICY_NESTED(ethnl_header_policy),
 196        [ETHTOOL_A_COALESCE_RX_USECS]           = { .type = NLA_U32 },
 197        [ETHTOOL_A_COALESCE_RX_MAX_FRAMES]      = { .type = NLA_U32 },
 198        [ETHTOOL_A_COALESCE_RX_USECS_IRQ]       = { .type = NLA_U32 },
 199        [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ]  = { .type = NLA_U32 },
 200        [ETHTOOL_A_COALESCE_TX_USECS]           = { .type = NLA_U32 },
 201        [ETHTOOL_A_COALESCE_TX_MAX_FRAMES]      = { .type = NLA_U32 },
 202        [ETHTOOL_A_COALESCE_TX_USECS_IRQ]       = { .type = NLA_U32 },
 203        [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ]  = { .type = NLA_U32 },
 204        [ETHTOOL_A_COALESCE_STATS_BLOCK_USECS]  = { .type = NLA_U32 },
 205        [ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX]    = { .type = NLA_U8 },
 206        [ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX]    = { .type = NLA_U8 },
 207        [ETHTOOL_A_COALESCE_PKT_RATE_LOW]       = { .type = NLA_U32 },
 208        [ETHTOOL_A_COALESCE_RX_USECS_LOW]       = { .type = NLA_U32 },
 209        [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW]  = { .type = NLA_U32 },
 210        [ETHTOOL_A_COALESCE_TX_USECS_LOW]       = { .type = NLA_U32 },
 211        [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW]  = { .type = NLA_U32 },
 212        [ETHTOOL_A_COALESCE_PKT_RATE_HIGH]      = { .type = NLA_U32 },
 213        [ETHTOOL_A_COALESCE_RX_USECS_HIGH]      = { .type = NLA_U32 },
 214        [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH] = { .type = NLA_U32 },
 215        [ETHTOOL_A_COALESCE_TX_USECS_HIGH]      = { .type = NLA_U32 },
 216        [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH] = { .type = NLA_U32 },
 217        [ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL] = { .type = NLA_U32 },
 218};
 219
 220int ethnl_set_coalesce(struct sk_buff *skb, struct genl_info *info)
 221{
 222        struct ethtool_coalesce coalesce = {};
 223        struct ethnl_req_info req_info = {};
 224        struct nlattr **tb = info->attrs;
 225        const struct ethtool_ops *ops;
 226        struct net_device *dev;
 227        u32 supported_params;
 228        bool mod = false;
 229        int ret;
 230        u16 a;
 231
 232        ret = ethnl_parse_header_dev_get(&req_info,
 233                                         tb[ETHTOOL_A_COALESCE_HEADER],
 234                                         genl_info_net(info), info->extack,
 235                                         true);
 236        if (ret < 0)
 237                return ret;
 238        dev = req_info.dev;
 239        ops = dev->ethtool_ops;
 240        ret = -EOPNOTSUPP;
 241        if (!ops->get_coalesce || !ops->set_coalesce)
 242                goto out_dev;
 243
 244        /* make sure that only supported parameters are present */
 245        supported_params = ops->supported_coalesce_params;
 246        for (a = ETHTOOL_A_COALESCE_RX_USECS; a < __ETHTOOL_A_COALESCE_CNT; a++)
 247                if (tb[a] && !(supported_params & attr_to_mask(a))) {
 248                        ret = -EINVAL;
 249                        NL_SET_ERR_MSG_ATTR(info->extack, tb[a],
 250                                            "cannot modify an unsupported parameter");
 251                        goto out_dev;
 252                }
 253
 254        rtnl_lock();
 255        ret = ethnl_ops_begin(dev);
 256        if (ret < 0)
 257                goto out_rtnl;
 258        ret = ops->get_coalesce(dev, &coalesce);
 259        if (ret < 0)
 260                goto out_ops;
 261
 262        ethnl_update_u32(&coalesce.rx_coalesce_usecs,
 263                         tb[ETHTOOL_A_COALESCE_RX_USECS], &mod);
 264        ethnl_update_u32(&coalesce.rx_max_coalesced_frames,
 265                         tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES], &mod);
 266        ethnl_update_u32(&coalesce.rx_coalesce_usecs_irq,
 267                         tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ], &mod);
 268        ethnl_update_u32(&coalesce.rx_max_coalesced_frames_irq,
 269                         tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ], &mod);
 270        ethnl_update_u32(&coalesce.tx_coalesce_usecs,
 271                         tb[ETHTOOL_A_COALESCE_TX_USECS], &mod);
 272        ethnl_update_u32(&coalesce.tx_max_coalesced_frames,
 273                         tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES], &mod);
 274        ethnl_update_u32(&coalesce.tx_coalesce_usecs_irq,
 275                         tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ], &mod);
 276        ethnl_update_u32(&coalesce.tx_max_coalesced_frames_irq,
 277                         tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ], &mod);
 278        ethnl_update_u32(&coalesce.stats_block_coalesce_usecs,
 279                         tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS], &mod);
 280        ethnl_update_bool32(&coalesce.use_adaptive_rx_coalesce,
 281                            tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX], &mod);
 282        ethnl_update_bool32(&coalesce.use_adaptive_tx_coalesce,
 283                            tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX], &mod);
 284        ethnl_update_u32(&coalesce.pkt_rate_low,
 285                         tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW], &mod);
 286        ethnl_update_u32(&coalesce.rx_coalesce_usecs_low,
 287                         tb[ETHTOOL_A_COALESCE_RX_USECS_LOW], &mod);
 288        ethnl_update_u32(&coalesce.rx_max_coalesced_frames_low,
 289                         tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW], &mod);
 290        ethnl_update_u32(&coalesce.tx_coalesce_usecs_low,
 291                         tb[ETHTOOL_A_COALESCE_TX_USECS_LOW], &mod);
 292        ethnl_update_u32(&coalesce.tx_max_coalesced_frames_low,
 293                         tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW], &mod);
 294        ethnl_update_u32(&coalesce.pkt_rate_high,
 295                         tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH], &mod);
 296        ethnl_update_u32(&coalesce.rx_coalesce_usecs_high,
 297                         tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH], &mod);
 298        ethnl_update_u32(&coalesce.rx_max_coalesced_frames_high,
 299                         tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH], &mod);
 300        ethnl_update_u32(&coalesce.tx_coalesce_usecs_high,
 301                         tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH], &mod);
 302        ethnl_update_u32(&coalesce.tx_max_coalesced_frames_high,
 303                         tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], &mod);
 304        ethnl_update_u32(&coalesce.rate_sample_interval,
 305                         tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL], &mod);
 306        ret = 0;
 307        if (!mod)
 308                goto out_ops;
 309
 310        ret = dev->ethtool_ops->set_coalesce(dev, &coalesce);
 311        if (ret < 0)
 312                goto out_ops;
 313        ethtool_notify(dev, ETHTOOL_MSG_COALESCE_NTF, NULL);
 314
 315out_ops:
 316        ethnl_ops_complete(dev);
 317out_rtnl:
 318        rtnl_unlock();
 319out_dev:
 320        dev_put(dev);
 321        return ret;
 322}
 323