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