Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 
0003 #include "netlink.h"
0004 #include "common.h"
0005 #include "bitset.h"
0006 
0007 struct privflags_req_info {
0008     struct ethnl_req_info       base;
0009 };
0010 
0011 struct privflags_reply_data {
0012     struct ethnl_reply_data     base;
0013     const char          (*priv_flag_names)[ETH_GSTRING_LEN];
0014     unsigned int            n_priv_flags;
0015     u32             priv_flags;
0016 };
0017 
0018 #define PRIVFLAGS_REPDATA(__reply_base) \
0019     container_of(__reply_base, struct privflags_reply_data, base)
0020 
0021 const struct nla_policy ethnl_privflags_get_policy[] = {
0022     [ETHTOOL_A_PRIVFLAGS_HEADER]        =
0023         NLA_POLICY_NESTED(ethnl_header_policy),
0024 };
0025 
0026 static int ethnl_get_priv_flags_info(struct net_device *dev,
0027                      unsigned int *count,
0028                      const char (**names)[ETH_GSTRING_LEN])
0029 {
0030     const struct ethtool_ops *ops = dev->ethtool_ops;
0031     int nflags;
0032 
0033     nflags = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
0034     if (nflags < 0)
0035         return nflags;
0036 
0037     if (names) {
0038         *names = kcalloc(nflags, ETH_GSTRING_LEN, GFP_KERNEL);
0039         if (!*names)
0040             return -ENOMEM;
0041         ops->get_strings(dev, ETH_SS_PRIV_FLAGS, (u8 *)*names);
0042     }
0043 
0044     /* We can pass more than 32 private flags to userspace via netlink but
0045      * we cannot get more with ethtool_ops::get_priv_flags(). Note that we
0046      * must not adjust nflags before allocating the space for flag names
0047      * as the buffer must be large enough for all flags.
0048      */
0049     if (WARN_ONCE(nflags > 32,
0050               "device %s reports more than 32 private flags (%d)\n",
0051               netdev_name(dev), nflags))
0052         nflags = 32;
0053     *count = nflags;
0054 
0055     return 0;
0056 }
0057 
0058 static int privflags_prepare_data(const struct ethnl_req_info *req_base,
0059                   struct ethnl_reply_data *reply_base,
0060                   struct genl_info *info)
0061 {
0062     struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base);
0063     struct net_device *dev = reply_base->dev;
0064     const char (*names)[ETH_GSTRING_LEN];
0065     const struct ethtool_ops *ops;
0066     unsigned int nflags;
0067     int ret;
0068 
0069     ops = dev->ethtool_ops;
0070     if (!ops->get_priv_flags || !ops->get_sset_count || !ops->get_strings)
0071         return -EOPNOTSUPP;
0072     ret = ethnl_ops_begin(dev);
0073     if (ret < 0)
0074         return ret;
0075 
0076     ret = ethnl_get_priv_flags_info(dev, &nflags, &names);
0077     if (ret < 0)
0078         goto out_ops;
0079     data->priv_flags = ops->get_priv_flags(dev);
0080     data->priv_flag_names = names;
0081     data->n_priv_flags = nflags;
0082 
0083 out_ops:
0084     ethnl_ops_complete(dev);
0085     return ret;
0086 }
0087 
0088 static int privflags_reply_size(const struct ethnl_req_info *req_base,
0089                 const struct ethnl_reply_data *reply_base)
0090 {
0091     const struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base);
0092     bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
0093     const u32 all_flags = ~(u32)0 >> (32 - data->n_priv_flags);
0094 
0095     return ethnl_bitset32_size(&data->priv_flags, &all_flags,
0096                    data->n_priv_flags,
0097                    data->priv_flag_names, compact);
0098 }
0099 
0100 static int privflags_fill_reply(struct sk_buff *skb,
0101                 const struct ethnl_req_info *req_base,
0102                 const struct ethnl_reply_data *reply_base)
0103 {
0104     const struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base);
0105     bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
0106     const u32 all_flags = ~(u32)0 >> (32 - data->n_priv_flags);
0107 
0108     return ethnl_put_bitset32(skb, ETHTOOL_A_PRIVFLAGS_FLAGS,
0109                   &data->priv_flags, &all_flags,
0110                   data->n_priv_flags, data->priv_flag_names,
0111                   compact);
0112 }
0113 
0114 static void privflags_cleanup_data(struct ethnl_reply_data *reply_data)
0115 {
0116     struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_data);
0117 
0118     kfree(data->priv_flag_names);
0119 }
0120 
0121 const struct ethnl_request_ops ethnl_privflags_request_ops = {
0122     .request_cmd        = ETHTOOL_MSG_PRIVFLAGS_GET,
0123     .reply_cmd      = ETHTOOL_MSG_PRIVFLAGS_GET_REPLY,
0124     .hdr_attr       = ETHTOOL_A_PRIVFLAGS_HEADER,
0125     .req_info_size      = sizeof(struct privflags_req_info),
0126     .reply_data_size    = sizeof(struct privflags_reply_data),
0127 
0128     .prepare_data       = privflags_prepare_data,
0129     .reply_size     = privflags_reply_size,
0130     .fill_reply     = privflags_fill_reply,
0131     .cleanup_data       = privflags_cleanup_data,
0132 };
0133 
0134 /* PRIVFLAGS_SET */
0135 
0136 const struct nla_policy ethnl_privflags_set_policy[] = {
0137     [ETHTOOL_A_PRIVFLAGS_HEADER]        =
0138         NLA_POLICY_NESTED(ethnl_header_policy),
0139     [ETHTOOL_A_PRIVFLAGS_FLAGS]     = { .type = NLA_NESTED },
0140 };
0141 
0142 int ethnl_set_privflags(struct sk_buff *skb, struct genl_info *info)
0143 {
0144     const char (*names)[ETH_GSTRING_LEN] = NULL;
0145     struct ethnl_req_info req_info = {};
0146     struct nlattr **tb = info->attrs;
0147     const struct ethtool_ops *ops;
0148     struct net_device *dev;
0149     unsigned int nflags;
0150     bool mod = false;
0151     bool compact;
0152     u32 flags;
0153     int ret;
0154 
0155     if (!tb[ETHTOOL_A_PRIVFLAGS_FLAGS])
0156         return -EINVAL;
0157     ret = ethnl_bitset_is_compact(tb[ETHTOOL_A_PRIVFLAGS_FLAGS], &compact);
0158     if (ret < 0)
0159         return ret;
0160     ret = ethnl_parse_header_dev_get(&req_info,
0161                      tb[ETHTOOL_A_PRIVFLAGS_HEADER],
0162                      genl_info_net(info), info->extack,
0163                      true);
0164     if (ret < 0)
0165         return ret;
0166     dev = req_info.dev;
0167     ops = dev->ethtool_ops;
0168     ret = -EOPNOTSUPP;
0169     if (!ops->get_priv_flags || !ops->set_priv_flags ||
0170         !ops->get_sset_count || !ops->get_strings)
0171         goto out_dev;
0172 
0173     rtnl_lock();
0174     ret = ethnl_ops_begin(dev);
0175     if (ret < 0)
0176         goto out_rtnl;
0177     ret = ethnl_get_priv_flags_info(dev, &nflags, compact ? NULL : &names);
0178     if (ret < 0)
0179         goto out_ops;
0180     flags = ops->get_priv_flags(dev);
0181 
0182     ret = ethnl_update_bitset32(&flags, nflags,
0183                     tb[ETHTOOL_A_PRIVFLAGS_FLAGS], names,
0184                     info->extack, &mod);
0185     if (ret < 0 || !mod)
0186         goto out_free;
0187     ret = ops->set_priv_flags(dev, flags);
0188     if (ret < 0)
0189         goto out_free;
0190     ethtool_notify(dev, ETHTOOL_MSG_PRIVFLAGS_NTF, NULL);
0191 
0192 out_free:
0193     kfree(names);
0194 out_ops:
0195     ethnl_ops_complete(dev);
0196 out_rtnl:
0197     rtnl_unlock();
0198 out_dev:
0199     ethnl_parse_header_dev_put(&req_info);
0200     return ret;
0201 }