Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 
0003 #include <net/xdp_sock_drv.h>
0004 
0005 #include "netlink.h"
0006 #include "common.h"
0007 
0008 struct channels_req_info {
0009     struct ethnl_req_info       base;
0010 };
0011 
0012 struct channels_reply_data {
0013     struct ethnl_reply_data     base;
0014     struct ethtool_channels     channels;
0015 };
0016 
0017 #define CHANNELS_REPDATA(__reply_base) \
0018     container_of(__reply_base, struct channels_reply_data, base)
0019 
0020 const struct nla_policy ethnl_channels_get_policy[] = {
0021     [ETHTOOL_A_CHANNELS_HEADER]     =
0022         NLA_POLICY_NESTED(ethnl_header_policy),
0023 };
0024 
0025 static int channels_prepare_data(const struct ethnl_req_info *req_base,
0026                  struct ethnl_reply_data *reply_base,
0027                  struct genl_info *info)
0028 {
0029     struct channels_reply_data *data = CHANNELS_REPDATA(reply_base);
0030     struct net_device *dev = reply_base->dev;
0031     int ret;
0032 
0033     if (!dev->ethtool_ops->get_channels)
0034         return -EOPNOTSUPP;
0035     ret = ethnl_ops_begin(dev);
0036     if (ret < 0)
0037         return ret;
0038     dev->ethtool_ops->get_channels(dev, &data->channels);
0039     ethnl_ops_complete(dev);
0040 
0041     return 0;
0042 }
0043 
0044 static int channels_reply_size(const struct ethnl_req_info *req_base,
0045                    const struct ethnl_reply_data *reply_base)
0046 {
0047     return nla_total_size(sizeof(u32)) +    /* _CHANNELS_RX_MAX */
0048            nla_total_size(sizeof(u32)) +    /* _CHANNELS_TX_MAX */
0049            nla_total_size(sizeof(u32)) +    /* _CHANNELS_OTHER_MAX */
0050            nla_total_size(sizeof(u32)) +    /* _CHANNELS_COMBINED_MAX */
0051            nla_total_size(sizeof(u32)) +    /* _CHANNELS_RX_COUNT */
0052            nla_total_size(sizeof(u32)) +    /* _CHANNELS_TX_COUNT */
0053            nla_total_size(sizeof(u32)) +    /* _CHANNELS_OTHER_COUNT */
0054            nla_total_size(sizeof(u32)); /* _CHANNELS_COMBINED_COUNT */
0055 }
0056 
0057 static int channels_fill_reply(struct sk_buff *skb,
0058                    const struct ethnl_req_info *req_base,
0059                    const struct ethnl_reply_data *reply_base)
0060 {
0061     const struct channels_reply_data *data = CHANNELS_REPDATA(reply_base);
0062     const struct ethtool_channels *channels = &data->channels;
0063 
0064     if ((channels->max_rx &&
0065          (nla_put_u32(skb, ETHTOOL_A_CHANNELS_RX_MAX,
0066               channels->max_rx) ||
0067           nla_put_u32(skb, ETHTOOL_A_CHANNELS_RX_COUNT,
0068               channels->rx_count))) ||
0069         (channels->max_tx &&
0070          (nla_put_u32(skb, ETHTOOL_A_CHANNELS_TX_MAX,
0071               channels->max_tx) ||
0072           nla_put_u32(skb, ETHTOOL_A_CHANNELS_TX_COUNT,
0073               channels->tx_count))) ||
0074         (channels->max_other &&
0075          (nla_put_u32(skb, ETHTOOL_A_CHANNELS_OTHER_MAX,
0076               channels->max_other) ||
0077           nla_put_u32(skb, ETHTOOL_A_CHANNELS_OTHER_COUNT,
0078               channels->other_count))) ||
0079         (channels->max_combined &&
0080          (nla_put_u32(skb, ETHTOOL_A_CHANNELS_COMBINED_MAX,
0081               channels->max_combined) ||
0082           nla_put_u32(skb, ETHTOOL_A_CHANNELS_COMBINED_COUNT,
0083               channels->combined_count))))
0084         return -EMSGSIZE;
0085 
0086     return 0;
0087 }
0088 
0089 const struct ethnl_request_ops ethnl_channels_request_ops = {
0090     .request_cmd        = ETHTOOL_MSG_CHANNELS_GET,
0091     .reply_cmd      = ETHTOOL_MSG_CHANNELS_GET_REPLY,
0092     .hdr_attr       = ETHTOOL_A_CHANNELS_HEADER,
0093     .req_info_size      = sizeof(struct channels_req_info),
0094     .reply_data_size    = sizeof(struct channels_reply_data),
0095 
0096     .prepare_data       = channels_prepare_data,
0097     .reply_size     = channels_reply_size,
0098     .fill_reply     = channels_fill_reply,
0099 };
0100 
0101 /* CHANNELS_SET */
0102 
0103 const struct nla_policy ethnl_channels_set_policy[] = {
0104     [ETHTOOL_A_CHANNELS_HEADER]     =
0105         NLA_POLICY_NESTED(ethnl_header_policy),
0106     [ETHTOOL_A_CHANNELS_RX_COUNT]       = { .type = NLA_U32 },
0107     [ETHTOOL_A_CHANNELS_TX_COUNT]       = { .type = NLA_U32 },
0108     [ETHTOOL_A_CHANNELS_OTHER_COUNT]    = { .type = NLA_U32 },
0109     [ETHTOOL_A_CHANNELS_COMBINED_COUNT] = { .type = NLA_U32 },
0110 };
0111 
0112 int ethnl_set_channels(struct sk_buff *skb, struct genl_info *info)
0113 {
0114     unsigned int from_channel, old_total, i;
0115     bool mod = false, mod_combined = false;
0116     struct ethtool_channels channels = {};
0117     struct ethnl_req_info req_info = {};
0118     struct nlattr **tb = info->attrs;
0119     u32 err_attr, max_rx_in_use = 0;
0120     const struct ethtool_ops *ops;
0121     struct net_device *dev;
0122     int ret;
0123 
0124     ret = ethnl_parse_header_dev_get(&req_info,
0125                      tb[ETHTOOL_A_CHANNELS_HEADER],
0126                      genl_info_net(info), info->extack,
0127                      true);
0128     if (ret < 0)
0129         return ret;
0130     dev = req_info.dev;
0131     ops = dev->ethtool_ops;
0132     ret = -EOPNOTSUPP;
0133     if (!ops->get_channels || !ops->set_channels)
0134         goto out_dev;
0135 
0136     rtnl_lock();
0137     ret = ethnl_ops_begin(dev);
0138     if (ret < 0)
0139         goto out_rtnl;
0140     ops->get_channels(dev, &channels);
0141     old_total = channels.combined_count +
0142             max(channels.rx_count, channels.tx_count);
0143 
0144     ethnl_update_u32(&channels.rx_count, tb[ETHTOOL_A_CHANNELS_RX_COUNT],
0145              &mod);
0146     ethnl_update_u32(&channels.tx_count, tb[ETHTOOL_A_CHANNELS_TX_COUNT],
0147              &mod);
0148     ethnl_update_u32(&channels.other_count,
0149              tb[ETHTOOL_A_CHANNELS_OTHER_COUNT], &mod);
0150     ethnl_update_u32(&channels.combined_count,
0151              tb[ETHTOOL_A_CHANNELS_COMBINED_COUNT], &mod_combined);
0152     mod |= mod_combined;
0153     ret = 0;
0154     if (!mod)
0155         goto out_ops;
0156 
0157     /* ensure new channel counts are within limits */
0158     if (channels.rx_count > channels.max_rx)
0159         err_attr = ETHTOOL_A_CHANNELS_RX_COUNT;
0160     else if (channels.tx_count > channels.max_tx)
0161         err_attr = ETHTOOL_A_CHANNELS_TX_COUNT;
0162     else if (channels.other_count > channels.max_other)
0163         err_attr = ETHTOOL_A_CHANNELS_OTHER_COUNT;
0164     else if (channels.combined_count > channels.max_combined)
0165         err_attr = ETHTOOL_A_CHANNELS_COMBINED_COUNT;
0166     else
0167         err_attr = 0;
0168     if (err_attr) {
0169         ret = -EINVAL;
0170         NL_SET_ERR_MSG_ATTR(info->extack, tb[err_attr],
0171                     "requested channel count exceeds maximum");
0172         goto out_ops;
0173     }
0174 
0175     /* ensure there is at least one RX and one TX channel */
0176     if (!channels.combined_count && !channels.rx_count)
0177         err_attr = ETHTOOL_A_CHANNELS_RX_COUNT;
0178     else if (!channels.combined_count && !channels.tx_count)
0179         err_attr = ETHTOOL_A_CHANNELS_TX_COUNT;
0180     else
0181         err_attr = 0;
0182     if (err_attr) {
0183         if (mod_combined)
0184             err_attr = ETHTOOL_A_CHANNELS_COMBINED_COUNT;
0185         ret = -EINVAL;
0186         NL_SET_ERR_MSG_ATTR(info->extack, tb[err_attr],
0187                     "requested channel counts would result in no RX or TX channel being configured");
0188         goto out_ops;
0189     }
0190 
0191     /* ensure the new Rx count fits within the configured Rx flow
0192      * indirection table settings
0193      */
0194     if (netif_is_rxfh_configured(dev) &&
0195         !ethtool_get_max_rxfh_channel(dev, &max_rx_in_use) &&
0196         (channels.combined_count + channels.rx_count) <= max_rx_in_use) {
0197         ret = -EINVAL;
0198         GENL_SET_ERR_MSG(info, "requested channel counts are too low for existing indirection table settings");
0199         goto out_ops;
0200     }
0201 
0202     /* Disabling channels, query zero-copy AF_XDP sockets */
0203     from_channel = channels.combined_count +
0204                min(channels.rx_count, channels.tx_count);
0205     for (i = from_channel; i < old_total; i++)
0206         if (xsk_get_pool_from_qid(dev, i)) {
0207             ret = -EINVAL;
0208             GENL_SET_ERR_MSG(info, "requested channel counts are too low for existing zerocopy AF_XDP sockets");
0209             goto out_ops;
0210         }
0211 
0212     ret = dev->ethtool_ops->set_channels(dev, &channels);
0213     if (ret < 0)
0214         goto out_ops;
0215     ethtool_notify(dev, ETHTOOL_MSG_CHANNELS_NTF, NULL);
0216 
0217 out_ops:
0218     ethnl_ops_complete(dev);
0219 out_rtnl:
0220     rtnl_unlock();
0221 out_dev:
0222     ethnl_parse_header_dev_put(&req_info);
0223     return ret;
0224 }