]> Git Repo - linux.git/blob - drivers/net/ethernet/mscc/ocelot_flower.c
drm/nouveau/kms: Don't change EDID when it hasn't actually changed
[linux.git] / drivers / net / ethernet / mscc / ocelot_flower.c
1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 /* Microsemi Ocelot Switch driver
3  * Copyright (c) 2019 Microsemi Corporation
4  */
5
6 #include <net/pkt_cls.h>
7 #include <net/tc_act/tc_gact.h>
8
9 #include "ocelot_vcap.h"
10
11 static int ocelot_flower_parse_action(struct flow_cls_offload *f,
12                                       struct ocelot_vcap_filter *filter)
13 {
14         const struct flow_action_entry *a;
15         u64 rate;
16         int i;
17
18         if (!flow_offload_has_one_action(&f->rule->action))
19                 return -EOPNOTSUPP;
20
21         if (!flow_action_basic_hw_stats_check(&f->rule->action,
22                                               f->common.extack))
23                 return -EOPNOTSUPP;
24
25         flow_action_for_each(i, a, &f->rule->action) {
26                 switch (a->id) {
27                 case FLOW_ACTION_DROP:
28                         filter->action = OCELOT_VCAP_ACTION_DROP;
29                         break;
30                 case FLOW_ACTION_TRAP:
31                         filter->action = OCELOT_VCAP_ACTION_TRAP;
32                         break;
33                 case FLOW_ACTION_POLICE:
34                         filter->action = OCELOT_VCAP_ACTION_POLICE;
35                         rate = a->police.rate_bytes_ps;
36                         filter->pol.rate = div_u64(rate, 1000) * 8;
37                         filter->pol.burst = a->police.burst;
38                         break;
39                 default:
40                         return -EOPNOTSUPP;
41                 }
42         }
43
44         return 0;
45 }
46
47 static int ocelot_flower_parse(struct flow_cls_offload *f,
48                                struct ocelot_vcap_filter *filter)
49 {
50         struct flow_rule *rule = flow_cls_offload_flow_rule(f);
51         struct flow_dissector *dissector = rule->match.dissector;
52         u16 proto = ntohs(f->common.protocol);
53         bool match_protocol = true;
54
55         if (dissector->used_keys &
56             ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
57               BIT(FLOW_DISSECTOR_KEY_BASIC) |
58               BIT(FLOW_DISSECTOR_KEY_PORTS) |
59               BIT(FLOW_DISSECTOR_KEY_VLAN) |
60               BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
61               BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
62               BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) {
63                 return -EOPNOTSUPP;
64         }
65
66         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
67                 struct flow_match_control match;
68
69                 flow_rule_match_control(rule, &match);
70         }
71
72         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
73                 struct flow_match_eth_addrs match;
74
75                 /* The hw support mac matches only for MAC_ETYPE key,
76                  * therefore if other matches(port, tcp flags, etc) are added
77                  * then just bail out
78                  */
79                 if ((dissector->used_keys &
80                     (BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
81                      BIT(FLOW_DISSECTOR_KEY_BASIC) |
82                      BIT(FLOW_DISSECTOR_KEY_CONTROL))) !=
83                     (BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
84                      BIT(FLOW_DISSECTOR_KEY_BASIC) |
85                      BIT(FLOW_DISSECTOR_KEY_CONTROL)))
86                         return -EOPNOTSUPP;
87
88                 flow_rule_match_eth_addrs(rule, &match);
89                 filter->key_type = OCELOT_VCAP_KEY_ETYPE;
90                 ether_addr_copy(filter->key.etype.dmac.value,
91                                 match.key->dst);
92                 ether_addr_copy(filter->key.etype.smac.value,
93                                 match.key->src);
94                 ether_addr_copy(filter->key.etype.dmac.mask,
95                                 match.mask->dst);
96                 ether_addr_copy(filter->key.etype.smac.mask,
97                                 match.mask->src);
98                 goto finished_key_parsing;
99         }
100
101         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
102                 struct flow_match_basic match;
103
104                 flow_rule_match_basic(rule, &match);
105                 if (ntohs(match.key->n_proto) == ETH_P_IP) {
106                         filter->key_type = OCELOT_VCAP_KEY_IPV4;
107                         filter->key.ipv4.proto.value[0] =
108                                 match.key->ip_proto;
109                         filter->key.ipv4.proto.mask[0] =
110                                 match.mask->ip_proto;
111                         match_protocol = false;
112                 }
113                 if (ntohs(match.key->n_proto) == ETH_P_IPV6) {
114                         filter->key_type = OCELOT_VCAP_KEY_IPV6;
115                         filter->key.ipv6.proto.value[0] =
116                                 match.key->ip_proto;
117                         filter->key.ipv6.proto.mask[0] =
118                                 match.mask->ip_proto;
119                         match_protocol = false;
120                 }
121         }
122
123         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS) &&
124             proto == ETH_P_IP) {
125                 struct flow_match_ipv4_addrs match;
126                 u8 *tmp;
127
128                 flow_rule_match_ipv4_addrs(rule, &match);
129                 tmp = &filter->key.ipv4.sip.value.addr[0];
130                 memcpy(tmp, &match.key->src, 4);
131
132                 tmp = &filter->key.ipv4.sip.mask.addr[0];
133                 memcpy(tmp, &match.mask->src, 4);
134
135                 tmp = &filter->key.ipv4.dip.value.addr[0];
136                 memcpy(tmp, &match.key->dst, 4);
137
138                 tmp = &filter->key.ipv4.dip.mask.addr[0];
139                 memcpy(tmp, &match.mask->dst, 4);
140                 match_protocol = false;
141         }
142
143         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS) &&
144             proto == ETH_P_IPV6) {
145                 return -EOPNOTSUPP;
146         }
147
148         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
149                 struct flow_match_ports match;
150
151                 flow_rule_match_ports(rule, &match);
152                 filter->key.ipv4.sport.value = ntohs(match.key->src);
153                 filter->key.ipv4.sport.mask = ntohs(match.mask->src);
154                 filter->key.ipv4.dport.value = ntohs(match.key->dst);
155                 filter->key.ipv4.dport.mask = ntohs(match.mask->dst);
156                 match_protocol = false;
157         }
158
159         if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
160                 struct flow_match_vlan match;
161
162                 flow_rule_match_vlan(rule, &match);
163                 filter->key_type = OCELOT_VCAP_KEY_ANY;
164                 filter->vlan.vid.value = match.key->vlan_id;
165                 filter->vlan.vid.mask = match.mask->vlan_id;
166                 filter->vlan.pcp.value[0] = match.key->vlan_priority;
167                 filter->vlan.pcp.mask[0] = match.mask->vlan_priority;
168                 match_protocol = false;
169         }
170
171 finished_key_parsing:
172         if (match_protocol && proto != ETH_P_ALL) {
173                 /* TODO: support SNAP, LLC etc */
174                 if (proto < ETH_P_802_3_MIN)
175                         return -EOPNOTSUPP;
176                 filter->key_type = OCELOT_VCAP_KEY_ETYPE;
177                 *(__be16 *)filter->key.etype.etype.value = htons(proto);
178                 *(__be16 *)filter->key.etype.etype.mask = htons(0xffff);
179         }
180         /* else, a filter of type OCELOT_VCAP_KEY_ANY is implicitly added */
181
182         filter->prio = f->common.prio;
183         filter->id = f->cookie;
184         return ocelot_flower_parse_action(f, filter);
185 }
186
187 static struct ocelot_vcap_filter
188 *ocelot_vcap_filter_create(struct ocelot *ocelot, int port,
189                          struct flow_cls_offload *f)
190 {
191         struct ocelot_vcap_filter *filter;
192
193         filter = kzalloc(sizeof(*filter), GFP_KERNEL);
194         if (!filter)
195                 return NULL;
196
197         filter->ingress_port_mask = BIT(port);
198         return filter;
199 }
200
201 int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
202                               struct flow_cls_offload *f, bool ingress)
203 {
204         struct ocelot_vcap_filter *filter;
205         int ret;
206
207         filter = ocelot_vcap_filter_create(ocelot, port, f);
208         if (!filter)
209                 return -ENOMEM;
210
211         ret = ocelot_flower_parse(f, filter);
212         if (ret) {
213                 kfree(filter);
214                 return ret;
215         }
216
217         return ocelot_vcap_filter_add(ocelot, filter, f->common.extack);
218 }
219 EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);
220
221 int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
222                               struct flow_cls_offload *f, bool ingress)
223 {
224         struct ocelot_vcap_filter filter;
225
226         filter.prio = f->common.prio;
227         filter.id = f->cookie;
228
229         return ocelot_vcap_filter_del(ocelot, &filter);
230 }
231 EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy);
232
233 int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,
234                             struct flow_cls_offload *f, bool ingress)
235 {
236         struct ocelot_vcap_filter filter;
237         int ret;
238
239         filter.prio = f->common.prio;
240         filter.id = f->cookie;
241         ret = ocelot_vcap_filter_stats_update(ocelot, &filter);
242         if (ret)
243                 return ret;
244
245         flow_stats_update(&f->stats, 0x0, filter.stats.pkts, 0, 0x0,
246                           FLOW_ACTION_HW_STATS_IMMEDIATE);
247         return 0;
248 }
249 EXPORT_SYMBOL_GPL(ocelot_cls_flower_stats);
This page took 0.047037 seconds and 4 git commands to generate.