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 struct kernel_ethtool_coalesce kernel_coalesce; 14 u32 supported_params; 15 }; 16 17 #define COALESCE_REPDATA(__reply_base) \ 18 container_of(__reply_base, struct coalesce_reply_data, base) 19 20 #define __SUPPORTED_OFFSET ETHTOOL_A_COALESCE_RX_USECS 21 static u32 attr_to_mask(unsigned int attr_type) 22 { 23 return BIT(attr_type - __SUPPORTED_OFFSET); 24 } 25 26 /* build time check that indices in ethtool_ops::supported_coalesce_params 27 * match corresponding attribute types with an offset 28 */ 29 #define __CHECK_SUPPORTED_OFFSET(x) \ 30 static_assert((ETHTOOL_ ## x) == \ 31 BIT((ETHTOOL_A_ ## x) - __SUPPORTED_OFFSET)) 32 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS); 33 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES); 34 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_IRQ); 35 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_IRQ); 36 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS); 37 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES); 38 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_IRQ); 39 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_IRQ); 40 __CHECK_SUPPORTED_OFFSET(COALESCE_STATS_BLOCK_USECS); 41 __CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_RX); 42 __CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_TX); 43 __CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_LOW); 44 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_LOW); 45 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_LOW); 46 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_LOW); 47 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_LOW); 48 __CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_HIGH); 49 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_HIGH); 50 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_HIGH); 51 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_HIGH); 52 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_HIGH); 53 __CHECK_SUPPORTED_OFFSET(COALESCE_RATE_SAMPLE_INTERVAL); 54 55 const struct nla_policy ethnl_coalesce_get_policy[] = { 56 [ETHTOOL_A_COALESCE_HEADER] = 57 NLA_POLICY_NESTED(ethnl_header_policy), 58 }; 59 60 static int coalesce_prepare_data(const struct ethnl_req_info *req_base, 61 struct ethnl_reply_data *reply_base, 62 const struct genl_info *info) 63 { 64 struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base); 65 struct net_device *dev = reply_base->dev; 66 int ret; 67 68 if (!dev->ethtool_ops->get_coalesce) 69 return -EOPNOTSUPP; 70 data->supported_params = dev->ethtool_ops->supported_coalesce_params; 71 ret = ethnl_ops_begin(dev); 72 if (ret < 0) 73 return ret; 74 ret = dev->ethtool_ops->get_coalesce(dev, &data->coalesce, 75 &data->kernel_coalesce, 76 info->extack); 77 ethnl_ops_complete(dev); 78 79 return ret; 80 } 81 82 static int coalesce_reply_size(const struct ethnl_req_info *req_base, 83 const struct ethnl_reply_data *reply_base) 84 { 85 return nla_total_size(sizeof(u32)) + /* _RX_USECS */ 86 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES */ 87 nla_total_size(sizeof(u32)) + /* _RX_USECS_IRQ */ 88 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_IRQ */ 89 nla_total_size(sizeof(u32)) + /* _TX_USECS */ 90 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES */ 91 nla_total_size(sizeof(u32)) + /* _TX_USECS_IRQ */ 92 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_IRQ */ 93 nla_total_size(sizeof(u32)) + /* _STATS_BLOCK_USECS */ 94 nla_total_size(sizeof(u8)) + /* _USE_ADAPTIVE_RX */ 95 nla_total_size(sizeof(u8)) + /* _USE_ADAPTIVE_TX */ 96 nla_total_size(sizeof(u32)) + /* _PKT_RATE_LOW */ 97 nla_total_size(sizeof(u32)) + /* _RX_USECS_LOW */ 98 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_LOW */ 99 nla_total_size(sizeof(u32)) + /* _TX_USECS_LOW */ 100 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_LOW */ 101 nla_total_size(sizeof(u32)) + /* _PKT_RATE_HIGH */ 102 nla_total_size(sizeof(u32)) + /* _RX_USECS_HIGH */ 103 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_HIGH */ 104 nla_total_size(sizeof(u32)) + /* _TX_USECS_HIGH */ 105 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_HIGH */ 106 nla_total_size(sizeof(u32)) + /* _RATE_SAMPLE_INTERVAL */ 107 nla_total_size(sizeof(u8)) + /* _USE_CQE_MODE_TX */ 108 nla_total_size(sizeof(u8)) + /* _USE_CQE_MODE_RX */ 109 nla_total_size(sizeof(u32)) + /* _TX_AGGR_MAX_BYTES */ 110 nla_total_size(sizeof(u32)) + /* _TX_AGGR_MAX_FRAMES */ 111 nla_total_size(sizeof(u32)); /* _TX_AGGR_TIME_USECS */ 112 } 113 114 static bool coalesce_put_u32(struct sk_buff *skb, u16 attr_type, u32 val, 115 u32 supported_params) 116 { 117 if (!val && !(supported_params & attr_to_mask(attr_type))) 118 return false; 119 return nla_put_u32(skb, attr_type, val); 120 } 121 122 static bool coalesce_put_bool(struct sk_buff *skb, u16 attr_type, u32 val, 123 u32 supported_params) 124 { 125 if (!val && !(supported_params & attr_to_mask(attr_type))) 126 return false; 127 return nla_put_u8(skb, attr_type, !!val); 128 } 129 130 static int coalesce_fill_reply(struct sk_buff *skb, 131 const struct ethnl_req_info *req_base, 132 const struct ethnl_reply_data *reply_base) 133 { 134 const struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base); 135 const struct kernel_ethtool_coalesce *kcoal = &data->kernel_coalesce; 136 const struct ethtool_coalesce *coal = &data->coalesce; 137 u32 supported = data->supported_params; 138 139 if (coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS, 140 coal->rx_coalesce_usecs, supported) || 141 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES, 142 coal->rx_max_coalesced_frames, supported) || 143 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_IRQ, 144 coal->rx_coalesce_usecs_irq, supported) || 145 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ, 146 coal->rx_max_coalesced_frames_irq, supported) || 147 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS, 148 coal->tx_coalesce_usecs, supported) || 149 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES, 150 coal->tx_max_coalesced_frames, supported) || 151 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_IRQ, 152 coal->tx_coalesce_usecs_irq, supported) || 153 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ, 154 coal->tx_max_coalesced_frames_irq, supported) || 155 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_STATS_BLOCK_USECS, 156 coal->stats_block_coalesce_usecs, supported) || 157 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX, 158 coal->use_adaptive_rx_coalesce, supported) || 159 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX, 160 coal->use_adaptive_tx_coalesce, supported) || 161 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_LOW, 162 coal->pkt_rate_low, supported) || 163 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_LOW, 164 coal->rx_coalesce_usecs_low, supported) || 165 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW, 166 coal->rx_max_coalesced_frames_low, supported) || 167 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_LOW, 168 coal->tx_coalesce_usecs_low, supported) || 169 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW, 170 coal->tx_max_coalesced_frames_low, supported) || 171 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_HIGH, 172 coal->pkt_rate_high, supported) || 173 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_HIGH, 174 coal->rx_coalesce_usecs_high, supported) || 175 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH, 176 coal->rx_max_coalesced_frames_high, supported) || 177 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_HIGH, 178 coal->tx_coalesce_usecs_high, supported) || 179 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH, 180 coal->tx_max_coalesced_frames_high, supported) || 181 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL, 182 coal->rate_sample_interval, supported) || 183 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_TX, 184 kcoal->use_cqe_mode_tx, supported) || 185 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_RX, 186 kcoal->use_cqe_mode_rx, supported) || 187 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES, 188 kcoal->tx_aggr_max_bytes, supported) || 189 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES, 190 kcoal->tx_aggr_max_frames, supported) || 191 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS, 192 kcoal->tx_aggr_time_usecs, supported)) 193 return -EMSGSIZE; 194 195 return 0; 196 } 197 198 /* COALESCE_SET */ 199 200 const struct nla_policy ethnl_coalesce_set_policy[] = { 201 [ETHTOOL_A_COALESCE_HEADER] = 202 NLA_POLICY_NESTED(ethnl_header_policy), 203 [ETHTOOL_A_COALESCE_RX_USECS] = { .type = NLA_U32 }, 204 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES] = { .type = NLA_U32 }, 205 [ETHTOOL_A_COALESCE_RX_USECS_IRQ] = { .type = NLA_U32 }, 206 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ] = { .type = NLA_U32 }, 207 [ETHTOOL_A_COALESCE_TX_USECS] = { .type = NLA_U32 }, 208 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES] = { .type = NLA_U32 }, 209 [ETHTOOL_A_COALESCE_TX_USECS_IRQ] = { .type = NLA_U32 }, 210 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ] = { .type = NLA_U32 }, 211 [ETHTOOL_A_COALESCE_STATS_BLOCK_USECS] = { .type = NLA_U32 }, 212 [ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX] = { .type = NLA_U8 }, 213 [ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX] = { .type = NLA_U8 }, 214 [ETHTOOL_A_COALESCE_PKT_RATE_LOW] = { .type = NLA_U32 }, 215 [ETHTOOL_A_COALESCE_RX_USECS_LOW] = { .type = NLA_U32 }, 216 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW] = { .type = NLA_U32 }, 217 [ETHTOOL_A_COALESCE_TX_USECS_LOW] = { .type = NLA_U32 }, 218 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW] = { .type = NLA_U32 }, 219 [ETHTOOL_A_COALESCE_PKT_RATE_HIGH] = { .type = NLA_U32 }, 220 [ETHTOOL_A_COALESCE_RX_USECS_HIGH] = { .type = NLA_U32 }, 221 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH] = { .type = NLA_U32 }, 222 [ETHTOOL_A_COALESCE_TX_USECS_HIGH] = { .type = NLA_U32 }, 223 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH] = { .type = NLA_U32 }, 224 [ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL] = { .type = NLA_U32 }, 225 [ETHTOOL_A_COALESCE_USE_CQE_MODE_TX] = NLA_POLICY_MAX(NLA_U8, 1), 226 [ETHTOOL_A_COALESCE_USE_CQE_MODE_RX] = NLA_POLICY_MAX(NLA_U8, 1), 227 [ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES] = { .type = NLA_U32 }, 228 [ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES] = { .type = NLA_U32 }, 229 [ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS] = { .type = NLA_U32 }, 230 }; 231 232 static int 233 ethnl_set_coalesce_validate(struct ethnl_req_info *req_info, 234 struct genl_info *info) 235 { 236 const struct ethtool_ops *ops = req_info->dev->ethtool_ops; 237 struct nlattr **tb = info->attrs; 238 u32 supported_params; 239 u16 a; 240 241 if (!ops->get_coalesce || !ops->set_coalesce) 242 return -EOPNOTSUPP; 243 244 /* make sure that only supported parameters are present */ 245 supported_params = ops->supported_coalesce_params; 246 for (a = ETHTOOL_A_COALESCE_RX_USECS; a < __ETHTOOL_A_COALESCE_CNT; a++) 247 if (tb[a] && !(supported_params & attr_to_mask(a))) { 248 NL_SET_ERR_MSG_ATTR(info->extack, tb[a], 249 "cannot modify an unsupported parameter"); 250 return -EINVAL; 251 } 252 253 return 1; 254 } 255 256 static int 257 __ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info, 258 bool *dual_change) 259 { 260 struct kernel_ethtool_coalesce kernel_coalesce = {}; 261 struct net_device *dev = req_info->dev; 262 struct ethtool_coalesce coalesce = {}; 263 bool mod_mode = false, mod = false; 264 struct nlattr **tb = info->attrs; 265 int ret; 266 267 ret = dev->ethtool_ops->get_coalesce(dev, &coalesce, &kernel_coalesce, 268 info->extack); 269 if (ret < 0) 270 return ret; 271 272 /* Update values */ 273 ethnl_update_u32(&coalesce.rx_coalesce_usecs, 274 tb[ETHTOOL_A_COALESCE_RX_USECS], &mod); 275 ethnl_update_u32(&coalesce.rx_max_coalesced_frames, 276 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES], &mod); 277 ethnl_update_u32(&coalesce.rx_coalesce_usecs_irq, 278 tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ], &mod); 279 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_irq, 280 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ], &mod); 281 ethnl_update_u32(&coalesce.tx_coalesce_usecs, 282 tb[ETHTOOL_A_COALESCE_TX_USECS], &mod); 283 ethnl_update_u32(&coalesce.tx_max_coalesced_frames, 284 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES], &mod); 285 ethnl_update_u32(&coalesce.tx_coalesce_usecs_irq, 286 tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ], &mod); 287 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_irq, 288 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ], &mod); 289 ethnl_update_u32(&coalesce.stats_block_coalesce_usecs, 290 tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS], &mod); 291 ethnl_update_u32(&coalesce.pkt_rate_low, 292 tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW], &mod); 293 ethnl_update_u32(&coalesce.rx_coalesce_usecs_low, 294 tb[ETHTOOL_A_COALESCE_RX_USECS_LOW], &mod); 295 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_low, 296 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW], &mod); 297 ethnl_update_u32(&coalesce.tx_coalesce_usecs_low, 298 tb[ETHTOOL_A_COALESCE_TX_USECS_LOW], &mod); 299 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_low, 300 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW], &mod); 301 ethnl_update_u32(&coalesce.pkt_rate_high, 302 tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH], &mod); 303 ethnl_update_u32(&coalesce.rx_coalesce_usecs_high, 304 tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH], &mod); 305 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_high, 306 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH], &mod); 307 ethnl_update_u32(&coalesce.tx_coalesce_usecs_high, 308 tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH], &mod); 309 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_high, 310 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], &mod); 311 ethnl_update_u32(&coalesce.rate_sample_interval, 312 tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL], &mod); 313 ethnl_update_u32(&kernel_coalesce.tx_aggr_max_bytes, 314 tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES], &mod); 315 ethnl_update_u32(&kernel_coalesce.tx_aggr_max_frames, 316 tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES], &mod); 317 ethnl_update_u32(&kernel_coalesce.tx_aggr_time_usecs, 318 tb[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS], &mod); 319 320 /* Update operation modes */ 321 ethnl_update_bool32(&coalesce.use_adaptive_rx_coalesce, 322 tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX], &mod_mode); 323 ethnl_update_bool32(&coalesce.use_adaptive_tx_coalesce, 324 tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX], &mod_mode); 325 ethnl_update_u8(&kernel_coalesce.use_cqe_mode_tx, 326 tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_TX], &mod_mode); 327 ethnl_update_u8(&kernel_coalesce.use_cqe_mode_rx, 328 tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_RX], &mod_mode); 329 330 *dual_change = mod && mod_mode; 331 if (!mod && !mod_mode) 332 return 0; 333 334 ret = dev->ethtool_ops->set_coalesce(dev, &coalesce, &kernel_coalesce, 335 info->extack); 336 return ret < 0 ? ret : 1; 337 } 338 339 static int 340 ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info) 341 { 342 bool dual_change; 343 int err, ret; 344 345 /* SET_COALESCE may change operation mode and parameters in one call. 346 * Changing operation mode may cause the driver to reset the parameter 347 * values, and therefore ignore user input (driver does not know which 348 * parameters come from user and which are echoed back from ->get). 349 * To not complicate the drivers if user tries to change both the mode 350 * and parameters at once - call the driver twice. 351 */ 352 err = __ethnl_set_coalesce(req_info, info, &dual_change); 353 if (err < 0) 354 return err; 355 ret = err; 356 357 if (ret && dual_change) { 358 err = __ethnl_set_coalesce(req_info, info, &dual_change); 359 if (err < 0) 360 return err; 361 } 362 return ret; 363 } 364 365 const struct ethnl_request_ops ethnl_coalesce_request_ops = { 366 .request_cmd = ETHTOOL_MSG_COALESCE_GET, 367 .reply_cmd = ETHTOOL_MSG_COALESCE_GET_REPLY, 368 .hdr_attr = ETHTOOL_A_COALESCE_HEADER, 369 .req_info_size = sizeof(struct coalesce_req_info), 370 .reply_data_size = sizeof(struct coalesce_reply_data), 371 372 .prepare_data = coalesce_prepare_data, 373 .reply_size = coalesce_reply_size, 374 .fill_reply = coalesce_fill_reply, 375 376 .set_validate = ethnl_set_coalesce_validate, 377 .set = ethnl_set_coalesce, 378 .set_ntf_cmd = ETHTOOL_MSG_COALESCE_NTF, 379 }; 380