LCOV - code coverage report
Current view: top level - net/ethtool - strset.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 0 166 0.0 %
Date: 2022-04-01 13:59:58 Functions: 0 9 0.0 %
Branches: 0 144 0.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-only
       2                 :            : 
       3                 :            : #include <linux/ethtool.h>
       4                 :            : #include <linux/phy.h>
       5                 :            : #include "netlink.h"
       6                 :            : #include "common.h"
       7                 :            : 
       8                 :            : struct strset_info {
       9                 :            :         bool per_dev;
      10                 :            :         bool free_strings;
      11                 :            :         unsigned int count;
      12                 :            :         const char (*strings)[ETH_GSTRING_LEN];
      13                 :            : };
      14                 :            : 
      15                 :            : static const struct strset_info info_template[] = {
      16                 :            :         [ETH_SS_TEST] = {
      17                 :            :                 .per_dev        = true,
      18                 :            :         },
      19                 :            :         [ETH_SS_STATS] = {
      20                 :            :                 .per_dev        = true,
      21                 :            :         },
      22                 :            :         [ETH_SS_PRIV_FLAGS] = {
      23                 :            :                 .per_dev        = true,
      24                 :            :         },
      25                 :            :         [ETH_SS_FEATURES] = {
      26                 :            :                 .per_dev        = false,
      27                 :            :                 .count          = ARRAY_SIZE(netdev_features_strings),
      28                 :            :                 .strings        = netdev_features_strings,
      29                 :            :         },
      30                 :            :         [ETH_SS_RSS_HASH_FUNCS] = {
      31                 :            :                 .per_dev        = false,
      32                 :            :                 .count          = ARRAY_SIZE(rss_hash_func_strings),
      33                 :            :                 .strings        = rss_hash_func_strings,
      34                 :            :         },
      35                 :            :         [ETH_SS_TUNABLES] = {
      36                 :            :                 .per_dev        = false,
      37                 :            :                 .count          = ARRAY_SIZE(tunable_strings),
      38                 :            :                 .strings        = tunable_strings,
      39                 :            :         },
      40                 :            :         [ETH_SS_PHY_STATS] = {
      41                 :            :                 .per_dev        = true,
      42                 :            :         },
      43                 :            :         [ETH_SS_PHY_TUNABLES] = {
      44                 :            :                 .per_dev        = false,
      45                 :            :                 .count          = ARRAY_SIZE(phy_tunable_strings),
      46                 :            :                 .strings        = phy_tunable_strings,
      47                 :            :         },
      48                 :            :         [ETH_SS_LINK_MODES] = {
      49                 :            :                 .per_dev        = false,
      50                 :            :                 .count          = __ETHTOOL_LINK_MODE_MASK_NBITS,
      51                 :            :                 .strings        = link_mode_names,
      52                 :            :         },
      53                 :            :         [ETH_SS_MSG_CLASSES] = {
      54                 :            :                 .per_dev        = false,
      55                 :            :                 .count          = NETIF_MSG_CLASS_COUNT,
      56                 :            :                 .strings        = netif_msg_class_names,
      57                 :            :         },
      58                 :            :         [ETH_SS_WOL_MODES] = {
      59                 :            :                 .per_dev        = false,
      60                 :            :                 .count          = WOL_MODE_COUNT,
      61                 :            :                 .strings        = wol_mode_names,
      62                 :            :         },
      63                 :            : };
      64                 :            : 
      65                 :            : struct strset_req_info {
      66                 :            :         struct ethnl_req_info           base;
      67                 :            :         u32                             req_ids;
      68                 :            :         bool                            counts_only;
      69                 :            : };
      70                 :            : 
      71                 :            : #define STRSET_REQINFO(__req_base) \
      72                 :            :         container_of(__req_base, struct strset_req_info, base)
      73                 :            : 
      74                 :            : struct strset_reply_data {
      75                 :            :         struct ethnl_reply_data         base;
      76                 :            :         struct strset_info              sets[ETH_SS_COUNT];
      77                 :            : };
      78                 :            : 
      79                 :            : #define STRSET_REPDATA(__reply_base) \
      80                 :            :         container_of(__reply_base, struct strset_reply_data, base)
      81                 :            : 
      82                 :            : static const struct nla_policy strset_get_policy[ETHTOOL_A_STRSET_MAX + 1] = {
      83                 :            :         [ETHTOOL_A_STRSET_UNSPEC]       = { .type = NLA_REJECT },
      84                 :            :         [ETHTOOL_A_STRSET_HEADER]       = { .type = NLA_NESTED },
      85                 :            :         [ETHTOOL_A_STRSET_STRINGSETS]   = { .type = NLA_NESTED },
      86                 :            : };
      87                 :            : 
      88                 :            : static const struct nla_policy
      89                 :            : get_stringset_policy[ETHTOOL_A_STRINGSET_MAX + 1] = {
      90                 :            :         [ETHTOOL_A_STRINGSET_UNSPEC]    = { .type = NLA_REJECT },
      91                 :            :         [ETHTOOL_A_STRINGSET_ID]        = { .type = NLA_U32 },
      92                 :            :         [ETHTOOL_A_STRINGSET_COUNT]     = { .type = NLA_REJECT },
      93                 :            :         [ETHTOOL_A_STRINGSET_STRINGS]   = { .type = NLA_REJECT },
      94                 :            : };
      95                 :            : 
      96                 :            : /**
      97                 :            :  * strset_include() - test if a string set should be included in reply
      98                 :            :  * @info: parsed client request
      99                 :            :  * @data: pointer to request data structure
     100                 :            :  * @id:   id of string set to check (ETH_SS_* constants)
     101                 :            :  */
     102                 :          0 : static bool strset_include(const struct strset_req_info *info,
     103                 :            :                            const struct strset_reply_data *data, u32 id)
     104                 :            : {
     105                 :          0 :         bool per_dev;
     106                 :            : 
     107                 :          0 :         BUILD_BUG_ON(ETH_SS_COUNT >= BITS_PER_BYTE * sizeof(info->req_ids));
     108                 :            : 
     109                 :          0 :         if (info->req_ids)
     110                 :          0 :                 return info->req_ids & (1U << id);
     111                 :          0 :         per_dev = data->sets[id].per_dev;
     112   [ #  #  #  #  :          0 :         if (!per_dev && !data->sets[id].strings)
          #  #  #  #  #  
                #  #  # ]
     113                 :            :                 return false;
     114                 :            : 
     115   [ #  #  #  #  :          0 :         return data->base.dev ? per_dev : !per_dev;
                   #  # ]
     116                 :            : }
     117                 :            : 
     118                 :          0 : static int strset_get_id(const struct nlattr *nest, u32 *val,
     119                 :            :                          struct netlink_ext_ack *extack)
     120                 :            : {
     121                 :          0 :         struct nlattr *tb[ETHTOOL_A_STRINGSET_MAX + 1];
     122                 :          0 :         int ret;
     123                 :            : 
     124                 :          0 :         ret = nla_parse_nested(tb, ETHTOOL_A_STRINGSET_MAX, nest,
     125                 :            :                                get_stringset_policy, extack);
     126         [ #  # ]:          0 :         if (ret < 0)
     127                 :            :                 return ret;
     128         [ #  # ]:          0 :         if (!tb[ETHTOOL_A_STRINGSET_ID])
     129                 :            :                 return -EINVAL;
     130                 :            : 
     131                 :          0 :         *val = nla_get_u32(tb[ETHTOOL_A_STRINGSET_ID]);
     132                 :          0 :         return 0;
     133                 :            : }
     134                 :            : 
     135                 :            : static const struct nla_policy
     136                 :            : strset_stringsets_policy[ETHTOOL_A_STRINGSETS_MAX + 1] = {
     137                 :            :         [ETHTOOL_A_STRINGSETS_UNSPEC]           = { .type = NLA_REJECT },
     138                 :            :         [ETHTOOL_A_STRINGSETS_STRINGSET]        = { .type = NLA_NESTED },
     139                 :            : };
     140                 :            : 
     141                 :          0 : static int strset_parse_request(struct ethnl_req_info *req_base,
     142                 :            :                                 struct nlattr **tb,
     143                 :            :                                 struct netlink_ext_ack *extack)
     144                 :            : {
     145                 :          0 :         struct strset_req_info *req_info = STRSET_REQINFO(req_base);
     146                 :          0 :         struct nlattr *nest = tb[ETHTOOL_A_STRSET_STRINGSETS];
     147                 :          0 :         struct nlattr *attr;
     148                 :          0 :         int rem, ret;
     149                 :            : 
     150         [ #  # ]:          0 :         if (!nest)
     151                 :            :                 return 0;
     152                 :          0 :         ret = nla_validate_nested(nest, ETHTOOL_A_STRINGSETS_MAX,
     153                 :            :                                   strset_stringsets_policy, extack);
     154         [ #  # ]:          0 :         if (ret < 0)
     155                 :            :                 return ret;
     156                 :            : 
     157                 :          0 :         req_info->counts_only = tb[ETHTOOL_A_STRSET_COUNTS_ONLY];
     158         [ #  # ]:          0 :         nla_for_each_nested(attr, nest, rem) {
     159                 :          0 :                 u32 id;
     160                 :            : 
     161   [ #  #  #  #  :          0 :                 if (WARN_ONCE(nla_type(attr) != ETHTOOL_A_STRINGSETS_STRINGSET,
                   #  # ]
     162                 :            :                               "unexpected attrtype %u in ETHTOOL_A_STRSET_STRINGSETS\n",
     163                 :            :                               nla_type(attr)))
     164                 :          0 :                         return -EINVAL;
     165                 :            : 
     166                 :          0 :                 ret = strset_get_id(attr, &id, extack);
     167         [ #  # ]:          0 :                 if (ret < 0)
     168                 :          0 :                         return ret;
     169         [ #  # ]:          0 :                 if (ret >= ETH_SS_COUNT) {
     170         [ #  # ]:          0 :                         NL_SET_ERR_MSG_ATTR(extack, attr,
     171                 :            :                                             "unknown string set id");
     172                 :          0 :                         return -EOPNOTSUPP;
     173                 :            :                 }
     174                 :            : 
     175                 :          0 :                 req_info->req_ids |= (1U << id);
     176                 :            :         }
     177                 :            : 
     178                 :            :         return 0;
     179                 :            : }
     180                 :            : 
     181                 :          0 : static void strset_cleanup_data(struct ethnl_reply_data *reply_base)
     182                 :            : {
     183                 :          0 :         struct strset_reply_data *data = STRSET_REPDATA(reply_base);
     184                 :          0 :         unsigned int i;
     185                 :            : 
     186   [ #  #  #  # ]:          0 :         for (i = 0; i < ETH_SS_COUNT; i++)
     187   [ #  #  #  # ]:          0 :                 if (data->sets[i].free_strings) {
     188                 :          0 :                         kfree(data->sets[i].strings);
     189                 :          0 :                         data->sets[i].strings = NULL;
     190                 :          0 :                         data->sets[i].free_strings = false;
     191                 :            :                 }
     192                 :          0 : }
     193                 :            : 
     194                 :          0 : static int strset_prepare_set(struct strset_info *info, struct net_device *dev,
     195                 :            :                               unsigned int id, bool counts_only)
     196                 :            : {
     197                 :          0 :         const struct ethtool_ops *ops = dev->ethtool_ops;
     198                 :          0 :         void *strings;
     199                 :          0 :         int count, ret;
     200                 :            : 
     201   [ #  #  #  # ]:          0 :         if (id == ETH_SS_PHY_STATS && dev->phydev &&
     202         [ #  # ]:          0 :             !ops->get_ethtool_phy_stats)
     203                 :          0 :                 ret = phy_ethtool_get_sset_count(dev->phydev);
     204   [ #  #  #  # ]:          0 :         else if (ops->get_sset_count && ops->get_strings)
     205                 :          0 :                 ret = ops->get_sset_count(dev, id);
     206                 :            :         else
     207                 :            :                 ret = -EOPNOTSUPP;
     208         [ #  # ]:          0 :         if (ret <= 0) {
     209                 :          0 :                 info->count = 0;
     210                 :          0 :                 return 0;
     211                 :            :         }
     212                 :            : 
     213                 :          0 :         count = ret;
     214         [ #  # ]:          0 :         if (!counts_only) {
     215                 :          0 :                 strings = kcalloc(count, ETH_GSTRING_LEN, GFP_KERNEL);
     216         [ #  # ]:          0 :                 if (!strings)
     217                 :            :                         return -ENOMEM;
     218   [ #  #  #  # ]:          0 :                 if (id == ETH_SS_PHY_STATS && dev->phydev &&
     219         [ #  # ]:          0 :                     !ops->get_ethtool_phy_stats)
     220                 :          0 :                         phy_ethtool_get_strings(dev->phydev, strings);
     221                 :            :                 else
     222                 :          0 :                         ops->get_strings(dev, id, strings);
     223                 :          0 :                 info->strings = strings;
     224                 :          0 :                 info->free_strings = true;
     225                 :            :         }
     226                 :          0 :         info->count = count;
     227                 :            : 
     228                 :          0 :         return 0;
     229                 :            : }
     230                 :            : 
     231                 :          0 : static int strset_prepare_data(const struct ethnl_req_info *req_base,
     232                 :            :                                struct ethnl_reply_data *reply_base,
     233                 :            :                                struct genl_info *info)
     234                 :            : {
     235                 :          0 :         const struct strset_req_info *req_info = STRSET_REQINFO(req_base);
     236                 :          0 :         struct strset_reply_data *data = STRSET_REPDATA(reply_base);
     237                 :          0 :         struct net_device *dev = reply_base->dev;
     238                 :          0 :         unsigned int i;
     239                 :          0 :         int ret;
     240                 :            : 
     241                 :          0 :         BUILD_BUG_ON(ARRAY_SIZE(info_template) != ETH_SS_COUNT);
     242                 :          0 :         memcpy(&data->sets, &info_template, sizeof(data->sets));
     243                 :            : 
     244         [ #  # ]:          0 :         if (!dev) {
     245         [ #  # ]:          0 :                 for (i = 0; i < ETH_SS_COUNT; i++) {
     246         [ #  # ]:          0 :                         if ((req_info->req_ids & (1U << i)) &&
     247         [ #  # ]:          0 :                             data->sets[i].per_dev) {
     248         [ #  # ]:          0 :                                 if (info)
     249         [ #  # ]:          0 :                                         GENL_SET_ERR_MSG(info, "requested per device strings without dev");
     250                 :          0 :                                 return -EINVAL;
     251                 :            :                         }
     252                 :            :                 }
     253                 :            :                 return 0;
     254                 :            :         }
     255                 :            : 
     256         [ #  # ]:          0 :         ret = ethnl_ops_begin(dev);
     257         [ #  # ]:          0 :         if (ret < 0)
     258                 :          0 :                 goto err_strset;
     259         [ #  # ]:          0 :         for (i = 0; i < ETH_SS_COUNT; i++) {
     260   [ #  #  #  # ]:          0 :                 if (!strset_include(req_info, data, i) ||
     261         [ #  # ]:          0 :                     !data->sets[i].per_dev)
     262                 :          0 :                         continue;
     263                 :            : 
     264                 :          0 :                 ret = strset_prepare_set(&data->sets[i], dev, i,
     265                 :          0 :                                          req_info->counts_only);
     266         [ #  # ]:          0 :                 if (ret < 0)
     267                 :          0 :                         goto err_ops;
     268                 :            :         }
     269         [ #  # ]:          0 :         ethnl_ops_complete(dev);
     270                 :            : 
     271                 :            :         return 0;
     272                 :            : err_ops:
     273         [ #  # ]:          0 :         ethnl_ops_complete(dev);
     274                 :          0 : err_strset:
     275                 :          0 :         strset_cleanup_data(reply_base);
     276                 :            :         return ret;
     277                 :            : }
     278                 :            : 
     279                 :            : /* calculate size of ETHTOOL_A_STRSET_STRINGSET nest for one string set */
     280                 :          0 : static int strset_set_size(const struct strset_info *info, bool counts_only)
     281                 :            : {
     282                 :          0 :         unsigned int len = 0;
     283                 :          0 :         unsigned int i;
     284                 :            : 
     285         [ #  # ]:          0 :         if (info->count == 0)
     286                 :            :                 return 0;
     287         [ #  # ]:          0 :         if (counts_only)
     288                 :            :                 return nla_total_size(2 * nla_total_size(sizeof(u32)));
     289                 :            : 
     290         [ #  # ]:          0 :         for (i = 0; i < info->count; i++) {
     291                 :          0 :                 const char *str = info->strings[i];
     292                 :            : 
     293                 :            :                 /* ETHTOOL_A_STRING_INDEX, ETHTOOL_A_STRING_VALUE, nest */
     294                 :          0 :                 len += nla_total_size(nla_total_size(sizeof(u32)) +
     295                 :            :                                       ethnl_strz_size(str));
     296                 :            :         }
     297                 :            :         /* ETHTOOL_A_STRINGSET_ID, ETHTOOL_A_STRINGSET_COUNT */
     298                 :          0 :         len = 2 * nla_total_size(sizeof(u32)) + nla_total_size(len);
     299                 :            : 
     300                 :          0 :         return nla_total_size(len);
     301                 :            : }
     302                 :            : 
     303                 :          0 : static int strset_reply_size(const struct ethnl_req_info *req_base,
     304                 :            :                              const struct ethnl_reply_data *reply_base)
     305                 :            : {
     306                 :          0 :         const struct strset_req_info *req_info = STRSET_REQINFO(req_base);
     307                 :          0 :         const struct strset_reply_data *data = STRSET_REPDATA(reply_base);
     308                 :          0 :         unsigned int i;
     309                 :          0 :         int len = 0;
     310                 :          0 :         int ret;
     311                 :            : 
     312                 :          0 :         len += ethnl_reply_header_size();
     313         [ #  # ]:          0 :         for (i = 0; i < ETH_SS_COUNT; i++) {
     314                 :          0 :                 const struct strset_info *set_info = &data->sets[i];
     315                 :            : 
     316   [ #  #  #  # ]:          0 :                 if (!strset_include(req_info, data, i))
     317                 :          0 :                         continue;
     318                 :            : 
     319                 :          0 :                 ret = strset_set_size(set_info, req_info->counts_only);
     320         [ #  # ]:          0 :                 if (ret < 0)
     321                 :          0 :                         return ret;
     322                 :          0 :                 len += ret;
     323                 :            :         }
     324                 :            : 
     325                 :            :         return len;
     326                 :            : }
     327                 :            : 
     328                 :            : /* fill one string into reply */
     329                 :            : static int strset_fill_string(struct sk_buff *skb,
     330                 :            :                               const struct strset_info *set_info, u32 idx)
     331                 :            : {
     332                 :            :         struct nlattr *string_attr;
     333                 :            :         const char *value;
     334                 :            : 
     335                 :            :         value = set_info->strings[idx];
     336                 :            : 
     337                 :            :         string_attr = nla_nest_start(skb, ETHTOOL_A_STRINGS_STRING);
     338                 :            :         if (!string_attr)
     339                 :            :                 return -EMSGSIZE;
     340                 :            :         if (nla_put_u32(skb, ETHTOOL_A_STRING_INDEX, idx) ||
     341                 :            :             ethnl_put_strz(skb, ETHTOOL_A_STRING_VALUE, value))
     342                 :            :                 goto nla_put_failure;
     343                 :            :         nla_nest_end(skb, string_attr);
     344                 :            : 
     345                 :            :         return 0;
     346                 :            : nla_put_failure:
     347                 :            :         nla_nest_cancel(skb, string_attr);
     348                 :            :         return -EMSGSIZE;
     349                 :            : }
     350                 :            : 
     351                 :            : /* fill one string set into reply */
     352                 :          0 : static int strset_fill_set(struct sk_buff *skb,
     353                 :            :                            const struct strset_info *set_info, u32 id,
     354                 :            :                            bool counts_only)
     355                 :            : {
     356                 :          0 :         struct nlattr *stringset_attr;
     357                 :          0 :         struct nlattr *strings_attr;
     358                 :          0 :         unsigned int i;
     359                 :            : 
     360   [ #  #  #  # ]:          0 :         if (!set_info->per_dev && !set_info->strings)
     361                 :            :                 return -EOPNOTSUPP;
     362         [ #  # ]:          0 :         if (set_info->count == 0)
     363                 :            :                 return 0;
     364                 :          0 :         stringset_attr = nla_nest_start(skb, ETHTOOL_A_STRINGSETS_STRINGSET);
     365         [ #  # ]:          0 :         if (!stringset_attr)
     366                 :            :                 return -EMSGSIZE;
     367                 :            : 
     368   [ #  #  #  # ]:          0 :         if (nla_put_u32(skb, ETHTOOL_A_STRINGSET_ID, id) ||
     369                 :          0 :             nla_put_u32(skb, ETHTOOL_A_STRINGSET_COUNT, set_info->count))
     370                 :          0 :                 goto nla_put_failure;
     371                 :            : 
     372         [ #  # ]:          0 :         if (!counts_only) {
     373                 :          0 :                 strings_attr = nla_nest_start(skb, ETHTOOL_A_STRINGSET_STRINGS);
     374         [ #  # ]:          0 :                 if (!strings_attr)
     375                 :          0 :                         goto nla_put_failure;
     376         [ #  # ]:          0 :                 for (i = 0; i < set_info->count; i++) {
     377         [ #  # ]:          0 :                         if (strset_fill_string(skb, set_info, i) < 0)
     378                 :          0 :                                 goto nla_put_failure;
     379                 :            :                 }
     380                 :          0 :                 nla_nest_end(skb, strings_attr);
     381                 :            :         }
     382                 :            : 
     383                 :          0 :         nla_nest_end(skb, stringset_attr);
     384                 :          0 :         return 0;
     385                 :            : 
     386                 :          0 : nla_put_failure:
     387                 :          0 :         nla_nest_cancel(skb, stringset_attr);
     388                 :          0 :         return -EMSGSIZE;
     389                 :            : }
     390                 :            : 
     391                 :          0 : static int strset_fill_reply(struct sk_buff *skb,
     392                 :            :                              const struct ethnl_req_info *req_base,
     393                 :            :                              const struct ethnl_reply_data *reply_base)
     394                 :            : {
     395                 :          0 :         const struct strset_req_info *req_info = STRSET_REQINFO(req_base);
     396                 :          0 :         const struct strset_reply_data *data = STRSET_REPDATA(reply_base);
     397                 :          0 :         struct nlattr *nest;
     398                 :          0 :         unsigned int i;
     399                 :          0 :         int ret;
     400                 :            : 
     401                 :          0 :         nest = nla_nest_start(skb, ETHTOOL_A_STRSET_STRINGSETS);
     402         [ #  # ]:          0 :         if (!nest)
     403                 :            :                 return -EMSGSIZE;
     404                 :            : 
     405         [ #  # ]:          0 :         for (i = 0; i < ETH_SS_COUNT; i++) {
     406   [ #  #  #  # ]:          0 :                 if (strset_include(req_info, data, i)) {
     407                 :          0 :                         ret = strset_fill_set(skb, &data->sets[i], i,
     408                 :          0 :                                               req_info->counts_only);
     409         [ #  # ]:          0 :                         if (ret < 0)
     410                 :          0 :                                 goto nla_put_failure;
     411                 :            :                 }
     412                 :            :         }
     413                 :            : 
     414                 :          0 :         nla_nest_end(skb, nest);
     415                 :          0 :         return 0;
     416                 :            : 
     417                 :            : nla_put_failure:
     418                 :          0 :         nla_nest_cancel(skb, nest);
     419                 :          0 :         return ret;
     420                 :            : }
     421                 :            : 
     422                 :            : const struct ethnl_request_ops ethnl_strset_request_ops = {
     423                 :            :         .request_cmd            = ETHTOOL_MSG_STRSET_GET,
     424                 :            :         .reply_cmd              = ETHTOOL_MSG_STRSET_GET_REPLY,
     425                 :            :         .hdr_attr               = ETHTOOL_A_STRSET_HEADER,
     426                 :            :         .max_attr               = ETHTOOL_A_STRSET_MAX,
     427                 :            :         .req_info_size          = sizeof(struct strset_req_info),
     428                 :            :         .reply_data_size        = sizeof(struct strset_reply_data),
     429                 :            :         .request_policy         = strset_get_policy,
     430                 :            :         .allow_nodev_do         = true,
     431                 :            : 
     432                 :            :         .parse_request          = strset_parse_request,
     433                 :            :         .prepare_data           = strset_prepare_data,
     434                 :            :         .reply_size             = strset_reply_size,
     435                 :            :         .fill_reply             = strset_fill_reply,
     436                 :            :         .cleanup_data           = strset_cleanup_data,
     437                 :            : };

Generated by: LCOV version 1.14