linux/net/ethtool/features.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2
   3#include "netlink.h"
   4#include "common.h"
   5#include "bitset.h"
   6
   7struct features_req_info {
   8        struct ethnl_req_info   base;
   9};
  10
  11struct features_reply_data {
  12        struct ethnl_reply_data base;
  13        u32                     hw[ETHTOOL_DEV_FEATURE_WORDS];
  14        u32                     wanted[ETHTOOL_DEV_FEATURE_WORDS];
  15        u32                     active[ETHTOOL_DEV_FEATURE_WORDS];
  16        u32                     nochange[ETHTOOL_DEV_FEATURE_WORDS];
  17        u32                     all[ETHTOOL_DEV_FEATURE_WORDS];
  18};
  19
  20#define FEATURES_REPDATA(__reply_base) \
  21        container_of(__reply_base, struct features_reply_data, base)
  22
  23static const struct nla_policy
  24features_get_policy[ETHTOOL_A_FEATURES_MAX + 1] = {
  25        [ETHTOOL_A_FEATURES_UNSPEC]     = { .type = NLA_REJECT },
  26        [ETHTOOL_A_FEATURES_HEADER]     = { .type = NLA_NESTED },
  27        [ETHTOOL_A_FEATURES_HW]         = { .type = NLA_REJECT },
  28        [ETHTOOL_A_FEATURES_WANTED]     = { .type = NLA_REJECT },
  29        [ETHTOOL_A_FEATURES_ACTIVE]     = { .type = NLA_REJECT },
  30        [ETHTOOL_A_FEATURES_NOCHANGE]   = { .type = NLA_REJECT },
  31};
  32
  33static void ethnl_features_to_bitmap32(u32 *dest, netdev_features_t src)
  34{
  35        unsigned int i;
  36
  37        for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; i++)
  38                dest[i] = src >> (32 * i);
  39}
  40
  41static int features_prepare_data(const struct ethnl_req_info *req_base,
  42                                 struct ethnl_reply_data *reply_base,
  43                                 struct genl_info *info)
  44{
  45        struct features_reply_data *data = FEATURES_REPDATA(reply_base);
  46        struct net_device *dev = reply_base->dev;
  47        netdev_features_t all_features;
  48
  49        ethnl_features_to_bitmap32(data->hw, dev->hw_features);
  50        ethnl_features_to_bitmap32(data->wanted, dev->wanted_features);
  51        ethnl_features_to_bitmap32(data->active, dev->features);
  52        ethnl_features_to_bitmap32(data->nochange, NETIF_F_NEVER_CHANGE);
  53        all_features = GENMASK_ULL(NETDEV_FEATURE_COUNT - 1, 0);
  54        ethnl_features_to_bitmap32(data->all, all_features);
  55
  56        return 0;
  57}
  58
  59static int features_reply_size(const struct ethnl_req_info *req_base,
  60                               const struct ethnl_reply_data *reply_base)
  61{
  62        const struct features_reply_data *data = FEATURES_REPDATA(reply_base);
  63        bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
  64        unsigned int len = 0;
  65        int ret;
  66
  67        ret = ethnl_bitset32_size(data->hw, data->all, NETDEV_FEATURE_COUNT,
  68                                  netdev_features_strings, compact);
  69        if (ret < 0)
  70                return ret;
  71        len += ret;
  72        ret = ethnl_bitset32_size(data->wanted, NULL, NETDEV_FEATURE_COUNT,
  73                                  netdev_features_strings, compact);
  74        if (ret < 0)
  75                return ret;
  76        len += ret;
  77        ret = ethnl_bitset32_size(data->active, NULL, NETDEV_FEATURE_COUNT,
  78                                  netdev_features_strings, compact);
  79        if (ret < 0)
  80                return ret;
  81        len += ret;
  82        ret = ethnl_bitset32_size(data->nochange, NULL, NETDEV_FEATURE_COUNT,
  83                                  netdev_features_strings, compact);
  84        if (ret < 0)
  85                return ret;
  86        len += ret;
  87
  88        return len;
  89}
  90
  91static int features_fill_reply(struct sk_buff *skb,
  92                               const struct ethnl_req_info *req_base,
  93                               const struct ethnl_reply_data *reply_base)
  94{
  95        const struct features_reply_data *data = FEATURES_REPDATA(reply_base);
  96        bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
  97        int ret;
  98
  99        ret = ethnl_put_bitset32(skb, ETHTOOL_A_FEATURES_HW, data->hw,
 100                                 data->all, NETDEV_FEATURE_COUNT,
 101                                 netdev_features_strings, compact);
 102        if (ret < 0)
 103                return ret;
 104        ret = ethnl_put_bitset32(skb, ETHTOOL_A_FEATURES_WANTED, data->wanted,
 105                                 NULL, NETDEV_FEATURE_COUNT,
 106                                 netdev_features_strings, compact);
 107        if (ret < 0)
 108                return ret;
 109        ret = ethnl_put_bitset32(skb, ETHTOOL_A_FEATURES_ACTIVE, data->active,
 110                                 NULL, NETDEV_FEATURE_COUNT,
 111                                 netdev_features_strings, compact);
 112        if (ret < 0)
 113                return ret;
 114        return ethnl_put_bitset32(skb, ETHTOOL_A_FEATURES_NOCHANGE,
 115                                  data->nochange, NULL, NETDEV_FEATURE_COUNT,
 116                                  netdev_features_strings, compact);
 117}
 118
 119const struct ethnl_request_ops ethnl_features_request_ops = {
 120        .request_cmd            = ETHTOOL_MSG_FEATURES_GET,
 121        .reply_cmd              = ETHTOOL_MSG_FEATURES_GET_REPLY,
 122        .hdr_attr               = ETHTOOL_A_FEATURES_HEADER,
 123        .max_attr               = ETHTOOL_A_FEATURES_MAX,
 124        .req_info_size          = sizeof(struct features_req_info),
 125        .reply_data_size        = sizeof(struct features_reply_data),
 126        .request_policy         = features_get_policy,
 127
 128        .prepare_data           = features_prepare_data,
 129        .reply_size             = features_reply_size,
 130        .fill_reply             = features_fill_reply,
 131};
 132
 133/* FEATURES_SET */
 134
 135static const struct nla_policy
 136features_set_policy[ETHTOOL_A_FEATURES_MAX + 1] = {
 137        [ETHTOOL_A_FEATURES_UNSPEC]     = { .type = NLA_REJECT },
 138        [ETHTOOL_A_FEATURES_HEADER]     = { .type = NLA_NESTED },
 139        [ETHTOOL_A_FEATURES_HW]         = { .type = NLA_REJECT },
 140        [ETHTOOL_A_FEATURES_WANTED]     = { .type = NLA_NESTED },
 141        [ETHTOOL_A_FEATURES_ACTIVE]     = { .type = NLA_REJECT },
 142        [ETHTOOL_A_FEATURES_NOCHANGE]   = { .type = NLA_REJECT },
 143};
 144
 145static void ethnl_features_to_bitmap(unsigned long *dest, netdev_features_t val)
 146{
 147        const unsigned int words = BITS_TO_LONGS(NETDEV_FEATURE_COUNT);
 148        unsigned int i;
 149
 150        bitmap_zero(dest, NETDEV_FEATURE_COUNT);
 151        for (i = 0; i < words; i++)
 152                dest[i] = (unsigned long)(val >> (i * BITS_PER_LONG));
 153}
 154
 155static netdev_features_t ethnl_bitmap_to_features(unsigned long *src)
 156{
 157        const unsigned int nft_bits = sizeof(netdev_features_t) * BITS_PER_BYTE;
 158        const unsigned int words = BITS_TO_LONGS(NETDEV_FEATURE_COUNT);
 159        netdev_features_t ret = 0;
 160        unsigned int i;
 161
 162        for (i = 0; i < words; i++)
 163                ret |= (netdev_features_t)(src[i]) << (i * BITS_PER_LONG);
 164        ret &= ~(netdev_features_t)0 >> (nft_bits - NETDEV_FEATURE_COUNT);
 165        return ret;
 166}
 167
 168static int features_send_reply(struct net_device *dev, struct genl_info *info,
 169                               const unsigned long *wanted,
 170                               const unsigned long *wanted_mask,
 171                               const unsigned long *active,
 172                               const unsigned long *active_mask, bool compact)
 173{
 174        struct sk_buff *rskb;
 175        void *reply_payload;
 176        int reply_len = 0;
 177        int ret;
 178
 179        reply_len = ethnl_reply_header_size();
 180        ret = ethnl_bitset_size(wanted, wanted_mask, NETDEV_FEATURE_COUNT,
 181                                netdev_features_strings, compact);
 182        if (ret < 0)
 183                goto err;
 184        reply_len += ret;
 185        ret = ethnl_bitset_size(active, active_mask, NETDEV_FEATURE_COUNT,
 186                                netdev_features_strings, compact);
 187        if (ret < 0)
 188                goto err;
 189        reply_len += ret;
 190
 191        ret = -ENOMEM;
 192        rskb = ethnl_reply_init(reply_len, dev, ETHTOOL_MSG_FEATURES_SET_REPLY,
 193                                ETHTOOL_A_FEATURES_HEADER, info,
 194                                &reply_payload);
 195        if (!rskb)
 196                goto err;
 197
 198        ret = ethnl_put_bitset(rskb, ETHTOOL_A_FEATURES_WANTED, wanted,
 199                               wanted_mask, NETDEV_FEATURE_COUNT,
 200                               netdev_features_strings, compact);
 201        if (ret < 0)
 202                goto nla_put_failure;
 203        ret = ethnl_put_bitset(rskb, ETHTOOL_A_FEATURES_ACTIVE, active,
 204                               active_mask, NETDEV_FEATURE_COUNT,
 205                               netdev_features_strings, compact);
 206        if (ret < 0)
 207                goto nla_put_failure;
 208
 209        genlmsg_end(rskb, reply_payload);
 210        ret = genlmsg_reply(rskb, info);
 211        return ret;
 212
 213nla_put_failure:
 214        nlmsg_free(rskb);
 215        WARN_ONCE(1, "calculated message payload length (%d) not sufficient\n",
 216                  reply_len);
 217err:
 218        GENL_SET_ERR_MSG(info, "failed to send reply message");
 219        return ret;
 220}
 221
 222int ethnl_set_features(struct sk_buff *skb, struct genl_info *info)
 223{
 224        DECLARE_BITMAP(wanted_diff_mask, NETDEV_FEATURE_COUNT);
 225        DECLARE_BITMAP(active_diff_mask, NETDEV_FEATURE_COUNT);
 226        DECLARE_BITMAP(old_active, NETDEV_FEATURE_COUNT);
 227        DECLARE_BITMAP(new_active, NETDEV_FEATURE_COUNT);
 228        DECLARE_BITMAP(req_wanted, NETDEV_FEATURE_COUNT);
 229        DECLARE_BITMAP(req_mask, NETDEV_FEATURE_COUNT);
 230        struct nlattr *tb[ETHTOOL_A_FEATURES_MAX + 1];
 231        struct ethnl_req_info req_info = {};
 232        struct net_device *dev;
 233        bool mod;
 234        int ret;
 235
 236        ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb,
 237                          ETHTOOL_A_FEATURES_MAX, features_set_policy,
 238                          info->extack);
 239        if (ret < 0)
 240                return ret;
 241        if (!tb[ETHTOOL_A_FEATURES_WANTED])
 242                return -EINVAL;
 243        ret = ethnl_parse_header_dev_get(&req_info,
 244                                         tb[ETHTOOL_A_FEATURES_HEADER],
 245                                         genl_info_net(info), info->extack,
 246                                         true);
 247        if (ret < 0)
 248                return ret;
 249        dev = req_info.dev;
 250
 251        rtnl_lock();
 252        ethnl_features_to_bitmap(old_active, dev->features);
 253        ret = ethnl_parse_bitset(req_wanted, req_mask, NETDEV_FEATURE_COUNT,
 254                                 tb[ETHTOOL_A_FEATURES_WANTED],
 255                                 netdev_features_strings, info->extack);
 256        if (ret < 0)
 257                goto out_rtnl;
 258        if (ethnl_bitmap_to_features(req_mask) & ~NETIF_F_ETHTOOL_BITS) {
 259                GENL_SET_ERR_MSG(info, "attempt to change non-ethtool features");
 260                ret = -EINVAL;
 261                goto out_rtnl;
 262        }
 263
 264        /* set req_wanted bits not in req_mask from old_active */
 265        bitmap_and(req_wanted, req_wanted, req_mask, NETDEV_FEATURE_COUNT);
 266        bitmap_andnot(new_active, old_active, req_mask, NETDEV_FEATURE_COUNT);
 267        bitmap_or(req_wanted, new_active, req_wanted, NETDEV_FEATURE_COUNT);
 268        if (bitmap_equal(req_wanted, old_active, NETDEV_FEATURE_COUNT)) {
 269                ret = 0;
 270                goto out_rtnl;
 271        }
 272
 273        dev->wanted_features = ethnl_bitmap_to_features(req_wanted);
 274        __netdev_update_features(dev);
 275        ethnl_features_to_bitmap(new_active, dev->features);
 276        mod = !bitmap_equal(old_active, new_active, NETDEV_FEATURE_COUNT);
 277
 278        ret = 0;
 279        if (!(req_info.flags & ETHTOOL_FLAG_OMIT_REPLY)) {
 280                bool compact = req_info.flags & ETHTOOL_FLAG_COMPACT_BITSETS;
 281
 282                bitmap_xor(wanted_diff_mask, req_wanted, new_active,
 283                           NETDEV_FEATURE_COUNT);
 284                bitmap_xor(active_diff_mask, old_active, new_active,
 285                           NETDEV_FEATURE_COUNT);
 286                bitmap_and(wanted_diff_mask, wanted_diff_mask, req_mask,
 287                           NETDEV_FEATURE_COUNT);
 288                bitmap_and(req_wanted, req_wanted, wanted_diff_mask,
 289                           NETDEV_FEATURE_COUNT);
 290                bitmap_and(new_active, new_active, active_diff_mask,
 291                           NETDEV_FEATURE_COUNT);
 292
 293                ret = features_send_reply(dev, info, req_wanted,
 294                                          wanted_diff_mask, new_active,
 295                                          active_diff_mask, compact);
 296        }
 297        if (mod)
 298                ethtool_notify(dev, ETHTOOL_MSG_FEATURES_NTF, NULL);
 299
 300out_rtnl:
 301        rtnl_unlock();
 302        dev_put(dev);
 303        return ret;
 304}
 305