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 } 168