enum mlxsw_sp_sample_trigger_type {
        MLXSW_SP_SAMPLE_TRIGGER_TYPE_INGRESS,
        MLXSW_SP_SAMPLE_TRIGGER_TYPE_EGRESS,
+       MLXSW_SP_SAMPLE_TRIGGER_TYPE_POLICY_ENGINE,
 };
 
 struct mlxsw_sp_sample_trigger {
        enum mlxsw_sp_sample_trigger_type type;
-       u8 local_port;
+       u8 local_port; /* Reserved when trigger type is not ingress / egress. */
 };
 
 struct mlxsw_sp_sample_params {
 int mlxsw_sp_acl_rulei_act_fid_set(struct mlxsw_sp *mlxsw_sp,
                                   struct mlxsw_sp_acl_rule_info *rulei,
                                   u16 fid, struct netlink_ext_ack *extack);
+int mlxsw_sp_acl_rulei_act_sample(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_acl_rule_info *rulei,
+                                 struct mlxsw_sp_flow_block *block,
+                                 struct psample_group *psample_group, u32 rate,
+                                 u32 trunc_size, bool truncate,
+                                 struct netlink_ext_ack *extack);
 
 struct mlxsw_sp_acl_rule;
 
 
        return mlxsw_afa_block_append_fid_set(rulei->act_block, fid, extack);
 }
 
+int mlxsw_sp_acl_rulei_act_sample(struct mlxsw_sp *mlxsw_sp,
+                                 struct mlxsw_sp_acl_rule_info *rulei,
+                                 struct mlxsw_sp_flow_block *block,
+                                 struct psample_group *psample_group, u32 rate,
+                                 u32 trunc_size, bool truncate,
+                                 struct netlink_ext_ack *extack)
+{
+       struct mlxsw_sp_flow_block_binding *binding;
+       struct mlxsw_sp_port *mlxsw_sp_port;
+
+       if (!list_is_singular(&block->binding_list)) {
+               NL_SET_ERR_MSG_MOD(extack, "Only a single sampling source is allowed");
+               return -EOPNOTSUPP;
+       }
+       binding = list_first_entry(&block->binding_list,
+                                  struct mlxsw_sp_flow_block_binding, list);
+       mlxsw_sp_port = binding->mlxsw_sp_port;
+
+       return mlxsw_afa_block_append_sampler(rulei->act_block,
+                                             mlxsw_sp_port->local_port,
+                                             psample_group, rate, trunc_size,
+                                             truncate, binding->ingress,
+                                             extack);
+}
+
 struct mlxsw_sp_acl_rule *
 mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp,
                         struct mlxsw_sp_acl_ruleset *ruleset,
 
                             policer_index);
 }
 
+static int mlxsw_sp1_act_sampler_add(void *priv, u8 local_port,
+                                    struct psample_group *psample_group,
+                                    u32 rate, u32 trunc_size, bool truncate,
+                                    bool ingress, int *p_span_id,
+                                    struct netlink_ext_ack *extack)
+{
+       NL_SET_ERR_MSG_MOD(extack, "Sampling action is not supported on Spectrum-1");
+       return -EOPNOTSUPP;
+}
+
+static void mlxsw_sp1_act_sampler_del(void *priv, u8 local_port, int span_id,
+                                     bool ingress)
+{
+       WARN_ON_ONCE(1);
+}
+
 const struct mlxsw_afa_ops mlxsw_sp1_act_afa_ops = {
        .kvdl_set_add           = mlxsw_sp1_act_kvdl_set_add,
        .kvdl_set_del           = mlxsw_sp_act_kvdl_set_del,
        .mirror_del             = mlxsw_sp_act_mirror_del,
        .policer_add            = mlxsw_sp_act_policer_add,
        .policer_del            = mlxsw_sp_act_policer_del,
+       .sampler_add            = mlxsw_sp1_act_sampler_add,
+       .sampler_del            = mlxsw_sp1_act_sampler_del,
 };
 
+static int mlxsw_sp2_act_sampler_add(void *priv, u8 local_port,
+                                    struct psample_group *psample_group,
+                                    u32 rate, u32 trunc_size, bool truncate,
+                                    bool ingress, int *p_span_id,
+                                    struct netlink_ext_ack *extack)
+{
+       struct mlxsw_sp_span_agent_parms agent_parms = {
+               .session_id = MLXSW_SP_SPAN_SESSION_ID_SAMPLING,
+       };
+       struct mlxsw_sp_sample_trigger trigger = {
+               .type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_POLICY_ENGINE,
+       };
+       struct mlxsw_sp_sample_params params;
+       struct mlxsw_sp_port *mlxsw_sp_port;
+       struct mlxsw_sp *mlxsw_sp = priv;
+       int err;
+
+       params.psample_group = psample_group;
+       params.trunc_size = trunc_size;
+       params.rate = rate;
+       params.truncate = truncate;
+       err = mlxsw_sp_sample_trigger_params_set(mlxsw_sp, &trigger, ¶ms,
+                                                extack);
+       if (err)
+               return err;
+
+       err = mlxsw_sp_span_agent_get(mlxsw_sp, p_span_id, &agent_parms);
+       if (err) {
+               NL_SET_ERR_MSG_MOD(extack, "Failed to get SPAN agent");
+               goto err_span_agent_get;
+       }
+
+       mlxsw_sp_port = mlxsw_sp->ports[local_port];
+       err = mlxsw_sp_span_analyzed_port_get(mlxsw_sp_port, ingress);
+       if (err) {
+               NL_SET_ERR_MSG_MOD(extack, "Failed to get analyzed port");
+               goto err_analyzed_port_get;
+       }
+
+       return 0;
+
+err_analyzed_port_get:
+       mlxsw_sp_span_agent_put(mlxsw_sp, *p_span_id);
+err_span_agent_get:
+       mlxsw_sp_sample_trigger_params_unset(mlxsw_sp, &trigger);
+       return err;
+}
+
+static void mlxsw_sp2_act_sampler_del(void *priv, u8 local_port, int span_id,
+                                     bool ingress)
+{
+       struct mlxsw_sp_sample_trigger trigger = {
+               .type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_POLICY_ENGINE,
+       };
+       struct mlxsw_sp_port *mlxsw_sp_port;
+       struct mlxsw_sp *mlxsw_sp = priv;
+
+       mlxsw_sp_port = mlxsw_sp->ports[local_port];
+       mlxsw_sp_span_analyzed_port_put(mlxsw_sp_port, ingress);
+       mlxsw_sp_span_agent_put(mlxsw_sp, span_id);
+       mlxsw_sp_sample_trigger_params_unset(mlxsw_sp, &trigger);
+}
+
 const struct mlxsw_afa_ops mlxsw_sp2_act_afa_ops = {
        .kvdl_set_add           = mlxsw_sp2_act_kvdl_set_add,
        .kvdl_set_del           = mlxsw_sp_act_kvdl_set_del,
        .mirror_del             = mlxsw_sp_act_mirror_del,
        .policer_add            = mlxsw_sp_act_policer_add,
        .policer_del            = mlxsw_sp_act_policer_del,
+       .sampler_add            = mlxsw_sp2_act_sampler_add,
+       .sampler_del            = mlxsw_sp2_act_sampler_del,
        .dummy_first_set        = true,
 };
 
 
        const struct flow_action_entry *act;
        int mirror_act_count = 0;
        int police_act_count = 0;
+       int sample_act_count = 0;
        int err, i;
 
        if (!flow_action_has_entries(flow_action))
                                return err;
                        break;
                        }
+               case FLOW_ACTION_SAMPLE: {
+                       if (sample_act_count++) {
+                               NL_SET_ERR_MSG_MOD(extack, "Multiple sample actions per rule are not supported");
+                               return -EOPNOTSUPP;
+                       }
+
+                       err = mlxsw_sp_acl_rulei_act_sample(mlxsw_sp, rulei,
+                                                           block,
+                                                           act->sample.psample_group,
+                                                           act->sample.rate,
+                                                           act->sample.trunc_size,
+                                                           act->sample.truncate,
+                                                           extack);
+                       if (err)
+                               return err;
+                       break;
+                       }
                default:
                        NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
                        dev_err(mlxsw_sp->bus_info->dev, "Unsupported action\n");
 
 enum {
        /* Packet was mirrored from ingress. */
        MLXSW_SP_MIRROR_REASON_INGRESS = 1,
+       /* Packet was mirrored from policy engine. */
+       MLXSW_SP_MIRROR_REASON_POLICY_ENGINE = 2,
        /* Packet was early dropped. */
        MLXSW_SP_MIRROR_REASON_INGRESS_WRED = 9,
        /* Packet was mirrored from egress. */
        consume_skb(skb);
 }
 
+static void mlxsw_sp_rx_sample_acl_listener(struct sk_buff *skb, u8 local_port,
+                                           void *trap_ctx)
+{
+       struct mlxsw_sp *mlxsw_sp = devlink_trap_ctx_priv(trap_ctx);
+       struct mlxsw_sp_sample_trigger trigger = {
+               .type = MLXSW_SP_SAMPLE_TRIGGER_TYPE_POLICY_ENGINE,
+       };
+       struct mlxsw_sp_sample_params *params;
+       struct mlxsw_sp_port *mlxsw_sp_port;
+       struct psample_metadata md = {};
+       int err;
+
+       err = __mlxsw_sp_rx_no_mark_listener(skb, local_port, trap_ctx);
+       if (err)
+               return;
+
+       mlxsw_sp_port = mlxsw_sp->ports[local_port];
+       if (!mlxsw_sp_port)
+               goto out;
+
+       params = mlxsw_sp_sample_trigger_params_lookup(mlxsw_sp, &trigger);
+       if (!params)
+               goto out;
+
+       /* The psample module expects skb->data to point to the start of the
+        * Ethernet header.
+        */
+       skb_push(skb, ETH_HLEN);
+       mlxsw_sp_psample_md_init(mlxsw_sp, &md, skb,
+                                mlxsw_sp_port->dev->ifindex, params->truncate,
+                                params->trunc_size);
+       psample_sample_packet(params->psample_group, skb, params->rate, &md);
+out:
+       consume_skb(skb);
+}
+
 #define MLXSW_SP_TRAP_DROP(_id, _group_id)                                   \
        DEVLINK_TRAP_GENERIC(DROP, DROP, _id,                                 \
                             DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,       \
                        MLXSW_RXL_MIRROR(mlxsw_sp_rx_sample_tx_listener, 1,
                                         SP_PKT_SAMPLE,
                                         MLXSW_SP_MIRROR_REASON_EGRESS),
+                       MLXSW_RXL_MIRROR(mlxsw_sp_rx_sample_acl_listener, 1,
+                                        SP_PKT_SAMPLE,
+                                        MLXSW_SP_MIRROR_REASON_POLICY_ENGINE),
                },
        },
 };