sfc: add ef100 MAE counter support functions
[platform/kernel/linux-rpi.git] / drivers / net / ethernet / sfc / mae.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /****************************************************************************
3  * Driver for Solarflare network controllers and boards
4  * Copyright 2019 Solarflare Communications Inc.
5  * Copyright 2020-2022 Xilinx Inc.
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 as published
9  * by the Free Software Foundation, incorporated herein by reference.
10  */
11
12 #include "mae.h"
13 #include "mcdi.h"
14 #include "mcdi_pcol_mae.h"
15
16 int efx_mae_allocate_mport(struct efx_nic *efx, u32 *id, u32 *label)
17 {
18         MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_MPORT_ALLOC_ALIAS_OUT_LEN);
19         MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_MPORT_ALLOC_ALIAS_IN_LEN);
20         size_t outlen;
21         int rc;
22
23         if (WARN_ON_ONCE(!id))
24                 return -EINVAL;
25         if (WARN_ON_ONCE(!label))
26                 return -EINVAL;
27
28         MCDI_SET_DWORD(inbuf, MAE_MPORT_ALLOC_ALIAS_IN_TYPE,
29                        MC_CMD_MAE_MPORT_ALLOC_ALIAS_IN_MPORT_TYPE_ALIAS);
30         MCDI_SET_DWORD(inbuf, MAE_MPORT_ALLOC_ALIAS_IN_DELIVER_MPORT,
31                        MAE_MPORT_SELECTOR_ASSIGNED);
32         rc = efx_mcdi_rpc(efx, MC_CMD_MAE_MPORT_ALLOC, inbuf, sizeof(inbuf),
33                           outbuf, sizeof(outbuf), &outlen);
34         if (rc)
35                 return rc;
36         if (outlen < sizeof(outbuf))
37                 return -EIO;
38         *id = MCDI_DWORD(outbuf, MAE_MPORT_ALLOC_ALIAS_OUT_MPORT_ID);
39         *label = MCDI_DWORD(outbuf, MAE_MPORT_ALLOC_ALIAS_OUT_LABEL);
40         return 0;
41 }
42
43 int efx_mae_free_mport(struct efx_nic *efx, u32 id)
44 {
45         MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_MPORT_FREE_IN_LEN);
46
47         BUILD_BUG_ON(MC_CMD_MAE_MPORT_FREE_OUT_LEN);
48         MCDI_SET_DWORD(inbuf, MAE_MPORT_FREE_IN_MPORT_ID, id);
49         return efx_mcdi_rpc(efx, MC_CMD_MAE_MPORT_FREE, inbuf, sizeof(inbuf),
50                             NULL, 0, NULL);
51 }
52
53 void efx_mae_mport_wire(struct efx_nic *efx, u32 *out)
54 {
55         efx_dword_t mport;
56
57         EFX_POPULATE_DWORD_2(mport,
58                              MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_PPORT,
59                              MAE_MPORT_SELECTOR_PPORT_ID, efx->port_num);
60         *out = EFX_DWORD_VAL(mport);
61 }
62
63 void efx_mae_mport_uplink(struct efx_nic *efx __always_unused, u32 *out)
64 {
65         efx_dword_t mport;
66
67         EFX_POPULATE_DWORD_3(mport,
68                              MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_FUNC,
69                              MAE_MPORT_SELECTOR_FUNC_PF_ID, MAE_MPORT_SELECTOR_FUNC_PF_ID_CALLER,
70                              MAE_MPORT_SELECTOR_FUNC_VF_ID, MAE_MPORT_SELECTOR_FUNC_VF_ID_NULL);
71         *out = EFX_DWORD_VAL(mport);
72 }
73
74 void efx_mae_mport_vf(struct efx_nic *efx __always_unused, u32 vf_id, u32 *out)
75 {
76         efx_dword_t mport;
77
78         EFX_POPULATE_DWORD_3(mport,
79                              MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_FUNC,
80                              MAE_MPORT_SELECTOR_FUNC_PF_ID, MAE_MPORT_SELECTOR_FUNC_PF_ID_CALLER,
81                              MAE_MPORT_SELECTOR_FUNC_VF_ID, vf_id);
82         *out = EFX_DWORD_VAL(mport);
83 }
84
85 /* Constructs an mport selector from an mport ID, because they're not the same */
86 void efx_mae_mport_mport(struct efx_nic *efx __always_unused, u32 mport_id, u32 *out)
87 {
88         efx_dword_t mport;
89
90         EFX_POPULATE_DWORD_2(mport,
91                              MAE_MPORT_SELECTOR_TYPE, MAE_MPORT_SELECTOR_TYPE_MPORT_ID,
92                              MAE_MPORT_SELECTOR_MPORT_ID, mport_id);
93         *out = EFX_DWORD_VAL(mport);
94 }
95
96 /* id is really only 24 bits wide */
97 int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id)
98 {
99         MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_MPORT_LOOKUP_OUT_LEN);
100         MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_MPORT_LOOKUP_IN_LEN);
101         size_t outlen;
102         int rc;
103
104         MCDI_SET_DWORD(inbuf, MAE_MPORT_LOOKUP_IN_MPORT_SELECTOR, selector);
105         rc = efx_mcdi_rpc(efx, MC_CMD_MAE_MPORT_LOOKUP, inbuf, sizeof(inbuf),
106                           outbuf, sizeof(outbuf), &outlen);
107         if (rc)
108                 return rc;
109         if (outlen < sizeof(outbuf))
110                 return -EIO;
111         *id = MCDI_DWORD(outbuf, MAE_MPORT_LOOKUP_OUT_MPORT_ID);
112         return 0;
113 }
114
115 int efx_mae_start_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue)
116 {
117         MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTERS_STREAM_START_V2_IN_LEN);
118         MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTERS_STREAM_START_OUT_LEN);
119         u32 out_flags;
120         size_t outlen;
121         int rc;
122
123         MCDI_SET_WORD(inbuf, MAE_COUNTERS_STREAM_START_V2_IN_QID,
124                       efx_rx_queue_index(rx_queue));
125         MCDI_SET_WORD(inbuf, MAE_COUNTERS_STREAM_START_V2_IN_PACKET_SIZE,
126                       efx->net_dev->mtu);
127         MCDI_SET_DWORD(inbuf, MAE_COUNTERS_STREAM_START_V2_IN_COUNTER_TYPES_MASK,
128                        BIT(MAE_COUNTER_TYPE_AR) | BIT(MAE_COUNTER_TYPE_CT) |
129                        BIT(MAE_COUNTER_TYPE_OR));
130         rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTERS_STREAM_START,
131                           inbuf, sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
132         if (rc)
133                 return rc;
134         if (outlen < sizeof(outbuf))
135                 return -EIO;
136         out_flags = MCDI_DWORD(outbuf, MAE_COUNTERS_STREAM_START_OUT_FLAGS);
137         if (out_flags & BIT(MC_CMD_MAE_COUNTERS_STREAM_START_OUT_USES_CREDITS_OFST)) {
138                 netif_dbg(efx, drv, efx->net_dev,
139                           "MAE counter stream uses credits\n");
140                 rx_queue->grant_credits = true;
141                 out_flags &= ~BIT(MC_CMD_MAE_COUNTERS_STREAM_START_OUT_USES_CREDITS_OFST);
142         }
143         if (out_flags) {
144                 netif_err(efx, drv, efx->net_dev,
145                           "MAE counter stream start: unrecognised flags %x\n",
146                           out_flags);
147                 goto out_stop;
148         }
149         return 0;
150 out_stop:
151         efx_mae_stop_counters(efx, rx_queue);
152         return -EOPNOTSUPP;
153 }
154
155 static bool efx_mae_counters_flushed(u32 *flush_gen, u32 *seen_gen)
156 {
157         int i;
158
159         for (i = 0; i < EFX_TC_COUNTER_TYPE_MAX; i++)
160                 if ((s32)(flush_gen[i] - seen_gen[i]) > 0)
161                         return false;
162         return true;
163 }
164
165 int efx_mae_stop_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue)
166 {
167         MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTERS_STREAM_STOP_V2_OUT_LENMAX);
168         MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTERS_STREAM_STOP_IN_LEN);
169         size_t outlen;
170         int rc, i;
171
172         MCDI_SET_WORD(inbuf, MAE_COUNTERS_STREAM_STOP_IN_QID,
173                       efx_rx_queue_index(rx_queue));
174         rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTERS_STREAM_STOP,
175                           inbuf, sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
176
177         if (rc)
178                 return rc;
179
180         netif_dbg(efx, drv, efx->net_dev, "Draining counters:\n");
181         /* Only process received generation counts */
182         for (i = 0; (i < (outlen / 4)) && (i < EFX_TC_COUNTER_TYPE_MAX); i++) {
183                 efx->tc->flush_gen[i] = MCDI_ARRAY_DWORD(outbuf,
184                                                          MAE_COUNTERS_STREAM_STOP_V2_OUT_GENERATION_COUNT,
185                                                          i);
186                 netif_dbg(efx, drv, efx->net_dev,
187                           "\ttype %u, awaiting gen %u\n", i,
188                           efx->tc->flush_gen[i]);
189         }
190
191         efx->tc->flush_counters = true;
192
193         /* Drain can take up to 2 seconds owing to FWRIVERHD-2884; whatever
194          * timeout we use, that delay is added to unload on nonresponsive
195          * hardware, so 2500ms seems like a reasonable compromise.
196          */
197         if (!wait_event_timeout(efx->tc->flush_wq,
198                                 efx_mae_counters_flushed(efx->tc->flush_gen,
199                                                          efx->tc->seen_gen),
200                                 msecs_to_jiffies(2500)))
201                 netif_warn(efx, drv, efx->net_dev,
202                            "Failed to drain counters RXQ, FW may be unhappy\n");
203
204         efx->tc->flush_counters = false;
205
206         return rc;
207 }
208
209 void efx_mae_counters_grant_credits(struct work_struct *work)
210 {
211         MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS_IN_LEN);
212         struct efx_rx_queue *rx_queue = container_of(work, struct efx_rx_queue,
213                                                      grant_work);
214         struct efx_nic *efx = rx_queue->efx;
215         unsigned int credits;
216
217         BUILD_BUG_ON(MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS_OUT_LEN);
218         credits = READ_ONCE(rx_queue->notified_count) - rx_queue->granted_count;
219         MCDI_SET_DWORD(inbuf, MAE_COUNTERS_STREAM_GIVE_CREDITS_IN_NUM_CREDITS,
220                        credits);
221         if (!efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS,
222                           inbuf, sizeof(inbuf), NULL, 0, NULL))
223                 rx_queue->granted_count += credits;
224 }
225
226 static int efx_mae_get_basic_caps(struct efx_nic *efx, struct mae_caps *caps)
227 {
228         MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_CAPS_OUT_LEN);
229         size_t outlen;
230         int rc;
231
232         BUILD_BUG_ON(MC_CMD_MAE_GET_CAPS_IN_LEN);
233
234         rc = efx_mcdi_rpc(efx, MC_CMD_MAE_GET_CAPS, NULL, 0, outbuf,
235                           sizeof(outbuf), &outlen);
236         if (rc)
237                 return rc;
238         if (outlen < sizeof(outbuf))
239                 return -EIO;
240         caps->match_field_count = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_MATCH_FIELD_COUNT);
241         caps->action_prios = MCDI_DWORD(outbuf, MAE_GET_CAPS_OUT_ACTION_PRIOS);
242         return 0;
243 }
244
245 static int efx_mae_get_rule_fields(struct efx_nic *efx, u32 cmd,
246                                    u8 *field_support)
247 {
248         MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_AR_CAPS_OUT_LEN(MAE_NUM_FIELDS));
249         MCDI_DECLARE_STRUCT_PTR(caps);
250         unsigned int count;
251         size_t outlen;
252         int rc, i;
253
254         BUILD_BUG_ON(MC_CMD_MAE_GET_AR_CAPS_IN_LEN);
255
256         rc = efx_mcdi_rpc(efx, cmd, NULL, 0, outbuf, sizeof(outbuf), &outlen);
257         if (rc)
258                 return rc;
259         count = MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_COUNT);
260         memset(field_support, MAE_FIELD_UNSUPPORTED, MAE_NUM_FIELDS);
261         caps = _MCDI_DWORD(outbuf, MAE_GET_AR_CAPS_OUT_FIELD_FLAGS);
262         /* We're only interested in the support status enum, not any other
263          * flags, so just extract that from each entry.
264          */
265         for (i = 0; i < count; i++)
266                 if (i * sizeof(*outbuf) + MC_CMD_MAE_GET_AR_CAPS_OUT_FIELD_FLAGS_OFST < outlen)
267                         field_support[i] = EFX_DWORD_FIELD(caps[i], MAE_FIELD_FLAGS_SUPPORT_STATUS);
268         return 0;
269 }
270
271 int efx_mae_get_caps(struct efx_nic *efx, struct mae_caps *caps)
272 {
273         int rc;
274
275         rc = efx_mae_get_basic_caps(efx, caps);
276         if (rc)
277                 return rc;
278         return efx_mae_get_rule_fields(efx, MC_CMD_MAE_GET_AR_CAPS,
279                                        caps->action_rule_fields);
280 }
281
282 /* Bit twiddling:
283  * Prefix: 1...110...0
284  *      ~: 0...001...1
285  *    + 1: 0...010...0 is power of two
286  * so (~x) & ((~x) + 1) == 0.  Converse holds also.
287  */
288 #define is_prefix_byte(_x)      !(((_x) ^ 0xff) & (((_x) ^ 0xff) + 1))
289
290 enum mask_type { MASK_ONES, MASK_ZEROES, MASK_PREFIX, MASK_OTHER };
291
292 static const char *mask_type_name(enum mask_type typ)
293 {
294         switch (typ) {
295         case MASK_ONES:
296                 return "all-1s";
297         case MASK_ZEROES:
298                 return "all-0s";
299         case MASK_PREFIX:
300                 return "prefix";
301         case MASK_OTHER:
302                 return "arbitrary";
303         default: /* can't happen */
304                 return "unknown";
305         }
306 }
307
308 /* Checks a (big-endian) bytestring is a bit prefix */
309 static enum mask_type classify_mask(const u8 *mask, size_t len)
310 {
311         bool zeroes = true; /* All bits seen so far are zeroes */
312         bool ones = true; /* All bits seen so far are ones */
313         bool prefix = true; /* Valid prefix so far */
314         size_t i;
315
316         for (i = 0; i < len; i++) {
317                 if (ones) {
318                         if (!is_prefix_byte(mask[i]))
319                                 prefix = false;
320                 } else if (mask[i]) {
321                         prefix = false;
322                 }
323                 if (mask[i] != 0xff)
324                         ones = false;
325                 if (mask[i])
326                         zeroes = false;
327         }
328         if (ones)
329                 return MASK_ONES;
330         if (zeroes)
331                 return MASK_ZEROES;
332         if (prefix)
333                 return MASK_PREFIX;
334         return MASK_OTHER;
335 }
336
337 static int efx_mae_match_check_cap_typ(u8 support, enum mask_type typ)
338 {
339         switch (support) {
340         case MAE_FIELD_UNSUPPORTED:
341         case MAE_FIELD_SUPPORTED_MATCH_NEVER:
342                 if (typ == MASK_ZEROES)
343                         return 0;
344                 return -EOPNOTSUPP;
345         case MAE_FIELD_SUPPORTED_MATCH_OPTIONAL:
346                 if (typ == MASK_ZEROES)
347                         return 0;
348                 fallthrough;
349         case MAE_FIELD_SUPPORTED_MATCH_ALWAYS:
350                 if (typ == MASK_ONES)
351                         return 0;
352                 return -EINVAL;
353         case MAE_FIELD_SUPPORTED_MATCH_PREFIX:
354                 if (typ == MASK_OTHER)
355                         return -EOPNOTSUPP;
356                 return 0;
357         case MAE_FIELD_SUPPORTED_MATCH_MASK:
358                 return 0;
359         default:
360                 return -EIO;
361         }
362 }
363
364 /* Validate field mask against hardware capabilities.  Captures caller's 'rc' */
365 #define CHECK(_mcdi, _field)    ({                                             \
366         enum mask_type typ = classify_mask((const u8 *)&mask->_field,          \
367                                            sizeof(mask->_field));              \
368                                                                                \
369         rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ ## _mcdi],\
370                                          typ);                                 \
371         if (rc)                                                                \
372                 NL_SET_ERR_MSG_FMT_MOD(extack,                                 \
373                                        "No support for %s mask in field %s",   \
374                                        mask_type_name(typ), #_field);          \
375         rc;                                                                    \
376 })
377 /* Booleans need special handling */
378 #define CHECK_BIT(_mcdi, _field)        ({                                     \
379         enum mask_type typ = mask->_field ? MASK_ONES : MASK_ZEROES;           \
380                                                                                \
381         rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ ## _mcdi],\
382                                          typ);                                 \
383         if (rc)                                                                \
384                 NL_SET_ERR_MSG_FMT_MOD(extack,                                 \
385                                        "No support for %s mask in field %s",   \
386                                        mask_type_name(typ), #_field);          \
387         rc;                                                                    \
388 })
389
390 int efx_mae_match_check_caps(struct efx_nic *efx,
391                              const struct efx_tc_match_fields *mask,
392                              struct netlink_ext_ack *extack)
393 {
394         const u8 *supported_fields = efx->tc->caps->action_rule_fields;
395         __be32 ingress_port = cpu_to_be32(mask->ingress_port);
396         enum mask_type ingress_port_mask_type;
397         int rc;
398
399         /* Check for _PREFIX assumes big-endian, so we need to convert */
400         ingress_port_mask_type = classify_mask((const u8 *)&ingress_port,
401                                                sizeof(ingress_port));
402         rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_INGRESS_PORT],
403                                          ingress_port_mask_type);
404         if (rc) {
405                 NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field ingress_port",
406                                        mask_type_name(ingress_port_mask_type));
407                 return rc;
408         }
409         if (CHECK(ETHER_TYPE, eth_proto) ||
410             CHECK(VLAN0_TCI, vlan_tci[0]) ||
411             CHECK(VLAN0_PROTO, vlan_proto[0]) ||
412             CHECK(VLAN1_TCI, vlan_tci[1]) ||
413             CHECK(VLAN1_PROTO, vlan_proto[1]) ||
414             CHECK(ETH_SADDR, eth_saddr) ||
415             CHECK(ETH_DADDR, eth_daddr) ||
416             CHECK(IP_PROTO, ip_proto) ||
417             CHECK(IP_TOS, ip_tos) ||
418             CHECK(IP_TTL, ip_ttl) ||
419             CHECK(SRC_IP4, src_ip) ||
420             CHECK(DST_IP4, dst_ip) ||
421 #ifdef CONFIG_IPV6
422             CHECK(SRC_IP6, src_ip6) ||
423             CHECK(DST_IP6, dst_ip6) ||
424 #endif
425             CHECK(L4_SPORT, l4_sport) ||
426             CHECK(L4_DPORT, l4_dport) ||
427             CHECK(TCP_FLAGS, tcp_flags) ||
428             CHECK_BIT(IS_IP_FRAG, ip_frag) ||
429             CHECK_BIT(IP_FIRST_FRAG, ip_firstfrag) ||
430             CHECK(RECIRC_ID, recirc_id))
431                 return rc;
432         return 0;
433 }
434 #undef CHECK_BIT
435 #undef CHECK
436
437 static bool efx_mae_asl_id(u32 id)
438 {
439         return !!(id & BIT(31));
440 }
441
442 int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act)
443 {
444         MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN);
445         MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_ALLOC_IN_LEN);
446         size_t outlen;
447         int rc;
448
449         MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID,
450                        MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
451         MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID,
452                        MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
453         MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID,
454                        MC_CMD_MAE_COUNTER_ALLOC_OUT_COUNTER_ID_NULL);
455         MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_LIST_ID,
456                        MC_CMD_MAE_COUNTER_LIST_ALLOC_OUT_COUNTER_LIST_ID_NULL);
457         MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID,
458                        MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL);
459         if (act->deliver)
460                 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DELIVER,
461                                act->dest_mport);
462         BUILD_BUG_ON(MAE_MPORT_SELECTOR_NULL);
463         rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_ALLOC, inbuf, sizeof(inbuf),
464                           outbuf, sizeof(outbuf), &outlen);
465         if (rc)
466                 return rc;
467         if (outlen < sizeof(outbuf))
468                 return -EIO;
469         act->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_ALLOC_OUT_AS_ID);
470         /* We rely on the high bit of AS IDs always being clear.
471          * The firmware API guarantees this, but let's check it ourselves.
472          */
473         if (WARN_ON_ONCE(efx_mae_asl_id(act->fw_id))) {
474                 efx_mae_free_action_set(efx, act->fw_id);
475                 return -EIO;
476         }
477         return 0;
478 }
479
480 int efx_mae_free_action_set(struct efx_nic *efx, u32 fw_id)
481 {
482         MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_FREE_OUT_LEN(1));
483         MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_FREE_IN_LEN(1));
484         size_t outlen;
485         int rc;
486
487         MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_FREE_IN_AS_ID, fw_id);
488         rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_FREE, inbuf, sizeof(inbuf),
489                           outbuf, sizeof(outbuf), &outlen);
490         if (rc)
491                 return rc;
492         if (outlen < sizeof(outbuf))
493                 return -EIO;
494         /* FW freed a different ID than we asked for, should never happen.
495          * Warn because it means we've now got a different idea to the FW of
496          * what action-sets exist, which could cause mayhem later.
497          */
498         if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_FREE_OUT_FREED_AS_ID) != fw_id))
499                 return -EIO;
500         return 0;
501 }
502
503 int efx_mae_alloc_action_set_list(struct efx_nic *efx,
504                                   struct efx_tc_action_set_list *acts)
505 {
506         MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_LEN);
507         struct efx_tc_action_set *act;
508         size_t inlen, outlen, i = 0;
509         efx_dword_t *inbuf;
510         int rc;
511
512         list_for_each_entry(act, &acts->list, list)
513                 i++;
514         if (i == 0)
515                 return -EINVAL;
516         if (i == 1) {
517                 /* Don't wrap an ASL around a single AS, just use the AS_ID
518                  * directly.  ASLs are a more limited resource.
519                  */
520                 act = list_first_entry(&acts->list, struct efx_tc_action_set, list);
521                 acts->fw_id = act->fw_id;
522                 return 0;
523         }
524         if (i > MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS_MAXNUM_MCDI2)
525                 return -EOPNOTSUPP; /* Too many actions */
526         inlen = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LEN(i);
527         inbuf = kzalloc(inlen, GFP_KERNEL);
528         if (!inbuf)
529                 return -ENOMEM;
530         i = 0;
531         list_for_each_entry(act, &acts->list, list) {
532                 MCDI_SET_ARRAY_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS,
533                                      i, act->fw_id);
534                 i++;
535         }
536         MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_COUNT, i);
537         rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_ALLOC, inbuf, inlen,
538                           outbuf, sizeof(outbuf), &outlen);
539         if (rc)
540                 goto out_free;
541         if (outlen < sizeof(outbuf)) {
542                 rc = -EIO;
543                 goto out_free;
544         }
545         acts->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_ALLOC_OUT_ASL_ID);
546         /* We rely on the high bit of ASL IDs always being set.
547          * The firmware API guarantees this, but let's check it ourselves.
548          */
549         if (WARN_ON_ONCE(!efx_mae_asl_id(acts->fw_id))) {
550                 efx_mae_free_action_set_list(efx, acts);
551                 rc = -EIO;
552         }
553 out_free:
554         kfree(inbuf);
555         return rc;
556 }
557
558 int efx_mae_free_action_set_list(struct efx_nic *efx,
559                                  struct efx_tc_action_set_list *acts)
560 {
561         MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_OUT_LEN(1));
562         MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_IN_LEN(1));
563         size_t outlen;
564         int rc;
565
566         /* If this is just an AS_ID with no ASL wrapper, then there is
567          * nothing for us to free.  (The AS will be freed later.)
568          */
569         if (efx_mae_asl_id(acts->fw_id)) {
570                 MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_FREE_IN_ASL_ID,
571                                acts->fw_id);
572                 rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_FREE, inbuf,
573                                   sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
574                 if (rc)
575                         return rc;
576                 if (outlen < sizeof(outbuf))
577                         return -EIO;
578                 /* FW freed a different ID than we asked for, should never happen.
579                  * Warn because it means we've now got a different idea to the FW of
580                  * what action-set-lists exist, which could cause mayhem later.
581                  */
582                 if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_FREE_OUT_FREED_ASL_ID) != acts->fw_id))
583                         return -EIO;
584         }
585         /* We're probably about to free @acts, but let's just make sure its
586          * fw_id is blatted so that it won't look valid if it leaks out.
587          */
588         acts->fw_id = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL;
589         return 0;
590 }
591
592 static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit),
593                                            const struct efx_tc_match *match)
594 {
595         if (match->mask.ingress_port) {
596                 if (~match->mask.ingress_port)
597                         return -EOPNOTSUPP;
598                 MCDI_STRUCT_SET_DWORD(match_crit,
599                                       MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR,
600                                       match->value.ingress_port);
601         }
602         MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR_MASK,
603                               match->mask.ingress_port);
604         EFX_POPULATE_DWORD_2(*_MCDI_STRUCT_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS),
605                              MAE_FIELD_MASK_VALUE_PAIRS_V2_IS_IP_FRAG,
606                              match->value.ip_frag,
607                              MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_FIRST_FRAG,
608                              match->value.ip_firstfrag);
609         EFX_POPULATE_DWORD_2(*_MCDI_STRUCT_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS_MASK),
610                              MAE_FIELD_MASK_VALUE_PAIRS_V2_IS_IP_FRAG,
611                              match->mask.ip_frag,
612                              MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_FIRST_FRAG,
613                              match->mask.ip_firstfrag);
614         MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID,
615                              match->value.recirc_id);
616         MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID_MASK,
617                              match->mask.recirc_id);
618         MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETHER_TYPE_BE,
619                                 match->value.eth_proto);
620         MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETHER_TYPE_BE_MASK,
621                                 match->mask.eth_proto);
622         MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_TCI_BE,
623                                 match->value.vlan_tci[0]);
624         MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_TCI_BE_MASK,
625                                 match->mask.vlan_tci[0]);
626         MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_PROTO_BE,
627                                 match->value.vlan_proto[0]);
628         MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_PROTO_BE_MASK,
629                                 match->mask.vlan_proto[0]);
630         MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_TCI_BE,
631                                 match->value.vlan_tci[1]);
632         MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_TCI_BE_MASK,
633                                 match->mask.vlan_tci[1]);
634         MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_PROTO_BE,
635                                 match->value.vlan_proto[1]);
636         MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_PROTO_BE_MASK,
637                                 match->mask.vlan_proto[1]);
638         memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_SADDR_BE),
639                match->value.eth_saddr, ETH_ALEN);
640         memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_SADDR_BE_MASK),
641                match->mask.eth_saddr, ETH_ALEN);
642         memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE),
643                match->value.eth_daddr, ETH_ALEN);
644         memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE_MASK),
645                match->mask.eth_daddr, ETH_ALEN);
646         MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_PROTO,
647                              match->value.ip_proto);
648         MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_PROTO_MASK,
649                              match->mask.ip_proto);
650         MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TOS,
651                              match->value.ip_tos);
652         MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TOS_MASK,
653                              match->mask.ip_tos);
654         MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TTL,
655                              match->value.ip_ttl);
656         MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TTL_MASK,
657                              match->mask.ip_ttl);
658         MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP4_BE,
659                                  match->value.src_ip);
660         MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP4_BE_MASK,
661                                  match->mask.src_ip);
662         MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP4_BE,
663                                  match->value.dst_ip);
664         MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP4_BE_MASK,
665                                  match->mask.dst_ip);
666 #ifdef CONFIG_IPV6
667         memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP6_BE),
668                &match->value.src_ip6, sizeof(struct in6_addr));
669         memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP6_BE_MASK),
670                &match->mask.src_ip6, sizeof(struct in6_addr));
671         memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE),
672                &match->value.dst_ip6, sizeof(struct in6_addr));
673         memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE_MASK),
674                &match->mask.dst_ip6, sizeof(struct in6_addr));
675 #endif
676         MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_SPORT_BE,
677                                 match->value.l4_sport);
678         MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_SPORT_BE_MASK,
679                                 match->mask.l4_sport);
680         MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_DPORT_BE,
681                                 match->value.l4_dport);
682         MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_DPORT_BE_MASK,
683                                 match->mask.l4_dport);
684         MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE,
685                                 match->value.tcp_flags);
686         MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE_MASK,
687                                 match->mask.tcp_flags);
688         return 0;
689 }
690
691 int efx_mae_insert_rule(struct efx_nic *efx, const struct efx_tc_match *match,
692                         u32 prio, u32 acts_id, u32 *id)
693 {
694         MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_INSERT_IN_LEN(MAE_FIELD_MASK_VALUE_PAIRS_V2_LEN));
695         MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_INSERT_OUT_LEN);
696         MCDI_DECLARE_STRUCT_PTR(match_crit);
697         MCDI_DECLARE_STRUCT_PTR(response);
698         size_t outlen;
699         int rc;
700
701         if (!id)
702                 return -EINVAL;
703
704         match_crit = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_MATCH_CRITERIA);
705         response = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_RESPONSE);
706         if (efx_mae_asl_id(acts_id)) {
707                 MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID, acts_id);
708                 MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID,
709                                       MC_CMD_MAE_ACTION_SET_ALLOC_OUT_ACTION_SET_ID_NULL);
710         } else {
711                 /* We only had one AS, so we didn't wrap it in an ASL */
712                 MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID,
713                                       MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL);
714                 MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID, acts_id);
715         }
716         MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_PRIO, prio);
717         rc = efx_mae_populate_match_criteria(match_crit, match);
718         if (rc)
719                 return rc;
720
721         rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_INSERT, inbuf, sizeof(inbuf),
722                           outbuf, sizeof(outbuf), &outlen);
723         if (rc)
724                 return rc;
725         if (outlen < sizeof(outbuf))
726                 return -EIO;
727         *id = MCDI_DWORD(outbuf, MAE_ACTION_RULE_INSERT_OUT_AR_ID);
728         return 0;
729 }
730
731 int efx_mae_delete_rule(struct efx_nic *efx, u32 id)
732 {
733         MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_DELETE_OUT_LEN(1));
734         MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_DELETE_IN_LEN(1));
735         size_t outlen;
736         int rc;
737
738         MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_DELETE_IN_AR_ID, id);
739         rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_DELETE, inbuf, sizeof(inbuf),
740                           outbuf, sizeof(outbuf), &outlen);
741         if (rc)
742                 return rc;
743         if (outlen < sizeof(outbuf))
744                 return -EIO;
745         /* FW freed a different ID than we asked for, should also never happen.
746          * Warn because it means we've now got a different idea to the FW of
747          * what rules exist, which could cause mayhem later.
748          */
749         if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_RULE_DELETE_OUT_DELETED_AR_ID) != id))
750                 return -EIO;
751         return 0;
752 }