linux/net/ethtool/privflags.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 privflags_req_info {
   8        struct ethnl_req_info           base;
   9};
  10
  11struct privflags_reply_data {
  12        struct ethnl_reply_data         base;
  13        const char                      (*priv_flag_names)[ETH_GSTRING_LEN];
  14        unsigned int                    n_priv_flags;
  15        u32                             priv_flags;
  16};
  17
  18#define PRIVFLAGS_REPDATA(__reply_base) \
  19        container_of(__reply_base, struct privflags_reply_data, base)
  20
  21const struct nla_policy ethnl_privflags_get_policy[] = {
  22        [ETHTOOL_A_PRIVFLAGS_HEADER]            =
  23                NLA_POLICY_NESTED(ethnl_header_policy),
  24};
  25
  26static int ethnl_get_priv_flags_info(struct net_device *dev,
  27                                     unsigned int *count,
  28                                     const char (**names)[ETH_GSTRING_LEN])
  29{
  30        const struct ethtool_ops *ops = dev->ethtool_ops;
  31        int nflags;
  32
  33        nflags = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
  34        if (nflags < 0)
  35                return nflags;
  36
  37        if (names) {
  38                *names = kcalloc(nflags, ETH_GSTRING_LEN, GFP_KERNEL);
  39                if (!*names)
  40                        return -ENOMEM;
  41                ops->get_strings(dev, ETH_SS_PRIV_FLAGS, (u8 *)*names);
  42        }
  43
  44        /* We can pass more than 32 private flags to userspace via netlink but
  45         * we cannot get more with ethtool_ops::get_priv_flags(). Note that we
  46         * must not adjust nflags before allocating the space for flag names
  47         * as the buffer must be large enough for all flags.
  48         */
  49        if (WARN_ONCE(nflags > 32,
  50                      "device %s reports more than 32 private flags (%d)\n",
  51                      netdev_name(dev), nflags))
  52                nflags = 32;
  53        *count = nflags;
  54
  55        return 0;
  56}
  57
  58static int privflags_prepare_data(const struct ethnl_req_info *req_base,
  59                                  struct ethnl_reply_data *reply_base,
  60                                  struct genl_info *info)
  61{
  62        struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base);
  63        struct net_device *dev = reply_base->dev;
  64        const char (*names)[ETH_GSTRING_LEN];
  65        const struct ethtool_ops *ops;
  66        unsigned int nflags;
  67        int ret;
  68
  69        ops = dev->ethtool_ops;
  70        if (!ops->get_priv_flags || !ops->get_sset_count || !ops->get_strings)
  71                return -EOPNOTSUPP;
  72        ret = ethnl_ops_begin(dev);
  73        if (ret < 0)
  74                return ret;
  75
  76        ret = ethnl_get_priv_flags_info(dev, &nflags, &names);
  77        if (ret < 0)
  78                goto out_ops;
  79        data->priv_flags = ops->get_priv_flags(dev);
  80        data->priv_flag_names = names;
  81        data->n_priv_flags = nflags;
  82
  83out_ops:
  84        ethnl_ops_complete(dev);
  85        return ret;
  86}
  87
  88static int privflags_reply_size(const struct ethnl_req_info *req_base,
  89                                const struct ethnl_reply_data *reply_base)
  90{
  91        const struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base);
  92        bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
  93        const u32 all_flags = ~(u32)0 >> (32 - data->n_priv_flags);
  94
  95        return ethnl_bitset32_size(&data->priv_flags, &all_flags,
  96                                   data->n_priv_flags,
  97                                   data->priv_flag_names, compact);
  98}
  99
 100static int privflags_fill_reply(struct sk_buff *skb,
 101                                const struct ethnl_req_info *req_base,
 102                                const struct ethnl_reply_data *reply_base)
 103{
 104        const struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base);
 105        bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
 106        const u32 all_flags = ~(u32)0 >> (32 - data->n_priv_flags);
 107
 108        return ethnl_put_bitset32(skb, ETHTOOL_A_PRIVFLAGS_FLAGS,
 109                                  &data->priv_flags, &all_flags,
 110                                  data->n_priv_flags, data->priv_flag_names,
 111                                  compact);
 112}
 113
 114static void privflags_cleanup_data(struct ethnl_reply_data *reply_data)
 115{
 116        struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_data);
 117
 118        kfree(data->priv_flag_names);
 119}
 120
 121const struct ethnl_request_ops ethnl_privflags_request_ops = {
 122        .request_cmd            = ETHTOOL_MSG_PRIVFLAGS_GET,
 123        .reply_cmd              = ETHTOOL_MSG_PRIVFLAGS_GET_REPLY,
 124        .hdr_attr               = ETHTOOL_A_PRIVFLAGS_HEADER,
 125        .req_info_size          = sizeof(struct privflags_req_info),
 126        .reply_data_size        = sizeof(struct privflags_reply_data),
 127
 128        .prepare_data           = privflags_prepare_data,
 129        .reply_size             = privflags_reply_size,
 130        .fill_reply             = privflags_fill_reply,
 131        .cleanup_data           = privflags_cleanup_data,
 132};
 133
 134/* PRIVFLAGS_SET */
 135
 136const struct nla_policy ethnl_privflags_set_policy[] = {
 137        [ETHTOOL_A_PRIVFLAGS_HEADER]            =
 138                NLA_POLICY_NESTED(ethnl_header_policy),
 139        [ETHTOOL_A_PRIVFLAGS_FLAGS]             = { .type = NLA_NESTED },
 140};
 141
 142int ethnl_set_privflags(struct sk_buff *skb, struct genl_info *info)
 143{
 144        const char (*names)[ETH_GSTRING_LEN] = NULL;
 145        struct ethnl_req_info req_info = {};
 146        struct nlattr **tb = info->attrs;
 147        const struct ethtool_ops *ops;
 148        struct net_device *dev;
 149        unsigned int nflags;
 150        bool mod = false;
 151        bool compact;
 152        u32 flags;
 153        int ret;
 154
 155        if (!tb[ETHTOOL_A_PRIVFLAGS_FLAGS])
 156                return -EINVAL;
 157        ret = ethnl_bitset_is_compact(tb[ETHTOOL_A_PRIVFLAGS_FLAGS], &compact);
 158        if (ret < 0)
 159                return ret;
 160        ret = ethnl_parse_header_dev_get(&req_info,
 161                                         tb[ETHTOOL_A_PRIVFLAGS_HEADER],
 162                                         genl_info_net(info), info->extack,
 163                                         true);
 164        if (ret < 0)
 165                return ret;
 166        dev = req_info.dev;
 167        ops = dev->ethtool_ops;
 168        ret = -EOPNOTSUPP;
 169        if (!ops->get_priv_flags || !ops->set_priv_flags ||
 170            !ops->get_sset_count || !ops->get_strings)
 171                goto out_dev;
 172
 173        rtnl_lock();
 174        ret = ethnl_ops_begin(dev);
 175        if (ret < 0)
 176                goto out_rtnl;
 177        ret = ethnl_get_priv_flags_info(dev, &nflags, compact ? NULL : &names);
 178        if (ret < 0)
 179                goto out_ops;
 180        flags = ops->get_priv_flags(dev);
 181
 182        ret = ethnl_update_bitset32(&flags, nflags,
 183                                    tb[ETHTOOL_A_PRIVFLAGS_FLAGS], names,
 184                                    info->extack, &mod);
 185        if (ret < 0 || !mod)
 186                goto out_free;
 187        ret = ops->set_priv_flags(dev, flags);
 188        if (ret < 0)
 189                goto out_free;
 190        ethtool_notify(dev, ETHTOOL_MSG_PRIVFLAGS_NTF, NULL);
 191
 192out_free:
 193        kfree(names);
 194out_ops:
 195        ethnl_ops_complete(dev);
 196out_rtnl:
 197        rtnl_unlock();
 198out_dev:
 199        dev_put(dev);
 200        return ret;
 201}
 202