linux/net/ethtool/channels.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2
   3#include <net/xdp_sock_drv.h>
   4
   5#include "netlink.h"
   6#include "common.h"
   7
   8struct channels_req_info {
   9        struct ethnl_req_info           base;
  10};
  11
  12struct channels_reply_data {
  13        struct ethnl_reply_data         base;
  14        struct ethtool_channels         channels;
  15};
  16
  17#define CHANNELS_REPDATA(__reply_base) \
  18        container_of(__reply_base, struct channels_reply_data, base)
  19
  20const struct nla_policy ethnl_channels_get_policy[] = {
  21        [ETHTOOL_A_CHANNELS_HEADER]             =
  22                NLA_POLICY_NESTED(ethnl_header_policy),
  23};
  24
  25static int channels_prepare_data(const struct ethnl_req_info *req_base,
  26                                 struct ethnl_reply_data *reply_base,
  27                                 struct genl_info *info)
  28{
  29        struct channels_reply_data *data = CHANNELS_REPDATA(reply_base);
  30        struct net_device *dev = reply_base->dev;
  31        int ret;
  32
  33        if (!dev->ethtool_ops->get_channels)
  34                return -EOPNOTSUPP;
  35        ret = ethnl_ops_begin(dev);
  36        if (ret < 0)
  37                return ret;
  38        dev->ethtool_ops->get_channels(dev, &data->channels);
  39        ethnl_ops_complete(dev);
  40
  41        return 0;
  42}
  43
  44static int channels_reply_size(const struct ethnl_req_info *req_base,
  45                               const struct ethnl_reply_data *reply_base)
  46{
  47        return nla_total_size(sizeof(u32)) +    /* _CHANNELS_RX_MAX */
  48               nla_total_size(sizeof(u32)) +    /* _CHANNELS_TX_MAX */
  49               nla_total_size(sizeof(u32)) +    /* _CHANNELS_OTHER_MAX */
  50               nla_total_size(sizeof(u32)) +    /* _CHANNELS_COMBINED_MAX */
  51               nla_total_size(sizeof(u32)) +    /* _CHANNELS_RX_COUNT */
  52               nla_total_size(sizeof(u32)) +    /* _CHANNELS_TX_COUNT */
  53               nla_total_size(sizeof(u32)) +    /* _CHANNELS_OTHER_COUNT */
  54               nla_total_size(sizeof(u32));     /* _CHANNELS_COMBINED_COUNT */
  55}
  56
  57static int channels_fill_reply(struct sk_buff *skb,
  58                               const struct ethnl_req_info *req_base,
  59                               const struct ethnl_reply_data *reply_base)
  60{
  61        const struct channels_reply_data *data = CHANNELS_REPDATA(reply_base);
  62        const struct ethtool_channels *channels = &data->channels;
  63
  64        if ((channels->max_rx &&
  65             (nla_put_u32(skb, ETHTOOL_A_CHANNELS_RX_MAX,
  66                          channels->max_rx) ||
  67              nla_put_u32(skb, ETHTOOL_A_CHANNELS_RX_COUNT,
  68                          channels->rx_count))) ||
  69            (channels->max_tx &&
  70             (nla_put_u32(skb, ETHTOOL_A_CHANNELS_TX_MAX,
  71                          channels->max_tx) ||
  72              nla_put_u32(skb, ETHTOOL_A_CHANNELS_TX_COUNT,
  73                          channels->tx_count))) ||
  74            (channels->max_other &&
  75             (nla_put_u32(skb, ETHTOOL_A_CHANNELS_OTHER_MAX,
  76                          channels->max_other) ||
  77              nla_put_u32(skb, ETHTOOL_A_CHANNELS_OTHER_COUNT,
  78                          channels->other_count))) ||
  79            (channels->max_combined &&
  80             (nla_put_u32(skb, ETHTOOL_A_CHANNELS_COMBINED_MAX,
  81                          channels->max_combined) ||
  82              nla_put_u32(skb, ETHTOOL_A_CHANNELS_COMBINED_COUNT,
  83                          channels->combined_count))))
  84                return -EMSGSIZE;
  85
  86        return 0;
  87}
  88
  89const struct ethnl_request_ops ethnl_channels_request_ops = {
  90        .request_cmd            = ETHTOOL_MSG_CHANNELS_GET,
  91        .reply_cmd              = ETHTOOL_MSG_CHANNELS_GET_REPLY,
  92        .hdr_attr               = ETHTOOL_A_CHANNELS_HEADER,
  93        .req_info_size          = sizeof(struct channels_req_info),
  94        .reply_data_size        = sizeof(struct channels_reply_data),
  95
  96        .prepare_data           = channels_prepare_data,
  97        .reply_size             = channels_reply_size,
  98        .fill_reply             = channels_fill_reply,
  99};
 100
 101/* CHANNELS_SET */
 102
 103const struct nla_policy ethnl_channels_set_policy[] = {
 104        [ETHTOOL_A_CHANNELS_HEADER]             =
 105                NLA_POLICY_NESTED(ethnl_header_policy),
 106        [ETHTOOL_A_CHANNELS_RX_COUNT]           = { .type = NLA_U32 },
 107        [ETHTOOL_A_CHANNELS_TX_COUNT]           = { .type = NLA_U32 },
 108        [ETHTOOL_A_CHANNELS_OTHER_COUNT]        = { .type = NLA_U32 },
 109        [ETHTOOL_A_CHANNELS_COMBINED_COUNT]     = { .type = NLA_U32 },
 110};
 111
 112int ethnl_set_channels(struct sk_buff *skb, struct genl_info *info)
 113{
 114        unsigned int from_channel, old_total, i;
 115        bool mod = false, mod_combined = false;
 116        struct ethtool_channels channels = {};
 117        struct ethnl_req_info req_info = {};
 118        struct nlattr **tb = info->attrs;
 119        u32 err_attr, max_rx_in_use = 0;
 120        const struct ethtool_ops *ops;
 121        struct net_device *dev;
 122        int ret;
 123
 124        ret = ethnl_parse_header_dev_get(&req_info,
 125                                         tb[ETHTOOL_A_CHANNELS_HEADER],
 126                                         genl_info_net(info), info->extack,
 127                                         true);
 128        if (ret < 0)
 129                return ret;
 130        dev = req_info.dev;
 131        ops = dev->ethtool_ops;
 132        ret = -EOPNOTSUPP;
 133        if (!ops->get_channels || !ops->set_channels)
 134                goto out_dev;
 135
 136        rtnl_lock();
 137        ret = ethnl_ops_begin(dev);
 138        if (ret < 0)
 139                goto out_rtnl;
 140        ops->get_channels(dev, &channels);
 141        old_total = channels.combined_count +
 142                    max(channels.rx_count, channels.tx_count);
 143
 144        ethnl_update_u32(&channels.rx_count, tb[ETHTOOL_A_CHANNELS_RX_COUNT],
 145                         &mod);
 146        ethnl_update_u32(&channels.tx_count, tb[ETHTOOL_A_CHANNELS_TX_COUNT],
 147                         &mod);
 148        ethnl_update_u32(&channels.other_count,
 149                         tb[ETHTOOL_A_CHANNELS_OTHER_COUNT], &mod);
 150        ethnl_update_u32(&channels.combined_count,
 151                         tb[ETHTOOL_A_CHANNELS_COMBINED_COUNT], &mod_combined);
 152        mod |= mod_combined;
 153        ret = 0;
 154        if (!mod)
 155                goto out_ops;
 156
 157        /* ensure new channel counts are within limits */
 158        if (channels.rx_count > channels.max_rx)
 159                err_attr = ETHTOOL_A_CHANNELS_RX_COUNT;
 160        else if (channels.tx_count > channels.max_tx)
 161                err_attr = ETHTOOL_A_CHANNELS_TX_COUNT;
 162        else if (channels.other_count > channels.max_other)
 163                err_attr = ETHTOOL_A_CHANNELS_OTHER_COUNT;
 164        else if (channels.combined_count > channels.max_combined)
 165                err_attr = ETHTOOL_A_CHANNELS_COMBINED_COUNT;
 166        else
 167                err_attr = 0;
 168        if (err_attr) {
 169                ret = -EINVAL;
 170                NL_SET_ERR_MSG_ATTR(info->extack, tb[err_attr],
 171                                    "requested channel count exceeds maximum");
 172                goto out_ops;
 173        }
 174
 175        /* ensure there is at least one RX and one TX channel */
 176        if (!channels.combined_count && !channels.rx_count)
 177                err_attr = ETHTOOL_A_CHANNELS_RX_COUNT;
 178        else if (!channels.combined_count && !channels.tx_count)
 179                err_attr = ETHTOOL_A_CHANNELS_TX_COUNT;
 180        else
 181                err_attr = 0;
 182        if (err_attr) {
 183                if (mod_combined)
 184                        err_attr = ETHTOOL_A_CHANNELS_COMBINED_COUNT;
 185                ret = -EINVAL;
 186                NL_SET_ERR_MSG_ATTR(info->extack, tb[err_attr],
 187                                    "requested channel counts would result in no RX or TX channel being configured");
 188                goto out_ops;
 189        }
 190
 191        /* ensure the new Rx count fits within the configured Rx flow
 192         * indirection table settings
 193         */
 194        if (netif_is_rxfh_configured(dev) &&
 195            !ethtool_get_max_rxfh_channel(dev, &max_rx_in_use) &&
 196            (channels.combined_count + channels.rx_count) <= max_rx_in_use) {
 197                ret = -EINVAL;
 198                GENL_SET_ERR_MSG(info, "requested channel counts are too low for existing indirection table settings");
 199                goto out_ops;
 200        }
 201
 202        /* Disabling channels, query zero-copy AF_XDP sockets */
 203        from_channel = channels.combined_count +
 204                       min(channels.rx_count, channels.tx_count);
 205        for (i = from_channel; i < old_total; i++)
 206                if (xsk_get_pool_from_qid(dev, i)) {
 207                        ret = -EINVAL;
 208                        GENL_SET_ERR_MSG(info, "requested channel counts are too low for existing zerocopy AF_XDP sockets");
 209                        goto out_ops;
 210                }
 211
 212        ret = dev->ethtool_ops->set_channels(dev, &channels);
 213        if (ret < 0)
 214                goto out_ops;
 215        ethtool_notify(dev, ETHTOOL_MSG_CHANNELS_NTF, NULL);
 216
 217out_ops:
 218        ethnl_ops_complete(dev);
 219out_rtnl:
 220        rtnl_unlock();
 221out_dev:
 222        ethnl_parse_header_dev_put(&req_info);
 223        return ret;
 224}
 225