[ovs-dev] [PATCH v2] Improved Packet Drop Statistics in OVS

Raymond Burkholder ray at oneunified.net
Thu Sep 6 14:18:07 UTC 2018


Looks interesting.

In the list of file changes, should there be a doc update for the new 
commands, changes to existing output, as well as a news entry?


On 2018-09-06 8:01 a.m., Keshav Gupta wrote:
> Currently OVS maintains explicit packet drop/error counters only on port
> level. Packets that are dropped as part of normal OpenFlow processing are
> counted in flow stats of “drop” flows or as table misses in table stats.
> These can only be interpreted by controllers that know the semantics of
> the configured OpenFlow pipeline. Without that knowledge, it is impossible
> for an OVS user to obtain e.g. the total number of packets dropped due to
> OpenFlow rules.
>
> Furthermore, there are numerous other reasons for which packets can be
> dropped by OVS slow path that are not related to the OpenFlow pipeline.
> The generated datapath flow entries include a drop action to avoid further
> expensive upcalls to the slow path, but subsequent packets dropped by the
> datapath are not accounted anywhere.
>
> Finally, the datapath itself drops packets in certain error situations.
> Also, these drops are today not accounted for.
>
> This makes it difficult for OVS users to monitor packet drop in an OVS
> instance and to alert a management system in case of a unexpected increase
> of such drops. Also OVS trouble-shooters face difficulties in analysing
> packet drops.
>
> With this patch we implement following changes to address the issues
> mentioned above.
>
> 1. Account and categorize all the packet drops in OVS.
> 2. Account & classify “drop” action packet drops according to the drop
>     reason.
> 3. Identify and account all the silent packet drop scenarios.
> 4. Have new cli command to display consolidated packet drop output
> 5. Modified ovs-appctl dpcls/dump-flows and ovs-appctl dpif/dump-flows
>     to print drop reason along with drop action
>
> A detailed presentation on this was presented at OvS conference 2017 and
> link for the corresponding presentation is available at:
> https://www.slideshare.net/LF_OpenvSwitch/lfovs17troubleshooting-the-data-plane-in-ovs-82280329
>
> In the subsequent commits plan to add the corresponding tests.
>
> Sample ovs-appctl dpcls/dump-flows & ovs-appctl dpif/dump-flows
> displaying drop reason along with drop action.
> ---------------------------------------------------------------
>
> $ ovs-appctl dpctl/dump-flows netdev at ovs-netdev
>    flow-dump from pmd on cpu core: 0
>    recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:12, bytes:1176, used:0.884s, actions:drop:recursion too deep
>
> $ ovs-appctl dpif/dump-flows br-int
>      recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:25, bytes:2450, used:5.008s, actions:drop:recursion too deep
>      recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0806), packets:7, bytes:294, used:0.009s, actions:drop:recursion too deep
>
>      Sample drop stats summary output.
>      ---------------------------------
> $ ovs-appctl dpif/show-drop-stats
>      netdev:
>      rx-drops                    :0
>      dataplane-processing-drops  :59
>           drop action            :59
>           upcall drops           :0
>           dp error drops         :0
>      tx-drops                    :0
>
>      Sample detailed drop stats output.
>      ---------------------------------
> $ ovs-appctl dpif/show-drop-stats --detail
>      netdev:
>      rx-drops:
>      [IDX]   Drop Reason                            Packets
>      -------  ------------------------------------- ------------
>      0       interface & policer                    0
>      1       parsing error/invalid packet           0
>      dataplane-processing-drops:
>      "drop" action:
>      2       pipeline drop                          0
>      3       bridge not found                       0
>      4       recursion too deep                     68
>      5       too many resubmits                     0
>      6       stack too deep                         0
>      7       no recirculation context               0
>      8       recirculation conflict                 0
>      9       too many mpls labels                   0
>      10      invalid tunnel metadata                0
>      11      unsupported packet type                0
>      12      ecn mismatch at tunnel decapsulation   0
>      13      forwarding disabled (stp/rstp)         0
>      upcall drops:
>      14      upcall lock contention drop            0
>      15      upcall error drops                     0
>      dp drops:
>      16      tunnel pop action errors               0
>      17      tunnel push action errors              0
>      18      nsh decap errors                       0
>      19      recirculation errors                   0
>      20      sampling error                         0
>      21      meter drop                             0
>      22      user space action error                0
>      23      invalid port                           0
>      24      invalid tunnel port                    0
>      tx-drops:
>      25      interface & policer                    0
>
>      Drop stats clear command.
>      ---------------------------------
> $ ovs-appctl dpif/clear-drop-stats
> $ ovs-appctl dpif/show-drop-stats
>      netdev:
>      rx-drops                    :0
>      dataplane-processing-drops  :0
>           drop action            :0
>           upcall drops           :0
>           dp error drops         :0
>      tx-drops                    :0
>
>
> Original author of this patch is Rohith Basavaraja <rohith.basavaraja at gmail.com>
> As our SMTP server does not allow him to put in From list putting him as co-author.
>
> Co-authored-by: Rohith Basavaraja <rohith.basavaraja at gmail.com>
> Co-authored-by: Anju Thomas <anju.thomas at ericsson.com>
> Signed-off-by: Rohith Basavaraja <rohith.basavaraja at gmail.com>
> Signed-off-by: Keshav Gupta <keshav.gupta at ericsson.com>
> Signed-off-by: Anju Thomas <anju.thomas at ericsson.com>
> ---
>   datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
>   lib/dpif-netdev-perf.c                            |  50 +++++
>   lib/dpif-netdev-perf.h                            |  52 ++++++
>   lib/dpif-netdev.c                                 | 128 ++++++++++++-
>   lib/dpif-netlink.c                                |   2 +
>   lib/dpif-provider.h                               |   7 +
>   lib/dpif.c                                        | 191 ++++++++++++++++++-
>   lib/dpif.h                                        |  64 ++++++-
>   lib/netdev-dpdk.c                                 |   4 +
>   lib/odp-execute.c                                 |  50 ++++-
>   lib/odp-execute.h                                 |   9 +-
>   lib/odp-util.c                                    |  53 +++++-
>   ofproto/ofproto-dpif-ipfix.c                      |   1 +
>   ofproto/ofproto-dpif-sflow.c                      |   1 +
>   ofproto/ofproto-dpif-upcall.c                     |   4 +-
>   ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
>   ofproto/ofproto-dpif-xlate.h                      |   4 +
>   ofproto/ofproto-dpif.c                            |  88 +++++++++
>   ofproto/ofproto-dpif.h                            |   8 +-
>   tests/automake.mk                                 |   3 +-
>   tests/bundle.at                                   |   2 +-
>   tests/classifier.at                               |  10 +-
>   tests/dpif-netdev.at                              |   6 +
>   tests/drop-stats.at                               | 212 ++++++++++++++++++++++
>   tests/ofproto-dpif.at                             | 120 ++++++------
>   tests/ovs-ofctl.at                                |   2 +-
>   tests/packet-type-aware.at                        |  16 +-
>   tests/testsuite.at                                |   1 +
>   tests/tunnel-push-pop.at                          |  23 ++-
>   tests/tunnel.at                                   |  21 ++-
>   30 files changed, 1149 insertions(+), 98 deletions(-)
>   create mode 100644 tests/drop-stats.at
>
> diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
> index aaeb034..679fac9 100644
> --- a/datapath/linux/compat/include/linux/openvswitch.h
> +++ b/datapath/linux/compat/include/linux/openvswitch.h
> @@ -819,6 +819,56 @@ struct ovs_action_push_eth {
>   };
>   
>   /**
> + * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP.
> + * @OVS_DROP_REASON_OF_PIPELINE: Explicit drop action in the pipeline.
> + * @OVS_DROP_REASON_BRIDGE_NOT_FOUND: Xlation error generated due to
> + * unable to determine bridge.
> + * @OVS_DROP_REASON_RECURSION_TOO_DEEP: Xlation error generated due to
> + * recursion reached maximum depth.
> + * @OVS_DROP_REASON_TOO_MANY_RESUBMITS: Xlation error generated due to
> + * too many resubmits.
> + * @OVS_DROP_REASON_STACK_TOO_DEEP: Xlation error generated due to stack
> + * too deep.
> + * @OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT: Xlation error generated
> + * due to no recirculation context.
> + * @OVS_DROP_REASON_RECIRCULATION_CONFLICT: Xlation error generated due to
> + * conflict in recirculation context.
> + * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to
> + * too many mpls labels.
> + * @OVS_DROP_REASON_INVALID_TUNNEL_METADATA: Xlation error generated due to
> + * invalid tunnel metadata.
> + * @OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE: Xlation error generated due to
> + * unsupported packet type.
> + * @OVS_DROP_REASON_CONGESTION: Xlation error generated due to ecn mismatch
> + * during tunnel decapsulation.
> + * @OVS_DROP_REASON_FORWARDING_DISABLED: Xlation error generated due to
> + * forwarding is disabled.
> + */
> +enum ovs_drop_reason {
> +    OVS_DROP_REASON_OF_PIPELINE = 0,
> +    OVS_DROP_REASON_BRIDGE_NOT_FOUND,
> +    OVS_DROP_REASON_RECURSION_TOO_DEEP,
> +    OVS_DROP_REASON_TOO_MANY_RESUBMITS,
> +    OVS_DROP_REASON_STACK_TOO_DEEP,
> +    OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT,
> +    OVS_DROP_REASON_RECIRCULATION_CONFLICT,
> +    OVS_DROP_REASON_TOO_MANY_MPLS_LABELS,
> +    OVS_DROP_REASON_INVALID_TUNNEL_METADATA,
> +    OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE,
> +    OVS_DROP_REASON_CONGESTION,
> +    OVS_DROP_REASON_FORWARDING_DISABLED,
> +    OVS_DROP_REASON_MAX,
> +};
> +
> +/*
> + * struct ovs_action_drop - %OVS_ACTION_ATTR_DROP action argument.
> + * @drop_reason: Reason for installing drop action.
> + */
> +struct ovs_action_drop {
> +    enum ovs_drop_reason drop_reason;
> +};
> +
> +/**
>    * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
>    *
>    * @OVS_NAT_ATTR_SRC: Flag for Source NAT (mangle source address/port).
> @@ -935,6 +985,7 @@ enum ovs_action_attr {
>   	OVS_ACTION_ATTR_PUSH_NSH,     /* Nested OVS_NSH_KEY_ATTR_*. */
>   	OVS_ACTION_ATTR_POP_NSH,      /* No argument. */
>   	OVS_ACTION_ATTR_METER,         /* u32 meter number. */
> +        OVS_ACTION_ATTR_DROP,         /* explicit drop action. */
>   
>   #ifndef __KERNEL__
>   	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
> diff --git a/lib/dpif-netdev-perf.c b/lib/dpif-netdev-perf.c
> index 13f1010..9b3b9e4 100644
> --- a/lib/dpif-netdev-perf.c
> +++ b/lib/dpif-netdev-perf.c
> @@ -388,6 +388,33 @@ pmd_perf_read_counters(struct pmd_perf_stats *s,
>       }
>   }
>   
> +void
> +pmd_perf_read_drop_counters(struct pmd_perf_stats *s,
> +                            struct dpif_dp_drop_stats *drop_stats)
> +{
> +    uint64_t val;
> +    int i;
> +
> +    for (i = 0; i < DPIF_RX_MAX_DROP; i++) {
> +        atomic_read_relaxed(&s->drop_counters.n.rx_drops[i], &val);
> +        drop_stats->rx_drops[i] += val - s->drop_counters.zero.rx_drops[i];
> +    }
> +    for (i = 0; i < OVS_DROP_REASON_MAX; i++) {
> +        atomic_read_relaxed(&s->drop_counters.n.drop_action_drops[i], &val);
> +        drop_stats->drop_action_drops[i] += val -
> +                                s->drop_counters.zero.drop_action_drops[i];
> +    }
> +    for (i = 0; i < DPIF_DP_MAX_DROP; i++) {
> +        atomic_read_relaxed(&s->drop_counters.n.dp_drops[i], &val);
> +        drop_stats->dp_drops[i] += val - s->drop_counters.zero.dp_drops[i];
> +    }
> +    for (i = 0; i < DPIF_TX_MAX_DROP; i++) {
> +        atomic_read_relaxed(&s->drop_counters.n.tx_drops[i], &val);
> +        drop_stats->tx_drops[i] += val - s->drop_counters.zero.tx_drops[i];
> +    }
> +
> +}
> +
>   /* This function clears the PMD performance counters from within the PMD
>    * thread or from another thread when the PMD thread is not executing its
>    * poll loop. */
> @@ -438,6 +465,29 @@ pmd_perf_stats_clear(struct pmd_perf_stats *s)
>       }
>   }
>   
> +void
> +pmd_perf_drop_stats_clear(struct pmd_perf_stats *s)
> +{
> +    int i;
> +
> +    for (i = 0; i < DPIF_RX_MAX_DROP; i++) {
> +        atomic_read_relaxed(&s->drop_counters.n.rx_drops[i],
> +                            &s->drop_counters.zero.rx_drops[i]);
> +    }
> +    for (i = 0; i < OVS_DROP_REASON_MAX; i++) {
> +        atomic_read_relaxed(&s->drop_counters.n.drop_action_drops[i],
> +                            &s->drop_counters.zero.drop_action_drops[i]);
> +    }
> +    for (i = 0; i < DPIF_DP_MAX_DROP; i++) {
> +        atomic_read_relaxed(&s->drop_counters.n.dp_drops[i],
> +                            &s->drop_counters.zero.dp_drops[i]);
> +    }
> +    for (i = 0; i < DPIF_TX_MAX_DROP; i++) {
> +        atomic_read_relaxed(&s->drop_counters.n.tx_drops[i],
> +                            &s->drop_counters.zero.tx_drops[i]);
> +    }
> +}
> +
>   /* Functions recording PMD metrics per iteration. */
>   
>   void
> diff --git a/lib/dpif-netdev-perf.h b/lib/dpif-netdev-perf.h
> index 299d52a..af54dfc 100644
> --- a/lib/dpif-netdev-perf.h
> +++ b/lib/dpif-netdev-perf.h
> @@ -33,6 +33,7 @@
>   #include "timeval.h"
>   #include "unixctl.h"
>   #include "util.h"
> +#include "dpif.h"
>   
>   #ifdef  __cplusplus
>   extern "C" {
> @@ -91,6 +92,21 @@ struct pmd_counters {
>       uint64_t zero[PMD_N_STATS];         /* Value at last _clear().  */
>   };
>   
> +/* Drop statistics maintained at per PMD level.
> + * It uses the same update/clear frame work used for other pmd_counters. */
> +struct pmd_drop_stats {
> +    atomic_uint64_t rx_drops[DPIF_RX_MAX_DROP];
> +    atomic_uint64_t drop_action_drops[OVS_DROP_REASON_MAX];
> +    atomic_uint64_t dp_drops[DPIF_DP_MAX_DROP];
> +    atomic_uint64_t tx_drops[DPIF_TX_MAX_DROP];
> +};
> +
> +struct pmd_drop_counters {
> +    struct pmd_drop_stats n;      /* Value since _init(). */
> +    struct pmd_drop_stats zero;   /* Value at last _clear().  */
> +};
> +
> +
>   /* Data structure to collect statistical distribution of an integer measurement
>    * type in form of a histogram. The wall[] array contains the inclusive
>    * upper boundaries of the bins, while the bin[] array contains the actual
> @@ -160,6 +176,8 @@ struct pmd_perf_stats {
>       struct cycle_timer *cur_timer;
>       /* Set of PMD counters with their zero offsets. */
>       struct pmd_counters counters;
> +    /* Set of PMD drop counters with their zero offsets. */
> +    struct pmd_drop_counters drop_counters;
>       /* Statistics of the current iteration. */
>       struct iter_stats current;
>       /* Totals for the current millisecond. */
> @@ -277,6 +295,12 @@ void pmd_perf_stats_clear_lock(struct pmd_perf_stats *s);
>   void pmd_perf_read_counters(struct pmd_perf_stats *s,
>                               uint64_t stats[PMD_N_STATS]);
>   
> +void pmd_perf_drop_stats_init(struct pmd_perf_stats *s);
> +void pmd_perf_drop_stats_clear(struct pmd_perf_stats *s);
> +void pmd_perf_read_drop_counters(struct pmd_perf_stats *s,
> +                                 struct dpif_dp_drop_stats *drops);
> +
> +
>   /* PMD performance counters are updated lock-less. For real PMDs
>    * they are only updated from the PMD thread itself. In the case of the
>    * NON-PMD they might be updated from multiple threads, but we can live
> @@ -398,6 +422,34 @@ void pmd_perf_log_set_cmd(struct unixctl_conn *conn,
>                             int argc, const char *argv[],
>                             void *aux OVS_UNUSED);
>   
> +static inline void
> +pmd_perf_update_drop_counter(struct pmd_perf_stats *s,
> +                             struct dpif_drop_counter *cntr, int delta)
> +{
> +    unsigned long val;
> +    switch (cntr->type) {
> +        case DPIF_DROP_TYPE_RX:
> +            atomic_add_relaxed(&s->drop_counters.n.rx_drops[cntr->counter.rx],
> +                               delta, &val);
> +            break;
> +        case DPIF_DROP_TYPE_DP:
> +            atomic_add_relaxed(&s->drop_counters.n.dp_drops[cntr->counter.dp],
> +                               delta, &val);
> +            break;
> +        case DPIF_DROP_TYPE_DA:
> +            atomic_add_relaxed(
> +                      &s->drop_counters.n.drop_action_drops[cntr->counter.da],
> +                      delta, &val);
> +            break;
> +        case DPIF_DROP_TYPE_TX:
> +            atomic_add_relaxed(&s->drop_counters.n.tx_drops[cntr->counter.tx],
> +                               delta, &val);
> +            break;
> +        OVS_NOT_REACHED();
> +    }
> +}
> +
> +
>   #ifdef  __cplusplus
>   }
>   #endif
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index 807a462..5c5e39e 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -1368,6 +1368,46 @@ dpif_netdev_init(void)
>   }
>   
>   static int
> +dpif_netdev_get_drop_stats(const struct dpif *dpif,
> +                           struct dpif_dp_drop_stats *drop_stats)
> +{
> +    struct dp_netdev *dp = get_dp_netdev(dpif);
> +    struct dp_netdev_pmd_thread *pmd;
> +    struct dp_netdev_port *port;
> +    struct netdev_stats stats;
> +
> +    memset(drop_stats, 0 , sizeof(*drop_stats));
> +    CMAP_FOR_EACH (pmd, node, &dp->poll_threads) {
> +        pmd_perf_read_drop_counters(&pmd->perf_stats, drop_stats);
> +    }
> +    ovs_mutex_lock(&dp->port_mutex);
> +    HMAP_FOR_EACH (port, node, &dp->ports) {
> +        if (!netdev_is_pmd(port->netdev)) {
> +            continue;
> +        }
> +
> +        port->netdev->netdev_class->get_stats(port->netdev,
> +                                              &stats);
> +        drop_stats->iface_rx_drops += stats.rx_dropped;
> +        drop_stats->iface_tx_drops += stats.tx_dropped;
> +    }
> +    ovs_mutex_unlock(&dp->port_mutex);
> +
> +    return 0;
> +}
> +
> +static int
> +dpif_netdev_clear_drop_stats(const struct dpif *dpif)
> +{
> +    struct dp_netdev *dp = get_dp_netdev(dpif);
> +    struct dp_netdev_pmd_thread *pmd;
> +    CMAP_FOR_EACH (pmd, node, &dp->poll_threads) {
> +        pmd_perf_drop_stats_clear(&pmd->perf_stats);
> +    }
> +    return 0;
> +}
> +
> +static int
>   dpif_netdev_enumerate(struct sset *all_dps,
>                         const struct dpif_class *dpif_class)
>   {
> @@ -5027,7 +5067,8 @@ dpif_netdev_meter_get_features(const struct dpif * dpif OVS_UNUSED,
>   /* Applies the meter identified by 'meter_id' to 'packets_'.  Packets
>    * that exceed a band are dropped in-place. */
>   static void
> -dp_netdev_run_meter(struct dp_netdev *dp, struct dp_packet_batch *packets_,
> +dp_netdev_run_meter(struct dp_netdev_pmd_thread *pmd,
> +                    struct dp_netdev *dp, struct dp_packet_batch *packets_,
>                       uint32_t meter_id, long long int now)
>   {
>       struct dp_meter *meter;
> @@ -5150,12 +5191,16 @@ dp_netdev_run_meter(struct dp_netdev *dp, struct dp_packet_batch *packets_,
>       size_t j;
>       DP_PACKET_BATCH_REFILL_FOR_EACH (j, cnt, packet, packets_) {
>           if (exceeded_band[j] >= 0) {
> +            struct dpif_drop_counter cntr;
>               /* Meter drop packet. */
>               band = &meter->bands[exceeded_band[j]];
>               band->packet_count += 1;
>               band->byte_count += dp_packet_size(packet);
>   
>               dp_packet_delete(packet);
> +            cntr.type = DPIF_DROP_TYPE_DP;
> +            cntr.counter.dp = DPIF_DP_METER_DROP;
> +            pmd_perf_update_drop_counter(&pmd->perf_stats, &cntr, 1);
>           } else {
>               /* Meter accepts packet. */
>               dp_packet_batch_refill(packets_, packet, j);
> @@ -5896,6 +5941,7 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
>       bool smc_enable_db;
>       size_t map_cnt = 0;
>       bool batch_enable = true;
> +    struct dpif_drop_counter cntr;
>   
>       atomic_read_relaxed(&pmd->dp->smc_enable_db, &smc_enable_db);
>       atomic_read_relaxed(&pmd->dp->emc_insert_min, &cur_min);
> @@ -5909,6 +5955,9 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
>   
>           if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) {
>               dp_packet_delete(packet);
> +            cntr.type = DPIF_DROP_TYPE_RX;
> +            cntr.counter.rx = DPIF_RX_INVALID_PACKET_DROP;
> +            pmd_perf_update_drop_counter(&pmd->perf_stats, &cntr, 1);
>               continue;
>           }
>   
> @@ -6022,6 +6071,7 @@ handle_packet_upcall(struct dp_netdev_pmd_thread *pmd,
>       ovs_u128 ufid;
>       int error;
>       uint64_t cycles = cycles_counter_update(&pmd->perf_stats);
> +    struct dpif_drop_counter cntr;
>   
>       match.tun_md.valid = false;
>       miniflow_expand(&key->mf, &match.flow);
> @@ -6035,6 +6085,9 @@ handle_packet_upcall(struct dp_netdev_pmd_thread *pmd,
>                                put_actions);
>       if (OVS_UNLIKELY(error && error != ENOSPC)) {
>           dp_packet_delete(packet);
> +        cntr.type = DPIF_DROP_TYPE_DP;
> +        cntr.counter.dp = DPIF_DP_UPCALL_ERROR_DROP;
> +        pmd_perf_update_drop_counter(&pmd->perf_stats, &cntr, 1);
>           return error;
>       }
>   
> @@ -6110,6 +6163,7 @@ fast_path_processing(struct dp_netdev_pmd_thread *pmd,
>       int upcall_ok_cnt = 0, upcall_fail_cnt = 0;
>       int lookup_cnt = 0, add_lookup_cnt;
>       bool any_miss;
> +    struct dpif_drop_counter cntr;
>   
>       for (size_t i = 0; i < cnt; i++) {
>           /* Key length is needed in all the cases, hash computed on demand. */
> @@ -6166,6 +6220,9 @@ fast_path_processing(struct dp_netdev_pmd_thread *pmd,
>           DP_PACKET_BATCH_FOR_EACH (i, packet, packets_) {
>               if (OVS_UNLIKELY(!rules[i])) {
>                   dp_packet_delete(packet);
> +                cntr.type = DPIF_DROP_TYPE_DP;
> +                cntr.counter.dp = DPIF_DP_UPCALL_LOCK_ERROR_DROP;
> +                pmd_perf_update_drop_counter(&pmd->perf_stats, &cntr, 1);
>                   upcall_fail_cnt++;
>               }
>           }
> @@ -6423,6 +6480,7 @@ dp_execute_userspace_action(struct dp_netdev_pmd_thread *pmd,
>   {
>       struct dp_packet_batch b;
>       int error;
> +    struct dpif_drop_counter cntr;
>   
>       ofpbuf_clear(actions);
>   
> @@ -6435,10 +6493,23 @@ dp_execute_userspace_action(struct dp_netdev_pmd_thread *pmd,
>                                     actions->data, actions->size);
>       } else if (should_steal) {
>           dp_packet_delete(packet);
> +        cntr.type = DPIF_DROP_TYPE_DP;
> +        cntr.counter.dp = DPIF_DP_USER_SPACE_ACTION_ERROR_DROP;
> +        pmd_perf_update_drop_counter(&pmd->perf_stats, &cntr, 1);
>       }
>   }
>   
>   static void
> +dp_update_drop_counter_cb(void *aux_, struct dpif_drop_counter *cntr,
> +                       int delta)
> +    OVS_NO_THREAD_SAFETY_ANALYSIS
> +{
> +    struct dp_netdev_execute_aux *aux = aux_;
> +    struct dp_netdev_pmd_thread *pmd = aux->pmd;
> +    pmd_perf_update_drop_counter(&pmd->perf_stats, cntr, delta);
> +}
> +
> +static void
>   dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                 const struct nlattr *a, bool should_steal)
>       OVS_NO_THREAD_SAFETY_ANALYSIS
> @@ -6449,6 +6520,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>       struct dp_netdev *dp = pmd->dp;
>       int type = nl_attr_type(a);
>       struct tx_port *p;
> +    uint32_t packet_count, packet_dropped;
> +    struct dpif_drop_counter cntr;
>   
>       switch ((enum ovs_action_attr)type) {
>       case OVS_ACTION_ATTR_OUTPUT:
> @@ -6490,6 +6563,11 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                   dp_packet_batch_add(&p->output_pkts, packet);
>               }
>               return;
> +        } else {
> +            cntr.type = DPIF_DROP_TYPE_DP;
> +            cntr.counter.dp = DPIF_DP_INVALID_PORT_DROP;
> +            pmd_perf_update_drop_counter(&pmd->perf_stats, &cntr,
> +                                          packets_->count);
>           }
>           break;
>   
> @@ -6499,10 +6577,19 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                * the ownership of these packets. Thus, we can avoid performing
>                * the action, because the caller will not use the result anyway.
>                * Just break to free the batch. */
> +            cntr.type = DPIF_DROP_TYPE_DP;
> +            cntr.counter.dp = DPIF_DP_TUNNEL_PUSH_ERROR_DROP;
> +            pmd_perf_update_drop_counter(&pmd->perf_stats, &cntr,
> +                                             packets_->count);
>               break;
>           }
>           dp_packet_batch_apply_cutlen(packets_);
> -        push_tnl_action(pmd, a, packets_);
> +        if (push_tnl_action(pmd, a, packets_)) {
> +            cntr.type = DPIF_DROP_TYPE_DP;
> +            cntr.counter.dp = DPIF_DP_TUNNEL_PUSH_ERROR_DROP;
> +            pmd_perf_update_drop_counter(&pmd->perf_stats, &cntr,
> +                                             packets_->count);
> +        }
>           return;
>   
>       case OVS_ACTION_ATTR_TUNNEL_POP:
> @@ -6522,7 +6609,16 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>   
>                   dp_packet_batch_apply_cutlen(packets_);
>   
> +                packet_count = packets_->count;
>                   netdev_pop_header(p->port->netdev, packets_);
> +                packet_dropped = packet_count - packets_->count;
> +                if (packet_dropped) {
> +                    cntr.type = DPIF_DROP_TYPE_DP;
> +                    cntr.counter.dp = DPIF_DP_TUNNEL_POP_ERROR_DROP;
> +                    pmd_perf_update_drop_counter(&pmd->perf_stats, &cntr,
> +                                                 packet_dropped);
> +                }
> +
>                   if (dp_packet_batch_is_empty(packets_)) {
>                       return;
>                   }
> @@ -6536,7 +6632,17 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                   dp_netdev_recirculate(pmd, packets_);
>                   (*depth)--;
>                   return;
> +            } else {
> +                cntr.type = DPIF_DROP_TYPE_DP;
> +                cntr.counter.dp = DPIF_DP_INVALID_TNL_PORT_DROP;
> +                pmd_perf_update_drop_counter(&pmd->perf_stats, &cntr,
> +                                             packets_->count);
>               }
> +        } else {
> +            cntr.type = DPIF_DROP_TYPE_DP;
> +            cntr.counter.dp = DPIF_DP_RECIRC_ERROR_DROP;
> +            pmd_perf_update_drop_counter(&pmd->perf_stats, &cntr,
> +                                             packets_->count);
>           }
>           break;
>   
> @@ -6580,6 +6686,11 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>               fat_rwlock_unlock(&dp->upcall_rwlock);
>   
>               return;
> +        } else {
> +            cntr.type = DPIF_DROP_TYPE_DP;
> +            cntr.counter.dp = DPIF_DP_UPCALL_LOCK_ERROR_DROP;
> +            pmd_perf_update_drop_counter(&pmd->perf_stats, &cntr,
> +                                             packets_->count);
>           }
>           break;
>   
> @@ -6602,6 +6713,11 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>               (*depth)--;
>   
>               return;
> +        } else {
> +            cntr.type = DPIF_DROP_TYPE_DP;
> +            cntr.counter.dp = DPIF_DP_RECIRC_ERROR_DROP;
> +            pmd_perf_update_drop_counter(&pmd->perf_stats, &cntr,
> +                                         packets_->count);
>           }
>   
>           VLOG_WARN("Packet dropped. Max recirculation depth exceeded.");
> @@ -6736,7 +6852,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>       }
>   
>       case OVS_ACTION_ATTR_METER:
> -        dp_netdev_run_meter(pmd->dp, packets_, nl_attr_get_u32(a),
> +        dp_netdev_run_meter(pmd, pmd->dp, packets_, nl_attr_get_u32(a),
>                               pmd->ctx.now);
>           break;
>   
> @@ -6756,6 +6872,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>       case OVS_ACTION_ATTR_PUSH_NSH:
>       case OVS_ACTION_ATTR_POP_NSH:
>       case OVS_ACTION_ATTR_CT_CLEAR:
> +    case OVS_ACTION_ATTR_DROP:
>       case __OVS_ACTION_ATTR_MAX:
>           OVS_NOT_REACHED();
>       }
> @@ -6772,7 +6889,8 @@ dp_netdev_execute_actions(struct dp_netdev_pmd_thread *pmd,
>       struct dp_netdev_execute_aux aux = { pmd, flow };
>   
>       odp_execute_actions(&aux, packets, should_steal, actions,
> -                        actions_len, dp_execute_cb);
> +                        actions_len, dp_execute_cb,
> +                        dp_update_drop_counter_cb);
>   }
>   
>   struct dp_netdev_ct_dump {
> @@ -6875,6 +6993,8 @@ const struct dpif_class dpif_netdev_class = {
>       dpif_netdev_run,
>       dpif_netdev_wait,
>       dpif_netdev_get_stats,
> +    dpif_netdev_get_drop_stats,
> +    dpif_netdev_clear_drop_stats,
>       dpif_netdev_port_add,
>       dpif_netdev_port_del,
>       dpif_netdev_port_set_config,
> diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
> index e6d5a6e..458991b 100644
> --- a/lib/dpif-netlink.c
> +++ b/lib/dpif-netlink.c
> @@ -3466,6 +3466,8 @@ const struct dpif_class dpif_netlink_class = {
>       dpif_netlink_run,
>       NULL,                       /* wait */
>       dpif_netlink_get_stats,
> +    NULL,
> +    NULL,
>       dpif_netlink_port_add,
>       dpif_netlink_port_del,
>       NULL,                       /* port_set_config */
> diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
> index 873b6e3..8e04daa 100644
> --- a/lib/dpif-provider.h
> +++ b/lib/dpif-provider.h
> @@ -156,6 +156,13 @@ struct dpif_class {
>       /* Retrieves statistics for 'dpif' into 'stats'. */
>       int (*get_stats)(const struct dpif *dpif, struct dpif_dp_stats *stats);
>   
> +    /* Retrieves drop statistics for 'dpif' into 'drop_stats'. */
> +    int (*get_drop_stats)(const struct dpif *dpif,
> +                          struct dpif_dp_drop_stats *drop_stats);
> +
> +    /* Clears drop statistics for 'dpif'. */
> +    int (*clear_drop_stats)(const struct dpif *dpif);
> +
>       /* Adds 'netdev' as a new port in 'dpif'.  If '*port_no' is not
>        * ODPP_NONE, attempts to use that as the port's port number.
>        *
> diff --git a/lib/dpif.c b/lib/dpif.c
> index d799f97..3f6dfa9 100644
> --- a/lib/dpif.c
> +++ b/lib/dpif.c
> @@ -538,6 +538,38 @@ dpif_get_dp_stats(const struct dpif *dpif, struct dpif_dp_stats *stats)
>       return error;
>   }
>   
> +/* Retrieves drop statistics for 'dpif' into 'drop_stats'.  Returns 0
> + * if successful, otherwise a positive errno value. */
> +int
> +dpif_get_dp_drop_stats(const struct dpif *dpif,
> +                  struct dpif_dp_drop_stats *drop_stats)
> +{
> +    int error = 0;
> +    if (dpif->dpif_class->get_drop_stats) {
> +        error = dpif->dpif_class->get_drop_stats(dpif, drop_stats);
> +        if (error) {
> +            memset(drop_stats, 0, sizeof *drop_stats);
> +        }
> +        log_operation(dpif, "get_drop_stats", error);
> +    } else {
> +        log_operation(dpif, "get_drop_stats not supported", error);
> +    }
> +    return error;
> +}
> +
> +/* Clears drop statistics in 'dpif' into 'drop_stats'. */
> +int
> +dpif_clear_dp_drop_stats(const struct dpif *dpif)
> +{
> +    int error = 0;
> +    if (dpif->dpif_class->clear_drop_stats) {
> +        error = dpif->dpif_class->clear_drop_stats(dpif);
> +    } else {
> +        log_operation(dpif, "clear_drop_stats not supported", error);
> +    }
> +    return error;
> +}
> +
>   const char *
>   dpif_port_open_type(const char *datapath_type, const char *port_type)
>   {
> @@ -1280,6 +1312,7 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
>       case OVS_ACTION_ATTR_PUSH_NSH:
>       case OVS_ACTION_ATTR_POP_NSH:
>       case OVS_ACTION_ATTR_CT_CLEAR:
> +    case OVS_ACTION_ATTR_DROP:
>       case OVS_ACTION_ATTR_UNSPEC:
>       case __OVS_ACTION_ATTR_MAX:
>           OVS_NOT_REACHED();
> @@ -1302,7 +1335,7 @@ dpif_execute_with_help(struct dpif *dpif, struct dpif_execute *execute)
>   
>       dp_packet_batch_init_packet(&pb, execute->packet);
>       odp_execute_actions(&aux, &pb, false, execute->actions,
> -                        execute->actions_len, dpif_execute_helper_cb);
> +                        execute->actions_len, dpif_execute_helper_cb, NULL);
>       return aux.error;
>   }
>   
> @@ -1875,6 +1908,12 @@ dpif_supports_tnl_push_pop(const struct dpif *dpif)
>       return dpif_is_netdev(dpif);
>   }
>   
> +bool
> +dpif_supports_explicit_drop_action(const struct dpif *dpif)
> +{
> +    return dpif_is_netdev(dpif);
> +}
> +
>   /* Meters */
>   void
>   dpif_meter_get_features(const struct dpif *dpif,
> @@ -1972,3 +2011,153 @@ dpif_meter_del(struct dpif *dpif, ofproto_meter_id meter_id,
>       }
>       return error;
>   }
> +
> +static void
> +dpif_show_drop_stats(struct ds *reply,
> +                    struct dpif_dp_drop_stats *drop_stats)
> +{
> +    uint64_t rx_drops,tx_drops,dp_drops,drop_action_drops;
> +    int i;
> +
> +    rx_drops = 0;
> +    tx_drops = 0;
> +    dp_drops = 0;
> +    drop_action_drops = 0;
> +    for (i = 0; i < DPIF_RX_MAX_DROP; i++) {
> +        rx_drops += drop_stats->rx_drops[i];
> +    }
> +    for (i = 0; i < OVS_DROP_REASON_MAX; i++) {
> +        drop_action_drops += drop_stats->drop_action_drops[i];
> +    }
> +    for (i = 0; i < DPIF_DP_MAX_DROP; i++) {
> +        dp_drops += drop_stats->dp_drops[i];
> +    }
> +    for (i = 0; i < DPIF_TX_MAX_DROP; i++) {
> +        tx_drops += drop_stats->tx_drops[i];
> +    }
> +    dp_drops += drop_action_drops;
> +    rx_drops += drop_stats->iface_rx_drops;
> +    tx_drops += drop_stats->iface_tx_drops;
> +
> +    ds_put_format(reply, "%-28s:%-12"PRIu64"\n", "rx-drops", rx_drops);
> +    ds_put_format(reply, "%-28s:%-12"PRIu64"\n",
> +                  "dataplane-processing-drops", dp_drops);
> +    ds_put_format(reply, "     %-23s:%-12"PRIu64"\n", "drop action",
> +                  drop_action_drops);
> +    ds_put_format(reply, "     %-23s:%-12"PRIu64"\n", "upcall drops",
> +                  drop_stats->dp_drops[DPIF_DP_UPCALL_ERROR_DROP] +
> +                  drop_stats->dp_drops[DPIF_DP_UPCALL_LOCK_ERROR_DROP]);
> +    ds_put_format(reply, "     %-23s:%-12"PRIu64"\n", "dp error drops",
> +                  dp_drops -
> +                  drop_action_drops -
> +                  drop_stats->dp_drops[DPIF_DP_UPCALL_ERROR_DROP] -
> +                  drop_stats->dp_drops[DPIF_DP_UPCALL_LOCK_ERROR_DROP]);
> +    ds_put_format(reply, "%-28s:%-12"PRIu64"\n", "tx-drops",
> +                  tx_drops);
> +}
> +
> +static void
> +dpif_show_drop_stats_detail(struct ds *reply,
> +                    struct dpif_dp_drop_stats *drop_stats)
> +{
> +    uint32_t idx = 0;
> +    ds_put_format(reply, "rx-drops: \n");
> +    ds_put_format(reply, "%-7s %-38s %-12s\n", "[IDX]", "Drop Reason",
> +                  "Packets");
> +    ds_put_format(reply, "-------  ------------------------------------- "
> +                         "------------\n");
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +                  "interface & policer", drop_stats->iface_rx_drops);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +                  "parsing error/invalid packet",
> +                  drop_stats->rx_drops[DPIF_RX_INVALID_PACKET_DROP]);
> +    ds_put_format(reply, "dataplane-processing-drops: \n");
> +    ds_put_format(reply, "\"drop\" action:\n");
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +                  "pipeline drop",
> +                  drop_stats->drop_action_drops[OVS_DROP_REASON_OF_PIPELINE]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +              "bridge not found",
> +              drop_stats->drop_action_drops[OVS_DROP_REASON_BRIDGE_NOT_FOUND]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +            "recursion too deep",
> +            drop_stats->drop_action_drops[OVS_DROP_REASON_RECURSION_TOO_DEEP]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +            "too many resubmits",
> +            drop_stats->drop_action_drops[OVS_DROP_REASON_TOO_MANY_RESUBMITS]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++, "stack too deep",
> +             drop_stats->drop_action_drops[OVS_DROP_REASON_STACK_TOO_DEEP]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +      "no recirculation context",
> +      drop_stats->drop_action_drops[OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +        "recirculation conflict",
> +        drop_stats->drop_action_drops[OVS_DROP_REASON_RECIRCULATION_CONFLICT]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +          "too many mpls labels",
> +          drop_stats->drop_action_drops[OVS_DROP_REASON_TOO_MANY_MPLS_LABELS]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +       "invalid tunnel metadata",
> +       drop_stats->drop_action_drops[OVS_DROP_REASON_INVALID_TUNNEL_METADATA]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +       "unsupported packet type",
> +       drop_stats->drop_action_drops[OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +              "ecn mismatch at tunnel decapsulation",
> +               drop_stats->drop_action_drops[OVS_DROP_REASON_CONGESTION]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +           "forwarding disabled (stp/rstp)",
> +           drop_stats->drop_action_drops[OVS_DROP_REASON_FORWARDING_DISABLED]);
> +    ds_put_format(reply, "upcall drops:\n");
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +                  "upcall lock contention drop",
> +                  drop_stats->dp_drops[DPIF_DP_UPCALL_LOCK_ERROR_DROP]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +                  "upcall error drops",
> +                  drop_stats->dp_drops[DPIF_DP_UPCALL_ERROR_DROP]);
> +    ds_put_format(reply, "dp drops:\n");
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +                  "tunnel pop action errors",
> +                  drop_stats->dp_drops[DPIF_DP_TUNNEL_POP_ERROR_DROP]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +                  "tunnel push action errors",
> +                  drop_stats->dp_drops[DPIF_DP_TUNNEL_PUSH_ERROR_DROP]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +                  "nsh decap errors",
> +                  drop_stats->dp_drops[DPIF_DP_NSH_DECAP_ERROR_DROP]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +                  "recirculation errors",
> +                  drop_stats->dp_drops[DPIF_DP_RECIRC_ERROR_DROP]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++, "sampling error",
> +                         drop_stats->dp_drops[DPIF_DP_SAMPLE_ERROR_DROP]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++, "meter drop",
> +                         drop_stats->dp_drops[DPIF_DP_METER_DROP]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +                  "user space action error",
> +                  drop_stats->dp_drops[DPIF_DP_USER_SPACE_ACTION_ERROR_DROP]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++, "invalid port",
> +                         drop_stats->dp_drops[DPIF_DP_INVALID_PORT_DROP]);
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +                  "invalid tunnel port",
> +                  drop_stats->dp_drops[DPIF_DP_INVALID_TNL_PORT_DROP]);
> +    ds_put_format(reply, "tx-drops: \n");
> +    ds_put_format(reply, "%-7d %-38s %-12"PRIu64"\n", idx++,
> +                  "interface & policer", drop_stats->iface_tx_drops);
> +}
> +
> +int
> +dpif_show_drop_stats_support(struct dpif *dpif, bool detail, struct ds *reply)
> +{
> +    struct dpif_dp_drop_stats drop_stats;
> +    int error = 0;
> +    error = dpif_get_dp_drop_stats(dpif, &drop_stats);
> +    if (error) {
> +        return error;
> +    }
> +    if (detail) {
> +        dpif_show_drop_stats_detail(reply, &drop_stats);
> +    } else {
> +        dpif_show_drop_stats(reply, &drop_stats);
> +    }
> +    return 0;
> +}
> diff --git a/lib/dpif.h b/lib/dpif.h
> index bbdc3eb..dbc1afd 100644
> --- a/lib/dpif.h
> +++ b/lib/dpif.h
> @@ -778,6 +778,66 @@ enum dpif_upcall_type {
>       DPIF_N_UC_TYPES
>   };
>   
> +/* Drop counter types */
> +enum dpif_drop_type {
> +    DPIF_DROP_TYPE_RX,      /* Rx drops */
> +    DPIF_DROP_TYPE_DP,      /* Data path processing drops */
> +    DPIF_DROP_TYPE_TX,      /* Tx drops */
> +    DPIF_DROP_TYPE_DA,      /* Drop action drops */
> +};
> +
> +/* Rx drop counters */
> +enum dpif_rx_drops {
> +    DPIF_RX_INVALID_PACKET_DROP = 0,
> +    DPIF_RX_MAX_DROP,
> +};
> +
> +/* Tx drop counters */
> +enum dpif_tx_drops {
> +    DPIF_TX_MAX_DROP,
> +};
> +
> +/* Data path processing drop counters */
> +enum dpif_dp_drops {
> +    DPIF_DP_METER_DROP = 0,
> +    DPIF_DP_UPCALL_ERROR_DROP,
> +    DPIF_DP_UPCALL_LOCK_ERROR_DROP,
> +    DPIF_DP_USER_SPACE_ACTION_ERROR_DROP,
> +    DPIF_DP_TUNNEL_PUSH_ERROR_DROP,
> +    DPIF_DP_TUNNEL_POP_ERROR_DROP,
> +    DPIF_DP_SAMPLE_ERROR_DROP,
> +    DPIF_DP_NSH_DECAP_ERROR_DROP,
> +    DPIF_DP_RECIRC_ERROR_DROP,
> +    DPIF_DP_INVALID_PORT_DROP,
> +    DPIF_DP_INVALID_TNL_PORT_DROP,
> +    DPIF_DP_MAX_DROP,
> +};
> +
> +/* Structure used to define any drop counter */
> +struct dpif_drop_counter {
> +    enum dpif_drop_type type;
> +    union {
> +        enum dpif_rx_drops   rx;
> +        enum dpif_dp_drops   dp;
> +        enum ovs_drop_reason da;
> +        enum dpif_tx_drops   tx;
> +    } counter;
> +};
> +
> +/* Drop statistics for a dpif as a whole.*/
> +struct dpif_dp_drop_stats {
> +    uint64_t iface_rx_drops;
> +    uint64_t rx_drops[DPIF_RX_MAX_DROP];
> +    uint64_t drop_action_drops[OVS_DROP_REASON_MAX];
> +    uint64_t dp_drops[DPIF_DP_MAX_DROP];
> +    uint64_t iface_tx_drops;
> +    uint64_t tx_drops[DPIF_TX_MAX_DROP];
> +};
> +
> +int dpif_get_dp_drop_stats(const struct dpif *, struct dpif_dp_drop_stats *);
> +int dpif_clear_dp_drop_stats(const struct dpif *);
> +
> +
>   const char *dpif_upcall_type_to_string(enum dpif_upcall_type);
>   
>   /* A packet passed up from the datapath to userspace.
> @@ -888,7 +948,9 @@ int dpif_get_pmds_for_port(const struct dpif * dpif, odp_port_t port_no,
>   
>   char *dpif_get_dp_version(const struct dpif *);
>   bool dpif_supports_tnl_push_pop(const struct dpif *);
> -
> +bool dpif_supports_explicit_drop_action(const struct dpif *);
> +int dpif_show_drop_stats_support(struct dpif *dpif, bool detail,
> +                                 struct ds *reply);
>   /* Log functions. */
>   struct vlog_module;
>   
> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
> index f91aa27..cf98e54 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -2358,6 +2358,10 @@ netdev_dpdk_send__(struct netdev_dpdk *dev, int qid,
>                      bool concurrent_txq)
>   {
>       if (OVS_UNLIKELY(!(dev->flags & NETDEV_UP))) {
> +        int batch_cnt = dp_packet_batch_size(batch);
> +        rte_spinlock_lock(&dev->stats_lock);
> +        dev->stats.tx_dropped += batch_cnt;
> +        rte_spinlock_unlock(&dev->stats_lock);
>           dp_packet_delete_batch(batch, true);
>           return;
>       }
> diff --git a/lib/odp-execute.c b/lib/odp-execute.c
> index 5831d1f..d0acee0 100644
> --- a/lib/odp-execute.c
> +++ b/lib/odp-execute.c
> @@ -575,12 +575,14 @@ odp_execute_masked_set_action(struct dp_packet *packet,
>   static void
>   odp_execute_sample(void *dp, struct dp_packet *packet, bool steal,
>                      const struct nlattr *action,
> -                   odp_execute_cb dp_execute_action)
> +                   odp_execute_cb dp_execute_action,
> +                   odp_update_drop_counter_cb dp_update_drop_counter)
>   {
>       const struct nlattr *subactions = NULL;
>       const struct nlattr *a;
>       struct dp_packet_batch pb;
>       size_t left;
> +    struct dpif_drop_counter cntr;
>   
>       NL_NESTED_FOR_EACH_UNSAFE (a, left, action) {
>           int type = nl_attr_type(a);
> @@ -589,6 +591,11 @@ odp_execute_sample(void *dp, struct dp_packet *packet, bool steal,
>           case OVS_SAMPLE_ATTR_PROBABILITY:
>               if (random_uint32() >= nl_attr_get_u32(a)) {
>                   if (steal) {
> +                    if (dp_update_drop_counter) {
> +                        cntr.type = DPIF_DROP_TYPE_DP;
> +                        cntr.counter.dp = DPIF_DP_SAMPLE_ERROR_DROP;
> +                        dp_update_drop_counter(dp, &cntr, 1);
> +                    }
>                       dp_packet_delete(packet);
>                   }
>                   return;
> @@ -616,13 +623,15 @@ odp_execute_sample(void *dp, struct dp_packet *packet, bool steal,
>       }
>       dp_packet_batch_init_packet(&pb, packet);
>       odp_execute_actions(dp, &pb, true, nl_attr_get(subactions),
> -                        nl_attr_get_size(subactions), dp_execute_action);
> +                        nl_attr_get_size(subactions), dp_execute_action,
> +                        dp_update_drop_counter);
>   }
>   
>   static void
>   odp_execute_clone(void *dp, struct dp_packet_batch *batch, bool steal,
>                      const struct nlattr *actions,
> -                   odp_execute_cb dp_execute_action)
> +                   odp_execute_cb dp_execute_action,
> +                   odp_update_drop_counter_cb dp_update_drop_counter)
>   {
>       if (!steal) {
>           /* The 'actions' may modify the packet, but the modification
> @@ -634,11 +643,13 @@ odp_execute_clone(void *dp, struct dp_packet_batch *batch, bool steal,
>           dp_packet_batch_clone(&clone_pkt_batch, batch);
>           dp_packet_batch_reset_cutlen(batch);
>           odp_execute_actions(dp, &clone_pkt_batch, true, nl_attr_get(actions),
> -                        nl_attr_get_size(actions), dp_execute_action);
> +                        nl_attr_get_size(actions), dp_execute_action,
> +                        dp_update_drop_counter);
>       }
>       else {
>           odp_execute_actions(dp, batch, true, nl_attr_get(actions),
> -                            nl_attr_get_size(actions), dp_execute_action);
> +                            nl_attr_get_size(actions), dp_execute_action,
> +                            dp_update_drop_counter);
>       }
>   }
>   
> @@ -673,6 +684,7 @@ requires_datapath_assistance(const struct nlattr *a)
>       case OVS_ACTION_ATTR_PUSH_NSH:
>       case OVS_ACTION_ATTR_POP_NSH:
>       case OVS_ACTION_ATTR_CT_CLEAR:
> +    case OVS_ACTION_ATTR_DROP:
>           return false;
>   
>       case OVS_ACTION_ATTR_UNSPEC:
> @@ -699,11 +711,14 @@ requires_datapath_assistance(const struct nlattr *a)
>   void
>   odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
>                       const struct nlattr *actions, size_t actions_len,
> -                    odp_execute_cb dp_execute_action)
> +                    odp_execute_cb dp_execute_action,
> +                    odp_update_drop_counter_cb dp_update_drop_counter)
>   {
>       struct dp_packet *packet;
>       const struct nlattr *a;
>       unsigned int left;
> +    struct dpif_drop_counter cntr;
> +
>   
>       NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
>           int type = nl_attr_type(a);
> @@ -822,7 +837,7 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
>           case OVS_ACTION_ATTR_SAMPLE:
>               DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
>                   odp_execute_sample(dp, packet, steal && last_action, a,
> -                                   dp_execute_action);
> +                                   dp_execute_action, dp_update_drop_counter);
>               }
>   
>               if (last_action) {
> @@ -845,7 +860,8 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
>   
>           case OVS_ACTION_ATTR_CLONE:
>               odp_execute_clone(dp, batch, steal && last_action, a,
> -                                                dp_execute_action);
> +                                         dp_execute_action,
> +                                         dp_update_drop_counter);
>               if (last_action) {
>                   /* We do not need to free the packets. odp_execute_clone() has
>                    * stolen them.  */
> @@ -889,6 +905,11 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
>                   if (pop_nsh(packet)) {
>                       dp_packet_batch_refill(batch, packet, i);
>                   } else {
> +                    if (dp_update_drop_counter) {
> +                        cntr.type = DPIF_DROP_TYPE_DP;
> +                        cntr.counter.dp = DPIF_DP_NSH_DECAP_ERROR_DROP;
> +                        dp_update_drop_counter(dp, &cntr, 1);
> +                    }
>                       dp_packet_delete(packet);
>                   }
>               }
> @@ -900,6 +921,19 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
>               }
>               break;
>   
> +        case OVS_ACTION_ATTR_DROP: {
> +            const struct ovs_action_drop *drop_action = nl_attr_get(a);
> +            enum ovs_drop_reason drop_reason = drop_action->drop_reason;
> +            if ((drop_reason < OVS_DROP_REASON_MAX) &&
> +                 dp_update_drop_counter) {
> +                 cntr.type = DPIF_DROP_TYPE_DA;
> +                 cntr.counter.da = drop_reason;
> +                 dp_update_drop_counter(dp, &cntr, batch->count);
> +            }
> +            dp_packet_delete_batch(batch, steal);
> +            return;
> +        }
> +
>           case OVS_ACTION_ATTR_OUTPUT:
>           case OVS_ACTION_ATTR_TUNNEL_PUSH:
>           case OVS_ACTION_ATTR_TUNNEL_POP:
> diff --git a/lib/odp-execute.h b/lib/odp-execute.h
> index a3578a5..8684227 100644
> --- a/lib/odp-execute.h
> +++ b/lib/odp-execute.h
> @@ -22,6 +22,8 @@
>   #include <stddef.h>
>   #include <stdint.h>
>   #include "openvswitch/types.h"
> +#include "ovs-atomic.h"
> +#include "dpif.h"
>   
>   struct nlattr;
>   struct dp_packet;
> @@ -31,6 +33,10 @@ struct dp_packet_batch;
>   typedef void (*odp_execute_cb)(void *dp, struct dp_packet_batch *batch,
>                                  const struct nlattr *action, bool should_steal);
>   
> +typedef void (*odp_update_drop_counter_cb) (void *aux_,
> +                                           struct dpif_drop_counter *cntr,
> +                                           int delta);
> +
>   /* Actions that need to be executed in the context of a datapath are handed
>    * to 'dp_execute_action', if non-NULL.  Currently this is called only for
>    * actions OVS_ACTION_ATTR_OUTPUT and OVS_ACTION_ATTR_USERSPACE so
> @@ -38,5 +44,6 @@ typedef void (*odp_execute_cb)(void *dp, struct dp_packet_batch *batch,
>   void odp_execute_actions(void *dp, struct dp_packet_batch *batch,
>                            bool steal,
>                            const struct nlattr *actions, size_t actions_len,
> -                         odp_execute_cb dp_execute_action);
> +                         odp_execute_cb dp_execute_action,
> +                         odp_update_drop_counter_cb dp_update_drop_counter_cb);
>   #endif
> diff --git a/lib/odp-util.c b/lib/odp-util.c
> index cf62550..bd8c9f5 100644
> --- a/lib/odp-util.c
> +++ b/lib/odp-util.c
> @@ -124,6 +124,7 @@ odp_action_len(uint16_t type)
>       case OVS_ACTION_ATTR_CLONE: return ATTR_LEN_VARIABLE;
>       case OVS_ACTION_ATTR_PUSH_NSH: return ATTR_LEN_VARIABLE;
>       case OVS_ACTION_ATTR_POP_NSH: return 0;
> +    case OVS_ACTION_ATTR_DROP: return sizeof(struct ovs_action_drop);
>   
>       case OVS_ACTION_ATTR_UNSPEC:
>       case __OVS_ACTION_ATTR_MAX:
> @@ -338,6 +339,48 @@ format_nsh_key_mask(struct ds *ds, const struct ovs_key_nsh *key,
>       }
>   }
>   
> +static const char *
> +dropreason_str(enum ovs_drop_reason reason)
> +{
> +    switch (reason) {
> +    case OVS_DROP_REASON_OF_PIPELINE:
> +        return "pipeline-drop";
> +    case OVS_DROP_REASON_BRIDGE_NOT_FOUND:
> +        return "bridge not found";
> +    case OVS_DROP_REASON_RECURSION_TOO_DEEP:
> +        return "recursion too deep";
> +    case OVS_DROP_REASON_TOO_MANY_RESUBMITS:
> +        return "too many resubmits";
> +    case OVS_DROP_REASON_STACK_TOO_DEEP:
> +        return "stack too deep";
> +    case OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT:
> +        return "no recirculation context";
> +    case OVS_DROP_REASON_RECIRCULATION_CONFLICT:
> +        return "recirculation conflict";
> +    case OVS_DROP_REASON_TOO_MANY_MPLS_LABELS:
> +        return "too many mpls labels";
> +    case OVS_DROP_REASON_INVALID_TUNNEL_METADATA:
> +        return "invalid tunnel metadata";
> +    case OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE:
> +        return "unsupported packet type";
> +    case OVS_DROP_REASON_CONGESTION:
> +        return "ecn mismatch at tunnel decapsulation";
> +    case OVS_DROP_REASON_FORWARDING_DISABLED:
> +        return "forwarding disabled (stp/rstp)";
> +    default:
> +        return "unknown reason";
> +    }
> +    return "unknown reason";
> +}
> +
> +static void
> +format_odp_drop_action(struct ds *ds,
> +                      const struct ovs_action_drop *drop_action)
> +{
> +    ds_put_format(ds, "drop:%s",
> +                  dropreason_str(drop_action->drop_reason));
> +}
> +
>   static void
>   format_odp_push_nsh_action(struct ds *ds,
>                              const struct nsh_hdr *nsh_hdr)
> @@ -1174,6 +1217,9 @@ format_odp_action(struct ds *ds, const struct nlattr *a,
>       case OVS_ACTION_ATTR_POP_NSH:
>           ds_put_cstr(ds, "pop_nsh()");
>           break;
> +    case OVS_ACTION_ATTR_DROP:
> +        format_odp_drop_action(ds, nl_attr_get(a));
> +        break;
>       case OVS_ACTION_ATTR_UNSPEC:
>       case __OVS_ACTION_ATTR_MAX:
>       default:
> @@ -2412,8 +2458,13 @@ odp_actions_from_string(const char *s, const struct simap *port_names,
>                           struct ofpbuf *actions)
>   {
>       size_t old_size;
> +    struct ovs_action_drop drop_action;
>   
> -    if (!strcasecmp(s, "drop")) {
> +    if ((!strcasecmp(s, "drop") ||
> +        !strcasecmp(s, "drop:pipeline-drop"))) {
> +        drop_action.drop_reason = OVS_DROP_REASON_OF_PIPELINE;
> +        nl_msg_put_unspec(actions, OVS_ACTION_ATTR_DROP,
> +                          &drop_action, sizeof drop_action);
>           return 0;
>       }
>   
> diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
> index 4029806..1d23a5a 100644
> --- a/ofproto/ofproto-dpif-ipfix.c
> +++ b/ofproto/ofproto-dpif-ipfix.c
> @@ -3015,6 +3015,7 @@ dpif_ipfix_read_actions(const struct flow *flow,
>           case OVS_ACTION_ATTR_PUSH_NSH:
>           case OVS_ACTION_ATTR_POP_NSH:
>           case OVS_ACTION_ATTR_UNSPEC:
> +        case OVS_ACTION_ATTR_DROP:
>           case __OVS_ACTION_ATTR_MAX:
>           default:
>               break;
> diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
> index d17d7a8..28d025d 100644
> --- a/ofproto/ofproto-dpif-sflow.c
> +++ b/ofproto/ofproto-dpif-sflow.c
> @@ -1221,6 +1221,7 @@ dpif_sflow_read_actions(const struct flow *flow,
>           case OVS_ACTION_ATTR_PUSH_NSH:
>           case OVS_ACTION_ATTR_POP_NSH:
>           case OVS_ACTION_ATTR_UNSPEC:
> +        case OVS_ACTION_ATTR_DROP:
>           case __OVS_ACTION_ATTR_MAX:
>           default:
>               break;
> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
> index 6222207..f102050 100644
> --- a/ofproto/ofproto-dpif-upcall.c
> +++ b/ofproto/ofproto-dpif-upcall.c
> @@ -1119,7 +1119,7 @@ upcall_receive(struct upcall *upcall, const struct dpif_backer *backer,
>       return 0;
>   }
>   
> -static void
> +static enum xlate_error
>   upcall_xlate(struct udpif *udpif, struct upcall *upcall,
>                struct ofpbuf *odp_actions, struct flow_wildcards *wc)
>   {
> @@ -1209,6 +1209,7 @@ upcall_xlate(struct udpif *udpif, struct upcall *upcall,
>       if (upcall->type == MISS_UPCALL) {
>           upcall->ukey = ukey_create_from_upcall(upcall, wc);
>       }
> +    return xerr;
>   }
>   
>   static void
> @@ -1279,6 +1280,7 @@ upcall_cb(const struct dp_packet *packet, const struct flow *flow, ovs_u128 *ufi
>   
>       upcall.fitness = ODP_FIT_PERFECT;
>       error = process_upcall(udpif, &upcall, actions, wc);
> +
>       if (error) {
>           goto out;
>       }
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index e26f6c8..9c396fb 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -444,10 +444,46 @@ const char *xlate_strerror(enum xlate_error error)
>           return "Invalid tunnel metadata";
>       case XLATE_UNSUPPORTED_PACKET_TYPE:
>           return "Unsupported packet type";
> +    case XLATE_CONGESTION_DROP:
> +        return "CONGESTION DROP";
> +    case XLATE_FORWARDING_DISABLED:
> +        return "Forwarding is disabled";
> +
>       }
>       return "Unknown error";
>   }
>   
> +enum ovs_drop_reason  xlate_error_to_drop_reason(enum xlate_error error)
> +{
> +     switch (error) {
> +        case XLATE_OK:
> +            return OVS_DROP_REASON_OF_PIPELINE;
> +        case XLATE_BRIDGE_NOT_FOUND:
> +            return OVS_DROP_REASON_BRIDGE_NOT_FOUND;
> +        case XLATE_RECURSION_TOO_DEEP:
> +            return OVS_DROP_REASON_RECURSION_TOO_DEEP;
> +        case XLATE_TOO_MANY_RESUBMITS:
> +            return OVS_DROP_REASON_TOO_MANY_RESUBMITS;
> +        case XLATE_STACK_TOO_DEEP:
> +            return OVS_DROP_REASON_STACK_TOO_DEEP;
> +        case XLATE_NO_RECIRCULATION_CONTEXT:
> +            return OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT;
> +        case XLATE_RECIRCULATION_CONFLICT:
> +            return OVS_DROP_REASON_RECIRCULATION_CONFLICT;
> +        case XLATE_TOO_MANY_MPLS_LABELS:
> +            return OVS_DROP_REASON_TOO_MANY_MPLS_LABELS;
> +        case XLATE_INVALID_TUNNEL_METADATA:
> +            return OVS_DROP_REASON_INVALID_TUNNEL_METADATA;
> +        case XLATE_UNSUPPORTED_PACKET_TYPE:
> +            return OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE;
> +        case XLATE_CONGESTION_DROP:
> +            return OVS_DROP_REASON_CONGESTION;
> +        case XLATE_FORWARDING_DISABLED:
> +            return OVS_DROP_REASON_MAX;
> +     }
> +     return OVS_DROP_REASON_OF_PIPELINE;
> +}
> +
>   static void xlate_action_set(struct xlate_ctx *ctx);
>   static void xlate_commit_actions(struct xlate_ctx *ctx);
>   
> @@ -5856,6 +5892,17 @@ put_ct_label(const struct flow *flow, struct ofpbuf *odp_actions,
>   }
>   
>   static void
> +put_drop_action(struct ofpbuf *odp_actions, enum xlate_error error)
> +{
> +    struct ovs_action_drop drop_action;
> +
> +    drop_action.drop_reason = xlate_error_to_drop_reason(error);
> +    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_DROP,
> +                          &drop_action, sizeof drop_action);
> +
> +}
> +
> +static void
>   put_ct_helper(struct xlate_ctx *ctx,
>                 struct ofpbuf *odp_actions, struct ofpact_conntrack *ofc)
>   {
> @@ -7318,6 +7365,10 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
>           }
>           size_t sample_actions_len = ctx.odp_actions->size;
>   
> +        if (!tnl_process_ecn(flow)) {
> +            ctx.error = XLATE_CONGESTION_DROP;
> +        }
> +
>           if (tnl_process_ecn(flow)
>               && (!in_port || may_receive(in_port, &ctx))) {
>               const struct ofpact *ofpacts;
> @@ -7350,6 +7401,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
>                   ctx.odp_actions->size = sample_actions_len;
>                   ctx_cancel_freeze(&ctx);
>                   ofpbuf_clear(&ctx.action_set);
> +                ctx.error = XLATE_FORWARDING_DISABLED;
>               }
>   
>               if (!ctx.freezing) {
> @@ -7457,6 +7509,18 @@ exit:
>               ofpbuf_clear(xin->odp_actions);
>           }
>       }
> +
> +    /*
> +     * If we are going to install "drop" action, check whether
> +     * datapath supports explicit "drop"action. If datapath
> +     * supports explicit "drop"action then install the "drop"
> +     * action containing the drop reason.
> +     */
> +    if (xin->odp_actions && !xin->odp_actions->size &&
> +         ovs_explicit_drop_action_supported(ctx.xbridge->ofproto)) {
> +        put_drop_action(xin->odp_actions, ctx.error);
> +    }
> +
>       return ctx.error;
>   }
>   
> diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h
> index 2cbb3c9..e1ce027 100644
> --- a/ofproto/ofproto-dpif-xlate.h
> +++ b/ofproto/ofproto-dpif-xlate.h
> @@ -216,12 +216,16 @@ enum xlate_error {
>       XLATE_TOO_MANY_MPLS_LABELS,
>       XLATE_INVALID_TUNNEL_METADATA,
>       XLATE_UNSUPPORTED_PACKET_TYPE,
> +    XLATE_CONGESTION_DROP,
> +    XLATE_FORWARDING_DISABLED,
>   };
>   
>   const char *xlate_strerror(enum xlate_error error);
>   
>   enum xlate_error xlate_actions(struct xlate_in *, struct xlate_out *);
>   
> +uint32_t xlate_error_to_drop_reason(enum xlate_error error);
> +
>   void xlate_in_init(struct xlate_in *, struct ofproto_dpif *, ovs_version_t,
>                      const struct flow *, ofp_port_t in_port, struct rule_dpif *,
>                      uint16_t tcp_flags, const struct dp_packet *packet,
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index 0a0c69a..258f32f 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -828,6 +828,12 @@ ovs_native_tunneling_is_on(struct ofproto_dpif *ofproto)
>           && atomic_count_get(&ofproto->backer->tnl_count);
>   }
>   
> +bool
> +ovs_explicit_drop_action_supported(struct ofproto_dpif *ofproto)
> +{
> +    return ofproto->backer->rt_support.explicit_drop_action;
> +}
> +
>   /* Tests whether 'backer''s datapath supports recirculation.  Only newer
>    * datapaths support OVS_KEY_ATTR_RECIRC_ID in keys.  We need to disable some
>    * features on older datapaths that don't support this feature.
> @@ -1398,6 +1404,8 @@ check_support(struct dpif_backer *backer)
>       backer->rt_support.ct_eventmask = check_ct_eventmask(backer);
>       backer->rt_support.ct_clear = check_ct_clear(backer);
>       backer->rt_support.max_hash_alg = check_max_dp_hash_alg(backer);
> +    backer->rt_support.explicit_drop_action =
> +        dpif_supports_explicit_drop_action(backer->dpif);
>   
>       /* Flow fields. */
>       backer->rt_support.odp.ct_state = check_ct_state(backer);
> @@ -5788,6 +5796,82 @@ ofproto_unixctl_dpif_set_dp_features(struct unixctl_conn *conn,
>   }
>   
>   static void
> +ofproto_unixctl_dpif_show_drop_stats(struct unixctl_conn *conn,
> +                                int argc OVS_UNUSED, const char *argv[],
> +                                void *aux OVS_UNUSED)
> +{
> +    struct ds ds = DS_EMPTY_INITIALIZER;
> +    int error = 0;
> +    int i;
> +    const struct shash_node **backers;
> +    struct dpif_backer *backer;
> +    bool detail = false;
> +
> +    for (int i = 1; i < argc; i++) {
> +        if (!strcmp(argv[i], "--detail")) {
> +            detail = true;
> +        }
> +    }
> +
> +    ds_init(&ds);
> +    backers = shash_sort(&all_dpif_backers);
> +    for (i = 0; i < shash_count(&all_dpif_backers); i++) {
> +        backer = (struct dpif_backer *)backers[i]->data;
> +        if (dpif_supports_explicit_drop_action(backer->dpif)) {
> +            ds_put_format(&ds, "%s:\n", backer->type);
> +            error = dpif_show_drop_stats_support(backer->dpif,
> +                                             detail, &ds);
> +            if (error) {
> +                break;
> +            }
> +        }
> +    }
> +    if (error) {
> +        ds_clear(&ds);
> +        ds_put_format(&ds, "dpif/show-drop-stats failed");
> +        unixctl_command_reply_error(conn, ds_cstr(&ds));
> +    } else {
> +        unixctl_command_reply(conn, ds_cstr(&ds));
> +    }
> +    free(backers);
> +    ds_destroy(&ds);
> +}
> +
> +
> +static void
> +ofproto_unixctl_dpif_clear_drop_stats(struct unixctl_conn *conn,
> +                                int argc OVS_UNUSED, const char *argv[],
> +                                void *aux OVS_UNUSED)
> +{
> +    struct ds ds = DS_EMPTY_INITIALIZER;
> +    int error = 0;
> +    int i;
> +    const struct shash_node **backers;
> +    struct dpif_backer *backer;
> +
> +    ds_init(&ds);
> +    backers = shash_sort(&all_dpif_backers);
> +    for (i = 0; i < shash_count(&all_dpif_backers) ; i++) {
> +        backer = (struct dpif_backer *)backers[i]->data;
> +        if (dpif_supports_explicit_drop_action(backer->dpif)) {
> +            error = dpif_clear_dp_drop_stats(backer->dpif);
> +            if (error) {
> +                break;
> +            }
> +        }
> +    }
> +    if (error) {
> +        ds_clear(&ds);
> +        ds_put_format(&ds, "dpif/clear-drop-stats failed");
> +        unixctl_command_reply_error(conn, ds_cstr(&ds));
> +    } else {
> +        unixctl_command_reply(conn, ds_cstr(&ds));
> +    }
> +    free(backers);
> +    ds_destroy(&ds);
> +}
> +
> +static void
>   ofproto_unixctl_init(void)
>   {
>       static bool registered;
> @@ -5819,6 +5903,10 @@ ofproto_unixctl_init(void)
>                                ofproto_unixctl_dpif_dump_flows, NULL);
>       unixctl_command_register("dpif/set-dp-features", "bridge", 1, 3 ,
>                                ofproto_unixctl_dpif_set_dp_features, NULL);
> +    unixctl_command_register("dpif/show-drop-stats", "[--detail]", 0, 1,
> +                             ofproto_unixctl_dpif_show_drop_stats, NULL);
> +    unixctl_command_register("dpif/clear-drop-stats", "", 0, 0,
> +                              ofproto_unixctl_dpif_clear_drop_stats, NULL);
>   }
>   
>   static odp_port_t
> diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
> index 1a404c8..9162ba0 100644
> --- a/ofproto/ofproto-dpif.h
> +++ b/ofproto/ofproto-dpif.h
> @@ -106,6 +106,7 @@ struct rule_dpif *rule_dpif_lookup_from_table(struct ofproto_dpif *,
>                                                 bool honor_table_miss,
>                                                 struct xlate_cache *);
>   
> +
>   void rule_dpif_credit_stats(struct rule_dpif *,
>                               const struct dpif_flow_stats *);
>   
> @@ -192,7 +193,10 @@ struct group_dpif *group_dpif_lookup(struct ofproto_dpif *,
>       DPIF_SUPPORT_FIELD(bool, ct_clear, "Conntrack clear")                   \
>                                                                               \
>       /* Highest supported dp_hash algorithm. */                              \
> -    DPIF_SUPPORT_FIELD(size_t, max_hash_alg, "Max dp_hash algorithm")
> +    DPIF_SUPPORT_FIELD(size_t, max_hash_alg, "Max dp_hash algorithm")       \
> +                                                                            \
> +    /* True if the datapath supports explicit drop action. */               \
> +    DPIF_SUPPORT_FIELD(bool, explicit_drop_action, "Explicit Drop action")
>   
>   /* Stores the various features which the corresponding backer supports. */
>   struct dpif_backer_support {
> @@ -361,4 +365,6 @@ int ofproto_dpif_delete_internal_flow(struct ofproto_dpif *, struct match *,
>   
>   bool ovs_native_tunneling_is_on(struct ofproto_dpif *);
>   
> +bool ovs_explicit_drop_action_supported(struct ofproto_dpif *);
> +
>   #endif /* ofproto-dpif.h */
> diff --git a/tests/automake.mk b/tests/automake.mk
> index b29a37e..391b6ee 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -106,7 +106,8 @@ TESTSUITE_AT = \
>   	tests/ovn-controller-vtep.at \
>   	tests/mcast-snooping.at \
>   	tests/packet-type-aware.at \
> -	tests/nsh.at
> +	tests/nsh.at \
> +	tests/drop-stats.at
>   
>   SYSTEM_KMOD_TESTSUITE_AT = \
>   	tests/system-common-macros.at \
> diff --git a/tests/bundle.at b/tests/bundle.at
> index 40dfbea..deb54ba 100644
> --- a/tests/bundle.at
> +++ b/tests/bundle.at
> @@ -241,7 +241,7 @@ AT_CHECK([tail -1 stdout], [0],
>   AT_CHECK([ovs-ofctl mod-port br0 p2 down])
>   AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=LOCAL,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06'], [0], [stdout])
>   AT_CHECK([tail -1 stdout], [0],
> -  [Datapath actions: drop
> +  [Datapath actions: drop:pipeline-drop
>   ])
>   AT_CHECK([ovs-ofctl mod-port br0 p1 up])
>   AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=LOCAL,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06'], [0], [stdout])
> diff --git a/tests/classifier.at b/tests/classifier.at
> index 86f872d..a7378a7 100644
> --- a/tests/classifier.at
> +++ b/tests/classifier.at
> @@ -50,12 +50,12 @@ Datapath actions: 1
>   AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=11.1.2.15,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout])
>   AT_CHECK([tail -2 stdout], [0],
>     [Megaflow: recirc_id=0,eth,ip,in_port=1,nw_dst=11.0.0.0/8,nw_frag=no
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>   ])
>   AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=10.1.2.15,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout])
>   AT_CHECK([tail -2 stdout], [0],
>     [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_dst=10.1.2.15,nw_frag=no,tp_dst=80
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>   ])
>   AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=10.1.2.15,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=79'], [0], [stdout])
>   AT_CHECK([tail -2 stdout], [0],
> @@ -88,7 +88,7 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
>   AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout])
>   AT_CHECK([tail -2 stdout], [0],
>     [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_dst=192.168.0.0/16,nw_frag=no
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>   ])
>   
>   AT_CHECK([ovs-vsctl set Flow_Table t0 prefixes=ipv6_label], [0])
> @@ -103,7 +103,7 @@ AT_CHECK([ovs-vsctl set Flow_Table t0 prefixes=nw_dst], [0])
>   AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout])
>   AT_CHECK([tail -2 stdout], [0],
>     [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_dst=192.168.0.0/16,nw_frag=no
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>   ])
>   AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=2,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout])
>   AT_CHECK([tail -2 stdout], [0],
> @@ -113,7 +113,7 @@ Datapath actions: 1
>   AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=10.1.2.15,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=80'], [0], [stdout])
>   AT_CHECK([tail -2 stdout], [0],
>     [Megaflow: recirc_id=0,eth,tcp,in_port=1,nw_dst=10.1.2.15,nw_frag=no,tp_dst=80
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>   ])
>   AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=10.1.2.15,nw_proto=6,nw_tos=0,nw_ttl=128,tp_src=8,tp_dst=79'], [0], [stdout])
>   AT_CHECK([tail -2 stdout], [0],
> diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
> index fff395d..bea2430 100644
> --- a/tests/dpif-netdev.at
> +++ b/tests/dpif-netdev.at
> @@ -249,6 +249,12 @@ meter:2 flow_count:1 packet_in_count:5 byte_in_count:300 duration:0.0s bands:
>   0: packet_count:1 byte_count:60
>   ])
>   
> +AT_CHECK([
> +    ovs-appctl dpif/show-drop-stats --detail | grep "meter drop" | tr -s " " | sed 's/[ \t]*$//'
> +], [0], [dnl
> +21 meter drop 5
> +])
> +
>   # Advance time by 1/2 second
>   ovs-appctl time/warp 500
>   
> diff --git a/tests/drop-stats.at b/tests/drop-stats.at
> new file mode 100644
> index 0000000..36f09ad
> --- /dev/null
> +++ b/tests/drop-stats.at
> @@ -0,0 +1,212 @@
> +AT_BANNER([drop-stats])
> +
> +AT_SETUP([drop-stats - cli tests])
> +
> +OVS_VSWITCHD_START([dnl
> +    set bridge br0 datapath_type=dummy \
> +        protocols=OpenFlow10,OpenFlow13,OpenFlow14,OpenFlow15 -- \
> +    add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1])
> +
> +AT_DATA([flows.txt], [dnl
> +table=0,in_port=1,actions=drop
> +])
> +
> +AT_CHECK([
> +    ovs-ofctl del-flows br0
> +    ovs-ofctl -Oopenflow13 add-flows br0 flows.txt
> +    ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions
> +], [0], [dnl
> + in_port=1 actions=drop
> +])
> +
> +AT_CHECK([
> +    ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
> +    ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
> +    ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
> +], [0], [ignore])
> +
> +AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/used:[[0-9]].[[0-9]]*s/used:0.0/' | sort], [0],
> +[flow-dump from non-dpdk interfaces:
> +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:2, bytes:196, used:0.0, actions:drop:pipeline-drop
> +])
> +
> +AT_CHECK([
> +    ovs-appctl dpif/show-drop-stats --detail | grep "pipeline drop" | tr -s " " | sed 's/[ \t]*$//'
> +], [0], [dnl
> +2 pipeline drop 3
> +])
> +
> +AT_CHECK([
> +    ovs-appctl dpif/show-drop-stats | tr -s " " | sed 's/[ \t]*$//'
> +], [0], [dnl
> +dummy:
> +rx-drops :0
> +dataplane-processing-drops :3
> + drop action :3
> + upcall drops :0
> + dp error drops :0
> +tx-drops :0
> +])
> +
> +
> +AT_CHECK([ovs-appctl dpif/clear-drop-stats])
> +
> +AT_CHECK([
> +    ovs-appctl dpif/show-drop-stats | tr -s " " | sed 's/[ \t]*$//'
> +], [0], [dnl
> +dummy:
> +rx-drops :0
> +dataplane-processing-drops :0
> + drop action :0
> + upcall drops :0
> + dp error drops :0
> +tx-drops :0
> +])
> +
> +AT_CHECK([
> +    ovs-appctl dpif/show-drop-stats --detail | grep "pipeline drop" | tr -s " " | sed 's/[ \t]*$//'
> +], [0], [dnl
> +2 pipeline drop 0
> +])
> +
> +OVS_VSWITCHD_STOP
> +AT_CLEANUP
> +
> +AT_SETUP([drop-stats - pipeline and recurssion drops])
> +
> +OVS_VSWITCHD_START([dnl
> +    set bridge br0 datapath_type=dummy \
> +        protocols=OpenFlow10,OpenFlow13,OpenFlow14,OpenFlow15 -- \
> +    add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \
> +    add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2])
> +
> +AT_DATA([flows.txt], [dnl
> +table=0,in_port=1,actions=drop
> +])
> +
> +AT_CHECK([
> +    ovs-ofctl del-flows br0
> +    ovs-ofctl -Oopenflow13 add-flows br0 flows.txt
> +    ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions
> +], [0], [dnl
> + in_port=1 actions=drop
> +])
> +
> +AT_CHECK([
> +    ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
> +], [0], [ignore])
> +
> +AT_CHECK([
> +    ovs-appctl dpif/show-drop-stats --detail | grep "pipeline drop" | tr -s " " | sed 's/[ \t]*$//'
> +], [0], [dnl
> +2 pipeline drop 1
> +])
> +
> +AT_DATA([flows.txt], [dnl
> +table=0, in_port=1, actions=goto_table:1
> +table=1, in_port=1, actions=goto_table:2
> +table=2, in_port=1, actions=resubmit(,1)
> +])
> +
> +AT_CHECK([
> +    ovs-ofctl del-flows br0
> +    ovs-ofctl -Oopenflow13 add-flows br0 flows.txt
> +    ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions
> +], [0], [ignore])
> +
> +AT_CHECK([
> +    ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
> +], [0], [ignore])
> +
> +AT_CHECK([
> +    ovs-appctl dpif/show-drop-stats --detail | grep "recursion too deep" | tr -s " " | sed 's/[ \t]*$//'
> +], [0], [dnl
> +4 recursion too deep 1
> +])
> +
> +OVS_VSWITCHD_STOP(["/|WARN|/d"])
> +AT_CLEANUP
> +
> +AT_SETUP([drop-stats - too many resubmit])
> +
> +OVS_VSWITCHD_START
> +add_of_ports br0 1
> +(for i in `seq 1 64`; do
> +     j=`expr $i + 1`
> +     echo "in_port=$i, actions=resubmit:$j, resubmit:$j, local"
> + done
> + echo "in_port=65, actions=local") > flows.txt
> +
> +AT_CHECK([
> +    ovs-ofctl del-flows br0
> +    ovs-ofctl -Oopenflow13 add-flows br0 flows.txt
> +], [0], [ignore])
> +
> +ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)'
> +
> +
> +AT_CHECK([
> +    ovs-appctl dpif/show-drop-stats --detail | grep "too many resubmits" | tr -s " " | sed 's/[ \t]*$//'
> +], [0], [dnl
> +5 too many resubmits 1
> +])
> +
> +OVS_VSWITCHD_STOP(["/|WARN|/d"])
> +AT_CLEANUP
> +
> +
> +AT_SETUP([drop-stats - stack too deep])
> +OVS_VSWITCHD_START
> +add_of_ports br0 1
> +(for i in `seq 1 12`; do
> +     j=`expr $i + 1`
> +     echo "in_port=$i, actions=resubmit:$j, resubmit:$j, local"
> + done
> + push="push:NXM_NX_REG0[[]]"
> + echo "in_port=13, actions=$push,$push,$push,$push,$push,$push,$push,$push") > flows
> + AT_CHECK([ovs-ofctl add-flows br0 flows])
> +
> +ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x1234)'
> +
> +AT_CHECK([
> +    ovs-appctl dpif/show-drop-stats --detail | grep "stack too deep" | tr -s " " | sed 's/[ \t]*$//'
> +], [0], [dnl
> +6 stack too deep 1
> +])
> +
> +OVS_VSWITCHD_STOP(["/resubmits yielded over 64 kB of stack/d"])
> +AT_CLEANUP
> +
> +AT_SETUP([drop-stats - too many mpls labels])
> +
> +OVS_VSWITCHD_START([dnl
> +    set bridge br0 datapath_type=dummy \
> +        protocols=OpenFlow10,OpenFlow13,OpenFlow14,OpenFlow15 -- \
> +    add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \
> +    add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2])
> +
> +AT_DATA([flows.txt], [dnl
> +table=0, in_port=1, actions=push_mpls:0x8847, resubmit:3
> +table=0, in_port=3, actions=push_mpls:0x8847, set_field:10->mpls_label, set_field:15->mpls_label,  resubmit:4
> +table=0, in_port=4, actions=push_mpls:0x8847, set_field:11->mpls_label, resubmit:5
> +table=0, in_port=5, actions=push_mpls:0x8847, set_field:12->mpls_label, resubmit:6
> +table=0, in_port=6, actions=push_mpls:0x8847, set_field:13->mpls_label, output:2
> +])
> +
> +AT_CHECK([
> +    ovs-ofctl del-flows br0
> +    ovs-ofctl -Oopenflow13 add-flows br0 flows.txt
> +])
> +
> +AT_CHECK([
> +    ovs-appctl netdev-dummy/receive p1 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e08006f200a4d0001fc509a58000000002715020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
> +], [0], [ignore])
> +
> +AT_CHECK([
> +    ovs-appctl dpif/show-drop-stats --detail | grep "too many mpls labels" | tr -s " " | sed 's/[ \t]*$//'
> +], [0], [dnl
> +9 too many mpls labels 1
> +])
> +
> +OVS_VSWITCHD_STOP(["/|WARN|/d"])
> +AT_CLEANUP
> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> index 362c58d..1d89b00 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -713,7 +713,7 @@ AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 'group_id=1234,type=ff,bucket=wa
>   AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=write_actions(group:1234)'])
>   AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
>   AT_CHECK([tail -1 stdout], [0],
> -  [Datapath actions: drop
> +  [Datapath actions: drop:pipeline-drop
>   ])
>   OVS_VSWITCHD_STOP
>   AT_CLEANUP
> @@ -3524,51 +3524,51 @@ dnl Each of these specifies an in_port by number, a VLAN VID (or "none"),
>   dnl a VLAN PCP (used if the VID isn't "none") and the expected set of datapath
>   dnl actions.
>   for tuple in \
> -        "100 none 0 drop" \
> -        "100 0    0 drop" \
> -        "100 0    1 drop" \
> +        "100 none 0 drop:pipeline-drop" \
> +        "100 0    0 drop:pipeline-drop" \
> +        "100 0    1 drop:pipeline-drop" \
>           "100 10   0 1,5,6,7,8,pop_vlan,2,9" \
>           "100 10   1 1,5,6,7,8,pop_vlan,2,9" \
>           "100 11   0 5,7" \
>           "100 11   1 5,7" \
>           "100 12   0 1,5,6,pop_vlan,3,4,7,8,11,12" \
>           "100 12   1 1,5,6,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,8,12" \
> -        "1  none 0 drop" \
> -        "1  0    0 drop" \
> -        "1  0    1 drop" \
> +        "1  none 0 drop:pipeline-drop" \
> +        "1  0    0 drop:pipeline-drop" \
> +        "1  0    1 drop:pipeline-drop" \
>           "1  10   0 5,6,7,8,100,pop_vlan,2,9" \
>           "1  10   1 5,6,7,8,100,pop_vlan,2,9" \
> -        "1  11   0 drop" \
> -        "1  11   1 drop" \
> +        "1  11   0 drop:pipeline-drop" \
> +        "1  11   1 drop:pipeline-drop" \
>           "1  12   0 5,6,100,pop_vlan,3,4,7,8,11,12" \
>           "1  12   1 5,6,100,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,8,12" \
>           "2  none 0 9,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
>           "2  0    0 pop_vlan,9,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
>           "2  0    1 pop_vlan,9,push_vlan(vid=10,pcp=1),1,5,6,7,8,100" \
> -        "2  10   0 drop" \
> -        "2  10   1 drop" \
> -        "2  11   0 drop" \
> -        "2  11   1 drop" \
> -        "2  12   0 drop" \
> -        "2  12   1 drop" \
> +        "2  10   0 drop:pipeline-drop" \
> +        "2  10   1 drop:pipeline-drop" \
> +        "2  11   0 drop:pipeline-drop" \
> +        "2  11   1 drop:pipeline-drop" \
> +        "2  12   0 drop:pipeline-drop" \
> +        "2  12   1 drop:pipeline-drop" \
>           "3  none 0 4,7,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
>           "3  0    0 pop_vlan,4,7,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
>           "3  0    1 8,12,pop_vlan,4,7,11,push_vlan(vid=12,pcp=1),1,5,6,100" \
> -        "3  10   0 drop" \
> -        "3  10   1 drop" \
> -        "3  11   0 drop" \
> -        "3  11   1 drop" \
> -        "3  12   0 drop" \
> -        "3  12   1 drop" \
> +        "3  10   0 drop:pipeline-drop" \
> +        "3  10   1 drop:pipeline-drop" \
> +        "3  11   0 drop:pipeline-drop" \
> +        "3  11   1 drop:pipeline-drop" \
> +        "3  12   0 drop:pipeline-drop" \
> +        "3  12   1 drop:pipeline-drop" \
>           "4  none 0 3,7,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
>           "4  0    0 pop_vlan,3,7,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
>           "4  0    1 3,8,12,pop_vlan,7,11,push_vlan(vid=12,pcp=1),1,5,6,100" \
> -        "4  10   0 drop" \
> -        "4  10   1 drop" \
> -        "4  11   0 drop" \
> -        "4  11   1 drop" \
> -        "4  12   0 drop" \
> -        "4  12   1 drop" \
> +        "4  10   0 drop:pipeline-drop" \
> +        "4  10   1 drop:pipeline-drop" \
> +        "4  11   0 drop:pipeline-drop" \
> +        "4  11   1 drop:pipeline-drop" \
> +        "4  12   0 drop:pipeline-drop" \
> +        "4  12   1 drop:pipeline-drop" \
>           "5  none 0 2,9,push_vlan(vid=10,pcp=0),1,6,7,8,100" \
>           "5  0    0 pop_vlan,2,9,push_vlan(vid=10,pcp=0),1,6,7,8,100" \
>           "5  0    1 pop_vlan,2,9,push_vlan(vid=10,pcp=1),1,6,7,8,100" \
> @@ -3583,8 +3583,8 @@ for tuple in \
>           "6  0    1 pop_vlan,2,9,push_vlan(vid=10,pcp=1),1,5,7,8,100" \
>           "6  10   0 1,5,7,8,100,pop_vlan,2,9" \
>           "6  10   1 1,5,7,8,100,pop_vlan,2,9" \
> -        "6  11   0 drop" \
> -        "6  11   1 drop" \
> +        "6  11   0 drop:pipeline-drop" \
> +        "6  11   1 drop:pipeline-drop" \
>           "6  12   0 1,5,100,pop_vlan,3,4,7,8,11,12" \
>           "6  12   1 1,5,100,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,8,12" \
>           "7  none 0 3,4,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
> @@ -3601,16 +3601,16 @@ for tuple in \
>           "8  0    1 3,12,pop_vlan,4,7,11,push_vlan(vid=12,pcp=1),1,5,6,100" \
>           "8  10   0 1,5,6,7,100,pop_vlan,2,9" \
>           "8  10   1 1,5,6,7,100,pop_vlan,2,9" \
> -        "8  11   0 drop" \
> -        "8  11   1 drop" \
> +        "8  11   0 drop:pipeline-drop" \
> +        "8  11   1 drop:pipeline-drop" \
>           "8  12   0 1,5,6,100,pop_vlan,3,4,7,11,12" \
>           "8  12   1 1,5,6,100,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,12" \
>           "9  none 0 2,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
>           "9  10   0 10,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
>           "9  11   0 push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
> -        "10 none 0 drop" \
> -        "10 0    0 drop" \
> -        "10 11   0 drop" \
> +        "10 none 0 drop:pipeline-drop" \
> +        "10 0    0 drop:pipeline-drop" \
> +        "10 11   0 drop:pipeline-drop" \
>           "10 12   0 9,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
>           "11 10   0 7,8,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
>           "11 10   1 7,8,12,push_vlan(vid=12,pcp=0),1,5,6,100"
> @@ -4350,11 +4350,11 @@ no_flow="$base_flow,frag=no),tcp(src=12345,dst=80)"
>   first_flow="$base_flow,frag=first),tcp(src=12345,dst=80)"
>   later_flow="$base_flow,frag=later)"
>   
> -    # mode    no  first  later
> +    # mode    no     first                later
>   for tuple in \
> -    'normal    1     5      6' \
> -    'drop      1  drop   drop' \
> -    'nx-match  1     2      6'
> +    'normal    1     5                    6' \
> +    'drop      1     drop:pipeline-drop   drop:pipeline-drop' \
> +    'nx-match  1     2                    6'
>   do
>     set $tuple
>     mode=$1
> @@ -4432,8 +4432,8 @@ done
>   AT_CHECK([ovs-appctl dpctl/dump-flows], [0], [dnl
>   flow-dump from non-dpdk interfaces:
>   recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(dst=80), packets:0, bytes:0, used:never, actions:set(tcp(dst=81)),1
> -recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=first), packets:0, bytes:0, used:never, actions:drop
> -recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=later), packets:0, bytes:0, used:never, actions:drop
> +recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=first), packets:0, bytes:0, used:never, actions:drop:pipeline-drop
> +recirc_id(0),in_port(90),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=later), packets:0, bytes:0, used:never, actions:drop:pipeline-drop
>   ])
>   
>   mode=nx-match
> @@ -5711,7 +5711,7 @@ bridge("br0")
>   
>   Final flow: <cleared>
>   Megaflow: <cleared>
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>   ])
>   
>   dnl Now, try again without megaflows:
> @@ -5732,7 +5732,7 @@ bridge("br0")
>   
>   Final flow: <cleared>
>   Megaflow: <cleared>
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>   ])
>   
>   OVS_VSWITCHD_STOP
> @@ -7026,7 +7026,7 @@ for i in `seq 1 3`; do
>   done
>   AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/.*\(packets:\)/\1/' | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl
>   flow-dump from non-dpdk interfaces:
> -packets:2, bytes:68, used:0.001s, actions:drop
> +packets:2, bytes:68, used:0.001s, actions:drop:pipeline-drop
>   ])
>   
>   OVS_VSWITCHD_STOP(["/sending to collector failed/d"])
> @@ -7118,7 +7118,7 @@ for i in `seq 1 3`; do
>   done
>   AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/.*\(packets:\)/\1/' | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl
>   flow-dump from non-dpdk interfaces:
> -packets:2, bytes:68, used:0.001s, actions:drop
> +packets:2, bytes:68, used:0.001s, actions:drop:pipeline-drop
>   ])
>   
>   OVS_VSWITCHD_STOP(["/sending to collector failed/d
> @@ -7792,21 +7792,21 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:
>   AT_CHECK([ovs-appctl netdev-dummy/receive p3 'in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'])
>   ovs-appctl revalidator/wait
>   AT_CHECK([ovs-appctl dpif/dump-flows br0 | strip_ufid | strip_used | sort], [0], [dnl
> -recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:drop
> -recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:drop
> +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:drop:pipeline-drop
> +recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:drop:pipeline-drop
>   ])
>   
>   AT_CHECK([ovs-appctl dpif/dump-flows br1 | strip_ufid | strip_used | sort], [0], [dnl
> -recirc_id(0),in_port(3),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:drop
> +recirc_id(0),in_port(3),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:drop:pipeline-drop
>   ])
>   
>   AT_CHECK([ovs-appctl dpif/dump-flows -m br0 | strip_ufid | strip_used | sort], [0], [dnl
> -skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
> -skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p2),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=0/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
> +skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop:pipeline-drop
> +skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p2),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=0/0,code=0/0), packets:0, bytes:0, used:never, actions:drop:pipeline-drop
>   ])
>   
>   AT_CHECK([ovs-appctl dpif/dump-flows -m br1 | strip_ufid | strip_used | sort], [0], [dnl
> -skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p3),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop
> +skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(p3),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop:pipeline-drop
>   ])
>   
>   OVS_VSWITCHD_STOP
> @@ -7829,7 +7829,7 @@ m4_define([OFPROTO_DPIF_GET_FLOW],
>   
>      UFID=`sed -n 's/\(ufid:[[-0-9a-fA-F]]*\).*/\1/p' stdout`
>      AT_CHECK([ovs-appctl dpctl/get-flow $UFID], [0], [dnl
> -recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:drop
> +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:drop:pipeline-drop
>   ])
>   
>      OVS_VSWITCHD_STOP
> @@ -8577,11 +8577,11 @@ table=0 in_port=1,ip,nw_dst=10.0.0.3 actions=drop
>      sleep 1
>      AT_CHECK([strip_ufid < ovs-vswitchd.log | filter_flow_install | strip_used], [0], [dnl
>   skb_priority(0),skb_mark(0),ct_state(-new-est-rel-rpl-inv-trk-snat-dnat),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), actions:2
> -skb_priority(0),skb_mark(0),ct_state(-new-est-rel-rpl-inv-trk-snat-dnat),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), actions:drop
> +skb_priority(0),skb_mark(0),ct_state(-new-est-rel-rpl-inv-trk-snat-dnat),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), actions:drop:pipeline-drop
>   ])
>      AT_CHECK([strip_ufid < ovs-vswitchd.log | filter_flow_dump | grep 'packets:3'], [0], [dnl
>   skb_priority(0),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:318, used:0.0s, actions:2
> -skb_priority(0),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:318, used:0.0s, actions:drop
> +skb_priority(0),skb_mark(0),ct_state(0/0xff),ct_zone(0),ct_mark(0),ct_label(0),recirc_id(0),dp_hash(0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:318, used:0.0s, actions:drop:pipeline-drop
>   ])
>      OVS_VSWITCHD_STOP
>      AT_CLEANUP])
> @@ -9312,7 +9312,7 @@ for i in 1 2 3; do
>   done
>   
>   AT_CHECK([ovs-appctl dpif/dump-flows br0 | strip_ufid | strip_used | sort], [0], [dnl
> -recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x1234), packets:5, bytes:70, used:0.0s, actions:drop
> +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x1234), packets:5, bytes:70, used:0.0s, actions:drop:pipeline-drop
>   ])
>   
>   # Add a flow that matches the non-presence of a vlan tag, and check
> @@ -9341,16 +9341,16 @@ done
>   
>   AT_CHECK([ovs-appctl dpif/dump-flows br0 | strip_ufid | strip_used | sort], [0], [dnl
>   recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x1234), packets:8, bytes:112, used:0.0s, actions:100
> -recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=7/0x0),encap(eth_type(0x1234)), packets:2, bytes:36, used:0.0s, actions:drop
> +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=7/0x0),encap(eth_type(0x1234)), packets:2, bytes:36, used:0.0s, actions:drop:pipeline-drop
>   ])
>   
>   # Check that the new flow matches the CFI bit, while both vid and pcp
>   # are wildcarded.
>   AT_CHECK([grep '\(modify\)\|\(flow_add\)' ovs-vswitchd.log | strip_ufid ], [0], [dnl
>   dpif_netdev|DBG|flow_add: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x1234), actions:100
> -dpif|DBG|dummy at ovs-dummy: put[[modify]] skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234)
> +dpif|DBG|dummy at ovs-dummy: put[[modify]] skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234), actions:drop:pipeline-drop
>   dpif|DBG|dummy at ovs-dummy: put[[modify]] skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234), actions:100
> -dpif_netdev|DBG|flow_add: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=7/0x0),encap(eth_type(0x1234)), actions:drop
> +dpif_netdev|DBG|flow_add: recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=7/0x0),encap(eth_type(0x1234)), actions:drop:pipeline-drop
>   ])
>   OVS_VSWITCHD_STOP
>   AT_CLEANUP
> @@ -9675,7 +9675,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p2 'in_port(2),eth(src=50:54:00:00:00:
>   
>   
>   AT_CHECK([cat ovs-vswitchd.log | strip_ufid | filter_flow_install], [0], [dnl
> -ct_state(+new-est+trk),recirc_id(0x1),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:drop
> +ct_state(+new-est+trk),recirc_id(0x1),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:drop:pipeline-drop
>   ct_state(-new+est+trk),recirc_id(0x1),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=17,frag=no), actions:1
>   recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=17,frag=no), actions:ct(commit),2
>   recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=17,frag=no), actions:ct,recirc(0x1)
> @@ -10362,7 +10362,7 @@ AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
>   
>   AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=2,udp'], [0], [stdout])
>   AT_CHECK([tail -1 stdout], [0],
> -  [Datapath actions: drop
> +  [Datapath actions: drop:pipeline-drop
>   ])
>   
>   AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,udp'], [0], [stdout])
> @@ -10454,7 +10454,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p1 'recirc_id(0),in_port(1),eth_type(0
>   ovs-appctl time/warp 5000
>   
>   AT_CHECK([strip_ufid < ovs-vswitchd.log | filter_flow_install | strip_used], [0], [dnl
> -recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=17,frag=later), actions:drop
> +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(proto=17,frag=later), actions:drop:pipeline-drop
>   ])
>   
>   dnl Change the flow table.  This will trigger revalidation of all the flows.
> diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
> index fef6aa4..8086bbf 100644
> --- a/tests/ovs-ofctl.at
> +++ b/tests/ovs-ofctl.at
> @@ -2987,7 +2987,7 @@ AT_CHECK([tail -1 stdout], [0],
>   dnl Inbound web traffic with SYN bit without ACK or RST bits
>   AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=80,dst=8),tcp_flags(0xfeb)'], [0], [stdout])
>   AT_CHECK([tail -1 stdout], [0],
> -  [Datapath actions: drop
> +  [Datapath actions: drop:pipeline-drop
>   ])
>   
>   OVS_VSWITCHD_STOP
> diff --git a/tests/packet-type-aware.at b/tests/packet-type-aware.at
> index bfb47b4..a56b0ee 100644
> --- a/tests/packet-type-aware.at
> +++ b/tests/packet-type-aware.at
> @@ -505,7 +505,7 @@ AT_CHECK([
>       ovs-appctl dpctl/dump-flows --names dummy at ovs-dummy | strip_used | grep -v ipv6 | sort
>   ], [0], [flow-dump from non-dpdk interfaces:
>   recirc_id(0),in_port(n3),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.20,tos=0/0x3,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,clone(tnl_push(tnl_port(gre_sys),header(size=38,type=3,eth(dst=aa:55:00:00:00:02,src=aa:55:00:00:00:03,dl_type=0x0800),ipv4(src=30.0.0.3,dst=30.0.0.2,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x0,proto=0x800))),out_port(br-p3)),set(ipv4(src=20.0.0.3,dst=20.0.0.2)),tnl_pop(gre_sys))
> -tunnel(src=20.0.0.3,dst=20.0.0.2,flags(-df-csum)),recirc_id(0),in_port(gre_sys),packet_type(ns=1,id=0x800),ipv4(dst=192.168.10.20,frag=no), packets:1, bytes:84, used:0.0s, actions:drop
> +tunnel(src=20.0.0.3,dst=20.0.0.2,flags(-df-csum)),recirc_id(0),in_port(gre_sys),packet_type(ns=1,id=0x800),ipv4(dst=192.168.10.20,frag=no), packets:1, bytes:84, used:0.0s, actions:drop:pipeline-drop
>   ])
>   
>   OVS_VSWITCHD_STOP(["/The Open vSwitch kernel module is probably not loaded/d"])
> @@ -565,7 +565,13 @@ ovs-appctl time/warp 1000
>   AT_CHECK([
>       ovs-appctl dpctl/dump-flows | strip_used | grep -v ipv6 | sort
>   ], [0], [flow-dump from non-dpdk interfaces:
> -recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:drop
> +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:drop:unsupported packet type
> +])
> +
> +AT_CHECK([
> +    ovs-appctl dpif/show-drop-stats --detail | grep "unsupported packet type" | tr -s " " | sed 's/[ \t]*$//'
> +], [0], [dnl
> +11 unsupported packet type 2
>   ])
>   
>   # Encap(ethernet) on Ethernet frame -> should be droped
> @@ -587,7 +593,7 @@ ovs-appctl time/warp 1000
>   AT_CHECK([
>       ovs-appctl dpctl/dump-flows | strip_used | grep -v ipv6 | sort
>   ], [0], [flow-dump from non-dpdk interfaces:
> -recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:drop
> +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:drop:pipeline-drop
>   ])
>   
>   # Encap(ethernet) on VLAN tagged Ethernet frame -> should be droped
> @@ -609,7 +615,7 @@ ovs-appctl time/warp 1000
>   AT_CHECK([
>       ovs-appctl dpctl/dump-flows | strip_used | grep -v ipv6 | sort
>   ], [0], [flow-dump from non-dpdk interfaces:
> -recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:drop
> +recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:drop:unsupported packet type
>   ])
>   
>   OVS_VSWITCHD_STOP
> @@ -770,7 +776,7 @@ ovs-appctl time/warp 1000
>   AT_CHECK([
>       ovs-appctl dpctl/dump-flows --names dummy at ovs-dummy | strip_used | grep -v ipv6 | sort
>   ], [0], [flow-dump from non-dpdk interfaces:
> -recirc_id(0),in_port(n0),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:drop
> +recirc_id(0),in_port(n0),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:drop:pipeline-drop
>   ])
>   
>   AT_CHECK([
> diff --git a/tests/testsuite.at b/tests/testsuite.at
> index 690904e..01def11 100644
> --- a/tests/testsuite.at
> +++ b/tests/testsuite.at
> @@ -81,3 +81,4 @@ m4_include([tests/ovn-controller-vtep.at])
>   m4_include([tests/mcast-snooping.at])
>   m4_include([tests/packet-type-aware.at])
>   m4_include([tests/nsh.at])
> +m4_include([tests/drop-stats.at])
> diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
> index f717243..9073420 100644
> --- a/tests/tunnel-push-pop.at
> +++ b/tests/tunnel-push-pop.at
> @@ -447,6 +447,24 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  7'], [0], [dnl
>     port  7: rx pkts=3, bytes=252, drop=?, errs=?, frame=?, over=?, crc=?
>   ])
>   
> +AT_CHECK([ovs-appctl dpif/clear-drop-stats])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +
> +AT_CHECK([
> +ovs-appctl dpif/show-drop-stats --detail | grep "tunnel pop action" | tr -s " " | sed 's/[ \t]*$//'
> +], [0], [dnl
> +16 tunnel pop action errors 1
> +])
> +
> +AT_CHECK([ovs-appctl dpif/clear-drop-stats])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +
> +AT_CHECK([
> +ovs-appctl dpif/show-drop-stats --detail | grep "ecn mismatch" | tr -s " " | sed 's/[ \t]*$//'
> +], [0], [dnl
> +12 ecn mismatch at tunnel decapsulation 1
> +])
> +
>   dnl Check GREL3 only accepts non-fragmented packets?
>   AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>   
> @@ -455,7 +473,7 @@ ovs-appctl time/warp 1000
>   
>   AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  [[37]]' | sort], [0], [dnl
>     port  3: rx pkts=3, bytes=294, drop=?, errs=?, frame=?, over=?, crc=?
> -  port  7: rx pkts=4, bytes=350, drop=?, errs=?, frame=?, over=?, crc=?
> +  port  7: rx pkts=5, bytes=434, drop=?, errs=?, frame=?, over=?, crc=?
>   ])
>   
>   dnl Check decapsulation of Geneve packet with options
> @@ -510,7 +528,8 @@ AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
>   Listening ports:
>   ])
>   
> -OVS_VSWITCHD_STOP
> +OVS_VSWITCHD_STOP(["/dropping tunnel packet marked ECN CE but is not ECN capable/d
> +/ip packet has invalid checksum/d"])
>   AT_CLEANUP
>   
>   AT_SETUP([tunnel_push_pop - packet_out])
> diff --git a/tests/tunnel.at b/tests/tunnel.at
> index e110a82..3d923f6 100644
> --- a/tests/tunnel.at
> +++ b/tests/tunnel.at
> @@ -102,10 +102,12 @@ Datapath actions: set(ipv4(tos=0x3/0x3)),2
>   
>   dnl Tunnel CE and encapsulated packet Non-ECT
>   AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
> -AT_CHECK([tail -2 stdout], [0],
> +AT_CHECK([tail -3 stdout], [0],
>     [Megaflow: recirc_id=0,eth,ip,tun_id=0,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=3,tun_flags=-df-csum-key,in_port=1,nw_ecn=0,nw_frag=no
> -Datapath actions: drop
> +Datapath actions: drop:ecn mismatch at tunnel decapsulation
> +Translation failed (CONGESTION DROP), packet is dropped.
>   ])
> +
>   OVS_VSWITCHD_STOP(["/dropping tunnel packet marked ECN CE but is not ECN capable/d"])
>   AT_CLEANUP
>   
> @@ -193,6 +195,15 @@ AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:
>   AT_CHECK([tail -1 stdout], [0],
>     [Datapath actions: set(tunnel(tun_id=0x5,src=2.2.2.2,dst=1.1.1.1,ttl=64,flags(df|key))),set(skb_mark(0x2)),1
>   ])
> +
> +AT_CHECK([ovs-appctl dpif/clear-drop-stats])
> +AT_CHECK([ovs-appctl netdev-dummy/receive p2 'aa55aa550001f8bc124434b6080045000054ba20000040018486010103580101037001004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +
> +AT_CHECK([
> +ovs-appctl dpif/show-drop-stats --detail | grep "invalid port" | tr -s " " | sed 's/[ \t]*$//'
> +], [0], [dnl
> +23 invalid port 1
> +])
>   OVS_VSWITCHD_STOP
>   AT_CLEANUP
>   
> @@ -364,7 +375,7 @@ Datapath actions: 4,3,5
>   
>   AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,ttl=64,flags(key)),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
>   AT_CHECK([tail -1 stdout], [0], [dnl
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>   ])
>   
>   OVS_VSWITCHD_STOP
> @@ -571,7 +582,7 @@ dnl receive packet from ERSPAN port with wrong v1 metadata
>   AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x1,src=1.1.1.1,dst=2.2.2.2,ttl=64,erspan(ver=1,idx=0xabcd),flags(df|key)),in_port(3),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout])
>   AT_CHECK([tail -2 stdout], [0],
>     [Megaflow: recirc_id=0,eth,ip,tun_id=0x1,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=0,tun_erspan_ver=1,tun_erspan_idx=0xabcd,tun_flags=+df-csum+key,in_port=3,nw_frag=no
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>   ])
>   
>   dnl receive packet from ERSPAN port with v2 metadata
> @@ -585,7 +596,7 @@ dnl receive packet from ERSPAN port with wrong v2 metadata
>   AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x2,src=1.1.1.2,dst=2.2.2.2,ttl=64,erspan(ver=2,dir=0,hwid=0x17),flags(df|key)),in_port(3),skb_mark(0),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(frag=no)'], [0], [stdout])
>   AT_CHECK([tail -2 stdout], [0],
>     [Megaflow: recirc_id=0,eth,ip,tun_id=0x2,tun_src=1.1.1.2,tun_dst=2.2.2.2,tun_tos=0,tun_erspan_ver=2,tun_erspan_dir=0,tun_erspan_hwid=0x1,tun_flags=+df-csum+key,in_port=4,nw_frag=no
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>   ])
>   
>   dnl test wildcard mask: recevie all v2 regardless of its metadata



More information about the dev mailing list