1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright 2022-2025 NXP 4 * Copyright 2024 Furong Xu <0x1207@gmail.com> 5 */ 6 #include "common.h" 7 #include "netlink.h" 8 9 struct mm_req_info { 10 struct ethnl_req_info base; 11 }; 12 13 struct mm_reply_data { 14 struct ethnl_reply_data base; 15 struct ethtool_mm_state state; 16 struct ethtool_mm_stats stats; 17 }; 18 19 #define MM_REPDATA(__reply_base) \ 20 container_of(__reply_base, struct mm_reply_data, base) 21 22 #define ETHTOOL_MM_STAT_CNT \ 23 (__ETHTOOL_A_MM_STAT_CNT - (ETHTOOL_A_MM_STAT_PAD + 1)) 24 25 const struct nla_policy ethnl_mm_get_policy[ETHTOOL_A_MM_HEADER + 1] = { 26 [ETHTOOL_A_MM_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy_stats), 27 }; 28 29 static int mm_prepare_data(const struct ethnl_req_info *req_base, 30 struct ethnl_reply_data *reply_base, 31 const struct genl_info *info) 32 { 33 struct mm_reply_data *data = MM_REPDATA(reply_base); 34 struct net_device *dev = reply_base->dev; 35 const struct ethtool_ops *ops; 36 int ret; 37 38 ops = dev->ethtool_ops; 39 40 if (!ops->get_mm) 41 return -EOPNOTSUPP; 42 43 ethtool_stats_init((u64 *)&data->stats, 44 sizeof(data->stats) / sizeof(u64)); 45 46 ret = ethnl_ops_begin(dev); 47 if (ret < 0) 48 return ret; 49 50 ret = ops->get_mm(dev, &data->state); 51 if (ret) 52 goto out_complete; 53 54 if (ops->get_mm_stats && (req_base->flags & ETHTOOL_FLAG_STATS)) 55 ops->get_mm_stats(dev, &data->stats); 56 57 out_complete: 58 ethnl_ops_complete(dev); 59 60 return ret; 61 } 62 63 static int mm_reply_size(const struct ethnl_req_info *req_base, 64 const struct ethnl_reply_data *reply_base) 65 { 66 int len = 0; 67 68 len += nla_total_size(sizeof(u8)); /* _MM_PMAC_ENABLED */ 69 len += nla_total_size(sizeof(u8)); /* _MM_TX_ENABLED */ 70 len += nla_total_size(sizeof(u8)); /* _MM_TX_ACTIVE */ 71 len += nla_total_size(sizeof(u8)); /* _MM_VERIFY_ENABLED */ 72 len += nla_total_size(sizeof(u8)); /* _MM_VERIFY_STATUS */ 73 len += nla_total_size(sizeof(u32)); /* _MM_VERIFY_TIME */ 74 len += nla_total_size(sizeof(u32)); /* _MM_MAX_VERIFY_TIME */ 75 len += nla_total_size(sizeof(u32)); /* _MM_TX_MIN_FRAG_SIZE */ 76 len += nla_total_size(sizeof(u32)); /* _MM_RX_MIN_FRAG_SIZE */ 77 78 if (req_base->flags & ETHTOOL_FLAG_STATS) 79 len += nla_total_size(0) + /* _MM_STATS */ 80 nla_total_size_64bit(sizeof(u64)) * ETHTOOL_MM_STAT_CNT; 81 82 return len; 83 } 84 85 static int mm_put_stat(struct sk_buff *skb, u64 val, u16 attrtype) 86 { 87 if (val == ETHTOOL_STAT_NOT_SET) 88 return 0; 89 if (nla_put_u64_64bit(skb, attrtype, val, ETHTOOL_A_MM_STAT_PAD)) 90 return -EMSGSIZE; 91 return 0; 92 } 93 94 static int mm_put_stats(struct sk_buff *skb, 95 const struct ethtool_mm_stats *stats) 96 { 97 struct nlattr *nest; 98 99 nest = nla_nest_start(skb, ETHTOOL_A_MM_STATS); 100 if (!nest) 101 return -EMSGSIZE; 102 103 if (mm_put_stat(skb, stats->MACMergeFrameAssErrorCount, 104 ETHTOOL_A_MM_STAT_REASSEMBLY_ERRORS) || 105 mm_put_stat(skb, stats->MACMergeFrameSmdErrorCount, 106 ETHTOOL_A_MM_STAT_SMD_ERRORS) || 107 mm_put_stat(skb, stats->MACMergeFrameAssOkCount, 108 ETHTOOL_A_MM_STAT_REASSEMBLY_OK) || 109 mm_put_stat(skb, stats->MACMergeFragCountRx, 110 ETHTOOL_A_MM_STAT_RX_FRAG_COUNT) || 111 mm_put_stat(skb, stats->MACMergeFragCountTx, 112 ETHTOOL_A_MM_STAT_TX_FRAG_COUNT) || 113 mm_put_stat(skb, stats->MACMergeHoldCount, 114 ETHTOOL_A_MM_STAT_HOLD_COUNT)) 115 goto err_cancel; 116 117 nla_nest_end(skb, nest); 118 return 0; 119 120 err_cancel: 121 nla_nest_cancel(skb, nest); 122 return -EMSGSIZE; 123 } 124 125 static int mm_fill_reply(struct sk_buff *skb, 126 const struct ethnl_req_info *req_base, 127 const struct ethnl_reply_data *reply_base) 128 { 129 const struct mm_reply_data *data = MM_REPDATA(reply_base); 130 const struct ethtool_mm_state *state = &data->state; 131 132 if (nla_put_u8(skb, ETHTOOL_A_MM_TX_ENABLED, state->tx_enabled) || 133 nla_put_u8(skb, ETHTOOL_A_MM_TX_ACTIVE, state->tx_active) || 134 nla_put_u8(skb, ETHTOOL_A_MM_PMAC_ENABLED, state->pmac_enabled) || 135 nla_put_u8(skb, ETHTOOL_A_MM_VERIFY_ENABLED, state->verify_enabled) || 136 nla_put_u8(skb, ETHTOOL_A_MM_VERIFY_STATUS, state->verify_status) || 137 nla_put_u32(skb, ETHTOOL_A_MM_VERIFY_TIME, state->verify_time) || 138 nla_put_u32(skb, ETHTOOL_A_MM_MAX_VERIFY_TIME, state->max_verify_time) || 139 nla_put_u32(skb, ETHTOOL_A_MM_TX_MIN_FRAG_SIZE, state->tx_min_frag_size) || 140 nla_put_u32(skb, ETHTOOL_A_MM_RX_MIN_FRAG_SIZE, state->rx_min_frag_size)) 141 return -EMSGSIZE; 142 143 if (req_base->flags & ETHTOOL_FLAG_STATS && 144 mm_put_stats(skb, &data->stats)) 145 return -EMSGSIZE; 146 147 return 0; 148 } 149 150 const struct nla_policy ethnl_mm_set_policy[ETHTOOL_A_MM_MAX + 1] = { 151 [ETHTOOL_A_MM_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 152 [ETHTOOL_A_MM_VERIFY_ENABLED] = NLA_POLICY_MAX(NLA_U8, 1), 153 [ETHTOOL_A_MM_VERIFY_TIME] = NLA_POLICY_RANGE(NLA_U32, 1, 128), 154 [ETHTOOL_A_MM_TX_ENABLED] = NLA_POLICY_MAX(NLA_U8, 1), 155 [ETHTOOL_A_MM_PMAC_ENABLED] = NLA_POLICY_MAX(NLA_U8, 1), 156 [ETHTOOL_A_MM_TX_MIN_FRAG_SIZE] = NLA_POLICY_RANGE(NLA_U32, 60, 252), 157 }; 158 159 static void mm_state_to_cfg(const struct ethtool_mm_state *state, 160 struct ethtool_mm_cfg *cfg) 161 { 162 /* We could also compare state->verify_status against 163 * ETHTOOL_MM_VERIFY_STATUS_DISABLED, but state->verify_enabled 164 * is more like an administrative state which should be seen in 165 * ETHTOOL_MSG_MM_GET replies. For example, a port with verification 166 * disabled might be in the ETHTOOL_MM_VERIFY_STATUS_INITIAL 167 * if it's down. 168 */ 169 cfg->verify_enabled = state->verify_enabled; 170 cfg->verify_time = state->verify_time; 171 cfg->tx_enabled = state->tx_enabled; 172 cfg->pmac_enabled = state->pmac_enabled; 173 cfg->tx_min_frag_size = state->tx_min_frag_size; 174 } 175 176 static int 177 ethnl_set_mm_validate(struct ethnl_req_info *req_info, struct genl_info *info) 178 { 179 const struct ethtool_ops *ops = req_info->dev->ethtool_ops; 180 181 return ops->get_mm && ops->set_mm ? 1 : -EOPNOTSUPP; 182 } 183 184 static int ethnl_set_mm(struct ethnl_req_info *req_info, struct genl_info *info) 185 { 186 struct netlink_ext_ack *extack = info->extack; 187 struct net_device *dev = req_info->dev; 188 struct ethtool_mm_state state = {}; 189 struct nlattr **tb = info->attrs; 190 struct ethtool_mm_cfg cfg = {}; 191 bool mod = false; 192 int ret; 193 194 ret = dev->ethtool_ops->get_mm(dev, &state); 195 if (ret) 196 return ret; 197 198 mm_state_to_cfg(&state, &cfg); 199 200 ethnl_update_bool(&cfg.verify_enabled, tb[ETHTOOL_A_MM_VERIFY_ENABLED], 201 &mod); 202 ethnl_update_u32(&cfg.verify_time, tb[ETHTOOL_A_MM_VERIFY_TIME], &mod); 203 ethnl_update_bool(&cfg.tx_enabled, tb[ETHTOOL_A_MM_TX_ENABLED], &mod); 204 ethnl_update_bool(&cfg.pmac_enabled, tb[ETHTOOL_A_MM_PMAC_ENABLED], 205 &mod); 206 ethnl_update_u32(&cfg.tx_min_frag_size, 207 tb[ETHTOOL_A_MM_TX_MIN_FRAG_SIZE], &mod); 208 209 if (!mod) 210 return 0; 211 212 if (cfg.verify_time > state.max_verify_time) { 213 NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_MM_VERIFY_TIME], 214 "verifyTime exceeds device maximum"); 215 return -ERANGE; 216 } 217 218 if (cfg.verify_enabled && !cfg.tx_enabled) { 219 NL_SET_ERR_MSG(extack, "Verification requires TX enabled"); 220 return -EINVAL; 221 } 222 223 if (cfg.tx_enabled && !cfg.pmac_enabled) { 224 NL_SET_ERR_MSG(extack, "TX enabled requires pMAC enabled"); 225 return -EINVAL; 226 } 227 228 ret = dev->ethtool_ops->set_mm(dev, &cfg, extack); 229 return ret < 0 ? ret : 1; 230 } 231 232 const struct ethnl_request_ops ethnl_mm_request_ops = { 233 .request_cmd = ETHTOOL_MSG_MM_GET, 234 .reply_cmd = ETHTOOL_MSG_MM_GET_REPLY, 235 .hdr_attr = ETHTOOL_A_MM_HEADER, 236 .req_info_size = sizeof(struct mm_req_info), 237 .reply_data_size = sizeof(struct mm_reply_data), 238 239 .prepare_data = mm_prepare_data, 240 .reply_size = mm_reply_size, 241 .fill_reply = mm_fill_reply, 242 243 .set_validate = ethnl_set_mm_validate, 244 .set = ethnl_set_mm, 245 .set_ntf_cmd = ETHTOOL_MSG_MM_NTF, 246 }; 247 248 /* Returns whether a given device supports the MAC merge layer 249 * (has an eMAC and a pMAC). Must be called under rtnl_lock() and 250 * ethnl_ops_begin(). 251 */ 252 bool __ethtool_dev_mm_supported(struct net_device *dev) 253 { 254 const struct ethtool_ops *ops = dev->ethtool_ops; 255 struct ethtool_mm_state state = {}; 256 int ret = -EOPNOTSUPP; 257 258 if (ops && ops->get_mm) 259 ret = ops->get_mm(dev, &state); 260 261 return !ret; 262 } 263 264 bool ethtool_dev_mm_supported(struct net_device *dev) 265 { 266 const struct ethtool_ops *ops = dev->ethtool_ops; 267 bool supported; 268 int ret; 269 270 ASSERT_RTNL(); 271 272 if (!ops) 273 return false; 274 275 ret = ethnl_ops_begin(dev); 276 if (ret < 0) 277 return false; 278 279 supported = __ethtool_dev_mm_supported(dev); 280 281 ethnl_ops_complete(dev); 282 283 return supported; 284 } 285 EXPORT_SYMBOL_GPL(ethtool_dev_mm_supported); 286 287 static void ethtool_mmsv_configure_tx(struct ethtool_mmsv *mmsv, 288 bool tx_active) 289 { 290 if (mmsv->ops->configure_tx) 291 mmsv->ops->configure_tx(mmsv, tx_active); 292 } 293 294 static void ethtool_mmsv_configure_pmac(struct ethtool_mmsv *mmsv, 295 bool pmac_enabled) 296 { 297 if (mmsv->ops->configure_pmac) 298 mmsv->ops->configure_pmac(mmsv, pmac_enabled); 299 } 300 301 static void ethtool_mmsv_send_mpacket(struct ethtool_mmsv *mmsv, 302 enum ethtool_mpacket mpacket) 303 { 304 if (mmsv->ops->send_mpacket) 305 mmsv->ops->send_mpacket(mmsv, mpacket); 306 } 307 308 /** 309 * ethtool_mmsv_verify_timer - Timer for MAC Merge verification 310 * @t: timer_list struct containing private info 311 * 312 * Verify the MAC Merge capability in the local TX direction, by 313 * transmitting Verify mPackets up to 3 times. Wait until link 314 * partner responds with a Response mPacket, otherwise fail. 315 */ 316 static void ethtool_mmsv_verify_timer(struct timer_list *t) 317 { 318 struct ethtool_mmsv *mmsv = timer_container_of(mmsv, t, verify_timer); 319 unsigned long flags; 320 bool rearm = false; 321 322 spin_lock_irqsave(&mmsv->lock, flags); 323 324 switch (mmsv->status) { 325 case ETHTOOL_MM_VERIFY_STATUS_INITIAL: 326 case ETHTOOL_MM_VERIFY_STATUS_VERIFYING: 327 if (mmsv->verify_retries != 0) { 328 ethtool_mmsv_send_mpacket(mmsv, ETHTOOL_MPACKET_VERIFY); 329 rearm = true; 330 } else { 331 mmsv->status = ETHTOOL_MM_VERIFY_STATUS_FAILED; 332 } 333 334 mmsv->verify_retries--; 335 break; 336 337 case ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED: 338 ethtool_mmsv_configure_tx(mmsv, true); 339 break; 340 341 default: 342 break; 343 } 344 345 if (rearm) { 346 mod_timer(&mmsv->verify_timer, 347 jiffies + msecs_to_jiffies(mmsv->verify_time)); 348 } 349 350 spin_unlock_irqrestore(&mmsv->lock, flags); 351 } 352 353 static void ethtool_mmsv_verify_timer_arm(struct ethtool_mmsv *mmsv) 354 { 355 if (mmsv->pmac_enabled && mmsv->tx_enabled && mmsv->verify_enabled && 356 mmsv->status != ETHTOOL_MM_VERIFY_STATUS_FAILED && 357 mmsv->status != ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED) { 358 timer_setup(&mmsv->verify_timer, ethtool_mmsv_verify_timer, 0); 359 mod_timer(&mmsv->verify_timer, jiffies); 360 } 361 } 362 363 static void ethtool_mmsv_apply(struct ethtool_mmsv *mmsv) 364 { 365 /* If verification is disabled, configure FPE right away. 366 * Otherwise let the timer code do it. 367 */ 368 if (!mmsv->verify_enabled) { 369 ethtool_mmsv_configure_pmac(mmsv, mmsv->pmac_enabled); 370 ethtool_mmsv_configure_tx(mmsv, mmsv->tx_enabled); 371 } else { 372 mmsv->status = ETHTOOL_MM_VERIFY_STATUS_INITIAL; 373 mmsv->verify_retries = ETHTOOL_MM_MAX_VERIFY_RETRIES; 374 375 if (netif_running(mmsv->dev)) 376 ethtool_mmsv_verify_timer_arm(mmsv); 377 } 378 } 379 380 /** 381 * ethtool_mmsv_stop() - Stop MAC Merge Software Verification 382 * @mmsv: MAC Merge Software Verification state 383 * 384 * Drivers should call this method in a state where the hardware is 385 * about to lose state, like ndo_stop() or suspend(), and turning off 386 * MAC Merge features would be superfluous. Otherwise, prefer 387 * ethtool_mmsv_link_state_handle() with up=false. 388 */ 389 void ethtool_mmsv_stop(struct ethtool_mmsv *mmsv) 390 { 391 timer_shutdown_sync(&mmsv->verify_timer); 392 } 393 EXPORT_SYMBOL_GPL(ethtool_mmsv_stop); 394 395 /** 396 * ethtool_mmsv_link_state_handle() - Inform MAC Merge Software Verification 397 * of link state changes 398 * @mmsv: MAC Merge Software Verification state 399 * @up: True if device carrier is up and able to pass verification packets 400 * 401 * Calling context is expected to be from a task, interrupts enabled. 402 */ 403 void ethtool_mmsv_link_state_handle(struct ethtool_mmsv *mmsv, bool up) 404 { 405 unsigned long flags; 406 407 ethtool_mmsv_stop(mmsv); 408 409 spin_lock_irqsave(&mmsv->lock, flags); 410 411 if (up && mmsv->pmac_enabled) { 412 /* VERIFY process requires pMAC enabled when NIC comes up */ 413 ethtool_mmsv_configure_pmac(mmsv, true); 414 415 /* New link => maybe new partner => new verification process */ 416 ethtool_mmsv_apply(mmsv); 417 } else { 418 /* Reset the reported verification state while the link is down */ 419 if (mmsv->verify_enabled) 420 mmsv->status = ETHTOOL_MM_VERIFY_STATUS_INITIAL; 421 422 /* No link or pMAC not enabled */ 423 ethtool_mmsv_configure_pmac(mmsv, false); 424 ethtool_mmsv_configure_tx(mmsv, false); 425 } 426 427 spin_unlock_irqrestore(&mmsv->lock, flags); 428 } 429 EXPORT_SYMBOL_GPL(ethtool_mmsv_link_state_handle); 430 431 /** 432 * ethtool_mmsv_event_handle() - Inform MAC Merge Software Verification 433 * of interrupt-based events 434 * @mmsv: MAC Merge Software Verification state 435 * @event: Event which took place (packet transmission or reception) 436 * 437 * Calling context expects to have interrupts disabled. 438 */ 439 void ethtool_mmsv_event_handle(struct ethtool_mmsv *mmsv, 440 enum ethtool_mmsv_event event) 441 { 442 /* This is interrupt context, just spin_lock() */ 443 spin_lock(&mmsv->lock); 444 445 if (!mmsv->pmac_enabled) 446 goto unlock; 447 448 switch (event) { 449 case ETHTOOL_MMSV_LP_SENT_VERIFY_MPACKET: 450 /* Link partner has sent verify mPacket */ 451 ethtool_mmsv_send_mpacket(mmsv, ETHTOOL_MPACKET_RESPONSE); 452 break; 453 case ETHTOOL_MMSV_LD_SENT_VERIFY_MPACKET: 454 /* Local device has sent verify mPacket */ 455 if (mmsv->status != ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED) 456 mmsv->status = ETHTOOL_MM_VERIFY_STATUS_VERIFYING; 457 break; 458 case ETHTOOL_MMSV_LP_SENT_RESPONSE_MPACKET: 459 /* Link partner has sent response mPacket */ 460 if (mmsv->status == ETHTOOL_MM_VERIFY_STATUS_VERIFYING) 461 mmsv->status = ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED; 462 break; 463 } 464 465 unlock: 466 spin_unlock(&mmsv->lock); 467 } 468 EXPORT_SYMBOL_GPL(ethtool_mmsv_event_handle); 469 470 static bool ethtool_mmsv_is_tx_active(struct ethtool_mmsv *mmsv) 471 { 472 /* TX is active if administratively enabled, and verification either 473 * succeeded, or was administratively disabled. 474 */ 475 return mmsv->tx_enabled && 476 (mmsv->status == ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED || 477 mmsv->status == ETHTOOL_MM_VERIFY_STATUS_DISABLED); 478 } 479 480 /** 481 * ethtool_mmsv_get_mm() - get_mm() hook for MAC Merge Software Verification 482 * @mmsv: MAC Merge Software Verification state 483 * @state: see struct ethtool_mm_state 484 * 485 * Drivers are expected to call this from their ethtool_ops :: get_mm() 486 * method. 487 */ 488 void ethtool_mmsv_get_mm(struct ethtool_mmsv *mmsv, 489 struct ethtool_mm_state *state) 490 { 491 unsigned long flags; 492 493 spin_lock_irqsave(&mmsv->lock, flags); 494 495 state->max_verify_time = ETHTOOL_MM_MAX_VERIFY_TIME_MS; 496 state->verify_enabled = mmsv->verify_enabled; 497 state->pmac_enabled = mmsv->pmac_enabled; 498 state->verify_time = mmsv->verify_time; 499 state->tx_enabled = mmsv->tx_enabled; 500 state->verify_status = mmsv->status; 501 state->tx_active = ethtool_mmsv_is_tx_active(mmsv); 502 503 spin_unlock_irqrestore(&mmsv->lock, flags); 504 } 505 EXPORT_SYMBOL_GPL(ethtool_mmsv_get_mm); 506 507 /** 508 * ethtool_mmsv_set_mm() - set_mm() hook for MAC Merge Software Verification 509 * @mmsv: MAC Merge Software Verification state 510 * @cfg: see struct ethtool_mm_cfg 511 * 512 * Drivers are expected to call this from their ethtool_ops :: set_mm() 513 * method. 514 */ 515 void ethtool_mmsv_set_mm(struct ethtool_mmsv *mmsv, struct ethtool_mm_cfg *cfg) 516 { 517 unsigned long flags; 518 519 /* Wait for the verification that's currently in progress to finish */ 520 ethtool_mmsv_stop(mmsv); 521 522 spin_lock_irqsave(&mmsv->lock, flags); 523 524 mmsv->verify_enabled = cfg->verify_enabled; 525 mmsv->pmac_enabled = cfg->pmac_enabled; 526 mmsv->verify_time = cfg->verify_time; 527 mmsv->tx_enabled = cfg->tx_enabled; 528 529 if (!cfg->verify_enabled) 530 mmsv->status = ETHTOOL_MM_VERIFY_STATUS_DISABLED; 531 532 ethtool_mmsv_apply(mmsv); 533 534 spin_unlock_irqrestore(&mmsv->lock, flags); 535 } 536 EXPORT_SYMBOL_GPL(ethtool_mmsv_set_mm); 537 538 /** 539 * ethtool_mmsv_init() - Initialize MAC Merge Software Verification state 540 * @mmsv: MAC Merge Software Verification state 541 * @dev: Pointer to network interface 542 * @ops: Methods for implementing the generic functionality 543 * 544 * The MAC Merge Software Verification is a timer- and event-based state 545 * machine intended for network interfaces which lack a hardware-based 546 * TX verification process (as per IEEE 802.3 clause 99.4.3). The timer 547 * is managed by the core code, whereas events are supplied by the 548 * driver explicitly calling one of the other API functions. 549 */ 550 void ethtool_mmsv_init(struct ethtool_mmsv *mmsv, struct net_device *dev, 551 const struct ethtool_mmsv_ops *ops) 552 { 553 mmsv->ops = ops; 554 mmsv->dev = dev; 555 mmsv->verify_retries = ETHTOOL_MM_MAX_VERIFY_RETRIES; 556 mmsv->verify_time = ETHTOOL_MM_MAX_VERIFY_TIME_MS; 557 mmsv->status = ETHTOOL_MM_VERIFY_STATUS_DISABLED; 558 timer_setup(&mmsv->verify_timer, ethtool_mmsv_verify_timer, 0); 559 spin_lock_init(&mmsv->lock); 560 } 561 EXPORT_SYMBOL_GPL(ethtool_mmsv_init); 562