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.

Merge branch 'add-l2-hw-acceleration-for-airoha_eth-driver'

Lorenzo Bianconi says:

====================
Add L2 hw acceleration for airoha_eth driver

Introduce the capability to offload L2 traffic defining flower rules in
the PSE/PPE engine available on EN7581 SoC.
Since the hw always reports L2/L3/L4 flower rules, link all L2 rules
sharing the same L2 info (with different L3/L4 info) in the L2 subflows
list of a given L2 PPE entry.

v1: https://lore.kernel.org/20250407-airoha-flowtable-l2b-v1-0-18777778e568@kernel.org
====================

Link: https://patch.msgid.link/20250409-airoha-flowtable-l2b-v2-0-4a1e3935ea92@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+212 -36
+1 -1
drivers/net/ethernet/airoha/airoha_eth.c
··· 694 694 695 695 reason = FIELD_GET(AIROHA_RXD4_PPE_CPU_REASON, msg1); 696 696 if (reason == PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) 697 - airoha_ppe_check_skb(eth->ppe, hash); 697 + airoha_ppe_check_skb(eth->ppe, q->skb, hash); 698 698 699 699 done++; 700 700 napi_gro_receive(&q->napi, q->skb);
+20 -2
drivers/net/ethernet/airoha/airoha_eth.h
··· 422 422 } pppoe; 423 423 }; 424 424 425 + enum airoha_flow_entry_type { 426 + FLOW_TYPE_L4, 427 + FLOW_TYPE_L2, 428 + FLOW_TYPE_L2_SUBFLOW, 429 + }; 430 + 425 431 struct airoha_flow_table_entry { 426 - struct hlist_node list; 432 + union { 433 + struct hlist_node list; /* PPE L3 flow entry */ 434 + struct { 435 + struct rhash_head l2_node; /* L2 flow entry */ 436 + struct hlist_head l2_flows; /* PPE L2 subflows list */ 437 + }; 438 + }; 427 439 428 440 struct airoha_foe_entry data; 441 + struct hlist_node l2_subflow_node; /* PPE L2 subflow entry */ 429 442 u32 hash; 443 + 444 + enum airoha_flow_entry_type type; 430 445 431 446 struct rhash_head node; 432 447 unsigned long cookie; ··· 494 479 495 480 void *foe; 496 481 dma_addr_t foe_dma; 482 + 483 + struct rhashtable l2_flows; 497 484 498 485 struct hlist_head *foe_flow; 499 486 u16 foe_check_time[PPE_NUM_ENTRIES]; ··· 552 535 bool airoha_is_valid_gdm_port(struct airoha_eth *eth, 553 536 struct airoha_gdm_port *port); 554 537 555 - void airoha_ppe_check_skb(struct airoha_ppe *ppe, u16 hash); 538 + void airoha_ppe_check_skb(struct airoha_ppe *ppe, struct sk_buff *skb, 539 + u16 hash); 556 540 int airoha_ppe_setup_tc_block_cb(enum tc_setup_type type, void *type_data, 557 541 void *cb_priv); 558 542 int airoha_ppe_init(struct airoha_eth *eth);
+191 -33
drivers/net/ethernet/airoha/airoha_ppe.c
··· 24 24 .automatic_shrinking = true, 25 25 }; 26 26 27 + static const struct rhashtable_params airoha_l2_flow_table_params = { 28 + .head_offset = offsetof(struct airoha_flow_table_entry, l2_node), 29 + .key_offset = offsetof(struct airoha_flow_table_entry, data.bridge), 30 + .key_len = 2 * ETH_ALEN, 31 + .automatic_shrinking = true, 32 + }; 33 + 27 34 static bool airoha_ppe2_is_enabled(struct airoha_eth *eth) 28 35 { 29 36 return airoha_fe_rr(eth, REG_PPE_GLO_CFG(1)) & PPE_GLO_CFG_EN_MASK; ··· 204 197 #endif 205 198 } 206 199 200 + static void airoha_ppe_foe_set_bridge_addrs(struct airoha_foe_bridge *br, 201 + struct ethhdr *eh) 202 + { 203 + br->dest_mac_hi = get_unaligned_be32(eh->h_dest); 204 + br->dest_mac_lo = get_unaligned_be16(eh->h_dest + 4); 205 + br->src_mac_hi = get_unaligned_be16(eh->h_source); 206 + br->src_mac_lo = get_unaligned_be32(eh->h_source + 2); 207 + } 208 + 207 209 static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth, 208 210 struct airoha_foe_entry *hwe, 209 211 struct net_device *dev, int type, ··· 263 247 264 248 qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f); 265 249 if (type == PPE_PKT_TYPE_BRIDGE) { 266 - hwe->bridge.dest_mac_hi = get_unaligned_be32(data->eth.h_dest); 267 - hwe->bridge.dest_mac_lo = 268 - get_unaligned_be16(data->eth.h_dest + 4); 269 - hwe->bridge.src_mac_hi = 270 - get_unaligned_be16(data->eth.h_source); 271 - hwe->bridge.src_mac_lo = 272 - get_unaligned_be32(data->eth.h_source + 2); 250 + airoha_ppe_foe_set_bridge_addrs(&hwe->bridge, &data->eth); 273 251 hwe->bridge.data = qdata; 274 252 hwe->bridge.ib2 = val; 275 253 l2 = &hwe->bridge.l2.common; ··· 388 378 hv3 = hwe->ipv6.src_ip[1] ^ hwe->ipv6.dest_ip[1]; 389 379 hv3 ^= hwe->ipv6.src_ip[0]; 390 380 break; 381 + case PPE_PKT_TYPE_BRIDGE: { 382 + struct airoha_foe_mac_info *l2 = &hwe->bridge.l2; 383 + 384 + hv1 = l2->common.src_mac_hi & 0xffff; 385 + hv1 = hv1 << 16 | l2->src_mac_lo; 386 + 387 + hv2 = l2->common.dest_mac_lo; 388 + hv2 = hv2 << 16; 389 + hv2 = hv2 | ((l2->common.src_mac_hi & 0xffff0000) >> 16); 390 + 391 + hv3 = l2->common.dest_mac_hi; 392 + break; 393 + } 391 394 case PPE_PKT_TYPE_IPV4_DSLITE: 392 395 case PPE_PKT_TYPE_IPV6_6RD: 393 396 default: ··· 499 476 return 0; 500 477 } 501 478 502 - static void airoha_ppe_foe_insert_entry(struct airoha_ppe *ppe, u32 hash) 479 + static void airoha_ppe_foe_remove_flow(struct airoha_ppe *ppe, 480 + struct airoha_flow_table_entry *e) 481 + { 482 + lockdep_assert_held(&ppe_lock); 483 + 484 + hlist_del_init(&e->list); 485 + if (e->hash != 0xffff) { 486 + e->data.ib1 &= ~AIROHA_FOE_IB1_BIND_STATE; 487 + e->data.ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_STATE, 488 + AIROHA_FOE_STATE_INVALID); 489 + airoha_ppe_foe_commit_entry(ppe, &e->data, e->hash); 490 + e->hash = 0xffff; 491 + } 492 + if (e->type == FLOW_TYPE_L2_SUBFLOW) { 493 + hlist_del_init(&e->l2_subflow_node); 494 + kfree(e); 495 + } 496 + } 497 + 498 + static void airoha_ppe_foe_remove_l2_flow(struct airoha_ppe *ppe, 499 + struct airoha_flow_table_entry *e) 500 + { 501 + struct hlist_head *head = &e->l2_flows; 502 + struct hlist_node *n; 503 + 504 + lockdep_assert_held(&ppe_lock); 505 + 506 + rhashtable_remove_fast(&ppe->l2_flows, &e->l2_node, 507 + airoha_l2_flow_table_params); 508 + hlist_for_each_entry_safe(e, n, head, l2_subflow_node) 509 + airoha_ppe_foe_remove_flow(ppe, e); 510 + } 511 + 512 + static void airoha_ppe_foe_flow_remove_entry(struct airoha_ppe *ppe, 513 + struct airoha_flow_table_entry *e) 514 + { 515 + spin_lock_bh(&ppe_lock); 516 + 517 + if (e->type == FLOW_TYPE_L2) 518 + airoha_ppe_foe_remove_l2_flow(ppe, e); 519 + else 520 + airoha_ppe_foe_remove_flow(ppe, e); 521 + 522 + spin_unlock_bh(&ppe_lock); 523 + } 524 + 525 + static int 526 + airoha_ppe_foe_commit_subflow_entry(struct airoha_ppe *ppe, 527 + struct airoha_flow_table_entry *e, 528 + u32 hash) 529 + { 530 + u32 mask = AIROHA_FOE_IB1_BIND_PACKET_TYPE | AIROHA_FOE_IB1_BIND_UDP; 531 + struct airoha_foe_entry *hwe_p, hwe; 532 + struct airoha_flow_table_entry *f; 533 + struct airoha_foe_mac_info *l2; 534 + int type; 535 + 536 + hwe_p = airoha_ppe_foe_get_entry(ppe, hash); 537 + if (!hwe_p) 538 + return -EINVAL; 539 + 540 + f = kzalloc(sizeof(*f), GFP_ATOMIC); 541 + if (!f) 542 + return -ENOMEM; 543 + 544 + hlist_add_head(&f->l2_subflow_node, &e->l2_flows); 545 + f->type = FLOW_TYPE_L2_SUBFLOW; 546 + f->hash = hash; 547 + 548 + memcpy(&hwe, hwe_p, sizeof(*hwe_p)); 549 + hwe.ib1 = (hwe.ib1 & mask) | (e->data.ib1 & ~mask); 550 + l2 = &hwe.bridge.l2; 551 + memcpy(l2, &e->data.bridge.l2, sizeof(*l2)); 552 + 553 + type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe.ib1); 554 + if (type == PPE_PKT_TYPE_IPV4_HNAPT) 555 + memcpy(&hwe.ipv4.new_tuple, &hwe.ipv4.orig_tuple, 556 + sizeof(hwe.ipv4.new_tuple)); 557 + else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T && 558 + l2->common.etype == ETH_P_IP) 559 + l2->common.etype = ETH_P_IPV6; 560 + 561 + hwe.bridge.ib2 = e->data.bridge.ib2; 562 + airoha_ppe_foe_commit_entry(ppe, &hwe, hash); 563 + 564 + return 0; 565 + } 566 + 567 + static void airoha_ppe_foe_insert_entry(struct airoha_ppe *ppe, 568 + struct sk_buff *skb, 569 + u32 hash) 503 570 { 504 571 struct airoha_flow_table_entry *e; 572 + struct airoha_foe_bridge br = {}; 505 573 struct airoha_foe_entry *hwe; 574 + bool commit_done = false; 506 575 struct hlist_node *n; 507 576 u32 index, state; 508 577 ··· 610 495 611 496 index = airoha_ppe_foe_get_entry_hash(hwe); 612 497 hlist_for_each_entry_safe(e, n, &ppe->foe_flow[index], list) { 613 - if (airoha_ppe_foe_compare_entry(e, hwe)) { 614 - airoha_ppe_foe_commit_entry(ppe, &e->data, hash); 615 - e->hash = hash; 616 - break; 498 + if (e->type == FLOW_TYPE_L2_SUBFLOW) { 499 + state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, hwe->ib1); 500 + if (state != AIROHA_FOE_STATE_BIND) { 501 + e->hash = 0xffff; 502 + airoha_ppe_foe_remove_flow(ppe, e); 503 + } 504 + continue; 617 505 } 506 + 507 + if (commit_done || !airoha_ppe_foe_compare_entry(e, hwe)) { 508 + e->hash = 0xffff; 509 + continue; 510 + } 511 + 512 + airoha_ppe_foe_commit_entry(ppe, &e->data, hash); 513 + commit_done = true; 514 + e->hash = hash; 618 515 } 516 + 517 + if (commit_done) 518 + goto unlock; 519 + 520 + airoha_ppe_foe_set_bridge_addrs(&br, eth_hdr(skb)); 521 + e = rhashtable_lookup_fast(&ppe->l2_flows, &br, 522 + airoha_l2_flow_table_params); 523 + if (e) 524 + airoha_ppe_foe_commit_subflow_entry(ppe, e, hash); 619 525 unlock: 620 526 spin_unlock_bh(&ppe_lock); 527 + } 528 + 529 + static int 530 + airoha_ppe_foe_l2_flow_commit_entry(struct airoha_ppe *ppe, 531 + struct airoha_flow_table_entry *e) 532 + { 533 + struct airoha_flow_table_entry *prev; 534 + 535 + e->type = FLOW_TYPE_L2; 536 + prev = rhashtable_lookup_get_insert_fast(&ppe->l2_flows, &e->l2_node, 537 + airoha_l2_flow_table_params); 538 + if (!prev) 539 + return 0; 540 + 541 + if (IS_ERR(prev)) 542 + return PTR_ERR(prev); 543 + 544 + return rhashtable_replace_fast(&ppe->l2_flows, &prev->l2_node, 545 + &e->l2_node, 546 + airoha_l2_flow_table_params); 621 547 } 622 548 623 549 static int airoha_ppe_foe_flow_commit_entry(struct airoha_ppe *ppe, 624 550 struct airoha_flow_table_entry *e) 625 551 { 626 - u32 hash = airoha_ppe_foe_get_entry_hash(&e->data); 552 + int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, e->data.ib1); 553 + u32 hash; 627 554 555 + if (type == PPE_PKT_TYPE_BRIDGE) 556 + return airoha_ppe_foe_l2_flow_commit_entry(ppe, e); 557 + 558 + hash = airoha_ppe_foe_get_entry_hash(&e->data); 559 + e->type = FLOW_TYPE_L4; 628 560 e->hash = 0xffff; 629 561 630 562 spin_lock_bh(&ppe_lock); ··· 679 517 spin_unlock_bh(&ppe_lock); 680 518 681 519 return 0; 682 - } 683 - 684 - static void airoha_ppe_foe_flow_remove_entry(struct airoha_ppe *ppe, 685 - struct airoha_flow_table_entry *e) 686 - { 687 - spin_lock_bh(&ppe_lock); 688 - 689 - hlist_del_init(&e->list); 690 - if (e->hash != 0xffff) { 691 - e->data.ib1 &= ~AIROHA_FOE_IB1_BIND_STATE; 692 - e->data.ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_STATE, 693 - AIROHA_FOE_STATE_INVALID); 694 - airoha_ppe_foe_commit_entry(ppe, &e->data, e->hash); 695 - e->hash = 0xffff; 696 - } 697 - 698 - spin_unlock_bh(&ppe_lock); 699 520 } 700 521 701 522 static int airoha_ppe_flow_offload_replace(struct airoha_gdm_port *port, ··· 991 846 return err; 992 847 } 993 848 994 - void airoha_ppe_check_skb(struct airoha_ppe *ppe, u16 hash) 849 + void airoha_ppe_check_skb(struct airoha_ppe *ppe, struct sk_buff *skb, 850 + u16 hash) 995 851 { 996 852 u16 now, diff; 997 853 ··· 1005 859 return; 1006 860 1007 861 ppe->foe_check_time[hash] = now; 1008 - airoha_ppe_foe_insert_entry(ppe, hash); 862 + airoha_ppe_foe_insert_entry(ppe, skb, hash); 1009 863 } 1010 864 1011 865 int airoha_ppe_init(struct airoha_eth *eth) ··· 1036 890 if (err) 1037 891 return err; 1038 892 893 + err = rhashtable_init(&ppe->l2_flows, &airoha_l2_flow_table_params); 894 + if (err) 895 + goto error_flow_table_destroy; 896 + 1039 897 err = airoha_ppe_debugfs_init(ppe); 1040 898 if (err) 1041 - rhashtable_destroy(&eth->flow_table); 899 + goto error_l2_flow_table_destroy; 900 + 901 + return 0; 902 + 903 + error_l2_flow_table_destroy: 904 + rhashtable_destroy(&ppe->l2_flows); 905 + error_flow_table_destroy: 906 + rhashtable_destroy(&eth->flow_table); 1042 907 1043 908 return err; 1044 909 } ··· 1066 909 } 1067 910 rcu_read_unlock(); 1068 911 912 + rhashtable_destroy(&eth->ppe->l2_flows); 1069 913 rhashtable_destroy(&eth->flow_table); 1070 914 debugfs_remove(eth->ppe->debugfs_dir); 1071 915 }