Skip to content

Commit 5c65c56

Browse files
ogerlitzSaeed Mahameed
authored andcommitted
net/mlx5e: Support offloading TC NIC hairpin flows
We refer to TC NIC rule that involves forwarding as "hairpin". All hairpin rules from the current NIC device (called "func" in the code) to a given NIC device ("peer") are steered into the same hairpin RQ/SQ pair. The hairpin pair is set on demand and removed when there are no TC rules that need it. Here's a TC rule that matches on icmp, does header re-write of the dst mac and hairpin from RX/enp1s2f1 to TX/enp1s2f2 (enp1s2f1/2 are two mlx5 devices): tc filter add dev enp1s2f1 protocol ip parent ffff: prio 2 flower skip_sw ip_proto icmp action pedit ex munge eth dst set 10:22:33:44:55:66 pipe action mirred egress redirect dev enp1s2f2 Signed-off-by: Or Gerlitz <[email protected]> Signed-off-by: Saeed Mahameed <[email protected]>
1 parent 77ab67b commit 5c65c56

File tree

2 files changed

+172
-12
lines changed

2 files changed

+172
-12
lines changed

drivers/net/ethernet/mellanox/mlx5/core/en.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,7 @@ struct mlx5e_tc_table {
659659
struct rhashtable ht;
660660

661661
DECLARE_HASHTABLE(mod_hdr_tbl, 8);
662+
DECLARE_HASHTABLE(hairpin_tbl, 8);
662663
};
663664

664665
struct mlx5e_vlan_table {

drivers/net/ethernet/mellanox/mlx5/core/en_tc.c

Lines changed: 171 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,14 @@ struct mlx5_nic_flow_attr {
5656
u32 action;
5757
u32 flow_tag;
5858
u32 mod_hdr_id;
59+
u32 hairpin_tirn;
5960
};
6061

6162
enum {
6263
MLX5E_TC_FLOW_ESWITCH = BIT(0),
6364
MLX5E_TC_FLOW_NIC = BIT(1),
6465
MLX5E_TC_FLOW_OFFLOADED = BIT(2),
66+
MLX5E_TC_FLOW_HAIRPIN = BIT(3),
6567
};
6668

6769
struct mlx5e_tc_flow {
@@ -71,6 +73,7 @@ struct mlx5e_tc_flow {
7173
struct mlx5_flow_handle *rule;
7274
struct list_head encap; /* flows sharing the same encap ID */
7375
struct list_head mod_hdr; /* flows sharing the same mod hdr ID */
76+
struct list_head hairpin; /* flows sharing the same hairpin */
7477
union {
7578
struct mlx5_esw_flow_attr esw_attr[0];
7679
struct mlx5_nic_flow_attr nic_attr[0];
@@ -101,6 +104,17 @@ struct mlx5e_hairpin {
101104
u32 tirn;
102105
};
103106

107+
struct mlx5e_hairpin_entry {
108+
/* a node of a hash table which keeps all the hairpin entries */
109+
struct hlist_node hairpin_hlist;
110+
111+
/* flows sharing the same hairpin */
112+
struct list_head flows;
113+
114+
int peer_ifindex;
115+
struct mlx5e_hairpin *hp;
116+
};
117+
104118
struct mod_hdr_key {
105119
int num_actions;
106120
void *actions;
@@ -319,14 +333,106 @@ static void mlx5e_hairpin_destroy(struct mlx5e_hairpin *hp)
319333
kvfree(hp);
320334
}
321335

336+
static struct mlx5e_hairpin_entry *mlx5e_hairpin_get(struct mlx5e_priv *priv,
337+
int peer_ifindex)
338+
{
339+
struct mlx5e_hairpin_entry *hpe;
340+
341+
hash_for_each_possible(priv->fs.tc.hairpin_tbl, hpe,
342+
hairpin_hlist, peer_ifindex) {
343+
if (hpe->peer_ifindex == peer_ifindex)
344+
return hpe;
345+
}
346+
347+
return NULL;
348+
}
349+
350+
static int mlx5e_hairpin_flow_add(struct mlx5e_priv *priv,
351+
struct mlx5e_tc_flow *flow,
352+
struct mlx5e_tc_flow_parse_attr *parse_attr)
353+
{
354+
int peer_ifindex = parse_attr->mirred_ifindex;
355+
struct mlx5_hairpin_params params;
356+
struct mlx5e_hairpin_entry *hpe;
357+
struct mlx5e_hairpin *hp;
358+
int err;
359+
360+
if (!MLX5_CAP_GEN(priv->mdev, hairpin)) {
361+
netdev_warn(priv->netdev, "hairpin is not supported\n");
362+
return -EOPNOTSUPP;
363+
}
364+
365+
hpe = mlx5e_hairpin_get(priv, peer_ifindex);
366+
if (hpe)
367+
goto attach_flow;
368+
369+
hpe = kzalloc(sizeof(*hpe), GFP_KERNEL);
370+
if (!hpe)
371+
return -ENOMEM;
372+
373+
INIT_LIST_HEAD(&hpe->flows);
374+
hpe->peer_ifindex = peer_ifindex;
375+
376+
params.log_data_size = 15;
377+
params.log_data_size = min_t(u8, params.log_data_size,
378+
MLX5_CAP_GEN(priv->mdev, log_max_hairpin_wq_data_sz));
379+
params.log_data_size = max_t(u8, params.log_data_size,
380+
MLX5_CAP_GEN(priv->mdev, log_min_hairpin_wq_data_sz));
381+
params.q_counter = priv->q_counter;
382+
383+
hp = mlx5e_hairpin_create(priv, &params, peer_ifindex);
384+
if (IS_ERR(hp)) {
385+
err = PTR_ERR(hp);
386+
goto create_hairpin_err;
387+
}
388+
389+
netdev_dbg(priv->netdev, "add hairpin: tirn %x rqn %x peer %s sqn %x log data size %d\n",
390+
hp->tirn, hp->pair->rqn, hp->pair->peer_mdev->priv.name,
391+
hp->pair->sqn, params.log_data_size);
392+
393+
hpe->hp = hp;
394+
hash_add(priv->fs.tc.hairpin_tbl, &hpe->hairpin_hlist, peer_ifindex);
395+
396+
attach_flow:
397+
flow->nic_attr->hairpin_tirn = hpe->hp->tirn;
398+
list_add(&flow->hairpin, &hpe->flows);
399+
return 0;
400+
401+
create_hairpin_err:
402+
kfree(hpe);
403+
return err;
404+
}
405+
406+
static void mlx5e_hairpin_flow_del(struct mlx5e_priv *priv,
407+
struct mlx5e_tc_flow *flow)
408+
{
409+
struct list_head *next = flow->hairpin.next;
410+
411+
list_del(&flow->hairpin);
412+
413+
/* no more hairpin flows for us, release the hairpin pair */
414+
if (list_empty(next)) {
415+
struct mlx5e_hairpin_entry *hpe;
416+
417+
hpe = list_entry(next, struct mlx5e_hairpin_entry, flows);
418+
419+
netdev_dbg(priv->netdev, "del hairpin: peer %s\n",
420+
hpe->hp->pair->peer_mdev->priv.name);
421+
422+
mlx5e_hairpin_destroy(hpe->hp);
423+
hash_del(&hpe->hairpin_hlist);
424+
kfree(hpe);
425+
}
426+
}
427+
322428
static struct mlx5_flow_handle *
323429
mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv,
324430
struct mlx5e_tc_flow_parse_attr *parse_attr,
325431
struct mlx5e_tc_flow *flow)
326432
{
327433
struct mlx5_nic_flow_attr *attr = flow->nic_attr;
328434
struct mlx5_core_dev *dev = priv->mdev;
329-
struct mlx5_flow_destination dest = {};
435+
struct mlx5_flow_destination dest[2] = {};
330436
struct mlx5_flow_act flow_act = {
331437
.action = attr->action,
332438
.flow_tag = attr->flow_tag,
@@ -335,18 +441,33 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv,
335441
struct mlx5_fc *counter = NULL;
336442
struct mlx5_flow_handle *rule;
337443
bool table_created = false;
338-
int err;
444+
int err, dest_ix = 0;
339445

340446
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) {
341-
dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
342-
dest.ft = priv->fs.vlan.ft.t;
343-
} else if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
344-
counter = mlx5_fc_create(dev, true);
345-
if (IS_ERR(counter))
346-
return ERR_CAST(counter);
447+
if (flow->flags & MLX5E_TC_FLOW_HAIRPIN) {
448+
err = mlx5e_hairpin_flow_add(priv, flow, parse_attr);
449+
if (err) {
450+
rule = ERR_PTR(err);
451+
goto err_add_hairpin_flow;
452+
}
453+
dest[dest_ix].type = MLX5_FLOW_DESTINATION_TYPE_TIR;
454+
dest[dest_ix].tir_num = attr->hairpin_tirn;
455+
} else {
456+
dest[dest_ix].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
457+
dest[dest_ix].ft = priv->fs.vlan.ft.t;
458+
}
459+
dest_ix++;
460+
}
347461

348-
dest.type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
349-
dest.counter = counter;
462+
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) {
463+
counter = mlx5_fc_create(dev, true);
464+
if (IS_ERR(counter)) {
465+
rule = ERR_CAST(counter);
466+
goto err_fc_create;
467+
}
468+
dest[dest_ix].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
469+
dest[dest_ix].counter = counter;
470+
dest_ix++;
350471
}
351472

352473
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
@@ -389,7 +510,7 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv,
389510

390511
parse_attr->spec.match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
391512
rule = mlx5_add_flow_rules(priv->fs.tc.t, &parse_attr->spec,
392-
&flow_act, &dest, 1);
513+
&flow_act, dest, dest_ix);
393514

394515
if (IS_ERR(rule))
395516
goto err_add_rule;
@@ -406,7 +527,10 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv,
406527
mlx5e_detach_mod_hdr(priv, flow);
407528
err_create_mod_hdr_id:
408529
mlx5_fc_destroy(dev, counter);
409-
530+
err_fc_create:
531+
if (flow->flags & MLX5E_TC_FLOW_HAIRPIN)
532+
mlx5e_hairpin_flow_del(priv, flow);
533+
err_add_hairpin_flow:
410534
return rule;
411535
}
412536

@@ -427,6 +551,9 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv,
427551

428552
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
429553
mlx5e_detach_mod_hdr(priv, flow);
554+
555+
if (flow->flags & MLX5E_TC_FLOW_HAIRPIN)
556+
mlx5e_hairpin_flow_del(priv, flow);
430557
}
431558

432559
static void mlx5e_detach_encap(struct mlx5e_priv *priv,
@@ -1519,6 +1646,20 @@ static bool actions_match_supported(struct mlx5e_priv *priv,
15191646
return true;
15201647
}
15211648

1649+
static bool same_hw_devs(struct mlx5e_priv *priv, struct mlx5e_priv *peer_priv)
1650+
{
1651+
struct mlx5_core_dev *fmdev, *pmdev;
1652+
u16 func_id, peer_id;
1653+
1654+
fmdev = priv->mdev;
1655+
pmdev = peer_priv->mdev;
1656+
1657+
func_id = (u16)((fmdev->pdev->bus->number << 8) | PCI_SLOT(fmdev->pdev->devfn));
1658+
peer_id = (u16)((pmdev->pdev->bus->number << 8) | PCI_SLOT(pmdev->pdev->devfn));
1659+
1660+
return (func_id == peer_id);
1661+
}
1662+
15221663
static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
15231664
struct mlx5e_tc_flow_parse_attr *parse_attr,
15241665
struct mlx5e_tc_flow *flow)
@@ -1563,6 +1704,23 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
15631704
return -EOPNOTSUPP;
15641705
}
15651706

1707+
if (is_tcf_mirred_egress_redirect(a)) {
1708+
struct net_device *peer_dev = tcf_mirred_dev(a);
1709+
1710+
if (priv->netdev->netdev_ops == peer_dev->netdev_ops &&
1711+
same_hw_devs(priv, netdev_priv(peer_dev))) {
1712+
parse_attr->mirred_ifindex = peer_dev->ifindex;
1713+
flow->flags |= MLX5E_TC_FLOW_HAIRPIN;
1714+
attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
1715+
MLX5_FLOW_CONTEXT_ACTION_COUNT;
1716+
} else {
1717+
netdev_warn(priv->netdev, "device %s not on same HW, can't offload\n",
1718+
peer_dev->name);
1719+
return -EINVAL;
1720+
}
1721+
continue;
1722+
}
1723+
15661724
if (is_tcf_skbedit_mark(a)) {
15671725
u32 mark = tcf_skbedit_mark(a);
15681726

@@ -2285,6 +2443,7 @@ int mlx5e_tc_init(struct mlx5e_priv *priv)
22852443
struct mlx5e_tc_table *tc = &priv->fs.tc;
22862444

22872445
hash_init(tc->mod_hdr_tbl);
2446+
hash_init(tc->hairpin_tbl);
22882447

22892448
tc->ht_params = mlx5e_tc_flow_ht_params;
22902449
return rhashtable_init(&tc->ht, &tc->ht_params);

0 commit comments

Comments
 (0)