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 const struct ethtool_coalesce *coal = &data->coalesce; 215 u32 supported = data->supported_params; 216 struct dim_irq_moder *moder; 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 (!req_base->dev || !req_base->dev->irq_moder) 276 return 0; 277 278 moder = req_base->dev->irq_moder; 279 rcu_read_lock(); 280 if (moder->profile_flags & DIM_PROFILE_RX) { 281 ret = coalesce_put_profile(skb, ETHTOOL_A_COALESCE_RX_PROFILE, 282 rcu_dereference(moder->rx_profile), 283 moder->coal_flags); 284 if (ret) 285 goto out; 286 } 287 288 if (moder->profile_flags & DIM_PROFILE_TX) 289 ret = coalesce_put_profile(skb, ETHTOOL_A_COALESCE_TX_PROFILE, 290 rcu_dereference(moder->tx_profile), 291 moder->coal_flags); 292 293 out: 294 rcu_read_unlock(); 295 return ret; 296 } 297 298 /* COALESCE_SET */ 299 300 static const struct nla_policy coalesce_irq_moderation_policy[] = { 301 [ETHTOOL_A_IRQ_MODERATION_USEC] = { .type = NLA_U32 }, 302 [ETHTOOL_A_IRQ_MODERATION_PKTS] = { .type = NLA_U32 }, 303 [ETHTOOL_A_IRQ_MODERATION_COMPS] = { .type = NLA_U32 }, 304 }; 305 306 static const struct nla_policy coalesce_profile_policy[] = { 307 [ETHTOOL_A_PROFILE_IRQ_MODERATION] = 308 NLA_POLICY_NESTED(coalesce_irq_moderation_policy), 309 }; 310 311 const struct nla_policy ethnl_coalesce_set_policy[] = { 312 [ETHTOOL_A_COALESCE_HEADER] = 313 NLA_POLICY_NESTED(ethnl_header_policy), 314 [ETHTOOL_A_COALESCE_RX_USECS] = { .type = NLA_U32 }, 315 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES] = { .type = NLA_U32 }, 316 [ETHTOOL_A_COALESCE_RX_USECS_IRQ] = { .type = NLA_U32 }, 317 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ] = { .type = NLA_U32 }, 318 [ETHTOOL_A_COALESCE_TX_USECS] = { .type = NLA_U32 }, 319 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES] = { .type = NLA_U32 }, 320 [ETHTOOL_A_COALESCE_TX_USECS_IRQ] = { .type = NLA_U32 }, 321 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ] = { .type = NLA_U32 }, 322 [ETHTOOL_A_COALESCE_STATS_BLOCK_USECS] = { .type = NLA_U32 }, 323 [ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX] = { .type = NLA_U8 }, 324 [ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX] = { .type = NLA_U8 }, 325 [ETHTOOL_A_COALESCE_PKT_RATE_LOW] = { .type = NLA_U32 }, 326 [ETHTOOL_A_COALESCE_RX_USECS_LOW] = { .type = NLA_U32 }, 327 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW] = { .type = NLA_U32 }, 328 [ETHTOOL_A_COALESCE_TX_USECS_LOW] = { .type = NLA_U32 }, 329 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW] = { .type = NLA_U32 }, 330 [ETHTOOL_A_COALESCE_PKT_RATE_HIGH] = { .type = NLA_U32 }, 331 [ETHTOOL_A_COALESCE_RX_USECS_HIGH] = { .type = NLA_U32 }, 332 [ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH] = { .type = NLA_U32 }, 333 [ETHTOOL_A_COALESCE_TX_USECS_HIGH] = { .type = NLA_U32 }, 334 [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH] = { .type = NLA_U32 }, 335 [ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL] = { .type = NLA_U32 }, 336 [ETHTOOL_A_COALESCE_USE_CQE_MODE_TX] = NLA_POLICY_MAX(NLA_U8, 1), 337 [ETHTOOL_A_COALESCE_USE_CQE_MODE_RX] = NLA_POLICY_MAX(NLA_U8, 1), 338 [ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES] = { .type = NLA_U32 }, 339 [ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES] = { .type = NLA_U32 }, 340 [ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS] = { .type = NLA_U32 }, 341 [ETHTOOL_A_COALESCE_RX_PROFILE] = 342 NLA_POLICY_NESTED(coalesce_profile_policy), 343 [ETHTOOL_A_COALESCE_TX_PROFILE] = 344 NLA_POLICY_NESTED(coalesce_profile_policy), 345 }; 346 347 static int 348 ethnl_set_coalesce_validate(struct ethnl_req_info *req_info, 349 struct genl_info *info) 350 { 351 const struct ethtool_ops *ops = req_info->dev->ethtool_ops; 352 struct dim_irq_moder *irq_moder = req_info->dev->irq_moder; 353 struct nlattr **tb = info->attrs; 354 u32 supported_params; 355 u16 a; 356 357 if (!ops->get_coalesce || !ops->set_coalesce) 358 return -EOPNOTSUPP; 359 360 /* make sure that only supported parameters are present */ 361 supported_params = ops->supported_coalesce_params; 362 if (irq_moder && irq_moder->profile_flags & DIM_PROFILE_RX) 363 supported_params |= ETHTOOL_COALESCE_RX_PROFILE; 364 365 if (irq_moder && irq_moder->profile_flags & DIM_PROFILE_TX) 366 supported_params |= ETHTOOL_COALESCE_TX_PROFILE; 367 368 for (a = ETHTOOL_A_COALESCE_RX_USECS; a < __ETHTOOL_A_COALESCE_CNT; a++) 369 if (tb[a] && !(supported_params & attr_to_mask(a))) { 370 NL_SET_ERR_MSG_ATTR(info->extack, tb[a], 371 "cannot modify an unsupported parameter"); 372 return -EINVAL; 373 } 374 375 return 1; 376 } 377 378 /** 379 * ethnl_update_irq_moder - update a specific field in the given profile 380 * @irq_moder: place that collects dim related information 381 * @irq_field: field in profile to modify 382 * @attr_type: attr type ETHTOOL_A_IRQ_MODERATION_* 383 * @tb: netlink attribute with new values or null 384 * @coal_bit: DIM_COALESCE_* bit from coal_flags 385 * @mod: pointer to bool for modification tracking 386 * @extack: netlink extended ack 387 * 388 * Return: 0 on success or a negative error code. 389 */ 390 static int ethnl_update_irq_moder(struct dim_irq_moder *irq_moder, 391 u16 *irq_field, u16 attr_type, 392 struct nlattr **tb, 393 u8 coal_bit, bool *mod, 394 struct netlink_ext_ack *extack) 395 { 396 int ret = 0; 397 u32 val; 398 399 if (!tb[attr_type]) 400 return 0; 401 402 if (irq_moder->coal_flags & coal_bit) { 403 val = nla_get_u32(tb[attr_type]); 404 if (*irq_field == val) 405 return 0; 406 407 *irq_field = val; 408 *mod = true; 409 } else { 410 NL_SET_BAD_ATTR(extack, tb[attr_type]); 411 ret = -EOPNOTSUPP; 412 } 413 414 return ret; 415 } 416 417 /** 418 * ethnl_update_profile - get a profile nest with child nests from userspace. 419 * @dev: netdevice to update the profile 420 * @dst: profile get from the driver and modified by ethnl_update_profile. 421 * @nests: nest attr ETHTOOL_A_COALESCE_*X_PROFILE to set profile. 422 * @mod: pointer to bool for modification tracking 423 * @extack: Netlink extended ack 424 * 425 * Layout of nests: 426 * Nested ETHTOOL_A_COALESCE_*X_PROFILE attr 427 * Nested ETHTOOL_A_PROFILE_IRQ_MODERATION attr 428 * ETHTOOL_A_IRQ_MODERATION_USEC attr 429 * ETHTOOL_A_IRQ_MODERATION_PKTS attr 430 * ETHTOOL_A_IRQ_MODERATION_COMPS attr 431 * ... 432 * Nested ETHTOOL_A_PROFILE_IRQ_MODERATION attr 433 * ETHTOOL_A_IRQ_MODERATION_USEC attr 434 * ETHTOOL_A_IRQ_MODERATION_PKTS attr 435 * ETHTOOL_A_IRQ_MODERATION_COMPS attr 436 * 437 * Return: 0 on success or a negative error code. 438 */ 439 static int ethnl_update_profile(struct net_device *dev, 440 struct dim_cq_moder __rcu **dst, 441 const struct nlattr *nests, 442 bool *mod, 443 struct netlink_ext_ack *extack) 444 { 445 int len_irq_moder = ARRAY_SIZE(coalesce_irq_moderation_policy); 446 struct nlattr *tb[ARRAY_SIZE(coalesce_irq_moderation_policy)]; 447 struct dim_irq_moder *irq_moder = dev->irq_moder; 448 struct dim_cq_moder *new_profile, *old_profile; 449 int ret, rem, i = 0, len; 450 struct nlattr *nest; 451 452 if (!nests) 453 return 0; 454 455 if (!*dst) 456 return -EOPNOTSUPP; 457 458 old_profile = rtnl_dereference(*dst); 459 len = NET_DIM_PARAMS_NUM_PROFILES * sizeof(*old_profile); 460 new_profile = kmemdup(old_profile, len, GFP_KERNEL); 461 if (!new_profile) 462 return -ENOMEM; 463 464 nla_for_each_nested_type(nest, ETHTOOL_A_PROFILE_IRQ_MODERATION, 465 nests, rem) { 466 ret = nla_parse_nested(tb, len_irq_moder - 1, nest, 467 coalesce_irq_moderation_policy, 468 extack); 469 if (ret) 470 goto err_out; 471 472 ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].usec, 473 ETHTOOL_A_IRQ_MODERATION_USEC, 474 tb, DIM_COALESCE_USEC, 475 mod, extack); 476 if (ret) 477 goto err_out; 478 479 ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].pkts, 480 ETHTOOL_A_IRQ_MODERATION_PKTS, 481 tb, DIM_COALESCE_PKTS, 482 mod, extack); 483 if (ret) 484 goto err_out; 485 486 ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].comps, 487 ETHTOOL_A_IRQ_MODERATION_COMPS, 488 tb, DIM_COALESCE_COMPS, 489 mod, extack); 490 if (ret) 491 goto err_out; 492 493 i++; 494 } 495 496 /* After the profile is modified, dim itself is a dynamic 497 * mechanism and will quickly fit to the appropriate 498 * coalescing parameters according to the new profile. 499 */ 500 rcu_assign_pointer(*dst, new_profile); 501 kfree_rcu(old_profile, rcu); 502 503 return 0; 504 505 err_out: 506 kfree(new_profile); 507 return ret; 508 } 509 510 static int 511 __ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info, 512 bool *dual_change) 513 { 514 struct kernel_ethtool_coalesce kernel_coalesce = {}; 515 struct net_device *dev = req_info->dev; 516 struct ethtool_coalesce coalesce = {}; 517 bool mod_mode = false, mod = false; 518 struct nlattr **tb = info->attrs; 519 int ret; 520 521 ret = dev->ethtool_ops->get_coalesce(dev, &coalesce, &kernel_coalesce, 522 info->extack); 523 if (ret < 0) 524 return ret; 525 526 /* Update values */ 527 ethnl_update_u32(&coalesce.rx_coalesce_usecs, 528 tb[ETHTOOL_A_COALESCE_RX_USECS], &mod); 529 ethnl_update_u32(&coalesce.rx_max_coalesced_frames, 530 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES], &mod); 531 ethnl_update_u32(&coalesce.rx_coalesce_usecs_irq, 532 tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ], &mod); 533 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_irq, 534 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ], &mod); 535 ethnl_update_u32(&coalesce.tx_coalesce_usecs, 536 tb[ETHTOOL_A_COALESCE_TX_USECS], &mod); 537 ethnl_update_u32(&coalesce.tx_max_coalesced_frames, 538 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES], &mod); 539 ethnl_update_u32(&coalesce.tx_coalesce_usecs_irq, 540 tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ], &mod); 541 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_irq, 542 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ], &mod); 543 ethnl_update_u32(&coalesce.stats_block_coalesce_usecs, 544 tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS], &mod); 545 ethnl_update_u32(&coalesce.pkt_rate_low, 546 tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW], &mod); 547 ethnl_update_u32(&coalesce.rx_coalesce_usecs_low, 548 tb[ETHTOOL_A_COALESCE_RX_USECS_LOW], &mod); 549 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_low, 550 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW], &mod); 551 ethnl_update_u32(&coalesce.tx_coalesce_usecs_low, 552 tb[ETHTOOL_A_COALESCE_TX_USECS_LOW], &mod); 553 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_low, 554 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW], &mod); 555 ethnl_update_u32(&coalesce.pkt_rate_high, 556 tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH], &mod); 557 ethnl_update_u32(&coalesce.rx_coalesce_usecs_high, 558 tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH], &mod); 559 ethnl_update_u32(&coalesce.rx_max_coalesced_frames_high, 560 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH], &mod); 561 ethnl_update_u32(&coalesce.tx_coalesce_usecs_high, 562 tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH], &mod); 563 ethnl_update_u32(&coalesce.tx_max_coalesced_frames_high, 564 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], &mod); 565 ethnl_update_u32(&coalesce.rate_sample_interval, 566 tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL], &mod); 567 ethnl_update_u32(&kernel_coalesce.tx_aggr_max_bytes, 568 tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES], &mod); 569 ethnl_update_u32(&kernel_coalesce.tx_aggr_max_frames, 570 tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES], &mod); 571 ethnl_update_u32(&kernel_coalesce.tx_aggr_time_usecs, 572 tb[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS], &mod); 573 574 if (dev->irq_moder && dev->irq_moder->profile_flags & DIM_PROFILE_RX) { 575 ret = ethnl_update_profile(dev, &dev->irq_moder->rx_profile, 576 tb[ETHTOOL_A_COALESCE_RX_PROFILE], 577 &mod, info->extack); 578 if (ret < 0) 579 return ret; 580 } 581 582 if (dev->irq_moder && dev->irq_moder->profile_flags & DIM_PROFILE_TX) { 583 ret = ethnl_update_profile(dev, &dev->irq_moder->tx_profile, 584 tb[ETHTOOL_A_COALESCE_TX_PROFILE], 585 &mod, info->extack); 586 if (ret < 0) 587 return ret; 588 } 589 590 /* Update operation modes */ 591 ethnl_update_bool32(&coalesce.use_adaptive_rx_coalesce, 592 tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX], &mod_mode); 593 ethnl_update_bool32(&coalesce.use_adaptive_tx_coalesce, 594 tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX], &mod_mode); 595 ethnl_update_u8(&kernel_coalesce.use_cqe_mode_tx, 596 tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_TX], &mod_mode); 597 ethnl_update_u8(&kernel_coalesce.use_cqe_mode_rx, 598 tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_RX], &mod_mode); 599 600 *dual_change = mod && mod_mode; 601 if (!mod && !mod_mode) 602 return 0; 603 604 ret = dev->ethtool_ops->set_coalesce(dev, &coalesce, &kernel_coalesce, 605 info->extack); 606 return ret < 0 ? ret : 1; 607 } 608 609 static int 610 ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info) 611 { 612 bool dual_change; 613 int err, ret; 614 615 /* SET_COALESCE may change operation mode and parameters in one call. 616 * Changing operation mode may cause the driver to reset the parameter 617 * values, and therefore ignore user input (driver does not know which 618 * parameters come from user and which are echoed back from ->get). 619 * To not complicate the drivers if user tries to change both the mode 620 * and parameters at once - call the driver twice. 621 */ 622 err = __ethnl_set_coalesce(req_info, info, &dual_change); 623 if (err < 0) 624 return err; 625 ret = err; 626 627 if (ret && dual_change) { 628 err = __ethnl_set_coalesce(req_info, info, &dual_change); 629 if (err < 0) 630 return err; 631 } 632 return ret; 633 } 634 635 const struct ethnl_request_ops ethnl_coalesce_request_ops = { 636 .request_cmd = ETHTOOL_MSG_COALESCE_GET, 637 .reply_cmd = ETHTOOL_MSG_COALESCE_GET_REPLY, 638 .hdr_attr = ETHTOOL_A_COALESCE_HEADER, 639 .req_info_size = sizeof(struct coalesce_req_info), 640 .reply_data_size = sizeof(struct coalesce_reply_data), 641 642 .prepare_data = coalesce_prepare_data, 643 .reply_size = coalesce_reply_size, 644 .fill_reply = coalesce_fill_reply, 645 646 .set_validate = ethnl_set_coalesce_validate, 647 .set = ethnl_set_coalesce, 648 .set_ntf_cmd = ETHTOOL_MSG_COALESCE_NTF, 649 }; 650