1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip VCAP API
3 *
4 * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
5 */
6
7 #include "sparx5_tc.h"
8 #include "vcap_api.h"
9 #include "vcap_api_client.h"
10 #include "sparx5_main_regs.h"
11 #include "sparx5_main.h"
12 #include "sparx5_vcap_impl.h"
13
14 static struct sparx5_mall_entry *
sparx5_tc_matchall_entry_find(struct list_head * entries,unsigned long cookie)15 sparx5_tc_matchall_entry_find(struct list_head *entries, unsigned long cookie)
16 {
17 struct sparx5_mall_entry *entry;
18
19 list_for_each_entry(entry, entries, list) {
20 if (entry->cookie == cookie)
21 return entry;
22 }
23
24 return NULL;
25 }
26
sparx5_tc_matchall_parse_action(struct sparx5_port * port,struct sparx5_mall_entry * entry,struct flow_action_entry * action,bool ingress,unsigned long cookie)27 static void sparx5_tc_matchall_parse_action(struct sparx5_port *port,
28 struct sparx5_mall_entry *entry,
29 struct flow_action_entry *action,
30 bool ingress,
31 unsigned long cookie)
32 {
33 entry->port = port;
34 entry->type = action->id;
35 entry->ingress = ingress;
36 entry->cookie = cookie;
37 }
38
39 static void
sparx5_tc_matchall_parse_mirror_action(struct sparx5_mall_entry * entry,struct flow_action_entry * action)40 sparx5_tc_matchall_parse_mirror_action(struct sparx5_mall_entry *entry,
41 struct flow_action_entry *action)
42 {
43 entry->mirror.port = netdev_priv(action->dev);
44 }
45
sparx5_tc_matchall_replace(struct net_device * ndev,struct tc_cls_matchall_offload * tmo,bool ingress)46 static int sparx5_tc_matchall_replace(struct net_device *ndev,
47 struct tc_cls_matchall_offload *tmo,
48 bool ingress)
49 {
50 struct sparx5_port *port = netdev_priv(ndev);
51 struct sparx5_mall_entry *mall_entry;
52 struct flow_action_entry *action;
53 struct sparx5 *sparx5;
54 int err;
55
56 if (!flow_offload_has_one_action(&tmo->rule->action)) {
57 NL_SET_ERR_MSG_MOD(tmo->common.extack,
58 "Only one action per filter is supported");
59 return -EOPNOTSUPP;
60 }
61 action = &tmo->rule->action.entries[0];
62
63 mall_entry = kzalloc(sizeof(*mall_entry), GFP_KERNEL);
64 if (!mall_entry)
65 return -ENOMEM;
66
67 sparx5_tc_matchall_parse_action(port,
68 mall_entry,
69 action,
70 ingress,
71 tmo->cookie);
72
73 sparx5 = port->sparx5;
74 switch (action->id) {
75 case FLOW_ACTION_MIRRED:
76 sparx5_tc_matchall_parse_mirror_action(mall_entry, action);
77 err = sparx5_mirror_add(mall_entry);
78 if (err) {
79 switch (err) {
80 case -EEXIST:
81 NL_SET_ERR_MSG_MOD(tmo->common.extack,
82 "Mirroring already exists");
83 break;
84 case -EINVAL:
85 NL_SET_ERR_MSG_MOD(tmo->common.extack,
86 "Cannot mirror a monitor port");
87 break;
88 case -ENOENT:
89 NL_SET_ERR_MSG_MOD(tmo->common.extack,
90 "No more mirror probes available");
91 break;
92 default:
93 NL_SET_ERR_MSG_MOD(tmo->common.extack,
94 "Unknown error");
95 break;
96 }
97 return err;
98 }
99 /* Get baseline stats for this port */
100 sparx5_mirror_stats(mall_entry, &tmo->stats);
101 break;
102 case FLOW_ACTION_GOTO:
103 err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev,
104 tmo->common.chain_index,
105 action->chain_index, tmo->cookie,
106 true);
107 if (err == -EFAULT) {
108 NL_SET_ERR_MSG_MOD(tmo->common.extack,
109 "Unsupported goto chain");
110 return -EOPNOTSUPP;
111 }
112 if (err == -EADDRINUSE) {
113 NL_SET_ERR_MSG_MOD(tmo->common.extack,
114 "VCAP already enabled");
115 return -EOPNOTSUPP;
116 }
117 if (err == -EADDRNOTAVAIL) {
118 NL_SET_ERR_MSG_MOD(tmo->common.extack,
119 "Already matching this chain");
120 return -EOPNOTSUPP;
121 }
122 if (err) {
123 NL_SET_ERR_MSG_MOD(tmo->common.extack,
124 "Could not enable VCAP lookups");
125 return err;
126 }
127 break;
128 default:
129 NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action");
130 return -EOPNOTSUPP;
131 }
132
133 list_add_tail(&mall_entry->list, &sparx5->mall_entries);
134
135 return 0;
136 }
137
sparx5_tc_matchall_destroy(struct net_device * ndev,struct tc_cls_matchall_offload * tmo,bool ingress)138 static int sparx5_tc_matchall_destroy(struct net_device *ndev,
139 struct tc_cls_matchall_offload *tmo,
140 bool ingress)
141 {
142 struct sparx5_port *port = netdev_priv(ndev);
143 struct sparx5 *sparx5 = port->sparx5;
144 struct sparx5_mall_entry *entry;
145 int err = 0;
146
147 entry = sparx5_tc_matchall_entry_find(&sparx5->mall_entries,
148 tmo->cookie);
149 if (!entry)
150 return -ENOENT;
151
152 if (entry->type == FLOW_ACTION_MIRRED) {
153 sparx5_mirror_del(entry);
154 } else if (entry->type == FLOW_ACTION_GOTO) {
155 err = vcap_enable_lookups(sparx5->vcap_ctrl, ndev,
156 0, 0, tmo->cookie, false);
157 } else {
158 NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action");
159 err = -EOPNOTSUPP;
160 }
161
162 list_del(&entry->list);
163
164 return err;
165 }
166
sparx5_tc_matchall_stats(struct net_device * ndev,struct tc_cls_matchall_offload * tmo,bool ingress)167 static int sparx5_tc_matchall_stats(struct net_device *ndev,
168 struct tc_cls_matchall_offload *tmo,
169 bool ingress)
170 {
171 struct sparx5_port *port = netdev_priv(ndev);
172 struct sparx5 *sparx5 = port->sparx5;
173 struct sparx5_mall_entry *entry;
174
175 entry = sparx5_tc_matchall_entry_find(&sparx5->mall_entries,
176 tmo->cookie);
177 if (!entry)
178 return -ENOENT;
179
180 if (entry->type == FLOW_ACTION_MIRRED) {
181 sparx5_mirror_stats(entry, &tmo->stats);
182 } else {
183 NL_SET_ERR_MSG_MOD(tmo->common.extack, "Unsupported action");
184 return -EOPNOTSUPP;
185 }
186
187 return 0;
188 }
189
sparx5_tc_matchall(struct net_device * ndev,struct tc_cls_matchall_offload * tmo,bool ingress)190 int sparx5_tc_matchall(struct net_device *ndev,
191 struct tc_cls_matchall_offload *tmo,
192 bool ingress)
193 {
194 switch (tmo->command) {
195 case TC_CLSMATCHALL_REPLACE:
196 return sparx5_tc_matchall_replace(ndev, tmo, ingress);
197 case TC_CLSMATCHALL_DESTROY:
198 return sparx5_tc_matchall_destroy(ndev, tmo, ingress);
199 case TC_CLSMATCHALL_STATS:
200 return sparx5_tc_matchall_stats(ndev, tmo, ingress);
201 default:
202 return -EOPNOTSUPP;
203 }
204 }
205