Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 
0003 #include <linux/ethtool.h>
0004 #include <linux/phy.h>
0005 #include "netlink.h"
0006 #include "common.h"
0007 
0008 struct strset_info {
0009     bool per_dev;
0010     bool free_strings;
0011     unsigned int count;
0012     const char (*strings)[ETH_GSTRING_LEN];
0013 };
0014 
0015 static const struct strset_info info_template[] = {
0016     [ETH_SS_TEST] = {
0017         .per_dev    = true,
0018     },
0019     [ETH_SS_STATS] = {
0020         .per_dev    = true,
0021     },
0022     [ETH_SS_PRIV_FLAGS] = {
0023         .per_dev    = true,
0024     },
0025     [ETH_SS_FEATURES] = {
0026         .per_dev    = false,
0027         .count      = ARRAY_SIZE(netdev_features_strings),
0028         .strings    = netdev_features_strings,
0029     },
0030     [ETH_SS_RSS_HASH_FUNCS] = {
0031         .per_dev    = false,
0032         .count      = ARRAY_SIZE(rss_hash_func_strings),
0033         .strings    = rss_hash_func_strings,
0034     },
0035     [ETH_SS_TUNABLES] = {
0036         .per_dev    = false,
0037         .count      = ARRAY_SIZE(tunable_strings),
0038         .strings    = tunable_strings,
0039     },
0040     [ETH_SS_PHY_STATS] = {
0041         .per_dev    = true,
0042     },
0043     [ETH_SS_PHY_TUNABLES] = {
0044         .per_dev    = false,
0045         .count      = ARRAY_SIZE(phy_tunable_strings),
0046         .strings    = phy_tunable_strings,
0047     },
0048     [ETH_SS_LINK_MODES] = {
0049         .per_dev    = false,
0050         .count      = __ETHTOOL_LINK_MODE_MASK_NBITS,
0051         .strings    = link_mode_names,
0052     },
0053     [ETH_SS_MSG_CLASSES] = {
0054         .per_dev    = false,
0055         .count      = NETIF_MSG_CLASS_COUNT,
0056         .strings    = netif_msg_class_names,
0057     },
0058     [ETH_SS_WOL_MODES] = {
0059         .per_dev    = false,
0060         .count      = WOL_MODE_COUNT,
0061         .strings    = wol_mode_names,
0062     },
0063     [ETH_SS_SOF_TIMESTAMPING] = {
0064         .per_dev    = false,
0065         .count      = __SOF_TIMESTAMPING_CNT,
0066         .strings    = sof_timestamping_names,
0067     },
0068     [ETH_SS_TS_TX_TYPES] = {
0069         .per_dev    = false,
0070         .count      = __HWTSTAMP_TX_CNT,
0071         .strings    = ts_tx_type_names,
0072     },
0073     [ETH_SS_TS_RX_FILTERS] = {
0074         .per_dev    = false,
0075         .count      = __HWTSTAMP_FILTER_CNT,
0076         .strings    = ts_rx_filter_names,
0077     },
0078     [ETH_SS_UDP_TUNNEL_TYPES] = {
0079         .per_dev    = false,
0080         .count      = __ETHTOOL_UDP_TUNNEL_TYPE_CNT,
0081         .strings    = udp_tunnel_type_names,
0082     },
0083     [ETH_SS_STATS_STD] = {
0084         .per_dev    = false,
0085         .count      = __ETHTOOL_STATS_CNT,
0086         .strings    = stats_std_names,
0087     },
0088     [ETH_SS_STATS_ETH_PHY] = {
0089         .per_dev    = false,
0090         .count      = __ETHTOOL_A_STATS_ETH_PHY_CNT,
0091         .strings    = stats_eth_phy_names,
0092     },
0093     [ETH_SS_STATS_ETH_MAC] = {
0094         .per_dev    = false,
0095         .count      = __ETHTOOL_A_STATS_ETH_MAC_CNT,
0096         .strings    = stats_eth_mac_names,
0097     },
0098     [ETH_SS_STATS_ETH_CTRL] = {
0099         .per_dev    = false,
0100         .count      = __ETHTOOL_A_STATS_ETH_CTRL_CNT,
0101         .strings    = stats_eth_ctrl_names,
0102     },
0103     [ETH_SS_STATS_RMON] = {
0104         .per_dev    = false,
0105         .count      = __ETHTOOL_A_STATS_RMON_CNT,
0106         .strings    = stats_rmon_names,
0107     },
0108 };
0109 
0110 struct strset_req_info {
0111     struct ethnl_req_info       base;
0112     u32             req_ids;
0113     bool                counts_only;
0114 };
0115 
0116 #define STRSET_REQINFO(__req_base) \
0117     container_of(__req_base, struct strset_req_info, base)
0118 
0119 struct strset_reply_data {
0120     struct ethnl_reply_data     base;
0121     struct strset_info      sets[ETH_SS_COUNT];
0122 };
0123 
0124 #define STRSET_REPDATA(__reply_base) \
0125     container_of(__reply_base, struct strset_reply_data, base)
0126 
0127 const struct nla_policy ethnl_strset_get_policy[] = {
0128     [ETHTOOL_A_STRSET_HEADER]   =
0129         NLA_POLICY_NESTED(ethnl_header_policy),
0130     [ETHTOOL_A_STRSET_STRINGSETS]   = { .type = NLA_NESTED },
0131     [ETHTOOL_A_STRSET_COUNTS_ONLY]  = { .type = NLA_FLAG },
0132 };
0133 
0134 static const struct nla_policy get_stringset_policy[] = {
0135     [ETHTOOL_A_STRINGSET_ID]    = { .type = NLA_U32 },
0136 };
0137 
0138 /**
0139  * strset_include() - test if a string set should be included in reply
0140  * @info: parsed client request
0141  * @data: pointer to request data structure
0142  * @id:   id of string set to check (ETH_SS_* constants)
0143  */
0144 static bool strset_include(const struct strset_req_info *info,
0145                const struct strset_reply_data *data, u32 id)
0146 {
0147     bool per_dev;
0148 
0149     BUILD_BUG_ON(ETH_SS_COUNT >= BITS_PER_BYTE * sizeof(info->req_ids));
0150 
0151     if (info->req_ids)
0152         return info->req_ids & (1U << id);
0153     per_dev = data->sets[id].per_dev;
0154     if (!per_dev && !data->sets[id].strings)
0155         return false;
0156 
0157     return data->base.dev ? per_dev : !per_dev;
0158 }
0159 
0160 static int strset_get_id(const struct nlattr *nest, u32 *val,
0161              struct netlink_ext_ack *extack)
0162 {
0163     struct nlattr *tb[ARRAY_SIZE(get_stringset_policy)];
0164     int ret;
0165 
0166     ret = nla_parse_nested(tb, ARRAY_SIZE(get_stringset_policy) - 1, nest,
0167                    get_stringset_policy, extack);
0168     if (ret < 0)
0169         return ret;
0170     if (!tb[ETHTOOL_A_STRINGSET_ID])
0171         return -EINVAL;
0172 
0173     *val = nla_get_u32(tb[ETHTOOL_A_STRINGSET_ID]);
0174     return 0;
0175 }
0176 
0177 static const struct nla_policy strset_stringsets_policy[] = {
0178     [ETHTOOL_A_STRINGSETS_STRINGSET]    = { .type = NLA_NESTED },
0179 };
0180 
0181 static int strset_parse_request(struct ethnl_req_info *req_base,
0182                 struct nlattr **tb,
0183                 struct netlink_ext_ack *extack)
0184 {
0185     struct strset_req_info *req_info = STRSET_REQINFO(req_base);
0186     struct nlattr *nest = tb[ETHTOOL_A_STRSET_STRINGSETS];
0187     struct nlattr *attr;
0188     int rem, ret;
0189 
0190     if (!nest)
0191         return 0;
0192     ret = nla_validate_nested(nest,
0193                   ARRAY_SIZE(strset_stringsets_policy) - 1,
0194                   strset_stringsets_policy, extack);
0195     if (ret < 0)
0196         return ret;
0197 
0198     req_info->counts_only = tb[ETHTOOL_A_STRSET_COUNTS_ONLY];
0199     nla_for_each_nested(attr, nest, rem) {
0200         u32 id;
0201 
0202         if (WARN_ONCE(nla_type(attr) != ETHTOOL_A_STRINGSETS_STRINGSET,
0203                   "unexpected attrtype %u in ETHTOOL_A_STRSET_STRINGSETS\n",
0204                   nla_type(attr)))
0205             return -EINVAL;
0206 
0207         ret = strset_get_id(attr, &id, extack);
0208         if (ret < 0)
0209             return ret;
0210         if (id >= ETH_SS_COUNT) {
0211             NL_SET_ERR_MSG_ATTR(extack, attr,
0212                         "unknown string set id");
0213             return -EOPNOTSUPP;
0214         }
0215 
0216         req_info->req_ids |= (1U << id);
0217     }
0218 
0219     return 0;
0220 }
0221 
0222 static void strset_cleanup_data(struct ethnl_reply_data *reply_base)
0223 {
0224     struct strset_reply_data *data = STRSET_REPDATA(reply_base);
0225     unsigned int i;
0226 
0227     for (i = 0; i < ETH_SS_COUNT; i++)
0228         if (data->sets[i].free_strings) {
0229             kfree(data->sets[i].strings);
0230             data->sets[i].strings = NULL;
0231             data->sets[i].free_strings = false;
0232         }
0233 }
0234 
0235 static int strset_prepare_set(struct strset_info *info, struct net_device *dev,
0236                   unsigned int id, bool counts_only)
0237 {
0238     const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops;
0239     const struct ethtool_ops *ops = dev->ethtool_ops;
0240     void *strings;
0241     int count, ret;
0242 
0243     if (id == ETH_SS_PHY_STATS && dev->phydev &&
0244         !ops->get_ethtool_phy_stats && phy_ops &&
0245         phy_ops->get_sset_count)
0246         ret = phy_ops->get_sset_count(dev->phydev);
0247     else if (ops->get_sset_count && ops->get_strings)
0248         ret = ops->get_sset_count(dev, id);
0249     else
0250         ret = -EOPNOTSUPP;
0251     if (ret <= 0) {
0252         info->count = 0;
0253         return 0;
0254     }
0255 
0256     count = ret;
0257     if (!counts_only) {
0258         strings = kcalloc(count, ETH_GSTRING_LEN, GFP_KERNEL);
0259         if (!strings)
0260             return -ENOMEM;
0261         if (id == ETH_SS_PHY_STATS && dev->phydev &&
0262             !ops->get_ethtool_phy_stats && phy_ops &&
0263             phy_ops->get_strings)
0264             phy_ops->get_strings(dev->phydev, strings);
0265         else
0266             ops->get_strings(dev, id, strings);
0267         info->strings = strings;
0268         info->free_strings = true;
0269     }
0270     info->count = count;
0271 
0272     return 0;
0273 }
0274 
0275 static int strset_prepare_data(const struct ethnl_req_info *req_base,
0276                    struct ethnl_reply_data *reply_base,
0277                    struct genl_info *info)
0278 {
0279     const struct strset_req_info *req_info = STRSET_REQINFO(req_base);
0280     struct strset_reply_data *data = STRSET_REPDATA(reply_base);
0281     struct net_device *dev = reply_base->dev;
0282     unsigned int i;
0283     int ret;
0284 
0285     BUILD_BUG_ON(ARRAY_SIZE(info_template) != ETH_SS_COUNT);
0286     memcpy(&data->sets, &info_template, sizeof(data->sets));
0287 
0288     if (!dev) {
0289         for (i = 0; i < ETH_SS_COUNT; i++) {
0290             if ((req_info->req_ids & (1U << i)) &&
0291                 data->sets[i].per_dev) {
0292                 if (info)
0293                     GENL_SET_ERR_MSG(info, "requested per device strings without dev");
0294                 return -EINVAL;
0295             }
0296         }
0297         return 0;
0298     }
0299 
0300     ret = ethnl_ops_begin(dev);
0301     if (ret < 0)
0302         goto err_strset;
0303     for (i = 0; i < ETH_SS_COUNT; i++) {
0304         if (!strset_include(req_info, data, i) ||
0305             !data->sets[i].per_dev)
0306             continue;
0307 
0308         ret = strset_prepare_set(&data->sets[i], dev, i,
0309                      req_info->counts_only);
0310         if (ret < 0)
0311             goto err_ops;
0312     }
0313     ethnl_ops_complete(dev);
0314 
0315     return 0;
0316 err_ops:
0317     ethnl_ops_complete(dev);
0318 err_strset:
0319     strset_cleanup_data(reply_base);
0320     return ret;
0321 }
0322 
0323 /* calculate size of ETHTOOL_A_STRSET_STRINGSET nest for one string set */
0324 static int strset_set_size(const struct strset_info *info, bool counts_only)
0325 {
0326     unsigned int len = 0;
0327     unsigned int i;
0328 
0329     if (info->count == 0)
0330         return 0;
0331     if (counts_only)
0332         return nla_total_size(2 * nla_total_size(sizeof(u32)));
0333 
0334     for (i = 0; i < info->count; i++) {
0335         const char *str = info->strings[i];
0336 
0337         /* ETHTOOL_A_STRING_INDEX, ETHTOOL_A_STRING_VALUE, nest */
0338         len += nla_total_size(nla_total_size(sizeof(u32)) +
0339                       ethnl_strz_size(str));
0340     }
0341     /* ETHTOOL_A_STRINGSET_ID, ETHTOOL_A_STRINGSET_COUNT */
0342     len = 2 * nla_total_size(sizeof(u32)) + nla_total_size(len);
0343 
0344     return nla_total_size(len);
0345 }
0346 
0347 static int strset_reply_size(const struct ethnl_req_info *req_base,
0348                  const struct ethnl_reply_data *reply_base)
0349 {
0350     const struct strset_req_info *req_info = STRSET_REQINFO(req_base);
0351     const struct strset_reply_data *data = STRSET_REPDATA(reply_base);
0352     unsigned int i;
0353     int len = 0;
0354     int ret;
0355 
0356     len += nla_total_size(0); /* ETHTOOL_A_STRSET_STRINGSETS */
0357 
0358     for (i = 0; i < ETH_SS_COUNT; i++) {
0359         const struct strset_info *set_info = &data->sets[i];
0360 
0361         if (!strset_include(req_info, data, i))
0362             continue;
0363 
0364         ret = strset_set_size(set_info, req_info->counts_only);
0365         if (ret < 0)
0366             return ret;
0367         len += ret;
0368     }
0369 
0370     return len;
0371 }
0372 
0373 /* fill one string into reply */
0374 static int strset_fill_string(struct sk_buff *skb,
0375                   const struct strset_info *set_info, u32 idx)
0376 {
0377     struct nlattr *string_attr;
0378     const char *value;
0379 
0380     value = set_info->strings[idx];
0381 
0382     string_attr = nla_nest_start(skb, ETHTOOL_A_STRINGS_STRING);
0383     if (!string_attr)
0384         return -EMSGSIZE;
0385     if (nla_put_u32(skb, ETHTOOL_A_STRING_INDEX, idx) ||
0386         ethnl_put_strz(skb, ETHTOOL_A_STRING_VALUE, value))
0387         goto nla_put_failure;
0388     nla_nest_end(skb, string_attr);
0389 
0390     return 0;
0391 nla_put_failure:
0392     nla_nest_cancel(skb, string_attr);
0393     return -EMSGSIZE;
0394 }
0395 
0396 /* fill one string set into reply */
0397 static int strset_fill_set(struct sk_buff *skb,
0398                const struct strset_info *set_info, u32 id,
0399                bool counts_only)
0400 {
0401     struct nlattr *stringset_attr;
0402     struct nlattr *strings_attr;
0403     unsigned int i;
0404 
0405     if (!set_info->per_dev && !set_info->strings)
0406         return -EOPNOTSUPP;
0407     if (set_info->count == 0)
0408         return 0;
0409     stringset_attr = nla_nest_start(skb, ETHTOOL_A_STRINGSETS_STRINGSET);
0410     if (!stringset_attr)
0411         return -EMSGSIZE;
0412 
0413     if (nla_put_u32(skb, ETHTOOL_A_STRINGSET_ID, id) ||
0414         nla_put_u32(skb, ETHTOOL_A_STRINGSET_COUNT, set_info->count))
0415         goto nla_put_failure;
0416 
0417     if (!counts_only) {
0418         strings_attr = nla_nest_start(skb, ETHTOOL_A_STRINGSET_STRINGS);
0419         if (!strings_attr)
0420             goto nla_put_failure;
0421         for (i = 0; i < set_info->count; i++) {
0422             if (strset_fill_string(skb, set_info, i) < 0)
0423                 goto nla_put_failure;
0424         }
0425         nla_nest_end(skb, strings_attr);
0426     }
0427 
0428     nla_nest_end(skb, stringset_attr);
0429     return 0;
0430 
0431 nla_put_failure:
0432     nla_nest_cancel(skb, stringset_attr);
0433     return -EMSGSIZE;
0434 }
0435 
0436 static int strset_fill_reply(struct sk_buff *skb,
0437                  const struct ethnl_req_info *req_base,
0438                  const struct ethnl_reply_data *reply_base)
0439 {
0440     const struct strset_req_info *req_info = STRSET_REQINFO(req_base);
0441     const struct strset_reply_data *data = STRSET_REPDATA(reply_base);
0442     struct nlattr *nest;
0443     unsigned int i;
0444     int ret;
0445 
0446     nest = nla_nest_start(skb, ETHTOOL_A_STRSET_STRINGSETS);
0447     if (!nest)
0448         return -EMSGSIZE;
0449 
0450     for (i = 0; i < ETH_SS_COUNT; i++) {
0451         if (strset_include(req_info, data, i)) {
0452             ret = strset_fill_set(skb, &data->sets[i], i,
0453                           req_info->counts_only);
0454             if (ret < 0)
0455                 goto nla_put_failure;
0456         }
0457     }
0458 
0459     nla_nest_end(skb, nest);
0460     return 0;
0461 
0462 nla_put_failure:
0463     nla_nest_cancel(skb, nest);
0464     return ret;
0465 }
0466 
0467 const struct ethnl_request_ops ethnl_strset_request_ops = {
0468     .request_cmd        = ETHTOOL_MSG_STRSET_GET,
0469     .reply_cmd      = ETHTOOL_MSG_STRSET_GET_REPLY,
0470     .hdr_attr       = ETHTOOL_A_STRSET_HEADER,
0471     .req_info_size      = sizeof(struct strset_req_info),
0472     .reply_data_size    = sizeof(struct strset_reply_data),
0473     .allow_nodev_do     = true,
0474 
0475     .parse_request      = strset_parse_request,
0476     .prepare_data       = strset_prepare_data,
0477     .reply_size     = strset_reply_size,
0478     .fill_reply     = strset_fill_reply,
0479     .cleanup_data       = strset_cleanup_data,
0480 };