0001
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
0045
0046
0047
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
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 }