[ovs-dev,v4] Improved Packet Drop Statistics in OVS

Message ID 1536698639-16646-1-git-send-email-keshav.gupta@ericsson.com
State Superseded
Headers show
Series
  • [ovs-dev,v4] Improved Packet Drop Statistics in OVS
Related show

Commit Message

Keshav Gupta Sept. 11, 2018, 8:43 p.m.
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

Sample ovs-appctl dpcls/dump-flows & ovs-appctl dpif/dump-flows
displaying drop reason along with drop action.
---------------------------------------------------------------

$ ovs-appctl dpctl/dump-flows netdev@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 error 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

Co-authored-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
Co-authored-by: Anju Thomas <anju.thomas@ericsson.com>
Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
Signed-off-by: Keshav Gupta <keshav.gupta@ericsson.com>
Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
---
 datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
 lib/dpif-netdev-perf.c                            |  62 +++++++
 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                                    |  54 +++++-
 ofproto/ofproto-dpif-ipfix.c                      |   1 +
 ofproto/ofproto-dpif-sflow.c                      |   1 +
 ofproto/ofproto-dpif-unixctl.man                  |  59 ++++++
 ofproto/ofproto-dpif-upcall.c                     |   4 +-
 ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
 ofproto/ofproto-dpif-xlate.h                      |   4 +
 ofproto/ofproto-dpif.c                            |  89 +++++++++
 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 ++-
 31 files changed, 1222 insertions(+), 98 deletions(-)
 create mode 100644 tests/drop-stats.at

Comments

0-day Robot Sept. 11, 2018, 12:54 p.m. | #1
Bleep bloop.  Greetings Keshav Gupta, I am a robot and I have tried out your patch.
Thanks for your contribution.

I encountered some error that I wasn't expecting.  See the details below.


git-am:
<stdin>:44: trailing whitespace.
 * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP. 
<stdin>:58: trailing whitespace.
 * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to 
<stdin>:90: trailing whitespace.
    enum ovs_drop_reason drop_reason;    
warning: 3 lines add whitespace errors.


Please check this out.  If you feel there has been an error, please email aconole@bytheb.org

Thanks,
0-day Robot
Ilya Maximets Sept. 11, 2018, 2:02 p.m. | #2
Hi Keshav,

Please fix following 'git am' warnings:
---
$ git am ./review/ovs-dev-v4-Improved-Packet-Drop-Statistics-in-OVS.patch
Applying: Improved Packet Drop Statistics in OVS
.git/rebase-apply/patch:44: trailing whitespace.
 * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP. 
.git/rebase-apply/patch:58: trailing whitespace.
 * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to 
.git/rebase-apply/patch:90: trailing whitespace.
    enum ovs_drop_reason drop_reason;    
warning: 3 lines add whitespace errors.
---

One general comment:
Did you consider using coverage counters for this reason?
I mean, if you replace pmd_perf_update_drop_counter() calls with
COVERAGE_INC(), this will cover most of the functionality by
existing code. What do you think?

Some comments inline.
Not a full review.

Best regards, Ilya Maximets.

On 11.09.2018 23:43, 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
> 
> Sample ovs-appctl dpcls/dump-flows & ovs-appctl dpif/dump-flows
> displaying drop reason along with drop action.
> ---------------------------------------------------------------
> 
> $ ovs-appctl dpctl/dump-flows netdev@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

Above lines are too long and there is no real profit having them
this way. You may shorten them omitting not so interesting parts.
Maybe something like this:

 recirc_id(0), <...>, used:5.008s, actions:drop:recursion too deep

Also, I'm thinking, maybe it'll be better to put the reason into
quotes or some kind of brackets for better automated parsing.
It's hard to understand how to parse current version.
What do you think? Ben?

> 
> 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

Why so curved?
Also, I'd like to see a space between ':' and a number.

> 
> 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 error 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
> 
> Co-authored-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Co-authored-by: Anju Thomas <anju.thomas@ericsson.com>
> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Signed-off-by: Keshav Gupta <keshav.gupta@ericsson.com>
> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
> ---
>  datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
>  lib/dpif-netdev-perf.c                            |  62 +++++++
>  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                                    |  54 +++++-
>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>  ofproto/ofproto-dpif-unixctl.man                  |  59 ++++++
>  ofproto/ofproto-dpif-upcall.c                     |   4 +-
>  ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
>  ofproto/ofproto-dpif-xlate.h                      |   4 +
>  ofproto/ofproto-dpif.c                            |  89 +++++++++
>  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 ++-
>  31 files changed, 1222 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

This is a linux file. You need to use linux kernel coding style for it.
i.e. tabs for indentation.
Usually, changes to this file should go to linux first and backported
after that. But, I guess, you discussed this already.

> @@ -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..56ca9c6 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,41 @@ pmd_perf_stats_clear(struct pmd_perf_stats *s)
>      }
>  }
>  
> +void
> +pmd_perf_drop_stats_clear(struct pmd_perf_stats *s)
> +{
> +    int i;
> +    uint64_t rx_drops;
> +    uint64_t drop_action_drops;
> +    uint64_t dp_drops;
> +    uint64_t tx_drops;
> +
> +    for (i = 0; i < DPIF_RX_MAX_DROP; i++) {
> +        atomic_read_relaxed(&s->drop_counters.n.rx_drops[i],
> +                            &rx_drops);
> +        atomic_store_relaxed(&s->drop_counters.zero.rx_drops[i],
> +                             rx_drops);

Actually, the unixctl threads are the only users of the 'zero' counters.
I see no practical reason to have them atomic. You may have them as usual
variables and protect access by existing stats_mutex.


> +    }
> +    for (i = 0; i < OVS_DROP_REASON_MAX; i++) {
> +        atomic_read_relaxed(&s->drop_counters.n.drop_action_drops[i],
> +                            &drop_action_drops);
> +        atomic_store_relaxed(&s->drop_counters.zero.drop_action_drops[i],
> +                             drop_action_drops);
> +    }
> +    for (i = 0; i < DPIF_DP_MAX_DROP; i++) {
> +        atomic_read_relaxed(&s->drop_counters.n.dp_drops[i],
> +                            &dp_drops);
> +        atomic_store_relaxed(&s->drop_counters.zero.dp_drops[i],
> +                             dp_drops);
> +    }
> +    for (i = 0; i < DPIF_TX_MAX_DROP; i++) {
> +        atomic_read_relaxed(&s->drop_counters.n.tx_drops[i],
> +                            &tx_drops);
> +        atomic_store_relaxed(&s->drop_counters.zero.tx_drops[i],
> +                             tx_drops);
> +    }
> +}
> +
>  /* 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..be62665 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 error 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..aeb8ef3 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,49 @@ 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)";
> +    case OVS_DROP_REASON_MAX:
> +    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 +1218,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 +2459,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-unixctl.man b/ofproto/ofproto-dpif-unixctl.man
> index bbf7fbb..ae107b9 100644
> --- a/ofproto/ofproto-dpif-unixctl.man
> +++ b/ofproto/ofproto-dpif-unixctl.man
> @@ -32,3 +32,62 @@ underlying datapath implementation (e.g., kernel datapath module).
>  This command is primarily useful for debugging Open vSwitch.  As
>  discussed in \fBdpif/dump\-flows\fR, these entries are
>  not OpenFlow flow entries.
> +.IP "\fBdpif/show\-drop\-stats\fR [\fB\--detail\fR]"
> +Prints the dataplane drops for Open vSwitch datapath.
> +With [\fB\--detail\fR], it shows the detailed classified drop.
> +
> +This command is primarily useful for debugging dataplane drops.
> +It helps in indentifying the reason of packet drop.
> +.IP
> +[currently supported for netdev datapath only]
> +
> + Sample 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
> + $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 error 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
> +
> +.IP "\fBdpif/clear\-drop\-stats\fR"
> +Clears the drop stats displayed in dpif/show-drop-stats command.
> +.IP
> +[currently supported for netdev datapath only]
> 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..6773004 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 *);
>  
> +enum ovs_drop_reason 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..942deeb 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,83 @@ 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 (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[] OVS_UNUSED,
> +                                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 +5904,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@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@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@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@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@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
>
Keshav Gupta Sept. 14, 2018, 12:01 p.m. | #3
Hi Ilya 
     Thanks for reviewing the patch. Please find my response inline.

I will send out a new v5 patch incorporating your comments.

Thanks
Keshav

-----Original Message-----
From: Ilya Maximets [mailto:i.maximets@samsung.com] 
Sent: Tuesday, September 11, 2018 7:32 PM
To: Keshav Gupta <keshav.gupta@ericsson.com>; dev@openvswitch.org
Cc: Anju Thomas <anju.thomas@ericsson.com>; Ben Pfaff <blp@ovn.org>
Subject: Re: [ovs-dev,v4] Improved Packet Drop Statistics in OVS

Hi Keshav,

Please fix following 'git am' warnings:
<ekesgup> Ok I will incorporate these warning in my next patch.

---
$ git am ./review/ovs-dev-v4-Improved-Packet-Drop-Statistics-in-OVS.patch
Applying: Improved Packet Drop Statistics in OVS
.git/rebase-apply/patch:44: trailing whitespace.
 * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP. 
.git/rebase-apply/patch:58: trailing whitespace.
 * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to 
.git/rebase-apply/patch:90: trailing whitespace.
    enum ovs_drop_reason drop_reason;    
warning: 3 lines add whitespace errors.
---

One general comment:
Did you consider using coverage counters for this reason?
I mean, if you replace pmd_perf_update_drop_counter() calls with
COVERAGE_INC(), this will cover most of the functionality by
existing code. What do you think?

<ekesgup> We did not consider this COVERAGE counter infra before. 
Now I looked at this infra but I believe this infra will not be useful for this implementation as 
these COVERAGE counter variable are maintained as thread local variable and we will have to use in different
thread for clearing and showing purpose. Moreover I believe there 
will be little performance penalty updating some of counter which are updated in fast path. 
Also in this infra we will have to  define may COVERAGE_DEFINE for all these granular drop counters. 


Some comments inline.
Not a full review.

Best regards, Ilya Maximets.

On 11.09.2018 23:43, 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
> 
> Sample ovs-appctl dpcls/dump-flows & ovs-appctl dpif/dump-flows
> displaying drop reason along with drop action.
> ---------------------------------------------------------------
> 
> $ ovs-appctl dpctl/dump-flows netdev@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

Above lines are too long and there is no real profit having them
this way. You may shorten them omitting not so interesting parts.
Maybe something like this:

 recirc_id(0), <...>, used:5.008s, actions:drop:recursion too deep

<ekesgup> I will change the commit message 

Also, I'm thinking, maybe it'll be better to put the reason into
quotes or some kind of brackets for better automated parsing.
It's hard to understand how to parse current version.
What do you think? Ben?

<ekesgup> Let me know if I need to change.

> 
> 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

Why so curved?
Also, I'd like to see a space between ':' and a number.
<ekesgup> I will add the space after colon. Curvy output was copy/pasting problem in commit message. I will
Incorporate this in my next patch. 
 
> 
> 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 error 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
> 
> Co-authored-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Co-authored-by: Anju Thomas <anju.thomas@ericsson.com>
> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Signed-off-by: Keshav Gupta <keshav.gupta@ericsson.com>
> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
> ---
>  datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
>  lib/dpif-netdev-perf.c                            |  62 +++++++
>  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                                    |  54 +++++-
>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>  ofproto/ofproto-dpif-unixctl.man                  |  59 ++++++
>  ofproto/ofproto-dpif-upcall.c                     |   4 +-
>  ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
>  ofproto/ofproto-dpif-xlate.h                      |   4 +
>  ofproto/ofproto-dpif.c                            |  89 +++++++++
>  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 ++-
>  31 files changed, 1222 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

This is a linux file. You need to use linux kernel coding style for it.
i.e. tabs for indentation.
Usually, changes to this file should go to linux first and backported
after that. But, I guess, you discussed this already.

<ekesgup> Ok I will changes it as per kernel coding style. Yes it was discussed already. 

> @@ -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..56ca9c6 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,41 @@ pmd_perf_stats_clear(struct pmd_perf_stats *s)
>      }
>  }
>  
> +void
> +pmd_perf_drop_stats_clear(struct pmd_perf_stats *s)
> +{
> +    int i;
> +    uint64_t rx_drops;
> +    uint64_t drop_action_drops;
> +    uint64_t dp_drops;
> +    uint64_t tx_drops;
> +
> +    for (i = 0; i < DPIF_RX_MAX_DROP; i++) {
> +        atomic_read_relaxed(&s->drop_counters.n.rx_drops[i],
> +                            &rx_drops);
> +        atomic_store_relaxed(&s->drop_counters.zero.rx_drops[i],
> +                             rx_drops);

Actually, the unixctl threads are the only users of the 'zero' counters.
I see no practical reason to have them atomic. You may have them as usual
variables and protect access by existing stats_mutex.

<ekesgup> Agreed, Zero drop stats will be used only by unixctl thread so there is no need of it to 
be atomic . We thought of using same structure for both drop stats and zero drop stats before. 
 I will define a separate struct for zero drop stats and send it in my next patch

> +    }
> +    for (i = 0; i < OVS_DROP_REASON_MAX; i++) {
> +        atomic_read_relaxed(&s->drop_counters.n.drop_action_drops[i],
> +                            &drop_action_drops);
> +        atomic_store_relaxed(&s->drop_counters.zero.drop_action_drops[i],
> +                             drop_action_drops);
> +    }
> +    for (i = 0; i < DPIF_DP_MAX_DROP; i++) {
> +        atomic_read_relaxed(&s->drop_counters.n.dp_drops[i],
> +                            &dp_drops);
> +        atomic_store_relaxed(&s->drop_counters.zero.dp_drops[i],
> +                             dp_drops);
> +    }
> +    for (i = 0; i < DPIF_TX_MAX_DROP; i++) {
> +        atomic_read_relaxed(&s->drop_counters.n.tx_drops[i],
> +                            &tx_drops);
> +        atomic_store_relaxed(&s->drop_counters.zero.tx_drops[i],
> +                             tx_drops);
> +    }
> +}
> +
>  /* 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..be62665 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 error 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..aeb8ef3 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,49 @@ 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)";
> +    case OVS_DROP_REASON_MAX:
> +    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 +1218,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 +2459,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-unixctl.man b/ofproto/ofproto-dpif-unixctl.man
> index bbf7fbb..ae107b9 100644
> --- a/ofproto/ofproto-dpif-unixctl.man
> +++ b/ofproto/ofproto-dpif-unixctl.man
> @@ -32,3 +32,62 @@ underlying datapath implementation (e.g., kernel datapath module).
>  This command is primarily useful for debugging Open vSwitch.  As
>  discussed in \fBdpif/dump\-flows\fR, these entries are
>  not OpenFlow flow entries.
> +.IP "\fBdpif/show\-drop\-stats\fR [\fB\--detail\fR]"
> +Prints the dataplane drops for Open vSwitch datapath.
> +With [\fB\--detail\fR], it shows the detailed classified drop.
> +
> +This command is primarily useful for debugging dataplane drops.
> +It helps in indentifying the reason of packet drop.
> +.IP
> +[currently supported for netdev datapath only]
> +
> + Sample 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
> + $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 error 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
> +
> +.IP "\fBdpif/clear\-drop\-stats\fR"
> +Clears the drop stats displayed in dpif/show-drop-stats command.
> +.IP
> +[currently supported for netdev datapath only]
> 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..6773004 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 *);
>  
> +enum ovs_drop_reason 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..942deeb 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,83 @@ 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 (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[] OVS_UNUSED,
> +                                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 +5904,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@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@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@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@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@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
>
Ilya Maximets Sept. 14, 2018, 12:54 p.m. | #4
On 14.09.2018 15:01, Keshav Gupta wrote:
> Hi Ilya 
>      Thanks for reviewing the patch. Please find my response inline.
> 
> I will send out a new v5 patch incorporating your comments.
> 
> Thanks
> Keshav
> 
> -----Original Message-----
> From: Ilya Maximets [mailto:i.maximets@samsung.com] 
> Sent: Tuesday, September 11, 2018 7:32 PM
> To: Keshav Gupta <keshav.gupta@ericsson.com>; dev@openvswitch.org
> Cc: Anju Thomas <anju.thomas@ericsson.com>; Ben Pfaff <blp@ovn.org>
> Subject: Re: [ovs-dev,v4] Improved Packet Drop Statistics in OVS
> 
> Hi Keshav,
> 
> Please fix following 'git am' warnings:
> <ekesgup> Ok I will incorporate these warning in my next patch.
> 
> ---
> $ git am ./review/ovs-dev-v4-Improved-Packet-Drop-Statistics-in-OVS.patch
> Applying: Improved Packet Drop Statistics in OVS
> .git/rebase-apply/patch:44: trailing whitespace.
>  * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP. 
> .git/rebase-apply/patch:58: trailing whitespace.
>  * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to 
> .git/rebase-apply/patch:90: trailing whitespace.
>     enum ovs_drop_reason drop_reason;    
> warning: 3 lines add whitespace errors.
> ---
> 
> One general comment:
> Did you consider using coverage counters for this reason?
> I mean, if you replace pmd_perf_update_drop_counter() calls with
> COVERAGE_INC(), this will cover most of the functionality by
> existing code. What do you think?
> 
> <ekesgup> We did not consider this COVERAGE counter infra before. 
> Now I looked at this infra but I believe this infra will not be useful for this implementation as 
> these COVERAGE counter variable are maintained as thread local variable and we will have to use in different
> thread for clearing and showing purpose.

It's already done. Look at the 'coverage_try_clear()' function which is
called from the 'pmd_thread_main()'. See also appctl commands 'coverage/clear'
and 'coverage/show'.

> Moreover I believe there 
> will be little performance penalty updating some of counter which are updated in fast path.

This is not an issue. atomics in your implementation should be much
more hungry than a single local variable increment in coverage infra.
For example, you may see that coverage counters are in use for almost
each memory allocation, for each send and receive which are on a hot path.
They was designed to have almost zero impact on datapath performance.
COVERAGE should be faster than your current implementation because it
does not use implicit function calls by a pointer and has no atomic
operations which could be very expensive on some non-x86 architectures.

 
> Also in this infra we will have to  define may COVERAGE_DEFINE for all these granular drop counters.

Yes, here you'll need to split definitions across files, but you need
to update counters only in dpif-netdev and odp-execute modules.
Should not be a big deal.

> 
> 
> Some comments inline.
> Not a full review.
> 
> Best regards, Ilya Maximets.
> 
> On 11.09.2018 23:43, 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
>>
>> Sample ovs-appctl dpcls/dump-flows & ovs-appctl dpif/dump-flows
>> displaying drop reason along with drop action.
>> ---------------------------------------------------------------
>>
>> $ ovs-appctl dpctl/dump-flows netdev@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
> 
> Above lines are too long and there is no real profit having them
> this way. You may shorten them omitting not so interesting parts.
> Maybe something like this:
> 
>  recirc_id(0), <...>, used:5.008s, actions:drop:recursion too deep
> 
> <ekesgup> I will change the commit message 
> 
> Also, I'm thinking, maybe it'll be better to put the reason into
> quotes or some kind of brackets for better automated parsing.
> It's hard to understand how to parse current version.
> What do you think? Ben?
> 
> <ekesgup> Let me know if I need to change.

I think you should, but I'm, probably, not the right person to ask.

> 
>>
>> 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
> 
> Why so curved?
> Also, I'd like to see a space between ':' and a number.
> <ekesgup> I will add the space after colon. Curvy output was copy/pasting problem in commit message. I will
> Incorporate this in my next patch. 
>  
>>
>> 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 error 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
>>
>> Co-authored-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
>> Co-authored-by: Anju Thomas <anju.thomas@ericsson.com>
>> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
>> Signed-off-by: Keshav Gupta <keshav.gupta@ericsson.com>
>> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
>> ---
>>  datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
>>  lib/dpif-netdev-perf.c                            |  62 +++++++
>>  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                                    |  54 +++++-
>>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>>  ofproto/ofproto-dpif-unixctl.man                  |  59 ++++++
>>  ofproto/ofproto-dpif-upcall.c                     |   4 +-
>>  ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
>>  ofproto/ofproto-dpif-xlate.h                      |   4 +
>>  ofproto/ofproto-dpif.c                            |  89 +++++++++
>>  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 ++-
>>  31 files changed, 1222 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
> 
> This is a linux file. You need to use linux kernel coding style for it.
> i.e. tabs for indentation.
> Usually, changes to this file should go to linux first and backported
> after that. But, I guess, you discussed this already.
> 
> <ekesgup> Ok I will changes it as per kernel coding style. Yes it was discussed already. 
> 
>> @@ -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..56ca9c6 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,41 @@ pmd_perf_stats_clear(struct pmd_perf_stats *s)
>>      }
>>  }
>>  
>> +void
>> +pmd_perf_drop_stats_clear(struct pmd_perf_stats *s)
>> +{
>> +    int i;
>> +    uint64_t rx_drops;
>> +    uint64_t drop_action_drops;
>> +    uint64_t dp_drops;
>> +    uint64_t tx_drops;
>> +
>> +    for (i = 0; i < DPIF_RX_MAX_DROP; i++) {
>> +        atomic_read_relaxed(&s->drop_counters.n.rx_drops[i],
>> +                            &rx_drops);
>> +        atomic_store_relaxed(&s->drop_counters.zero.rx_drops[i],
>> +                             rx_drops);
> 
> Actually, the unixctl threads are the only users of the 'zero' counters.
> I see no practical reason to have them atomic. You may have them as usual
> variables and protect access by existing stats_mutex.
> 
> <ekesgup> Agreed, Zero drop stats will be used only by unixctl thread so there is no need of it to 
> be atomic . We thought of using same structure for both drop stats and zero drop stats before. 
>  I will define a separate struct for zero drop stats and send it in my next patch

Beside the discussion of COVERAGE,
Have you considered using single stats array or a 2-dimentional array?
This could save some LOCs while processing, i.e. will eliminate 'switch'es.
Also, in this case you will not need to have structures. Just two arrays.

> 
>> +    }
>> +    for (i = 0; i < OVS_DROP_REASON_MAX; i++) {
>> +        atomic_read_relaxed(&s->drop_counters.n.drop_action_drops[i],
>> +                            &drop_action_drops);
>> +        atomic_store_relaxed(&s->drop_counters.zero.drop_action_drops[i],
>> +                             drop_action_drops);
>> +    }
>> +    for (i = 0; i < DPIF_DP_MAX_DROP; i++) {
>> +        atomic_read_relaxed(&s->drop_counters.n.dp_drops[i],
>> +                            &dp_drops);
>> +        atomic_store_relaxed(&s->drop_counters.zero.dp_drops[i],
>> +                             dp_drops);
>> +    }
>> +    for (i = 0; i < DPIF_TX_MAX_DROP; i++) {
>> +        atomic_read_relaxed(&s->drop_counters.n.tx_drops[i],
>> +                            &tx_drops);
>> +        atomic_store_relaxed(&s->drop_counters.zero.tx_drops[i],
>> +                             tx_drops);
>> +    }
>> +}
>> +
>>  /* 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..be62665 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 error 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..aeb8ef3 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,49 @@ 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)";
>> +    case OVS_DROP_REASON_MAX:
>> +    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 +1218,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 +2459,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-unixctl.man b/ofproto/ofproto-dpif-unixctl.man
>> index bbf7fbb..ae107b9 100644
>> --- a/ofproto/ofproto-dpif-unixctl.man
>> +++ b/ofproto/ofproto-dpif-unixctl.man
>> @@ -32,3 +32,62 @@ underlying datapath implementation (e.g., kernel datapath module).
>>  This command is primarily useful for debugging Open vSwitch.  As
>>  discussed in \fBdpif/dump\-flows\fR, these entries are
>>  not OpenFlow flow entries.
>> +.IP "\fBdpif/show\-drop\-stats\fR [\fB\--detail\fR]"
>> +Prints the dataplane drops for Open vSwitch datapath.
>> +With [\fB\--detail\fR], it shows the detailed classified drop.
>> +
>> +This command is primarily useful for debugging dataplane drops.
>> +It helps in indentifying the reason of packet drop.
>> +.IP
>> +[currently supported for netdev datapath only]
>> +
>> + Sample 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
>> + $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 error 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
>> +
>> +.IP "\fBdpif/clear\-drop\-stats\fR"
>> +Clears the drop stats displayed in dpif/show-drop-stats command.
>> +.IP
>> +[currently supported for netdev datapath only]
>> 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..6773004 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 *);
>>  
>> +enum ovs_drop_reason 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..942deeb 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,83 @@ 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 (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[] OVS_UNUSED,
>> +                                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 +5904,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@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@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@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@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@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
>>
Keshav Gupta Sept. 20, 2018, 1:27 p.m. | #5
Hi Ilya
            Sorry for the late reply. Please find my inline reply with <keshav>.

Thanks
Keshav


-----Original Message-----	
From: Ilya Maximets [mailto:i.maximets@samsung.com] 
Sent: Friday, September 14, 2018 6:25 PM
To: Keshav Gupta <keshav.gupta@ericsson.com>; dev@openvswitch.org
Cc: Anju Thomas <anju.thomas@ericsson.com>; Ben Pfaff <blp@ovn.org>
Subject: Re: [ovs-dev,v4] Improved Packet Drop Statistics in OVS

On 14.09.2018 15:01, Keshav Gupta wrote:
> Hi Ilya 
>      Thanks for reviewing the patch. Please find my response inline.
> 
> I will send out a new v5 patch incorporating your comments.
> 
> Thanks
> Keshav
> 
> -----Original Message-----
> From: Ilya Maximets [mailto:i.maximets@samsung.com] 
> Sent: Tuesday, September 11, 2018 7:32 PM
> To: Keshav Gupta <keshav.gupta@ericsson.com>; dev@openvswitch.org
> Cc: Anju Thomas <anju.thomas@ericsson.com>; Ben Pfaff <blp@ovn.org>
> Subject: Re: [ovs-dev,v4] Improved Packet Drop Statistics in OVS
> 
> Hi Keshav,
> 
> Please fix following 'git am' warnings:
> <ekesgup> Ok I will incorporate these warning in my next patch.
> 
> ---
> $ git am ./review/ovs-dev-v4-Improved-Packet-Drop-Statistics-in-OVS.patch
> Applying: Improved Packet Drop Statistics in OVS
> .git/rebase-apply/patch:44: trailing whitespace.
>  * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP. 
> .git/rebase-apply/patch:58: trailing whitespace.
>  * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to 
> .git/rebase-apply/patch:90: trailing whitespace.
>     enum ovs_drop_reason drop_reason;    
> warning: 3 lines add whitespace errors.
> ---
> 
> One general comment:
> Did you consider using coverage counters for this reason?
> I mean, if you replace pmd_perf_update_drop_counter() calls with
> COVERAGE_INC(), this will cover most of the functionality by
> existing code. What do you think?
> 
> <ekesgup> We did not consider this COVERAGE counter infra before. 
> Now I looked at this infra but I believe this infra will not be useful for this implementation as 
> these COVERAGE counter variable are maintained as thread local variable and we will have to use in different
> thread for clearing and showing purpose.

It's already done. Look at the 'coverage_try_clear()' function which is
called from the 'pmd_thread_main()'. See also appctl commands 'coverage/clear'
and 'coverage/show'.


<keshav> Thanks for this info. Now I see this infra is already placed and written very efficiently. 
I think it is possible to leverage this infra in the existing design.
To make the debugging easier we show all the drop counters including hardware drops(tx/rx) with ovs-appctl dpif-netdev/show-drop-stats . 
If we use this infra then most of drop counter will be displayed with "coverage/show" output also.  Only thing is if somebody clears the counter
Using ovs-appctl dpif-netdev/clear-drop-stats then counters will be shown differently with coverage/show command, which I believe should be OK

In the code I  see only coverage/show command implementation not for the coverage/clear. If somebody implement the coverage/clear command 
By taking the snapshot then it will be Ok otherwise I think it may be problem. But I believe coverage/clear command implementation may not be required
as it also keeps the 5 second,1 minute and 1 hour drop counters. This info will also be useful in packet drop debugging.



> Moreover I believe there 
> will be little performance penalty updating some of counter which are updated in fast path.

This is not an issue. atomics in your implementation should be much
more hungry than a single local variable increment in coverage infra.
For example, you may see that coverage counters are in use for almost
each memory allocation, for each send and receive which are on a hot path.
They was designed to have almost zero impact on datapath performance.
COVERAGE should be faster than your current implementation because it
does not use implicit function calls by a pointer and has no atomic
operations which could be very expensive on some non-x86 architectures.

 
> Also in this infra we will have to  define may COVERAGE_DEFINE for all these granular drop counters.

Yes, here you'll need to split definitions across files, but you need
to update counters only in dpif-netdev and odp-execute modules.
Should not be a big deal.

> 
> 
> Some comments inline.
> Not a full review.
> 
> Best regards, Ilya Maximets.
> 
> On 11.09.2018 23:43, 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
>>
>> Sample ovs-appctl dpcls/dump-flows & ovs-appctl dpif/dump-flows
>> displaying drop reason along with drop action.
>> ---------------------------------------------------------------
>>
>> $ ovs-appctl dpctl/dump-flows netdev@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
> 
> Above lines are too long and there is no real profit having them
> this way. You may shorten them omitting not so interesting parts.
> Maybe something like this:
> 
>  recirc_id(0), <...>, used:5.008s, actions:drop:recursion too deep
> 
> <ekesgup> I will change the commit message 
> 
> Also, I'm thinking, maybe it'll be better to put the reason into
> quotes or some kind of brackets for better automated parsing.
> It's hard to understand how to parse current version.
> What do you think? Ben?
> 
> <ekesgup> Let me know if I need to change.

I think you should, but I'm, probably, not the right person to ask.

> 
>>
>> 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
> 
> Why so curved?
> Also, I'd like to see a space between ':' and a number.
> <ekesgup> I will add the space after colon. Curvy output was copy/pasting problem in commit message. I will
> Incorporate this in my next patch. 
>  
>>
>> 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 error 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
>>
>> Co-authored-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
>> Co-authored-by: Anju Thomas <anju.thomas@ericsson.com>
>> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
>> Signed-off-by: Keshav Gupta <keshav.gupta@ericsson.com>
>> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
>> ---
>>  datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
>>  lib/dpif-netdev-perf.c                            |  62 +++++++
>>  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                                    |  54 +++++-
>>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>>  ofproto/ofproto-dpif-unixctl.man                  |  59 ++++++
>>  ofproto/ofproto-dpif-upcall.c                     |   4 +-
>>  ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
>>  ofproto/ofproto-dpif-xlate.h                      |   4 +
>>  ofproto/ofproto-dpif.c                            |  89 +++++++++
>>  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 ++-
>>  31 files changed, 1222 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
> 
> This is a linux file. You need to use linux kernel coding style for it.
> i.e. tabs for indentation.
> Usually, changes to this file should go to linux first and backported
> after that. But, I guess, you discussed this already.
> 
> <ekesgup> Ok I will changes it as per kernel coding style. Yes it was discussed already. 
> 
>> @@ -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..56ca9c6 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,41 @@ pmd_perf_stats_clear(struct pmd_perf_stats *s)
>>      }
>>  }
>>  
>> +void
>> +pmd_perf_drop_stats_clear(struct pmd_perf_stats *s)
>> +{
>> +    int i;
>> +    uint64_t rx_drops;
>> +    uint64_t drop_action_drops;
>> +    uint64_t dp_drops;
>> +    uint64_t tx_drops;
>> +
>> +    for (i = 0; i < DPIF_RX_MAX_DROP; i++) {
>> +        atomic_read_relaxed(&s->drop_counters.n.rx_drops[i],
>> +                            &rx_drops);
>> +        atomic_store_relaxed(&s->drop_counters.zero.rx_drops[i],
>> +                             rx_drops);
> 
> Actually, the unixctl threads are the only users of the 'zero' counters.
> I see no practical reason to have them atomic. You may have them as usual
> variables and protect access by existing stats_mutex.
> 
> <ekesgup> Agreed, Zero drop stats will be used only by unixctl thread so there is no need of it to 
> be atomic . We thought of using same structure for both drop stats and zero drop stats before. 
>  I will define a separate struct for zero drop stats and send it in my next patch

Beside the discussion of COVERAGE,
Have you considered using single stats array or a 2-dimentional array?
This could save some LOCs while processing, i.e. will eliminate 'switch'es.
Also, in this case you will not need to have structures. Just two arrays.

<keshav> I think we can use the single stats array by allocating unique enum for the each drop counter type .
Agreed with this we can avoid some LOC and case statements                    

> 
>> +    }
>> +    for (i = 0; i < OVS_DROP_REASON_MAX; i++) {
>> +        atomic_read_relaxed(&s->drop_counters.n.drop_action_drops[i],
>> +                            &drop_action_drops);
>> +        atomic_store_relaxed(&s->drop_counters.zero.drop_action_drops[i],
>> +                             drop_action_drops);
>> +    }
>> +    for (i = 0; i < DPIF_DP_MAX_DROP; i++) {
>> +        atomic_read_relaxed(&s->drop_counters.n.dp_drops[i],
>> +                            &dp_drops);
>> +        atomic_store_relaxed(&s->drop_counters.zero.dp_drops[i],
>> +                             dp_drops);
>> +    }
>> +    for (i = 0; i < DPIF_TX_MAX_DROP; i++) {
>> +        atomic_read_relaxed(&s->drop_counters.n.tx_drops[i],
>> +                            &tx_drops);
>> +        atomic_store_relaxed(&s->drop_counters.zero.tx_drops[i],
>> +                             tx_drops);
>> +    }
>> +}
>> +
>>  /* 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..be62665 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 error 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..aeb8ef3 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,49 @@ 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)";
>> +    case OVS_DROP_REASON_MAX:
>> +    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 +1218,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 +2459,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-unixctl.man b/ofproto/ofproto-dpif-unixctl.man
>> index bbf7fbb..ae107b9 100644
>> --- a/ofproto/ofproto-dpif-unixctl.man
>> +++ b/ofproto/ofproto-dpif-unixctl.man
>> @@ -32,3 +32,62 @@ underlying datapath implementation (e.g., kernel datapath module).
>>  This command is primarily useful for debugging Open vSwitch.  As
>>  discussed in \fBdpif/dump\-flows\fR, these entries are
>>  not OpenFlow flow entries.
>> +.IP "\fBdpif/show\-drop\-stats\fR [\fB\--detail\fR]"
>> +Prints the dataplane drops for Open vSwitch datapath.
>> +With [\fB\--detail\fR], it shows the detailed classified drop.
>> +
>> +This command is primarily useful for debugging dataplane drops.
>> +It helps in indentifying the reason of packet drop.
>> +.IP
>> +[currently supported for netdev datapath only]
>> +
>> + Sample 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
>> + $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 error 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
>> +
>> +.IP "\fBdpif/clear\-drop\-stats\fR"
>> +Clears the drop stats displayed in dpif/show-drop-stats command.
>> +.IP
>> +[currently supported for netdev datapath only]
>> 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..6773004 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 *);
>>  
>> +enum ovs_drop_reason 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..942deeb 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,83 @@ 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 (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[] OVS_UNUSED,
>> +                                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 +5904,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@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@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@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@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@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
>>
Keshav Gupta Sept. 24, 2018, 1:48 p.m. | #6
Hi Ilya
       Please find my response inline [keshav]

Thanks
Keshav

-----Original Message-----
From: Ilya Maximets [mailto:i.maximets@samsung.com] 
Sent: Friday, September 21, 2018 4:16 PM
To: Keshav Gupta <keshav.gupta@ericsson.com>; dev@openvswitch.org
Cc: Anju Thomas <anju.thomas@ericsson.com>; Ben Pfaff <blp@ovn.org>; Rohith Basavaraja <rohith.basavaraja@gmail.com>; keshugupta1@gmail.com
Subject: Re: [ovs-dev,v4] Improved Packet Drop Statistics in OVS

On 20.09.2018 16:27, Keshav Gupta wrote:
> Hi Ilya
>             Sorry for the late reply. Please find my inline reply with <keshav>.
> 
> Thanks
> Keshav
> 
> 
> -----Original Message-----	
> From: Ilya Maximets [mailto:i.maximets@samsung.com] 
> Sent: Friday, September 14, 2018 6:25 PM
> To: Keshav Gupta <keshav.gupta@ericsson.com>; dev@openvswitch.org
> Cc: Anju Thomas <anju.thomas@ericsson.com>; Ben Pfaff <blp@ovn.org>
> Subject: Re: [ovs-dev,v4] Improved Packet Drop Statistics in OVS
> 
> On 14.09.2018 15:01, Keshav Gupta wrote:
>> Hi Ilya 
>>      Thanks for reviewing the patch. Please find my response inline.
>>
>> I will send out a new v5 patch incorporating your comments.
>>
>> Thanks
>> Keshav
>>
>> -----Original Message-----
>> From: Ilya Maximets [mailto:i.maximets@samsung.com] 
>> Sent: Tuesday, September 11, 2018 7:32 PM
>> To: Keshav Gupta <keshav.gupta@ericsson.com>; dev@openvswitch.org
>> Cc: Anju Thomas <anju.thomas@ericsson.com>; Ben Pfaff <blp@ovn.org>
>> Subject: Re: [ovs-dev,v4] Improved Packet Drop Statistics in OVS
>>
>> Hi Keshav,
>>
>> Please fix following 'git am' warnings:
>> <ekesgup> Ok I will incorporate these warning in my next patch.
>>
>> ---
>> $ git am ./review/ovs-dev-v4-Improved-Packet-Drop-Statistics-in-OVS.patch
>> Applying: Improved Packet Drop Statistics in OVS
>> .git/rebase-apply/patch:44: trailing whitespace.
>>  * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP. 
>> .git/rebase-apply/patch:58: trailing whitespace.
>>  * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to 
>> .git/rebase-apply/patch:90: trailing whitespace.
>>     enum ovs_drop_reason drop_reason;    
>> warning: 3 lines add whitespace errors.
>> ---
>>
>> One general comment:
>> Did you consider using coverage counters for this reason?
>> I mean, if you replace pmd_perf_update_drop_counter() calls with
>> COVERAGE_INC(), this will cover most of the functionality by
>> existing code. What do you think?
>>
>> <ekesgup> We did not consider this COVERAGE counter infra before. 
>> Now I looked at this infra but I believe this infra will not be useful for this implementation as 
>> these COVERAGE counter variable are maintained as thread local variable and we will have to use in different
>> thread for clearing and showing purpose.
> 
> It's already done. Look at the 'coverage_try_clear()' function which is
> called from the 'pmd_thread_main()'. See also appctl commands 'coverage/clear'
> and 'coverage/show'.
> 
> 
> <keshav> Thanks for this info. Now I see this infra is already placed and written very efficiently. 
> I think it is possible to leverage this infra in the existing design.
> To make the debugging easier we show all the drop counters including hardware drops(tx/rx) with ovs-appctl dpif-netdev/show-drop-stats . 
> If we use this infra then most of drop counter will be displayed with "coverage/show" output also.  Only thing is if somebody clears the counter
> Using ovs-appctl dpif-netdev/clear-drop-stats then counters will be shown differently with coverage/show command, which I believe should be OK

IMHO, we don't need two different implementations of the same counters.
Originally, pmd-perf-stats was introduced to collect separate counters
for each thread for debugging purposes. And there was no good solution
for that. In your case you need only aggregated stats for the whole
switch and 'coverage' counters perfectly fit for that.

[Keshav]  I meant of leveraging same infra  but for showing the output in user friendly way and 
              all type of drop including hardware layer drops in one place. And same time providing user an option to 
              clear the counter (by taking the snapshot) for making  the debugging easier.

Was thinking of implementing something like below:

COVERAGE_DEFINE(drop_action_too_many_mpls_labels);
DROP_COUNTER_COVERAGE_REGISTER(drop_action_too_many_mpls_labels,OVS_DROP_REASON_TOO_MANY_MPLS_LABELS);

#define DROP_COUNTER_COVERAGE_REGISTER(COUNTER,TYPE)     \
     OVS_CONSTRUCTOR(COUNTER##_init_drop_counter_coverage) {  \
        drop_counter_register(&counter_##COUNTER,TYPE);  \
     }

void drop_counter_register(struct coverage_counter *counter, enum dpif_drops reason)
{
     drop_stats_counter. drop_stats_counter_p[reason] = counter;
}

/* Drop statistics for a dpif as a whole.*/
typedef struct dpif_drop_stats_s {
    /* Pointers to coverage drop counter */
    struct coverage_counter *drop_stats_counter_p[DPIF_MAX_DROPS];
    /* Stats at last clear */
    uint64_t zero_stats_counter[DPIF_TX_MAX_DROP];
    uint64_t zero_iface_rx_drops;
    uint64_t zero_iface_tx_drops;
} dpif_drop_stats_t;



> In the code I  see only coverage/show command implementation not for the coverage/clear. If somebody implement the coverage/clear command 
> By taking the snapshot then it will be Ok otherwise I think it may be problem. But I believe coverage/clear command implementation may not be required
> as it also keeps the 5 second,1 minute and 1 hour drop counters. This info will also be useful in packet drop debugging.

I don't think that clearing is needed since we have average rates for
the different time slices.

> 
> 
> 
>> Moreover I believe there 
>> will be little performance penalty updating some of counter which are updated in fast path.
> 
> This is not an issue. atomics in your implementation should be much
> more hungry than a single local variable increment in coverage infra.
> For example, you may see that coverage counters are in use for almost
> each memory allocation, for each send and receive which are on a hot path.
> They was designed to have almost zero impact on datapath performance.
> COVERAGE should be faster than your current implementation because it
> does not use implicit function calls by a pointer and has no atomic
> operations which could be very expensive on some non-x86 architectures.
> 
>  
>> Also in this infra we will have to  define may COVERAGE_DEFINE for all these granular drop counters.
> 
> Yes, here you'll need to split definitions across files, but you need
> to update counters only in dpif-netdev and odp-execute modules.
> Should not be a big deal.
> 
>>
>>
>> Some comments inline.
>> Not a full review.
>>
>> Best regards, Ilya Maximets.
>>
>> On 11.09.2018 23:43, 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
>>>
>>> Sample ovs-appctl dpcls/dump-flows & ovs-appctl dpif/dump-flows
>>> displaying drop reason along with drop action.
>>> ---------------------------------------------------------------
>>>
>>> $ ovs-appctl dpctl/dump-flows netdev@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
>>
>> Above lines are too long and there is no real profit having them
>> this way. You may shorten them omitting not so interesting parts.
>> Maybe something like this:
>>
>>  recirc_id(0), <...>, used:5.008s, actions:drop:recursion too deep
>>
>> <ekesgup> I will change the commit message 
>>
>> Also, I'm thinking, maybe it'll be better to put the reason into
>> quotes or some kind of brackets for better automated parsing.
>> It's hard to understand how to parse current version.
>> What do you think? Ben?
>>
>> <ekesgup> Let me know if I need to change.
> 
> I think you should, but I'm, probably, not the right person to ask.
> 
>>
>>>
>>> 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
>>
>> Why so curved?
>> Also, I'd like to see a space between ':' and a number.
>> <ekesgup> I will add the space after colon. Curvy output was copy/pasting problem in commit message. I will
>> Incorporate this in my next patch. 
>>  
>>>
>>> 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 error 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
>>>
>>> Co-authored-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
>>> Co-authored-by: Anju Thomas <anju.thomas@ericsson.com>
>>> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
>>> Signed-off-by: Keshav Gupta <keshav.gupta@ericsson.com>
>>> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
>>> ---
>>>  datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
>>>  lib/dpif-netdev-perf.c                            |  62 +++++++
>>>  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                                    |  54 +++++-
>>>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>>>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>>>  ofproto/ofproto-dpif-unixctl.man                  |  59 ++++++
>>>  ofproto/ofproto-dpif-upcall.c                     |   4 +-
>>>  ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
>>>  ofproto/ofproto-dpif-xlate.h                      |   4 +
>>>  ofproto/ofproto-dpif.c                            |  89 +++++++++
>>>  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 ++-
>>>  31 files changed, 1222 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
>>
>> This is a linux file. You need to use linux kernel coding style for it.
>> i.e. tabs for indentation.
>> Usually, changes to this file should go to linux first and backported
>> after that. But, I guess, you discussed this already.
>>
>> <ekesgup> Ok I will changes it as per kernel coding style. Yes it was discussed already. 
>>
>>> @@ -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..56ca9c6 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,41 @@ pmd_perf_stats_clear(struct pmd_perf_stats *s)
>>>      }
>>>  }
>>>  
>>> +void
>>> +pmd_perf_drop_stats_clear(struct pmd_perf_stats *s)
>>> +{
>>> +    int i;
>>> +    uint64_t rx_drops;
>>> +    uint64_t drop_action_drops;
>>> +    uint64_t dp_drops;
>>> +    uint64_t tx_drops;
>>> +
>>> +    for (i = 0; i < DPIF_RX_MAX_DROP; i++) {
>>> +        atomic_read_relaxed(&s->drop_counters.n.rx_drops[i],
>>> +                            &rx_drops);
>>> +        atomic_store_relaxed(&s->drop_counters.zero.rx_drops[i],
>>> +                             rx_drops);
>>
>> Actually, the unixctl threads are the only users of the 'zero' counters.
>> I see no practical reason to have them atomic. You may have them as usual
>> variables and protect access by existing stats_mutex.
>>
>> <ekesgup> Agreed, Zero drop stats will be used only by unixctl thread so there is no need of it to 
>> be atomic . We thought of using same structure for both drop stats and zero drop stats before. 
>>  I will define a separate struct for zero drop stats and send it in my next patch
> 
> Beside the discussion of COVERAGE,
> Have you considered using single stats array or a 2-dimentional array?
> This could save some LOCs while processing, i.e. will eliminate 'switch'es.
> Also, in this case you will not need to have structures. Just two arrays.
> 
> <keshav> I think we can use the single stats array by allocating unique enum for the each drop counter type .
> Agreed with this we can avoid some LOC and case statements                    
> 
>>
>>> +    }
>>> +    for (i = 0; i < OVS_DROP_REASON_MAX; i++) {
>>> +        atomic_read_relaxed(&s->drop_counters.n.drop_action_drops[i],
>>> +                            &drop_action_drops);
>>> +        atomic_store_relaxed(&s->drop_counters.zero.drop_action_drops[i],
>>> +                             drop_action_drops);
>>> +    }
>>> +    for (i = 0; i < DPIF_DP_MAX_DROP; i++) {
>>> +        atomic_read_relaxed(&s->drop_counters.n.dp_drops[i],
>>> +                            &dp_drops);
>>> +        atomic_store_relaxed(&s->drop_counters.zero.dp_drops[i],
>>> +                             dp_drops);
>>> +    }
>>> +    for (i = 0; i < DPIF_TX_MAX_DROP; i++) {
>>> +        atomic_read_relaxed(&s->drop_counters.n.tx_drops[i],
>>> +                            &tx_drops);
>>> +        atomic_store_relaxed(&s->drop_counters.zero.tx_drops[i],
>>> +                             tx_drops);
>>> +    }
>>> +}
>>> +
>>>  /* 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..be62665 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 error 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..aeb8ef3 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,49 @@ 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)";
>>> +    case OVS_DROP_REASON_MAX:
>>> +    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 +1218,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 +2459,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-unixctl.man b/ofproto/ofproto-dpif-unixctl.man
>>> index bbf7fbb..ae107b9 100644
>>> --- a/ofproto/ofproto-dpif-unixctl.man
>>> +++ b/ofproto/ofproto-dpif-unixctl.man
>>> @@ -32,3 +32,62 @@ underlying datapath implementation (e.g., kernel datapath module).
>>>  This command is primarily useful for debugging Open vSwitch.  As
>>>  discussed in \fBdpif/dump\-flows\fR, these entries are
>>>  not OpenFlow flow entries.
>>> +.IP "\fBdpif/show\-drop\-stats\fR [\fB\--detail\fR]"
>>> +Prints the dataplane drops for Open vSwitch datapath.
>>> +With [\fB\--detail\fR], it shows the detailed classified drop.
>>> +
>>> +This command is primarily useful for debugging dataplane drops.
>>> +It helps in indentifying the reason of packet drop.
>>> +.IP
>>> +[currently supported for netdev datapath only]
>>> +
>>> + Sample 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
>>> + $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 error 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
>>> +
>>> +.IP "\fBdpif/clear\-drop\-stats\fR"
>>> +Clears the drop stats displayed in dpif/show-drop-stats command.
>>> +.IP
>>> +[currently supported for netdev datapath only]
>>> 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..6773004 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 *);
>>>  
>>> +enum ovs_drop_reason 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..942deeb 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,83 @@ 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 (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[] OVS_UNUSED,
>>> +                                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 +5904,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),reci