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 'net-dsa-mv88e6xxx-add-partial-support-for-tcam-entries'

Cedric Jehasse says:

====================
net: dsa: mv88e6xxx: Add partial support for TCAM entries

This series adds partial Ternary Content Addressable Memory (TCAM) for
the mv88e6390 and mv88e6393 family of switches. TCAM entries allow the
switch to match the first 48 or 96 bytes of a frame and take actions on
matched frames.

This patch introduces a subset of the available TCAM functionality.
Matching on ip addresses/protocol and trapping to the cpu.

Eg. to trap traffic with destination ip 224.0.1.129 to the cpu:

tc qdisc add dev p1 clsact
tc filter add dev p1 ingress protocol ip flower skip_sw \
dst_ip 224.0.1.129 action trap

Review of the mv88e6xxx changes have brought to light something in
cls_flower:
When adding a classifier with an ipv4 address both
FLOW_DISSECTOR_KEY_IPV4_ADDRS and FLOW_DISSECTOR_KEY_IPV6_ADDRS bits are
set in dissector->used_keys.
A change was made to address this.

Signed-off-by: Cedric Jehasse <cedric.jehasse@luminex.be>
====================

Link: https://patch.msgid.link/20260311-net-next-mv88e6xxx-tcam-v8-0-32dd5ba30002@luminex.be
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

+686 -10
+2
drivers/net/dsa/mv88e6xxx/Makefile
··· 21 21 mv88e6xxx-objs += smi.o 22 22 mv88e6xxx-objs += switchdev.o 23 23 mv88e6xxx-objs += trace.o 24 + mv88e6xxx-objs += tcflower.o 25 + mv88e6xxx-objs += tcam.o 24 26 25 27 # for tracing framework to find trace.h 26 28 CFLAGS_trace.o := -I$(src)
+35
drivers/net/dsa/mv88e6xxx/chip.c
··· 43 43 #include "ptp.h" 44 44 #include "serdes.h" 45 45 #include "smi.h" 46 + #include "tcam.h" 47 + #include "tcflower.h" 46 48 47 49 static void assert_reg_lock(struct mv88e6xxx_chip *chip) 48 50 { ··· 3562 3560 if (err) 3563 3561 return err; 3564 3562 } 3563 + if (chip->info->ops->port_enable_tcam) { 3564 + err = chip->info->ops->port_enable_tcam(chip, port); 3565 + if (err) 3566 + return err; 3567 + } 3565 3568 3566 3569 if (chip->info->ops->port_tag_remap) { 3567 3570 err = chip->info->ops->port_tag_remap(chip, port); ··· 3945 3938 return 0; 3946 3939 } 3947 3940 3941 + static int mv88e6xxx_tcam_setup(struct mv88e6xxx_chip *chip) 3942 + { 3943 + if (!mv88e6xxx_has_tcam(chip)) 3944 + return 0; 3945 + 3946 + return chip->info->ops->tcam_ops->flush_tcam(chip); 3947 + } 3948 + 3948 3949 static void mv88e6xxx_teardown(struct dsa_switch *ds) 3949 3950 { 3950 3951 struct mv88e6xxx_chip *chip = ds->priv; ··· 3962 3947 mv88e6xxx_teardown_devlink_regions_global(ds); 3963 3948 mv88e6xxx_hwtstamp_free(chip); 3964 3949 mv88e6xxx_ptp_free(chip); 3950 + mv88e6xxx_flower_teardown(chip); 3965 3951 mv88e6xxx_mdios_unregister(chip); 3966 3952 } 3967 3953 ··· 4096 4080 } 4097 4081 4098 4082 err = mv88e6xxx_stats_setup(chip); 4083 + if (err) 4084 + goto unlock; 4085 + 4086 + err = mv88e6xxx_tcam_setup(chip); 4099 4087 if (err) 4100 4088 goto unlock; 4101 4089 ··· 5154 5134 .ptp_ops = &mv88e6390_ptp_ops, 5155 5135 .phylink_get_caps = mv88e6390_phylink_get_caps, 5156 5136 .pcs_ops = &mv88e6390_pcs_ops, 5137 + .tcam_ops = &mv88e6390_tcam_ops, 5157 5138 }; 5158 5139 5159 5140 static const struct mv88e6xxx_ops mv88e6320_ops = { ··· 5546 5525 .serdes_get_regs = mv88e6390_serdes_get_regs, 5547 5526 .phylink_get_caps = mv88e6390_phylink_get_caps, 5548 5527 .pcs_ops = &mv88e6390_pcs_ops, 5528 + .tcam_ops = &mv88e6390_tcam_ops, 5549 5529 }; 5550 5530 5551 5531 static const struct mv88e6xxx_ops mv88e6390x_ops = { ··· 5643 5621 .port_set_cmode = mv88e6393x_port_set_cmode, 5644 5622 .port_setup_message_port = mv88e6xxx_setup_message_port, 5645 5623 .port_set_upstream_port = mv88e6393x_port_set_upstream_port, 5624 + .port_enable_tcam = mv88e6xxx_port_enable_tcam, 5646 5625 .stats_snapshot = mv88e6390_g1_stats_snapshot, 5647 5626 .stats_set_histogram = mv88e6390_g1_stats_set_histogram, 5648 5627 .stats_get_sset_count = mv88e6320_stats_get_sset_count, ··· 5675 5652 .ptp_ops = &mv88e6352_ptp_ops, 5676 5653 .phylink_get_caps = mv88e6393x_phylink_get_caps, 5677 5654 .pcs_ops = &mv88e6393x_pcs_ops, 5655 + .tcam_ops = &mv88e6393_tcam_ops, 5678 5656 }; 5679 5657 5680 5658 static const struct mv88e6xxx_info mv88e6xxx_table[] = { ··· 6149 6125 .num_databases = 4096, 6150 6126 .num_ports = 11, /* 10 + Z80 */ 6151 6127 .num_internal_phys = 8, 6128 + .num_tcam_entries = 256, 6152 6129 .internal_phys_offset = 1, 6153 6130 .max_vid = 8191, 6154 6131 .max_sid = 63, ··· 6157 6132 .phy_base_addr = 0x0, 6158 6133 .global1_addr = 0x1b, 6159 6134 .global2_addr = 0x1c, 6135 + .tcam_addr = 0x1f, 6160 6136 .age_time_coeff = 3750, 6161 6137 .g1_irqs = 10, 6162 6138 .g2_irqs = 14, ··· 6253 6227 .num_ports = 11, /* 10 + Z80 */ 6254 6228 .num_internal_phys = 9, 6255 6229 .num_gpio = 16, 6230 + .num_tcam_entries = 256, 6256 6231 .max_vid = 8191, 6257 6232 .max_sid = 63, 6258 6233 .port_base_addr = 0x0, 6259 6234 .phy_base_addr = 0x0, 6260 6235 .global1_addr = 0x1b, 6261 6236 .global2_addr = 0x1c, 6237 + .tcam_addr = 0x1f, 6262 6238 .age_time_coeff = 3750, 6263 6239 .g1_irqs = 9, 6264 6240 .g2_irqs = 14, ··· 6467 6439 .num_ports = 11, /* 10 + Z80 */ 6468 6440 .num_internal_phys = 9, 6469 6441 .num_gpio = 16, 6442 + .num_tcam_entries = 256, 6470 6443 .max_vid = 8191, 6471 6444 .max_sid = 63, 6472 6445 .port_base_addr = 0x0, 6473 6446 .phy_base_addr = 0x0, 6474 6447 .global1_addr = 0x1b, 6475 6448 .global2_addr = 0x1c, 6449 + .tcam_addr = 0x1f, 6476 6450 .age_time_coeff = 3750, 6477 6451 .g1_irqs = 9, 6478 6452 .g2_irqs = 14, ··· 6520 6490 .num_databases = 4096, 6521 6491 .num_ports = 11, /* 10 + Z80 */ 6522 6492 .num_internal_phys = 8, 6493 + .num_tcam_entries = 256, 6523 6494 .internal_phys_offset = 1, 6524 6495 .max_vid = 8191, 6525 6496 .max_sid = 63, ··· 6528 6497 .phy_base_addr = 0x0, 6529 6498 .global1_addr = 0x1b, 6530 6499 .global2_addr = 0x1c, 6500 + .tcam_addr = 0x1f, 6531 6501 .age_time_coeff = 3750, 6532 6502 .g1_irqs = 10, 6533 6503 .g2_irqs = 14, ··· 6621 6589 INIT_LIST_HEAD(&chip->mdios); 6622 6590 idr_init(&chip->policies); 6623 6591 INIT_LIST_HEAD(&chip->msts); 6592 + INIT_LIST_HEAD(&chip->tcam.entries); 6624 6593 6625 6594 return chip; 6626 6595 } ··· 7217 7184 .port_hwtstamp_get = mv88e6xxx_port_hwtstamp_get, 7218 7185 .port_txtstamp = mv88e6xxx_port_txtstamp, 7219 7186 .port_rxtstamp = mv88e6xxx_port_rxtstamp, 7187 + .cls_flower_add = mv88e6xxx_cls_flower_add, 7188 + .cls_flower_del = mv88e6xxx_cls_flower_del, 7220 7189 .get_ts_info = mv88e6xxx_get_ts_info, 7221 7190 .devlink_param_get = mv88e6xxx_devlink_param_get, 7222 7191 .devlink_param_set = mv88e6xxx_devlink_param_set,
+52
drivers/net/dsa/mv88e6xxx/chip.h
··· 135 135 unsigned int num_ports; 136 136 unsigned int num_internal_phys; 137 137 unsigned int num_gpio; 138 + unsigned int num_tcam_entries; 138 139 unsigned int max_vid; 139 140 unsigned int max_sid; 140 141 unsigned int port_base_addr; 141 142 unsigned int phy_base_addr; 142 143 unsigned int global1_addr; 143 144 unsigned int global2_addr; 145 + unsigned int tcam_addr; 144 146 unsigned int age_time_coeff; 145 147 unsigned int g1_irqs; 146 148 unsigned int g2_irqs; ··· 212 210 struct mv88e6xxx_ptp_ops; 213 211 struct mv88e6xxx_pcs_ops; 214 212 struct mv88e6xxx_cc_coeffs; 213 + struct mv88e6xxx_tcam_ops; 215 214 216 215 struct mv88e6xxx_irq { 217 216 u16 masked; ··· 342 339 int type; 343 340 }; 344 341 342 + struct mv88e6xxx_tcam { 343 + struct list_head entries; 344 + }; 345 + 345 346 struct mv88e6xxx_chip { 346 347 const struct mv88e6xxx_info *info; 347 348 ··· 451 444 452 445 /* FID map */ 453 446 DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); 447 + 448 + /* TCAM entries */ 449 + struct mv88e6xxx_tcam tcam; 450 + }; 451 + 452 + #define TCAM_MATCH_SIZE 96 453 + 454 + struct mv88e6xxx_tcam_key { 455 + u16 spv; 456 + u16 spv_mask; 457 + 458 + u8 frame_data[TCAM_MATCH_SIZE]; 459 + u8 frame_mask[TCAM_MATCH_SIZE]; 460 + }; 461 + 462 + struct mv88e6xxx_tcam_action { 463 + u8 dpv_mode; 464 + u16 dpv; 465 + }; 466 + 467 + struct mv88e6xxx_tcam_entry { 468 + struct list_head list; 469 + unsigned long cookie; 470 + u32 prio; 471 + u8 hw_idx; 472 + 473 + struct mv88e6xxx_tcam_key key; 474 + struct mv88e6xxx_tcam_action action; 475 + 454 476 }; 455 477 456 478 struct mv88e6xxx_bus_ops { ··· 714 678 715 679 /* Max Frame Size */ 716 680 int (*set_max_frame_size)(struct mv88e6xxx_chip *chip, int mtu); 681 + 682 + int (*port_enable_tcam)(struct mv88e6xxx_chip *chip, int port); 683 + 684 + /* Ternary Content Addressable Memory operations */ 685 + const struct mv88e6xxx_tcam_ops *tcam_ops; 717 686 }; 718 687 719 688 struct mv88e6xxx_irq_ops { ··· 793 752 794 753 }; 795 754 755 + struct mv88e6xxx_tcam_ops { 756 + int (*entry_add)(struct mv88e6xxx_chip *chip, 757 + struct mv88e6xxx_tcam_entry *entry, u8 idx); 758 + int (*flush_tcam)(struct mv88e6xxx_chip *chip); 759 + }; 760 + 796 761 static inline bool mv88e6xxx_has_stu(struct mv88e6xxx_chip *chip) 797 762 { 798 763 return chip->info->max_sid > 0 && ··· 814 767 static inline bool mv88e6xxx_has_lag(struct mv88e6xxx_chip *chip) 815 768 { 816 769 return !!chip->info->global2_addr; 770 + } 771 + 772 + static inline bool mv88e6xxx_has_tcam(struct mv88e6xxx_chip *chip) 773 + { 774 + return !!chip->info->tcam_addr; 817 775 } 818 776 819 777 static inline unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_chip *chip)
+27 -1
drivers/net/dsa/mv88e6xxx/port.c
··· 1380 1380 1381 1381 int mv88e6xxx_port_disable_pri_override(struct mv88e6xxx_chip *chip, int port) 1382 1382 { 1383 - return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_PRI_OVERRIDE, 0); 1383 + u16 reg; 1384 + int err; 1385 + 1386 + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_PRI_OVERRIDE, 1387 + &reg); 1388 + if (err) 1389 + return err; 1390 + 1391 + reg &= MV88E6XXX_PORT_PRI_OVERRIDE_TCAM_MODE_MASK; 1392 + return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_PRI_OVERRIDE, 1393 + reg); 1394 + } 1395 + 1396 + int mv88e6xxx_port_enable_tcam(struct mv88e6xxx_chip *chip, int port) 1397 + { 1398 + u16 reg; 1399 + int err; 1400 + 1401 + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_PRI_OVERRIDE, 1402 + &reg); 1403 + if (err) 1404 + return err; 1405 + 1406 + reg &= ~MV88E6XXX_PORT_PRI_OVERRIDE_TCAM_MODE_MASK; 1407 + reg |= MV88E6XXX_PORT_PRI_OVERRIDE_TCAM_MODE_96_BYTE; 1408 + return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_PRI_OVERRIDE, 1409 + reg); 1384 1410 } 1385 1411 1386 1412 /* Offset 0x0E: Policy & MGMT Control Register for FAMILY 6191X 6193X 6393X */
+6 -1
drivers/net/dsa/mv88e6xxx/port.h
··· 254 254 #define MV88E6XXX_PORT_ATU_CTL 0x0c 255 255 256 256 /* Offset 0x0D: Priority Override Register */ 257 - #define MV88E6XXX_PORT_PRI_OVERRIDE 0x0d 257 + #define MV88E6XXX_PORT_PRI_OVERRIDE 0x0d 258 + #define MV88E6XXX_PORT_PRI_OVERRIDE_TCAM_MODE_MASK 0x0003 259 + #define MV88E6XXX_PORT_PRI_OVERRIDE_TCAM_MODE_48_BYTE 0x0001 260 + #define MV88E6XXX_PORT_PRI_OVERRIDE_TCAM_MODE_96_BYTE 0x0002 258 261 259 262 /* Offset 0x0E: Policy Control Register */ 260 263 #define MV88E6XXX_PORT_POLICY_CTL 0x0e ··· 610 607 int mv88e6xxx_port_hidden_wait(struct mv88e6xxx_chip *chip); 611 608 int mv88e6xxx_port_hidden_read(struct mv88e6xxx_chip *chip, int block, int port, 612 609 int reg, u16 *val); 610 + 611 + int mv88e6xxx_port_enable_tcam(struct mv88e6xxx_chip *chip, int port); 613 612 614 613 #endif /* _MV88E6XXX_PORT_H */
+338
drivers/net/dsa/mv88e6xxx/tcam.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Marvell 88E6xxx Switch TCAM support 4 + * 5 + * Copyright (c) 2026 Luminex Network Intelligence 6 + */ 7 + 8 + #include "linux/list.h" 9 + 10 + #include "chip.h" 11 + #include "tcam.h" 12 + 13 + /* TCAM operatation register */ 14 + #define MV88E6XXX_TCAM_OP 0x00 15 + #define MV88E6XXX_TCAM_OP_BUSY 0x8000 16 + #define MV88E6XXX_TCAM_OP_OP_MASK 0x7000 17 + #define MV88E6XXX_TCAM_OP_OP_FLUSH_ALL 0x1000 18 + #define MV88E6XXX_TCAM_OP_OP_FLUSH 0x2000 19 + #define MV88E6XXX_TCAM_OP_OP_LOAD 0x3000 20 + #define MV88E6XXX_TCAM_OP_OP_GET_NEXT 0x4000 21 + #define MV88E6XXX_TCAM_OP_OP_READ 0x5000 22 + 23 + /* TCAM extension register */ 24 + #define MV88E6XXX_TCAM_EXTENSION 0x01 25 + 26 + /* TCAM keys register 1 */ 27 + #define MV88E6XXX_TCAM_KEYS1 0x02 28 + #define MV88E6XXX_TCAM_KEYS1_FT_MASK 0xC000 29 + #define MV88E6XXX_TCAM_KEYS1_SPV_MASK 0x0007 30 + #define MV88E6XXX_TCAM_KEYS1_SPV_MASK_MASK 0x0700 31 + 32 + /* TCAM keys register 2 */ 33 + #define MV88E6XXX_TCAM_KEYS2 0x03 34 + #define MV88E6XXX_TCAM_KEYS2_SPV_MASK 0x00ff 35 + #define MV88E6XXX_TCAM_KEYS2_SPV_MASK_MASK 0xff00 36 + 37 + #define MV88E6XXX_TCAM_ING_ACT3 0x04 38 + #define MV88E6XXX_TCAM_ING_ACT3_SF 0x0800 39 + #define MV88E6XXX_TCAM_ING_ACT3_DPV_MASK 0x07ff 40 + 41 + #define MV88E6XXX_TCAM_ING_ACT5 0x06 42 + #define MV88E6XXX_TCAM_ING_ACT5_DPV_MODE_MASK 0xc000 43 + 44 + static int mv88e6xxx_tcam_write(struct mv88e6xxx_chip *chip, int reg, u16 val) 45 + { 46 + return mv88e6xxx_write(chip, chip->info->tcam_addr, reg, val); 47 + } 48 + 49 + static int mv88e6xxx_tcam_wait(struct mv88e6xxx_chip *chip) 50 + { 51 + int bit = __bf_shf(MV88E6XXX_TCAM_OP_BUSY); 52 + 53 + return mv88e6xxx_wait_bit(chip, chip->info->tcam_addr, 54 + MV88E6XXX_TCAM_OP, bit, 0); 55 + } 56 + 57 + static int mv88e6xxx_tcam_read_page(struct mv88e6xxx_chip *chip, u8 page, 58 + u8 entry) 59 + 60 + { 61 + u16 val = MV88E6XXX_TCAM_OP_BUSY | MV88E6XXX_TCAM_OP_OP_READ | 62 + (page & 0x3) << 10 | entry; 63 + int err; 64 + 65 + err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_OP, val); 66 + if (err) 67 + return err; 68 + 69 + return mv88e6xxx_tcam_wait(chip); 70 + } 71 + 72 + static int mv88e6xxx_tcam_load_page(struct mv88e6xxx_chip *chip, u8 page, 73 + u8 entry) 74 + { 75 + u16 val = MV88E6XXX_TCAM_OP_BUSY | MV88E6XXX_TCAM_OP_OP_LOAD | 76 + (page & 0x3) << 10 | entry; 77 + int err; 78 + 79 + err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_OP, val); 80 + if (err) 81 + return err; 82 + 83 + return mv88e6xxx_tcam_wait(chip); 84 + } 85 + 86 + static int mv88e6xxx_tcam_flush_entry(struct mv88e6xxx_chip *chip, u8 entry) 87 + { 88 + u16 val = MV88E6XXX_TCAM_OP_BUSY | MV88E6XXX_TCAM_OP_OP_FLUSH | entry; 89 + int err; 90 + 91 + err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_OP, val); 92 + if (err) 93 + return err; 94 + 95 + return mv88e6xxx_tcam_wait(chip); 96 + } 97 + 98 + static int mv88e6xxx_tcam_flush_all(struct mv88e6xxx_chip *chip) 99 + { 100 + u16 val = MV88E6XXX_TCAM_OP_BUSY | MV88E6XXX_TCAM_OP_OP_FLUSH_ALL; 101 + int err; 102 + 103 + err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_OP, val); 104 + if (err) 105 + return err; 106 + 107 + return mv88e6xxx_tcam_wait(chip); 108 + } 109 + 110 + struct mv88e6xxx_tcam_entry * 111 + mv88e6xxx_tcam_entry_find(struct mv88e6xxx_chip *chip, unsigned long cookie) 112 + { 113 + struct mv88e6xxx_tcam_entry *entry; 114 + 115 + list_for_each_entry(entry, &chip->tcam.entries, list) 116 + if (entry->cookie == cookie) 117 + return entry; 118 + 119 + return NULL; 120 + } 121 + 122 + /* insert tcam entry in ordered list and move existing entries if necessary */ 123 + static int mv88e6xxx_tcam_insert_entry(struct mv88e6xxx_chip *chip, 124 + struct mv88e6xxx_tcam_entry *entry) 125 + { 126 + struct mv88e6xxx_tcam_entry *elem; 127 + struct list_head *hpos; 128 + int err; 129 + 130 + list_for_each_prev(hpos, &chip->tcam.entries) { 131 + u8 move_idx; 132 + 133 + elem = list_entry(hpos, struct mv88e6xxx_tcam_entry, list); 134 + if (entry->prio >= elem->prio) 135 + break; 136 + 137 + move_idx = elem->hw_idx + 1; 138 + 139 + err = mv88e6xxx_tcam_flush_entry(chip, move_idx); 140 + if (err) 141 + return err; 142 + 143 + err = chip->info->ops->tcam_ops->entry_add(chip, elem, 144 + move_idx); 145 + if (err) 146 + return err; 147 + 148 + elem->hw_idx = move_idx; 149 + } 150 + 151 + if (list_is_head(hpos, &chip->tcam.entries)) { 152 + entry->hw_idx = 0; 153 + } else { 154 + elem = list_entry(hpos, struct mv88e6xxx_tcam_entry, list); 155 + entry->hw_idx = elem->hw_idx + 1; 156 + } 157 + list_add(&entry->list, hpos); 158 + return 0; 159 + } 160 + 161 + int mv88e6xxx_tcam_entry_add(struct mv88e6xxx_chip *chip, 162 + struct mv88e6xxx_tcam_entry *entry) 163 + { 164 + int err; 165 + struct mv88e6xxx_tcam_entry *last; 166 + 167 + last = list_last_entry_or_null(&chip->tcam.entries, 168 + struct mv88e6xxx_tcam_entry, list); 169 + if (last && last->hw_idx == chip->info->num_tcam_entries - 1) 170 + return -ENOSPC; 171 + 172 + err = mv88e6xxx_tcam_insert_entry(chip, entry); 173 + if (err) 174 + return err; 175 + 176 + err = mv88e6xxx_tcam_flush_entry(chip, entry->hw_idx); 177 + if (err) 178 + goto unlink_out; 179 + 180 + err = chip->info->ops->tcam_ops->entry_add(chip, entry, entry->hw_idx); 181 + if (err) 182 + goto unlink_out; 183 + 184 + return 0; 185 + 186 + unlink_out: 187 + list_del(&entry->list); 188 + return err; 189 + } 190 + 191 + int mv88e6xxx_tcam_entry_del(struct mv88e6xxx_chip *chip, 192 + struct mv88e6xxx_tcam_entry *entry) 193 + { 194 + struct mv88e6xxx_tcam_entry *elem = entry; 195 + u8 move_idx = entry->hw_idx; 196 + int err; 197 + 198 + err = mv88e6xxx_tcam_flush_entry(chip, entry->hw_idx); 199 + if (err) 200 + return err; 201 + 202 + /* move entries that come after the deleted entry forward */ 203 + list_for_each_entry_continue(elem, &chip->tcam.entries, list) { 204 + u8 tmp_idx = elem->hw_idx; 205 + 206 + err = mv88e6xxx_tcam_flush_entry(chip, move_idx); 207 + if (err) 208 + break; 209 + 210 + err = chip->info->ops->tcam_ops->entry_add(chip, elem, 211 + move_idx); 212 + if (err) 213 + break; 214 + 215 + elem->hw_idx = move_idx; 216 + move_idx = tmp_idx; 217 + 218 + /* flush the last entry after moving entries */ 219 + if (list_is_last(&elem->list, &chip->tcam.entries)) 220 + err = mv88e6xxx_tcam_flush_entry(chip, tmp_idx); 221 + } 222 + 223 + list_del(&entry->list); 224 + kfree(entry); 225 + return err; 226 + } 227 + 228 + static int mv88e6390_tcam_entry_add(struct mv88e6xxx_chip *chip, 229 + struct mv88e6xxx_tcam_entry *entry, u8 idx) 230 + { 231 + int err = 0; 232 + int i; 233 + u16 val, spv_mask, spv; 234 + 235 + err = mv88e6xxx_tcam_read_page(chip, 2, idx); 236 + if (err) 237 + return err; 238 + if (entry->action.dpv_mode != 0) { 239 + val = MV88E6XXX_TCAM_ING_ACT3_SF | 240 + (entry->action.dpv & MV88E6XXX_TCAM_ING_ACT3_DPV_MASK); 241 + 242 + err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_ING_ACT3, val); 243 + if (err) 244 + return err; 245 + 246 + val = entry->action.dpv_mode << 14; 247 + err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_ING_ACT5, val); 248 + if (err) 249 + return err; 250 + } 251 + err = mv88e6xxx_tcam_load_page(chip, 2, idx); 252 + if (err) 253 + return err; 254 + 255 + err = mv88e6xxx_tcam_read_page(chip, 1, idx); 256 + if (err) 257 + return err; 258 + 259 + for (i = PAGE0_MATCH_SIZE; 260 + i < PAGE0_MATCH_SIZE + PAGE1_MATCH_SIZE; i++) { 261 + if (entry->key.frame_mask[i]) { 262 + val = entry->key.frame_mask[i] << 8 | 263 + entry->key.frame_data[i]; 264 + 265 + err = mv88e6xxx_tcam_write(chip, 266 + i - PAGE0_MATCH_SIZE + 2, 267 + val); 268 + if (err) 269 + return err; 270 + } 271 + } 272 + err = mv88e6xxx_tcam_load_page(chip, 1, idx); 273 + if (err) 274 + return err; 275 + 276 + err = mv88e6xxx_tcam_read_page(chip, 0, idx); 277 + if (err) 278 + return err; 279 + 280 + for (i = 0; i < PAGE0_MATCH_SIZE; i++) { 281 + if (entry->key.frame_mask[i]) { 282 + val = entry->key.frame_mask[i] << 8 | 283 + entry->key.frame_data[i]; 284 + 285 + err = mv88e6xxx_tcam_write(chip, i + 6, val); 286 + if (err) 287 + return err; 288 + } 289 + } 290 + 291 + spv_mask = entry->key.spv_mask & mv88e6xxx_port_mask(chip); 292 + spv = entry->key.spv & mv88e6xxx_port_mask(chip); 293 + /* frame type mask bits must be set for a valid entry */ 294 + val = MV88E6XXX_TCAM_KEYS1_FT_MASK | 295 + (spv_mask & MV88E6XXX_TCAM_KEYS1_SPV_MASK_MASK) | 296 + ((spv >> 8) & MV88E6XXX_TCAM_KEYS1_SPV_MASK); 297 + err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_KEYS1, val); 298 + if (err) 299 + return err; 300 + 301 + val = ((spv_mask << 8) & MV88E6XXX_TCAM_KEYS2_SPV_MASK_MASK) | 302 + (spv & MV88E6XXX_TCAM_KEYS2_SPV_MASK); 303 + err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_KEYS2, val); 304 + if (err) 305 + return err; 306 + 307 + err = mv88e6xxx_tcam_load_page(chip, 0, idx); 308 + if (err) 309 + return err; 310 + 311 + entry->hw_idx = idx; 312 + return 0; 313 + } 314 + 315 + static int mv88e6393_tcam_entry_add(struct mv88e6xxx_chip *chip, 316 + struct mv88e6xxx_tcam_entry *entry, u8 idx) 317 + { 318 + int err; 319 + 320 + /* select block 0 port 0, then adding an entry is the same as 6390 as 321 + * other blocks aren't used at the moment 322 + */ 323 + err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_EXTENSION, 0x00); 324 + if (err) 325 + return err; 326 + 327 + return mv88e6390_tcam_entry_add(chip, entry, idx); 328 + } 329 + 330 + const struct mv88e6xxx_tcam_ops mv88e6390_tcam_ops = { 331 + .entry_add = mv88e6390_tcam_entry_add, 332 + .flush_tcam = mv88e6xxx_tcam_flush_all, 333 + }; 334 + 335 + const struct mv88e6xxx_tcam_ops mv88e6393_tcam_ops = { 336 + .entry_add = mv88e6393_tcam_entry_add, 337 + .flush_tcam = mv88e6xxx_tcam_flush_all, 338 + };
+41
drivers/net/dsa/mv88e6xxx/tcam.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + /* 4 + * Copyright (c) 2026 Luminex Network Intelligence 5 + */ 6 + #ifndef _MV88E6XXX_TCAM_H_ 7 + #define _MV88E6XXX_TCAM_H_ 8 + 9 + #define PAGE0_MATCH_SIZE 22 10 + #define PAGE1_MATCH_SIZE 26 11 + 12 + #define DPV_MODE_NOP 0x0 13 + #define DPV_MODE_AND 0x1 14 + #define DPV_MODE_OR 0x2 15 + #define DPV_MODE_REPLACE 0x3 16 + 17 + int mv88e6xxx_tcam_entry_add(struct mv88e6xxx_chip *chip, 18 + struct mv88e6xxx_tcam_entry *entry); 19 + int mv88e6xxx_tcam_entry_del(struct mv88e6xxx_chip *chip, 20 + struct mv88e6xxx_tcam_entry *entry); 21 + struct mv88e6xxx_tcam_entry * 22 + mv88e6xxx_tcam_entry_find(struct mv88e6xxx_chip *chip, unsigned long cookie); 23 + #define mv88e6xxx_tcam_match_set(key, _offset, data, mask) \ 24 + do { \ 25 + typeof(_offset) (offset) = (_offset); \ 26 + BUILD_BUG_ON((offset) + sizeof((data)) > TCAM_MATCH_SIZE); \ 27 + __mv88e6xxx_tcam_match_set(key, offset, sizeof(data), \ 28 + (u8 *)&(data), (u8 *)&(mask)); \ 29 + } while (0) 30 + 31 + static inline void __mv88e6xxx_tcam_match_set(struct mv88e6xxx_tcam_key *key, 32 + unsigned int offset, size_t size, 33 + u8 *data, u8 *mask) 34 + { 35 + memcpy(&key->frame_data[offset], data, size); 36 + memcpy(&key->frame_mask[offset], mask, size); 37 + } 38 + 39 + extern const struct mv88e6xxx_tcam_ops mv88e6390_tcam_ops; 40 + extern const struct mv88e6xxx_tcam_ops mv88e6393_tcam_ops; 41 + #endif
+167
drivers/net/dsa/mv88e6xxx/tcflower.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Marvell 88E6xxx Switch flower support 4 + * 5 + * Copyright (c) 2026 Luminex Network Intelligence 6 + */ 7 + 8 + #include "chip.h" 9 + #include "tcflower.h" 10 + #include "tcam.h" 11 + 12 + #define MV88E6XXX_ETHTYPE_OFFSET 16 13 + #define MV88E6XXX_IP_PROTO_OFFSET 27 14 + #define MV88E6XXX_IPV4_SRC_OFFSET 30 15 + #define MV88E6XXX_IPV4_DST_OFFSET 34 16 + 17 + static int mv88e6xxx_flower_parse_key(struct mv88e6xxx_chip *chip, 18 + struct netlink_ext_ack *extack, 19 + struct flow_cls_offload *cls, 20 + struct mv88e6xxx_tcam_key *key) 21 + { 22 + struct flow_rule *rule = flow_cls_offload_flow_rule(cls); 23 + struct flow_dissector *dissector = rule->match.dissector; 24 + u16 addr_type = 0; 25 + 26 + if (dissector->used_keys & 27 + ~(BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) | 28 + BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) | 29 + BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS))) { 30 + NL_SET_ERR_MSG_MOD(extack, 31 + "Unsupported keys used"); 32 + return -EOPNOTSUPP; 33 + } 34 + 35 + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) { 36 + struct flow_match_control match; 37 + 38 + flow_rule_match_control(rule, &match); 39 + addr_type = match.key->addr_type; 40 + 41 + if (flow_rule_has_control_flags(match.mask->flags, 42 + cls->common.extack)) 43 + return -EOPNOTSUPP; 44 + } 45 + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { 46 + struct flow_match_basic match; 47 + 48 + flow_rule_match_basic(rule, &match); 49 + mv88e6xxx_tcam_match_set(key, MV88E6XXX_ETHTYPE_OFFSET, 50 + match.key->n_proto, 51 + match.mask->n_proto); 52 + mv88e6xxx_tcam_match_set(key, MV88E6XXX_IP_PROTO_OFFSET, 53 + match.key->ip_proto, 54 + match.mask->ip_proto); 55 + } 56 + 57 + if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) { 58 + struct flow_match_ipv4_addrs match; 59 + 60 + flow_rule_match_ipv4_addrs(cls->rule, &match); 61 + mv88e6xxx_tcam_match_set(key, MV88E6XXX_IPV4_SRC_OFFSET, 62 + match.key->src, 63 + match.mask->src); 64 + mv88e6xxx_tcam_match_set(key, MV88E6XXX_IPV4_DST_OFFSET, 65 + match.key->dst, 66 + match.mask->dst); 67 + } 68 + 69 + return 0; 70 + } 71 + 72 + int mv88e6xxx_cls_flower_add(struct dsa_switch *ds, int port, 73 + struct flow_cls_offload *cls, bool ingress) 74 + { 75 + struct flow_rule *rule = flow_cls_offload_flow_rule(cls); 76 + struct netlink_ext_ack *extack = cls->common.extack; 77 + struct mv88e6xxx_chip *chip = ds->priv; 78 + struct mv88e6xxx_tcam_key key = { 0 }; 79 + const struct flow_action_entry *act; 80 + unsigned long cookie = cls->cookie; 81 + struct mv88e6xxx_tcam_entry *entry; 82 + int err, i; 83 + 84 + if (!mv88e6xxx_has_tcam(chip)) { 85 + NL_SET_ERR_MSG_MOD(extack, "hardware offload not supported"); 86 + return -EOPNOTSUPP; 87 + } 88 + 89 + err = mv88e6xxx_flower_parse_key(chip, extack, cls, &key); 90 + if (err) 91 + return err; 92 + 93 + mv88e6xxx_reg_lock(chip); 94 + entry = mv88e6xxx_tcam_entry_find(chip, cookie); 95 + if (entry) { 96 + err = -EEXIST; 97 + goto err_unlock; 98 + } 99 + 100 + entry = kzalloc(sizeof(*entry), GFP_KERNEL); 101 + if (!entry) { 102 + err = -ENOMEM; 103 + goto err_unlock; 104 + } 105 + 106 + entry->cookie = cookie; 107 + entry->prio = cls->common.prio; 108 + entry->key = key; 109 + 110 + flow_action_for_each(i, act, &rule->action) { 111 + switch (act->id) { 112 + case FLOW_ACTION_TRAP: { 113 + int cpu = dsa_upstream_port(ds, port); 114 + 115 + entry->action.dpv_mode = DPV_MODE_REPLACE; 116 + entry->action.dpv = BIT(cpu); 117 + break; 118 + } 119 + default: 120 + NL_SET_ERR_MSG_MOD(extack, "action not supported"); 121 + err = -EOPNOTSUPP; 122 + goto err_free_entry; 123 + } 124 + } 125 + 126 + entry->key.spv = BIT(port); 127 + entry->key.spv_mask = mv88e6xxx_port_mask(chip); 128 + 129 + err = mv88e6xxx_tcam_entry_add(chip, entry); 130 + if (err) 131 + goto err_free_entry; 132 + 133 + mv88e6xxx_reg_unlock(chip); 134 + return 0; 135 + 136 + err_free_entry: 137 + kfree(entry); 138 + err_unlock: 139 + mv88e6xxx_reg_unlock(chip); 140 + return err; 141 + } 142 + 143 + int mv88e6xxx_cls_flower_del(struct dsa_switch *ds, int port, 144 + struct flow_cls_offload *cls, bool ingress) 145 + { 146 + struct mv88e6xxx_chip *chip = ds->priv; 147 + struct mv88e6xxx_tcam_entry *entry; 148 + int err = 0; 149 + 150 + mv88e6xxx_reg_lock(chip); 151 + entry = mv88e6xxx_tcam_entry_find(chip, cls->cookie); 152 + 153 + if (entry) 154 + err = mv88e6xxx_tcam_entry_del(chip, entry); 155 + mv88e6xxx_reg_unlock(chip); 156 + return err; 157 + } 158 + 159 + void mv88e6xxx_flower_teardown(struct mv88e6xxx_chip *chip) 160 + { 161 + struct mv88e6xxx_tcam_entry *pos, *n; 162 + 163 + list_for_each_entry_safe(pos, n, &chip->tcam.entries, list) { 164 + list_del(&pos->list); 165 + kfree(pos); 166 + } 167 + }
+14
drivers/net/dsa/mv88e6xxx/tcflower.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + /* 4 + * Copyright (c) 2026 Luminex Network Intelligence 5 + */ 6 + #ifndef _MV88E6XXX_TCFLOWER_H_ 7 + #define _MV88E6XXX_TCFLOWER_H_ 8 + 9 + int mv88e6xxx_cls_flower_add(struct dsa_switch *ds, int port, 10 + struct flow_cls_offload *cls, bool ingress); 11 + int mv88e6xxx_cls_flower_del(struct dsa_switch *ds, int port, 12 + struct flow_cls_offload *cls, bool ingress); 13 + void mv88e6xxx_flower_teardown(struct mv88e6xxx_chip *chip); 14 + #endif
+4 -8
net/sched/cls_flower.c
··· 59 59 struct flow_dissector_key_eth_addrs eth; 60 60 struct flow_dissector_key_vlan vlan; 61 61 struct flow_dissector_key_vlan cvlan; 62 - union { 63 - struct flow_dissector_key_ipv4_addrs ipv4; 64 - struct flow_dissector_key_ipv6_addrs ipv6; 65 - }; 62 + struct flow_dissector_key_ipv4_addrs ipv4; 63 + struct flow_dissector_key_ipv6_addrs ipv6; 66 64 struct flow_dissector_key_ports tp; 67 65 struct flow_dissector_key_icmp icmp; 68 66 struct flow_dissector_key_arp arp; 69 67 struct flow_dissector_key_keyid enc_key_id; 70 - union { 71 - struct flow_dissector_key_ipv4_addrs enc_ipv4; 72 - struct flow_dissector_key_ipv6_addrs enc_ipv6; 73 - }; 68 + struct flow_dissector_key_ipv4_addrs enc_ipv4; 69 + struct flow_dissector_key_ipv6_addrs enc_ipv6; 74 70 struct flow_dissector_key_ports enc_tp; 75 71 struct flow_dissector_key_mpls mpls; 76 72 struct flow_dissector_key_tcp tcp;