1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include "netlink.h" 4 #include "common.h" 5 #include "bitset.h" 6 7 struct privflags_req_info { 8 struct ethnl_req_info base; 9 }; 10 11 struct 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 21 const struct nla_policy ethnl_privflags_get_policy[] = { 22 [ETHTOOL_A_PRIVFLAGS_HEADER] = 23 NLA_POLICY_NESTED(ethnl_header_policy), 24 }; 25 26 static 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 58 static int privflags_prepare_data(const struct ethnl_req_info *req_base, 59 struct ethnl_reply_data *reply_base, 60 const 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 83 out_ops: 84 ethnl_ops_complete(dev); 85 return ret; 86 } 87 88 static 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 100 static 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 114 static 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 121 /* PRIVFLAGS_SET */ 122 123 const struct nla_policy ethnl_privflags_set_policy[] = { 124 [ETHTOOL_A_PRIVFLAGS_HEADER] = 125 NLA_POLICY_NESTED(ethnl_header_policy), 126 [ETHTOOL_A_PRIVFLAGS_FLAGS] = { .type = NLA_NESTED }, 127 }; 128 129 static int 130 ethnl_set_privflags_validate(struct ethnl_req_info *req_info, 131 struct genl_info *info) 132 { 133 const struct ethtool_ops *ops = req_info->dev->ethtool_ops; 134 135 if (!info->attrs[ETHTOOL_A_PRIVFLAGS_FLAGS]) 136 return -EINVAL; 137 138 if (!ops->get_priv_flags || !ops->set_priv_flags || 139 !ops->get_sset_count || !ops->get_strings) 140 return -EOPNOTSUPP; 141 return 1; 142 } 143 144 static int 145 ethnl_set_privflags(struct ethnl_req_info *req_info, struct genl_info *info) 146 { 147 const char (*names)[ETH_GSTRING_LEN] = NULL; 148 struct net_device *dev = req_info->dev; 149 struct nlattr **tb = info->attrs; 150 unsigned int nflags; 151 bool mod = false; 152 bool compact; 153 u32 flags; 154 int ret; 155 156 ret = ethnl_bitset_is_compact(tb[ETHTOOL_A_PRIVFLAGS_FLAGS], &compact); 157 if (ret < 0) 158 return ret; 159 160 ret = ethnl_get_priv_flags_info(dev, &nflags, compact ? NULL : &names); 161 if (ret < 0) 162 return ret; 163 flags = dev->ethtool_ops->get_priv_flags(dev); 164 165 ret = ethnl_update_bitset32(&flags, nflags, 166 tb[ETHTOOL_A_PRIVFLAGS_FLAGS], names, 167 info->extack, &mod); 168 if (ret < 0 || !mod) 169 goto out_free; 170 ret = dev->ethtool_ops->set_priv_flags(dev, flags); 171 if (ret < 0) 172 goto out_free; 173 ret = 1; 174 175 out_free: 176 kfree(names); 177 return ret; 178 } 179 180 const struct ethnl_request_ops ethnl_privflags_request_ops = { 181 .request_cmd = ETHTOOL_MSG_PRIVFLAGS_GET, 182 .reply_cmd = ETHTOOL_MSG_PRIVFLAGS_GET_REPLY, 183 .hdr_attr = ETHTOOL_A_PRIVFLAGS_HEADER, 184 .req_info_size = sizeof(struct privflags_req_info), 185 .reply_data_size = sizeof(struct privflags_reply_data), 186 187 .prepare_data = privflags_prepare_data, 188 .reply_size = privflags_reply_size, 189 .fill_reply = privflags_fill_reply, 190 .cleanup_data = privflags_cleanup_data, 191 192 .set_validate = ethnl_set_privflags_validate, 193 .set = ethnl_set_privflags, 194 .set_ntf_cmd = ETHTOOL_MSG_PRIVFLAGS_NTF, 195 }; 196