diff --git a/doc/guides/rel_notes/release_23_11.rst b/doc/guides/rel_notes/release_23_11.rst
index dd10110fff..066495c622 100644
--- a/doc/guides/rel_notes/release_23_11.rst
+++ b/doc/guides/rel_notes/release_23_11.rst
@@ -59,6 +59,8 @@ New Features
 
   * Added support for transfer flow action INDIRECT with subtype VXLAN_ENCAP.
 
+  * Supported packet replay (multi-count / multi-delivery) in transfer flows.
+
 
 Removed Items
 -------------
diff --git a/drivers/common/sfc_efx/base/efx.h b/drivers/common/sfc_efx/base/efx.h
index b4d8cfe9d8..3312c2fa8f 100644
--- a/drivers/common/sfc_efx/base/efx.h
+++ b/drivers/common/sfc_efx/base/efx.h
@@ -5327,6 +5327,38 @@ efx_table_entry_delete(
 	__in_bcount(data_size)		uint8_t *entry_datap,
 	__in				unsigned int data_size);
 
+/*
+ * Clone the given MAE action set specification
+ * and drop actions COUNT and DELIVER from it.
+ */
+LIBEFX_API
+extern	__checkReturn		efx_rc_t
+efx_mae_action_set_replay(
+	__in			efx_nic_t *enp,
+	__in			const efx_mae_actions_t *spec_orig,
+	__out			efx_mae_actions_t **spec_clonep);
+
+/*
+ * The actual limit may be lower than this.
+ * This define merely limits the number of
+ * entries in a single allocation request.
+ */
+#define EFX_MAE_ACTION_SET_LIST_MAX_NENTRIES	254
+
+LIBEFX_API
+extern	__checkReturn		efx_rc_t
+efx_mae_action_set_list_alloc(
+	__in			efx_nic_t *enp,
+	__in			unsigned int n_asets,
+	__in_ecount(n_asets)	const efx_mae_aset_id_t *aset_ids,
+	__out			efx_mae_aset_list_id_t *aset_list_idp);
+
+LIBEFX_API
+extern	__checkReturn		efx_rc_t
+efx_mae_action_set_list_free(
+	__in			efx_nic_t *enp,
+	__in			const efx_mae_aset_list_id_t *aset_list_idp);
+
 #ifdef	__cplusplus
 }
 #endif
diff --git a/drivers/common/sfc_efx/base/efx_mae.c b/drivers/common/sfc_efx/base/efx_mae.c
index 0d7b24d351..9ae136dcce 100644
--- a/drivers/common/sfc_efx/base/efx_mae.c
+++ b/drivers/common/sfc_efx/base/efx_mae.c
@@ -4273,4 +4273,179 @@ efx_mae_read_mport_journal(
 	return (rc);
 }
 
+	__checkReturn		efx_rc_t
+efx_mae_action_set_replay(
+	__in			efx_nic_t *enp,
+	__in			const efx_mae_actions_t *spec_orig,
+	__out			efx_mae_actions_t **spec_clonep)
+{
+	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
+	efx_mae_actions_t *spec_clone;
+	efx_rc_t rc;
+
+	EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (*spec_clone), spec_clone);
+	if (spec_clone == NULL) {
+		rc = ENOMEM;
+		goto fail1;
+	}
+
+	*spec_clone = *spec_orig;
+
+	spec_clone->ema_rsrc.emar_counter_id.id = EFX_MAE_RSRC_ID_INVALID;
+	spec_clone->ema_actions &= ~(1U << EFX_MAE_ACTION_COUNT);
+	spec_clone->ema_n_count_actions = 0;
+
+	(void)efx_mae_mport_invalid(&spec_clone->ema_deliver_mport);
+	spec_clone->ema_actions &= ~(1U << EFX_MAE_ACTION_DELIVER);
+
+	*spec_clonep = spec_clone;
+
+	return (0);
+
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+	return (rc);
+}
+
+	__checkReturn		efx_rc_t
+efx_mae_action_set_list_alloc(
+	__in			efx_nic_t *enp,
+	__in			unsigned int n_asets,
+	__in_ecount(n_asets)	const efx_mae_aset_id_t *aset_ids,
+	__out			efx_mae_aset_list_id_t *aset_list_idp)
+{
+	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
+	EFX_MCDI_DECLARE_BUF(payload,
+	    MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LENMAX_MCDI2,
+	    MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_LEN);
+	efx_mae_aset_list_id_t aset_list_id;
+	efx_mcdi_req_t req;
+	efx_rc_t rc;
+
+	EFX_STATIC_ASSERT(EFX_MAE_ACTION_SET_LIST_MAX_NENTRIES ==
+	    MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS_MAXNUM_MCDI2);
+
+	EFX_STATIC_ASSERT(EFX_MAE_RSRC_ID_INVALID ==
+	    MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL);
+
+	EFX_STATIC_ASSERT(sizeof (aset_list_idp->id) ==
+	    MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ASL_ID_LEN);
+
+	if (encp->enc_mae_supported == B_FALSE) {
+		rc = ENOTSUP;
+		goto fail1;
+	}
+
+	if (MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LEN(n_asets) >
+	    MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LENMAX_MCDI2) {
+		rc = EINVAL;
+		goto fail2;
+	}
+
+	req.emr_cmd = MC_CMD_MAE_ACTION_SET_LIST_ALLOC;
+	req.emr_in_buf = payload;
+	req.emr_in_length = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_IN_LEN(n_asets);
+	req.emr_out_buf = payload;
+	req.emr_out_length = MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_LEN;
+
+	MCDI_IN_SET_DWORD(req,
+	    MAE_ACTION_SET_LIST_ALLOC_IN_COUNT, n_asets);
+
+	memcpy(MCDI_IN2(req, uint8_t, MAE_ACTION_SET_LIST_ALLOC_IN_AS_IDS),
+	    aset_ids, n_asets * sizeof (*aset_ids));
+
+	efx_mcdi_execute(enp, &req);
+
+	if (req.emr_rc != 0) {
+		rc = req.emr_rc;
+		goto fail3;
+	}
+
+	if (req.emr_out_length_used < MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_LEN) {
+		rc = EMSGSIZE;
+		goto fail4;
+	}
+
+	aset_list_id.id =
+	    MCDI_OUT_DWORD(req, MAE_ACTION_SET_LIST_ALLOC_OUT_ASL_ID);
+	if (aset_list_id.id == EFX_MAE_RSRC_ID_INVALID) {
+		rc = ENOENT;
+		goto fail5;
+	}
+
+	aset_list_idp->id = aset_list_id.id;
+
+	return (0);
+
+fail5:
+	EFSYS_PROBE(fail5);
+fail4:
+	EFSYS_PROBE(fail4);
+fail3:
+	EFSYS_PROBE(fail3);
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+	return (rc);
+}
+
+	__checkReturn		efx_rc_t
+efx_mae_action_set_list_free(
+	__in			efx_nic_t *enp,
+	__in			const efx_mae_aset_list_id_t *aset_list_idp)
+{
+	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
+	EFX_MCDI_DECLARE_BUF(payload,
+	    MC_CMD_MAE_ACTION_SET_LIST_FREE_IN_LEN(1),
+	    MC_CMD_MAE_ACTION_SET_LIST_FREE_OUT_LEN(1));
+	efx_mcdi_req_t req;
+	efx_rc_t rc;
+
+	if (encp->enc_mae_supported == B_FALSE) {
+		rc = ENOTSUP;
+		goto fail1;
+	}
+
+	req.emr_cmd = MC_CMD_MAE_ACTION_SET_LIST_FREE;
+	req.emr_in_buf = payload;
+	req.emr_in_length = MC_CMD_MAE_ACTION_SET_LIST_FREE_IN_LEN(1);
+	req.emr_out_buf = payload;
+	req.emr_out_length = MC_CMD_MAE_ACTION_SET_LIST_FREE_OUT_LEN(1);
+
+	MCDI_IN_SET_DWORD(req,
+	    MAE_ACTION_SET_LIST_FREE_IN_ASL_ID, aset_list_idp->id);
+
+	efx_mcdi_execute(enp, &req);
+
+	if (req.emr_rc != 0) {
+		rc = req.emr_rc;
+		goto fail2;
+	}
+
+	if (req.emr_out_length_used < MC_CMD_MAE_ACTION_SET_LIST_FREE_OUT_LENMIN) {
+		rc = EMSGSIZE;
+		goto fail3;
+	}
+
+	if (MCDI_OUT_DWORD(req, MAE_ACTION_SET_LIST_FREE_OUT_FREED_ASL_ID) !=
+	    aset_list_idp->id) {
+		/* Firmware failed to free the action set list. */
+		rc = EAGAIN;
+		goto fail4;
+	}
+
+	return (0);
+
+fail4:
+	EFSYS_PROBE(fail4);
+fail3:
+	EFSYS_PROBE(fail3);
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+	return (rc);
+}
+
 #endif /* EFSYS_OPT_MAE */
diff --git a/drivers/common/sfc_efx/version.map b/drivers/common/sfc_efx/version.map
index 43e8e52ab9..b2b90f5512 100644
--- a/drivers/common/sfc_efx/version.map
+++ b/drivers/common/sfc_efx/version.map
@@ -97,6 +97,8 @@ INTERNAL {
 	efx_mae_action_set_fill_in_src_mac_id;
 	efx_mae_action_set_free;
 	efx_mae_action_set_get_nb_count;
+	efx_mae_action_set_list_alloc;
+	efx_mae_action_set_list_free;
 	efx_mae_action_set_populate_count;
 	efx_mae_action_set_populate_decap;
 	efx_mae_action_set_populate_decr_ip_ttl;
@@ -111,6 +113,7 @@ INTERNAL {
 	efx_mae_action_set_populate_set_src_mac;
 	efx_mae_action_set_populate_vlan_pop;
 	efx_mae_action_set_populate_vlan_push;
+	efx_mae_action_set_replay;
 	efx_mae_action_set_spec_fini;
 	efx_mae_action_set_spec_init;
 	efx_mae_action_set_specs_equal;
diff --git a/drivers/net/sfc/sfc_mae.c b/drivers/net/sfc/sfc_mae.c
index d4c76a2c63..e3a8b9917a 100644
--- a/drivers/net/sfc/sfc_mae.c
+++ b/drivers/net/sfc/sfc_mae.c
@@ -220,6 +220,33 @@ sfc_mae_attach(struct sfc_adapter *sa)
 			goto fail_mae_alloc_bounce_eh;
 		}
 
+		sfc_log_init(sa, "allocate bounce action set pointer array");
+		mae->bounce_aset_ptrs = rte_calloc("sfc_mae_bounce_aset_ptrs",
+					EFX_MAE_ACTION_SET_LIST_MAX_NENTRIES,
+					sizeof(*mae->bounce_aset_ptrs), 0);
+		if (mae->bounce_aset_ptrs == NULL) {
+			rc = ENOMEM;
+			goto fail_mae_alloc_bounce_aset_ptrs;
+		}
+
+		sfc_log_init(sa, "allocate bounce action set contexts");
+		mae->bounce_aset_ctxs = rte_calloc("sfc_mae_bounce_aset_ctxs",
+					EFX_MAE_ACTION_SET_LIST_MAX_NENTRIES,
+					sizeof(*mae->bounce_aset_ctxs), 0);
+		if (mae->bounce_aset_ctxs == NULL) {
+			rc = ENOMEM;
+			goto fail_mae_alloc_bounce_aset_ctxs;
+		}
+
+		sfc_log_init(sa, "allocate bounce action set ID array");
+		mae->bounce_aset_ids = rte_calloc("sfc_mae_bounce_aset_ids",
+					EFX_MAE_ACTION_SET_LIST_MAX_NENTRIES,
+					sizeof(*mae->bounce_aset_ids), 0);
+		if (mae->bounce_aset_ids == NULL) {
+			rc = ENOMEM;
+			goto fail_mae_alloc_bounce_aset_ids;
+		}
+
 		mae->nb_outer_rule_prios_max = limits.eml_max_n_outer_prios;
 		mae->nb_action_rule_prios_max = limits.eml_max_n_action_prios;
 		mae->encap_types_supported = limits.eml_encap_types_supported;
@@ -230,6 +257,7 @@ sfc_mae_attach(struct sfc_adapter *sa)
 	TAILQ_INIT(&mae->encap_headers);
 	TAILQ_INIT(&mae->counters);
 	TAILQ_INIT(&mae->action_sets);
+	TAILQ_INIT(&mae->action_set_lists);
 	TAILQ_INIT(&mae->action_rules);
 
 	if (encp->enc_mae_admin)
@@ -241,6 +269,15 @@ sfc_mae_attach(struct sfc_adapter *sa)
 
 	return 0;
 
+fail_mae_alloc_bounce_aset_ids:
+	rte_free(mae->bounce_aset_ctxs);
+
+fail_mae_alloc_bounce_aset_ctxs:
+	rte_free(mae->bounce_aset_ptrs);
+
+fail_mae_alloc_bounce_aset_ptrs:
+	rte_free(mae->bounce_eh.buf);
+
 fail_mae_alloc_bounce_eh:
 fail_mae_assign_switch_port:
 fail_mae_assign_switch_domain:
@@ -274,6 +311,9 @@ sfc_mae_detach(struct sfc_adapter *sa)
 	if (status_prev != SFC_MAE_STATUS_ADMIN)
 		return;
 
+	rte_free(mae->bounce_aset_ids);
+	rte_free(mae->bounce_aset_ctxs);
+	rte_free(mae->bounce_aset_ptrs);
 	rte_free(mae->bounce_eh.buf);
 	sfc_mae_counter_registry_fini(&mae->counter_registry);
 
@@ -1036,15 +1076,6 @@ sfc_mae_counter_disable(struct sfc_adapter *sa, struct sfc_mae_counter *counter)
 	--(fw_rsrc->refcnt);
 }
 
-struct sfc_mae_aset_ctx {
-	struct sfc_mae_encap_header	*encap_header;
-	struct sfc_mae_counter		*counter;
-	struct sfc_mae_mac_addr		*dst_mac;
-	struct sfc_mae_mac_addr		*src_mac;
-
-	efx_mae_actions_t		*spec;
-};
-
 static struct sfc_mae_action_set *
 sfc_mae_action_set_attach(struct sfc_adapter *sa,
 			  const struct sfc_mae_aset_ctx *ctx)
@@ -1272,9 +1303,222 @@ sfc_mae_action_set_disable(struct sfc_adapter *sa,
 	--(fw_rsrc->refcnt);
 }
 
+static struct sfc_mae_action_set_list *
+sfc_mae_action_set_list_attach(struct sfc_adapter *sa)
+{
+	struct sfc_mae_action_set_list *action_set_list;
+	struct sfc_mae *mae = &sa->mae;
+
+	SFC_ASSERT(sfc_adapter_is_locked(sa));
+
+	TAILQ_FOREACH(action_set_list, &mae->action_set_lists, entries) {
+		if (action_set_list->nb_action_sets != mae->nb_bounce_asets)
+			continue;
+
+		if (memcmp(action_set_list->action_sets, mae->bounce_aset_ptrs,
+			   sizeof(struct sfc_mae_action_set *) *
+			   mae->nb_bounce_asets) == 0) {
+			sfc_dbg(sa, "attaching to action_set_list=%p",
+				action_set_list);
+			++(action_set_list->refcnt);
+			return action_set_list;
+		}
+	}
+
+	return NULL;
+}
+
+static int
+sfc_mae_action_set_list_add(struct sfc_adapter *sa,
+			    struct sfc_mae_action_set_list **action_set_listp)
+{
+	struct sfc_mae_action_set_list *action_set_list;
+	struct sfc_mae *mae = &sa->mae;
+
+	SFC_ASSERT(sfc_adapter_is_locked(sa));
+
+	action_set_list = rte_zmalloc("sfc_mae_action_set_list",
+				      sizeof(*action_set_list), 0);
+	if (action_set_list == NULL) {
+		sfc_err(sa, "failed to alloc action set list");
+		return ENOMEM;
+	}
+
+	action_set_list->refcnt = 1;
+	action_set_list->nb_action_sets = mae->nb_bounce_asets;
+	action_set_list->fw_rsrc.aset_list_id.id = EFX_MAE_RSRC_ID_INVALID;
+
+	action_set_list->action_sets =
+		rte_malloc("sfc_mae_action_set_list_action_sets",
+			   sizeof(struct sfc_mae_action_set *) *
+			   action_set_list->nb_action_sets, 0);
+	if (action_set_list->action_sets == NULL) {
+		sfc_err(sa, "failed to alloc action set list");
+		rte_free(action_set_list);
+		return ENOMEM;
+	}
+
+	rte_memcpy(action_set_list->action_sets, mae->bounce_aset_ptrs,
+		   sizeof(struct sfc_mae_action_set *) *
+		   action_set_list->nb_action_sets);
+
+	TAILQ_INSERT_TAIL(&mae->action_set_lists, action_set_list, entries);
+
+	*action_set_listp = action_set_list;
+
+	sfc_dbg(sa, "added action_set_list=%p", action_set_list);
+
+	return 0;
+}
+
+static void
+sfc_mae_action_set_list_del(struct sfc_adapter *sa,
+			    struct sfc_mae_action_set_list *action_set_list)
+{
+	struct sfc_mae *mae = &sa->mae;
+	unsigned int i;
+
+	if (action_set_list == NULL)
+		return;
+
+	SFC_ASSERT(sfc_adapter_is_locked(sa));
+	SFC_ASSERT(action_set_list->refcnt != 0);
+
+	--(action_set_list->refcnt);
+
+	if (action_set_list->refcnt != 0)
+		return;
+
+	if (action_set_list->fw_rsrc.aset_list_id.id !=
+	    EFX_MAE_RSRC_ID_INVALID || action_set_list->fw_rsrc.refcnt != 0) {
+		sfc_err(sa, "deleting action_set_list=%p abandons its FW resource: ASL_ID=0x%08x, refcnt=%u",
+			action_set_list,
+			action_set_list->fw_rsrc.aset_list_id.id,
+			action_set_list->fw_rsrc.refcnt);
+	}
+
+	for (i = 0; i < action_set_list->nb_action_sets; ++i)
+		sfc_mae_action_set_del(sa, action_set_list->action_sets[i]);
+
+	TAILQ_REMOVE(&mae->action_set_lists, action_set_list, entries);
+	rte_free(action_set_list->action_sets);
+	rte_free(action_set_list);
+
+	sfc_dbg(sa, "deleted action_set_list=%p", action_set_list);
+}
+
+static int
+sfc_mae_action_set_list_enable(struct sfc_adapter *sa,
+			       struct sfc_mae_action_set_list *action_set_list)
+{
+	struct sfc_mae_fw_rsrc *fw_rsrc;
+	unsigned int i;
+	unsigned int j;
+	int rc;
+
+	if (action_set_list == NULL)
+		return 0;
+
+	SFC_ASSERT(sfc_adapter_is_locked(sa));
+
+	fw_rsrc = &action_set_list->fw_rsrc;
+
+	if (fw_rsrc->refcnt == 0) {
+		struct sfc_mae *mae = &sa->mae;
+
+		SFC_ASSERT(fw_rsrc->aset_list_id.id == EFX_MAE_RSRC_ID_INVALID);
+
+		for (i = 0; i < action_set_list->nb_action_sets; ++i) {
+			const struct sfc_mae_fw_rsrc *as_fw_rsrc;
+
+			rc = sfc_mae_action_set_enable(sa,
+						action_set_list->action_sets[i]);
+			if (rc != 0)
+				goto fail_action_set_enable;
+
+			as_fw_rsrc = &action_set_list->action_sets[i]->fw_rsrc;
+			mae->bounce_aset_ids[i].id = as_fw_rsrc->aset_id.id;
+		}
+
+		rc = efx_mae_action_set_list_alloc(sa->nic,
+						action_set_list->nb_action_sets,
+						mae->bounce_aset_ids,
+						&fw_rsrc->aset_list_id);
+		if (rc != 0) {
+			sfc_err(sa, "failed to enable action_set_list=%p: %s",
+				action_set_list, strerror(rc));
+			goto fail_action_set_list_alloc;
+		}
+
+		sfc_dbg(sa, "enabled action_set_list=%p: ASL_ID=0x%08x",
+			action_set_list, fw_rsrc->aset_list_id.id);
+	}
+
+	++(fw_rsrc->refcnt);
+
+	return 0;
+
+fail_action_set_list_alloc:
+fail_action_set_enable:
+	for (j = 0; j < i; ++j)
+		sfc_mae_action_set_disable(sa, action_set_list->action_sets[j]);
+
+	return rc;
+}
+
+static void
+sfc_mae_action_set_list_disable(struct sfc_adapter *sa,
+				struct sfc_mae_action_set_list *action_set_list)
+{
+	struct sfc_mae_fw_rsrc *fw_rsrc;
+	int rc;
+
+	if (action_set_list == NULL)
+		return;
+
+	SFC_ASSERT(sfc_adapter_is_locked(sa));
+
+	fw_rsrc = &action_set_list->fw_rsrc;
+
+	if (fw_rsrc->aset_list_id.id == EFX_MAE_RSRC_ID_INVALID ||
+	    fw_rsrc->refcnt == 0) {
+		sfc_err(sa, "failed to disable action_set_list=%p: already disabled; ASL_ID=0x%08x, refcnt=%u",
+			action_set_list, fw_rsrc->aset_list_id.id,
+			fw_rsrc->refcnt);
+		return;
+	}
+
+	if (fw_rsrc->refcnt == 1) {
+		unsigned int i;
+
+		rc = efx_mae_action_set_list_free(sa->nic,
+						  &fw_rsrc->aset_list_id);
+		if (rc == 0) {
+			sfc_dbg(sa, "disabled action_set_list=%p with ASL_ID=0x%08x",
+				action_set_list, fw_rsrc->aset_list_id.id);
+		} else {
+			sfc_err(sa, "failed to disable action_set_list=%p with ASL_ID=0x%08x: %s",
+				action_set_list, fw_rsrc->aset_list_id.id,
+				strerror(rc));
+		}
+		fw_rsrc->aset_list_id.id = EFX_MAE_RSRC_ID_INVALID;
+
+		for (i = 0; i < action_set_list->nb_action_sets; ++i) {
+			sfc_mae_action_set_disable(sa,
+					action_set_list->action_sets[i]);
+		}
+	}
+
+	--(fw_rsrc->refcnt);
+}
+
 struct sfc_mae_action_rule_ctx {
 	struct sfc_mae_outer_rule	*outer_rule;
+	/*
+	 * When action_set_list != NULL, action_set is NULL, and vice versa.
+	 */
 	struct sfc_mae_action_set	*action_set;
+	struct sfc_mae_action_set_list	*action_set_list;
 	efx_mae_match_spec_t		*match_spec;
 	uint32_t			ct_mark;
 };
@@ -1305,6 +1549,7 @@ sfc_mae_action_rule_attach(struct sfc_adapter *sa,
 
 		if (rule->outer_rule != ctx->outer_rule ||
 		    rule->action_set != ctx->action_set ||
+		    rule->action_set_list != ctx->action_set_list ||
 		    !!rule->ct_mark != !!ctx->ct_mark)
 			continue;
 
@@ -1380,6 +1625,7 @@ sfc_mae_action_rule_add(struct sfc_adapter *sa,
 
 	rule->outer_rule = ctx->outer_rule;
 	rule->action_set = ctx->action_set;
+	rule->action_set_list = ctx->action_set_list;
 	rule->match_spec = ctx->match_spec;
 
 	rule->fw_rsrc.rule_id.id = EFX_MAE_RSRC_ID_INVALID;
@@ -1416,6 +1662,7 @@ sfc_mae_action_rule_del(struct sfc_adapter *sa,
 	}
 
 	efx_mae_match_spec_fini(sa->nic, rule->match_spec);
+	sfc_mae_action_set_list_del(sa, rule->action_set_list);
 	sfc_mae_action_set_del(sa, rule->action_set);
 	sfc_mae_outer_rule_del(sa, rule->outer_rule);
 
@@ -1429,6 +1676,8 @@ static int
 sfc_mae_action_rule_enable(struct sfc_adapter *sa,
 			   struct sfc_mae_action_rule *rule)
 {
+	const efx_mae_aset_list_id_t *asl_idp = NULL;
+	const efx_mae_aset_id_t *as_idp = NULL;
 	struct sfc_mae_fw_rsrc *fw_rsrc;
 	int rc;
 
@@ -1447,9 +1696,18 @@ sfc_mae_action_rule_enable(struct sfc_adapter *sa,
 	if (rc != 0)
 		goto fail_action_set_enable;
 
-	rc = efx_mae_action_rule_insert(sa->nic, rule->match_spec, NULL,
-					&rule->action_set->fw_rsrc.aset_id,
-					&fw_rsrc->rule_id);
+	rc = sfc_mae_action_set_list_enable(sa, rule->action_set_list);
+	if (rc != 0)
+		goto fail_action_set_list_enable;
+
+	if (rule->action_set_list != NULL)
+		asl_idp = &rule->action_set_list->fw_rsrc.aset_list_id;
+
+	if (rule->action_set != NULL)
+		as_idp = &rule->action_set->fw_rsrc.aset_id;
+
+	rc = efx_mae_action_rule_insert(sa->nic, rule->match_spec, asl_idp,
+					as_idp, &fw_rsrc->rule_id);
 	if (rc != 0) {
 		sfc_err(sa, "failed to enable action_rule=%p: %s",
 			rule, strerror(rc));
@@ -1467,6 +1725,9 @@ sfc_mae_action_rule_enable(struct sfc_adapter *sa,
 	return 0;
 
 fail_action_rule_insert:
+	sfc_mae_action_set_list_disable(sa, rule->action_set_list);
+
+fail_action_set_list_enable:
 	sfc_mae_action_set_disable(sa, rule->action_set);
 
 fail_action_set_enable:
@@ -1505,6 +1766,8 @@ sfc_mae_action_rule_disable(struct sfc_adapter *sa,
 
 		fw_rsrc->rule_id.id = EFX_MAE_RSRC_ID_INVALID;
 
+		sfc_mae_action_set_list_disable(sa, rule->action_set_list);
+
 		sfc_mae_action_set_disable(sa, rule->action_set);
 
 		sfc_mae_outer_rule_disable(sa, rule->outer_rule,
@@ -4198,7 +4461,7 @@ sfc_mae_rule_parse_action_count(struct sfc_adapter *sa,
 }
 
 static int
-sfc_mae_rule_parse_action_indirect(struct sfc_adapter *sa,
+sfc_mae_rule_parse_action_indirect(struct sfc_adapter *sa, bool replayable_only,
 				   const struct rte_flow_action_handle *handle,
 				   enum sfc_ft_rule_type ft_rule_type,
 				   struct sfc_mae_aset_ctx *ctx,
@@ -4211,6 +4474,9 @@ sfc_mae_rule_parse_action_indirect(struct sfc_adapter *sa,
 		if (entry == handle) {
 			sfc_dbg(sa, "attaching to indirect_action=%p", entry);
 
+			if (replayable_only)
+				goto replayable_actions;
+
 			switch (entry->type) {
 			case RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP:
 				if (ctx->encap_header != NULL) {
@@ -4229,18 +4495,30 @@ sfc_mae_rule_parse_action_indirect(struct sfc_adapter *sa,
 				ctx->encap_header = entry->encap_header;
 				++(ctx->encap_header->refcnt);
 				break;
+			default:
+				goto replayable_actions;
+			}
+
+			return 0;
+
+replayable_actions:
+			switch (entry->type) {
 			case RTE_FLOW_ACTION_TYPE_COUNT:
+				if (!replayable_only && ctx->counter != NULL) {
+					/*
+					 * Signal the caller to "replay" the action
+					 * set context and re-invoke this function.
+					 */
+					return EEXIST;
+				}
+
 				if (ft_rule_type != SFC_FT_RULE_NONE) {
 					return rte_flow_error_set(error, EINVAL,
 					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
 					  "cannot use indirect count action in tunnel model");
 				}
 
-				if (ctx->counter != NULL) {
-					return rte_flow_error_set(error, EINVAL,
-					  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
-					  "cannot have multiple actions COUNT in one flow");
-				}
+				SFC_ASSERT(ctx->counter == NULL);
 
 				rc = efx_mae_action_set_populate_count(ctx->spec);
 				if (rc != 0) {
@@ -4253,8 +4531,9 @@ sfc_mae_rule_parse_action_indirect(struct sfc_adapter *sa,
 				++(ctx->counter->refcnt);
 				break;
 			default:
-				SFC_ASSERT(B_FALSE);
-				break;
+				return rte_flow_error_set(error, EINVAL,
+				  RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+				  "the indirect action handle cannot be used");
 			}
 
 			return 0;
@@ -4416,31 +4695,89 @@ static const char * const action_names[] = {
 	[RTE_FLOW_ACTION_TYPE_JUMP] = "JUMP",
 };
 
+static void sfc_mae_bounce_eh_invalidate(struct sfc_mae_bounce_eh *bounce_eh);
+
+static int sfc_mae_process_encap_header(struct sfc_adapter *sa,
+				const struct sfc_mae_bounce_eh *bounce_eh,
+				struct sfc_mae_encap_header **encap_headerp);
+
+static int
+sfc_mae_aset_ctx_replay(struct sfc_adapter *sa, struct sfc_mae_aset_ctx **ctxp)
+{
+	const struct sfc_mae_aset_ctx *ctx_cur;
+	struct sfc_mae_aset_ctx *ctx_new;
+	struct sfc_mae *mae = &sa->mae;
+	int rc;
+
+	ctx_cur = &mae->bounce_aset_ctxs[mae->nb_bounce_asets];
+
+	++(mae->nb_bounce_asets);
+
+	if (mae->nb_bounce_asets == EFX_MAE_ACTION_SET_LIST_MAX_NENTRIES)
+		return ENOSPC;
+
+	ctx_new = &mae->bounce_aset_ctxs[mae->nb_bounce_asets];
+
+	*ctx_new = *ctx_cur;
+	ctx_new->counter = NULL;
+	ctx_new->fate_set = false;
+
+	/*
+	 * This clones the action set specification and drops
+	 * actions COUNT and DELIVER from the clone so that
+	 * such can be added to it by later action parsing.
+	 */
+	rc = efx_mae_action_set_replay(sa->nic, ctx_cur->spec, &ctx_new->spec);
+	if (rc != 0)
+		return rc;
+
+	*ctxp = ctx_new;
+
+	return 0;
+}
+
 static int
 sfc_mae_rule_parse_action(struct sfc_adapter *sa,
 			  const struct rte_flow_action *action,
 			  struct rte_flow *flow, bool ct,
 			  struct sfc_mae_actions_bundle *bundle,
-			  struct sfc_mae_aset_ctx *ctx,
 			  struct rte_flow_error *error)
 {
 	struct sfc_flow_spec_mae *spec_mae = &flow->spec.mae;
 	const struct sfc_mae_outer_rule *outer_rule = spec_mae->outer_rule;
 	efx_counter_type_t mae_counter_type = EFX_COUNTER_TYPE_ACTION;
 	const uint64_t rx_metadata = sa->negotiated_rx_metadata;
-	struct sfc_mae_counter **counterp = &ctx->counter;
-	efx_mae_actions_t *spec = ctx->spec;
-	efx_mae_actions_t *spec_ptr = spec;
 	unsigned int switch_port_type_mask;
+	struct sfc_mae_counter **counterp;
+	struct sfc_mae *mae = &sa->mae;
+	struct sfc_mae_aset_ctx *ctx;
+	efx_mae_actions_t *spec_ptr;
 	bool custom_error = B_FALSE;
+	bool new_fate_set = B_FALSE;
+	bool need_replay = false;
+	efx_mae_actions_t *spec;
 	int rc = 0;
 
+	ctx = &mae->bounce_aset_ctxs[mae->nb_bounce_asets];
+	counterp = &ctx->counter;
+	spec = ctx->spec;
+	spec_ptr = spec;
+
 	if (ct) {
 		mae_counter_type = EFX_COUNTER_TYPE_CONNTRACK;
 		counterp = &spec_mae->ct_counter;
 		spec_ptr = NULL;
 	}
 
+	if (mae->nb_bounce_asets != 0 || ctx->fate_set) {
+		/*
+		 * When at least one delivery action has been encountered,
+		 * non-replayable actions (packet edits, for instance)
+		 * will be turned down.
+		 */
+		goto replayable_actions;
+	}
+
 	switch (action->type) {
 	case RTE_FLOW_ACTION_TYPE_VXLAN_DECAP:
 		SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_VXLAN_DECAP,
@@ -4516,10 +4853,18 @@ sfc_mae_rule_parse_action(struct sfc_adapter *sa,
 	case RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP:
 		SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP,
 				       bundle->actions_mask);
-		rc = sfc_mae_rule_parse_action_vxlan_encap(&sa->mae,
-							   action->conf,
+
+		/* Cleanup after previous encap. header bounce buffer usage. */
+		sfc_mae_bounce_eh_invalidate(&mae->bounce_eh);
+
+		rc = sfc_mae_rule_parse_action_vxlan_encap(mae, action->conf,
 							   spec, error);
-		custom_error = B_TRUE;
+		if (rc == 0) {
+			rc = sfc_mae_process_encap_header(sa, &mae->bounce_eh,
+							  &ctx->encap_header);
+		} else {
+			custom_error = B_TRUE;
+		}
 		break;
 	case RTE_FLOW_ACTION_TYPE_COUNT:
 		SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_COUNT,
@@ -4531,9 +4876,13 @@ sfc_mae_rule_parse_action(struct sfc_adapter *sa,
 	case RTE_FLOW_ACTION_TYPE_INDIRECT:
 		SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_INDIRECT,
 				       bundle->actions_mask);
-		rc = sfc_mae_rule_parse_action_indirect(sa, action->conf,
+		rc = sfc_mae_rule_parse_action_indirect(sa, false, action->conf,
 							spec_mae->ft_rule_type,
 							ctx, error);
+		if (rc == EEXIST) {
+			rc = 0;
+			goto replayable_actions;
+		}
 		custom_error = B_TRUE;
 		break;
 	case RTE_FLOW_ACTION_TYPE_FLAG:
@@ -4564,6 +4913,88 @@ sfc_mae_rule_parse_action(struct sfc_adapter *sa,
 			custom_error = B_TRUE;
 		}
 		break;
+	case RTE_FLOW_ACTION_TYPE_JUMP:
+		if (spec_mae->ft_rule_type == SFC_FT_RULE_TUNNEL) {
+			/* Workaround. See sfc_flow_parse_rte_to_mae() */
+			break;
+		}
+		/* FALLTHROUGH */
+	default:
+		goto replayable_actions;
+	}
+
+	goto skip_replayable_actions;
+
+replayable_actions:
+	/*
+	 * Decide whether the current action set context is
+	 * complete. If yes, "replay" it = go to a new one.
+	 */
+	switch (action->type) {
+	case RTE_FLOW_ACTION_TYPE_INDIRECT:
+		if (ctx->fate_set || ctx->counter != NULL)
+			need_replay = true;
+		break;
+	case RTE_FLOW_ACTION_TYPE_PF:
+		/* FALLTHROUGH */
+	case RTE_FLOW_ACTION_TYPE_VF:
+		/* FALLTHROUGH */
+	case RTE_FLOW_ACTION_TYPE_PORT_ID:
+		/* FALLTHROUGH */
+	case RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR:
+		/* FALLTHROUGH */
+	case RTE_FLOW_ACTION_TYPE_REPRESENTED_PORT:
+		/* FALLTHROUGH */
+	case RTE_FLOW_ACTION_TYPE_DROP:
+		if (ctx->fate_set)
+			need_replay = true;
+
+		new_fate_set = true;
+		break;
+	default:
+		return rte_flow_error_set(error, ENOTSUP,
+				RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+				"Unsupported action");
+	}
+
+	if (need_replay) {
+		if (spec_mae->ft_rule_type != SFC_FT_RULE_NONE) {
+			/* No support for packet replay in tunnel offload */
+			rc = EINVAL;
+			goto skip_replayable_actions;
+		}
+
+		if (!ctx->fate_set) {
+			/*
+			 * With regard to replayable actions, the current action
+			 * set is only needed to hold one of the counters.
+			 * That is, it does not have a fate action, so
+			 * add one to suppress undesired delivery.
+			 */
+			rc = efx_mae_action_set_populate_drop(ctx->spec);
+			if (rc != 0) {
+				return rte_flow_error_set(error, rc,
+					RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+					"failed to auto-add action DROP");
+			}
+		}
+
+		rc = sfc_mae_aset_ctx_replay(sa, &ctx);
+		if (rc != 0)
+			goto skip_replayable_actions;
+
+		spec = ctx->spec;
+	}
+
+	ctx->fate_set = new_fate_set;
+
+	switch (action->type) {
+	case RTE_FLOW_ACTION_TYPE_INDIRECT:
+		rc = sfc_mae_rule_parse_action_indirect(sa, true, action->conf,
+							spec_mae->ft_rule_type,
+							ctx, error);
+		custom_error = B_TRUE;
+		break;
 	case RTE_FLOW_ACTION_TYPE_PF:
 		SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_PF,
 				       bundle->actions_mask);
@@ -4604,18 +5035,12 @@ sfc_mae_rule_parse_action(struct sfc_adapter *sa,
 				       bundle->actions_mask);
 		rc = efx_mae_action_set_populate_drop(spec);
 		break;
-	case RTE_FLOW_ACTION_TYPE_JUMP:
-		if (spec_mae->ft_rule_type == SFC_FT_RULE_TUNNEL) {
-			/* Workaround. See sfc_flow_parse_rte_to_mae() */
-			break;
-		}
-		/* FALLTHROUGH */
 	default:
-		return rte_flow_error_set(error, ENOTSUP,
-				RTE_FLOW_ERROR_TYPE_ACTION, NULL,
-				"Unsupported action");
+		SFC_ASSERT(B_FALSE);
+		break;
 	}
 
+skip_replayable_actions:
 	if (rc == 0) {
 		bundle->actions_mask |= (1ULL << action->type);
 	} else if (!custom_error) {
@@ -4657,6 +5082,82 @@ sfc_mae_process_encap_header(struct sfc_adapter *sa,
 	return sfc_mae_encap_header_add(sa, bounce_eh, encap_headerp);
 }
 
+static int
+sfc_mae_rule_parse_replay(struct sfc_adapter *sa,
+			  struct sfc_mae_action_rule_ctx *action_rule_ctx)
+{
+	struct sfc_mae *mae = &sa->mae;
+	struct sfc_mae_action_set *base_aset;
+	struct sfc_mae_action_set **asetp;
+	struct sfc_mae_aset_ctx *ctx;
+	unsigned int i;
+	unsigned int j;
+	int rc;
+
+	if (mae->nb_bounce_asets == 1)
+		return 0;
+
+	mae->bounce_aset_ptrs[0] = action_rule_ctx->action_set;
+	base_aset = mae->bounce_aset_ptrs[0];
+
+	for (i = 1; i < mae->nb_bounce_asets; ++i) {
+		asetp = &mae->bounce_aset_ptrs[i];
+		ctx = &mae->bounce_aset_ctxs[i];
+
+		*asetp = sfc_mae_action_set_attach(sa, ctx);
+		if (*asetp != NULL) {
+			efx_mae_action_set_spec_fini(sa->nic, ctx->spec);
+			sfc_mae_counter_del(sa, ctx->counter);
+			continue;
+		}
+
+		rc = sfc_mae_action_set_add(sa, ctx, asetp);
+		if (rc != 0)
+			goto fail_action_set_add;
+
+		if (base_aset->encap_header != NULL)
+			++(base_aset->encap_header->refcnt);
+
+		if (base_aset->dst_mac_addr != NULL)
+			++(base_aset->dst_mac_addr->refcnt);
+
+		if (base_aset->src_mac_addr != NULL)
+			++(base_aset->src_mac_addr->refcnt);
+	}
+
+	action_rule_ctx->action_set_list = sfc_mae_action_set_list_attach(sa);
+	if (action_rule_ctx->action_set_list != NULL) {
+		for (i = 0; i < mae->nb_bounce_asets; ++i)
+			sfc_mae_action_set_del(sa, mae->bounce_aset_ptrs[i]);
+	} else {
+		rc = sfc_mae_action_set_list_add(sa,
+					&action_rule_ctx->action_set_list);
+		if (rc != 0)
+			goto fail_action_set_list_add;
+	}
+
+	action_rule_ctx->action_set = NULL;
+
+	return 0;
+
+fail_action_set_list_add:
+fail_action_set_add:
+	for (j = i; j < mae->nb_bounce_asets; ++j) {
+		ctx = &mae->bounce_aset_ctxs[j];
+
+		efx_mae_action_set_spec_fini(sa->nic, ctx->spec);
+		sfc_mae_counter_del(sa, ctx->counter);
+	}
+
+	while (--i > 0) {
+		asetp = &mae->bounce_aset_ptrs[i];
+
+		sfc_mae_action_set_del(sa, *asetp);
+	}
+
+	return rc;
+}
+
 static int
 sfc_mae_rule_parse_actions(struct sfc_adapter *sa,
 			   const struct rte_flow_action actions[],
@@ -4668,8 +5169,9 @@ sfc_mae_rule_parse_actions(struct sfc_adapter *sa,
 	struct sfc_mae_actions_bundle bundle = {0};
 	bool ct = (action_rule_ctx->ct_mark != 0);
 	const struct rte_flow_action *action;
-	struct sfc_mae_aset_ctx ctx = {0};
+	struct sfc_mae_aset_ctx *last_ctx;
 	struct sfc_mae *mae = &sa->mae;
+	struct sfc_mae_aset_ctx *ctx;
 	int rc;
 
 	rte_errno = 0;
@@ -4680,7 +5182,18 @@ sfc_mae_rule_parse_actions(struct sfc_adapter *sa,
 				"NULL actions");
 	}
 
-	rc = efx_mae_action_set_spec_init(sa->nic, &ctx.spec);
+	/*
+	 * Cleanup after action parsing of the previous flow.
+	 *
+	 * This particular variable always points at the
+	 * 1st (base) action set context, which can hold
+	 * both non-replayable and replayable actions.
+	 */
+	ctx = &mae->bounce_aset_ctxs[0];
+	memset(ctx, 0, sizeof(*ctx));
+	mae->nb_bounce_asets = 0;
+
+	rc = efx_mae_action_set_spec_init(sa->nic, &ctx->spec);
 	if (rc != 0)
 		goto fail_action_set_spec_init;
 
@@ -4688,7 +5201,7 @@ sfc_mae_rule_parse_actions(struct sfc_adapter *sa,
 		bool have_user_action_count = false;
 
 		/* TUNNEL rules don't decapsulate packets. SWITCH rules do. */
-		rc = efx_mae_action_set_populate_decap(ctx.spec);
+		rc = efx_mae_action_set_populate_decap(ctx->spec);
 		if (rc != 0)
 			goto fail_enforce_ft_decap;
 
@@ -4708,63 +5221,62 @@ sfc_mae_rule_parse_actions(struct sfc_adapter *sa,
 			 * packets hitting this rule contribute to the tunnel's
 			 * total number of hits. See sfc_mae_counter_get().
 			 */
-			rc = efx_mae_action_set_populate_count(ctx.spec);
+			rc = efx_mae_action_set_populate_count(ctx->spec);
 			if (rc != 0)
 				goto fail_enforce_ft_count;
 
-			rc = sfc_mae_counter_add(sa, NULL, &ctx.counter);
+			rc = sfc_mae_counter_add(sa, NULL, &ctx->counter);
 			if (rc != 0)
 				goto fail_enforce_ft_count;
 		}
 	}
 
-	/* Cleanup after previous encap. header bounce buffer usage. */
-	sfc_mae_bounce_eh_invalidate(&mae->bounce_eh);
-
 	for (action = actions;
 	     action->type != RTE_FLOW_ACTION_TYPE_END; ++action) {
-		rc = sfc_mae_actions_bundle_sync(action, &bundle, spec_mae,
-						 ctx.spec, ct, error);
-		if (rc != 0)
-			goto fail_rule_parse_action;
+		if (mae->nb_bounce_asets == 0) {
+			rc = sfc_mae_actions_bundle_sync(action, &bundle,
+							 spec_mae, ctx->spec,
+							 ct, error);
+			if (rc != 0)
+				goto fail_rule_parse_action;
+		}
 
 		rc = sfc_mae_rule_parse_action(sa, action, flow, ct,
-					       &bundle, &ctx, error);
+					       &bundle, error);
 		if (rc != 0)
 			goto fail_rule_parse_action;
 	}
 
-	rc = sfc_mae_actions_bundle_sync(action, &bundle, spec_mae,
-					 ctx.spec, ct, error);
-	if (rc != 0)
-		goto fail_rule_parse_action;
-
-	rc = sfc_mae_process_encap_header(sa, &mae->bounce_eh,
-					  &ctx.encap_header);
-	if (rc != 0)
-		goto fail_process_encap_header;
+	if (mae->nb_bounce_asets == 0) {
+		rc = sfc_mae_actions_bundle_sync(action, &bundle, spec_mae,
+						 ctx->spec, ct, error);
+		if (rc != 0)
+			goto fail_rule_parse_action;
+	}
 
 	switch (spec_mae->ft_rule_type) {
 	case SFC_FT_RULE_NONE:
 		break;
 	case SFC_FT_RULE_TUNNEL:
 		/* Workaround. See sfc_flow_parse_rte_to_mae() */
-		rc = sfc_mae_rule_parse_action_pf_vf(sa, NULL, ctx.spec);
+		rc = sfc_mae_rule_parse_action_pf_vf(sa, NULL, ctx->spec);
 		if (rc != 0)
 			goto fail_workaround_tunnel_delivery;
 
-		if (ctx.counter != NULL)
-			(ctx.counter)->ft_ctx = spec_mae->ft_ctx;
+		if (ctx->counter != NULL)
+			(ctx->counter)->ft_ctx = spec_mae->ft_ctx;
+
+		ctx->fate_set = true;
 		break;
 	case SFC_FT_RULE_SWITCH:
 		/*
 		 * Packets that go to the rule's AR have FT mark set (from
 		 * the TUNNEL rule OR's RECIRC_ID). Reset the mark to zero.
 		 */
-		efx_mae_action_set_populate_mark_reset(ctx.spec);
+		efx_mae_action_set_populate_mark_reset(ctx->spec);
 
-		if (ctx.counter != NULL) {
-			(ctx.counter)->ft_switch_hit_counter =
+		if (ctx->counter != NULL) {
+			(ctx->counter)->ft_switch_hit_counter =
 				&spec_mae->ft_ctx->switch_hit_counter;
 		} else if (sfc_mae_counter_stream_enabled(sa)) {
 			SFC_ASSERT(ct);
@@ -4777,48 +5289,52 @@ sfc_mae_rule_parse_actions(struct sfc_adapter *sa,
 		SFC_ASSERT(B_FALSE);
 	}
 
-	/*
-	 * A DPDK flow entry must specify a fate action, which the parser
-	 * converts into a DELIVER action in a libefx action set. An
-	 * attempt to replace the action in the action set should
-	 * fail. If it succeeds then report an error, as the
-	 * parsed flow entry did not contain a fate action.
-	 */
-	rc = efx_mae_action_set_populate_drop(ctx.spec);
-	if (rc == 0) {
+	last_ctx = &mae->bounce_aset_ctxs[mae->nb_bounce_asets];
+	++(mae->nb_bounce_asets);
+
+	if (!last_ctx->fate_set) {
 		rc = rte_flow_error_set(error, EINVAL,
 					RTE_FLOW_ERROR_TYPE_ACTION, NULL,
 					"no fate action found");
 		goto fail_check_fate_action;
 	}
 
-	action_rule_ctx->action_set = sfc_mae_action_set_attach(sa, &ctx);
+	action_rule_ctx->action_set = sfc_mae_action_set_attach(sa, ctx);
 	if (action_rule_ctx->action_set != NULL) {
-		sfc_mae_counter_del(sa, ctx.counter);
-		sfc_mae_mac_addr_del(sa, ctx.src_mac);
-		sfc_mae_mac_addr_del(sa, ctx.dst_mac);
-		sfc_mae_encap_header_del(sa, ctx.encap_header);
-		efx_mae_action_set_spec_fini(sa->nic, ctx.spec);
-		return 0;
+		sfc_mae_counter_del(sa, ctx->counter);
+		sfc_mae_mac_addr_del(sa, ctx->src_mac);
+		sfc_mae_mac_addr_del(sa, ctx->dst_mac);
+		sfc_mae_encap_header_del(sa, ctx->encap_header);
+		efx_mae_action_set_spec_fini(sa->nic, ctx->spec);
+	} else {
+		rc = sfc_mae_action_set_add(sa, ctx,
+					    &action_rule_ctx->action_set);
+		if (rc != 0)
+			goto fail_action_set_add;
 	}
 
-	rc = sfc_mae_action_set_add(sa, &ctx, &action_rule_ctx->action_set);
+	memset(ctx, 0, sizeof(*ctx));
+
+	rc = sfc_mae_rule_parse_replay(sa, action_rule_ctx);
 	if (rc != 0)
-		goto fail_action_set_add;
+		goto fail_rule_parse_replay;
 
 	return 0;
 
+fail_rule_parse_replay:
+	sfc_mae_action_set_del(sa, action_rule_ctx->action_set);
+
 fail_action_set_add:
 fail_check_fate_action:
 fail_workaround_tunnel_delivery:
-	sfc_mae_encap_header_del(sa, ctx.encap_header);
-
-fail_process_encap_header:
 fail_rule_parse_action:
-	sfc_mae_counter_del(sa, ctx.counter);
-	sfc_mae_mac_addr_del(sa, ctx.src_mac);
-	sfc_mae_mac_addr_del(sa, ctx.dst_mac);
-	efx_mae_action_set_spec_fini(sa->nic, ctx.spec);
+	sfc_mae_encap_header_del(sa, ctx->encap_header);
+	sfc_mae_counter_del(sa, ctx->counter);
+	sfc_mae_mac_addr_del(sa, ctx->src_mac);
+	sfc_mae_mac_addr_del(sa, ctx->dst_mac);
+
+	if (ctx->spec != NULL)
+		efx_mae_action_set_spec_fini(sa->nic, ctx->spec);
 
 fail_enforce_ft_count:
 fail_enforce_ft_decap:
@@ -4875,6 +5391,7 @@ sfc_mae_rule_parse(struct sfc_adapter *sa, const struct rte_flow_item pattern[],
 					error);
 	if (rc == 0) {
 		efx_mae_match_spec_fini(sa->nic, ctx.match_spec);
+		sfc_mae_action_set_list_del(sa, ctx.action_set_list);
 		sfc_mae_action_set_del(sa, ctx.action_set);
 		sfc_mae_outer_rule_del(sa, ctx.outer_rule);
 	} else if (rc == -ENOENT) {
@@ -4902,6 +5419,7 @@ sfc_mae_rule_parse(struct sfc_adapter *sa, const struct rte_flow_item pattern[],
 	if (ctx.match_spec != NULL)
 		efx_mae_match_spec_fini(sa->nic, ctx.match_spec);
 
+	sfc_mae_action_set_list_del(sa, ctx.action_set_list);
 	sfc_mae_action_set_del(sa, ctx.action_set);
 	sfc_mae_outer_rule_del(sa, ctx.outer_rule);
 
@@ -5120,6 +5638,7 @@ sfc_mae_query_counter(struct sfc_adapter *sa,
 	const struct rte_flow_action_count *conf = action->conf;
 	struct sfc_mae_counter *counters[1 /* action rule counter */ +
 					 1 /* conntrack counter */];
+	struct sfc_mae_counter *counter;
 	unsigned int i;
 	int rc;
 
@@ -5137,7 +5656,7 @@ sfc_mae_query_counter(struct sfc_adapter *sa,
 	counters[1] = spec->ct_counter;
 
 	for (i = 0; i < RTE_DIM(counters); ++i) {
-		struct sfc_mae_counter *counter = counters[i];
+		counter = counters[i];
 
 		if (counter == NULL)
 			continue;
@@ -5155,6 +5674,29 @@ sfc_mae_query_counter(struct sfc_adapter *sa,
 		}
 	}
 
+	if (action_rule == NULL || action_rule->action_set_list == NULL)
+		goto exit;
+
+	for (i = 0; i < action_rule->action_set_list->nb_action_sets; ++i) {
+		counter = action_rule->action_set_list->action_sets[i]->counter;
+
+		if (counter == NULL || counter->indirect)
+			continue;
+
+		if (conf == NULL ||
+		    (counter->rte_id_valid && conf->id == counter->rte_id)) {
+			rc = sfc_mae_counter_get(sa, counter, data);
+			if (rc != 0) {
+				return rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ACTION, action,
+					"Queried flow rule counter action is invalid");
+			}
+
+			return 0;
+		}
+	}
+
+exit:
 	return rte_flow_error_set(error, ENOENT,
 				  RTE_FLOW_ERROR_TYPE_ACTION, action,
 				  "no such flow rule action or such count ID");
diff --git a/drivers/net/sfc/sfc_mae.h b/drivers/net/sfc/sfc_mae.h
index 7f4c3324bd..d5509b0582 100644
--- a/drivers/net/sfc/sfc_mae.h
+++ b/drivers/net/sfc/sfc_mae.h
@@ -27,6 +27,7 @@ struct sfc_mae_fw_rsrc {
 	unsigned int			refcnt;
 	RTE_STD_C11
 	union {
+		efx_mae_aset_list_id_t	aset_list_id;
 		efx_counter_t		counter_id;
 		efx_mae_aset_id_t	aset_id;
 		efx_mae_rule_id_t	rule_id;
@@ -106,12 +107,27 @@ struct sfc_mae_action_set {
 
 TAILQ_HEAD(sfc_mae_action_sets, sfc_mae_action_set);
 
+/** Action set list registry entry */
+struct sfc_mae_action_set_list {
+	TAILQ_ENTRY(sfc_mae_action_set_list)	entries;
+	unsigned int				refcnt;
+	unsigned int				nb_action_sets;
+	struct sfc_mae_action_set		**action_sets;
+	struct sfc_mae_fw_rsrc			fw_rsrc;
+};
+
+TAILQ_HEAD(sfc_mae_action_set_lists, sfc_mae_action_set_list);
+
 /** Action rule registry entry */
 struct sfc_mae_action_rule {
 	TAILQ_ENTRY(sfc_mae_action_rule)	entries;
 	uint32_t				ct_mark;
 	struct sfc_mae_outer_rule		*outer_rule;
+	/*
+	 * When action_set_list != NULL, action_set is NULL, and vice versa.
+	 */
 	struct sfc_mae_action_set		*action_set;
+	struct sfc_mae_action_set_list		*action_set_list;
 	efx_mae_match_spec_t			*match_spec;
 	struct sfc_mae_fw_rsrc			fw_rsrc;
 	unsigned int				refcnt;
@@ -205,6 +221,18 @@ struct sfc_mae_counter_registry {
 	} polling;
 };
 
+/* Entry format for the action parsing bounce buffer */
+struct sfc_mae_aset_ctx {
+	struct sfc_mae_encap_header	*encap_header;
+	struct sfc_mae_counter		*counter;
+	struct sfc_mae_mac_addr		*dst_mac;
+	struct sfc_mae_mac_addr		*src_mac;
+
+	bool				fate_set;
+
+	efx_mae_actions_t		*spec;
+};
+
 struct sfc_mae {
 	/** Assigned switch domain identifier */
 	uint16_t			switch_domain_id;
@@ -226,10 +254,19 @@ struct sfc_mae {
 	struct sfc_mae_mac_addrs	mac_addrs;
 	/** Action set registry */
 	struct sfc_mae_action_sets	action_sets;
+	/** Action set list registry */
+	struct sfc_mae_action_set_lists	action_set_lists;
 	/** Action rule registry */
 	struct sfc_mae_action_rules	action_rules;
 	/** Encap. header bounce buffer */
 	struct sfc_mae_bounce_eh	bounce_eh;
+	/**
+	 * Action parsing bounce buffers
+	 */
+	struct sfc_mae_action_set	**bounce_aset_ptrs;
+	struct sfc_mae_aset_ctx		*bounce_aset_ctxs;
+	efx_mae_aset_id_t		*bounce_aset_ids;
+	unsigned int			nb_bounce_asets;
 	/** Flag indicating whether counter-only RxQ is running */
 	bool				counter_rxq_running;
 	/** Counter record registry */
