1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/dim.h> 4 5 #include "common.h" 6 #include "netlink.h" 7 8 struct coalesce_req_info { 9 struct ethnl_req_info base; 10 }; 11 12 struct coalesce_reply_data { 13 struct ethnl_reply_data base; 14 struct ethtool_coalesce coalesce; 15 struct kernel_ethtool_coalesce kernel_coalesce; 16 u32 supported_params; 17 }; 18 19 #define COALESCE_REPDATA(__reply_base) \ 20 container_of(__reply_base, struct coalesce_reply_data, base) 21 22 #define __SUPPORTED_OFFSET ETHTOOL_A_COALESCE_RX_USECS 23 static u32 attr_to_mask(unsigned int attr_type) 24 { 25 return BIT(attr_type - __SUPPORTED_OFFSET); 26 } 27 28 /* build time check that indices in ethtool_ops::supported_coalesce_params 29 * match corresponding attribute types with an offset 30 */ 31 #define __CHECK_SUPPORTED_OFFSET(x) \ 32 static_assert((ETHTOOL_ ## x) == \ 33 BIT((ETHTOOL_A_ ## x) - __SUPPORTED_OFFSET)) 34 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS); 35 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES); 36 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_IRQ); 37 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_IRQ); 38 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS); 39 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES); 40 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_IRQ); 41 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_IRQ); 42 __CHECK_SUPPORTED_OFFSET(COALESCE_STATS_BLOCK_USECS); 43 __CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_RX); 44 __CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_TX); 45 __CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_LOW); 46 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_LOW); 47 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_LOW); 48 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_LOW); 49 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_LOW); 50 __CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_HIGH); 51 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_HIGH); 52 __CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_HIGH); 53 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_HIGH); 54 __CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_HIGH); 55 __CHECK_SUPPORTED_OFFSET(COALESCE_RATE_SAMPLE_INTERVAL); 56 57 const struct nla_policy ethnl_coalesce_get_policy[] = { 58 [ETHTOOL_A_COALESCE_HEADER] = 59 NLA_POLICY_NESTED(ethnl_header_policy), 60 }; 61 62 static int coalesce_prepare_data(const struct ethnl_req_info *req_base, 63 struct ethnl_reply_data *reply_base, 64 const struct genl_info *info) 65 { 66 struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base); 67 struct net_device *dev = reply_base->dev; 68 int ret; 69 70 if (!dev->ethtool_ops->get_coalesce) 71 return -EOPNOTSUPP; 72 data->supported_params = dev->ethtool_ops->supported_coalesce_params; 73 ret = ethnl_ops_begin(dev); 74 if (ret < 0) 75 return ret; 76 ret = dev->ethtool_ops->get_coalesce(dev, &data->coalesce, 77 &data->kernel_coalesce, 78 info->extack); 79 ethnl_ops_complete(dev); 80 81 return ret; 82 } 83 84 static int coalesce_reply_size(const struct ethnl_req_info *req_base, 85 const struct ethnl_reply_data *reply_base) 86 { 87 int modersz = nla_total_size(0) + /* _PROFILE_IRQ_MODERATION, nest */ 88 nla_total_size(sizeof(u32)) + /* _IRQ_MODERATION_USEC */ 89 nla_total_size(sizeof(u32)) + /* _IRQ_MODERATION_PKTS */ 90 nla_total_size(sizeof(u32)); /* _IRQ_MODERATION_COMPS */ 91 92 int total_modersz = nla_total_size(0) + /* _{R,T}X_PROFILE, nest */ 93 modersz * NET_DIM_PARAMS_NUM_PROFILES; 94 95 return nla_total_size(sizeof(u32)) + /* _RX_USECS */ 96 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES */ 97 nla_total_size(sizeof(u32)) + /* _RX_USECS_IRQ */ 98 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_IRQ */ 99 nla_total_size(sizeof(u32)) + /* _TX_USECS */ 100 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES */ 101 nla_total_size(sizeof(u32)) + /* _TX_USECS_IRQ */ 102 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_IRQ */ 103 nla_total_size(sizeof(u32)) + /* _STATS_BLOCK_USECS */ 104 nla_total_size(sizeof(u8)) + /* _USE_ADAPTIVE_RX */ 105 nla_total_size(sizeof(u8)) + /* _USE_ADAPTIVE_TX */ 106 nla_total_size(sizeof(u32)) + /* _PKT_RATE_LOW */ 107 nla_total_size(sizeof(u32)) + /* _RX_USECS_LOW */ 108 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_LOW */ 109 nla_total_size(sizeof(u32)) + /* _TX_USECS_LOW */ 110 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_LOW */ 111 nla_total_size(sizeof(u32)) + /* _PKT_RATE_HIGH */ 112 nla_total_size(sizeof(u32)) + /* _RX_USECS_HIGH */ 113 nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_HIGH */ 114 nla_total_size(sizeof(u32)) + /* _TX_USECS_HIGH */ 115 nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_HIGH */ 116 nla_total_size(sizeof(u32)) + /* _RATE_SAMPLE_INTERVAL */ 117 nla_total_size(sizeof(u8)) + /* _USE_CQE_MODE_TX */ 118 nla_total_size(sizeof(u8)) + /* _USE_CQE_MODE_RX */ 119 nla_total_size(sizeof(u32)) + /* _TX_AGGR_MAX_BYTES */ 120 nla_total_size(sizeof(u32)) + /* _TX_AGGR_MAX_FRAMES */ 121 nla_total_size(sizeof(u32)) + /* _TX_AGGR_TIME_USECS */ 122 nla_total_size(sizeof(u32)) + /* _RX_CQE_FRAMES */ 123 nla_total_size(sizeof(u32)) + /* _RX_CQE_NSECS */ 124 total_modersz * 2; /* _{R,T}X_PROFILE */ 125 } 126 127 static bool coalesce_put_u32(struct sk_buff *skb, u16 attr_type, u32 val, 128 u32 supported_params) 129 { 130 if (!val && !(supported_params & attr_to_mask(attr_type))) 131 return false; 132 return nla_put_u32(skb, attr_type, val); 133 } 134 135 static bool coalesce_put_bool(struct sk_buff *skb, u16 attr_type, u32 val, 136 u32 supported_params) 137 { 138 if (!val && !(supported_params & attr_to_mask(attr_type))) 139 return false; 140 return nla_put_u8(skb, attr_type, !!val); 141 } 142 143 /** 144 * coalesce_put_profile - fill reply with a nla nest with four child nla nests. 145 * @skb: socket buffer the message is stored in 146 * @attr_type: nest attr type ETHTOOL_A_COALESCE_*X_PROFILE 147 * @profile: data passed to userspace 148 * @coal_flags: modifiable parameters supported by the driver 149 * 150 * Put a dim profile nest attribute. Refer to ETHTOOL_A_PROFILE_IRQ_MODERATION. 151 * 152 * Return: 0 on success or a negative error code. 153 */ 154 static int coalesce_put_profile(struct sk_buff *skb, u16 attr_type, 155 const struct dim_cq_moder *profile, 156 u8 coal_flags) 157 { 158 struct nlattr *profile_attr, *moder_attr; 159 int i, ret; 160 161 if (!profile || !coal_flags) 162 return 0; 163 164 profile_attr = nla_nest_start(skb, attr_type); 165 if (!profile_attr) 166 return -EMSGSIZE; 167 168 for (i = 0; i < NET_DIM_PARAMS_NUM_PROFILES; i++) { 169 moder_attr = nla_nest_start(skb, 170 ETHTOOL_A_PROFILE_IRQ_MODERATION); 171 if (!moder_attr) { 172 ret = -EMSGSIZE; 173 goto cancel_profile; 174 } 175 176 if (coal_flags & DIM_COALESCE_USEC) { 177 ret = nla_put_u32(skb, ETHTOOL_A_IRQ_MODERATION_USEC, 178 profile[i].usec); 179 if (ret) 180 goto cancel_moder; 181 } 182 183 if (coal_flags & DIM_COALESCE_PKTS) { 184 ret = nla_put_u32(skb, ETHTOOL_A_IRQ_MODERATION_PKTS, 185 profile[i].pkts); 186 if (ret) 187 goto cancel_moder; 188 } 189 190 if (coal_flags & DIM_COALESCE_COMPS) { 191 ret = nla_put_u32(skb, ETHTOOL_A_IRQ_MODERATION_COMPS, 192 profile[i].comps); 193 if (ret) 194 goto cancel_moder; 195 } 196 197 nla_nest_end(skb, moder_attr); 198 } 199 200 nla_nest_end(skb, profile_attr); 201 202 return 0; 203 204 cancel_moder: 205 nla_nest_cancel(skb, moder_attr); 206 cancel_profile: 207 nla_nest_cancel(skb, profile_attr); 208 return ret; 209 } 210 211 static int coalesce_fill_reply(struct sk_buff *skb, 212 const struct ethnl_req_info *req_base, 213 const struct ethnl_reply_data *reply_base) 214 { 215 const struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base); 216 const struct kernel_ethtool_coalesce *kcoal = &data->kernel_coalesce; 217 const struct ethtool_coalesce *coal = &data->coalesce; 218 u32 supported = data->supported_params; 219 struct dim_irq_moder *moder; 220 int ret = 0; 221 222 if (coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS, 223 coal->rx_coalesce_usecs, supported) || 224 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES, 225 coal->rx_max_coalesced_frames, supported) || 226 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_IRQ, 227 coal->rx_coalesce_usecs_irq, supported) || 228 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ, 229 coal->rx_max_coalesced_frames_irq, supported) || 230 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS, 231 coal->tx_coalesce_usecs, supported) || 232 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES, 233 coal->tx_max_coalesced_frames, supported) || 234 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_IRQ, 235 coal->tx_coalesce_usecs_irq, supported) || 236 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ, 237 coal->tx_max_coalesced_frames_irq, supported) || 238 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_STATS_BLOCK_USECS, 239 coal->stats_block_coalesce_usecs, supported) || 240 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX, 241 coal->use_adaptive_rx_coalesce, supported) || 242 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX, 243 coal->use_adaptive_tx_coalesce, supported) || 244 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_LOW, 245 coal->pkt_rate_low, supported) || 246 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_LOW, 247 coal->rx_coalesce_usecs_low, supported) || 248 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW, 249 coal->rx_max_coalesced_frames_low, supported) || 250 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_LOW, 251 coal->tx_coalesce_usecs_low, supported) || 252 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW, 253 coal->tx_max_coalesced_frames_low, supported) || 254 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_HIGH, 255 coal->pkt_rate_high, supported) || 256 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_HIGH, 257 coal->rx_coalesce_usecs_high, supported) || 258 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH, 259 coal->rx_max_coalesced_frames_high, supported) || 260 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_HIGH, 261 coal->tx_coalesce_usecs_high, supported) || 262 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH, 263 coal->tx_max_coalesced_frames_high, supported) || 264 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL, 265 coal->rate_sample_interval, supported) || 266 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_TX, 267 kcoal->use_cqe_mode_tx, supported) || 268 coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_RX, 269 kcoal->use_cqe_mode_rx, supported) || 270 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES, 271 kcoal->tx_aggr_max_bytes, supported) || 272 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES, 273 kcoal->tx_aggr_max_frames, supported) || 274 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS, 275 kcoal->tx_aggr_time_usecs, supported) || 276 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_CQE_FRAMES, 277 kcoal->rx_cqe_frames, supported) || 278 coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_CQE_NSECS, 279 kcoal->rx_cqe_nsecs, supported)) 280 return -EMSGSIZE; 281 282 if (!req_base->dev || !req_base->dev->irq_moder) 283 return 0; 284 285 moder = req_base->dev->irq_moder; 286 rcu_read_lock(); 287 if (moder->profile_flags & DIM_PROFILE_RX) { 288 ret = coalesce_put_profile(skb, ETHTOOL_A_COALESCE_RX_PROFILE, 289 rcu_dereference(moder->rx_profile), 290 moder->coal_flags); 291 if (ret) 292 goto out; 293 } 294 295 if (moder->profile_flags & DIM_PROFILE_TX) 296 ret = coalesce_put_profile(skb, ETHTOOL_A_COALESCE_TX_PROFILE, 297 rcu_dereference(moder->tx_profile), 298 moder->coal_flags); 299 300 out: 301 rcu_read_unlock(); 302 return ret; 303 } 304 305 /* COALESCE_SET */ 306 307 static const struct nla_policy coalesce_irq_moderation_policy[] = { 308 [ETHTOOL_A_IRQ_MODERATION_USEC] = { .type = NLA_U32 }, 309 [ETHTOOL_A_IRQ_MODERATION_PKTS] = { .type = NLA_U32 }, 310 [ETHTOOL_A_IRQ_MODERATION_COMPS] = { .type = NLA_U32 }, 311 }; 312 313 static const struct nla_policy coalesce_profile_policy[] = { 314 [ETHTOOL_A_PROFILE_IRQ_MODERATION] = 315 NLA_POLICY_NESTED(coalesce_irq_moderation_policy), 316 }; 317 318 const struct nla_policy ethnl_coalesce_set_policy[] = { 319 [ETHTOOL_A_COALESCE_HEADER] = 320 NLA_POLICY_NESTED(ethnl_header_policy), 321 [ETHTOOL_A_COALESCE_RX_USECS] = { .type = NLA_U32 }, 322 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES] = { .type = NLA_U32 }, 323 [ETHTOOL_A_COALESCE_RX_USECS_IRQ] = { .type = NLA_U32 }, 324 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ] = { .type = NLA_U32 }, 325 [ETHTOOL_A_COALESCE_TX_USECS] = { .type = NLA_U32 }, 326 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES] = { .type = NLA_U32 }, 327 [ETHTOOL_A_COALESCE_TX_USECS_IRQ] = { .type = NLA_U32 }, 328 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ] = { .type = NLA_U32 }, 329 [ETHTOOL_A_COALESCE_STATS_BLOCK_USECS] = { .type = NLA_U32 }, 330 [ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX] = { .type = NLA_U8 }, 331 [ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX] = { .type = NLA_U8 }, 332 [ETHTOOL_A_COALESCE_PKT_RATE_LOW] = { .type = NLA_U32 }, 333 [ETHTOOL_A_COALESCE_RX_USECS_LOW] = { .type = NLA_U32 }, 334 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW] = { .type = NLA_U32 }, 335 [ETHTOOL_A_COALESCE_TX_USECS_LOW] = { .type = NLA_U32 }, 336 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW] = { .type = NLA_U32 }, 337 [ETHTOOL_A_COALESCE_PKT_RATE_HIGH] = { .type = NLA_U32 }, 338 [ETHTOOL_A_COALESCE_RX_USECS_HIGH] = { .type = NLA_U32 }, 339 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH] = { .type = NLA_U32 }, 340 [ETHTOOL_A_COALESCE_TX_USECS_HIGH] = { .type = NLA_U32 }, 341 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH] = { .type = NLA_U32 }, 342 [ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL] = { .type = NLA_U32 }, 343 [ETHTOOL_A_COALESCE_USE_CQE_MODE_TX] = NLA_POLICY_MAX(NLA_U8, 1), 344 [ETHTOOL_A_COALESCE_USE_CQE_MODE_RX] = NLA_POLICY_MAX(NLA_U8, 1), 345 [ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES] = { .type = NLA_U32 }, 346 [ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES] = { .type = NLA_U32 }, 347 [ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS] = { .type = NLA_U32 }, 348 [ETHTOOL_A_COALESCE_RX_CQE_FRAMES] = { .type = NLA_U32 }, 349 [ETHTOOL_A_COALESCE_RX_CQE_NSECS] = { .type = NLA_U32 }, 350 [ETHTOOL_A_COALESCE_RX_PROFILE] = 351 NLA_POLICY_NESTED(coalesce_profile_policy), 352 [ETHTOOL_A_COALESCE_TX_PROFILE] = 353 NLA_POLICY_NESTED(coalesce_profile_policy), 354 }; 355 356 static int 357 ethnl_set_coalesce_validate(struct ethnl_req_info *req_info, 358 struct genl_info *info) 359 { 360 const struct ethtool_ops *ops = req_info->dev->ethtool_ops; 361 struct dim_irq_moder *irq_moder = req_info->dev->irq_moder; 362 struct nlattr **tb = info->attrs; 363 u32 supported_params; 364 u16 a; 365 366 if (!ops->get_coalesce || !ops->set_coalesce) 367 return -EOPNOTSUPP; 368 369 /* make sure that only supported parameters are present */ 370 supported_params = ops->supported_coalesce_params; 371 if (irq_moder && irq_moder->profile_flags & DIM_PROFILE_RX) 372 supported_params |= ETHTOOL_COALESCE_RX_PROFILE; 373 374 if (irq_moder && irq_moder->profile_flags & DIM_PROFILE_TX) 375 supported_params |= ETHTOOL_COALESCE_TX_PROFILE; 376 377 for (a = ETHTOOL_A_COALESCE_RX_USECS; a < __ETHTOOL_A_COALESCE_CNT; a++) 378 if (tb[a] && !(supported_params & attr_to_mask(a))) { 379 NL_SET_ERR_MSG_ATTR(info->extack, tb[a], 380 "cannot modify an unsupported parameter"); 381 return -EINVAL; 382 } 383 384 return 1; 385 } 386 387 /** 388 * ethnl_update_irq_moder - update a specific field in the given profile 389 * @irq_moder: place that collects dim related information 390 * @irq_field: field in profile to modify 391 * @attr_type: attr type ETHTOOL_A_IRQ_MODERATION_* 392 * @tb: netlink attribute with new values or null 393 * @coal_bit: DIM_COALESCE_* bit from coal_flags 394 * @mod: pointer to bool for modification tracking 395 * @extack: netlink extended ack 396 * 397 * Return: 0 on success or a negative error code. 398 */ 399 static int ethnl_update_irq_moder(struct dim_irq_moder *irq_moder, 400 u16 *irq_field, u16 attr_type, 401 struct nlattr **tb, 402 u8 coal_bit, bool *mod, 403 struct netlink_ext_ack *extack) 404 { 405 int ret = 0; 406 u32 val; 407 408 if (!tb[attr_type]) 409 return 0; 410 411 if (irq_moder->coal_flags & coal_bit) { 412 val = nla_get_u32(tb[attr_type]); 413 if (*irq_field == val) 414 return 0; 415 416 *irq_field = val; 417 *mod = true; 418 } else { 419 NL_SET_BAD_ATTR(extack, tb[attr_type]); 420 ret = -EOPNOTSUPP; 421 } 422 423 return ret; 424 } 425 426 /** 427 * ethnl_update_profile - get a profile nest with child nests from userspace. 428 * @dev: netdevice to update the profile 429 * @dst: profile get from the driver and modified by ethnl_update_profile. 430 * @nests: nest attr ETHTOOL_A_COALESCE_*X_PROFILE to set profile. 431 * @mod: pointer to bool for modification tracking 432 * @extack: Netlink extended ack 433 * 434 * Layout of nests: 435 * Nested ETHTOOL_A_COALESCE_*X_PROFILE attr 436 * Nested ETHTOOL_A_PROFILE_IRQ_MODERATION attr 437 * ETHTOOL_A_IRQ_MODERATION_USEC attr 438 * ETHTOOL_A_IRQ_MODERATION_PKTS attr 439 * ETHTOOL_A_IRQ_MODERATION_COMPS attr 440 * ... 441 * Nested ETHTOOL_A_PROFILE_IRQ_MODERATION attr 442 * ETHTOOL_A_IRQ_MODERATION_USEC attr 443 * ETHTOOL_A_IRQ_MODERATION_PKTS attr 444 * ETHTOOL_A_IRQ_MODERATION_COMPS attr 445 * 446 * Return: 0 on success or a negative error code. 447 */ 448 static int ethnl_update_profile(struct net_device *dev, 449 struct dim_cq_moder __rcu **dst, 450 const struct nlattr *nests, 451 bool *mod, 452 struct netlink_ext_ack *extack) 453 { 454 int len_irq_moder = ARRAY_SIZE(coalesce_irq_moderation_policy); 455 struct nlattr *tb[ARRAY_SIZE(coalesce_irq_moderation_policy)]; 456 struct dim_irq_moder *irq_moder = dev->irq_moder; 457 struct dim_cq_moder *new_profile, *old_profile; 458 int ret, rem, i = 0, len; 459 struct nlattr *nest; 460 461 if (!nests) 462 return 0; 463 464 if (!*dst) 465 return -EOPNOTSUPP; 466 467 old_profile = rtnl_dereference(*dst); 468 len = NET_DIM_PARAMS_NUM_PROFILES * sizeof(*old_profile); 469 new_profile = kmemdup(old_profile, len, GFP_KERNEL); 470 if (!new_profile) 471 return -ENOMEM; 472 473 nla_for_each_nested_type(nest, ETHTOOL_A_PROFILE_IRQ_MODERATION, 474 nests, rem) { 475 ret = nla_parse_nested(tb, len_irq_moder - 1, nest, 476 coalesce_irq_moderation_policy, 477 extack); 478 if (ret) 479 goto err_out; 480 481 ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].usec, 482 ETHTOOL_A_IRQ_MODERATION_USEC, 483 tb, DIM_COALESCE_USEC, 484 mod, extack); 485 if (ret) 486 goto err_out; 487 488 ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].pkts, 489 ETHTOOL_A_IRQ_MODERATION_PKTS, 490 tb, DIM_COALESCE_PKTS, 491 mod, extack); 492 if (ret) 493 goto err_out; 494 495 ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].comps, 496 ETHTOOL_A_IRQ_MODERATION_COMPS, 497 tb, DIM_COALESCE_COMPS, 498 mod, extack); 499 if (ret) 500 goto err_out; 501 502 i++; 503 } 504 505 /* After the profile is modified, dim itself is a dynamic 506 * mechanism and will quickly fit to the appropriate 507 * coalescing parameters according to the new profile. 508 */ 509 rcu_assign_pointer(*dst, new_profile); 510 kfree_rcu(old_profile, rcu); 511 512 return 0; 513 514 err_out: 515 kfree(new_profile); 516 return ret; 517 } 518 519 static int 520 __ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info, 521 bool *dual_change) 522 { 523 struct kernel_ethtool_coalesce kernel_coalesce = {}; 524 struct net_device *dev = req_info->dev; 525 struct ethtool_coalesce coalesce = {}; 526 bool mod_mode = false, mod = false; 527 struct nlattr **tb = info->attrs; 528 int ret; 529 530 ret = dev->ethtool_ops->get_coalesce(dev, &coalesce, &kernel_coalesce, 531 info->extack); 532 if (ret < 0) 533 return ret; 534 535 /* Update values */ 536 ethnl_update_u32(&coalesce.rx_coalesce_usecs, 537 tb[ETHTOOL_A_COALESCE_RX_USECS], &mod); 538 ethnl_update_u32(&coalesce.rx_max_coalesced_frames, 539 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES], &mod); 540 ethnl_update_u32(&coalesce.rx_coalesce_usecs_irq, 541 tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ], &mod); 542 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_irq, 543 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ], &mod); 544 ethnl_update_u32(&coalesce.tx_coalesce_usecs, 545 tb[ETHTOOL_A_COALESCE_TX_USECS], &mod); 546 ethnl_update_u32(&coalesce.tx_max_coalesced_frames, 547 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES], &mod); 548 ethnl_update_u32(&coalesce.tx_coalesce_usecs_irq, 549 tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ], &mod); 550 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_irq, 551 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ], &mod); 552 ethnl_update_u32(&coalesce.stats_block_coalesce_usecs, 553 tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS], &mod); 554 ethnl_update_u32(&coalesce.pkt_rate_low, 555 tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW], &mod); 556 ethnl_update_u32(&coalesce.rx_coalesce_usecs_low, 557 tb[ETHTOOL_A_COALESCE_RX_USECS_LOW], &mod); 558 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_low, 559 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW], &mod); 560 ethnl_update_u32(&coalesce.tx_coalesce_usecs_low, 561 tb[ETHTOOL_A_COALESCE_TX_USECS_LOW], &mod); 562 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_low, 563 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW], &mod); 564 ethnl_update_u32(&coalesce.pkt_rate_high, 565 tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH], &mod); 566 ethnl_update_u32(&coalesce.rx_coalesce_usecs_high, 567 tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH], &mod); 568 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_high, 569 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH], &mod); 570 ethnl_update_u32(&coalesce.tx_coalesce_usecs_high, 571 tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH], &mod); 572 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_high, 573 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], &mod); 574 ethnl_update_u32(&coalesce.rate_sample_interval, 575 tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL], &mod); 576 ethnl_update_u32(&kernel_coalesce.tx_aggr_max_bytes, 577 tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES], &mod); 578 ethnl_update_u32(&kernel_coalesce.tx_aggr_max_frames, 579 tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES], &mod); 580 ethnl_update_u32(&kernel_coalesce.tx_aggr_time_usecs, 581 tb[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS], &mod); 582 ethnl_update_u32(&kernel_coalesce.rx_cqe_frames, 583 tb[ETHTOOL_A_COALESCE_RX_CQE_FRAMES], &mod); 584 ethnl_update_u32(&kernel_coalesce.rx_cqe_nsecs, 585 tb[ETHTOOL_A_COALESCE_RX_CQE_NSECS], &mod); 586 587 if (dev->irq_moder && dev->irq_moder->profile_flags & DIM_PROFILE_RX) { 588 ret = ethnl_update_profile(dev, &dev->irq_moder->rx_profile, 589 tb[ETHTOOL_A_COALESCE_RX_PROFILE], 590 &mod, info->extack); 591 if (ret < 0) 592 return ret; 593 } 594 595 if (dev->irq_moder && dev->irq_moder->profile_flags & DIM_PROFILE_TX) { 596 ret = ethnl_update_profile(dev, &dev->irq_moder->tx_profile, 597 tb[ETHTOOL_A_COALESCE_TX_PROFILE], 598 &mod, info->extack); 599 if (ret < 0) 600 return ret; 601 } 602 603 /* Update operation modes */ 604 ethnl_update_bool32(&coalesce.use_adaptive_rx_coalesce, 605 tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX], &mod_mode); 606 ethnl_update_bool32(&coalesce.use_adaptive_tx_coalesce, 607 tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX], &mod_mode); 608 ethnl_update_u8(&kernel_coalesce.use_cqe_mode_tx, 609 tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_TX], &mod_mode); 610 ethnl_update_u8(&kernel_coalesce.use_cqe_mode_rx, 611 tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_RX], &mod_mode); 612 613 *dual_change = mod && mod_mode; 614 if (!mod && !mod_mode) 615 return 0; 616 617 ret = dev->ethtool_ops->set_coalesce(dev, &coalesce, &kernel_coalesce, 618 info->extack); 619 return ret < 0 ? ret : 1; 620 } 621 622 static int 623 ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info) 624 { 625 bool dual_change; 626 int err, ret; 627 628 /* SET_COALESCE may change operation mode and parameters in one call. 629 * Changing operation mode may cause the driver to reset the parameter 630 * values, and therefore ignore user input (driver does not know which 631 * parameters come from user and which are echoed back from ->get). 632 * To not complicate the drivers if user tries to change both the mode 633 * and parameters at once - call the driver twice. 634 */ 635 err = __ethnl_set_coalesce(req_info, info, &dual_change); 636 if (err < 0) 637 return err; 638 ret = err; 639 640 if (ret && dual_change) { 641 err = __ethnl_set_coalesce(req_info, info, &dual_change); 642 if (err < 0) 643 return err; 644 } 645 return ret; 646 } 647 648 const struct ethnl_request_ops ethnl_coalesce_request_ops = { 649 .request_cmd = ETHTOOL_MSG_COALESCE_GET, 650 .reply_cmd = ETHTOOL_MSG_COALESCE_GET_REPLY, 651 .hdr_attr = ETHTOOL_A_COALESCE_HEADER, 652 .req_info_size = sizeof(struct coalesce_req_info), 653 .reply_data_size = sizeof(struct coalesce_reply_data), 654 655 .prepare_data = coalesce_prepare_data, 656 .reply_size = coalesce_reply_size, 657 .fill_reply = coalesce_fill_reply, 658 659 .set_validate = ethnl_set_coalesce_validate, 660 .set = ethnl_set_coalesce, 661 .set_ntf_cmd = ETHTOOL_MSG_COALESCE_NTF, 662 }; 663