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

Configure Feed

Select the types of activity you want to include in your feed.

{net/RDMA}/mlx5: Add LAG demux table API and vport demux rules

Downstream patches will introduce SW-only LAG (e.g. shared_fdb without
HW LAG). In this mode the firmware cannot create the LAG demux table,
but vport demuxing is still required.

Move LAG demux flow-table ownership to the LAG layer and introduce APIs
to init/cleanup the demux table and add/delete per-vport rules. Adjust
the RDMA driver to use the new APIs.

In this mode, the LAG layer will create a flow group that matches vport
metadata. Vports that are not native to the LAG master eswitch add the
demux rule during IB representor load and remove it on unload.
The demux rule forward traffic from said vports to their native eswitch
manager via a new dest type - MLX5_FLOW_DESTINATION_TYPE_VHCA_RX.

Signed-off-by: Shay Drory <shayd@nvidia.com>
Reviewed-by: Mark Bloch <mbloch@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
Link: https://patch.msgid.link/20260309093435.1850724-9-tariqt@nvidia.com
Signed-off-by: Leon Romanovsky <leon@kernel.org>

authored by

Shay Drory and committed by
Leon Romanovsky
d6c9b4de 0bc9059f

+301 -28
+17 -3
drivers/infiniband/hw/mlx5/ib_rep.c
··· 10 10 11 11 static int 12 12 mlx5_ib_set_vport_rep(struct mlx5_core_dev *dev, 13 + struct mlx5_core_dev *rep_dev, 13 14 struct mlx5_eswitch_rep *rep, 14 15 int vport_index) 15 16 { 16 17 struct mlx5_ib_dev *ibdev; 17 18 struct net_device *ndev; 19 + int ret; 18 20 19 21 ibdev = mlx5_eswitch_uplink_get_proto_dev(dev->priv.eswitch, REP_IB); 20 22 if (!ibdev) ··· 26 24 rep->rep_data[REP_IB].priv = ibdev; 27 25 ndev = mlx5_ib_get_rep_netdev(rep->esw, rep->vport); 28 26 29 - return ib_device_set_netdev(&ibdev->ib_dev, ndev, vport_index + 1); 27 + ret = ib_device_set_netdev(&ibdev->ib_dev, ndev, vport_index + 1); 28 + if (ret) 29 + return ret; 30 + 31 + /* Only Vports that are not native to the LAG master eswitch need to add 32 + * demux rule. 33 + */ 34 + if (mlx5_eswitch_get_total_vports(dev) > vport_index) 35 + return 0; 36 + 37 + return mlx5_lag_demux_rule_add(rep_dev, rep->vport, vport_index); 30 38 } 31 39 32 40 static void mlx5_ib_register_peer_vport_reps(struct mlx5_core_dev *mdev); ··· 143 131 144 132 if (mlx5_lag_is_master(peer_dev)) 145 133 lag_master = peer_dev; 146 - else if (!mlx5_lag_is_mpesw(dev)) 134 + else if (!mlx5_lag_is_mpesw(peer_dev)) 147 135 /* Only 1 ib port is the representor for all uplinks */ 148 136 peer_n_ports--; 149 137 ··· 157 145 if (rep->vport == MLX5_VPORT_UPLINK && !new_uplink) 158 146 profile = &raw_eth_profile; 159 147 else 160 - return mlx5_ib_set_vport_rep(lag_master, rep, vport_index); 148 + return mlx5_ib_set_vport_rep(lag_master, dev, rep, vport_index); 161 149 162 150 if (mlx5_lag_is_shared_fdb(dev)) { 163 151 ret = mlx5_ib_take_transport(lag_master); ··· 244 232 return; 245 233 vport_index = i; 246 234 } 235 + 236 + mlx5_lag_demux_rule_del(mdev, vport_index); 247 237 248 238 port = &dev->port[vport_index]; 249 239
+11 -10
drivers/infiniband/hw/mlx5/main.c
··· 26 26 #include <linux/mlx5/fs.h> 27 27 #include <linux/mlx5/eswitch.h> 28 28 #include <linux/mlx5/driver.h> 29 + #include <linux/mlx5/lag.h> 29 30 #include <linux/list.h> 30 31 #include <rdma/ib_smi.h> 31 32 #include <rdma/ib_umem_odp.h> ··· 3679 3678 3680 3679 static int mlx5_eth_lag_init(struct mlx5_ib_dev *dev) 3681 3680 { 3681 + struct mlx5_flow_table_attr ft_attr = {}; 3682 3682 struct mlx5_core_dev *mdev = dev->mdev; 3683 - struct mlx5_flow_namespace *ns = mlx5_get_flow_namespace(mdev, 3684 - MLX5_FLOW_NAMESPACE_LAG); 3685 - struct mlx5_flow_table *ft; 3683 + struct mlx5_flow_namespace *ns; 3686 3684 int err; 3687 3685 3686 + ns = mlx5_get_flow_namespace(mdev, MLX5_FLOW_NAMESPACE_LAG); 3688 3687 if (!ns || !mlx5_lag_is_active(mdev)) 3689 3688 return 0; 3690 3689 ··· 3692 3691 if (err) 3693 3692 return err; 3694 3693 3695 - ft = mlx5_create_lag_demux_flow_table(ns, 0, 0); 3696 - if (IS_ERR(ft)) { 3697 - err = PTR_ERR(ft); 3694 + ft_attr.level = 0; 3695 + ft_attr.prio = 0; 3696 + ft_attr.max_fte = dev->num_ports; 3697 + 3698 + err = mlx5_lag_demux_init(mdev, &ft_attr); 3699 + if (err) 3698 3700 goto err_destroy_vport_lag; 3699 - } 3700 3701 3701 3702 mlx5e_lag_event_register(dev); 3702 - dev->flow_db->lag_demux_ft = ft; 3703 3703 dev->lag_ports = mlx5_lag_get_num_ports(mdev); 3704 3704 dev->lag_active = true; 3705 3705 return 0; ··· 3718 3716 dev->lag_active = false; 3719 3717 3720 3718 mlx5e_lag_event_unregister(dev); 3721 - mlx5_destroy_flow_table(dev->flow_db->lag_demux_ft); 3722 - dev->flow_db->lag_demux_ft = NULL; 3719 + mlx5_lag_demux_cleanup(mdev); 3723 3720 3724 3721 mlx5_cmd_destroy_vport_lag(mdev); 3725 3722 }
-1
drivers/infiniband/hw/mlx5/mlx5_ib.h
··· 306 306 struct mlx5_ib_flow_prio rdma_rx[MLX5_IB_NUM_FLOW_FT]; 307 307 struct mlx5_ib_flow_prio rdma_tx[MLX5_IB_NUM_FLOW_FT]; 308 308 struct mlx5_ib_flow_prio opfcs[MLX5_IB_OPCOUNTER_MAX]; 309 - struct mlx5_flow_table *lag_demux_ft; 310 309 struct mlx5_ib_flow_prio *rdma_transport_rx[MLX5_RDMA_TRANSPORT_BYPASS_PRIO]; 311 310 struct mlx5_ib_flow_prio *rdma_transport_tx[MLX5_RDMA_TRANSPORT_BYPASS_PRIO]; 312 311 /* Protect flow steering bypass flow tables
+12
drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
··· 940 940 u16 vport_num); 941 941 bool mlx5_esw_host_functions_enabled(const struct mlx5_core_dev *dev); 942 942 void mlx5_eswitch_safe_aux_devs_remove(struct mlx5_core_dev *dev); 943 + struct mlx5_flow_group * 944 + mlx5_esw_lag_demux_fg_create(struct mlx5_eswitch *esw, 945 + struct mlx5_flow_table *ft); 946 + struct mlx5_flow_handle * 947 + mlx5_esw_lag_demux_rule_create(struct mlx5_eswitch *esw, u16 vport_num, 948 + struct mlx5_flow_table *lag_ft); 943 949 #else /* CONFIG_MLX5_ESWITCH */ 944 950 /* eswitch API stubs */ 945 951 static inline int mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; } ··· 1031 1025 1032 1026 static inline void 1033 1027 mlx5_eswitch_safe_aux_devs_remove(struct mlx5_core_dev *dev) {} 1028 + static inline struct mlx5_flow_handle * 1029 + mlx5_esw_lag_demux_rule_create(struct mlx5_eswitch *esw, u16 vport_num, 1030 + struct mlx5_flow_table *lag_ft) 1031 + { 1032 + return ERR_PTR(-EOPNOTSUPP); 1033 + } 1034 1034 1035 1035 #endif /* CONFIG_MLX5_ESWITCH */ 1036 1036
+81 -2
drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
··· 1459 1459 return flow_rule; 1460 1460 } 1461 1461 1462 + struct mlx5_flow_group * 1463 + mlx5_esw_lag_demux_fg_create(struct mlx5_eswitch *esw, 1464 + struct mlx5_flow_table *ft) 1465 + { 1466 + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); 1467 + struct mlx5_flow_group *fg; 1468 + void *match_criteria; 1469 + void *flow_group_in; 1470 + 1471 + if (!mlx5_eswitch_vport_match_metadata_enabled(esw)) 1472 + return ERR_PTR(-EOPNOTSUPP); 1473 + 1474 + if (IS_ERR(ft)) 1475 + return ERR_CAST(ft); 1476 + 1477 + flow_group_in = kvzalloc(inlen, GFP_KERNEL); 1478 + if (!flow_group_in) 1479 + return ERR_PTR(-ENOMEM); 1480 + 1481 + match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, 1482 + match_criteria); 1483 + MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, 1484 + MLX5_MATCH_MISC_PARAMETERS_2); 1485 + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0); 1486 + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 1487 + ft->max_fte - 1); 1488 + 1489 + MLX5_SET(fte_match_param, match_criteria, 1490 + misc_parameters_2.metadata_reg_c_0, 1491 + mlx5_eswitch_get_vport_metadata_mask()); 1492 + 1493 + fg = mlx5_create_flow_group(ft, flow_group_in); 1494 + kvfree(flow_group_in); 1495 + if (IS_ERR(fg)) 1496 + esw_warn(esw->dev, "Can't create LAG demux flow group\n"); 1497 + 1498 + return fg; 1499 + } 1500 + 1501 + struct mlx5_flow_handle * 1502 + mlx5_esw_lag_demux_rule_create(struct mlx5_eswitch *esw, u16 vport_num, 1503 + struct mlx5_flow_table *lag_ft) 1504 + { 1505 + struct mlx5_flow_spec *spec = kvzalloc(sizeof(*spec), GFP_KERNEL); 1506 + struct mlx5_flow_destination dest = {}; 1507 + struct mlx5_flow_act flow_act = {}; 1508 + struct mlx5_flow_handle *ret; 1509 + void *misc; 1510 + 1511 + if (!spec) 1512 + return ERR_PTR(-ENOMEM); 1513 + 1514 + if (!mlx5_eswitch_vport_match_metadata_enabled(esw)) { 1515 + kvfree(spec); 1516 + return ERR_PTR(-EOPNOTSUPP); 1517 + } 1518 + 1519 + misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, 1520 + misc_parameters_2); 1521 + MLX5_SET(fte_match_set_misc2, misc, metadata_reg_c_0, 1522 + mlx5_eswitch_get_vport_metadata_mask()); 1523 + spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS_2; 1524 + 1525 + misc = MLX5_ADDR_OF(fte_match_param, spec->match_value, 1526 + misc_parameters_2); 1527 + MLX5_SET(fte_match_set_misc2, misc, metadata_reg_c_0, 1528 + mlx5_eswitch_get_vport_metadata_for_match(esw, vport_num)); 1529 + 1530 + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; 1531 + dest.type = MLX5_FLOW_DESTINATION_TYPE_VHCA_RX; 1532 + dest.vhca.id = MLX5_CAP_GEN(esw->dev, vhca_id); 1533 + 1534 + ret = mlx5_add_flow_rules(lag_ft, spec, &flow_act, &dest, 1); 1535 + kvfree(spec); 1536 + return ret; 1537 + } 1538 + 1462 1539 #define MAX_PF_SQ 256 1463 1540 #define MAX_SQ_NVPORTS 32 1464 1541 ··· 2124 2047 2125 2048 if (IS_ERR(g)) { 2126 2049 err = PTR_ERR(g); 2127 - mlx5_core_warn(esw->dev, "Failed to create vport rx group err %d\n", err); 2050 + esw_warn(esw->dev, "Failed to create vport rx group err %d\n", 2051 + err); 2128 2052 goto out; 2129 2053 } 2130 2054 ··· 2170 2092 2171 2093 if (IS_ERR(g)) { 2172 2094 err = PTR_ERR(g); 2173 - mlx5_core_warn(esw->dev, "Failed to create vport rx drop group err %d\n", err); 2095 + esw_warn(esw->dev, 2096 + "Failed to create vport rx drop group err %d\n", err); 2174 2097 goto out; 2175 2098 } 2176 2099
+2 -8
drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
··· 1438 1438 1439 1439 struct mlx5_flow_table* 1440 1440 mlx5_create_lag_demux_flow_table(struct mlx5_flow_namespace *ns, 1441 - int prio, u32 level) 1441 + struct mlx5_flow_table_attr *ft_attr) 1442 1442 { 1443 - struct mlx5_flow_table_attr ft_attr = {}; 1444 - 1445 - ft_attr.level = level; 1446 - ft_attr.prio = prio; 1447 - ft_attr.max_fte = 1; 1448 - 1449 - return __mlx5_create_flow_table(ns, &ft_attr, FS_FT_OP_MOD_LAG_DEMUX, 0); 1443 + return __mlx5_create_flow_table(ns, ft_attr, FS_FT_OP_MOD_LAG_DEMUX, 0); 1450 1444 } 1451 1445 EXPORT_SYMBOL(mlx5_create_lag_demux_flow_table); 1452 1446
+152
drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
··· 1471 1471 return devcom; 1472 1472 } 1473 1473 1474 + static int mlx5_lag_demux_ft_fg_init(struct mlx5_core_dev *dev, 1475 + struct mlx5_flow_table_attr *ft_attr, 1476 + struct mlx5_lag *ldev) 1477 + { 1478 + #ifdef CONFIG_MLX5_ESWITCH 1479 + struct mlx5_flow_namespace *ns; 1480 + struct mlx5_flow_group *fg; 1481 + int err; 1482 + 1483 + ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_LAG); 1484 + if (!ns) 1485 + return 0; 1486 + 1487 + ldev->lag_demux_ft = mlx5_create_flow_table(ns, ft_attr); 1488 + if (IS_ERR(ldev->lag_demux_ft)) 1489 + return PTR_ERR(ldev->lag_demux_ft); 1490 + 1491 + fg = mlx5_esw_lag_demux_fg_create(dev->priv.eswitch, 1492 + ldev->lag_demux_ft); 1493 + if (IS_ERR(fg)) { 1494 + err = PTR_ERR(fg); 1495 + mlx5_destroy_flow_table(ldev->lag_demux_ft); 1496 + ldev->lag_demux_ft = NULL; 1497 + return err; 1498 + } 1499 + 1500 + ldev->lag_demux_fg = fg; 1501 + return 0; 1502 + #else 1503 + return -EOPNOTSUPP; 1504 + #endif 1505 + } 1506 + 1507 + static int mlx5_lag_demux_fw_init(struct mlx5_core_dev *dev, 1508 + struct mlx5_flow_table_attr *ft_attr, 1509 + struct mlx5_lag *ldev) 1510 + { 1511 + struct mlx5_flow_namespace *ns; 1512 + int err; 1513 + 1514 + ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_LAG); 1515 + if (!ns) 1516 + return 0; 1517 + 1518 + ldev->lag_demux_fg = NULL; 1519 + ft_attr->max_fte = 1; 1520 + ldev->lag_demux_ft = mlx5_create_lag_demux_flow_table(ns, ft_attr); 1521 + if (IS_ERR(ldev->lag_demux_ft)) { 1522 + err = PTR_ERR(ldev->lag_demux_ft); 1523 + ldev->lag_demux_ft = NULL; 1524 + return err; 1525 + } 1526 + 1527 + return 0; 1528 + } 1529 + 1530 + int mlx5_lag_demux_init(struct mlx5_core_dev *dev, 1531 + struct mlx5_flow_table_attr *ft_attr) 1532 + { 1533 + struct mlx5_lag *ldev; 1534 + 1535 + if (!ft_attr) 1536 + return -EINVAL; 1537 + 1538 + ldev = mlx5_lag_dev(dev); 1539 + if (!ldev) 1540 + return -ENODEV; 1541 + 1542 + xa_init(&ldev->lag_demux_rules); 1543 + 1544 + if (mlx5_get_sd(dev)) 1545 + return mlx5_lag_demux_ft_fg_init(dev, ft_attr, ldev); 1546 + 1547 + return mlx5_lag_demux_fw_init(dev, ft_attr, ldev); 1548 + } 1549 + EXPORT_SYMBOL(mlx5_lag_demux_init); 1550 + 1551 + void mlx5_lag_demux_cleanup(struct mlx5_core_dev *dev) 1552 + { 1553 + struct mlx5_flow_handle *rule; 1554 + struct mlx5_lag *ldev; 1555 + unsigned long vport_num; 1556 + 1557 + ldev = mlx5_lag_dev(dev); 1558 + if (!ldev) 1559 + return; 1560 + 1561 + xa_for_each(&ldev->lag_demux_rules, vport_num, rule) 1562 + mlx5_del_flow_rules(rule); 1563 + xa_destroy(&ldev->lag_demux_rules); 1564 + 1565 + if (ldev->lag_demux_fg) 1566 + mlx5_destroy_flow_group(ldev->lag_demux_fg); 1567 + if (ldev->lag_demux_ft) 1568 + mlx5_destroy_flow_table(ldev->lag_demux_ft); 1569 + ldev->lag_demux_fg = NULL; 1570 + ldev->lag_demux_ft = NULL; 1571 + } 1572 + EXPORT_SYMBOL(mlx5_lag_demux_cleanup); 1573 + 1574 + int mlx5_lag_demux_rule_add(struct mlx5_core_dev *vport_dev, u16 vport_num, 1575 + int index) 1576 + { 1577 + struct mlx5_flow_handle *rule; 1578 + struct mlx5_lag *ldev; 1579 + int err; 1580 + 1581 + ldev = mlx5_lag_dev(vport_dev); 1582 + if (!ldev || !ldev->lag_demux_fg) 1583 + return 0; 1584 + 1585 + if (xa_load(&ldev->lag_demux_rules, index)) 1586 + return 0; 1587 + 1588 + rule = mlx5_esw_lag_demux_rule_create(vport_dev->priv.eswitch, 1589 + vport_num, ldev->lag_demux_ft); 1590 + if (IS_ERR(rule)) { 1591 + err = PTR_ERR(rule); 1592 + mlx5_core_warn(vport_dev, 1593 + "Failed to create LAG demux rule for vport %u, err %d\n", 1594 + vport_num, err); 1595 + return err; 1596 + } 1597 + 1598 + err = xa_err(xa_store(&ldev->lag_demux_rules, index, rule, 1599 + GFP_KERNEL)); 1600 + if (err) { 1601 + mlx5_del_flow_rules(rule); 1602 + mlx5_core_warn(vport_dev, 1603 + "Failed to store LAG demux rule for vport %u, err %d\n", 1604 + vport_num, err); 1605 + } 1606 + 1607 + return err; 1608 + } 1609 + EXPORT_SYMBOL(mlx5_lag_demux_rule_add); 1610 + 1611 + void mlx5_lag_demux_rule_del(struct mlx5_core_dev *dev, int index) 1612 + { 1613 + struct mlx5_flow_handle *rule; 1614 + struct mlx5_lag *ldev; 1615 + 1616 + ldev = mlx5_lag_dev(dev); 1617 + if (!ldev || !ldev->lag_demux_fg) 1618 + return; 1619 + 1620 + rule = xa_erase(&ldev->lag_demux_rules, index); 1621 + if (rule) 1622 + mlx5_del_flow_rules(rule); 1623 + } 1624 + EXPORT_SYMBOL(mlx5_lag_demux_rule_del); 1625 + 1474 1626 static void mlx5_queue_bond_work(struct mlx5_lag *ldev, unsigned long delay) 1475 1627 { 1476 1628 queue_delayed_work(ldev->wq, &ldev->bond_work, delay);
+12
drivers/net/ethernet/mellanox/mlx5/core/lag/lag.h
··· 5 5 #define __MLX5_LAG_H__ 6 6 7 7 #include <linux/debugfs.h> 8 + #include <linux/errno.h> 9 + #include <linux/xarray.h> 10 + #include <linux/mlx5/fs.h> 8 11 9 12 #define MLX5_LAG_MAX_HASH_BUCKETS 16 10 13 /* XArray mark for the LAG master device ··· 86 83 /* Protect lag fields/state changes */ 87 84 struct mutex lock; 88 85 struct lag_mpesw lag_mpesw; 86 + struct mlx5_flow_table *lag_demux_ft; 87 + struct mlx5_flow_group *lag_demux_fg; 88 + struct xarray lag_demux_rules; 89 89 }; 90 90 91 91 static inline struct mlx5_lag * ··· 139 133 140 134 bool mlx5_lag_shared_fdb_supported(struct mlx5_lag *ldev); 141 135 bool mlx5_lag_check_prereq(struct mlx5_lag *ldev); 136 + int mlx5_lag_demux_init(struct mlx5_core_dev *dev, 137 + struct mlx5_flow_table_attr *ft_attr); 138 + void mlx5_lag_demux_cleanup(struct mlx5_core_dev *dev); 139 + int mlx5_lag_demux_rule_add(struct mlx5_core_dev *dev, u16 vport_num, 140 + int vport_index); 141 + void mlx5_lag_demux_rule_del(struct mlx5_core_dev *dev, int vport_index); 142 142 void mlx5_modify_lag(struct mlx5_lag *ldev, 143 143 struct lag_tracker *tracker); 144 144 int mlx5_activate_lag(struct mlx5_lag *ldev,
+3 -3
include/linux/mlx5/fs.h
··· 252 252 struct mlx5_flow_table * 253 253 mlx5_create_vport_flow_table(struct mlx5_flow_namespace *ns, 254 254 struct mlx5_flow_table_attr *ft_attr, u16 vport); 255 - struct mlx5_flow_table *mlx5_create_lag_demux_flow_table( 256 - struct mlx5_flow_namespace *ns, 257 - int prio, u32 level); 255 + struct mlx5_flow_table * 256 + mlx5_create_lag_demux_flow_table(struct mlx5_flow_namespace *ns, 257 + struct mlx5_flow_table_attr *ft_attr); 258 258 int mlx5_destroy_flow_table(struct mlx5_flow_table *ft); 259 259 260 260 /* inbox should be set with the following values:
+11 -1
include/linux/mlx5/lag.h
··· 4 4 #ifndef __MLX5_LAG_API_H__ 5 5 #define __MLX5_LAG_API_H__ 6 6 7 - struct mlx5_core_dev; 7 + #include <linux/types.h> 8 8 9 + struct mlx5_core_dev; 10 + struct mlx5_flow_table; 11 + struct mlx5_flow_table_attr; 12 + 13 + int mlx5_lag_demux_init(struct mlx5_core_dev *dev, 14 + struct mlx5_flow_table_attr *ft_attr); 15 + void mlx5_lag_demux_cleanup(struct mlx5_core_dev *dev); 16 + int mlx5_lag_demux_rule_add(struct mlx5_core_dev *dev, u16 vport_num, 17 + int vport_index); 18 + void mlx5_lag_demux_rule_del(struct mlx5_core_dev *dev, int vport_index); 9 19 int mlx5_lag_get_dev_seq(struct mlx5_core_dev *dev); 10 20 11 21 #endif /* __MLX5_LAG_API_H__ */