GNU Linux-libre 6.8.9-gnu
[releases.git] / drivers / net / dsa / microchip / ksz9477_tc_flower.c
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2023 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
3
4 #include "ksz9477.h"
5 #include "ksz9477_reg.h"
6 #include "ksz_common.h"
7
8 #define ETHER_TYPE_FULL_MASK            cpu_to_be16(~0)
9 #define KSZ9477_MAX_TC                  7
10
11 /**
12  * ksz9477_flower_parse_key_l2 - Parse Layer 2 key from flow rule and configure
13  *                               ACL entries accordingly.
14  * @dev: Pointer to the ksz_device.
15  * @port: Port number.
16  * @extack: Pointer to the netlink_ext_ack.
17  * @rule: Pointer to the flow_rule.
18  * @cookie: The cookie to associate with the entry.
19  * @prio: The priority of the entry.
20  *
21  * This function parses the Layer 2 key from the flow rule and configures
22  * the corresponding ACL entries. It checks for unsupported offloads and
23  * available entries before proceeding with the configuration.
24  *
25  * Returns: 0 on success or a negative error code on failure.
26  */
27 static int ksz9477_flower_parse_key_l2(struct ksz_device *dev, int port,
28                                        struct netlink_ext_ack *extack,
29                                        struct flow_rule *rule,
30                                        unsigned long cookie, u32 prio)
31 {
32         struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
33         struct flow_match_eth_addrs ematch;
34         struct ksz9477_acl_entries *acles;
35         int required_entries;
36         u8 *src_mac = NULL;
37         u8 *dst_mac = NULL;
38         u16 ethtype = 0;
39
40         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
41                 struct flow_match_basic match;
42
43                 flow_rule_match_basic(rule, &match);
44
45                 if (match.key->n_proto) {
46                         if (match.mask->n_proto != ETHER_TYPE_FULL_MASK) {
47                                 NL_SET_ERR_MSG_MOD(extack,
48                                                    "ethernet type mask must be a full mask");
49                                 return -EINVAL;
50                         }
51
52                         ethtype = be16_to_cpu(match.key->n_proto);
53                 }
54         }
55
56         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
57                 flow_rule_match_eth_addrs(rule, &ematch);
58
59                 if (!is_zero_ether_addr(ematch.key->src)) {
60                         if (!is_broadcast_ether_addr(ematch.mask->src))
61                                 goto not_full_mask_err;
62
63                         src_mac = ematch.key->src;
64                 }
65
66                 if (!is_zero_ether_addr(ematch.key->dst)) {
67                         if (!is_broadcast_ether_addr(ematch.mask->dst))
68                                 goto not_full_mask_err;
69
70                         dst_mac = ematch.key->dst;
71                 }
72         }
73
74         acles = &acl->acles;
75         /* ACL supports only one MAC per entry */
76         required_entries = src_mac && dst_mac ? 2 : 1;
77
78         /* Check if there are enough available entries */
79         if (acles->entries_count + required_entries > KSZ9477_ACL_MAX_ENTRIES) {
80                 NL_SET_ERR_MSG_MOD(extack, "ACL entry limit reached");
81                 return -EOPNOTSUPP;
82         }
83
84         ksz9477_acl_match_process_l2(dev, port, ethtype, src_mac, dst_mac,
85                                      cookie, prio);
86
87         return 0;
88
89 not_full_mask_err:
90         NL_SET_ERR_MSG_MOD(extack, "MAC address mask must be a full mask");
91         return -EOPNOTSUPP;
92 }
93
94 /**
95  * ksz9477_flower_parse_key - Parse flow rule keys for a specified port on a
96  *                            ksz_device.
97  * @dev: The ksz_device instance.
98  * @port: The port number to parse the flow rule keys for.
99  * @extack: The netlink extended ACK for reporting errors.
100  * @rule: The flow_rule to parse.
101  * @cookie: The cookie to associate with the entry.
102  * @prio: The priority of the entry.
103  *
104  * This function checks if the used keys in the flow rule are supported by
105  * the device and parses the L2 keys if they match. If unsupported keys are
106  * used, an error message is set in the extended ACK.
107  *
108  * Returns: 0 on success or a negative error code on failure.
109  */
110 static int ksz9477_flower_parse_key(struct ksz_device *dev, int port,
111                                     struct netlink_ext_ack *extack,
112                                     struct flow_rule *rule,
113                                     unsigned long cookie, u32 prio)
114 {
115         struct flow_dissector *dissector = rule->match.dissector;
116         int ret;
117
118         if (dissector->used_keys &
119             ~(BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
120               BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
121               BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL))) {
122                 NL_SET_ERR_MSG_MOD(extack,
123                                    "Unsupported keys used");
124                 return -EOPNOTSUPP;
125         }
126
127         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC) ||
128             flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
129                 ret = ksz9477_flower_parse_key_l2(dev, port, extack, rule,
130                                                   cookie, prio);
131                 if (ret)
132                         return ret;
133         }
134
135         return 0;
136 }
137
138 /**
139  * ksz9477_flower_parse_action - Parse flow rule actions for a specified port
140  *                               on a ksz_device.
141  * @dev: The ksz_device instance.
142  * @port: The port number to parse the flow rule actions for.
143  * @extack: The netlink extended ACK for reporting errors.
144  * @cls: The flow_cls_offload instance containing the flow rule.
145  * @entry_idx: The index of the ACL entry to store the action.
146  *
147  * This function checks if the actions in the flow rule are supported by
148  * the device. Currently, only actions that change priorities are supported.
149  * If unsupported actions are encountered, an error message is set in the
150  * extended ACK.
151  *
152  * Returns: 0 on success or a negative error code on failure.
153  */
154 static int ksz9477_flower_parse_action(struct ksz_device *dev, int port,
155                                        struct netlink_ext_ack *extack,
156                                        struct flow_cls_offload *cls,
157                                        int entry_idx)
158 {
159         struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
160         struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
161         const struct flow_action_entry *act;
162         struct ksz9477_acl_entry *entry;
163         bool prio_force = false;
164         u8 prio_val = 0;
165         int i;
166
167         if (TC_H_MIN(cls->classid)) {
168                 NL_SET_ERR_MSG_MOD(extack, "hw_tc is not supported. Use: action skbedit prio");
169                 return -EOPNOTSUPP;
170         }
171
172         flow_action_for_each(i, act, &rule->action) {
173                 switch (act->id) {
174                 case FLOW_ACTION_PRIORITY:
175                         if (act->priority > KSZ9477_MAX_TC) {
176                                 NL_SET_ERR_MSG_MOD(extack, "Priority value is too high");
177                                 return -EOPNOTSUPP;
178                         }
179                         prio_force = true;
180                         prio_val = act->priority;
181                         break;
182                 default:
183                         NL_SET_ERR_MSG_MOD(extack, "action not supported");
184                         return -EOPNOTSUPP;
185                 }
186         }
187
188         /* pick entry to store action */
189         entry = &acl->acles.entries[entry_idx];
190
191         ksz9477_acl_action_rule_cfg(entry->entry, prio_force, prio_val);
192         ksz9477_acl_processing_rule_set_action(entry->entry, entry_idx);
193
194         return 0;
195 }
196
197 /**
198  * ksz9477_cls_flower_add - Add a flow classification rule for a specified port
199  *                          on a ksz_device.
200  * @ds: The DSA switch instance.
201  * @port: The port number to add the flow classification rule to.
202  * @cls: The flow_cls_offload instance containing the flow rule.
203  * @ingress: A flag indicating if the rule is applied on the ingress path.
204  *
205  * This function adds a flow classification rule for a specified port on a
206  * ksz_device. It checks if the ACL offloading is supported and parses the flow
207  * keys and actions. If the ACL is not supported, it returns an error. If there
208  * are unprocessed entries, it parses the action for the rule.
209  *
210  * Returns: 0 on success or a negative error code on failure.
211  */
212 int ksz9477_cls_flower_add(struct dsa_switch *ds, int port,
213                            struct flow_cls_offload *cls, bool ingress)
214 {
215         struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
216         struct netlink_ext_ack *extack = cls->common.extack;
217         struct ksz_device *dev = ds->priv;
218         struct ksz9477_acl_priv *acl;
219         int action_entry_idx;
220         int ret;
221
222         acl = dev->ports[port].acl_priv;
223
224         if (!acl) {
225                 NL_SET_ERR_MSG_MOD(extack, "ACL offloading is not supported");
226                 return -EOPNOTSUPP;
227         }
228
229         /* A complex rule set can take multiple entries. Use first entry
230          * to store the action.
231          */
232         action_entry_idx = acl->acles.entries_count;
233
234         ret = ksz9477_flower_parse_key(dev, port, extack, rule, cls->cookie,
235                                        cls->common.prio);
236         if (ret)
237                 return ret;
238
239         ret = ksz9477_flower_parse_action(dev, port, extack, cls,
240                                           action_entry_idx);
241         if (ret)
242                 return ret;
243
244         ret = ksz9477_sort_acl_entries(dev, port);
245         if (ret)
246                 return ret;
247
248         return ksz9477_acl_write_list(dev, port);
249 }
250
251 /**
252  * ksz9477_cls_flower_del - Remove a flow classification rule for a specified
253  *                          port on a ksz_device.
254  * @ds: The DSA switch instance.
255  * @port: The port number to remove the flow classification rule from.
256  * @cls: The flow_cls_offload instance containing the flow rule.
257  * @ingress: A flag indicating if the rule is applied on the ingress path.
258  *
259  * This function removes a flow classification rule for a specified port on a
260  * ksz_device. It checks if the ACL is initialized, and if not, returns an
261  * error. If the ACL is initialized, it removes entries with the specified
262  * cookie and rewrites the ACL list.
263  *
264  * Returns: 0 on success or a negative error code on failure.
265  */
266 int ksz9477_cls_flower_del(struct dsa_switch *ds, int port,
267                            struct flow_cls_offload *cls, bool ingress)
268 {
269         unsigned long cookie = cls->cookie;
270         struct ksz_device *dev = ds->priv;
271         struct ksz9477_acl_priv *acl;
272
273         acl = dev->ports[port].acl_priv;
274
275         if (!acl)
276                 return -EOPNOTSUPP;
277
278         ksz9477_acl_remove_entries(dev, port, &acl->acles, cookie);
279
280         return ksz9477_acl_write_list(dev, port);
281 }