1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include "netlink.h" 4 #include "common.h" 5 6 struct coalesce_req_info { 7 struct ethnl_req_info base; 8 }; 9 10 struct coalesce_reply_data { 11 struct ethnl_reply_data base; 12 struct ethtool_coalesce coalesce; 13 u32 supported_params; 14 }; 15 16 #define COALESCE_REPDATA(__reply_base) \ 17 container_of(__reply_base, struct coalesce_reply_data, base) 18 19 #define __SUPPORTED_OFFSET ETHTOOL_A_COALESCE_RX_USECS 20 static u32 attr_to_mask(unsigned int attr_type) 21 { 22 return BIT(attr_type - __SUPPORTED_OFFSET); 23 } 24 25 /* build time check that indices in ethtool_ops::supported_coalesce_params 26 * match corresponding attribute types with an offset 27 */ 28 #define __CHECK_SUPPORTED_OFFSET(x) \ 29 static_assert((ETHTOOL_ ## x) == \ 30 BIT((ETHTOOL_A_ ## x) - __SUPPORTED_OFFSET)) 31 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS); 32 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES); 33 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_IRQ); 34 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_IRQ); 35 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS); 36 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES); 37 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_IRQ); 38 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_IRQ); 39 __CHECK_SUPPORTED_OFFSET(COALESCE_STATS_BLOCK_USECS); 40 __CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_RX); 41 __CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_TX); 42 __CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_LOW); 43 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_LOW); 44 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_LOW); 45 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_LOW); 46 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_LOW); 47 __CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_HIGH); 48 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_HIGH); 49 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_HIGH); 50 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_HIGH); 51 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_HIGH); 52 __CHECK_SUPPORTED_OFFSET(COALESCE_RATE_SAMPLE_INTERVAL); 53 54 static const struct nla_policy 55 coalesce_get_policy[ETHTOOL_A_COALESCE_MAX + 1] = { 56 [ETHTOOL_A_COALESCE_UNSPEC] = { .type = NLA_REJECT }, 57 [ETHTOOL_A_COALESCE_HEADER] = { .type = NLA_NESTED }, 58 [ETHTOOL_A_COALESCE_RX_USECS] = { .type = NLA_REJECT }, 59 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES] = { .type = NLA_REJECT }, 60 [ETHTOOL_A_COALESCE_RX_USECS_IRQ] = { .type = NLA_REJECT }, 61 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ] = { .type = NLA_REJECT }, 62 [ETHTOOL_A_COALESCE_TX_USECS] = { .type = NLA_REJECT }, 63 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES] = { .type = NLA_REJECT }, 64 [ETHTOOL_A_COALESCE_TX_USECS_IRQ] = { .type = NLA_REJECT }, 65 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ] = { .type = NLA_REJECT }, 66 [ETHTOOL_A_COALESCE_STATS_BLOCK_USECS] = { .type = NLA_REJECT }, 67 [ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX] = { .type = NLA_REJECT }, 68 [ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX] = { .type = NLA_REJECT }, 69 [ETHTOOL_A_COALESCE_PKT_RATE_LOW] = { .type = NLA_REJECT }, 70 [ETHTOOL_A_COALESCE_RX_USECS_LOW] = { .type = NLA_REJECT }, 71 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW] = { .type = NLA_REJECT }, 72 [ETHTOOL_A_COALESCE_TX_USECS_LOW] = { .type = NLA_REJECT }, 73 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW] = { .type = NLA_REJECT }, 74 [ETHTOOL_A_COALESCE_PKT_RATE_HIGH] = { .type = NLA_REJECT }, 75 [ETHTOOL_A_COALESCE_RX_USECS_HIGH] = { .type = NLA_REJECT }, 76 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH] = { .type = NLA_REJECT }, 77 [ETHTOOL_A_COALESCE_TX_USECS_HIGH] = { .type = NLA_REJECT }, 78 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH] = { .type = NLA_REJECT }, 79 [ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL] = { .type = NLA_REJECT }, 80 }; 81 82 static int coalesce_prepare_data(const struct ethnl_req_info *req_base, 83 struct ethnl_reply_data *reply_base, 84 struct genl_info *info) 85 { 86 struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base); 87 struct net_device *dev = reply_base->dev; 88 int ret; 89 90 if (!dev->ethtool_ops->get_coalesce) 91 return -EOPNOTSUPP; 92 data->supported_params = dev->ethtool_ops->supported_coalesce_params; 93 ret = ethnl_ops_begin(dev); 94 if (ret < 0) 95 return ret; 96 ret = dev->ethtool_ops->get_coalesce(dev, &data->coalesce); 97 ethnl_ops_complete(dev); 98 99 return ret; 100 } 101 102 static int coalesce_reply_size(const struct ethnl_req_info *req_base, 103 const struct ethnl_reply_data *reply_base) 104 { 105 return nla_total_size(sizeof(u32)) + /* _RX_USECS */ 106 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES */ 107 nla_total_size(sizeof(u32)) + /* _RX_USECS_IRQ */ 108 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_IRQ */ 109 nla_total_size(sizeof(u32)) + /* _TX_USECS */ 110 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES */ 111 nla_total_size(sizeof(u32)) + /* _TX_USECS_IRQ */ 112 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_IRQ */ 113 nla_total_size(sizeof(u32)) + /* _STATS_BLOCK_USECS */ 114 nla_total_size(sizeof(u8)) + /* _USE_ADAPTIVE_RX */ 115 nla_total_size(sizeof(u8)) + /* _USE_ADAPTIVE_TX */ 116 nla_total_size(sizeof(u32)) + /* _PKT_RATE_LOW */ 117 nla_total_size(sizeof(u32)) + /* _RX_USECS_LOW */ 118 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_LOW */ 119 nla_total_size(sizeof(u32)) + /* _TX_USECS_LOW */ 120 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_LOW */ 121 nla_total_size(sizeof(u32)) + /* _PKT_RATE_HIGH */ 122 nla_total_size(sizeof(u32)) + /* _RX_USECS_HIGH */ 123 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_HIGH */ 124 nla_total_size(sizeof(u32)) + /* _TX_USECS_HIGH */ 125 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_HIGH */ 126 nla_total_size(sizeof(u32)); /* _RATE_SAMPLE_INTERVAL */ 127 } 128 129 static bool coalesce_put_u32(struct sk_buff *skb, u16 attr_type, u32 val, 130 u32 supported_params) 131 { 132 if (!val && !(supported_params & attr_to_mask(attr_type))) 133 return false; 134 return nla_put_u32(skb, attr_type, val); 135 } 136 137 static bool coalesce_put_bool(struct sk_buff *skb, u16 attr_type, u32 val, 138 u32 supported_params) 139 { 140 if (!val && !(supported_params & attr_to_mask(attr_type))) 141 return false; 142 return nla_put_u8(skb, attr_type, !!val); 143 } 144 145 static int coalesce_fill_reply(struct sk_buff *skb, 146 const struct ethnl_req_info *req_base, 147 const struct ethnl_reply_data *reply_base) 148 { 149 const struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base); 150 const struct ethtool_coalesce *coal = &data->coalesce; 151 u32 supported = data->supported_params; 152 153 if (coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS, 154 coal->rx_coalesce_usecs, supported) || 155 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES, 156 coal->rx_max_coalesced_frames, supported) || 157 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_IRQ, 158 coal->rx_coalesce_usecs_irq, supported) || 159 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ, 160 coal->rx_max_coalesced_frames_irq, supported) || 161 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS, 162 coal->tx_coalesce_usecs, supported) || 163 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES, 164 coal->tx_max_coalesced_frames, supported) || 165 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_IRQ, 166 coal->tx_coalesce_usecs_irq, supported) || 167 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ, 168 coal->tx_max_coalesced_frames_irq, supported) || 169 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_STATS_BLOCK_USECS, 170 coal->stats_block_coalesce_usecs, supported) || 171 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX, 172 coal->use_adaptive_rx_coalesce, supported) || 173 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX, 174 coal->use_adaptive_tx_coalesce, supported) || 175 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_LOW, 176 coal->pkt_rate_low, supported) || 177 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_LOW, 178 coal->rx_coalesce_usecs_low, supported) || 179 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW, 180 coal->rx_max_coalesced_frames_low, supported) || 181 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_LOW, 182 coal->tx_coalesce_usecs_low, supported) || 183 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW, 184 coal->tx_max_coalesced_frames_low, supported) || 185 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_HIGH, 186 coal->pkt_rate_high, supported) || 187 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_HIGH, 188 coal->rx_coalesce_usecs_high, supported) || 189 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH, 190 coal->rx_max_coalesced_frames_high, supported) || 191 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_HIGH, 192 coal->tx_coalesce_usecs_high, supported) || 193 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH, 194 coal->tx_max_coalesced_frames_high, supported) || 195 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL, 196 coal->rate_sample_interval, supported)) 197 return -EMSGSIZE; 198 199 return 0; 200 } 201 202 const struct ethnl_request_ops ethnl_coalesce_request_ops = { 203 .request_cmd = ETHTOOL_MSG_COALESCE_GET, 204 .reply_cmd = ETHTOOL_MSG_COALESCE_GET_REPLY, 205 .hdr_attr = ETHTOOL_A_COALESCE_HEADER, 206 .max_attr = ETHTOOL_A_COALESCE_MAX, 207 .req_info_size = sizeof(struct coalesce_req_info), 208 .reply_data_size = sizeof(struct coalesce_reply_data), 209 .request_policy = coalesce_get_policy, 210 211 .prepare_data = coalesce_prepare_data, 212 .reply_size = coalesce_reply_size, 213 .fill_reply = coalesce_fill_reply, 214 }; 215 216 /* COALESCE_SET */ 217 218 static const struct nla_policy 219 coalesce_set_policy[ETHTOOL_A_COALESCE_MAX + 1] = { 220 [ETHTOOL_A_COALESCE_UNSPEC] = { .type = NLA_REJECT }, 221 [ETHTOOL_A_COALESCE_HEADER] = { .type = NLA_NESTED }, 222 [ETHTOOL_A_COALESCE_RX_USECS] = { .type = NLA_U32 }, 223 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES] = { .type = NLA_U32 }, 224 [ETHTOOL_A_COALESCE_RX_USECS_IRQ] = { .type = NLA_U32 }, 225 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ] = { .type = NLA_U32 }, 226 [ETHTOOL_A_COALESCE_TX_USECS] = { .type = NLA_U32 }, 227 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES] = { .type = NLA_U32 }, 228 [ETHTOOL_A_COALESCE_TX_USECS_IRQ] = { .type = NLA_U32 }, 229 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ] = { .type = NLA_U32 }, 230 [ETHTOOL_A_COALESCE_STATS_BLOCK_USECS] = { .type = NLA_U32 }, 231 [ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX] = { .type = NLA_U8 }, 232 [ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX] = { .type = NLA_U8 }, 233 [ETHTOOL_A_COALESCE_PKT_RATE_LOW] = { .type = NLA_U32 }, 234 [ETHTOOL_A_COALESCE_RX_USECS_LOW] = { .type = NLA_U32 }, 235 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW] = { .type = NLA_U32 }, 236 [ETHTOOL_A_COALESCE_TX_USECS_LOW] = { .type = NLA_U32 }, 237 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW] = { .type = NLA_U32 }, 238 [ETHTOOL_A_COALESCE_PKT_RATE_HIGH] = { .type = NLA_U32 }, 239 [ETHTOOL_A_COALESCE_RX_USECS_HIGH] = { .type = NLA_U32 }, 240 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH] = { .type = NLA_U32 }, 241 [ETHTOOL_A_COALESCE_TX_USECS_HIGH] = { .type = NLA_U32 }, 242 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH] = { .type = NLA_U32 }, 243 [ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL] = { .type = NLA_U32 }, 244 }; 245 246 int ethnl_set_coalesce(struct sk_buff *skb, struct genl_info *info) 247 { 248 struct nlattr *tb[ETHTOOL_A_COALESCE_MAX + 1]; 249 struct ethtool_coalesce coalesce = {}; 250 struct ethnl_req_info req_info = {}; 251 const struct ethtool_ops *ops; 252 struct net_device *dev; 253 u32 supported_params; 254 bool mod = false; 255 int ret; 256 u16 a; 257 258 ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, 259 ETHTOOL_A_COALESCE_MAX, coalesce_set_policy, 260 info->extack); 261 if (ret < 0) 262 return ret; 263 ret = ethnl_parse_header_dev_get(&req_info, 264 tb[ETHTOOL_A_COALESCE_HEADER], 265 genl_info_net(info), info->extack, 266 true); 267 if (ret < 0) 268 return ret; 269 dev = req_info.dev; 270 ops = dev->ethtool_ops; 271 ret = -EOPNOTSUPP; 272 if (!ops->get_coalesce || !ops->set_coalesce) 273 goto out_dev; 274 275 /* make sure that only supported parameters are present */ 276 supported_params = ops->supported_coalesce_params; 277 for (a = ETHTOOL_A_COALESCE_RX_USECS; a < __ETHTOOL_A_COALESCE_CNT; a++) 278 if (tb[a] && !(supported_params & attr_to_mask(a))) { 279 ret = -EINVAL; 280 NL_SET_ERR_MSG_ATTR(info->extack, tb[a], 281 "cannot modify an unsupported parameter"); 282 goto out_dev; 283 } 284 285 rtnl_lock(); 286 ret = ethnl_ops_begin(dev); 287 if (ret < 0) 288 goto out_rtnl; 289 ret = ops->get_coalesce(dev, &coalesce); 290 if (ret < 0) 291 goto out_ops; 292 293 ethnl_update_u32(&coalesce.rx_coalesce_usecs, 294 tb[ETHTOOL_A_COALESCE_RX_USECS], &mod); 295 ethnl_update_u32(&coalesce.rx_max_coalesced_frames, 296 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES], &mod); 297 ethnl_update_u32(&coalesce.rx_coalesce_usecs_irq, 298 tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ], &mod); 299 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_irq, 300 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ], &mod); 301 ethnl_update_u32(&coalesce.tx_coalesce_usecs, 302 tb[ETHTOOL_A_COALESCE_TX_USECS], &mod); 303 ethnl_update_u32(&coalesce.tx_max_coalesced_frames, 304 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES], &mod); 305 ethnl_update_u32(&coalesce.tx_coalesce_usecs_irq, 306 tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ], &mod); 307 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_irq, 308 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ], &mod); 309 ethnl_update_u32(&coalesce.stats_block_coalesce_usecs, 310 tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS], &mod); 311 ethnl_update_bool32(&coalesce.use_adaptive_rx_coalesce, 312 tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX], &mod); 313 ethnl_update_bool32(&coalesce.use_adaptive_tx_coalesce, 314 tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX], &mod); 315 ethnl_update_u32(&coalesce.pkt_rate_low, 316 tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW], &mod); 317 ethnl_update_u32(&coalesce.rx_coalesce_usecs_low, 318 tb[ETHTOOL_A_COALESCE_RX_USECS_LOW], &mod); 319 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_low, 320 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW], &mod); 321 ethnl_update_u32(&coalesce.tx_coalesce_usecs_low, 322 tb[ETHTOOL_A_COALESCE_TX_USECS_LOW], &mod); 323 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_low, 324 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW], &mod); 325 ethnl_update_u32(&coalesce.pkt_rate_high, 326 tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH], &mod); 327 ethnl_update_u32(&coalesce.rx_coalesce_usecs_high, 328 tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH], &mod); 329 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_high, 330 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH], &mod); 331 ethnl_update_u32(&coalesce.tx_coalesce_usecs_high, 332 tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH], &mod); 333 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_high, 334 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], &mod); 335 ethnl_update_u32(&coalesce.rate_sample_interval, 336 tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL], &mod); 337 ret = 0; 338 if (!mod) 339 goto out_ops; 340 341 ret = dev->ethtool_ops->set_coalesce(dev, &coalesce); 342 if (ret < 0) 343 goto out_ops; 344 ethtool_notify(dev, ETHTOOL_MSG_COALESCE_NTF, NULL); 345 346 out_ops: 347 ethnl_ops_complete(dev); 348 out_rtnl: 349 rtnl_unlock(); 350 out_dev: 351 dev_put(dev); 352 return ret; 353 } 354