Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 16int 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
 43int 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
 53void 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
 63void 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
 74void 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 */
 86void 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 */
 97int 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
115int 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;
150out_stop:
151	efx_mae_stop_counters(efx, rx_queue);
152	return -EOPNOTSUPP;
153}
154
155static 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
165int 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
209void 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
226static 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
245static 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
271int 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
290enum mask_type { MASK_ONES, MASK_ZEROES, MASK_PREFIX, MASK_OTHER };
291
292static 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 */
309static 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
337static 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
390int 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
437int efx_mae_allocate_counter(struct efx_nic *efx, struct efx_tc_counter *cnt)
438{
439	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTER_ALLOC_OUT_LEN(1));
440	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTER_ALLOC_V2_IN_LEN);
441	size_t outlen;
442	int rc;
443
444	if (!cnt)
445		return -EINVAL;
446
447	MCDI_SET_DWORD(inbuf, MAE_COUNTER_ALLOC_V2_IN_REQUESTED_COUNT, 1);
448	MCDI_SET_DWORD(inbuf, MAE_COUNTER_ALLOC_V2_IN_COUNTER_TYPE, cnt->type);
449	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTER_ALLOC, inbuf, sizeof(inbuf),
450			  outbuf, sizeof(outbuf), &outlen);
451	if (rc)
452		return rc;
453	/* pcol says this can't happen, since count is 1 */
454	if (outlen < sizeof(outbuf))
455		return -EIO;
456	cnt->fw_id = MCDI_DWORD(outbuf, MAE_COUNTER_ALLOC_OUT_COUNTER_ID);
457	cnt->gen = MCDI_DWORD(outbuf, MAE_COUNTER_ALLOC_OUT_GENERATION_COUNT);
458	return 0;
459}
460
461int efx_mae_free_counter(struct efx_nic *efx, struct efx_tc_counter *cnt)
462{
463	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTER_FREE_OUT_LEN(1));
464	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTER_FREE_V2_IN_LEN);
465	size_t outlen;
466	int rc;
467
468	MCDI_SET_DWORD(inbuf, MAE_COUNTER_FREE_V2_IN_COUNTER_ID_COUNT, 1);
469	MCDI_SET_DWORD(inbuf, MAE_COUNTER_FREE_V2_IN_FREE_COUNTER_ID, cnt->fw_id);
470	MCDI_SET_DWORD(inbuf, MAE_COUNTER_FREE_V2_IN_COUNTER_TYPE, cnt->type);
471	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTER_FREE, inbuf, sizeof(inbuf),
472			  outbuf, sizeof(outbuf), &outlen);
473	if (rc)
474		return rc;
475	/* pcol says this can't happen, since count is 1 */
476	if (outlen < sizeof(outbuf))
477		return -EIO;
478	/* FW freed a different ID than we asked for, should also never happen.
479	 * Warn because it means we've now got a different idea to the FW of
480	 * what counters exist, which could cause mayhem later.
481	 */
482	if (WARN_ON(MCDI_DWORD(outbuf, MAE_COUNTER_FREE_OUT_FREED_COUNTER_ID) !=
483		    cnt->fw_id))
484		return -EIO;
485	return 0;
486}
487
488static bool efx_mae_asl_id(u32 id)
489{
490	return !!(id & BIT(31));
491}
492
493int efx_mae_alloc_action_set(struct efx_nic *efx, struct efx_tc_action_set *act)
494{
495	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_ALLOC_OUT_LEN);
496	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_ALLOC_IN_LEN);
497	size_t outlen;
498	int rc;
499
500	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_SRC_MAC_ID,
501		       MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
502	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DST_MAC_ID,
503		       MC_CMD_MAE_MAC_ADDR_ALLOC_OUT_MAC_ID_NULL);
504	if (act->count && !WARN_ON(!act->count->cnt))
505		MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID,
506			       act->count->cnt->fw_id);
507	else
508		MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_ID,
509			       MC_CMD_MAE_COUNTER_ALLOC_OUT_COUNTER_ID_NULL);
510	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_COUNTER_LIST_ID,
511		       MC_CMD_MAE_COUNTER_LIST_ALLOC_OUT_COUNTER_LIST_ID_NULL);
512	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_ENCAP_HEADER_ID,
513		       MC_CMD_MAE_ENCAP_HEADER_ALLOC_OUT_ENCAP_HEADER_ID_NULL);
514	if (act->deliver)
515		MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_ALLOC_IN_DELIVER,
516			       act->dest_mport);
517	BUILD_BUG_ON(MAE_MPORT_SELECTOR_NULL);
518	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_ALLOC, inbuf, sizeof(inbuf),
519			  outbuf, sizeof(outbuf), &outlen);
520	if (rc)
521		return rc;
522	if (outlen < sizeof(outbuf))
523		return -EIO;
524	act->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_ALLOC_OUT_AS_ID);
525	/* We rely on the high bit of AS IDs always being clear.
526	 * The firmware API guarantees this, but let's check it ourselves.
527	 */
528	if (WARN_ON_ONCE(efx_mae_asl_id(act->fw_id))) {
529		efx_mae_free_action_set(efx, act->fw_id);
530		return -EIO;
531	}
532	return 0;
533}
534
535int efx_mae_free_action_set(struct efx_nic *efx, u32 fw_id)
536{
537	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_FREE_OUT_LEN(1));
538	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_FREE_IN_LEN(1));
539	size_t outlen;
540	int rc;
541
542	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_FREE_IN_AS_ID, fw_id);
543	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_FREE, inbuf, sizeof(inbuf),
544			  outbuf, sizeof(outbuf), &outlen);
545	if (rc)
546		return rc;
547	if (outlen < sizeof(outbuf))
548		return -EIO;
549	/* FW freed a different ID than we asked for, should never happen.
550	 * Warn because it means we've now got a different idea to the FW of
551	 * what action-sets exist, which could cause mayhem later.
552	 */
553	if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_FREE_OUT_FREED_AS_ID) != fw_id))
554		return -EIO;
555	return 0;
556}
557
558int efx_mae_alloc_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_ALLOC_OUT_LEN);
562	struct efx_tc_action_set *act;
563	size_t inlen, outlen, i = 0;
564	efx_dword_t *inbuf;
565	int rc;
566
567	list_for_each_entry(act, &acts->list, list)
568		i++;
569	if (i == 0)
570		return -EINVAL;
571	if (i == 1) {
572		/* Don't wrap an ASL around a single AS, just use the AS_ID
573		 * directly.  ASLs are a more limited resource.
574		 */
575		act = list_first_entry(&acts->list, struct efx_tc_action_set, list);
576		acts->fw_id = act->fw_id;
577		return 0;
578	}
579	if (i > MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS_MAXNUM_MCDI2)
580		return -EOPNOTSUPP; /* Too many actions */
581	inlen = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LEN(i);
582	inbuf = kzalloc(inlen, GFP_KERNEL);
583	if (!inbuf)
584		return -ENOMEM;
585	i = 0;
586	list_for_each_entry(act, &acts->list, list) {
587		MCDI_SET_ARRAY_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS,
588				     i, act->fw_id);
589		i++;
590	}
591	MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_ALLOC_IN_COUNT, i);
592	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_ALLOC, inbuf, inlen,
593			  outbuf, sizeof(outbuf), &outlen);
594	if (rc)
595		goto out_free;
596	if (outlen < sizeof(outbuf)) {
597		rc = -EIO;
598		goto out_free;
599	}
600	acts->fw_id = MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_ALLOC_OUT_ASL_ID);
601	/* We rely on the high bit of ASL IDs always being set.
602	 * The firmware API guarantees this, but let's check it ourselves.
603	 */
604	if (WARN_ON_ONCE(!efx_mae_asl_id(acts->fw_id))) {
605		efx_mae_free_action_set_list(efx, acts);
606		rc = -EIO;
607	}
608out_free:
609	kfree(inbuf);
610	return rc;
611}
612
613int efx_mae_free_action_set_list(struct efx_nic *efx,
614				 struct efx_tc_action_set_list *acts)
615{
616	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_OUT_LEN(1));
617	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_SET_LIST_FREE_IN_LEN(1));
618	size_t outlen;
619	int rc;
620
621	/* If this is just an AS_ID with no ASL wrapper, then there is
622	 * nothing for us to free.  (The AS will be freed later.)
623	 */
624	if (efx_mae_asl_id(acts->fw_id)) {
625		MCDI_SET_DWORD(inbuf, MAE_ACTION_SET_LIST_FREE_IN_ASL_ID,
626			       acts->fw_id);
627		rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_SET_LIST_FREE, inbuf,
628				  sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
629		if (rc)
630			return rc;
631		if (outlen < sizeof(outbuf))
632			return -EIO;
633		/* FW freed a different ID than we asked for, should never happen.
634		 * Warn because it means we've now got a different idea to the FW of
635		 * what action-set-lists exist, which could cause mayhem later.
636		 */
637		if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_SET_LIST_FREE_OUT_FREED_ASL_ID) != acts->fw_id))
638			return -EIO;
639	}
640	/* We're probably about to free @acts, but let's just make sure its
641	 * fw_id is blatted so that it won't look valid if it leaks out.
642	 */
643	acts->fw_id = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL;
644	return 0;
645}
646
647static int efx_mae_populate_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit),
648					   const struct efx_tc_match *match)
649{
650	if (match->mask.ingress_port) {
651		if (~match->mask.ingress_port)
652			return -EOPNOTSUPP;
653		MCDI_STRUCT_SET_DWORD(match_crit,
654				      MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR,
655				      match->value.ingress_port);
656	}
657	MCDI_STRUCT_SET_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_INGRESS_MPORT_SELECTOR_MASK,
658			      match->mask.ingress_port);
659	EFX_POPULATE_DWORD_2(*_MCDI_STRUCT_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS),
660			     MAE_FIELD_MASK_VALUE_PAIRS_V2_IS_IP_FRAG,
661			     match->value.ip_frag,
662			     MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_FIRST_FRAG,
663			     match->value.ip_firstfrag);
664	EFX_POPULATE_DWORD_2(*_MCDI_STRUCT_DWORD(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_FLAGS_MASK),
665			     MAE_FIELD_MASK_VALUE_PAIRS_V2_IS_IP_FRAG,
666			     match->mask.ip_frag,
667			     MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_FIRST_FRAG,
668			     match->mask.ip_firstfrag);
669	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID,
670			     match->value.recirc_id);
671	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_RECIRC_ID_MASK,
672			     match->mask.recirc_id);
673	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETHER_TYPE_BE,
674				match->value.eth_proto);
675	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETHER_TYPE_BE_MASK,
676				match->mask.eth_proto);
677	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_TCI_BE,
678				match->value.vlan_tci[0]);
679	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_TCI_BE_MASK,
680				match->mask.vlan_tci[0]);
681	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_PROTO_BE,
682				match->value.vlan_proto[0]);
683	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN0_PROTO_BE_MASK,
684				match->mask.vlan_proto[0]);
685	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_TCI_BE,
686				match->value.vlan_tci[1]);
687	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_TCI_BE_MASK,
688				match->mask.vlan_tci[1]);
689	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_PROTO_BE,
690				match->value.vlan_proto[1]);
691	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_VLAN1_PROTO_BE_MASK,
692				match->mask.vlan_proto[1]);
693	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_SADDR_BE),
694	       match->value.eth_saddr, ETH_ALEN);
695	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_SADDR_BE_MASK),
696	       match->mask.eth_saddr, ETH_ALEN);
697	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE),
698	       match->value.eth_daddr, ETH_ALEN);
699	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_ETH_DADDR_BE_MASK),
700	       match->mask.eth_daddr, ETH_ALEN);
701	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_PROTO,
702			     match->value.ip_proto);
703	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_PROTO_MASK,
704			     match->mask.ip_proto);
705	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TOS,
706			     match->value.ip_tos);
707	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TOS_MASK,
708			     match->mask.ip_tos);
709	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TTL,
710			     match->value.ip_ttl);
711	MCDI_STRUCT_SET_BYTE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_IP_TTL_MASK,
712			     match->mask.ip_ttl);
713	MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP4_BE,
714				 match->value.src_ip);
715	MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP4_BE_MASK,
716				 match->mask.src_ip);
717	MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP4_BE,
718				 match->value.dst_ip);
719	MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP4_BE_MASK,
720				 match->mask.dst_ip);
721#ifdef CONFIG_IPV6
722	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP6_BE),
723	       &match->value.src_ip6, sizeof(struct in6_addr));
724	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_SRC_IP6_BE_MASK),
725	       &match->mask.src_ip6, sizeof(struct in6_addr));
726	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE),
727	       &match->value.dst_ip6, sizeof(struct in6_addr));
728	memcpy(MCDI_STRUCT_PTR(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_DST_IP6_BE_MASK),
729	       &match->mask.dst_ip6, sizeof(struct in6_addr));
730#endif
731	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_SPORT_BE,
732				match->value.l4_sport);
733	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_SPORT_BE_MASK,
734				match->mask.l4_sport);
735	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_DPORT_BE,
736				match->value.l4_dport);
737	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_L4_DPORT_BE_MASK,
738				match->mask.l4_dport);
739	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE,
740				match->value.tcp_flags);
741	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_FIELD_MASK_VALUE_PAIRS_V2_TCP_FLAGS_BE_MASK,
742				match->mask.tcp_flags);
743	return 0;
744}
745
746int efx_mae_insert_rule(struct efx_nic *efx, const struct efx_tc_match *match,
747			u32 prio, u32 acts_id, u32 *id)
748{
749	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_INSERT_IN_LEN(MAE_FIELD_MASK_VALUE_PAIRS_V2_LEN));
750	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_INSERT_OUT_LEN);
751	MCDI_DECLARE_STRUCT_PTR(match_crit);
752	MCDI_DECLARE_STRUCT_PTR(response);
753	size_t outlen;
754	int rc;
755
756	if (!id)
757		return -EINVAL;
758
759	match_crit = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_MATCH_CRITERIA);
760	response = _MCDI_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_RESPONSE);
761	if (efx_mae_asl_id(acts_id)) {
762		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID, acts_id);
763		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID,
764				      MC_CMD_MAE_ACTION_SET_ALLOC_OUT_ACTION_SET_ID_NULL);
765	} else {
766		/* We only had one AS, so we didn't wrap it in an ASL */
767		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_ASL_ID,
768				      MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL);
769		MCDI_STRUCT_SET_DWORD(response, MAE_ACTION_RULE_RESPONSE_AS_ID, acts_id);
770	}
771	MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_INSERT_IN_PRIO, prio);
772	rc = efx_mae_populate_match_criteria(match_crit, match);
773	if (rc)
774		return rc;
775
776	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_INSERT, inbuf, sizeof(inbuf),
777			  outbuf, sizeof(outbuf), &outlen);
778	if (rc)
779		return rc;
780	if (outlen < sizeof(outbuf))
781		return -EIO;
782	*id = MCDI_DWORD(outbuf, MAE_ACTION_RULE_INSERT_OUT_AR_ID);
783	return 0;
784}
785
786int efx_mae_delete_rule(struct efx_nic *efx, u32 id)
787{
788	MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_ACTION_RULE_DELETE_OUT_LEN(1));
789	MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_ACTION_RULE_DELETE_IN_LEN(1));
790	size_t outlen;
791	int rc;
792
793	MCDI_SET_DWORD(inbuf, MAE_ACTION_RULE_DELETE_IN_AR_ID, id);
794	rc = efx_mcdi_rpc(efx, MC_CMD_MAE_ACTION_RULE_DELETE, inbuf, sizeof(inbuf),
795			  outbuf, sizeof(outbuf), &outlen);
796	if (rc)
797		return rc;
798	if (outlen < sizeof(outbuf))
799		return -EIO;
800	/* FW freed a different ID than we asked for, should also never happen.
801	 * Warn because it means we've now got a different idea to the FW of
802	 * what rules exist, which could cause mayhem later.
803	 */
804	if (WARN_ON(MCDI_DWORD(outbuf, MAE_ACTION_RULE_DELETE_OUT_DELETED_AR_ID) != id))
805		return -EIO;
806	return 0;
807}