Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'mlxsw-add-support-of-latency-tlv'

Petr Machata says:

====================
mlxsw: Add support of latency TLV

Amit Cohen writes:

Ethernet Management Datagrams (EMADs) are Ethernet packets sent between
the driver and device's firmware. They are used to pass various
configurations to the device, but also to get events (e.g., port up)
from it. After the Ethernet header, these packets are built in a TLV
format.

This is the structure of EMADs:
* Ethernet header
* Operation TLV
* String TLV (optional)
* Latency TLV (optional)
* Reg TLV
* End TLV

The latency of each EMAD is measured by firmware. The driver can get the
measurement via latency TLV which can be added to each EMAD. This TLV is
optional, when EMAD is sent with this TLV, the EMAD's response will include
the TLV and will contain the firmware measurement.

Add support for Latency TLV and use it by default for all EMADs (see
more information in commit messages). The latency measurements can be
processed using BPF program for example, to create a histogram and average
of the latency per register. In addition, it is possible to measure the
end-to-end latency, so then the latency of the software overhead can be
calculated. This information can be useful to improve the driver
performance.

See an example of output of BPF tool which presents these measurements:

$ ./emadlatency -f -a
Tracing EMADs... Hit Ctrl-C to end.
Register write = RALUE (0x8013)
E2E Measurements:
average = 23 usecs, total = 32052693 usecs, count = 1337061
usecs : count distribution
0 -> 1 : 0 | |
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 0 | |
16 -> 31 : 1290814 |*********************************|
32 -> 63 : 45339 |* |
64 -> 127 : 532 | |
128 -> 255 : 247 | |
256 -> 511 : 57 | |
512 -> 1023 : 26 | |
1024 -> 2047 : 33 | |
2048 -> 4095 : 0 | |
4096 -> 8191 : 10 | |
8192 -> 16383 : 1 | |
16384 -> 32767 : 1 | |
32768 -> 65535 : 1 | |

Firmware Measurements:
average = 10 usecs, total = 13884128 usecs, count = 1337061
usecs : count distribution
0 -> 1 : 0 | |
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 1337035 |*********************************|
16 -> 31 : 17 | |
32 -> 63 : 7 | |
64 -> 127 : 0 | |
128 -> 255 : 2 | |

Diff between measurements: 13 usecs

Patch set overview:
Patches #1-#3 add support for querying MGIR, to know if string TLV and
latency TLV are supported
Patches #4-#5 add some relevant fields to support latency TLV
Patch #6 adds support of latency TLV
====================

Link: https://lore.kernel.org/r/cover.1674123673.git.petrm@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+103 -24
+87 -21
drivers/net/ethernet/mellanox/mlxsw/core.c
··· 78 78 spinlock_t trans_list_lock; /* protects trans_list writes */ 79 79 bool use_emad; 80 80 bool enable_string_tlv; 81 + bool enable_latency_tlv; 81 82 } emad; 82 83 struct { 83 84 u16 *mapping; /* lag_id+port_index to local_port mapping */ ··· 379 378 MLXSW_ITEM_BUF(emad, string_tlv, string, 0x04, 380 379 MLXSW_EMAD_STRING_TLV_STRING_LEN); 381 380 381 + /* emad_latency_tlv_type 382 + * Type of the TLV. 383 + * Must be set to 0x4 (latency TLV). 384 + */ 385 + MLXSW_ITEM32(emad, latency_tlv, type, 0x00, 27, 5); 386 + 387 + /* emad_latency_tlv_len 388 + * Length of the latency TLV in u32. 389 + */ 390 + MLXSW_ITEM32(emad, latency_tlv, len, 0x00, 16, 11); 391 + 392 + /* emad_latency_tlv_latency_time 393 + * EMAD latency time in units of uSec. 394 + */ 395 + MLXSW_ITEM32(emad, latency_tlv, latency_time, 0x04, 0, 32); 396 + 382 397 /* emad_reg_tlv_type 383 398 * Type of the TLV. 384 399 * Must be set to 0x3 (register TLV). ··· 478 461 mlxsw_emad_op_tlv_tid_set(op_tlv, tid); 479 462 } 480 463 464 + static void mlxsw_emad_pack_latency_tlv(char *latency_tlv) 465 + { 466 + mlxsw_emad_latency_tlv_type_set(latency_tlv, MLXSW_EMAD_TLV_TYPE_LATENCY); 467 + mlxsw_emad_latency_tlv_len_set(latency_tlv, MLXSW_EMAD_LATENCY_TLV_LEN); 468 + } 469 + 481 470 static int mlxsw_emad_construct_eth_hdr(struct sk_buff *skb) 482 471 { 483 472 char *eth_hdr = skb_push(skb, MLXSW_EMAD_ETH_HDR_LEN); ··· 499 476 return 0; 500 477 } 501 478 502 - static void mlxsw_emad_construct(struct sk_buff *skb, 479 + static void mlxsw_emad_construct(const struct mlxsw_core *mlxsw_core, 480 + struct sk_buff *skb, 503 481 const struct mlxsw_reg_info *reg, 504 482 char *payload, 505 - enum mlxsw_core_reg_access_type type, 506 - u64 tid, bool enable_string_tlv) 483 + enum mlxsw_core_reg_access_type type, u64 tid) 507 484 { 508 485 char *buf; 509 486 ··· 513 490 buf = skb_push(skb, reg->len + sizeof(u32)); 514 491 mlxsw_emad_pack_reg_tlv(buf, reg, payload); 515 492 516 - if (enable_string_tlv) { 493 + if (mlxsw_core->emad.enable_latency_tlv) { 494 + buf = skb_push(skb, MLXSW_EMAD_LATENCY_TLV_LEN * sizeof(u32)); 495 + mlxsw_emad_pack_latency_tlv(buf); 496 + } 497 + 498 + if (mlxsw_core->emad.enable_string_tlv) { 517 499 buf = skb_push(skb, MLXSW_EMAD_STRING_TLV_LEN * sizeof(u32)); 518 500 mlxsw_emad_pack_string_tlv(buf); 519 501 } ··· 532 504 struct mlxsw_emad_tlv_offsets { 533 505 u16 op_tlv; 534 506 u16 string_tlv; 507 + u16 latency_tlv; 535 508 u16 reg_tlv; 536 509 }; 537 510 ··· 543 514 return tlv_type == MLXSW_EMAD_TLV_TYPE_STRING; 544 515 } 545 516 517 + static bool mlxsw_emad_tlv_is_latency_tlv(const char *tlv) 518 + { 519 + u8 tlv_type = mlxsw_emad_latency_tlv_type_get(tlv); 520 + 521 + return tlv_type == MLXSW_EMAD_TLV_TYPE_LATENCY; 522 + } 523 + 546 524 static void mlxsw_emad_tlv_parse(struct sk_buff *skb) 547 525 { 548 526 struct mlxsw_emad_tlv_offsets *offsets = ··· 557 521 558 522 offsets->op_tlv = MLXSW_EMAD_ETH_HDR_LEN; 559 523 offsets->string_tlv = 0; 524 + offsets->latency_tlv = 0; 525 + 560 526 offsets->reg_tlv = MLXSW_EMAD_ETH_HDR_LEN + 561 527 MLXSW_EMAD_OP_TLV_LEN * sizeof(u32); 562 528 ··· 566 528 if (mlxsw_emad_tlv_is_string_tlv(skb->data + offsets->reg_tlv)) { 567 529 offsets->string_tlv = offsets->reg_tlv; 568 530 offsets->reg_tlv += MLXSW_EMAD_STRING_TLV_LEN * sizeof(u32); 531 + } 532 + 533 + if (mlxsw_emad_tlv_is_latency_tlv(skb->data + offsets->reg_tlv)) { 534 + offsets->latency_tlv = offsets->reg_tlv; 535 + offsets->reg_tlv += MLXSW_EMAD_LATENCY_TLV_LEN * sizeof(u32); 569 536 } 570 537 } 571 538 ··· 837 794 MLXSW_RXL(mlxsw_emad_rx_listener_func, ETHEMAD, TRAP_TO_CPU, false, 838 795 EMAD, DISCARD); 839 796 797 + static int mlxsw_emad_tlv_enable(struct mlxsw_core *mlxsw_core) 798 + { 799 + char mgir_pl[MLXSW_REG_MGIR_LEN]; 800 + bool string_tlv, latency_tlv; 801 + int err; 802 + 803 + mlxsw_reg_mgir_pack(mgir_pl); 804 + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgir), mgir_pl); 805 + if (err) 806 + return err; 807 + 808 + string_tlv = mlxsw_reg_mgir_fw_info_string_tlv_get(mgir_pl); 809 + mlxsw_core->emad.enable_string_tlv = string_tlv; 810 + 811 + latency_tlv = mlxsw_reg_mgir_fw_info_latency_tlv_get(mgir_pl); 812 + mlxsw_core->emad.enable_latency_tlv = latency_tlv; 813 + 814 + return 0; 815 + } 816 + 817 + static void mlxsw_emad_tlv_disable(struct mlxsw_core *mlxsw_core) 818 + { 819 + mlxsw_core->emad.enable_latency_tlv = false; 820 + mlxsw_core->emad.enable_string_tlv = false; 821 + } 822 + 840 823 static int mlxsw_emad_init(struct mlxsw_core *mlxsw_core) 841 824 { 842 825 struct workqueue_struct *emad_wq; ··· 893 824 if (err) 894 825 goto err_trap_register; 895 826 827 + err = mlxsw_emad_tlv_enable(mlxsw_core); 828 + if (err) 829 + goto err_emad_tlv_enable; 830 + 896 831 mlxsw_core->emad.use_emad = true; 897 832 898 833 return 0; 899 834 835 + err_emad_tlv_enable: 836 + mlxsw_core_trap_unregister(mlxsw_core, &mlxsw_emad_rx_listener, 837 + mlxsw_core); 900 838 err_trap_register: 901 839 destroy_workqueue(mlxsw_core->emad_wq); 902 840 return err; ··· 916 840 return; 917 841 918 842 mlxsw_core->emad.use_emad = false; 843 + mlxsw_emad_tlv_disable(mlxsw_core); 919 844 mlxsw_core_trap_unregister(mlxsw_core, &mlxsw_emad_rx_listener, 920 845 mlxsw_core); 921 846 destroy_workqueue(mlxsw_core->emad_wq); 922 847 } 923 848 924 849 static struct sk_buff *mlxsw_emad_alloc(const struct mlxsw_core *mlxsw_core, 925 - u16 reg_len, bool enable_string_tlv) 850 + u16 reg_len) 926 851 { 927 852 struct sk_buff *skb; 928 853 u16 emad_len; ··· 931 854 emad_len = (reg_len + sizeof(u32) + MLXSW_EMAD_ETH_HDR_LEN + 932 855 (MLXSW_EMAD_OP_TLV_LEN + MLXSW_EMAD_END_TLV_LEN) * 933 856 sizeof(u32) + mlxsw_core->driver->txhdr_len); 934 - if (enable_string_tlv) 857 + if (mlxsw_core->emad.enable_string_tlv) 935 858 emad_len += MLXSW_EMAD_STRING_TLV_LEN * sizeof(u32); 859 + if (mlxsw_core->emad.enable_latency_tlv) 860 + emad_len += MLXSW_EMAD_LATENCY_TLV_LEN * sizeof(u32); 936 861 if (emad_len > MLXSW_EMAD_MAX_FRAME_LEN) 937 862 return NULL; 938 863 ··· 956 877 mlxsw_reg_trans_cb_t *cb, 957 878 unsigned long cb_priv, u64 tid) 958 879 { 959 - bool enable_string_tlv; 960 880 struct sk_buff *skb; 961 881 int err; 962 882 ··· 963 885 tid, reg->id, mlxsw_reg_id_str(reg->id), 964 886 mlxsw_core_reg_access_type_str(type)); 965 887 966 - /* Since this can be changed during emad_reg_access, read it once and 967 - * use the value all the way. 968 - */ 969 - enable_string_tlv = mlxsw_core->emad.enable_string_tlv; 970 - 971 - skb = mlxsw_emad_alloc(mlxsw_core, reg->len, enable_string_tlv); 888 + skb = mlxsw_emad_alloc(mlxsw_core, reg->len); 972 889 if (!skb) 973 890 return -ENOMEM; 974 891 ··· 980 907 trans->reg = reg; 981 908 trans->type = type; 982 909 983 - mlxsw_emad_construct(skb, reg, payload, type, trans->tid, 984 - enable_string_tlv); 910 + mlxsw_emad_construct(mlxsw_core, skb, reg, payload, type, trans->tid); 985 911 mlxsw_core->driver->txhdr_construct(skb, &trans->tx_info); 986 912 987 913 spin_lock_bh(&mlxsw_core->emad.trans_list_lock); ··· 3448 3376 return mlxsw_core->driver->sdq_supports_cqe_v2; 3449 3377 } 3450 3378 EXPORT_SYMBOL(mlxsw_core_sdq_supports_cqe_v2); 3451 - 3452 - void mlxsw_core_emad_string_tlv_enable(struct mlxsw_core *mlxsw_core) 3453 - { 3454 - mlxsw_core->emad.enable_string_tlv = true; 3455 - } 3456 - EXPORT_SYMBOL(mlxsw_core_emad_string_tlv_enable); 3457 3379 3458 3380 static int __init mlxsw_core_module_init(void) 3459 3381 {
-2
drivers/net/ethernet/mellanox/mlxsw/core.h
··· 448 448 449 449 bool mlxsw_core_sdq_supports_cqe_v2(struct mlxsw_core *mlxsw_core); 450 450 451 - void mlxsw_core_emad_string_tlv_enable(struct mlxsw_core *mlxsw_core); 452 - 453 451 bool mlxsw_core_res_valid(struct mlxsw_core *mlxsw_core, 454 452 enum mlxsw_res_id res_id); 455 453
+4
drivers/net/ethernet/mellanox/mlxsw/emad.h
··· 21 21 MLXSW_EMAD_TLV_TYPE_OP, 22 22 MLXSW_EMAD_TLV_TYPE_STRING, 23 23 MLXSW_EMAD_TLV_TYPE_REG, 24 + MLXSW_EMAD_TLV_TYPE_LATENCY, 24 25 }; 25 26 26 27 /* OP TLV */ ··· 90 89 91 90 /* STRING TLV */ 92 91 #define MLXSW_EMAD_STRING_TLV_LEN 33 /* Length in u32 */ 92 + 93 + /* LATENCY TLV */ 94 + #define MLXSW_EMAD_LATENCY_TLV_LEN 7 /* Length in u32 */ 93 95 94 96 /* END TLV */ 95 97 #define MLXSW_EMAD_END_TLV_LEN 1 /* Length in u32 */
+12
drivers/net/ethernet/mellanox/mlxsw/reg.h
··· 10009 10009 */ 10010 10010 MLXSW_ITEM32(reg, mgir, hw_info_device_hw_revision, 0x0, 16, 16); 10011 10011 10012 + /* reg_mgir_fw_info_latency_tlv 10013 + * When set, latency-TLV is supported. 10014 + * Access: RO 10015 + */ 10016 + MLXSW_ITEM32(reg, mgir, fw_info_latency_tlv, 0x20, 29, 1); 10017 + 10018 + /* reg_mgir_fw_info_string_tlv 10019 + * When set, string-TLV is supported. 10020 + * Access: RO 10021 + */ 10022 + MLXSW_ITEM32(reg, mgir, fw_info_string_tlv, 0x20, 28, 1); 10023 + 10012 10024 #define MLXSW_REG_MGIR_FW_INFO_PSID_SIZE 16 10013 10025 10014 10026 /* reg_mgir_fw_info_psid
-1
drivers/net/ethernet/mellanox/mlxsw/spectrum.c
··· 3092 3092 mlxsw_sp->bus_info = mlxsw_bus_info; 3093 3093 3094 3094 mlxsw_sp_parsing_init(mlxsw_sp); 3095 - mlxsw_core_emad_string_tlv_enable(mlxsw_core); 3096 3095 3097 3096 err = mlxsw_sp_base_mac_get(mlxsw_sp); 3098 3097 if (err) {