xref: /linux/drivers/net/dsa/mv88e6xxx/tcflower.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
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