diff mbox series

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

Message ID 1547729446-24805-1-git-send-email-anju.thomas@ericsson.com
State Changes Requested
Headers show
Series [ovs-dev,v6] Improved Packet Drop Statistics in OVS | expand

Commit Message

Anju Thomas Jan. 17, 2019, 4:49 a.m. UTC
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. Display these drops in ovs-appctl coverage/show
    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.

     The idea is to use the coverage infrastructure to maintain the drops

    $ ovs-appctl dpctl/dump-flows netdev at ovs-netdev
    flow-dump from pmd on cpu core: 0
    recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:12, bytes:1176, used:0.884s, actions:drop:recursion too deep

    $ ovs-appctl dpif/dump-flows br-int
    recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:25, bytes:2450, used:5.008s, actions:drop:recursion too deep
    recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0806), packets:7, bytes:294, used:0.009s, actions:drop:recursion too deep

   In subsequent commits, we are planning to see if we can use this infrastructure to create a
   wrapper to clear and display counters as well.

Co-authored-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
Co-authored-by: Keshav Gupta <keshugupta1@gmail.com>
Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
Signed-off-by: Keshav Gupta <keshugupta1@gmail.com>
---
 datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
 lib/coverage.c                                    |   6 +-
 lib/coverage.h                                    |   3 +-
 lib/dpif-netdev.c                                 | 105 +++++++++++-
 lib/dpif.c                                        |  10 +-
 lib/dpif.h                                        |   3 +
 lib/netdev-dpdk.c                                 |   4 +
 lib/odp-execute.c                                 |  50 ++++--
 lib/odp-execute.h                                 |  10 +-
 lib/odp-util.c                                    |  54 +++++-
 ofproto/ofproto-dpif-ipfix.c                      |   1 +
 ofproto/ofproto-dpif-sflow.c                      |   1 +
 ofproto/ofproto-dpif-upcall.c                     |   3 +-
 ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
 ofproto/ofproto-dpif-xlate.h                      |   4 +
 ofproto/ofproto-dpif.c                            |  11 +-
 ofproto/ofproto-dpif.h                            |   8 +-
 tests/automake.mk                                 |   3 +-
 tests/bundle.at                                   |   2 +-
 tests/classifier.at                               |  10 +-
 tests/dpif-netdev.at                              |  17 +-
 tests/drop-stats.at                               | 197 ++++++++++++++++++++++
 tests/ofproto-dpif.at                             | 118 ++++++-------
 tests/ovs-ofctl.at                                |   2 +-
 tests/packet-type-aware.at                        |  17 +-
 tests/testsuite.at                                |   1 +
 tests/tunnel-push-pop.at                          |  26 ++-
 tests/tunnel.at                                   |  21 ++-
 28 files changed, 695 insertions(+), 107 deletions(-)
 create mode 100644 tests/drop-stats.at

Comments

Anju Thomas Jan. 18, 2019, 4:54 a.m. UTC | #1
Hi Ben,
Were you able to apply this patch successfully?
Regards
Anju

-----Original Message-----
From: Anju Thomas [mailto:anju.thomas@ericsson.com] 
Sent: Thursday, January 17, 2019 10:19 AM
To: dev@openvswitch.org
Cc: Anju Thomas <anju.thomas@ericsson.com>; Rohith Basavaraja <rohith.basavaraja@gmail.com>; Keshav Gupta <keshugupta1@gmail.com>
Subject: [PATCH v6] Improved Packet Drop Statistics in OVS

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. Display these drops in ovs-appctl coverage/show
    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.

     The idea is to use the coverage infrastructure to maintain the drops

    $ ovs-appctl dpctl/dump-flows netdev at ovs-netdev
    flow-dump from pmd on cpu core: 0
    recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:12, bytes:1176, used:0.884s, actions:drop:recursion too deep

    $ ovs-appctl dpif/dump-flows br-int
    recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:25, bytes:2450, used:5.008s, actions:drop:recursion too deep
    recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0806), packets:7, bytes:294, used:0.009s, actions:drop:recursion too deep

   In subsequent commits, we are planning to see if we can use this infrastructure to create a
   wrapper to clear and display counters as well.

Co-authored-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
Co-authored-by: Keshav Gupta <keshugupta1@gmail.com>
Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
Signed-off-by: Keshav Gupta <keshugupta1@gmail.com>
---
 datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
 lib/coverage.c                                    |   6 +-
 lib/coverage.h                                    |   3 +-
 lib/dpif-netdev.c                                 | 105 +++++++++++-
 lib/dpif.c                                        |  10 +-
 lib/dpif.h                                        |   3 +
 lib/netdev-dpdk.c                                 |   4 +
 lib/odp-execute.c                                 |  50 ++++--
 lib/odp-execute.h                                 |  10 +-
 lib/odp-util.c                                    |  54 +++++-
 ofproto/ofproto-dpif-ipfix.c                      |   1 +
 ofproto/ofproto-dpif-sflow.c                      |   1 +
 ofproto/ofproto-dpif-upcall.c                     |   3 +-
 ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
 ofproto/ofproto-dpif-xlate.h                      |   4 +
 ofproto/ofproto-dpif.c                            |  11 +-
 ofproto/ofproto-dpif.h                            |   8 +-
 tests/automake.mk                                 |   3 +-
 tests/bundle.at                                   |   2 +-
 tests/classifier.at                               |  10 +-
 tests/dpif-netdev.at                              |  17 +-
 tests/drop-stats.at                               | 197 ++++++++++++++++++++++
 tests/ofproto-dpif.at                             | 118 ++++++-------
 tests/ovs-ofctl.at                                |   2 +-
 tests/packet-type-aware.at                        |  17 +-
 tests/testsuite.at                                |   1 +
 tests/tunnel-push-pop.at                          |  26 ++-
 tests/tunnel.at                                   |  21 ++-
 28 files changed, 695 insertions(+), 107 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 9b087f1..b66b46f 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -819,6 +819,56 @@ struct ovs_action_push_eth {
 };
 
 /**
+ * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP.
+ * @OVS_DROP_REASON_OF_PIPELINE: Explicit drop action in the pipeline.
+ * @OVS_DROP_REASON_BRIDGE_NOT_FOUND: Xlation error generated due to
+ * unable to determine bridge.
+ * @OVS_DROP_REASON_RECURSION_TOO_DEEP: Xlation error generated due to
+ * recursion reached maximum depth.
+ * @OVS_DROP_REASON_TOO_MANY_RESUBMITS: Xlation error generated due to
+ * too many resubmits.
+ * @OVS_DROP_REASON_STACK_TOO_DEEP: Xlation error generated due to stack
+ * too deep.
+ * @OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT: Xlation error generated
+ * due to no recirculation context.
+ * @OVS_DROP_REASON_RECIRCULATION_CONFLICT: Xlation error generated due to
+ * conflict in recirculation context.
+ * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to
+ * too many mpls labels.
+ * @OVS_DROP_REASON_INVALID_TUNNEL_METADATA: Xlation error generated due to
+ * invalid tunnel metadata.
+ * @OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE: Xlation error generated due to
+ * unsupported packet type.
+ * @OVS_DROP_REASON_CONGESTION: Xlation error generated due to ecn mismatch
+ * during tunnel decapsulation.
+ * @OVS_DROP_REASON_FORWARDING_DISABLED: Xlation error generated due to
+ * forwarding is disabled.
+ */
+enum ovs_drop_reason {
+	OVS_DROP_REASON_OF_PIPELINE = 0,
+	OVS_DROP_REASON_BRIDGE_NOT_FOUND,
+	OVS_DROP_REASON_RECURSION_TOO_DEEP,
+	OVS_DROP_REASON_TOO_MANY_RESUBMITS,
+	OVS_DROP_REASON_STACK_TOO_DEEP,
+	OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT,
+	OVS_DROP_REASON_RECIRCULATION_CONFLICT,
+	OVS_DROP_REASON_TOO_MANY_MPLS_LABELS,
+	OVS_DROP_REASON_INVALID_TUNNEL_METADATA,
+	OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE,
+	OVS_DROP_REASON_CONGESTION,
+	OVS_DROP_REASON_FORWARDING_DISABLED,
+	OVS_DROP_REASON_MAX,
+};
+
+/*
+ * struct ovs_action_drop - %OVS_ACTION_ATTR_DROP action argument.
+ * @drop_reason: Reason for installing drop action.
+ */
+struct ovs_action_drop {
+	enum ovs_drop_reason drop_reason;
+};
+
+/**
  * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
  *
  * @OVS_NAT_ATTR_SRC: Flag for Source NAT (mangle source address/port).
@@ -938,6 +988,7 @@ enum ovs_action_attr {
 	OVS_ACTION_ATTR_POP_NSH,      /* No argument. */
 	OVS_ACTION_ATTR_METER,        /* u32 meter number. */
 	OVS_ACTION_ATTR_CLONE,        /* Nested OVS_CLONE_ATTR_*.  */
+        OVS_ACTION_ATTR_DROP,         /* explicit drop action. */
 
 #ifndef __KERNEL__
 	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
diff --git a/lib/coverage.c b/lib/coverage.c
index 6cef826..c9acc37 100644
--- a/lib/coverage.c
+++ b/lib/coverage.c
@@ -32,9 +32,7 @@ VLOG_DEFINE_THIS_MODULE(coverage);
 static struct coverage_counter **coverage_counters = NULL;
 static size_t n_coverage_counters = 0;
 static size_t allocated_coverage_counters = 0;
-
-static struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
-
+struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
 DEFINE_STATIC_PER_THREAD_DATA(long long int, coverage_clear_time, LLONG_MIN);
 static long long int coverage_run_time = LLONG_MIN;
 
@@ -52,7 +50,7 @@ coverage_counter_register(struct coverage_counter* counter)
     if (n_coverage_counters >= allocated_coverage_counters) {
         coverage_counters = x2nrealloc(coverage_counters,
                                        &allocated_coverage_counters,
-                                       sizeof(struct coverage_counter*));
+                                       sizeof(struct coverage_counter *));
     }
     coverage_counters[n_coverage_counters++] = counter;
 }
diff --git a/lib/coverage.h b/lib/coverage.h
index dea990e..45b1dbd 100644
--- a/lib/coverage.h
+++ b/lib/coverage.h
@@ -54,7 +54,8 @@ struct coverage_counter {
     unsigned int hr[HR_AVG_LEN];
 };
 
-void coverage_counter_register(struct coverage_counter*);
+extern struct ovs_mutex coverage_mutex;
+void coverage_counter_register(struct coverage_counter *);
 
 /* Defines COUNTER.  There must be exactly one such definition at file scope
  * within a program. */
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index be529b6..8749cba 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -100,6 +100,30 @@ enum { MAX_METERS = 65536 };    /* Maximum number of meters. */
 enum { MAX_BANDS = 8 };         /* Maximum number of bands / meter. */
 enum { N_METER_LOCKS = 64 };    /* Maximum number of meters. */
 
+
+COVERAGE_DEFINE(drop_action_of_pipeline);
+COVERAGE_DEFINE(drop_action_bridge_not_found);
+COVERAGE_DEFINE(drop_action_recursion_too_deep);
+COVERAGE_DEFINE(drop_action_too_many_resubmit);
+COVERAGE_DEFINE(drop_action_stack_too_deep);
+COVERAGE_DEFINE(drop_action_no_recirculation_context);
+COVERAGE_DEFINE(drop_action_recirculation_conflict);
+COVERAGE_DEFINE(drop_action_too_many_mpls_labels);
+COVERAGE_DEFINE(drop_action_invalid_tunnel_metadata);
+COVERAGE_DEFINE(drop_action_unsupported_packet_type);
+COVERAGE_DEFINE(drop_action_congestion);
+COVERAGE_DEFINE(drop_action_forwarding_disabled);
+COVERAGE_DEFINE(dp_meter_drop);
+COVERAGE_DEFINE(dp_upcall_error_drop);
+COVERAGE_DEFINE(dp_lock_error_drop);
+COVERAGE_DEFINE(dp_userspace_action_error_drop);
+COVERAGE_DEFINE(dp_tunnel_push_error_drop);
+COVERAGE_DEFINE(dp_tunnel_pop_error_drop);
+COVERAGE_DEFINE(dp_recirc_error_drop);
+COVERAGE_DEFINE(dp_invalid_port_drop);
+COVERAGE_DEFINE(dp_invalid_tnl_port_drop);
+COVERAGE_DEFINE(rx_invalid_packet_drop);
+
 /* Protects against changes to 'dp_netdevs'. */
 static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
 
@@ -829,7 +853,6 @@ static inline bool
 pmd_perf_metrics_enabled(const struct dp_netdev_pmd_thread *pmd);
 static void queue_netdev_flow_del(struct dp_netdev_pmd_thread *pmd,
                                   struct dp_netdev_flow *flow);
-
 static void
 emc_cache_init(struct emc_cache *flow_cache)
 {
@@ -1388,6 +1411,7 @@ dpif_netdev_init(void)
     return 0;
 }
 
+
 static int
 dpif_netdev_enumerate(struct sset *all_dps,
                       const struct dpif_class *dpif_class)
@@ -5563,7 +5587,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, struct dp_packet_batch *packets_,
             band = &meter->bands[exceeded_band[j]];
             band->packet_count += 1;
             band->byte_count += dp_packet_size(packet);
-
+            COVERAGE_INC(dp_meter_drop);
             dp_packet_delete(packet);
         } else {
             /* Meter accepts packet. */
@@ -6320,6 +6344,7 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
 
         if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) {
             dp_packet_delete(packet);
+            COVERAGE_INC(rx_invalid_packet_drop);
             continue;
         }
 
@@ -6446,6 +6471,7 @@ handle_packet_upcall(struct dp_netdev_pmd_thread *pmd,
                              put_actions);
     if (OVS_UNLIKELY(error && error != ENOSPC)) {
         dp_packet_delete(packet);
+        COVERAGE_INC(dp_upcall_error_drop);
         return error;
     }
 
@@ -6577,6 +6603,7 @@ 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);
+                COVERAGE_INC(dp_lock_error_drop);
                 upcall_fail_cnt++;
             }
         }
@@ -6846,10 +6873,59 @@ dp_execute_userspace_action(struct dp_netdev_pmd_thread *pmd,
                                   actions->data, actions->size);
     } else if (should_steal) {
         dp_packet_delete(packet);
+        COVERAGE_INC(dp_userspace_action_error_drop);
     }
 }
 
 static void
+dp_update_drop_action_counter_cb(enum ovs_drop_reason drop_reason,
+                          int delta)
+    OVS_NO_THREAD_SAFETY_ANALYSIS
+{
+   switch (drop_reason) {
+   case OVS_DROP_REASON_OF_PIPELINE:
+        COVERAGE_ADD(drop_action_of_pipeline, delta);
+        break;
+   case OVS_DROP_REASON_BRIDGE_NOT_FOUND:
+        COVERAGE_ADD(drop_action_bridge_not_found, delta);
+        break;
+   case OVS_DROP_REASON_RECURSION_TOO_DEEP:
+        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
+        break;
+   case OVS_DROP_REASON_TOO_MANY_RESUBMITS:
+        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
+        break;
+   case OVS_DROP_REASON_STACK_TOO_DEEP:
+        COVERAGE_ADD(drop_action_stack_too_deep, delta);
+        break;
+   case OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT:
+        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
+        break;
+   case OVS_DROP_REASON_RECIRCULATION_CONFLICT:
+        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
+        break;
+   case OVS_DROP_REASON_TOO_MANY_MPLS_LABELS:
+        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
+        break;
+   case OVS_DROP_REASON_INVALID_TUNNEL_METADATA:
+        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
+        break;
+   case OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE:
+        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
+        break;
+   case OVS_DROP_REASON_CONGESTION:
+        COVERAGE_ADD(drop_action_congestion, delta);
+        break;
+   case OVS_DROP_REASON_FORWARDING_DISABLED:
+        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
+        break;
+   case OVS_DROP_REASON_MAX:
+   default:
+        VLOG_ERR("Invalid Drop reason type:%d",drop_reason);
+   }
+}
+
+static void
 dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
               const struct nlattr *a, bool should_steal)
     OVS_NO_THREAD_SAFETY_ANALYSIS
@@ -6860,6 +6936,7 @@ 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;
 
     switch ((enum ovs_action_attr)type) {
     case OVS_ACTION_ATTR_OUTPUT:
@@ -6901,6 +6978,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
                 dp_packet_batch_add(&p->output_pkts, packet);
             }
             return;
+        } else {
+            COVERAGE_ADD(dp_invalid_port_drop, packets_->count);
         }
         break;
 
@@ -6910,10 +6989,13 @@ 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. */
+            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
             break;
         }
         dp_packet_batch_apply_cutlen(packets_);
-        push_tnl_action(pmd, a, packets_);
+        if (push_tnl_action(pmd, a, packets_)) {
+            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
+        }
         return;
 
     case OVS_ACTION_ATTR_TUNNEL_POP:
@@ -6933,7 +7015,12 @@ 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) {
+                    COVERAGE_ADD(dp_tunnel_pop_error_drop, packet_dropped);
+                }
                 if (dp_packet_batch_is_empty(packets_)) {
                     return;
                 }
@@ -6947,7 +7034,11 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
                 dp_netdev_recirculate(pmd, packets_);
                 (*depth)--;
                 return;
+            } else {
+                COVERAGE_ADD(dp_invalid_tnl_port_drop, packets_->count);
             }
+        } else {
+            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
         }
         break;
 
@@ -6991,6 +7082,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
             fat_rwlock_unlock(&dp->upcall_rwlock);
 
             return;
+        } else {
+            COVERAGE_ADD(dp_lock_error_drop, packets_->count);
         }
         break;
 
@@ -7013,6 +7106,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
             (*depth)--;
 
             return;
+        } else {
+            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
         }
 
         VLOG_WARN("Packet dropped. Max recirculation depth exceeded.");
@@ -7167,6 +7262,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();
     }
@@ -7183,7 +7279,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_action_counter_cb);
 }
 
 struct dp_netdev_ct_dump {
diff --git a/lib/dpif.c b/lib/dpif.c
index e35f111..21f9f54 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1273,6 +1273,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();
@@ -1295,7 +1296,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;
 }
 
@@ -1879,6 +1880,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,
@@ -1976,3 +1983,4 @@ dpif_meter_del(struct dpif *dpif, ofproto_meter_id meter_id,
     }
     return error;
 }
+
diff --git a/lib/dpif.h b/lib/dpif.h
index 475d5a6..e799da8 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -888,6 +888,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 320422b..0c3bc9c 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -2395,6 +2395,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 3b6890e..c0db93f 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -25,6 +25,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "coverage.h"
 #include "dp-packet.h"
 #include "dpif.h"
 #include "netlink.h"
@@ -37,6 +38,10 @@
 #include "csum.h"
 #include "conntrack.h"
 
+COVERAGE_DEFINE(dp_sample_error_drop);
+COVERAGE_DEFINE(dp_nsh_decap_error_drop);
+
+
 /* Masked copy of an ethernet address. 'src' is already properly masked. */
 static void
 ether_addr_copy_masked(struct eth_addr *dst, const struct eth_addr src,
@@ -575,7 +580,9 @@ 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_action_counter_cb
+                     dp_update_drop_action_counter)
 {
     const struct nlattr *subactions = NULL;
     const struct nlattr *a;
@@ -589,6 +596,7 @@ 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) {
+                    COVERAGE_ADD(dp_sample_error_drop, 1);
                     dp_packet_delete(packet);
                 }
                 return;
@@ -616,13 +624,16 @@ 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_action_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_action_counter_cb
+                      dp_update_drop_action_counter)
 {
     if (!steal) {
         /* The 'actions' may modify the packet, but the modification
@@ -634,11 +645,12 @@ 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);
-    }
-    else {
+                        nl_attr_get_size(actions), dp_execute_action,
+                        dp_update_drop_action_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_action_counter);
     }
 }
 
@@ -673,6 +685,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,12 +712,15 @@ 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_action_counter_cb
+                       dp_update_drop_action_counter)
 {
     struct dp_packet *packet;
     const struct nlattr *a;
     unsigned int left;
 
+
     NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
         int type = nl_attr_type(a);
         bool last_action = (left <= NLA_ALIGN(a->nla_len));
@@ -822,7 +838,8 @@ 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_action_counter);
             }
 
             if (last_action) {
@@ -845,7 +862,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_action_counter);
             if (last_action) {
                 /* We do not need to free the packets. odp_execute_clone() has
                  * stolen them.  */
@@ -889,6 +907,7 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
                 if (pop_nsh(packet)) {
                     dp_packet_batch_refill(batch, packet, i);
                 } else {
+                    COVERAGE_INC(dp_nsh_decap_error_drop);
                     dp_packet_delete(packet);
                 }
             }
@@ -900,6 +919,17 @@ 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_action_counter) {
+                 dp_update_drop_action_counter(drop_reason, 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..a84ecbb 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_action_counter_cb) (
+                                           enum  ovs_drop_reason drop_reason,
+                                           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,7 @@ 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_action_counter_cb
+                             dp_update_drop_action_counter_cb);
 #endif
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 0491bed..9754fd5 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -131,6 +131,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:
@@ -345,6 +346,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)
@@ -1181,6 +1225,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:
@@ -2427,8 +2474,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 7da3175..69ed7b8 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -1222,6 +1222,7 @@ dpif_sflow_read_actions(const struct flow *flow,
         case OVS_ACTION_ATTR_PUSH_NSH:
         case OVS_ACTION_ATTR_POP_NSH:
         case OVS_ACTION_ATTR_UNSPEC:
+        case OVS_ACTION_ATTR_DROP:
         case __OVS_ACTION_ATTR_MAX:
         default:
             break;
diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
index dc30824..b1ce2ec 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -1154,7 +1154,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)
 {
@@ -1244,6 +1244,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
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 839fddd..32c4edf 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);
 
@@ -5921,6 +5957,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)
 {
@@ -7383,6 +7430,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;
@@ -7415,6 +7466,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) {
@@ -7522,6 +7574,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 0a5a528..bb3f9ff 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 14fe6fc..609226a 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -827,6 +827,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.
@@ -1397,6 +1403,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);
@@ -5776,6 +5784,7 @@ ofproto_unixctl_dpif_set_dp_features(struct unixctl_conn *conn,
     ds_destroy(&ds);
 }
 
+
 static void
 ofproto_unixctl_init(void)
 {
@@ -5809,7 +5818,7 @@ ofproto_unixctl_init(void)
     unixctl_command_register("dpif/set-dp-features", "bridge", 1, 3 ,
                              ofproto_unixctl_dpif_set_dp_features, NULL);
 }
-

+
 static odp_port_t
 ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
 {
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 92d56b2..22b4722 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -108,7 +108,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
 
 EXTRA_DIST += $(FUZZ_REGRESSION_TESTS)
 FUZZ_REGRESSION_TESTS = \
diff --git a/tests/bundle.at b/tests/bundle.at
index 0a4eadc..33fe249 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 6915d43..08349f6 100644
--- a/tests/dpif-netdev.at
+++ b/tests/dpif-netdev.at
@@ -1,4 +1,4 @@
-AT_BANNER([dpif-netdev])
+T_BANNER([dpif-netdev])
 
 m4_divert_push([PREPARE_TESTS])
 [
@@ -281,6 +281,7 @@ type=drop rate=1 burst_size=2
 ])
 
 ovs-appctl time/warp 5000
+sleep 10
 AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
 AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
 AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
@@ -291,7 +292,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
 AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
 AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
 AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
-sleep 1  # wait for forwarders process packets
+# wait for forwarders process packets
 
 # Meter 1 is measuring packets, allowing one packet per second with
 # bursts of one packet, so 4 out of 5 packets should hit the drop
@@ -320,7 +321,8 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
 AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
 AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
 AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
-sleep 1  # wait for forwarders process packets
+
+sleep 10  # wait for forwarders process packets
 
 # Meter 1 is measuring packets, allowing one packet per second with
 # bursts of one packet, so all 5 of the new packets should hit the drop
@@ -337,6 +339,15 @@ meter:2 flow_count:1 packet_in_count:10 byte_in_count:600 duration:0.0s bands:
 0: packet_count:5 byte_count:300
 ])
 
+ovs-appctl time/warp 5000
+sleep 10
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "dp_meter_drop" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+14
+])
+
 AT_CHECK([cat ovs-vswitchd.log | filter_flow_install | strip_xout_keep_actions], [0], [dnl
 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:meter(0),7
 recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:8
diff --git a/tests/drop-stats.at b/tests/drop-stats.at
new file mode 100644
index 0000000..be8b8ae
--- /dev/null
+++ b/tests/drop-stats.at
@@ -0,0 +1,197 @@
+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
+])
+
+sleep 1
+
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+3
+])
+
+
+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])
+
+sleep 1
+
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+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])
+
+sleep 1
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "drop_action_recursion_too_deep" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+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)'
+
+sleep 1
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "drop_action_too_many_resubmit" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+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)'
+
+sleep 1
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "drop_action_stack_too_deep" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+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])
+
+sleep 1
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "drop_action_too_many_mpls_labels" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+1
+])
+
+
+OVS_VSWITCHD_STOP(["/|WARN|/d"])
+AT_CLEANUP
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index ded2ef0..298aa9a 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -3496,51 +3496,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" \
@@ -3555,8 +3555,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" \
@@ -3573,16 +3573,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"
@@ -4322,11 +4322,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
@@ -4404,8 +4404,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
@@ -5751,7 +5751,7 @@ bridge("br0")
 
 Final flow: <cleared>
 Megaflow: <cleared>
-Datapath actions: drop
+Datapath actions: drop:pipeline-drop
 ])
 
 dnl Now, try again without megaflows:
@@ -5772,7 +5772,7 @@ bridge("br0")
 
 Final flow: <cleared>
 Megaflow: <cleared>
-Datapath actions: drop
+Datapath actions: drop:pipeline-drop
 ])
 
 OVS_VSWITCHD_STOP
@@ -7066,7 +7066,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"])
@@ -7158,7 +7158,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
@@ -7832,21 +7832,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
@@ -7869,7 +7869,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
@@ -8617,11 +8617,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])
@@ -9348,7 +9348,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
@@ -9377,16 +9377,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
@@ -9711,7 +9711,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)
@@ -10398,7 +10398,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])
@@ -10490,7 +10490,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 6ae3470..cd19d41 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -2999,7 +2999,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..7c1b551 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,14 @@ 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 coverage/show | grep "drop_action_unsupported_packet_type" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+2
 ])
 
 # Encap(ethernet) on Ethernet frame -> should be droped
@@ -587,7 +594,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 +616,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 +777,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 b840dbf..922ba48 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -82,3 +82,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..385c114 100644
--- a/tests/tunnel-push-pop.at
+++ b/tests/tunnel-push-pop.at
@@ -447,6 +447,27 @@ 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 netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+
+ovs-appctl time/warp 1200
+
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "dp_tunnel_pop_error_drop" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+1
+])
+
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+ovs-appctl time/warp 1200
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "drop_action_congestion" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+1
+])
+
 dnl Check GREL3 only accepts non-fragmented packets?
 AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
 
@@ -455,7 +476,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 +531,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 55fb1d3..3646c06 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 netdev-dummy/receive p2 'aa55aa550001f8bc124434b6080045000054ba20000040018486010103580101037001004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+sleep 2
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "dp_invalid_port_drop" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+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
Ben Pfaff Jan. 18, 2019, 5:05 a.m. UTC | #2
Yes.  Thank you.  I'll review it tomorrow.

On Fri, Jan 18, 2019 at 04:54:51AM +0000, Anju Thomas wrote:
> Hi Ben,
> Were you able to apply this patch successfully?
> Regards
> Anju
> 
> -----Original Message-----
> From: Anju Thomas [mailto:anju.thomas@ericsson.com] 
> Sent: Thursday, January 17, 2019 10:19 AM
> To: dev@openvswitch.org
> Cc: Anju Thomas <anju.thomas@ericsson.com>; Rohith Basavaraja <rohith.basavaraja@gmail.com>; Keshav Gupta <keshugupta1@gmail.com>
> Subject: [PATCH v6] Improved Packet Drop Statistics in OVS
> 
> 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. Display these drops in ovs-appctl coverage/show
>     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.
> 
>      The idea is to use the coverage infrastructure to maintain the drops
> 
>     $ ovs-appctl dpctl/dump-flows netdev at ovs-netdev
>     flow-dump from pmd on cpu core: 0
>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:12, bytes:1176, used:0.884s, actions:drop:recursion too deep
> 
>     $ ovs-appctl dpif/dump-flows br-int
>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:25, bytes:2450, used:5.008s, actions:drop:recursion too deep
>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0806), packets:7, bytes:294, used:0.009s, actions:drop:recursion too deep
> 
>    In subsequent commits, we are planning to see if we can use this infrastructure to create a
>    wrapper to clear and display counters as well.
> 
> Co-authored-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Co-authored-by: Keshav Gupta <keshugupta1@gmail.com>
> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Signed-off-by: Keshav Gupta <keshugupta1@gmail.com>
> ---
>  datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
>  lib/coverage.c                                    |   6 +-
>  lib/coverage.h                                    |   3 +-
>  lib/dpif-netdev.c                                 | 105 +++++++++++-
>  lib/dpif.c                                        |  10 +-
>  lib/dpif.h                                        |   3 +
>  lib/netdev-dpdk.c                                 |   4 +
>  lib/odp-execute.c                                 |  50 ++++--
>  lib/odp-execute.h                                 |  10 +-
>  lib/odp-util.c                                    |  54 +++++-
>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>  ofproto/ofproto-dpif-upcall.c                     |   3 +-
>  ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
>  ofproto/ofproto-dpif-xlate.h                      |   4 +
>  ofproto/ofproto-dpif.c                            |  11 +-
>  ofproto/ofproto-dpif.h                            |   8 +-
>  tests/automake.mk                                 |   3 +-
>  tests/bundle.at                                   |   2 +-
>  tests/classifier.at                               |  10 +-
>  tests/dpif-netdev.at                              |  17 +-
>  tests/drop-stats.at                               | 197 ++++++++++++++++++++++
>  tests/ofproto-dpif.at                             | 118 ++++++-------
>  tests/ovs-ofctl.at                                |   2 +-
>  tests/packet-type-aware.at                        |  17 +-
>  tests/testsuite.at                                |   1 +
>  tests/tunnel-push-pop.at                          |  26 ++-
>  tests/tunnel.at                                   |  21 ++-
>  28 files changed, 695 insertions(+), 107 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 9b087f1..b66b46f 100644
> --- a/datapath/linux/compat/include/linux/openvswitch.h
> +++ b/datapath/linux/compat/include/linux/openvswitch.h
> @@ -819,6 +819,56 @@ struct ovs_action_push_eth {
>  };
>  
>  /**
> + * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP.
> + * @OVS_DROP_REASON_OF_PIPELINE: Explicit drop action in the pipeline.
> + * @OVS_DROP_REASON_BRIDGE_NOT_FOUND: Xlation error generated due to
> + * unable to determine bridge.
> + * @OVS_DROP_REASON_RECURSION_TOO_DEEP: Xlation error generated due to
> + * recursion reached maximum depth.
> + * @OVS_DROP_REASON_TOO_MANY_RESUBMITS: Xlation error generated due to
> + * too many resubmits.
> + * @OVS_DROP_REASON_STACK_TOO_DEEP: Xlation error generated due to stack
> + * too deep.
> + * @OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT: Xlation error generated
> + * due to no recirculation context.
> + * @OVS_DROP_REASON_RECIRCULATION_CONFLICT: Xlation error generated due to
> + * conflict in recirculation context.
> + * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to
> + * too many mpls labels.
> + * @OVS_DROP_REASON_INVALID_TUNNEL_METADATA: Xlation error generated due to
> + * invalid tunnel metadata.
> + * @OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE: Xlation error generated due to
> + * unsupported packet type.
> + * @OVS_DROP_REASON_CONGESTION: Xlation error generated due to ecn mismatch
> + * during tunnel decapsulation.
> + * @OVS_DROP_REASON_FORWARDING_DISABLED: Xlation error generated due to
> + * forwarding is disabled.
> + */
> +enum ovs_drop_reason {
> +	OVS_DROP_REASON_OF_PIPELINE = 0,
> +	OVS_DROP_REASON_BRIDGE_NOT_FOUND,
> +	OVS_DROP_REASON_RECURSION_TOO_DEEP,
> +	OVS_DROP_REASON_TOO_MANY_RESUBMITS,
> +	OVS_DROP_REASON_STACK_TOO_DEEP,
> +	OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT,
> +	OVS_DROP_REASON_RECIRCULATION_CONFLICT,
> +	OVS_DROP_REASON_TOO_MANY_MPLS_LABELS,
> +	OVS_DROP_REASON_INVALID_TUNNEL_METADATA,
> +	OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE,
> +	OVS_DROP_REASON_CONGESTION,
> +	OVS_DROP_REASON_FORWARDING_DISABLED,
> +	OVS_DROP_REASON_MAX,
> +};
> +
> +/*
> + * struct ovs_action_drop - %OVS_ACTION_ATTR_DROP action argument.
> + * @drop_reason: Reason for installing drop action.
> + */
> +struct ovs_action_drop {
> +	enum ovs_drop_reason drop_reason;
> +};
> +
> +/**
>   * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
>   *
>   * @OVS_NAT_ATTR_SRC: Flag for Source NAT (mangle source address/port).
> @@ -938,6 +988,7 @@ enum ovs_action_attr {
>  	OVS_ACTION_ATTR_POP_NSH,      /* No argument. */
>  	OVS_ACTION_ATTR_METER,        /* u32 meter number. */
>  	OVS_ACTION_ATTR_CLONE,        /* Nested OVS_CLONE_ATTR_*.  */
> +        OVS_ACTION_ATTR_DROP,         /* explicit drop action. */
>  
>  #ifndef __KERNEL__
>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
> diff --git a/lib/coverage.c b/lib/coverage.c
> index 6cef826..c9acc37 100644
> --- a/lib/coverage.c
> +++ b/lib/coverage.c
> @@ -32,9 +32,7 @@ VLOG_DEFINE_THIS_MODULE(coverage);
>  static struct coverage_counter **coverage_counters = NULL;
>  static size_t n_coverage_counters = 0;
>  static size_t allocated_coverage_counters = 0;
> -
> -static struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
> -
> +struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
>  DEFINE_STATIC_PER_THREAD_DATA(long long int, coverage_clear_time, LLONG_MIN);
>  static long long int coverage_run_time = LLONG_MIN;
>  
> @@ -52,7 +50,7 @@ coverage_counter_register(struct coverage_counter* counter)
>      if (n_coverage_counters >= allocated_coverage_counters) {
>          coverage_counters = x2nrealloc(coverage_counters,
>                                         &allocated_coverage_counters,
> -                                       sizeof(struct coverage_counter*));
> +                                       sizeof(struct coverage_counter *));
>      }
>      coverage_counters[n_coverage_counters++] = counter;
>  }
> diff --git a/lib/coverage.h b/lib/coverage.h
> index dea990e..45b1dbd 100644
> --- a/lib/coverage.h
> +++ b/lib/coverage.h
> @@ -54,7 +54,8 @@ struct coverage_counter {
>      unsigned int hr[HR_AVG_LEN];
>  };
>  
> -void coverage_counter_register(struct coverage_counter*);
> +extern struct ovs_mutex coverage_mutex;
> +void coverage_counter_register(struct coverage_counter *);
>  
>  /* Defines COUNTER.  There must be exactly one such definition at file scope
>   * within a program. */
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index be529b6..8749cba 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -100,6 +100,30 @@ enum { MAX_METERS = 65536 };    /* Maximum number of meters. */
>  enum { MAX_BANDS = 8 };         /* Maximum number of bands / meter. */
>  enum { N_METER_LOCKS = 64 };    /* Maximum number of meters. */
>  
> +
> +COVERAGE_DEFINE(drop_action_of_pipeline);
> +COVERAGE_DEFINE(drop_action_bridge_not_found);
> +COVERAGE_DEFINE(drop_action_recursion_too_deep);
> +COVERAGE_DEFINE(drop_action_too_many_resubmit);
> +COVERAGE_DEFINE(drop_action_stack_too_deep);
> +COVERAGE_DEFINE(drop_action_no_recirculation_context);
> +COVERAGE_DEFINE(drop_action_recirculation_conflict);
> +COVERAGE_DEFINE(drop_action_too_many_mpls_labels);
> +COVERAGE_DEFINE(drop_action_invalid_tunnel_metadata);
> +COVERAGE_DEFINE(drop_action_unsupported_packet_type);
> +COVERAGE_DEFINE(drop_action_congestion);
> +COVERAGE_DEFINE(drop_action_forwarding_disabled);
> +COVERAGE_DEFINE(dp_meter_drop);
> +COVERAGE_DEFINE(dp_upcall_error_drop);
> +COVERAGE_DEFINE(dp_lock_error_drop);
> +COVERAGE_DEFINE(dp_userspace_action_error_drop);
> +COVERAGE_DEFINE(dp_tunnel_push_error_drop);
> +COVERAGE_DEFINE(dp_tunnel_pop_error_drop);
> +COVERAGE_DEFINE(dp_recirc_error_drop);
> +COVERAGE_DEFINE(dp_invalid_port_drop);
> +COVERAGE_DEFINE(dp_invalid_tnl_port_drop);
> +COVERAGE_DEFINE(rx_invalid_packet_drop);
> +
>  /* Protects against changes to 'dp_netdevs'. */
>  static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>  
> @@ -829,7 +853,6 @@ static inline bool
>  pmd_perf_metrics_enabled(const struct dp_netdev_pmd_thread *pmd);
>  static void queue_netdev_flow_del(struct dp_netdev_pmd_thread *pmd,
>                                    struct dp_netdev_flow *flow);
> -
>  static void
>  emc_cache_init(struct emc_cache *flow_cache)
>  {
> @@ -1388,6 +1411,7 @@ dpif_netdev_init(void)
>      return 0;
>  }
>  
> +
>  static int
>  dpif_netdev_enumerate(struct sset *all_dps,
>                        const struct dpif_class *dpif_class)
> @@ -5563,7 +5587,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, struct dp_packet_batch *packets_,
>              band = &meter->bands[exceeded_band[j]];
>              band->packet_count += 1;
>              band->byte_count += dp_packet_size(packet);
> -
> +            COVERAGE_INC(dp_meter_drop);
>              dp_packet_delete(packet);
>          } else {
>              /* Meter accepts packet. */
> @@ -6320,6 +6344,7 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
>  
>          if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) {
>              dp_packet_delete(packet);
> +            COVERAGE_INC(rx_invalid_packet_drop);
>              continue;
>          }
>  
> @@ -6446,6 +6471,7 @@ handle_packet_upcall(struct dp_netdev_pmd_thread *pmd,
>                               put_actions);
>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>          dp_packet_delete(packet);
> +        COVERAGE_INC(dp_upcall_error_drop);
>          return error;
>      }
>  
> @@ -6577,6 +6603,7 @@ 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);
> +                COVERAGE_INC(dp_lock_error_drop);
>                  upcall_fail_cnt++;
>              }
>          }
> @@ -6846,10 +6873,59 @@ dp_execute_userspace_action(struct dp_netdev_pmd_thread *pmd,
>                                    actions->data, actions->size);
>      } else if (should_steal) {
>          dp_packet_delete(packet);
> +        COVERAGE_INC(dp_userspace_action_error_drop);
>      }
>  }
>  
>  static void
> +dp_update_drop_action_counter_cb(enum ovs_drop_reason drop_reason,
> +                          int delta)
> +    OVS_NO_THREAD_SAFETY_ANALYSIS
> +{
> +   switch (drop_reason) {
> +   case OVS_DROP_REASON_OF_PIPELINE:
> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
> +        break;
> +   case OVS_DROP_REASON_BRIDGE_NOT_FOUND:
> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
> +        break;
> +   case OVS_DROP_REASON_RECURSION_TOO_DEEP:
> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
> +        break;
> +   case OVS_DROP_REASON_TOO_MANY_RESUBMITS:
> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
> +        break;
> +   case OVS_DROP_REASON_STACK_TOO_DEEP:
> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
> +        break;
> +   case OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT:
> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
> +        break;
> +   case OVS_DROP_REASON_RECIRCULATION_CONFLICT:
> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
> +        break;
> +   case OVS_DROP_REASON_TOO_MANY_MPLS_LABELS:
> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
> +        break;
> +   case OVS_DROP_REASON_INVALID_TUNNEL_METADATA:
> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
> +        break;
> +   case OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE:
> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
> +        break;
> +   case OVS_DROP_REASON_CONGESTION:
> +        COVERAGE_ADD(drop_action_congestion, delta);
> +        break;
> +   case OVS_DROP_REASON_FORWARDING_DISABLED:
> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
> +        break;
> +   case OVS_DROP_REASON_MAX:
> +   default:
> +        VLOG_ERR("Invalid Drop reason type:%d",drop_reason);
> +   }
> +}
> +
> +static void
>  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                const struct nlattr *a, bool should_steal)
>      OVS_NO_THREAD_SAFETY_ANALYSIS
> @@ -6860,6 +6936,7 @@ 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;
>  
>      switch ((enum ovs_action_attr)type) {
>      case OVS_ACTION_ATTR_OUTPUT:
> @@ -6901,6 +6978,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                  dp_packet_batch_add(&p->output_pkts, packet);
>              }
>              return;
> +        } else {
> +            COVERAGE_ADD(dp_invalid_port_drop, packets_->count);
>          }
>          break;
>  
> @@ -6910,10 +6989,13 @@ 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. */
> +            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
>              break;
>          }
>          dp_packet_batch_apply_cutlen(packets_);
> -        push_tnl_action(pmd, a, packets_);
> +        if (push_tnl_action(pmd, a, packets_)) {
> +            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
> +        }
>          return;
>  
>      case OVS_ACTION_ATTR_TUNNEL_POP:
> @@ -6933,7 +7015,12 @@ 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) {
> +                    COVERAGE_ADD(dp_tunnel_pop_error_drop, packet_dropped);
> +                }
>                  if (dp_packet_batch_is_empty(packets_)) {
>                      return;
>                  }
> @@ -6947,7 +7034,11 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                  dp_netdev_recirculate(pmd, packets_);
>                  (*depth)--;
>                  return;
> +            } else {
> +                COVERAGE_ADD(dp_invalid_tnl_port_drop, packets_->count);
>              }
> +        } else {
> +            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
>          }
>          break;
>  
> @@ -6991,6 +7082,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>              fat_rwlock_unlock(&dp->upcall_rwlock);
>  
>              return;
> +        } else {
> +            COVERAGE_ADD(dp_lock_error_drop, packets_->count);
>          }
>          break;
>  
> @@ -7013,6 +7106,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>              (*depth)--;
>  
>              return;
> +        } else {
> +            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
>          }
>  
>          VLOG_WARN("Packet dropped. Max recirculation depth exceeded.");
> @@ -7167,6 +7262,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();
>      }
> @@ -7183,7 +7279,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_action_counter_cb);
>  }
>  
>  struct dp_netdev_ct_dump {
> diff --git a/lib/dpif.c b/lib/dpif.c
> index e35f111..21f9f54 100644
> --- a/lib/dpif.c
> +++ b/lib/dpif.c
> @@ -1273,6 +1273,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();
> @@ -1295,7 +1296,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;
>  }
>  
> @@ -1879,6 +1880,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,
> @@ -1976,3 +1983,4 @@ dpif_meter_del(struct dpif *dpif, ofproto_meter_id meter_id,
>      }
>      return error;
>  }
> +
> diff --git a/lib/dpif.h b/lib/dpif.h
> index 475d5a6..e799da8 100644
> --- a/lib/dpif.h
> +++ b/lib/dpif.h
> @@ -888,6 +888,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 320422b..0c3bc9c 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -2395,6 +2395,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 3b6890e..c0db93f 100644
> --- a/lib/odp-execute.c
> +++ b/lib/odp-execute.c
> @@ -25,6 +25,7 @@
>  #include <stdlib.h>
>  #include <string.h>
>  
> +#include "coverage.h"
>  #include "dp-packet.h"
>  #include "dpif.h"
>  #include "netlink.h"
> @@ -37,6 +38,10 @@
>  #include "csum.h"
>  #include "conntrack.h"
>  
> +COVERAGE_DEFINE(dp_sample_error_drop);
> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
> +
> +
>  /* Masked copy of an ethernet address. 'src' is already properly masked. */
>  static void
>  ether_addr_copy_masked(struct eth_addr *dst, const struct eth_addr src,
> @@ -575,7 +580,9 @@ 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_action_counter_cb
> +                     dp_update_drop_action_counter)
>  {
>      const struct nlattr *subactions = NULL;
>      const struct nlattr *a;
> @@ -589,6 +596,7 @@ 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) {
> +                    COVERAGE_ADD(dp_sample_error_drop, 1);
>                      dp_packet_delete(packet);
>                  }
>                  return;
> @@ -616,13 +624,16 @@ 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_action_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_action_counter_cb
> +                      dp_update_drop_action_counter)
>  {
>      if (!steal) {
>          /* The 'actions' may modify the packet, but the modification
> @@ -634,11 +645,12 @@ 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);
> -    }
> -    else {
> +                        nl_attr_get_size(actions), dp_execute_action,
> +                        dp_update_drop_action_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_action_counter);
>      }
>  }
>  
> @@ -673,6 +685,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,12 +712,15 @@ 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_action_counter_cb
> +                       dp_update_drop_action_counter)
>  {
>      struct dp_packet *packet;
>      const struct nlattr *a;
>      unsigned int left;
>  
> +
>      NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
>          int type = nl_attr_type(a);
>          bool last_action = (left <= NLA_ALIGN(a->nla_len));
> @@ -822,7 +838,8 @@ 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_action_counter);
>              }
>  
>              if (last_action) {
> @@ -845,7 +862,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_action_counter);
>              if (last_action) {
>                  /* We do not need to free the packets. odp_execute_clone() has
>                   * stolen them.  */
> @@ -889,6 +907,7 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
>                  if (pop_nsh(packet)) {
>                      dp_packet_batch_refill(batch, packet, i);
>                  } else {
> +                    COVERAGE_INC(dp_nsh_decap_error_drop);
>                      dp_packet_delete(packet);
>                  }
>              }
> @@ -900,6 +919,17 @@ 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_action_counter) {
> +                 dp_update_drop_action_counter(drop_reason, 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..a84ecbb 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_action_counter_cb) (
> +                                           enum  ovs_drop_reason drop_reason,
> +                                           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,7 @@ 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_action_counter_cb
> +                             dp_update_drop_action_counter_cb);
>  #endif
> diff --git a/lib/odp-util.c b/lib/odp-util.c
> index 0491bed..9754fd5 100644
> --- a/lib/odp-util.c
> +++ b/lib/odp-util.c
> @@ -131,6 +131,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:
> @@ -345,6 +346,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)
> @@ -1181,6 +1225,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:
> @@ -2427,8 +2474,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 7da3175..69ed7b8 100644
> --- a/ofproto/ofproto-dpif-sflow.c
> +++ b/ofproto/ofproto-dpif-sflow.c
> @@ -1222,6 +1222,7 @@ dpif_sflow_read_actions(const struct flow *flow,
>          case OVS_ACTION_ATTR_PUSH_NSH:
>          case OVS_ACTION_ATTR_POP_NSH:
>          case OVS_ACTION_ATTR_UNSPEC:
> +        case OVS_ACTION_ATTR_DROP:
>          case __OVS_ACTION_ATTR_MAX:
>          default:
>              break;
> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
> index dc30824..b1ce2ec 100644
> --- a/ofproto/ofproto-dpif-upcall.c
> +++ b/ofproto/ofproto-dpif-upcall.c
> @@ -1154,7 +1154,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)
>  {
> @@ -1244,6 +1244,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
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index 839fddd..32c4edf 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);
>  
> @@ -5921,6 +5957,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)
>  {
> @@ -7383,6 +7430,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;
> @@ -7415,6 +7466,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) {
> @@ -7522,6 +7574,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 0a5a528..bb3f9ff 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 14fe6fc..609226a 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -827,6 +827,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.
> @@ -1397,6 +1403,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);
> @@ -5776,6 +5784,7 @@ ofproto_unixctl_dpif_set_dp_features(struct unixctl_conn *conn,
>      ds_destroy(&ds);
>  }
>  
> +
>  static void
>  ofproto_unixctl_init(void)
>  {
> @@ -5809,7 +5818,7 @@ ofproto_unixctl_init(void)
>      unixctl_command_register("dpif/set-dp-features", "bridge", 1, 3 ,
>                               ofproto_unixctl_dpif_set_dp_features, NULL);
>  }
> -
> 
> +
>  static odp_port_t
>  ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
>  {
> 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 92d56b2..22b4722 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -108,7 +108,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
>  
>  EXTRA_DIST += $(FUZZ_REGRESSION_TESTS)
>  FUZZ_REGRESSION_TESTS = \
> diff --git a/tests/bundle.at b/tests/bundle.at
> index 0a4eadc..33fe249 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 6915d43..08349f6 100644
> --- a/tests/dpif-netdev.at
> +++ b/tests/dpif-netdev.at
> @@ -1,4 +1,4 @@
> -AT_BANNER([dpif-netdev])
> +T_BANNER([dpif-netdev])
>  
>  m4_divert_push([PREPARE_TESTS])
>  [
> @@ -281,6 +281,7 @@ type=drop rate=1 burst_size=2
>  ])
>  
>  ovs-appctl time/warp 5000
> +sleep 10
>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
> @@ -291,7 +292,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
> -sleep 1  # wait for forwarders process packets
> +# wait for forwarders process packets
>  
>  # Meter 1 is measuring packets, allowing one packet per second with
>  # bursts of one packet, so 4 out of 5 packets should hit the drop
> @@ -320,7 +321,8 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
> -sleep 1  # wait for forwarders process packets
> +
> +sleep 10  # wait for forwarders process packets
>  
>  # Meter 1 is measuring packets, allowing one packet per second with
>  # bursts of one packet, so all 5 of the new packets should hit the drop
> @@ -337,6 +339,15 @@ meter:2 flow_count:1 packet_in_count:10 byte_in_count:600 duration:0.0s bands:
>  0: packet_count:5 byte_count:300
>  ])
>  
> +ovs-appctl time/warp 5000
> +sleep 10
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "dp_meter_drop" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +14
> +])
> +
>  AT_CHECK([cat ovs-vswitchd.log | filter_flow_install | strip_xout_keep_actions], [0], [dnl
>  recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:meter(0),7
>  recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:8
> diff --git a/tests/drop-stats.at b/tests/drop-stats.at
> new file mode 100644
> index 0000000..be8b8ae
> --- /dev/null
> +++ b/tests/drop-stats.at
> @@ -0,0 +1,197 @@
> +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
> +])
> +
> +sleep 1
> +
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +3
> +])
> +
> +
> +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])
> +
> +sleep 1
> +
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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])
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_recursion_too_deep" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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)'
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_too_many_resubmit" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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)'
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_stack_too_deep" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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])
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_too_many_mpls_labels" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
> +
> +OVS_VSWITCHD_STOP(["/|WARN|/d"])
> +AT_CLEANUP
> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> index ded2ef0..298aa9a 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -3496,51 +3496,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" \
> @@ -3555,8 +3555,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" \
> @@ -3573,16 +3573,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"
> @@ -4322,11 +4322,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
> @@ -4404,8 +4404,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
> @@ -5751,7 +5751,7 @@ bridge("br0")
>  
>  Final flow: <cleared>
>  Megaflow: <cleared>
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>  ])
>  
>  dnl Now, try again without megaflows:
> @@ -5772,7 +5772,7 @@ bridge("br0")
>  
>  Final flow: <cleared>
>  Megaflow: <cleared>
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>  ])
>  
>  OVS_VSWITCHD_STOP
> @@ -7066,7 +7066,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"])
> @@ -7158,7 +7158,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
> @@ -7832,21 +7832,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
> @@ -7869,7 +7869,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
> @@ -8617,11 +8617,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])
> @@ -9348,7 +9348,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
> @@ -9377,16 +9377,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
> @@ -9711,7 +9711,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)
> @@ -10398,7 +10398,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])
> @@ -10490,7 +10490,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 6ae3470..cd19d41 100644
> --- a/tests/ovs-ofctl.at
> +++ b/tests/ovs-ofctl.at
> @@ -2999,7 +2999,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..7c1b551 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,14 @@ 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 coverage/show | grep "drop_action_unsupported_packet_type" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +2
>  ])
>  
>  # Encap(ethernet) on Ethernet frame -> should be droped
> @@ -587,7 +594,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 +616,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 +777,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 b840dbf..922ba48 100644
> --- a/tests/testsuite.at
> +++ b/tests/testsuite.at
> @@ -82,3 +82,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..385c114 100644
> --- a/tests/tunnel-push-pop.at
> +++ b/tests/tunnel-push-pop.at
> @@ -447,6 +447,27 @@ 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 netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +
> +ovs-appctl time/warp 1200
> +
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "dp_tunnel_pop_error_drop" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
> +
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +ovs-appctl time/warp 1200
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_congestion" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
>  dnl Check GREL3 only accepts non-fragmented packets?
>  AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>  
> @@ -455,7 +476,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 +531,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 55fb1d3..3646c06 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 netdev-dummy/receive p2 'aa55aa550001f8bc124434b6080045000054ba20000040018486010103580101037001004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +sleep 2
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "dp_invalid_port_drop" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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
> -- 
> 1.9.1
>
Anju Thomas Jan. 18, 2019, 6:07 a.m. UTC | #3
Thank you Ben,

Regards
Anju
-----Original Message-----
From: Ben Pfaff [mailto:blp@ovn.org] 
Sent: Friday, January 18, 2019 10:35 AM
To: Anju Thomas <anju.thomas@ericsson.com>
Cc: dev@openvswitch.org; Rohith Basavaraja <rohith.basavaraja@gmail.com>; Keshav Gupta <keshugupta1@gmail.com>
Subject: Re: [PATCH v6] Improved Packet Drop Statistics in OVS

Yes.  Thank you.  I'll review it tomorrow.

On Fri, Jan 18, 2019 at 04:54:51AM +0000, Anju Thomas wrote:
> Hi Ben,
> Were you able to apply this patch successfully?
> Regards
> Anju
> 
> -----Original Message-----
> From: Anju Thomas [mailto:anju.thomas@ericsson.com] 
> Sent: Thursday, January 17, 2019 10:19 AM
> To: dev@openvswitch.org
> Cc: Anju Thomas <anju.thomas@ericsson.com>; Rohith Basavaraja <rohith.basavaraja@gmail.com>; Keshav Gupta <keshugupta1@gmail.com>
> Subject: [PATCH v6] Improved Packet Drop Statistics in OVS
> 
> 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. Display these drops in ovs-appctl coverage/show
>     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.
> 
>      The idea is to use the coverage infrastructure to maintain the drops
> 
>     $ ovs-appctl dpctl/dump-flows netdev at ovs-netdev
>     flow-dump from pmd on cpu core: 0
>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:12, bytes:1176, used:0.884s, actions:drop:recursion too deep
> 
>     $ ovs-appctl dpif/dump-flows br-int
>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:25, bytes:2450, used:5.008s, actions:drop:recursion too deep
>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0806), packets:7, bytes:294, used:0.009s, actions:drop:recursion too deep
> 
>    In subsequent commits, we are planning to see if we can use this infrastructure to create a
>    wrapper to clear and display counters as well.
> 
> Co-authored-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Co-authored-by: Keshav Gupta <keshugupta1@gmail.com>
> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Signed-off-by: Keshav Gupta <keshugupta1@gmail.com>
> ---
>  datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
>  lib/coverage.c                                    |   6 +-
>  lib/coverage.h                                    |   3 +-
>  lib/dpif-netdev.c                                 | 105 +++++++++++-
>  lib/dpif.c                                        |  10 +-
>  lib/dpif.h                                        |   3 +
>  lib/netdev-dpdk.c                                 |   4 +
>  lib/odp-execute.c                                 |  50 ++++--
>  lib/odp-execute.h                                 |  10 +-
>  lib/odp-util.c                                    |  54 +++++-
>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>  ofproto/ofproto-dpif-upcall.c                     |   3 +-
>  ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
>  ofproto/ofproto-dpif-xlate.h                      |   4 +
>  ofproto/ofproto-dpif.c                            |  11 +-
>  ofproto/ofproto-dpif.h                            |   8 +-
>  tests/automake.mk                                 |   3 +-
>  tests/bundle.at                                   |   2 +-
>  tests/classifier.at                               |  10 +-
>  tests/dpif-netdev.at                              |  17 +-
>  tests/drop-stats.at                               | 197 ++++++++++++++++++++++
>  tests/ofproto-dpif.at                             | 118 ++++++-------
>  tests/ovs-ofctl.at                                |   2 +-
>  tests/packet-type-aware.at                        |  17 +-
>  tests/testsuite.at                                |   1 +
>  tests/tunnel-push-pop.at                          |  26 ++-
>  tests/tunnel.at                                   |  21 ++-
>  28 files changed, 695 insertions(+), 107 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 9b087f1..b66b46f 100644
> --- a/datapath/linux/compat/include/linux/openvswitch.h
> +++ b/datapath/linux/compat/include/linux/openvswitch.h
> @@ -819,6 +819,56 @@ struct ovs_action_push_eth {
>  };
>  
>  /**
> + * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP.
> + * @OVS_DROP_REASON_OF_PIPELINE: Explicit drop action in the pipeline.
> + * @OVS_DROP_REASON_BRIDGE_NOT_FOUND: Xlation error generated due to
> + * unable to determine bridge.
> + * @OVS_DROP_REASON_RECURSION_TOO_DEEP: Xlation error generated due to
> + * recursion reached maximum depth.
> + * @OVS_DROP_REASON_TOO_MANY_RESUBMITS: Xlation error generated due to
> + * too many resubmits.
> + * @OVS_DROP_REASON_STACK_TOO_DEEP: Xlation error generated due to stack
> + * too deep.
> + * @OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT: Xlation error generated
> + * due to no recirculation context.
> + * @OVS_DROP_REASON_RECIRCULATION_CONFLICT: Xlation error generated due to
> + * conflict in recirculation context.
> + * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to
> + * too many mpls labels.
> + * @OVS_DROP_REASON_INVALID_TUNNEL_METADATA: Xlation error generated due to
> + * invalid tunnel metadata.
> + * @OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE: Xlation error generated due to
> + * unsupported packet type.
> + * @OVS_DROP_REASON_CONGESTION: Xlation error generated due to ecn mismatch
> + * during tunnel decapsulation.
> + * @OVS_DROP_REASON_FORWARDING_DISABLED: Xlation error generated due to
> + * forwarding is disabled.
> + */
> +enum ovs_drop_reason {
> +	OVS_DROP_REASON_OF_PIPELINE = 0,
> +	OVS_DROP_REASON_BRIDGE_NOT_FOUND,
> +	OVS_DROP_REASON_RECURSION_TOO_DEEP,
> +	OVS_DROP_REASON_TOO_MANY_RESUBMITS,
> +	OVS_DROP_REASON_STACK_TOO_DEEP,
> +	OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT,
> +	OVS_DROP_REASON_RECIRCULATION_CONFLICT,
> +	OVS_DROP_REASON_TOO_MANY_MPLS_LABELS,
> +	OVS_DROP_REASON_INVALID_TUNNEL_METADATA,
> +	OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE,
> +	OVS_DROP_REASON_CONGESTION,
> +	OVS_DROP_REASON_FORWARDING_DISABLED,
> +	OVS_DROP_REASON_MAX,
> +};
> +
> +/*
> + * struct ovs_action_drop - %OVS_ACTION_ATTR_DROP action argument.
> + * @drop_reason: Reason for installing drop action.
> + */
> +struct ovs_action_drop {
> +	enum ovs_drop_reason drop_reason;
> +};
> +
> +/**
>   * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
>   *
>   * @OVS_NAT_ATTR_SRC: Flag for Source NAT (mangle source address/port).
> @@ -938,6 +988,7 @@ enum ovs_action_attr {
>  	OVS_ACTION_ATTR_POP_NSH,      /* No argument. */
>  	OVS_ACTION_ATTR_METER,        /* u32 meter number. */
>  	OVS_ACTION_ATTR_CLONE,        /* Nested OVS_CLONE_ATTR_*.  */
> +        OVS_ACTION_ATTR_DROP,         /* explicit drop action. */
>  
>  #ifndef __KERNEL__
>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
> diff --git a/lib/coverage.c b/lib/coverage.c
> index 6cef826..c9acc37 100644
> --- a/lib/coverage.c
> +++ b/lib/coverage.c
> @@ -32,9 +32,7 @@ VLOG_DEFINE_THIS_MODULE(coverage);
>  static struct coverage_counter **coverage_counters = NULL;
>  static size_t n_coverage_counters = 0;
>  static size_t allocated_coverage_counters = 0;
> -
> -static struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
> -
> +struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
>  DEFINE_STATIC_PER_THREAD_DATA(long long int, coverage_clear_time, LLONG_MIN);
>  static long long int coverage_run_time = LLONG_MIN;
>  
> @@ -52,7 +50,7 @@ coverage_counter_register(struct coverage_counter* counter)
>      if (n_coverage_counters >= allocated_coverage_counters) {
>          coverage_counters = x2nrealloc(coverage_counters,
>                                         &allocated_coverage_counters,
> -                                       sizeof(struct coverage_counter*));
> +                                       sizeof(struct coverage_counter *));
>      }
>      coverage_counters[n_coverage_counters++] = counter;
>  }
> diff --git a/lib/coverage.h b/lib/coverage.h
> index dea990e..45b1dbd 100644
> --- a/lib/coverage.h
> +++ b/lib/coverage.h
> @@ -54,7 +54,8 @@ struct coverage_counter {
>      unsigned int hr[HR_AVG_LEN];
>  };
>  
> -void coverage_counter_register(struct coverage_counter*);
> +extern struct ovs_mutex coverage_mutex;
> +void coverage_counter_register(struct coverage_counter *);
>  
>  /* Defines COUNTER.  There must be exactly one such definition at file scope
>   * within a program. */
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index be529b6..8749cba 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -100,6 +100,30 @@ enum { MAX_METERS = 65536 };    /* Maximum number of meters. */
>  enum { MAX_BANDS = 8 };         /* Maximum number of bands / meter. */
>  enum { N_METER_LOCKS = 64 };    /* Maximum number of meters. */
>  
> +
> +COVERAGE_DEFINE(drop_action_of_pipeline);
> +COVERAGE_DEFINE(drop_action_bridge_not_found);
> +COVERAGE_DEFINE(drop_action_recursion_too_deep);
> +COVERAGE_DEFINE(drop_action_too_many_resubmit);
> +COVERAGE_DEFINE(drop_action_stack_too_deep);
> +COVERAGE_DEFINE(drop_action_no_recirculation_context);
> +COVERAGE_DEFINE(drop_action_recirculation_conflict);
> +COVERAGE_DEFINE(drop_action_too_many_mpls_labels);
> +COVERAGE_DEFINE(drop_action_invalid_tunnel_metadata);
> +COVERAGE_DEFINE(drop_action_unsupported_packet_type);
> +COVERAGE_DEFINE(drop_action_congestion);
> +COVERAGE_DEFINE(drop_action_forwarding_disabled);
> +COVERAGE_DEFINE(dp_meter_drop);
> +COVERAGE_DEFINE(dp_upcall_error_drop);
> +COVERAGE_DEFINE(dp_lock_error_drop);
> +COVERAGE_DEFINE(dp_userspace_action_error_drop);
> +COVERAGE_DEFINE(dp_tunnel_push_error_drop);
> +COVERAGE_DEFINE(dp_tunnel_pop_error_drop);
> +COVERAGE_DEFINE(dp_recirc_error_drop);
> +COVERAGE_DEFINE(dp_invalid_port_drop);
> +COVERAGE_DEFINE(dp_invalid_tnl_port_drop);
> +COVERAGE_DEFINE(rx_invalid_packet_drop);
> +
>  /* Protects against changes to 'dp_netdevs'. */
>  static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>  
> @@ -829,7 +853,6 @@ static inline bool
>  pmd_perf_metrics_enabled(const struct dp_netdev_pmd_thread *pmd);
>  static void queue_netdev_flow_del(struct dp_netdev_pmd_thread *pmd,
>                                    struct dp_netdev_flow *flow);
> -
>  static void
>  emc_cache_init(struct emc_cache *flow_cache)
>  {
> @@ -1388,6 +1411,7 @@ dpif_netdev_init(void)
>      return 0;
>  }
>  
> +
>  static int
>  dpif_netdev_enumerate(struct sset *all_dps,
>                        const struct dpif_class *dpif_class)
> @@ -5563,7 +5587,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, struct dp_packet_batch *packets_,
>              band = &meter->bands[exceeded_band[j]];
>              band->packet_count += 1;
>              band->byte_count += dp_packet_size(packet);
> -
> +            COVERAGE_INC(dp_meter_drop);
>              dp_packet_delete(packet);
>          } else {
>              /* Meter accepts packet. */
> @@ -6320,6 +6344,7 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
>  
>          if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) {
>              dp_packet_delete(packet);
> +            COVERAGE_INC(rx_invalid_packet_drop);
>              continue;
>          }
>  
> @@ -6446,6 +6471,7 @@ handle_packet_upcall(struct dp_netdev_pmd_thread *pmd,
>                               put_actions);
>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>          dp_packet_delete(packet);
> +        COVERAGE_INC(dp_upcall_error_drop);
>          return error;
>      }
>  
> @@ -6577,6 +6603,7 @@ 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);
> +                COVERAGE_INC(dp_lock_error_drop);
>                  upcall_fail_cnt++;
>              }
>          }
> @@ -6846,10 +6873,59 @@ dp_execute_userspace_action(struct dp_netdev_pmd_thread *pmd,
>                                    actions->data, actions->size);
>      } else if (should_steal) {
>          dp_packet_delete(packet);
> +        COVERAGE_INC(dp_userspace_action_error_drop);
>      }
>  }
>  
>  static void
> +dp_update_drop_action_counter_cb(enum ovs_drop_reason drop_reason,
> +                          int delta)
> +    OVS_NO_THREAD_SAFETY_ANALYSIS
> +{
> +   switch (drop_reason) {
> +   case OVS_DROP_REASON_OF_PIPELINE:
> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
> +        break;
> +   case OVS_DROP_REASON_BRIDGE_NOT_FOUND:
> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
> +        break;
> +   case OVS_DROP_REASON_RECURSION_TOO_DEEP:
> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
> +        break;
> +   case OVS_DROP_REASON_TOO_MANY_RESUBMITS:
> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
> +        break;
> +   case OVS_DROP_REASON_STACK_TOO_DEEP:
> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
> +        break;
> +   case OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT:
> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
> +        break;
> +   case OVS_DROP_REASON_RECIRCULATION_CONFLICT:
> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
> +        break;
> +   case OVS_DROP_REASON_TOO_MANY_MPLS_LABELS:
> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
> +        break;
> +   case OVS_DROP_REASON_INVALID_TUNNEL_METADATA:
> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
> +        break;
> +   case OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE:
> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
> +        break;
> +   case OVS_DROP_REASON_CONGESTION:
> +        COVERAGE_ADD(drop_action_congestion, delta);
> +        break;
> +   case OVS_DROP_REASON_FORWARDING_DISABLED:
> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
> +        break;
> +   case OVS_DROP_REASON_MAX:
> +   default:
> +        VLOG_ERR("Invalid Drop reason type:%d",drop_reason);
> +   }
> +}
> +
> +static void
>  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                const struct nlattr *a, bool should_steal)
>      OVS_NO_THREAD_SAFETY_ANALYSIS
> @@ -6860,6 +6936,7 @@ 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;
>  
>      switch ((enum ovs_action_attr)type) {
>      case OVS_ACTION_ATTR_OUTPUT:
> @@ -6901,6 +6978,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                  dp_packet_batch_add(&p->output_pkts, packet);
>              }
>              return;
> +        } else {
> +            COVERAGE_ADD(dp_invalid_port_drop, packets_->count);
>          }
>          break;
>  
> @@ -6910,10 +6989,13 @@ 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. */
> +            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
>              break;
>          }
>          dp_packet_batch_apply_cutlen(packets_);
> -        push_tnl_action(pmd, a, packets_);
> +        if (push_tnl_action(pmd, a, packets_)) {
> +            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
> +        }
>          return;
>  
>      case OVS_ACTION_ATTR_TUNNEL_POP:
> @@ -6933,7 +7015,12 @@ 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) {
> +                    COVERAGE_ADD(dp_tunnel_pop_error_drop, packet_dropped);
> +                }
>                  if (dp_packet_batch_is_empty(packets_)) {
>                      return;
>                  }
> @@ -6947,7 +7034,11 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                  dp_netdev_recirculate(pmd, packets_);
>                  (*depth)--;
>                  return;
> +            } else {
> +                COVERAGE_ADD(dp_invalid_tnl_port_drop, packets_->count);
>              }
> +        } else {
> +            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
>          }
>          break;
>  
> @@ -6991,6 +7082,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>              fat_rwlock_unlock(&dp->upcall_rwlock);
>  
>              return;
> +        } else {
> +            COVERAGE_ADD(dp_lock_error_drop, packets_->count);
>          }
>          break;
>  
> @@ -7013,6 +7106,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>              (*depth)--;
>  
>              return;
> +        } else {
> +            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
>          }
>  
>          VLOG_WARN("Packet dropped. Max recirculation depth exceeded.");
> @@ -7167,6 +7262,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();
>      }
> @@ -7183,7 +7279,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_action_counter_cb);
>  }
>  
>  struct dp_netdev_ct_dump {
> diff --git a/lib/dpif.c b/lib/dpif.c
> index e35f111..21f9f54 100644
> --- a/lib/dpif.c
> +++ b/lib/dpif.c
> @@ -1273,6 +1273,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();
> @@ -1295,7 +1296,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;
>  }
>  
> @@ -1879,6 +1880,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,
> @@ -1976,3 +1983,4 @@ dpif_meter_del(struct dpif *dpif, ofproto_meter_id meter_id,
>      }
>      return error;
>  }
> +
> diff --git a/lib/dpif.h b/lib/dpif.h
> index 475d5a6..e799da8 100644
> --- a/lib/dpif.h
> +++ b/lib/dpif.h
> @@ -888,6 +888,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 320422b..0c3bc9c 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -2395,6 +2395,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 3b6890e..c0db93f 100644
> --- a/lib/odp-execute.c
> +++ b/lib/odp-execute.c
> @@ -25,6 +25,7 @@
>  #include <stdlib.h>
>  #include <string.h>
>  
> +#include "coverage.h"
>  #include "dp-packet.h"
>  #include "dpif.h"
>  #include "netlink.h"
> @@ -37,6 +38,10 @@
>  #include "csum.h"
>  #include "conntrack.h"
>  
> +COVERAGE_DEFINE(dp_sample_error_drop);
> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
> +
> +
>  /* Masked copy of an ethernet address. 'src' is already properly masked. */
>  static void
>  ether_addr_copy_masked(struct eth_addr *dst, const struct eth_addr src,
> @@ -575,7 +580,9 @@ 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_action_counter_cb
> +                     dp_update_drop_action_counter)
>  {
>      const struct nlattr *subactions = NULL;
>      const struct nlattr *a;
> @@ -589,6 +596,7 @@ 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) {
> +                    COVERAGE_ADD(dp_sample_error_drop, 1);
>                      dp_packet_delete(packet);
>                  }
>                  return;
> @@ -616,13 +624,16 @@ 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_action_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_action_counter_cb
> +                      dp_update_drop_action_counter)
>  {
>      if (!steal) {
>          /* The 'actions' may modify the packet, but the modification
> @@ -634,11 +645,12 @@ 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);
> -    }
> -    else {
> +                        nl_attr_get_size(actions), dp_execute_action,
> +                        dp_update_drop_action_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_action_counter);
>      }
>  }
>  
> @@ -673,6 +685,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,12 +712,15 @@ 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_action_counter_cb
> +                       dp_update_drop_action_counter)
>  {
>      struct dp_packet *packet;
>      const struct nlattr *a;
>      unsigned int left;
>  
> +
>      NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
>          int type = nl_attr_type(a);
>          bool last_action = (left <= NLA_ALIGN(a->nla_len));
> @@ -822,7 +838,8 @@ 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_action_counter);
>              }
>  
>              if (last_action) {
> @@ -845,7 +862,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_action_counter);
>              if (last_action) {
>                  /* We do not need to free the packets. odp_execute_clone() has
>                   * stolen them.  */
> @@ -889,6 +907,7 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
>                  if (pop_nsh(packet)) {
>                      dp_packet_batch_refill(batch, packet, i);
>                  } else {
> +                    COVERAGE_INC(dp_nsh_decap_error_drop);
>                      dp_packet_delete(packet);
>                  }
>              }
> @@ -900,6 +919,17 @@ 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_action_counter) {
> +                 dp_update_drop_action_counter(drop_reason, 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..a84ecbb 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_action_counter_cb) (
> +                                           enum  ovs_drop_reason drop_reason,
> +                                           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,7 @@ 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_action_counter_cb
> +                             dp_update_drop_action_counter_cb);
>  #endif
> diff --git a/lib/odp-util.c b/lib/odp-util.c
> index 0491bed..9754fd5 100644
> --- a/lib/odp-util.c
> +++ b/lib/odp-util.c
> @@ -131,6 +131,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:
> @@ -345,6 +346,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)
> @@ -1181,6 +1225,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:
> @@ -2427,8 +2474,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 7da3175..69ed7b8 100644
> --- a/ofproto/ofproto-dpif-sflow.c
> +++ b/ofproto/ofproto-dpif-sflow.c
> @@ -1222,6 +1222,7 @@ dpif_sflow_read_actions(const struct flow *flow,
>          case OVS_ACTION_ATTR_PUSH_NSH:
>          case OVS_ACTION_ATTR_POP_NSH:
>          case OVS_ACTION_ATTR_UNSPEC:
> +        case OVS_ACTION_ATTR_DROP:
>          case __OVS_ACTION_ATTR_MAX:
>          default:
>              break;
> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
> index dc30824..b1ce2ec 100644
> --- a/ofproto/ofproto-dpif-upcall.c
> +++ b/ofproto/ofproto-dpif-upcall.c
> @@ -1154,7 +1154,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)
>  {
> @@ -1244,6 +1244,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
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index 839fddd..32c4edf 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);
>  
> @@ -5921,6 +5957,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)
>  {
> @@ -7383,6 +7430,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;
> @@ -7415,6 +7466,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) {
> @@ -7522,6 +7574,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 0a5a528..bb3f9ff 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 14fe6fc..609226a 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -827,6 +827,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.
> @@ -1397,6 +1403,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);
> @@ -5776,6 +5784,7 @@ ofproto_unixctl_dpif_set_dp_features(struct unixctl_conn *conn,
>      ds_destroy(&ds);
>  }
>  
> +
>  static void
>  ofproto_unixctl_init(void)
>  {
> @@ -5809,7 +5818,7 @@ ofproto_unixctl_init(void)
>      unixctl_command_register("dpif/set-dp-features", "bridge", 1, 3 ,
>                               ofproto_unixctl_dpif_set_dp_features, NULL);
>  }
> -
> 
> +
>  static odp_port_t
>  ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
>  {
> 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 92d56b2..22b4722 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -108,7 +108,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
>  
>  EXTRA_DIST += $(FUZZ_REGRESSION_TESTS)
>  FUZZ_REGRESSION_TESTS = \
> diff --git a/tests/bundle.at b/tests/bundle.at
> index 0a4eadc..33fe249 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 6915d43..08349f6 100644
> --- a/tests/dpif-netdev.at
> +++ b/tests/dpif-netdev.at
> @@ -1,4 +1,4 @@
> -AT_BANNER([dpif-netdev])
> +T_BANNER([dpif-netdev])
>  
>  m4_divert_push([PREPARE_TESTS])
>  [
> @@ -281,6 +281,7 @@ type=drop rate=1 burst_size=2
>  ])
>  
>  ovs-appctl time/warp 5000
> +sleep 10
>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
> @@ -291,7 +292,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
> -sleep 1  # wait for forwarders process packets
> +# wait for forwarders process packets
>  
>  # Meter 1 is measuring packets, allowing one packet per second with
>  # bursts of one packet, so 4 out of 5 packets should hit the drop
> @@ -320,7 +321,8 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
> -sleep 1  # wait for forwarders process packets
> +
> +sleep 10  # wait for forwarders process packets
>  
>  # Meter 1 is measuring packets, allowing one packet per second with
>  # bursts of one packet, so all 5 of the new packets should hit the drop
> @@ -337,6 +339,15 @@ meter:2 flow_count:1 packet_in_count:10 byte_in_count:600 duration:0.0s bands:
>  0: packet_count:5 byte_count:300
>  ])
>  
> +ovs-appctl time/warp 5000
> +sleep 10
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "dp_meter_drop" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +14
> +])
> +
>  AT_CHECK([cat ovs-vswitchd.log | filter_flow_install | strip_xout_keep_actions], [0], [dnl
>  recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:meter(0),7
>  recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:8
> diff --git a/tests/drop-stats.at b/tests/drop-stats.at
> new file mode 100644
> index 0000000..be8b8ae
> --- /dev/null
> +++ b/tests/drop-stats.at
> @@ -0,0 +1,197 @@
> +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
> +])
> +
> +sleep 1
> +
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +3
> +])
> +
> +
> +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])
> +
> +sleep 1
> +
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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])
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_recursion_too_deep" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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)'
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_too_many_resubmit" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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)'
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_stack_too_deep" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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])
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_too_many_mpls_labels" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
> +
> +OVS_VSWITCHD_STOP(["/|WARN|/d"])
> +AT_CLEANUP
> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> index ded2ef0..298aa9a 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -3496,51 +3496,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" \
> @@ -3555,8 +3555,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" \
> @@ -3573,16 +3573,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"
> @@ -4322,11 +4322,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
> @@ -4404,8 +4404,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
> @@ -5751,7 +5751,7 @@ bridge("br0")
>  
>  Final flow: <cleared>
>  Megaflow: <cleared>
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>  ])
>  
>  dnl Now, try again without megaflows:
> @@ -5772,7 +5772,7 @@ bridge("br0")
>  
>  Final flow: <cleared>
>  Megaflow: <cleared>
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>  ])
>  
>  OVS_VSWITCHD_STOP
> @@ -7066,7 +7066,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"])
> @@ -7158,7 +7158,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
> @@ -7832,21 +7832,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
> @@ -7869,7 +7869,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
> @@ -8617,11 +8617,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])
> @@ -9348,7 +9348,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
> @@ -9377,16 +9377,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
> @@ -9711,7 +9711,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)
> @@ -10398,7 +10398,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])
> @@ -10490,7 +10490,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 6ae3470..cd19d41 100644
> --- a/tests/ovs-ofctl.at
> +++ b/tests/ovs-ofctl.at
> @@ -2999,7 +2999,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..7c1b551 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,14 @@ 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 coverage/show | grep "drop_action_unsupported_packet_type" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +2
>  ])
>  
>  # Encap(ethernet) on Ethernet frame -> should be droped
> @@ -587,7 +594,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 +616,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 +777,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 b840dbf..922ba48 100644
> --- a/tests/testsuite.at
> +++ b/tests/testsuite.at
> @@ -82,3 +82,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..385c114 100644
> --- a/tests/tunnel-push-pop.at
> +++ b/tests/tunnel-push-pop.at
> @@ -447,6 +447,27 @@ 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 netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +
> +ovs-appctl time/warp 1200
> +
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "dp_tunnel_pop_error_drop" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
> +
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +ovs-appctl time/warp 1200
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_congestion" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
>  dnl Check GREL3 only accepts non-fragmented packets?
>  AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>  
> @@ -455,7 +476,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 +531,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 55fb1d3..3646c06 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 netdev-dummy/receive p2 'aa55aa550001f8bc124434b6080045000054ba20000040018486010103580101037001004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +sleep 2
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "dp_invalid_port_drop" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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
> -- 
> 1.9.1
>
Ilya Maximets Jan. 18, 2019, 12:02 p.m. UTC | #4
Hi.
Thanks for working on this.
Some general comments:

1. This patch consists of two separate features:
   - Reporting the drop reason in flow dumps.
   - Counters for different drop types.
   I still think that there should be patch-set of two separate patches.
   This will simplify the review and allow to apply them separately/speed
   up accepting.

2. There are some issues with the patch formatting. The commit message is
   indented like in the output of 'git show' command.  So, it's double
   shifted after applying in git. Please remove the additional indentation.
   You probably should start using 'git format-patch'.

   Second issue:

   Applying: Improved Packet Drop Statistics in OVS
   .git/rebase-apply/patch:415: new blank line at EOF.
   +
   warning: 1 line adds whitespace errors.

   One more thing is that for better readability it's a common practice to
   limit the width of lines in commit-message to 72 characters. For example,
   you may strip not important fields of the dump-flows output. Like this:

       recirc_id(0),in_port(5),<...>, actions:drop:recursion too deep


Other comments inline.

Best regards, Ilya Maximets.

On 17.01.2019 7:49, Anju Thomas 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. Display these drops in ovs-appctl coverage/show
>     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.
> 
>      The idea is to use the coverage infrastructure to maintain the drops
> 
>     $ ovs-appctl dpctl/dump-flows netdev at ovs-netdev
>     flow-dump from pmd on cpu core: 0
>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:12, bytes:1176, used:0.884s, actions:drop:recursion too deep
> 
>     $ ovs-appctl dpif/dump-flows br-int
>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:25, bytes:2450, used:5.008s, actions:drop:recursion too deep
>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0806), packets:7, bytes:294, used:0.009s, actions:drop:recursion too deep
> 
>    In subsequent commits, we are planning to see if we can use this infrastructure to create a
>    wrapper to clear and display counters as well.
> 
> Co-authored-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Co-authored-by: Keshav Gupta <keshugupta1@gmail.com>
> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Signed-off-by: Keshav Gupta <keshugupta1@gmail.com>
> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Signed-off-by: Keshav Gupta <keshugupta1@gmail.com>
> ---
>  datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
>  lib/coverage.c                                    |   6 +-
>  lib/coverage.h                                    |   3 +-
>  lib/dpif-netdev.c                                 | 105 +++++++++++-
>  lib/dpif.c                                        |  10 +-
>  lib/dpif.h                                        |   3 +
>  lib/netdev-dpdk.c                                 |   4 +
>  lib/odp-execute.c                                 |  50 ++++--
>  lib/odp-execute.h                                 |  10 +-
>  lib/odp-util.c                                    |  54 +++++-
>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>  ofproto/ofproto-dpif-upcall.c                     |   3 +-
>  ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
>  ofproto/ofproto-dpif-xlate.h                      |   4 +
>  ofproto/ofproto-dpif.c                            |  11 +-
>  ofproto/ofproto-dpif.h                            |   8 +-
>  tests/automake.mk                                 |   3 +-
>  tests/bundle.at                                   |   2 +-
>  tests/classifier.at                               |  10 +-
>  tests/dpif-netdev.at                              |  17 +-
>  tests/drop-stats.at                               | 197 ++++++++++++++++++++++
>  tests/ofproto-dpif.at                             | 118 ++++++-------
>  tests/ovs-ofctl.at                                |   2 +-
>  tests/packet-type-aware.at                        |  17 +-
>  tests/testsuite.at                                |   1 +
>  tests/tunnel-push-pop.at                          |  26 ++-
>  tests/tunnel.at                                   |  21 ++-
>  28 files changed, 695 insertions(+), 107 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 9b087f1..b66b46f 100644
> --- a/datapath/linux/compat/include/linux/openvswitch.h
> +++ b/datapath/linux/compat/include/linux/openvswitch.h
> @@ -819,6 +819,56 @@ struct ovs_action_push_eth {
>  };
>  
>  /**
> + * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP.
> + * @OVS_DROP_REASON_OF_PIPELINE: Explicit drop action in the pipeline.
> + * @OVS_DROP_REASON_BRIDGE_NOT_FOUND: Xlation error generated due to
> + * unable to determine bridge.
> + * @OVS_DROP_REASON_RECURSION_TOO_DEEP: Xlation error generated due to
> + * recursion reached maximum depth.
> + * @OVS_DROP_REASON_TOO_MANY_RESUBMITS: Xlation error generated due to
> + * too many resubmits.
> + * @OVS_DROP_REASON_STACK_TOO_DEEP: Xlation error generated due to stack
> + * too deep.
> + * @OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT: Xlation error generated
> + * due to no recirculation context.
> + * @OVS_DROP_REASON_RECIRCULATION_CONFLICT: Xlation error generated due to
> + * conflict in recirculation context.
> + * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to
> + * too many mpls labels.
> + * @OVS_DROP_REASON_INVALID_TUNNEL_METADATA: Xlation error generated due to
> + * invalid tunnel metadata.
> + * @OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE: Xlation error generated due to
> + * unsupported packet type.
> + * @OVS_DROP_REASON_CONGESTION: Xlation error generated due to ecn mismatch
> + * during tunnel decapsulation.
> + * @OVS_DROP_REASON_FORWARDING_DISABLED: Xlation error generated due to
> + * forwarding is disabled.
> + */
> +enum ovs_drop_reason {
> +	OVS_DROP_REASON_OF_PIPELINE = 0,
> +	OVS_DROP_REASON_BRIDGE_NOT_FOUND,
> +	OVS_DROP_REASON_RECURSION_TOO_DEEP,
> +	OVS_DROP_REASON_TOO_MANY_RESUBMITS,
> +	OVS_DROP_REASON_STACK_TOO_DEEP,
> +	OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT,
> +	OVS_DROP_REASON_RECIRCULATION_CONFLICT,
> +	OVS_DROP_REASON_TOO_MANY_MPLS_LABELS,
> +	OVS_DROP_REASON_INVALID_TUNNEL_METADATA,
> +	OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE,
> +	OVS_DROP_REASON_CONGESTION,
> +	OVS_DROP_REASON_FORWARDING_DISABLED,
> +	OVS_DROP_REASON_MAX,
> +};
> +
> +/*
> + * struct ovs_action_drop - %OVS_ACTION_ATTR_DROP action argument.
> + * @drop_reason: Reason for installing drop action.
> + */
> +struct ovs_action_drop {
> +	enum ovs_drop_reason drop_reason;
> +};
> +
> +/**
>   * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
>   *
>   * @OVS_NAT_ATTR_SRC: Flag for Source NAT (mangle source address/port).
> @@ -938,6 +988,7 @@ enum ovs_action_attr {
>  	OVS_ACTION_ATTR_POP_NSH,      /* No argument. */
>  	OVS_ACTION_ATTR_METER,        /* u32 meter number. */
>  	OVS_ACTION_ATTR_CLONE,        /* Nested OVS_CLONE_ATTR_*.  */
> +        OVS_ACTION_ATTR_DROP,         /* explicit drop action. */

This is the linux kernel header. You should use tabs here.

>  
>  #ifndef __KERNEL__
>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
> diff --git a/lib/coverage.c b/lib/coverage.c
> index 6cef826..c9acc37 100644
> --- a/lib/coverage.c
> +++ b/lib/coverage.c
> @@ -32,9 +32,7 @@ VLOG_DEFINE_THIS_MODULE(coverage);
>  static struct coverage_counter **coverage_counters = NULL;
>  static size_t n_coverage_counters = 0;
>  static size_t allocated_coverage_counters = 0;
> -
> -static struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;

This is leftover from the previous versions. No need to expose
'coverage_mutex' in current patch.

> -
> +struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
>  DEFINE_STATIC_PER_THREAD_DATA(long long int, coverage_clear_time, LLONG_MIN);
>  static long long int coverage_run_time = LLONG_MIN;
>  
> @@ -52,7 +50,7 @@ coverage_counter_register(struct coverage_counter* counter)
>      if (n_coverage_counters >= allocated_coverage_counters) {
>          coverage_counters = x2nrealloc(coverage_counters,
>                                         &allocated_coverage_counters,
> -                                       sizeof(struct coverage_counter*));
> +                                       sizeof(struct coverage_counter *));

this change is also not needed.

>      }
>      coverage_counters[n_coverage_counters++] = counter;
>  }
> diff --git a/lib/coverage.h b/lib/coverage.h
> index dea990e..45b1dbd 100644
> --- a/lib/coverage.h
> +++ b/lib/coverage.h
> @@ -54,7 +54,8 @@ struct coverage_counter {
>      unsigned int hr[HR_AVG_LEN];
>  };
>  
> -void coverage_counter_register(struct coverage_counter*);
> +extern struct ovs_mutex coverage_mutex;
> +void coverage_counter_register(struct coverage_counter *);

This is leftover from the previous versions. No need to expose
'coverage_mutex' in current patch.

>  
>  /* Defines COUNTER.  There must be exactly one such definition at file scope
>   * within a program. */
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index be529b6..8749cba 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -100,6 +100,30 @@ enum { MAX_METERS = 65536 };    /* Maximum number of meters. */
>  enum { MAX_BANDS = 8 };         /* Maximum number of bands / meter. */
>  enum { N_METER_LOCKS = 64 };    /* Maximum number of meters. */
>  
> +
> +COVERAGE_DEFINE(drop_action_of_pipeline);
> +COVERAGE_DEFINE(drop_action_bridge_not_found);
> +COVERAGE_DEFINE(drop_action_recursion_too_deep);
> +COVERAGE_DEFINE(drop_action_too_many_resubmit);
> +COVERAGE_DEFINE(drop_action_stack_too_deep);
> +COVERAGE_DEFINE(drop_action_no_recirculation_context);
> +COVERAGE_DEFINE(drop_action_recirculation_conflict);
> +COVERAGE_DEFINE(drop_action_too_many_mpls_labels);
> +COVERAGE_DEFINE(drop_action_invalid_tunnel_metadata);
> +COVERAGE_DEFINE(drop_action_unsupported_packet_type);
> +COVERAGE_DEFINE(drop_action_congestion);
> +COVERAGE_DEFINE(drop_action_forwarding_disabled);

Above counters could be defined inside odp-execute.c. You already have
2 other counters there. In this case there will be no need to introduce
additional 'dp_update_drop_action_counter_cb' callback and pass it around.
All the required logic will also be moved to odp-execute.c.

> +COVERAGE_DEFINE(dp_meter_drop);
> +COVERAGE_DEFINE(dp_upcall_error_drop);
> +COVERAGE_DEFINE(dp_lock_error_drop);
> +COVERAGE_DEFINE(dp_userspace_action_error_drop);
> +COVERAGE_DEFINE(dp_tunnel_push_error_drop);
> +COVERAGE_DEFINE(dp_tunnel_pop_error_drop);
> +COVERAGE_DEFINE(dp_recirc_error_drop);
> +COVERAGE_DEFINE(dp_invalid_port_drop);
> +COVERAGE_DEFINE(dp_invalid_tnl_port_drop);
> +COVERAGE_DEFINE(rx_invalid_packet_drop);

What do you think about renaming above counters to 'datapath_drop_*' ?
IMHO, it'll be more visually recognizable.
Like 'datapath_drop_rx_invalid_packet' or 'datapath_drop_upcall_error'.

> +
>  /* Protects against changes to 'dp_netdevs'. */
>  static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>  
> @@ -829,7 +853,6 @@ static inline bool
>  pmd_perf_metrics_enabled(const struct dp_netdev_pmd_thread *pmd);
>  static void queue_netdev_flow_del(struct dp_netdev_pmd_thread *pmd,
>                                    struct dp_netdev_flow *flow);
> -

No need to remove an empty line.

>  static void
>  emc_cache_init(struct emc_cache *flow_cache)
>  {
> @@ -1388,6 +1411,7 @@ dpif_netdev_init(void)
>      return 0;
>  }
>  
> +

No need to add an empty line.

>  static int
>  dpif_netdev_enumerate(struct sset *all_dps,
>                        const struct dpif_class *dpif_class)
> @@ -5563,7 +5587,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, struct dp_packet_batch *packets_,
>              band = &meter->bands[exceeded_band[j]];
>              band->packet_count += 1;
>              band->byte_count += dp_packet_size(packet);
> -

Please, keep this empty line for readability.

> +            COVERAGE_INC(dp_meter_drop);
>              dp_packet_delete(packet);
>          } else {
>              /* Meter accepts packet. */
> @@ -6320,6 +6344,7 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
>  
>          if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) {
>              dp_packet_delete(packet);
> +            COVERAGE_INC(rx_invalid_packet_drop);
>              continue;
>          }
>  
> @@ -6446,6 +6471,7 @@ handle_packet_upcall(struct dp_netdev_pmd_thread *pmd,
>                               put_actions);
>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>          dp_packet_delete(packet);
> +        COVERAGE_INC(dp_upcall_error_drop);
>          return error;
>      }
>  
> @@ -6577,6 +6603,7 @@ 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);
> +                COVERAGE_INC(dp_lock_error_drop);
>                  upcall_fail_cnt++;
>              }
>          }
> @@ -6846,10 +6873,59 @@ dp_execute_userspace_action(struct dp_netdev_pmd_thread *pmd,
>                                    actions->data, actions->size);
>      } else if (should_steal) {
>          dp_packet_delete(packet);
> +        COVERAGE_INC(dp_userspace_action_error_drop);
>      }
>  }
>  
>  static void
> +dp_update_drop_action_counter_cb(enum ovs_drop_reason drop_reason,
> +                          int delta)

Indents are a bit off.

> +    OVS_NO_THREAD_SAFETY_ANALYSIS

Is it necessary to disable thread safety analysis ?

> +{
> +   switch (drop_reason) {
> +   case OVS_DROP_REASON_OF_PIPELINE:
> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
> +        break;
> +   case OVS_DROP_REASON_BRIDGE_NOT_FOUND:
> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
> +        break;
> +   case OVS_DROP_REASON_RECURSION_TOO_DEEP:
> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
> +        break;
> +   case OVS_DROP_REASON_TOO_MANY_RESUBMITS:
> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
> +        break;
> +   case OVS_DROP_REASON_STACK_TOO_DEEP:
> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
> +        break;
> +   case OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT:
> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
> +        break;
> +   case OVS_DROP_REASON_RECIRCULATION_CONFLICT:
> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
> +        break;
> +   case OVS_DROP_REASON_TOO_MANY_MPLS_LABELS:
> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
> +        break;
> +   case OVS_DROP_REASON_INVALID_TUNNEL_METADATA:
> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
> +        break;
> +   case OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE:
> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
> +        break;
> +   case OVS_DROP_REASON_CONGESTION:
> +        COVERAGE_ADD(drop_action_congestion, delta);
> +        break;
> +   case OVS_DROP_REASON_FORWARDING_DISABLED:
> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
> +        break;
> +   case OVS_DROP_REASON_MAX:
> +   default:
> +        VLOG_ERR("Invalid Drop reason type:%d",drop_reason);

We might want the rate limiting for above message to not have it for each packet.

> +   }
> +}
> +
> +static void
>  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                const struct nlattr *a, bool should_steal)
>      OVS_NO_THREAD_SAFETY_ANALYSIS
> @@ -6860,6 +6936,7 @@ 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;
>  
>      switch ((enum ovs_action_attr)type) {
>      case OVS_ACTION_ATTR_OUTPUT:
> @@ -6901,6 +6978,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                  dp_packet_batch_add(&p->output_pkts, packet);
>              }
>              return;
> +        } else {
> +            COVERAGE_ADD(dp_invalid_port_drop, packets_->count);
>          }
>          break;
>  
> @@ -6910,10 +6989,13 @@ 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. */
> +            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
>              break;
>          }
>          dp_packet_batch_apply_cutlen(packets_);
> -        push_tnl_action(pmd, a, packets_);
> +        if (push_tnl_action(pmd, a, packets_)) {
> +            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
> +        }
>          return;
>  
>      case OVS_ACTION_ATTR_TUNNEL_POP:
> @@ -6933,7 +7015,12 @@ 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) {
> +                    COVERAGE_ADD(dp_tunnel_pop_error_drop, packet_dropped);
> +                }
>                  if (dp_packet_batch_is_empty(packets_)) {
>                      return;
>                  }
> @@ -6947,7 +7034,11 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                  dp_netdev_recirculate(pmd, packets_);
>                  (*depth)--;
>                  return;
> +            } else {
> +                COVERAGE_ADD(dp_invalid_tnl_port_drop, packets_->count);
>              }
> +        } else {
> +            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
>          }
>          break;
>  
> @@ -6991,6 +7082,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>              fat_rwlock_unlock(&dp->upcall_rwlock);
>  
>              return;
> +        } else {
> +            COVERAGE_ADD(dp_lock_error_drop, packets_->count);
>          }
>          break;
>  
> @@ -7013,6 +7106,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>              (*depth)--;
>  
>              return;
> +        } else {
> +            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
>          }

'Else' condition is not needed here as we're returning inside the 'if'.
This is actually applicable to most of other places in this function,
but in this one we already have a warning below without 'else' condition.

>  
>          VLOG_WARN("Packet dropped. Max recirculation depth exceeded.");
> @@ -7167,6 +7262,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();
>      }
> @@ -7183,7 +7279,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_action_counter_cb);

As I already wrote, we'll not need to have a callback function if you'll
move the 'drop_action_*' counters to odp-execute.c.

>  }
>  
>  struct dp_netdev_ct_dump {
> diff --git a/lib/dpif.c b/lib/dpif.c
> index e35f111..21f9f54 100644
> --- a/lib/dpif.c
> +++ b/lib/dpif.c
> @@ -1273,6 +1273,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();
> @@ -1295,7 +1296,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;
>  }
>  
> @@ -1879,6 +1880,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,
> @@ -1976,3 +1983,4 @@ dpif_meter_del(struct dpif *dpif, ofproto_meter_id meter_id,
>      }
>      return error;
>  }
> +
> diff --git a/lib/dpif.h b/lib/dpif.h
> index 475d5a6..e799da8 100644
> --- a/lib/dpif.h
> +++ b/lib/dpif.h
> @@ -888,6 +888,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 320422b..0c3bc9c 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -2395,6 +2395,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);

This is the unrelated bug fix that should be sent as a separate patch.
In this case we'll be able to apply it and backport to previous versions.

Adding Ian to CC.

>          dp_packet_delete_batch(batch, true);
>          return;
>      }
> diff --git a/lib/odp-execute.c b/lib/odp-execute.c
> index 3b6890e..c0db93f 100644
> --- a/lib/odp-execute.c
> +++ b/lib/odp-execute.c
> @@ -25,6 +25,7 @@
>  #include <stdlib.h>
>  #include <string.h>
>  
> +#include "coverage.h"
>  #include "dp-packet.h"
>  #include "dpif.h"
>  #include "netlink.h"
> @@ -37,6 +38,10 @@
>  #include "csum.h"
>  #include "conntrack.h"
>  
> +COVERAGE_DEFINE(dp_sample_error_drop);
> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
> +
> +
>  /* Masked copy of an ethernet address. 'src' is already properly masked. */
>  static void
>  ether_addr_copy_masked(struct eth_addr *dst, const struct eth_addr src,
> @@ -575,7 +580,9 @@ 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_action_counter_cb
> +                     dp_update_drop_action_counter)
>  {
>      const struct nlattr *subactions = NULL;
>      const struct nlattr *a;
> @@ -589,6 +596,7 @@ 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) {
> +                    COVERAGE_ADD(dp_sample_error_drop, 1);
>                      dp_packet_delete(packet);
>                  }
>                  return;
> @@ -616,13 +624,16 @@ 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_action_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_action_counter_cb
> +                      dp_update_drop_action_counter)
>  {
>      if (!steal) {
>          /* The 'actions' may modify the packet, but the modification
> @@ -634,11 +645,12 @@ 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);
> -    }
> -    else {
> +                        nl_attr_get_size(actions), dp_execute_action,
> +                        dp_update_drop_action_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_action_counter);
>      }
>  }
>  
> @@ -673,6 +685,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,12 +712,15 @@ 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_action_counter_cb
> +                       dp_update_drop_action_counter)
>  {
>      struct dp_packet *packet;
>      const struct nlattr *a;
>      unsigned int left;
>  
> +

No need to add an empty line.

>      NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
>          int type = nl_attr_type(a);
>          bool last_action = (left <= NLA_ALIGN(a->nla_len));
> @@ -822,7 +838,8 @@ 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_action_counter);
>              }
>  
>              if (last_action) {
> @@ -845,7 +862,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_action_counter);
>              if (last_action) {
>                  /* We do not need to free the packets. odp_execute_clone() has
>                   * stolen them.  */
> @@ -889,6 +907,7 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
>                  if (pop_nsh(packet)) {
>                      dp_packet_batch_refill(batch, packet, i);
>                  } else {
> +                    COVERAGE_INC(dp_nsh_decap_error_drop);
>                      dp_packet_delete(packet);
>                  }
>              }
> @@ -900,6 +919,17 @@ 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_action_counter) {
> +                 dp_update_drop_action_counter(drop_reason, 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..a84ecbb 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_action_counter_cb) (
> +                                           enum  ovs_drop_reason drop_reason,
> +                                           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,7 @@ 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_action_counter_cb
> +                             dp_update_drop_action_counter_cb);
>  #endif
> diff --git a/lib/odp-util.c b/lib/odp-util.c
> index 0491bed..9754fd5 100644
> --- a/lib/odp-util.c
> +++ b/lib/odp-util.c
> @@ -131,6 +131,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:
> @@ -345,6 +346,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));

As I already wrote for the previous versions it'll be good to add
some visible markers around the drop reason. Spaces in flow dumps
are usually not used and it could add some complications for the
parsing scripts. You may use brackets like "drop(stack too deep)"
or replace the spaces with '-' or '_' like "drop:stack_too_deep".

> +}
> +
>  static void
>  format_odp_push_nsh_action(struct ds *ds,
>                             const struct nsh_hdr *nsh_hdr)
> @@ -1181,6 +1225,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:
> @@ -2427,8 +2474,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"))) {

Why you're adding parse only for this type of drop action ?

> +        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 7da3175..69ed7b8 100644
> --- a/ofproto/ofproto-dpif-sflow.c
> +++ b/ofproto/ofproto-dpif-sflow.c
> @@ -1222,6 +1222,7 @@ dpif_sflow_read_actions(const struct flow *flow,
>          case OVS_ACTION_ATTR_PUSH_NSH:
>          case OVS_ACTION_ATTR_POP_NSH:
>          case OVS_ACTION_ATTR_UNSPEC:
> +        case OVS_ACTION_ATTR_DROP:
>          case __OVS_ACTION_ATTR_MAX:
>          default:
>              break;
> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
> index dc30824..b1ce2ec 100644
> --- a/ofproto/ofproto-dpif-upcall.c
> +++ b/ofproto/ofproto-dpif-upcall.c
> @@ -1154,7 +1154,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)
>  {
> @@ -1244,6 +1244,7 @@ upcall_xlate(struct udpif *udpif, struct upcall *upcall,
>      if (upcall->type == MISS_UPCALL) {
>          upcall->ukey = ukey_create_from_upcall(upcall, wc);
>      }
> +    return xerr;

The only user of this function is 'process_upcall' and it does not check
the result.

>  }
>  
>  static void
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index 839fddd..32c4edf 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);
>  
> @@ -5921,6 +5957,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);

Indents are a bit off.

> +
> +}
> +
> +static void
>  put_ct_helper(struct xlate_ctx *ctx,
>                struct ofpbuf *odp_actions, struct ofpact_conntrack *ofc)
>  {
> @@ -7383,6 +7430,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;
> @@ -7415,6 +7466,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) {
> @@ -7522,6 +7574,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 0a5a528..bb3f9ff 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 14fe6fc..609226a 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -827,6 +827,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.
> @@ -1397,6 +1403,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);
> @@ -5776,6 +5784,7 @@ ofproto_unixctl_dpif_set_dp_features(struct unixctl_conn *conn,
>      ds_destroy(&ds);
>  }
>  
> +

No need to add an empty line.

>  static void
>  ofproto_unixctl_init(void)
>  {
> @@ -5809,7 +5818,7 @@ ofproto_unixctl_init(void)
>      unixctl_command_register("dpif/set-dp-features", "bridge", 1, 3 ,
>                               ofproto_unixctl_dpif_set_dp_features, NULL);
>  }
> -
> +

Don't drop the ^L character.

>  static odp_port_t
>  ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
>  {
> 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 *);
>  
> +

No need to add an empty line.

>  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 92d56b2..22b4722 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -108,7 +108,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

automake.mk uses tabs.

>  
>  EXTRA_DIST += $(FUZZ_REGRESSION_TESTS)
>  FUZZ_REGRESSION_TESTS = \
> diff --git a/tests/bundle.at b/tests/bundle.at
> index 0a4eadc..33fe249 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 6915d43..08349f6 100644
> --- a/tests/dpif-netdev.at
> +++ b/tests/dpif-netdev.at
> @@ -1,4 +1,4 @@
> -AT_BANNER([dpif-netdev])
> +T_BANNER([dpif-netdev])

Something wrong here.

>  
>  m4_divert_push([PREPARE_TESTS])
>  [
> @@ -281,6 +281,7 @@ type=drop rate=1 burst_size=2
>  ])
>  
>  ovs-appctl time/warp 5000
> +sleep 10

Do we really need to sleep for so long ?

>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
> @@ -291,7 +292,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
> -sleep 1  # wait for forwarders process packets
> +# wait for forwarders process packets

Something wrong here too.

>  
>  # Meter 1 is measuring packets, allowing one packet per second with
>  # bursts of one packet, so 4 out of 5 packets should hit the drop
> @@ -320,7 +321,8 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
> -sleep 1  # wait for forwarders process packets
> +
> +sleep 10  # wait for forwarders process packets
>  
>  # Meter 1 is measuring packets, allowing one packet per second with
>  # bursts of one packet, so all 5 of the new packets should hit the drop
> @@ -337,6 +339,15 @@ meter:2 flow_count:1 packet_in_count:10 byte_in_count:600 duration:0.0s bands:
>  0: packet_count:5 byte_count:300
>  ])
>  
> +ovs-appctl time/warp 5000
> +sleep 10
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "dp_meter_drop" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +14
> +])
> +
>  AT_CHECK([cat ovs-vswitchd.log | filter_flow_install | strip_xout_keep_actions], [0], [dnl
>  recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:meter(0),7
>  recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:8
> diff --git a/tests/drop-stats.at b/tests/drop-stats.at
> new file mode 100644
> index 0000000..be8b8ae
> --- /dev/null
> +++ b/tests/drop-stats.at
> @@ -0,0 +1,197 @@
> +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
> +])
> +
> +sleep 1
> +
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +3
> +])
> +
> +
> +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

If possible, it's preferred to use odp format (i.e. declarative) for
packets definition. This allowes user to see what packet is sent without
parsing it by external tools.
Same for all other places.

> +], [0], [ignore])
> +
> +sleep 1
> +
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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])
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_recursion_too_deep" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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)'
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_too_many_resubmit" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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)'
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_stack_too_deep" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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])
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_too_many_mpls_labels" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
> +
> +OVS_VSWITCHD_STOP(["/|WARN|/d"])
> +AT_CLEANUP
> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> index ded2ef0..298aa9a 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -3496,51 +3496,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" \
> @@ -3555,8 +3555,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" \
> @@ -3573,16 +3573,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"
> @@ -4322,11 +4322,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
> @@ -4404,8 +4404,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
> @@ -5751,7 +5751,7 @@ bridge("br0")
>  
>  Final flow: <cleared>
>  Megaflow: <cleared>
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>  ])
>  
>  dnl Now, try again without megaflows:
> @@ -5772,7 +5772,7 @@ bridge("br0")
>  
>  Final flow: <cleared>
>  Megaflow: <cleared>
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>  ])
>  
>  OVS_VSWITCHD_STOP
> @@ -7066,7 +7066,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"])
> @@ -7158,7 +7158,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
> @@ -7832,21 +7832,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
> @@ -7869,7 +7869,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
> @@ -8617,11 +8617,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])
> @@ -9348,7 +9348,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
> @@ -9377,16 +9377,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
> @@ -9711,7 +9711,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)
> @@ -10398,7 +10398,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])
> @@ -10490,7 +10490,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 6ae3470..cd19d41 100644
> --- a/tests/ovs-ofctl.at
> +++ b/tests/ovs-ofctl.at
> @@ -2999,7 +2999,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..7c1b551 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,14 @@ 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 coverage/show | grep "drop_action_unsupported_packet_type" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +2
>  ])
>  
>  # Encap(ethernet) on Ethernet frame -> should be droped
> @@ -587,7 +594,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 +616,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 +777,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 b840dbf..922ba48 100644
> --- a/tests/testsuite.at
> +++ b/tests/testsuite.at
> @@ -82,3 +82,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..385c114 100644
> --- a/tests/tunnel-push-pop.at
> +++ b/tests/tunnel-push-pop.at
> @@ -447,6 +447,27 @@ 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 netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +
> +ovs-appctl time/warp 1200
> +
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "dp_tunnel_pop_error_drop" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
> +
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +ovs-appctl time/warp 1200
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_congestion" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
>  dnl Check GREL3 only accepts non-fragmented packets?
>  AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>  
> @@ -455,7 +476,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 +531,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 55fb1d3..3646c06 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 netdev-dummy/receive p2 'aa55aa550001f8bc124434b6080045000054ba20000040018486010103580101037001004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +sleep 2
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "dp_invalid_port_drop" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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
>
Ben Pfaff Jan. 18, 2019, 6:15 p.m. UTC | #5
On Thu, Jan 17, 2019 at 04:49:20AM +0000, Anju Thomas 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.

Thanks for the patch!  I agree with your motivations--it is useful to
understand why packets are dropped.  I have some comments to add to
Ilya's.

It looks like the drop actions that this formats in
format_odp_drop_action() can't necessarily be parsed by
odp_actions_from_string().  Usually we expect this.  (Probably the syntax
should be adjusted to make parsing more straightforward.)

It looks like xlate_error maps one-to-one to drop reasons (except why is
XLATE_FORWARDING_DISABLED mapped to OVS_DROP_REASON_MAX?), so do we
really want different enumerations?  Mapping back and forth is a bit of
a slog, and there's already a way to translate xlate_errors to strings.

This exports coverage_mutex but I don't see why since nothing new uses
it.  Actually I think all the changes to coverage.[ch] are unneeded.

This adds and removes a number of blank lines, I don't see the value in
that.

Thanks,

Ben.
Anju Thomas Jan. 21, 2019, 6:10 a.m. UTC | #6
Thanks Ilya. I will work on these as well.

-----Original Message-----
From: Ilya Maximets [mailto:i.maximets@samsung.com] 
Sent: Friday, January 18, 2019 5:33 PM
To: Anju Thomas <anju.thomas@ericsson.com>; dev@openvswitch.org
Cc: Keshav Gupta <keshugupta1@gmail.com>; Stokes, Ian <ian.stokes@intel.com>; Ben Pfaff <blp@ovn.org>
Subject: Re: [ovs-dev,v6] Improved Packet Drop Statistics in OVS

Hi.
Thanks for working on this.
Some general comments:

1. This patch consists of two separate features:
   - Reporting the drop reason in flow dumps.
   - Counters for different drop types.
   I still think that there should be patch-set of two separate patches.
   This will simplify the review and allow to apply them separately/speed
   up accepting.

2. There are some issues with the patch formatting. The commit message is
   indented like in the output of 'git show' command.  So, it's double
   shifted after applying in git. Please remove the additional indentation.
   You probably should start using 'git format-patch'.

   Second issue:

   Applying: Improved Packet Drop Statistics in OVS
   .git/rebase-apply/patch:415: new blank line at EOF.
   +
   warning: 1 line adds whitespace errors.

   One more thing is that for better readability it's a common practice to
   limit the width of lines in commit-message to 72 characters. For example,
   you may strip not important fields of the dump-flows output. Like this:

       recirc_id(0),in_port(5),<...>, actions:drop:recursion too deep


Other comments inline.

Best regards, Ilya Maximets.

On 17.01.2019 7:49, Anju Thomas 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. Display these drops in ovs-appctl coverage/show
>     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.
> 
>      The idea is to use the coverage infrastructure to maintain the drops
> 
>     $ ovs-appctl dpctl/dump-flows netdev at ovs-netdev
>     flow-dump from pmd on cpu core: 0
>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:12, bytes:1176, used:0.884s, actions:drop:recursion too deep
> 
>     $ ovs-appctl dpif/dump-flows br-int
>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:25, bytes:2450, used:5.008s, actions:drop:recursion too deep
>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0806), packets:7, bytes:294, used:0.009s, actions:drop:recursion too deep
> 
>    In subsequent commits, we are planning to see if we can use this infrastructure to create a
>    wrapper to clear and display counters as well.
> 
> Co-authored-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Co-authored-by: Keshav Gupta <keshugupta1@gmail.com>
> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Signed-off-by: Keshav Gupta <keshugupta1@gmail.com>
> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Signed-off-by: Keshav Gupta <keshugupta1@gmail.com>
> ---
>  datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
>  lib/coverage.c                                    |   6 +-
>  lib/coverage.h                                    |   3 +-
>  lib/dpif-netdev.c                                 | 105 +++++++++++-
>  lib/dpif.c                                        |  10 +-
>  lib/dpif.h                                        |   3 +
>  lib/netdev-dpdk.c                                 |   4 +
>  lib/odp-execute.c                                 |  50 ++++--
>  lib/odp-execute.h                                 |  10 +-
>  lib/odp-util.c                                    |  54 +++++-
>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>  ofproto/ofproto-dpif-upcall.c                     |   3 +-
>  ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
>  ofproto/ofproto-dpif-xlate.h                      |   4 +
>  ofproto/ofproto-dpif.c                            |  11 +-
>  ofproto/ofproto-dpif.h                            |   8 +-
>  tests/automake.mk                                 |   3 +-
>  tests/bundle.at                                   |   2 +-
>  tests/classifier.at                               |  10 +-
>  tests/dpif-netdev.at                              |  17 +-
>  tests/drop-stats.at                               | 197 ++++++++++++++++++++++
>  tests/ofproto-dpif.at                             | 118 ++++++-------
>  tests/ovs-ofctl.at                                |   2 +-
>  tests/packet-type-aware.at                        |  17 +-
>  tests/testsuite.at                                |   1 +
>  tests/tunnel-push-pop.at                          |  26 ++-
>  tests/tunnel.at                                   |  21 ++-
>  28 files changed, 695 insertions(+), 107 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 9b087f1..b66b46f 100644
> --- a/datapath/linux/compat/include/linux/openvswitch.h
> +++ b/datapath/linux/compat/include/linux/openvswitch.h
> @@ -819,6 +819,56 @@ struct ovs_action_push_eth {
>  };
>  
>  /**
> + * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP.
> + * @OVS_DROP_REASON_OF_PIPELINE: Explicit drop action in the pipeline.
> + * @OVS_DROP_REASON_BRIDGE_NOT_FOUND: Xlation error generated due to
> + * unable to determine bridge.
> + * @OVS_DROP_REASON_RECURSION_TOO_DEEP: Xlation error generated due to
> + * recursion reached maximum depth.
> + * @OVS_DROP_REASON_TOO_MANY_RESUBMITS: Xlation error generated due to
> + * too many resubmits.
> + * @OVS_DROP_REASON_STACK_TOO_DEEP: Xlation error generated due to stack
> + * too deep.
> + * @OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT: Xlation error generated
> + * due to no recirculation context.
> + * @OVS_DROP_REASON_RECIRCULATION_CONFLICT: Xlation error generated due to
> + * conflict in recirculation context.
> + * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to
> + * too many mpls labels.
> + * @OVS_DROP_REASON_INVALID_TUNNEL_METADATA: Xlation error generated due to
> + * invalid tunnel metadata.
> + * @OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE: Xlation error generated due to
> + * unsupported packet type.
> + * @OVS_DROP_REASON_CONGESTION: Xlation error generated due to ecn mismatch
> + * during tunnel decapsulation.
> + * @OVS_DROP_REASON_FORWARDING_DISABLED: Xlation error generated due to
> + * forwarding is disabled.
> + */
> +enum ovs_drop_reason {
> +	OVS_DROP_REASON_OF_PIPELINE = 0,
> +	OVS_DROP_REASON_BRIDGE_NOT_FOUND,
> +	OVS_DROP_REASON_RECURSION_TOO_DEEP,
> +	OVS_DROP_REASON_TOO_MANY_RESUBMITS,
> +	OVS_DROP_REASON_STACK_TOO_DEEP,
> +	OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT,
> +	OVS_DROP_REASON_RECIRCULATION_CONFLICT,
> +	OVS_DROP_REASON_TOO_MANY_MPLS_LABELS,
> +	OVS_DROP_REASON_INVALID_TUNNEL_METADATA,
> +	OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE,
> +	OVS_DROP_REASON_CONGESTION,
> +	OVS_DROP_REASON_FORWARDING_DISABLED,
> +	OVS_DROP_REASON_MAX,
> +};
> +
> +/*
> + * struct ovs_action_drop - %OVS_ACTION_ATTR_DROP action argument.
> + * @drop_reason: Reason for installing drop action.
> + */
> +struct ovs_action_drop {
> +	enum ovs_drop_reason drop_reason;
> +};
> +
> +/**
>   * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
>   *
>   * @OVS_NAT_ATTR_SRC: Flag for Source NAT (mangle source address/port).
> @@ -938,6 +988,7 @@ enum ovs_action_attr {
>  	OVS_ACTION_ATTR_POP_NSH,      /* No argument. */
>  	OVS_ACTION_ATTR_METER,        /* u32 meter number. */
>  	OVS_ACTION_ATTR_CLONE,        /* Nested OVS_CLONE_ATTR_*.  */
> +        OVS_ACTION_ATTR_DROP,         /* explicit drop action. */

This is the linux kernel header. You should use tabs here.

>  
>  #ifndef __KERNEL__
>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
> diff --git a/lib/coverage.c b/lib/coverage.c
> index 6cef826..c9acc37 100644
> --- a/lib/coverage.c
> +++ b/lib/coverage.c
> @@ -32,9 +32,7 @@ VLOG_DEFINE_THIS_MODULE(coverage);
>  static struct coverage_counter **coverage_counters = NULL;
>  static size_t n_coverage_counters = 0;
>  static size_t allocated_coverage_counters = 0;
> -
> -static struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;

This is leftover from the previous versions. No need to expose
'coverage_mutex' in current patch.

> -
> +struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
>  DEFINE_STATIC_PER_THREAD_DATA(long long int, coverage_clear_time, LLONG_MIN);
>  static long long int coverage_run_time = LLONG_MIN;
>  
> @@ -52,7 +50,7 @@ coverage_counter_register(struct coverage_counter* counter)
>      if (n_coverage_counters >= allocated_coverage_counters) {
>          coverage_counters = x2nrealloc(coverage_counters,
>                                         &allocated_coverage_counters,
> -                                       sizeof(struct coverage_counter*));
> +                                       sizeof(struct coverage_counter *));

this change is also not needed.

>      }
>      coverage_counters[n_coverage_counters++] = counter;
>  }
> diff --git a/lib/coverage.h b/lib/coverage.h
> index dea990e..45b1dbd 100644
> --- a/lib/coverage.h
> +++ b/lib/coverage.h
> @@ -54,7 +54,8 @@ struct coverage_counter {
>      unsigned int hr[HR_AVG_LEN];
>  };
>  
> -void coverage_counter_register(struct coverage_counter*);
> +extern struct ovs_mutex coverage_mutex;
> +void coverage_counter_register(struct coverage_counter *);

This is leftover from the previous versions. No need to expose
'coverage_mutex' in current patch.

>  
>  /* Defines COUNTER.  There must be exactly one such definition at file scope
>   * within a program. */
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index be529b6..8749cba 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -100,6 +100,30 @@ enum { MAX_METERS = 65536 };    /* Maximum number of meters. */
>  enum { MAX_BANDS = 8 };         /* Maximum number of bands / meter. */
>  enum { N_METER_LOCKS = 64 };    /* Maximum number of meters. */
>  
> +
> +COVERAGE_DEFINE(drop_action_of_pipeline);
> +COVERAGE_DEFINE(drop_action_bridge_not_found);
> +COVERAGE_DEFINE(drop_action_recursion_too_deep);
> +COVERAGE_DEFINE(drop_action_too_many_resubmit);
> +COVERAGE_DEFINE(drop_action_stack_too_deep);
> +COVERAGE_DEFINE(drop_action_no_recirculation_context);
> +COVERAGE_DEFINE(drop_action_recirculation_conflict);
> +COVERAGE_DEFINE(drop_action_too_many_mpls_labels);
> +COVERAGE_DEFINE(drop_action_invalid_tunnel_metadata);
> +COVERAGE_DEFINE(drop_action_unsupported_packet_type);
> +COVERAGE_DEFINE(drop_action_congestion);
> +COVERAGE_DEFINE(drop_action_forwarding_disabled);

Above counters could be defined inside odp-execute.c. You already have
2 other counters there. In this case there will be no need to introduce
additional 'dp_update_drop_action_counter_cb' callback and pass it around.
All the required logic will also be moved to odp-execute.c.

> +COVERAGE_DEFINE(dp_meter_drop);
> +COVERAGE_DEFINE(dp_upcall_error_drop);
> +COVERAGE_DEFINE(dp_lock_error_drop);
> +COVERAGE_DEFINE(dp_userspace_action_error_drop);
> +COVERAGE_DEFINE(dp_tunnel_push_error_drop);
> +COVERAGE_DEFINE(dp_tunnel_pop_error_drop);
> +COVERAGE_DEFINE(dp_recirc_error_drop);
> +COVERAGE_DEFINE(dp_invalid_port_drop);
> +COVERAGE_DEFINE(dp_invalid_tnl_port_drop);
> +COVERAGE_DEFINE(rx_invalid_packet_drop);

What do you think about renaming above counters to 'datapath_drop_*' ?
IMHO, it'll be more visually recognizable.
Like 'datapath_drop_rx_invalid_packet' or 'datapath_drop_upcall_error'.

> +
>  /* Protects against changes to 'dp_netdevs'. */
>  static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>  
> @@ -829,7 +853,6 @@ static inline bool
>  pmd_perf_metrics_enabled(const struct dp_netdev_pmd_thread *pmd);
>  static void queue_netdev_flow_del(struct dp_netdev_pmd_thread *pmd,
>                                    struct dp_netdev_flow *flow);
> -

No need to remove an empty line.

>  static void
>  emc_cache_init(struct emc_cache *flow_cache)
>  {
> @@ -1388,6 +1411,7 @@ dpif_netdev_init(void)
>      return 0;
>  }
>  
> +

No need to add an empty line.

>  static int
>  dpif_netdev_enumerate(struct sset *all_dps,
>                        const struct dpif_class *dpif_class)
> @@ -5563,7 +5587,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, struct dp_packet_batch *packets_,
>              band = &meter->bands[exceeded_band[j]];
>              band->packet_count += 1;
>              band->byte_count += dp_packet_size(packet);
> -

Please, keep this empty line for readability.

> +            COVERAGE_INC(dp_meter_drop);
>              dp_packet_delete(packet);
>          } else {
>              /* Meter accepts packet. */
> @@ -6320,6 +6344,7 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
>  
>          if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) {
>              dp_packet_delete(packet);
> +            COVERAGE_INC(rx_invalid_packet_drop);
>              continue;
>          }
>  
> @@ -6446,6 +6471,7 @@ handle_packet_upcall(struct dp_netdev_pmd_thread *pmd,
>                               put_actions);
>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>          dp_packet_delete(packet);
> +        COVERAGE_INC(dp_upcall_error_drop);
>          return error;
>      }
>  
> @@ -6577,6 +6603,7 @@ 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);
> +                COVERAGE_INC(dp_lock_error_drop);
>                  upcall_fail_cnt++;
>              }
>          }
> @@ -6846,10 +6873,59 @@ dp_execute_userspace_action(struct dp_netdev_pmd_thread *pmd,
>                                    actions->data, actions->size);
>      } else if (should_steal) {
>          dp_packet_delete(packet);
> +        COVERAGE_INC(dp_userspace_action_error_drop);
>      }
>  }
>  
>  static void
> +dp_update_drop_action_counter_cb(enum ovs_drop_reason drop_reason,
> +                          int delta)

Indents are a bit off.

> +    OVS_NO_THREAD_SAFETY_ANALYSIS

Is it necessary to disable thread safety analysis ?

> +{
> +   switch (drop_reason) {
> +   case OVS_DROP_REASON_OF_PIPELINE:
> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
> +        break;
> +   case OVS_DROP_REASON_BRIDGE_NOT_FOUND:
> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
> +        break;
> +   case OVS_DROP_REASON_RECURSION_TOO_DEEP:
> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
> +        break;
> +   case OVS_DROP_REASON_TOO_MANY_RESUBMITS:
> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
> +        break;
> +   case OVS_DROP_REASON_STACK_TOO_DEEP:
> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
> +        break;
> +   case OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT:
> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
> +        break;
> +   case OVS_DROP_REASON_RECIRCULATION_CONFLICT:
> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
> +        break;
> +   case OVS_DROP_REASON_TOO_MANY_MPLS_LABELS:
> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
> +        break;
> +   case OVS_DROP_REASON_INVALID_TUNNEL_METADATA:
> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
> +        break;
> +   case OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE:
> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
> +        break;
> +   case OVS_DROP_REASON_CONGESTION:
> +        COVERAGE_ADD(drop_action_congestion, delta);
> +        break;
> +   case OVS_DROP_REASON_FORWARDING_DISABLED:
> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
> +        break;
> +   case OVS_DROP_REASON_MAX:
> +   default:
> +        VLOG_ERR("Invalid Drop reason type:%d",drop_reason);

We might want the rate limiting for above message to not have it for each packet.

> +   }
> +}
> +
> +static void
>  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                const struct nlattr *a, bool should_steal)
>      OVS_NO_THREAD_SAFETY_ANALYSIS
> @@ -6860,6 +6936,7 @@ 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;
>  
>      switch ((enum ovs_action_attr)type) {
>      case OVS_ACTION_ATTR_OUTPUT:
> @@ -6901,6 +6978,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                  dp_packet_batch_add(&p->output_pkts, packet);
>              }
>              return;
> +        } else {
> +            COVERAGE_ADD(dp_invalid_port_drop, packets_->count);
>          }
>          break;
>  
> @@ -6910,10 +6989,13 @@ 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. */
> +            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
>              break;
>          }
>          dp_packet_batch_apply_cutlen(packets_);
> -        push_tnl_action(pmd, a, packets_);
> +        if (push_tnl_action(pmd, a, packets_)) {
> +            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
> +        }
>          return;
>  
>      case OVS_ACTION_ATTR_TUNNEL_POP:
> @@ -6933,7 +7015,12 @@ 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) {
> +                    COVERAGE_ADD(dp_tunnel_pop_error_drop, packet_dropped);
> +                }
>                  if (dp_packet_batch_is_empty(packets_)) {
>                      return;
>                  }
> @@ -6947,7 +7034,11 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                  dp_netdev_recirculate(pmd, packets_);
>                  (*depth)--;
>                  return;
> +            } else {
> +                COVERAGE_ADD(dp_invalid_tnl_port_drop, packets_->count);
>              }
> +        } else {
> +            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
>          }
>          break;
>  
> @@ -6991,6 +7082,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>              fat_rwlock_unlock(&dp->upcall_rwlock);
>  
>              return;
> +        } else {
> +            COVERAGE_ADD(dp_lock_error_drop, packets_->count);
>          }
>          break;
>  
> @@ -7013,6 +7106,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>              (*depth)--;
>  
>              return;
> +        } else {
> +            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
>          }

'Else' condition is not needed here as we're returning inside the 'if'.
This is actually applicable to most of other places in this function,
but in this one we already have a warning below without 'else' condition.

>  
>          VLOG_WARN("Packet dropped. Max recirculation depth exceeded.");
> @@ -7167,6 +7262,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();
>      }
> @@ -7183,7 +7279,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_action_counter_cb);

As I already wrote, we'll not need to have a callback function if you'll
move the 'drop_action_*' counters to odp-execute.c.

>  }
>  
>  struct dp_netdev_ct_dump {
> diff --git a/lib/dpif.c b/lib/dpif.c
> index e35f111..21f9f54 100644
> --- a/lib/dpif.c
> +++ b/lib/dpif.c
> @@ -1273,6 +1273,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();
> @@ -1295,7 +1296,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;
>  }
>  
> @@ -1879,6 +1880,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,
> @@ -1976,3 +1983,4 @@ dpif_meter_del(struct dpif *dpif, ofproto_meter_id meter_id,
>      }
>      return error;
>  }
> +
> diff --git a/lib/dpif.h b/lib/dpif.h
> index 475d5a6..e799da8 100644
> --- a/lib/dpif.h
> +++ b/lib/dpif.h
> @@ -888,6 +888,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 320422b..0c3bc9c 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -2395,6 +2395,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);

This is the unrelated bug fix that should be sent as a separate patch.
In this case we'll be able to apply it and backport to previous versions.

Adding Ian to CC.

>          dp_packet_delete_batch(batch, true);
>          return;
>      }
> diff --git a/lib/odp-execute.c b/lib/odp-execute.c
> index 3b6890e..c0db93f 100644
> --- a/lib/odp-execute.c
> +++ b/lib/odp-execute.c
> @@ -25,6 +25,7 @@
>  #include <stdlib.h>
>  #include <string.h>
>  
> +#include "coverage.h"
>  #include "dp-packet.h"
>  #include "dpif.h"
>  #include "netlink.h"
> @@ -37,6 +38,10 @@
>  #include "csum.h"
>  #include "conntrack.h"
>  
> +COVERAGE_DEFINE(dp_sample_error_drop);
> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
> +
> +
>  /* Masked copy of an ethernet address. 'src' is already properly masked. */
>  static void
>  ether_addr_copy_masked(struct eth_addr *dst, const struct eth_addr src,
> @@ -575,7 +580,9 @@ 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_action_counter_cb
> +                     dp_update_drop_action_counter)
>  {
>      const struct nlattr *subactions = NULL;
>      const struct nlattr *a;
> @@ -589,6 +596,7 @@ 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) {
> +                    COVERAGE_ADD(dp_sample_error_drop, 1);
>                      dp_packet_delete(packet);
>                  }
>                  return;
> @@ -616,13 +624,16 @@ 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_action_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_action_counter_cb
> +                      dp_update_drop_action_counter)
>  {
>      if (!steal) {
>          /* The 'actions' may modify the packet, but the modification
> @@ -634,11 +645,12 @@ 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);
> -    }
> -    else {
> +                        nl_attr_get_size(actions), dp_execute_action,
> +                        dp_update_drop_action_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_action_counter);
>      }
>  }
>  
> @@ -673,6 +685,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,12 +712,15 @@ 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_action_counter_cb
> +                       dp_update_drop_action_counter)
>  {
>      struct dp_packet *packet;
>      const struct nlattr *a;
>      unsigned int left;
>  
> +

No need to add an empty line.

>      NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
>          int type = nl_attr_type(a);
>          bool last_action = (left <= NLA_ALIGN(a->nla_len));
> @@ -822,7 +838,8 @@ 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_action_counter);
>              }
>  
>              if (last_action) {
> @@ -845,7 +862,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_action_counter);
>              if (last_action) {
>                  /* We do not need to free the packets. odp_execute_clone() has
>                   * stolen them.  */
> @@ -889,6 +907,7 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
>                  if (pop_nsh(packet)) {
>                      dp_packet_batch_refill(batch, packet, i);
>                  } else {
> +                    COVERAGE_INC(dp_nsh_decap_error_drop);
>                      dp_packet_delete(packet);
>                  }
>              }
> @@ -900,6 +919,17 @@ 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_action_counter) {
> +                 dp_update_drop_action_counter(drop_reason, 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..a84ecbb 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_action_counter_cb) (
> +                                           enum  ovs_drop_reason drop_reason,
> +                                           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,7 @@ 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_action_counter_cb
> +                             dp_update_drop_action_counter_cb);
>  #endif
> diff --git a/lib/odp-util.c b/lib/odp-util.c
> index 0491bed..9754fd5 100644
> --- a/lib/odp-util.c
> +++ b/lib/odp-util.c
> @@ -131,6 +131,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:
> @@ -345,6 +346,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));

As I already wrote for the previous versions it'll be good to add
some visible markers around the drop reason. Spaces in flow dumps
are usually not used and it could add some complications for the
parsing scripts. You may use brackets like "drop(stack too deep)"
or replace the spaces with '-' or '_' like "drop:stack_too_deep".

> +}
> +
>  static void
>  format_odp_push_nsh_action(struct ds *ds,
>                             const struct nsh_hdr *nsh_hdr)
> @@ -1181,6 +1225,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:
> @@ -2427,8 +2474,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"))) {

Why you're adding parse only for this type of drop action ?

> +        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 7da3175..69ed7b8 100644
> --- a/ofproto/ofproto-dpif-sflow.c
> +++ b/ofproto/ofproto-dpif-sflow.c
> @@ -1222,6 +1222,7 @@ dpif_sflow_read_actions(const struct flow *flow,
>          case OVS_ACTION_ATTR_PUSH_NSH:
>          case OVS_ACTION_ATTR_POP_NSH:
>          case OVS_ACTION_ATTR_UNSPEC:
> +        case OVS_ACTION_ATTR_DROP:
>          case __OVS_ACTION_ATTR_MAX:
>          default:
>              break;
> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
> index dc30824..b1ce2ec 100644
> --- a/ofproto/ofproto-dpif-upcall.c
> +++ b/ofproto/ofproto-dpif-upcall.c
> @@ -1154,7 +1154,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)
>  {
> @@ -1244,6 +1244,7 @@ upcall_xlate(struct udpif *udpif, struct upcall *upcall,
>      if (upcall->type == MISS_UPCALL) {
>          upcall->ukey = ukey_create_from_upcall(upcall, wc);
>      }
> +    return xerr;

The only user of this function is 'process_upcall' and it does not check
the result.

>  }
>  
>  static void
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index 839fddd..32c4edf 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);
>  
> @@ -5921,6 +5957,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);

Indents are a bit off.

> +
> +}
> +
> +static void
>  put_ct_helper(struct xlate_ctx *ctx,
>                struct ofpbuf *odp_actions, struct ofpact_conntrack *ofc)
>  {
> @@ -7383,6 +7430,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;
> @@ -7415,6 +7466,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) {
> @@ -7522,6 +7574,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 0a5a528..bb3f9ff 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 14fe6fc..609226a 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -827,6 +827,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.
> @@ -1397,6 +1403,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);
> @@ -5776,6 +5784,7 @@ ofproto_unixctl_dpif_set_dp_features(struct unixctl_conn *conn,
>      ds_destroy(&ds);
>  }
>  
> +

No need to add an empty line.

>  static void
>  ofproto_unixctl_init(void)
>  {
> @@ -5809,7 +5818,7 @@ ofproto_unixctl_init(void)
>      unixctl_command_register("dpif/set-dp-features", "bridge", 1, 3 ,
>                               ofproto_unixctl_dpif_set_dp_features, NULL);
>  }
> -

> +

Don't drop the ^L character.

>  static odp_port_t
>  ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
>  {
> 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 *);
>  
> +

No need to add an empty line.

>  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 92d56b2..22b4722 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -108,7 +108,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

automake.mk uses tabs.

>  
>  EXTRA_DIST += $(FUZZ_REGRESSION_TESTS)
>  FUZZ_REGRESSION_TESTS = \
> diff --git a/tests/bundle.at b/tests/bundle.at
> index 0a4eadc..33fe249 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 6915d43..08349f6 100644
> --- a/tests/dpif-netdev.at
> +++ b/tests/dpif-netdev.at
> @@ -1,4 +1,4 @@
> -AT_BANNER([dpif-netdev])
> +T_BANNER([dpif-netdev])

Something wrong here.

>  
>  m4_divert_push([PREPARE_TESTS])
>  [
> @@ -281,6 +281,7 @@ type=drop rate=1 burst_size=2
>  ])
>  
>  ovs-appctl time/warp 5000
> +sleep 10

Do we really need to sleep for so long ?

>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
> @@ -291,7 +292,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
> -sleep 1  # wait for forwarders process packets
> +# wait for forwarders process packets

Something wrong here too.

>  
>  # Meter 1 is measuring packets, allowing one packet per second with
>  # bursts of one packet, so 4 out of 5 packets should hit the drop
> @@ -320,7 +321,8 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
> -sleep 1  # wait for forwarders process packets
> +
> +sleep 10  # wait for forwarders process packets
>  
>  # Meter 1 is measuring packets, allowing one packet per second with
>  # bursts of one packet, so all 5 of the new packets should hit the drop
> @@ -337,6 +339,15 @@ meter:2 flow_count:1 packet_in_count:10 byte_in_count:600 duration:0.0s bands:
>  0: packet_count:5 byte_count:300
>  ])
>  
> +ovs-appctl time/warp 5000
> +sleep 10
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "dp_meter_drop" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +14
> +])
> +
>  AT_CHECK([cat ovs-vswitchd.log | filter_flow_install | strip_xout_keep_actions], [0], [dnl
>  recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:meter(0),7
>  recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:8
> diff --git a/tests/drop-stats.at b/tests/drop-stats.at
> new file mode 100644
> index 0000000..be8b8ae
> --- /dev/null
> +++ b/tests/drop-stats.at
> @@ -0,0 +1,197 @@
> +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
> +])
> +
> +sleep 1
> +
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +3
> +])
> +
> +
> +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

If possible, it's preferred to use odp format (i.e. declarative) for
packets definition. This allowes user to see what packet is sent without
parsing it by external tools.
Same for all other places.

> +], [0], [ignore])
> +
> +sleep 1
> +
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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])
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_recursion_too_deep" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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)'
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_too_many_resubmit" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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)'
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_stack_too_deep" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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])
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_too_many_mpls_labels" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
> +
> +OVS_VSWITCHD_STOP(["/|WARN|/d"])
> +AT_CLEANUP
> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> index ded2ef0..298aa9a 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -3496,51 +3496,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" \
> @@ -3555,8 +3555,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" \
> @@ -3573,16 +3573,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"
> @@ -4322,11 +4322,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
> @@ -4404,8 +4404,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
> @@ -5751,7 +5751,7 @@ bridge("br0")
>  
>  Final flow: <cleared>
>  Megaflow: <cleared>
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>  ])
>  
>  dnl Now, try again without megaflows:
> @@ -5772,7 +5772,7 @@ bridge("br0")
>  
>  Final flow: <cleared>
>  Megaflow: <cleared>
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>  ])
>  
>  OVS_VSWITCHD_STOP
> @@ -7066,7 +7066,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"])
> @@ -7158,7 +7158,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
> @@ -7832,21 +7832,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
> @@ -7869,7 +7869,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
> @@ -8617,11 +8617,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])
> @@ -9348,7 +9348,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
> @@ -9377,16 +9377,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
> @@ -9711,7 +9711,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)
> @@ -10398,7 +10398,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])
> @@ -10490,7 +10490,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 6ae3470..cd19d41 100644
> --- a/tests/ovs-ofctl.at
> +++ b/tests/ovs-ofctl.at
> @@ -2999,7 +2999,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..7c1b551 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,14 @@ 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 coverage/show | grep "drop_action_unsupported_packet_type" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +2
>  ])
>  
>  # Encap(ethernet) on Ethernet frame -> should be droped
> @@ -587,7 +594,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 +616,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 +777,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 b840dbf..922ba48 100644
> --- a/tests/testsuite.at
> +++ b/tests/testsuite.at
> @@ -82,3 +82,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..385c114 100644
> --- a/tests/tunnel-push-pop.at
> +++ b/tests/tunnel-push-pop.at
> @@ -447,6 +447,27 @@ 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 netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +
> +ovs-appctl time/warp 1200
> +
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "dp_tunnel_pop_error_drop" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
> +
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +ovs-appctl time/warp 1200
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_congestion" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
>  dnl Check GREL3 only accepts non-fragmented packets?
>  AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>  
> @@ -455,7 +476,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 +531,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 55fb1d3..3646c06 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 netdev-dummy/receive p2 'aa55aa550001f8bc124434b6080045000054ba20000040018486010103580101037001004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +sleep 2
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "dp_invalid_port_drop" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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
>
Anju Thomas Jan. 21, 2019, 7:01 a.m. UTC | #7
Hi Ben,

Thank you for the comments. Replies inline

Regards
Anju
-----Original Message-----
From: Ben Pfaff [mailto:blp@ovn.org] 
Sent: Friday, January 18, 2019 11:45 PM
To: Anju Thomas <anju.thomas@ericsson.com>
Cc: dev@openvswitch.org; Keshav Gupta <keshugupta1@gmail.com>
Subject: Re: [ovs-dev] [PATCH v6] Improved Packet Drop Statistics in OVS

On Thu, Jan 17, 2019 at 04:49:20AM +0000, Anju Thomas 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.

Thanks for the patch!  I agree with your motivations--it is useful to understand why packets are dropped.  I have some comments to add to Ilya's.

It looks like the drop actions that this formats in
format_odp_drop_action() can't necessarily be parsed by odp_actions_from_string().  Usually we expect this.  (Probably the syntax should be adjusted to make parsing more straightforward.)
<Anju>  odp_actions_from string is used to convert string to action for parsing .But we are using the drop action reason only fo display. We do not want to provide an option for its parsing. Is my understanding correct.


It looks like xlate_error maps one-to-one to drop reasons (except why is XLATE_FORWARDING_DISABLED mapped to OVS_DROP_REASON_MAX?), so do we really want different enumerations?  Mapping back and forth is a bit of a slog, and there's already a way to translate xlate_errors to strings.

<Anju> .We just kept it separate for better code readability and also that XLATE_** error were more in the ofproto layer and we wanted something in the datapath . If you believe that that is not justified compared to the slogging then I will modify this .  What do you say ?

This exports coverage_mutex but I don't see why since nothing new uses it.  Actually I think all the changes to coverage.[ch] are unneeded.
<Anju> You are right. I will change this
This adds and removes a number of blank lines, I don't see the value in that.

Thanks,

Ben.
Ben Pfaff Jan. 23, 2019, 12:27 a.m. UTC | #8
On Mon, Jan 21, 2019 at 07:01:33AM +0000, Anju Thomas wrote:
> On Thu, Jan 17, 2019 at 04:49:20AM +0000, Anju Thomas 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.
> 
> Thanks for the patch!  I agree with your motivations--it is useful to understand why packets are dropped.  I have some comments to add to Ilya's.
> 
> It looks like the drop actions that this formats in
> format_odp_drop_action() can't necessarily be parsed by odp_actions_from_string().  Usually we expect this.  (Probably the syntax should be adjusted to make parsing more straightforward.)
> <Anju>  odp_actions_from string is used to convert string to action for parsing .But we are using the drop action reason only fo display. We do not want to provide an option for its parsing. Is my understanding correct.

Why not?  Ordinarily, anything that can be output can be input.  It is
useful for testing to be able to input datapath actions.

> It looks like xlate_error maps one-to-one to drop reasons (except why is XLATE_FORWARDING_DISABLED mapped to OVS_DROP_REASON_MAX?), so do we really want different enumerations?  Mapping back and forth is a bit of a slog, and there's already a way to translate xlate_errors to strings.
> 
> <Anju> .We just kept it separate for better code readability and also that XLATE_** error were more in the ofproto layer and we wanted something in the datapath . If you believe that that is not justified compared to the slogging then I will modify this .  What do you say ?

I'd leave them the same for now, until there's a need for them to be
different.
Anju Thomas Jan. 23, 2019, 9:52 a.m. UTC | #9
Hi Ben,

Thanks for reply. I just have one doubt . Reply inline 

Regards
Anju

-----Original Message-----
From: Ben Pfaff [mailto:blp@ovn.org] 
Sent: Wednesday, January 23, 2019 5:57 AM
To: Anju Thomas <anju.thomas@ericsson.com>
Cc: dev@openvswitch.org; Keshav Gupta <keshugupta1@gmail.com>
Subject: Re: [ovs-dev] [PATCH v6] Improved Packet Drop Statistics in OVS

On Mon, Jan 21, 2019 at 07:01:33AM +0000, Anju Thomas wrote:
> On Thu, Jan 17, 2019 at 04:49:20AM +0000, Anju Thomas 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.
> 
> Thanks for the patch!  I agree with your motivations--it is useful to understand why packets are dropped.  I have some comments to add to Ilya's.
> 
> It looks like the drop actions that this formats in
> format_odp_drop_action() can't necessarily be parsed by 
> odp_actions_from_string().  Usually we expect this.  (Probably the syntax should be adjusted to make parsing more straightforward.) <Anju>  odp_actions_from string is used to convert string to action for parsing .But we are using the drop action reason only fo display. We do not want to provide an option for its parsing. Is my understanding correct.

Why not?  Ordinarily, anything that can be output can be input.  It is useful for testing to be able to input datapath actions.

Agreed. Ideally yes. But in this case the error are set due to some error during translation like *bridge not found 
*too many resubmits 
*recursion too deep 
*too many resubmits 
*sack too deeo
*no recirculation context
*too many mpls labels
* invalid tunnel metadata
* unsupported packet type
* ecn mismatch at tunnel decapsulation
* forwarding disabled
* pipeline drop

Except pipeline drop I don’t understand how it will make sense to inject others as input . Can you explain if I am missing something ?


> It looks like xlate_error maps one-to-one to drop reasons (except why is XLATE_FORWARDING_DISABLED mapped to OVS_DROP_REASON_MAX?), so do we really want different enumerations?  Mapping back and forth is a bit of a slog, and there's already a way to translate xlate_errors to strings.
> 
> <Anju> .We just kept it separate for better code readability and also that XLATE_** error were more in the ofproto layer and we wanted something in the datapath . If you believe that that is not justified compared to the slogging then I will modify this .  What do you say ?

I'd leave them the same for now, until there's a need for them to be different.


<Anju> Sure. I will keep them same error viz XLATE_** codes.
Anju Thomas Jan. 23, 2019, 1:53 p.m. UTC | #10
Hi Ilya ,
Some queries w.r.t your comments .

Regards
Anju

-----Original Message-----
From: Ilya Maximets [mailto:i.maximets@samsung.com] 
Sent: Friday, January 18, 2019 5:33 PM
To: Anju Thomas <anju.thomas@ericsson.com>; dev@openvswitch.org
Cc: Keshav Gupta <keshugupta1@gmail.com>; Stokes, Ian <ian.stokes@intel.com>; Ben Pfaff <blp@ovn.org>
Subject: Re: [ovs-dev,v6] Improved Packet Drop Statistics in OVS

Hi.
Thanks for working on this.
Some general comments:

1. This patch consists of two separate features:
   - Reporting the drop reason in flow dumps.
   - Counters for different drop types.
   I still think that there should be patch-set of two separate patches.
   This will simplify the review and allow to apply them separately/speed
   up accepting.

2. There are some issues with the patch formatting. The commit message is
   indented like in the output of 'git show' command.  So, it's double
   shifted after applying in git. Please remove the additional indentation.
   You probably should start using 'git format-patch'.

   Second issue:

   Applying: Improved Packet Drop Statistics in OVS
   .git/rebase-apply/patch:415: new blank line at EOF.
   +
   warning: 1 line adds whitespace errors.

   One more thing is that for better readability it's a common practice to
   limit the width of lines in commit-message to 72 characters. For example,
   you may strip not important fields of the dump-flows output. Like this:

       recirc_id(0),in_port(5),<...>, actions:drop:recursion too deep


Other comments inline.

<Anju> I use 
a)git format-patch -1 -s 
b)  ./utilities/checkpatch.py -1 <patch name>

Are these the correct parameters that I should be using ?

Best regards, Ilya Maximets. 

On 17.01.2019 7:49, Anju Thomas 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. Display these drops in ovs-appctl coverage/show
>     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.
> 
>      The idea is to use the coverage infrastructure to maintain the drops
> 
>     $ ovs-appctl dpctl/dump-flows netdev at ovs-netdev
>     flow-dump from pmd on cpu core: 0
>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:12, bytes:1176, used:0.884s, actions:drop:recursion too deep
> 
>     $ ovs-appctl dpif/dump-flows br-int
>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:25, bytes:2450, used:5.008s, actions:drop:recursion too deep
>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0806), packets:7, bytes:294, used:0.009s, actions:drop:recursion too deep
> 
>    In subsequent commits, we are planning to see if we can use this infrastructure to create a
>    wrapper to clear and display counters as well.
> 
> Co-authored-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Co-authored-by: Keshav Gupta <keshugupta1@gmail.com>
> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Signed-off-by: Keshav Gupta <keshugupta1@gmail.com>
> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
> Signed-off-by: Keshav Gupta <keshugupta1@gmail.com>
> ---
>  datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
>  lib/coverage.c                                    |   6 +-
>  lib/coverage.h                                    |   3 +-
>  lib/dpif-netdev.c                                 | 105 +++++++++++-
>  lib/dpif.c                                        |  10 +-
>  lib/dpif.h                                        |   3 +
>  lib/netdev-dpdk.c                                 |   4 +
>  lib/odp-execute.c                                 |  50 ++++--
>  lib/odp-execute.h                                 |  10 +-
>  lib/odp-util.c                                    |  54 +++++-
>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>  ofproto/ofproto-dpif-upcall.c                     |   3 +-
>  ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
>  ofproto/ofproto-dpif-xlate.h                      |   4 +
>  ofproto/ofproto-dpif.c                            |  11 +-
>  ofproto/ofproto-dpif.h                            |   8 +-
>  tests/automake.mk                                 |   3 +-
>  tests/bundle.at                                   |   2 +-
>  tests/classifier.at                               |  10 +-
>  tests/dpif-netdev.at                              |  17 +-
>  tests/drop-stats.at                               | 197 ++++++++++++++++++++++
>  tests/ofproto-dpif.at                             | 118 ++++++-------
>  tests/ovs-ofctl.at                                |   2 +-
>  tests/packet-type-aware.at                        |  17 +-
>  tests/testsuite.at                                |   1 +
>  tests/tunnel-push-pop.at                          |  26 ++-
>  tests/tunnel.at                                   |  21 ++-
>  28 files changed, 695 insertions(+), 107 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 9b087f1..b66b46f 100644
> --- a/datapath/linux/compat/include/linux/openvswitch.h
> +++ b/datapath/linux/compat/include/linux/openvswitch.h
> @@ -819,6 +819,56 @@ struct ovs_action_push_eth {
>  };
>  
>  /**
> + * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP.
> + * @OVS_DROP_REASON_OF_PIPELINE: Explicit drop action in the pipeline.
> + * @OVS_DROP_REASON_BRIDGE_NOT_FOUND: Xlation error generated due to
> + * unable to determine bridge.
> + * @OVS_DROP_REASON_RECURSION_TOO_DEEP: Xlation error generated due to
> + * recursion reached maximum depth.
> + * @OVS_DROP_REASON_TOO_MANY_RESUBMITS: Xlation error generated due to
> + * too many resubmits.
> + * @OVS_DROP_REASON_STACK_TOO_DEEP: Xlation error generated due to stack
> + * too deep.
> + * @OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT: Xlation error generated
> + * due to no recirculation context.
> + * @OVS_DROP_REASON_RECIRCULATION_CONFLICT: Xlation error generated due to
> + * conflict in recirculation context.
> + * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to
> + * too many mpls labels.
> + * @OVS_DROP_REASON_INVALID_TUNNEL_METADATA: Xlation error generated due to
> + * invalid tunnel metadata.
> + * @OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE: Xlation error generated due to
> + * unsupported packet type.
> + * @OVS_DROP_REASON_CONGESTION: Xlation error generated due to ecn mismatch
> + * during tunnel decapsulation.
> + * @OVS_DROP_REASON_FORWARDING_DISABLED: Xlation error generated due to
> + * forwarding is disabled.
> + */
> +enum ovs_drop_reason {
> +	OVS_DROP_REASON_OF_PIPELINE = 0,
> +	OVS_DROP_REASON_BRIDGE_NOT_FOUND,
> +	OVS_DROP_REASON_RECURSION_TOO_DEEP,
> +	OVS_DROP_REASON_TOO_MANY_RESUBMITS,
> +	OVS_DROP_REASON_STACK_TOO_DEEP,
> +	OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT,
> +	OVS_DROP_REASON_RECIRCULATION_CONFLICT,
> +	OVS_DROP_REASON_TOO_MANY_MPLS_LABELS,
> +	OVS_DROP_REASON_INVALID_TUNNEL_METADATA,
> +	OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE,
> +	OVS_DROP_REASON_CONGESTION,
> +	OVS_DROP_REASON_FORWARDING_DISABLED,
> +	OVS_DROP_REASON_MAX,
> +};
> +
> +/*
> + * struct ovs_action_drop - %OVS_ACTION_ATTR_DROP action argument.
> + * @drop_reason: Reason for installing drop action.
> + */
> +struct ovs_action_drop {
> +	enum ovs_drop_reason drop_reason;
> +};
> +
> +/**
>   * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
>   *
>   * @OVS_NAT_ATTR_SRC: Flag for Source NAT (mangle source address/port).
> @@ -938,6 +988,7 @@ enum ovs_action_attr {
>  	OVS_ACTION_ATTR_POP_NSH,      /* No argument. */
>  	OVS_ACTION_ATTR_METER,        /* u32 meter number. */
>  	OVS_ACTION_ATTR_CLONE,        /* Nested OVS_CLONE_ATTR_*.  */
> +        OVS_ACTION_ATTR_DROP,         /* explicit drop action. */

This is the linux kernel header. You should use tabs here.
>  
>  #ifndef __KERNEL__
>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
> diff --git a/lib/coverage.c b/lib/coverage.c
> index 6cef826..c9acc37 100644
> --- a/lib/coverage.c
> +++ b/lib/coverage.c
> @@ -32,9 +32,7 @@ VLOG_DEFINE_THIS_MODULE(coverage);
>  static struct coverage_counter **coverage_counters = NULL;
>  static size_t n_coverage_counters = 0;
>  static size_t allocated_coverage_counters = 0;
> -
> -static struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;

This is leftover from the previous versions. No need to expose
'coverage_mutex' in current patch.
> -
> +struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
>  DEFINE_STATIC_PER_THREAD_DATA(long long int, coverage_clear_time, LLONG_MIN);
>  static long long int coverage_run_time = LLONG_MIN;
>  
> @@ -52,7 +50,7 @@ coverage_counter_register(struct coverage_counter* counter)
>      if (n_coverage_counters >= allocated_coverage_counters) {
>          coverage_counters = x2nrealloc(coverage_counters,
>                                         &allocated_coverage_counters,
> -                                       sizeof(struct coverage_counter*));
> +                                       sizeof(struct coverage_counter *));

this change is also not needed.
>      }
>      coverage_counters[n_coverage_counters++] = counter;
>  }
> diff --git a/lib/coverage.h b/lib/coverage.h
> index dea990e..45b1dbd 100644
> --- a/lib/coverage.h
> +++ b/lib/coverage.h
> @@ -54,7 +54,8 @@ struct coverage_counter {
>      unsigned int hr[HR_AVG_LEN];
>  };
>  
> -void coverage_counter_register(struct coverage_counter*);
> +extern struct ovs_mutex coverage_mutex;
> +void coverage_counter_register(struct coverage_counter *);

This is leftover from the previous versions. No need to expose
'coverage_mutex' in current patch.
>  
>  /* Defines COUNTER.  There must be exactly one such definition at file scope
>   * within a program. */
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index be529b6..8749cba 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -100,6 +100,30 @@ enum { MAX_METERS = 65536 };    /* Maximum number of meters. */
>  enum { MAX_BANDS = 8 };         /* Maximum number of bands / meter. */
>  enum { N_METER_LOCKS = 64 };    /* Maximum number of meters. */
>  
> +
> +COVERAGE_DEFINE(drop_action_of_pipeline);
> +COVERAGE_DEFINE(drop_action_bridge_not_found);
> +COVERAGE_DEFINE(drop_action_recursion_too_deep);
> +COVERAGE_DEFINE(drop_action_too_many_resubmit);
> +COVERAGE_DEFINE(drop_action_stack_too_deep);
> +COVERAGE_DEFINE(drop_action_no_recirculation_context);
> +COVERAGE_DEFINE(drop_action_recirculation_conflict);
> +COVERAGE_DEFINE(drop_action_too_many_mpls_labels);
> +COVERAGE_DEFINE(drop_action_invalid_tunnel_metadata);
> +COVERAGE_DEFINE(drop_action_unsupported_packet_type);
> +COVERAGE_DEFINE(drop_action_congestion);
> +COVERAGE_DEFINE(drop_action_forwarding_disabled);

Above counters could be defined inside odp-execute.c. You already have
2 other counters there. In this case there will be no need to introduce
additional 'dp_update_drop_action_counter_cb' callback and pass it around.
All the required logic will also be moved to odp-execute.c.

<Anju> But I want the callback to be NULL when called from dpif_execute_with_help

> +COVERAGE_DEFINE(dp_meter_drop);
> +COVERAGE_DEFINE(dp_upcall_error_drop);
> +COVERAGE_DEFINE(dp_lock_error_drop);
> +COVERAGE_DEFINE(dp_userspace_action_error_drop);
> +COVERAGE_DEFINE(dp_tunnel_push_error_drop);
> +COVERAGE_DEFINE(dp_tunnel_pop_error_drop);
> +COVERAGE_DEFINE(dp_recirc_error_drop);
> +COVERAGE_DEFINE(dp_invalid_port_drop);
> +COVERAGE_DEFINE(dp_invalid_tnl_port_drop);
> +COVERAGE_DEFINE(rx_invalid_packet_drop);

What do you think about renaming above counters to 'datapath_drop_*' ?
IMHO, it'll be more visually recognizable.
Like 'datapath_drop_rx_invalid_packet' or 'datapath_drop_upcall_error'.
 
<Anju> Do you mean instead of using dp_* to use datapath_*??
> +
>  /* Protects against changes to 'dp_netdevs'. */
>  static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>  
> @@ -829,7 +853,6 @@ static inline bool
>  pmd_perf_metrics_enabled(const struct dp_netdev_pmd_thread *pmd);
>  static void queue_netdev_flow_del(struct dp_netdev_pmd_thread *pmd,
>                                    struct dp_netdev_flow *flow);
> -

No need to remove an empty line.

>  static void
>  emc_cache_init(struct emc_cache *flow_cache)
>  {
> @@ -1388,6 +1411,7 @@ dpif_netdev_init(void)
>      return 0;
>  }
>  
> +

No need to add an empty line.
>  static int
>  dpif_netdev_enumerate(struct sset *all_dps,
>                        const struct dpif_class *dpif_class)
> @@ -5563,7 +5587,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, struct dp_packet_batch *packets_,
>              band = &meter->bands[exceeded_band[j]];
>              band->packet_count += 1;
>              band->byte_count += dp_packet_size(packet);
> -
Please, keep this empty line for readability.

> +            COVERAGE_INC(dp_meter_drop);
>              dp_packet_delete(packet);
>          } else {
>              /* Meter accepts packet. */
> @@ -6320,6 +6344,7 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
>  
>          if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) {
>              dp_packet_delete(packet);
> +            COVERAGE_INC(rx_invalid_packet_drop);
>              continue;
>          }
>  
> @@ -6446,6 +6471,7 @@ handle_packet_upcall(struct dp_netdev_pmd_thread *pmd,
>                               put_actions);
>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>          dp_packet_delete(packet);
> +        COVERAGE_INC(dp_upcall_error_drop);
>          return error;
>      }
>  
> @@ -6577,6 +6603,7 @@ 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);
> +                COVERAGE_INC(dp_lock_error_drop);
>                  upcall_fail_cnt++;
>              }
>          }
> @@ -6846,10 +6873,59 @@ dp_execute_userspace_action(struct dp_netdev_pmd_thread *pmd,
>                                    actions->data, actions->size);
>      } else if (should_steal) {
>          dp_packet_delete(packet);
> +        COVERAGE_INC(dp_userspace_action_error_drop);
>      }
>  }
>  
>  static void
> +dp_update_drop_action_counter_cb(enum ovs_drop_reason drop_reason,
> +                          int delta)

Indents are a bit off.
> +    OVS_NO_THREAD_SAFETY_ANALYSIS

Is it necessary to disable thread safety analysis ?
<Anju> Since we are using COVERAGE infra which is defining thread specific data, i thought we might need to use this. Is my understanding incorrect?
> +{
> +   switch (drop_reason) {
> +   case OVS_DROP_REASON_OF_PIPELINE:
> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
> +        break;
> +   case OVS_DROP_REASON_BRIDGE_NOT_FOUND:
> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
> +        break;
> +   case OVS_DROP_REASON_RECURSION_TOO_DEEP:
> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
> +        break;
> +   case OVS_DROP_REASON_TOO_MANY_RESUBMITS:
> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
> +        break;
> +   case OVS_DROP_REASON_STACK_TOO_DEEP:
> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
> +        break;
> +   case OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT:
> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
> +        break;
> +   case OVS_DROP_REASON_RECIRCULATION_CONFLICT:
> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
> +        break;
> +   case OVS_DROP_REASON_TOO_MANY_MPLS_LABELS:
> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
> +        break;
> +   case OVS_DROP_REASON_INVALID_TUNNEL_METADATA:
> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
> +        break;
> +   case OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE:
> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
> +        break;
> +   case OVS_DROP_REASON_CONGESTION:
> +        COVERAGE_ADD(drop_action_congestion, delta);
> +        break;
> +   case OVS_DROP_REASON_FORWARDING_DISABLED:
> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
> +        break;
> +   case OVS_DROP_REASON_MAX:
> +   default:
> +        VLOG_ERR("Invalid Drop reason type:%d",drop_reason);

We might want the rate limiting for above message to not have it for each packet.
> +   }
> +}
> +
> +static void
>  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                const struct nlattr *a, bool should_steal)
>      OVS_NO_THREAD_SAFETY_ANALYSIS
> @@ -6860,6 +6936,7 @@ 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;
>  
>      switch ((enum ovs_action_attr)type) {
>      case OVS_ACTION_ATTR_OUTPUT:
> @@ -6901,6 +6978,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                  dp_packet_batch_add(&p->output_pkts, packet);
>              }
>              return;
> +        } else {
> +            COVERAGE_ADD(dp_invalid_port_drop, packets_->count);
>          }
>          break;
>  
> @@ -6910,10 +6989,13 @@ 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. */
> +            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
>              break;
>          }
>          dp_packet_batch_apply_cutlen(packets_);
> -        push_tnl_action(pmd, a, packets_);
> +        if (push_tnl_action(pmd, a, packets_)) {
> +            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
> +        }
>          return;
>  
>      case OVS_ACTION_ATTR_TUNNEL_POP:
> @@ -6933,7 +7015,12 @@ 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) {
> +                    COVERAGE_ADD(dp_tunnel_pop_error_drop, packet_dropped);
> +                }
>                  if (dp_packet_batch_is_empty(packets_)) {
>                      return;
>                  }
> @@ -6947,7 +7034,11 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                  dp_netdev_recirculate(pmd, packets_);
>                  (*depth)--;
>                  return;
> +            } else {
> +                COVERAGE_ADD(dp_invalid_tnl_port_drop, packets_->count);
>              }
> +        } else {
> +            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
>          }
>          break;
>  
> @@ -6991,6 +7082,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>              fat_rwlock_unlock(&dp->upcall_rwlock);
>  
>              return;
> +        } else {
> +            COVERAGE_ADD(dp_lock_error_drop, packets_->count);
>          }
>          break;
>  
> @@ -7013,6 +7106,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>              (*depth)--;
>  
>              return;
> +        } else {
> +            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
>          }

'Else' condition is not needed here as we're returning inside the 'if'.
This is actually applicable to most of other places in this function,
but in this one we already have a warning below without 'else' condition.
>  
>          VLOG_WARN("Packet dropped. Max recirculation depth exceeded.");
> @@ -7167,6 +7262,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();
>      }
> @@ -7183,7 +7279,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_action_counter_cb);

As I already wrote, we'll not need to have a callback function if you'll
move the 'drop_action_*' counters to odp-execute.c.
>  }
>  
>  struct dp_netdev_ct_dump {
> diff --git a/lib/dpif.c b/lib/dpif.c
> index e35f111..21f9f54 100644
> --- a/lib/dpif.c
> +++ b/lib/dpif.c
> @@ -1273,6 +1273,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();
> @@ -1295,7 +1296,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;
>  }
>  
> @@ -1879,6 +1880,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,
> @@ -1976,3 +1983,4 @@ dpif_meter_del(struct dpif *dpif, ofproto_meter_id meter_id,
>      }
>      return error;
>  }
> +
> diff --git a/lib/dpif.h b/lib/dpif.h
> index 475d5a6..e799da8 100644
> --- a/lib/dpif.h
> +++ b/lib/dpif.h
> @@ -888,6 +888,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 320422b..0c3bc9c 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -2395,6 +2395,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);

This is the unrelated bug fix that should be sent as a separate patch.
In this case we'll be able to apply it and backport to previous versions.

Adding Ian to CC.

>          dp_packet_delete_batch(batch, true);
>          return;
>      }
> diff --git a/lib/odp-execute.c b/lib/odp-execute.c
> index 3b6890e..c0db93f 100644
> --- a/lib/odp-execute.c
> +++ b/lib/odp-execute.c
> @@ -25,6 +25,7 @@
>  #include <stdlib.h>
>  #include <string.h>
>  
> +#include "coverage.h"
>  #include "dp-packet.h"
>  #include "dpif.h"
>  #include "netlink.h"
> @@ -37,6 +38,10 @@
>  #include "csum.h"
>  #include "conntrack.h"
>  
> +COVERAGE_DEFINE(dp_sample_error_drop);
> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
> +
> +
>  /* Masked copy of an ethernet address. 'src' is already properly masked. */
>  static void
>  ether_addr_copy_masked(struct eth_addr *dst, const struct eth_addr src,
> @@ -575,7 +580,9 @@ 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_action_counter_cb
> +                     dp_update_drop_action_counter)
>  {
>      const struct nlattr *subactions = NULL;
>      const struct nlattr *a;
> @@ -589,6 +596,7 @@ 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) {
> +                    COVERAGE_ADD(dp_sample_error_drop, 1);
>                      dp_packet_delete(packet);
>                  }
>                  return;
> @@ -616,13 +624,16 @@ 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_action_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_action_counter_cb
> +                      dp_update_drop_action_counter)
>  {
>      if (!steal) {
>          /* The 'actions' may modify the packet, but the modification
> @@ -634,11 +645,12 @@ 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);
> -    }
> -    else {
> +                        nl_attr_get_size(actions), dp_execute_action,
> +                        dp_update_drop_action_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_action_counter);
>      }
>  }
>  
> @@ -673,6 +685,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,12 +712,15 @@ 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_action_counter_cb
> +                       dp_update_drop_action_counter)
>  {
>      struct dp_packet *packet;
>      const struct nlattr *a;
>      unsigned int left;
>  
> +

No need to add an empty line.

>      NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
>          int type = nl_attr_type(a);
>          bool last_action = (left <= NLA_ALIGN(a->nla_len));
> @@ -822,7 +838,8 @@ 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_action_counter);
>              }
>  
>              if (last_action) {
> @@ -845,7 +862,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_action_counter);
>              if (last_action) {
>                  /* We do not need to free the packets. odp_execute_clone() has
>                   * stolen them.  */
> @@ -889,6 +907,7 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
>                  if (pop_nsh(packet)) {
>                      dp_packet_batch_refill(batch, packet, i);
>                  } else {
> +                    COVERAGE_INC(dp_nsh_decap_error_drop);
>                      dp_packet_delete(packet);
>                  }
>              }
> @@ -900,6 +919,17 @@ 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_action_counter) {
> +                 dp_update_drop_action_counter(drop_reason, 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..a84ecbb 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_action_counter_cb) (
> +                                           enum  ovs_drop_reason drop_reason,
> +                                           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,7 @@ 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_action_counter_cb
> +                             dp_update_drop_action_counter_cb);
>  #endif
> diff --git a/lib/odp-util.c b/lib/odp-util.c
> index 0491bed..9754fd5 100644
> --- a/lib/odp-util.c
> +++ b/lib/odp-util.c
> @@ -131,6 +131,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:
> @@ -345,6 +346,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));

As I already wrote for the previous versions it'll be good to add
some visible markers around the drop reason. Spaces in flow dumps
are usually not used and it could add some complications for the
parsing scripts. You may use brackets like "drop(stack too deep)"
or replace the spaces with '-' or '_' like "drop:stack_too_deep".
> +}
> +
>  static void
>  format_odp_push_nsh_action(struct ds *ds,
>                             const struct nsh_hdr *nsh_hdr)
> @@ -1181,6 +1225,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:
> @@ -2427,8 +2474,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"))) {

Why you're adding parse only for this type of drop action ?
<Anju>The rest are all below error scenarios which we hit during processing and hence I thought perhaps may not make sense as an input?? . Do you think it make sense to do that ?

*no recirculation context 
*too many mpls labels
* invalid tunnel metadata
* unsupported packet type
* ecn mismatch at tunnel decapsulation
* forwarding disabled
* 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 7da3175..69ed7b8 100644
> --- a/ofproto/ofproto-dpif-sflow.c
> +++ b/ofproto/ofproto-dpif-sflow.c
> @@ -1222,6 +1222,7 @@ dpif_sflow_read_actions(const struct flow *flow,
>          case OVS_ACTION_ATTR_PUSH_NSH:
>          case OVS_ACTION_ATTR_POP_NSH:
>          case OVS_ACTION_ATTR_UNSPEC:
> +        case OVS_ACTION_ATTR_DROP:
>          case __OVS_ACTION_ATTR_MAX:
>          default:
>              break;
> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
> index dc30824..b1ce2ec 100644
> --- a/ofproto/ofproto-dpif-upcall.c
> +++ b/ofproto/ofproto-dpif-upcall.c
> @@ -1154,7 +1154,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)
>  {
> @@ -1244,6 +1244,7 @@ upcall_xlate(struct udpif *udpif, struct upcall *upcall,
>      if (upcall->type == MISS_UPCALL) {
>          upcall->ukey = ukey_create_from_upcall(upcall, wc);
>      }
> +    return xerr;

The only user of this function is 'process_upcall' and it does not check
the result.

>  }
>  
>  static void
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index 839fddd..32c4edf 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);
>  
> @@ -5921,6 +5957,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);

Indents are a bit off.

> +
> +}
> +
> +static void
>  put_ct_helper(struct xlate_ctx *ctx,
>                struct ofpbuf *odp_actions, struct ofpact_conntrack *ofc)
>  {
> @@ -7383,6 +7430,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;
> @@ -7415,6 +7466,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) {
> @@ -7522,6 +7574,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 0a5a528..bb3f9ff 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 14fe6fc..609226a 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -827,6 +827,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.
> @@ -1397,6 +1403,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);
> @@ -5776,6 +5784,7 @@ ofproto_unixctl_dpif_set_dp_features(struct unixctl_conn *conn,
>      ds_destroy(&ds);
>  }
>  
> +

No need to add an empty line.

>  static void
>  ofproto_unixctl_init(void)
>  {
> @@ -5809,7 +5818,7 @@ ofproto_unixctl_init(void)
>      unixctl_command_register("dpif/set-dp-features", "bridge", 1, 3 ,
>                               ofproto_unixctl_dpif_set_dp_features, NULL);
>  }
> -

> +

Don't drop the ^L character.
<Anju> What is the significance of this character
>  static odp_port_t
>  ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
>  {
> 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 *);
>  
> +

No need to add an empty line.

>  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 92d56b2..22b4722 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -108,7 +108,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

automake.mk uses tabs.

>  
>  EXTRA_DIST += $(FUZZ_REGRESSION_TESTS)
>  FUZZ_REGRESSION_TESTS = \
> diff --git a/tests/bundle.at b/tests/bundle.at
> index 0a4eadc..33fe249 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 6915d43..08349f6 100644
> --- a/tests/dpif-netdev.at
> +++ b/tests/dpif-netdev.at
> @@ -1,4 +1,4 @@
> -AT_BANNER([dpif-netdev])
> +T_BANNER([dpif-netdev])

Something wrong here.

>  
>  m4_divert_push([PREPARE_TESTS])
>  [
> @@ -281,6 +281,7 @@ type=drop rate=1 burst_size=2
>  ])
>  
>  ovs-appctl time/warp 5000
> +sleep 10

Do we really need to sleep for so long ?

>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
> @@ -291,7 +292,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
> -sleep 1  # wait for forwarders process packets
> +# wait for forwarders process packets

Something wrong here too.

>  
>  # Meter 1 is measuring packets, allowing one packet per second with
>  # bursts of one packet, so 4 out of 5 packets should hit the drop
> @@ -320,7 +321,8 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
> -sleep 1  # wait for forwarders process packets
> +
> +sleep 10  # wait for forwarders process packets
>  
>  # Meter 1 is measuring packets, allowing one packet per second with
>  # bursts of one packet, so all 5 of the new packets should hit the drop
> @@ -337,6 +339,15 @@ meter:2 flow_count:1 packet_in_count:10 byte_in_count:600 duration:0.0s bands:
>  0: packet_count:5 byte_count:300
>  ])
>  
> +ovs-appctl time/warp 5000
> +sleep 10
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "dp_meter_drop" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +14
> +])
> +
>  AT_CHECK([cat ovs-vswitchd.log | filter_flow_install | strip_xout_keep_actions], [0], [dnl
>  recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:meter(0),7
>  recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:8
> diff --git a/tests/drop-stats.at b/tests/drop-stats.at
> new file mode 100644
> index 0000000..be8b8ae
> --- /dev/null
> +++ b/tests/drop-stats.at
> @@ -0,0 +1,197 @@
> +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
> +])
> +
> +sleep 1
> +
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +3
> +])
> +
> +
> +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

If possible, it's preferred to use odp format (i.e. declarative) for
packets definition. This allowes user to see what packet is sent without
parsing it by external tools.
Same for all other places.

> +], [0], [ignore])
> +
> +sleep 1
> +
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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])
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_recursion_too_deep" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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)'
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_too_many_resubmit" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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)'
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_stack_too_deep" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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])
> +
> +sleep 1
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_too_many_mpls_labels" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
> +
> +OVS_VSWITCHD_STOP(["/|WARN|/d"])
> +AT_CLEANUP
> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
> index ded2ef0..298aa9a 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -3496,51 +3496,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" \
> @@ -3555,8 +3555,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" \
> @@ -3573,16 +3573,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"
> @@ -4322,11 +4322,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
> @@ -4404,8 +4404,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
> @@ -5751,7 +5751,7 @@ bridge("br0")
>  
>  Final flow: <cleared>
>  Megaflow: <cleared>
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>  ])
>  
>  dnl Now, try again without megaflows:
> @@ -5772,7 +5772,7 @@ bridge("br0")
>  
>  Final flow: <cleared>
>  Megaflow: <cleared>
> -Datapath actions: drop
> +Datapath actions: drop:pipeline-drop
>  ])
>  
>  OVS_VSWITCHD_STOP
> @@ -7066,7 +7066,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"])
> @@ -7158,7 +7158,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
> @@ -7832,21 +7832,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
> @@ -7869,7 +7869,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
> @@ -8617,11 +8617,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])
> @@ -9348,7 +9348,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
> @@ -9377,16 +9377,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
> @@ -9711,7 +9711,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)
> @@ -10398,7 +10398,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])
> @@ -10490,7 +10490,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 6ae3470..cd19d41 100644
> --- a/tests/ovs-ofctl.at
> +++ b/tests/ovs-ofctl.at
> @@ -2999,7 +2999,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..7c1b551 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,14 @@ 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 coverage/show | grep "drop_action_unsupported_packet_type" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +2
>  ])
>  
>  # Encap(ethernet) on Ethernet frame -> should be droped
> @@ -587,7 +594,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 +616,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 +777,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 b840dbf..922ba48 100644
> --- a/tests/testsuite.at
> +++ b/tests/testsuite.at
> @@ -82,3 +82,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..385c114 100644
> --- a/tests/tunnel-push-pop.at
> +++ b/tests/tunnel-push-pop.at
> @@ -447,6 +447,27 @@ 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 netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +
> +ovs-appctl time/warp 1200
> +
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "dp_tunnel_pop_error_drop" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
> +
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +ovs-appctl time/warp 1200
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "drop_action_congestion" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
>  dnl Check GREL3 only accepts non-fragmented packets?
>  AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>  
> @@ -455,7 +476,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 +531,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 55fb1d3..3646c06 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 netdev-dummy/receive p2 'aa55aa550001f8bc124434b6080045000054ba20000040018486010103580101037001004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +sleep 2
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "dp_invalid_port_drop" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +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 Jan. 23, 2019, 2:57 p.m. UTC | #11
On 23.01.2019 16:53, Anju Thomas wrote:
> Hi Ilya ,
> Some queries w.r.t your comments .
> 
> Regards
> Anju
> 
> -----Original Message-----
> From: Ilya Maximets [mailto:i.maximets@samsung.com] 
> Sent: Friday, January 18, 2019 5:33 PM
> To: Anju Thomas <anju.thomas@ericsson.com>; dev@openvswitch.org
> Cc: Keshav Gupta <keshugupta1@gmail.com>; Stokes, Ian <ian.stokes@intel.com>; Ben Pfaff <blp@ovn.org>
> Subject: Re: [ovs-dev,v6] Improved Packet Drop Statistics in OVS
> 
> Hi.
> Thanks for working on this.
> Some general comments:
> 
> 1. This patch consists of two separate features:
>    - Reporting the drop reason in flow dumps.
>    - Counters for different drop types.
>    I still think that there should be patch-set of two separate patches.
>    This will simplify the review and allow to apply them separately/speed
>    up accepting.
> 
> 2. There are some issues with the patch formatting. The commit message is
>    indented like in the output of 'git show' command.  So, it's double
>    shifted after applying in git. Please remove the additional indentation.
>    You probably should start using 'git format-patch'.
> 
>    Second issue:
> 
>    Applying: Improved Packet Drop Statistics in OVS
>    .git/rebase-apply/patch:415: new blank line at EOF.
>    +
>    warning: 1 line adds whitespace errors.
> 
>    One more thing is that for better readability it's a common practice to
>    limit the width of lines in commit-message to 72 characters. For example,
>    you may strip not important fields of the dump-flows output. Like this:
> 
>        recirc_id(0),in_port(5),<...>, actions:drop:recursion too deep
> 
> 
> Other comments inline.
> 
> <Anju> I use 
> a)git format-patch -1 -s 
> b)  ./utilities/checkpatch.py -1 <patch name>
> 
> Are these the correct parameters that I should be using ?

Commands looks fine. If the patch looks same in your git repo, I'd suggest you
to re-format the patch by hands.

Also, I just spotted that you have a lot of duplicated sign-offs in the patch.

> 
> Best regards, Ilya Maximets. 
> 
> On 17.01.2019 7:49, Anju Thomas 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. Display these drops in ovs-appctl coverage/show
>>     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.
>>
>>      The idea is to use the coverage infrastructure to maintain the drops
>>
>>     $ ovs-appctl dpctl/dump-flows netdev at ovs-netdev
>>     flow-dump from pmd on cpu core: 0
>>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:12, bytes:1176, used:0.884s, actions:drop:recursion too deep
>>
>>     $ ovs-appctl dpif/dump-flows br-int
>>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:25, bytes:2450, used:5.008s, actions:drop:recursion too deep
>>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0806), packets:7, bytes:294, used:0.009s, actions:drop:recursion too deep
>>
>>    In subsequent commits, we are planning to see if we can use this infrastructure to create a
>>    wrapper to clear and display counters as well.
>>
>> Co-authored-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
>> Co-authored-by: Keshav Gupta <keshugupta1@gmail.com>
>> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
>> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
>> Signed-off-by: Keshav Gupta <keshugupta1@gmail.com>
>> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
>> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
>> Signed-off-by: Keshav Gupta <keshugupta1@gmail.com>
>> ---
>>  datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
>>  lib/coverage.c                                    |   6 +-
>>  lib/coverage.h                                    |   3 +-
>>  lib/dpif-netdev.c                                 | 105 +++++++++++-
>>  lib/dpif.c                                        |  10 +-
>>  lib/dpif.h                                        |   3 +
>>  lib/netdev-dpdk.c                                 |   4 +
>>  lib/odp-execute.c                                 |  50 ++++--
>>  lib/odp-execute.h                                 |  10 +-
>>  lib/odp-util.c                                    |  54 +++++-
>>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>>  ofproto/ofproto-dpif-upcall.c                     |   3 +-
>>  ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
>>  ofproto/ofproto-dpif-xlate.h                      |   4 +
>>  ofproto/ofproto-dpif.c                            |  11 +-
>>  ofproto/ofproto-dpif.h                            |   8 +-
>>  tests/automake.mk                                 |   3 +-
>>  tests/bundle.at                                   |   2 +-
>>  tests/classifier.at                               |  10 +-
>>  tests/dpif-netdev.at                              |  17 +-
>>  tests/drop-stats.at                               | 197 ++++++++++++++++++++++
>>  tests/ofproto-dpif.at                             | 118 ++++++-------
>>  tests/ovs-ofctl.at                                |   2 +-
>>  tests/packet-type-aware.at                        |  17 +-
>>  tests/testsuite.at                                |   1 +
>>  tests/tunnel-push-pop.at                          |  26 ++-
>>  tests/tunnel.at                                   |  21 ++-
>>  28 files changed, 695 insertions(+), 107 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 9b087f1..b66b46f 100644
>> --- a/datapath/linux/compat/include/linux/openvswitch.h
>> +++ b/datapath/linux/compat/include/linux/openvswitch.h
>> @@ -819,6 +819,56 @@ struct ovs_action_push_eth {
>>  };
>>  
>>  /**
>> + * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP.
>> + * @OVS_DROP_REASON_OF_PIPELINE: Explicit drop action in the pipeline.
>> + * @OVS_DROP_REASON_BRIDGE_NOT_FOUND: Xlation error generated due to
>> + * unable to determine bridge.
>> + * @OVS_DROP_REASON_RECURSION_TOO_DEEP: Xlation error generated due to
>> + * recursion reached maximum depth.
>> + * @OVS_DROP_REASON_TOO_MANY_RESUBMITS: Xlation error generated due to
>> + * too many resubmits.
>> + * @OVS_DROP_REASON_STACK_TOO_DEEP: Xlation error generated due to stack
>> + * too deep.
>> + * @OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT: Xlation error generated
>> + * due to no recirculation context.
>> + * @OVS_DROP_REASON_RECIRCULATION_CONFLICT: Xlation error generated due to
>> + * conflict in recirculation context.
>> + * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to
>> + * too many mpls labels.
>> + * @OVS_DROP_REASON_INVALID_TUNNEL_METADATA: Xlation error generated due to
>> + * invalid tunnel metadata.
>> + * @OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE: Xlation error generated due to
>> + * unsupported packet type.
>> + * @OVS_DROP_REASON_CONGESTION: Xlation error generated due to ecn mismatch
>> + * during tunnel decapsulation.
>> + * @OVS_DROP_REASON_FORWARDING_DISABLED: Xlation error generated due to
>> + * forwarding is disabled.
>> + */
>> +enum ovs_drop_reason {
>> +	OVS_DROP_REASON_OF_PIPELINE = 0,
>> +	OVS_DROP_REASON_BRIDGE_NOT_FOUND,
>> +	OVS_DROP_REASON_RECURSION_TOO_DEEP,
>> +	OVS_DROP_REASON_TOO_MANY_RESUBMITS,
>> +	OVS_DROP_REASON_STACK_TOO_DEEP,
>> +	OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT,
>> +	OVS_DROP_REASON_RECIRCULATION_CONFLICT,
>> +	OVS_DROP_REASON_TOO_MANY_MPLS_LABELS,
>> +	OVS_DROP_REASON_INVALID_TUNNEL_METADATA,
>> +	OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE,
>> +	OVS_DROP_REASON_CONGESTION,
>> +	OVS_DROP_REASON_FORWARDING_DISABLED,
>> +	OVS_DROP_REASON_MAX,
>> +};
>> +
>> +/*
>> + * struct ovs_action_drop - %OVS_ACTION_ATTR_DROP action argument.
>> + * @drop_reason: Reason for installing drop action.
>> + */
>> +struct ovs_action_drop {
>> +	enum ovs_drop_reason drop_reason;
>> +};
>> +
>> +/**
>>   * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
>>   *
>>   * @OVS_NAT_ATTR_SRC: Flag for Source NAT (mangle source address/port).
>> @@ -938,6 +988,7 @@ enum ovs_action_attr {
>>  	OVS_ACTION_ATTR_POP_NSH,      /* No argument. */
>>  	OVS_ACTION_ATTR_METER,        /* u32 meter number. */
>>  	OVS_ACTION_ATTR_CLONE,        /* Nested OVS_CLONE_ATTR_*.  */
>> +        OVS_ACTION_ATTR_DROP,         /* explicit drop action. */
> 
> This is the linux kernel header. You should use tabs here.
>>  
>>  #ifndef __KERNEL__
>>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
>> diff --git a/lib/coverage.c b/lib/coverage.c
>> index 6cef826..c9acc37 100644
>> --- a/lib/coverage.c
>> +++ b/lib/coverage.c
>> @@ -32,9 +32,7 @@ VLOG_DEFINE_THIS_MODULE(coverage);
>>  static struct coverage_counter **coverage_counters = NULL;
>>  static size_t n_coverage_counters = 0;
>>  static size_t allocated_coverage_counters = 0;
>> -
>> -static struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
> 
> This is leftover from the previous versions. No need to expose
> 'coverage_mutex' in current patch.
>> -
>> +struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
>>  DEFINE_STATIC_PER_THREAD_DATA(long long int, coverage_clear_time, LLONG_MIN);
>>  static long long int coverage_run_time = LLONG_MIN;
>>  
>> @@ -52,7 +50,7 @@ coverage_counter_register(struct coverage_counter* counter)
>>      if (n_coverage_counters >= allocated_coverage_counters) {
>>          coverage_counters = x2nrealloc(coverage_counters,
>>                                         &allocated_coverage_counters,
>> -                                       sizeof(struct coverage_counter*));
>> +                                       sizeof(struct coverage_counter *));
> 
> this change is also not needed.
>>      }
>>      coverage_counters[n_coverage_counters++] = counter;
>>  }
>> diff --git a/lib/coverage.h b/lib/coverage.h
>> index dea990e..45b1dbd 100644
>> --- a/lib/coverage.h
>> +++ b/lib/coverage.h
>> @@ -54,7 +54,8 @@ struct coverage_counter {
>>      unsigned int hr[HR_AVG_LEN];
>>  };
>>  
>> -void coverage_counter_register(struct coverage_counter*);
>> +extern struct ovs_mutex coverage_mutex;
>> +void coverage_counter_register(struct coverage_counter *);
> 
> This is leftover from the previous versions. No need to expose
> 'coverage_mutex' in current patch.
>>  
>>  /* Defines COUNTER.  There must be exactly one such definition at file scope
>>   * within a program. */
>> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
>> index be529b6..8749cba 100644
>> --- a/lib/dpif-netdev.c
>> +++ b/lib/dpif-netdev.c
>> @@ -100,6 +100,30 @@ enum { MAX_METERS = 65536 };    /* Maximum number of meters. */
>>  enum { MAX_BANDS = 8 };         /* Maximum number of bands / meter. */
>>  enum { N_METER_LOCKS = 64 };    /* Maximum number of meters. */
>>  
>> +
>> +COVERAGE_DEFINE(drop_action_of_pipeline);
>> +COVERAGE_DEFINE(drop_action_bridge_not_found);
>> +COVERAGE_DEFINE(drop_action_recursion_too_deep);
>> +COVERAGE_DEFINE(drop_action_too_many_resubmit);
>> +COVERAGE_DEFINE(drop_action_stack_too_deep);
>> +COVERAGE_DEFINE(drop_action_no_recirculation_context);
>> +COVERAGE_DEFINE(drop_action_recirculation_conflict);
>> +COVERAGE_DEFINE(drop_action_too_many_mpls_labels);
>> +COVERAGE_DEFINE(drop_action_invalid_tunnel_metadata);
>> +COVERAGE_DEFINE(drop_action_unsupported_packet_type);
>> +COVERAGE_DEFINE(drop_action_congestion);
>> +COVERAGE_DEFINE(drop_action_forwarding_disabled);
> 
> Above counters could be defined inside odp-execute.c. You already have
> 2 other counters there. In this case there will be no need to introduce
> additional 'dp_update_drop_action_counter_cb' callback and pass it around.
> All the required logic will also be moved to odp-execute.c.
> 
> <Anju> But I want the callback to be NULL when called from dpif_execute_with_help

Why? This seems strange to count drops only for actions that do not need
help from the dpif layer.

> 
>> +COVERAGE_DEFINE(dp_meter_drop);
>> +COVERAGE_DEFINE(dp_upcall_error_drop);
>> +COVERAGE_DEFINE(dp_lock_error_drop);
>> +COVERAGE_DEFINE(dp_userspace_action_error_drop);
>> +COVERAGE_DEFINE(dp_tunnel_push_error_drop);
>> +COVERAGE_DEFINE(dp_tunnel_pop_error_drop);
>> +COVERAGE_DEFINE(dp_recirc_error_drop);
>> +COVERAGE_DEFINE(dp_invalid_port_drop);
>> +COVERAGE_DEFINE(dp_invalid_tnl_port_drop);
>> +COVERAGE_DEFINE(rx_invalid_packet_drop);
> 
> What do you think about renaming above counters to 'datapath_drop_*' ?
> IMHO, it'll be more visually recognizable.
> Like 'datapath_drop_rx_invalid_packet' or 'datapath_drop_upcall_error'.
>  
> <Anju> Do you mean instead of using dp_* to use datapath_*??

I mean using 'datapath_drop_' as a prefix.

>> +
>>  /* Protects against changes to 'dp_netdevs'. */
>>  static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>>  
>> @@ -829,7 +853,6 @@ static inline bool
>>  pmd_perf_metrics_enabled(const struct dp_netdev_pmd_thread *pmd);
>>  static void queue_netdev_flow_del(struct dp_netdev_pmd_thread *pmd,
>>                                    struct dp_netdev_flow *flow);
>> -
> 
> No need to remove an empty line.
> 
>>  static void
>>  emc_cache_init(struct emc_cache *flow_cache)
>>  {
>> @@ -1388,6 +1411,7 @@ dpif_netdev_init(void)
>>      return 0;
>>  }
>>  
>> +
> 
> No need to add an empty line.
>>  static int
>>  dpif_netdev_enumerate(struct sset *all_dps,
>>                        const struct dpif_class *dpif_class)
>> @@ -5563,7 +5587,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, struct dp_packet_batch *packets_,
>>              band = &meter->bands[exceeded_band[j]];
>>              band->packet_count += 1;
>>              band->byte_count += dp_packet_size(packet);
>> -
> Please, keep this empty line for readability.
> 
>> +            COVERAGE_INC(dp_meter_drop);
>>              dp_packet_delete(packet);
>>          } else {
>>              /* Meter accepts packet. */
>> @@ -6320,6 +6344,7 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
>>  
>>          if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) {
>>              dp_packet_delete(packet);
>> +            COVERAGE_INC(rx_invalid_packet_drop);
>>              continue;
>>          }
>>  
>> @@ -6446,6 +6471,7 @@ handle_packet_upcall(struct dp_netdev_pmd_thread *pmd,
>>                               put_actions);
>>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>>          dp_packet_delete(packet);
>> +        COVERAGE_INC(dp_upcall_error_drop);
>>          return error;
>>      }
>>  
>> @@ -6577,6 +6603,7 @@ 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);
>> +                COVERAGE_INC(dp_lock_error_drop);
>>                  upcall_fail_cnt++;
>>              }
>>          }
>> @@ -6846,10 +6873,59 @@ dp_execute_userspace_action(struct dp_netdev_pmd_thread *pmd,
>>                                    actions->data, actions->size);
>>      } else if (should_steal) {
>>          dp_packet_delete(packet);
>> +        COVERAGE_INC(dp_userspace_action_error_drop);
>>      }
>>  }
>>  
>>  static void
>> +dp_update_drop_action_counter_cb(enum ovs_drop_reason drop_reason,
>> +                          int delta)
> 
> Indents are a bit off.
>> +    OVS_NO_THREAD_SAFETY_ANALYSIS
> 
> Is it necessary to disable thread safety analysis ?
> <Anju> Since we are using COVERAGE infra which is defining thread specific data, i thought we might need to use this. Is my understanding incorrect?

Coverage infra is thread safe. And clang doesn't seem to produce any warnings
without this annotation. Thread safety analysis should be disabled only if you
have clang warnings and can not resolve them in other way.

>> +{
>> +   switch (drop_reason) {
>> +   case OVS_DROP_REASON_OF_PIPELINE:
>> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
>> +        break;
>> +   case OVS_DROP_REASON_BRIDGE_NOT_FOUND:
>> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
>> +        break;
>> +   case OVS_DROP_REASON_RECURSION_TOO_DEEP:
>> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
>> +        break;
>> +   case OVS_DROP_REASON_TOO_MANY_RESUBMITS:
>> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
>> +        break;
>> +   case OVS_DROP_REASON_STACK_TOO_DEEP:
>> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
>> +        break;
>> +   case OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT:
>> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
>> +        break;
>> +   case OVS_DROP_REASON_RECIRCULATION_CONFLICT:
>> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
>> +        break;
>> +   case OVS_DROP_REASON_TOO_MANY_MPLS_LABELS:
>> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
>> +        break;
>> +   case OVS_DROP_REASON_INVALID_TUNNEL_METADATA:
>> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
>> +        break;
>> +   case OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE:
>> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
>> +        break;
>> +   case OVS_DROP_REASON_CONGESTION:
>> +        COVERAGE_ADD(drop_action_congestion, delta);
>> +        break;
>> +   case OVS_DROP_REASON_FORWARDING_DISABLED:
>> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
>> +        break;
>> +   case OVS_DROP_REASON_MAX:
>> +   default:
>> +        VLOG_ERR("Invalid Drop reason type:%d",drop_reason);
> 
> We might want the rate limiting for above message to not have it for each packet.
>> +   }
>> +}
>> +
>> +static void
>>  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>>                const struct nlattr *a, bool should_steal)
>>      OVS_NO_THREAD_SAFETY_ANALYSIS
>> @@ -6860,6 +6936,7 @@ 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;
>>  
>>      switch ((enum ovs_action_attr)type) {
>>      case OVS_ACTION_ATTR_OUTPUT:
>> @@ -6901,6 +6978,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>>                  dp_packet_batch_add(&p->output_pkts, packet);
>>              }
>>              return;
>> +        } else {
>> +            COVERAGE_ADD(dp_invalid_port_drop, packets_->count);
>>          }
>>          break;
>>  
>> @@ -6910,10 +6989,13 @@ 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. */
>> +            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
>>              break;
>>          }
>>          dp_packet_batch_apply_cutlen(packets_);
>> -        push_tnl_action(pmd, a, packets_);
>> +        if (push_tnl_action(pmd, a, packets_)) {
>> +            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
>> +        }
>>          return;
>>  
>>      case OVS_ACTION_ATTR_TUNNEL_POP:
>> @@ -6933,7 +7015,12 @@ 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) {
>> +                    COVERAGE_ADD(dp_tunnel_pop_error_drop, packet_dropped);
>> +                }
>>                  if (dp_packet_batch_is_empty(packets_)) {
>>                      return;
>>                  }
>> @@ -6947,7 +7034,11 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>>                  dp_netdev_recirculate(pmd, packets_);
>>                  (*depth)--;
>>                  return;
>> +            } else {
>> +                COVERAGE_ADD(dp_invalid_tnl_port_drop, packets_->count);
>>              }
>> +        } else {
>> +            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
>>          }
>>          break;
>>  
>> @@ -6991,6 +7082,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>>              fat_rwlock_unlock(&dp->upcall_rwlock);
>>  
>>              return;
>> +        } else {
>> +            COVERAGE_ADD(dp_lock_error_drop, packets_->count);
>>          }
>>          break;
>>  
>> @@ -7013,6 +7106,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>>              (*depth)--;
>>  
>>              return;
>> +        } else {
>> +            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
>>          }
> 
> 'Else' condition is not needed here as we're returning inside the 'if'.
> This is actually applicable to most of other places in this function,
> but in this one we already have a warning below without 'else' condition.
>>  
>>          VLOG_WARN("Packet dropped. Max recirculation depth exceeded.");
>> @@ -7167,6 +7262,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();
>>      }
>> @@ -7183,7 +7279,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_action_counter_cb);
> 
> As I already wrote, we'll not need to have a callback function if you'll
> move the 'drop_action_*' counters to odp-execute.c.
>>  }
>>  
>>  struct dp_netdev_ct_dump {
>> diff --git a/lib/dpif.c b/lib/dpif.c
>> index e35f111..21f9f54 100644
>> --- a/lib/dpif.c
>> +++ b/lib/dpif.c
>> @@ -1273,6 +1273,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();
>> @@ -1295,7 +1296,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;
>>  }
>>  
>> @@ -1879,6 +1880,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,
>> @@ -1976,3 +1983,4 @@ dpif_meter_del(struct dpif *dpif, ofproto_meter_id meter_id,
>>      }
>>      return error;
>>  }
>> +
>> diff --git a/lib/dpif.h b/lib/dpif.h
>> index 475d5a6..e799da8 100644
>> --- a/lib/dpif.h
>> +++ b/lib/dpif.h
>> @@ -888,6 +888,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 320422b..0c3bc9c 100644
>> --- a/lib/netdev-dpdk.c
>> +++ b/lib/netdev-dpdk.c
>> @@ -2395,6 +2395,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);
> 
> This is the unrelated bug fix that should be sent as a separate patch.
> In this case we'll be able to apply it and backport to previous versions.
> 
> Adding Ian to CC.
> 
>>          dp_packet_delete_batch(batch, true);
>>          return;
>>      }
>> diff --git a/lib/odp-execute.c b/lib/odp-execute.c
>> index 3b6890e..c0db93f 100644
>> --- a/lib/odp-execute.c
>> +++ b/lib/odp-execute.c
>> @@ -25,6 +25,7 @@
>>  #include <stdlib.h>
>>  #include <string.h>
>>  
>> +#include "coverage.h"
>>  #include "dp-packet.h"
>>  #include "dpif.h"
>>  #include "netlink.h"
>> @@ -37,6 +38,10 @@
>>  #include "csum.h"
>>  #include "conntrack.h"
>>  
>> +COVERAGE_DEFINE(dp_sample_error_drop);
>> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
>> +
>> +
>>  /* Masked copy of an ethernet address. 'src' is already properly masked. */
>>  static void
>>  ether_addr_copy_masked(struct eth_addr *dst, const struct eth_addr src,
>> @@ -575,7 +580,9 @@ 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_action_counter_cb
>> +                     dp_update_drop_action_counter)
>>  {
>>      const struct nlattr *subactions = NULL;
>>      const struct nlattr *a;
>> @@ -589,6 +596,7 @@ 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) {
>> +                    COVERAGE_ADD(dp_sample_error_drop, 1);
>>                      dp_packet_delete(packet);
>>                  }
>>                  return;
>> @@ -616,13 +624,16 @@ 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_action_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_action_counter_cb
>> +                      dp_update_drop_action_counter)
>>  {
>>      if (!steal) {
>>          /* The 'actions' may modify the packet, but the modification
>> @@ -634,11 +645,12 @@ 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);
>> -    }
>> -    else {
>> +                        nl_attr_get_size(actions), dp_execute_action,
>> +                        dp_update_drop_action_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_action_counter);
>>      }
>>  }
>>  
>> @@ -673,6 +685,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,12 +712,15 @@ 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_action_counter_cb
>> +                       dp_update_drop_action_counter)
>>  {
>>      struct dp_packet *packet;
>>      const struct nlattr *a;
>>      unsigned int left;
>>  
>> +
> 
> No need to add an empty line.
> 
>>      NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
>>          int type = nl_attr_type(a);
>>          bool last_action = (left <= NLA_ALIGN(a->nla_len));
>> @@ -822,7 +838,8 @@ 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_action_counter);
>>              }
>>  
>>              if (last_action) {
>> @@ -845,7 +862,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_action_counter);
>>              if (last_action) {
>>                  /* We do not need to free the packets. odp_execute_clone() has
>>                   * stolen them.  */
>> @@ -889,6 +907,7 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
>>                  if (pop_nsh(packet)) {
>>                      dp_packet_batch_refill(batch, packet, i);
>>                  } else {
>> +                    COVERAGE_INC(dp_nsh_decap_error_drop);
>>                      dp_packet_delete(packet);
>>                  }
>>              }
>> @@ -900,6 +919,17 @@ 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_action_counter) {
>> +                 dp_update_drop_action_counter(drop_reason, 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..a84ecbb 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_action_counter_cb) (
>> +                                           enum  ovs_drop_reason drop_reason,
>> +                                           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,7 @@ 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_action_counter_cb
>> +                             dp_update_drop_action_counter_cb);
>>  #endif
>> diff --git a/lib/odp-util.c b/lib/odp-util.c
>> index 0491bed..9754fd5 100644
>> --- a/lib/odp-util.c
>> +++ b/lib/odp-util.c
>> @@ -131,6 +131,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:
>> @@ -345,6 +346,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));
> 
> As I already wrote for the previous versions it'll be good to add
> some visible markers around the drop reason. Spaces in flow dumps
> are usually not used and it could add some complications for the
> parsing scripts. You may use brackets like "drop(stack too deep)"
> or replace the spaces with '-' or '_' like "drop:stack_too_deep".
>> +}
>> +
>>  static void
>>  format_odp_push_nsh_action(struct ds *ds,
>>                             const struct nsh_hdr *nsh_hdr)
>> @@ -1181,6 +1225,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:
>> @@ -2427,8 +2474,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"))) {
> 
> Why you're adding parse only for this type of drop action ?
> <Anju>The rest are all below error scenarios which we hit during processing and hence I thought perhaps may not make sense as an input?? . Do you think it make sense to do that ?

I think, Ben already answered this questin.
IMHO, that parsing of only one type makes no sence. There should be all
or nothing. As we think that OVS should be able to parse flows it generates,
there should be all.

> 
> *no recirculation context 
> *too many mpls labels
> * invalid tunnel metadata
> * unsupported packet type
> * ecn mismatch at tunnel decapsulation
> * forwarding disabled
> * 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 7da3175..69ed7b8 100644
>> --- a/ofproto/ofproto-dpif-sflow.c
>> +++ b/ofproto/ofproto-dpif-sflow.c
>> @@ -1222,6 +1222,7 @@ dpif_sflow_read_actions(const struct flow *flow,
>>          case OVS_ACTION_ATTR_PUSH_NSH:
>>          case OVS_ACTION_ATTR_POP_NSH:
>>          case OVS_ACTION_ATTR_UNSPEC:
>> +        case OVS_ACTION_ATTR_DROP:
>>          case __OVS_ACTION_ATTR_MAX:
>>          default:
>>              break;
>> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
>> index dc30824..b1ce2ec 100644
>> --- a/ofproto/ofproto-dpif-upcall.c
>> +++ b/ofproto/ofproto-dpif-upcall.c
>> @@ -1154,7 +1154,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)
>>  {
>> @@ -1244,6 +1244,7 @@ upcall_xlate(struct udpif *udpif, struct upcall *upcall,
>>      if (upcall->type == MISS_UPCALL) {
>>          upcall->ukey = ukey_create_from_upcall(upcall, wc);
>>      }
>> +    return xerr;
> 
> The only user of this function is 'process_upcall' and it does not check
> the result.
> 
>>  }
>>  
>>  static void
>> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
>> index 839fddd..32c4edf 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);
>>  
>> @@ -5921,6 +5957,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);
> 
> Indents are a bit off.
> 
>> +
>> +}
>> +
>> +static void
>>  put_ct_helper(struct xlate_ctx *ctx,
>>                struct ofpbuf *odp_actions, struct ofpact_conntrack *ofc)
>>  {
>> @@ -7383,6 +7430,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;
>> @@ -7415,6 +7466,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) {
>> @@ -7522,6 +7574,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 0a5a528..bb3f9ff 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 14fe6fc..609226a 100644
>> --- a/ofproto/ofproto-dpif.c
>> +++ b/ofproto/ofproto-dpif.c
>> @@ -827,6 +827,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.
>> @@ -1397,6 +1403,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);
>> @@ -5776,6 +5784,7 @@ ofproto_unixctl_dpif_set_dp_features(struct unixctl_conn *conn,
>>      ds_destroy(&ds);
>>  }
>>  
>> +
> 
> No need to add an empty line.
> 
>>  static void
>>  ofproto_unixctl_init(void)
>>  {
>> @@ -5809,7 +5818,7 @@ ofproto_unixctl_init(void)
>>      unixctl_command_register("dpif/set-dp-features", "bridge", 1, 3 ,
>>                               ofproto_unixctl_dpif_set_dp_features, NULL);
>>  }
>> -
> 
>> +
> 
> Don't drop the ^L character.
> <Anju> What is the significance of this character

Form feeds used in OVS to divide long source files into logical pieces.
This is documented in the coding-style.

>>  static odp_port_t
>>  ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
>>  {
>> 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 *);
>>  
>> +
> 
> No need to add an empty line.
> 
>>  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 92d56b2..22b4722 100644
>> --- a/tests/automake.mk
>> +++ b/tests/automake.mk
>> @@ -108,7 +108,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
> 
> automake.mk uses tabs.
> 
>>  
>>  EXTRA_DIST += $(FUZZ_REGRESSION_TESTS)
>>  FUZZ_REGRESSION_TESTS = \
>> diff --git a/tests/bundle.at b/tests/bundle.at
>> index 0a4eadc..33fe249 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 6915d43..08349f6 100644
>> --- a/tests/dpif-netdev.at
>> +++ b/tests/dpif-netdev.at
>> @@ -1,4 +1,4 @@
>> -AT_BANNER([dpif-netdev])
>> +T_BANNER([dpif-netdev])
> 
> Something wrong here.
> 
>>  
>>  m4_divert_push([PREPARE_TESTS])
>>  [
>> @@ -281,6 +281,7 @@ type=drop rate=1 burst_size=2
>>  ])
>>  
>>  ovs-appctl time/warp 5000
>> +sleep 10
> 
> Do we really need to sleep for so long ?
> 
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>> @@ -291,7 +292,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>> -sleep 1  # wait for forwarders process packets
>> +# wait for forwarders process packets
> 
> Something wrong here too.
> 
>>  
>>  # Meter 1 is measuring packets, allowing one packet per second with
>>  # bursts of one packet, so 4 out of 5 packets should hit the drop
>> @@ -320,7 +321,8 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>> -sleep 1  # wait for forwarders process packets
>> +
>> +sleep 10  # wait for forwarders process packets
>>  
>>  # Meter 1 is measuring packets, allowing one packet per second with
>>  # bursts of one packet, so all 5 of the new packets should hit the drop
>> @@ -337,6 +339,15 @@ meter:2 flow_count:1 packet_in_count:10 byte_in_count:600 duration:0.0s bands:
>>  0: packet_count:5 byte_count:300
>>  ])
>>  
>> +ovs-appctl time/warp 5000
>> +sleep 10
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "dp_meter_drop" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +14
>> +])
>> +
>>  AT_CHECK([cat ovs-vswitchd.log | filter_flow_install | strip_xout_keep_actions], [0], [dnl
>>  recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:meter(0),7
>>  recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:8
>> diff --git a/tests/drop-stats.at b/tests/drop-stats.at
>> new file mode 100644
>> index 0000000..be8b8ae
>> --- /dev/null
>> +++ b/tests/drop-stats.at
>> @@ -0,0 +1,197 @@
>> +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
>> +])
>> +
>> +sleep 1
>> +
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +3
>> +])
>> +
>> +
>> +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
> 
> If possible, it's preferred to use odp format (i.e. declarative) for
> packets definition. This allowes user to see what packet is sent without
> parsing it by external tools.
> Same for all other places.
> 
>> +], [0], [ignore])
>> +
>> +sleep 1
>> +
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +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])
>> +
>> +sleep 1
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "drop_action_recursion_too_deep" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +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)'
>> +
>> +sleep 1
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "drop_action_too_many_resubmit" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +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)'
>> +
>> +sleep 1
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "drop_action_stack_too_deep" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +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])
>> +
>> +sleep 1
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "drop_action_too_many_mpls_labels" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +1
>> +])
>> +
>> +
>> +OVS_VSWITCHD_STOP(["/|WARN|/d"])
>> +AT_CLEANUP
>> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
>> index ded2ef0..298aa9a 100644
>> --- a/tests/ofproto-dpif.at
>> +++ b/tests/ofproto-dpif.at
>> @@ -3496,51 +3496,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" \
>> @@ -3555,8 +3555,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" \
>> @@ -3573,16 +3573,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"
>> @@ -4322,11 +4322,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
>> @@ -4404,8 +4404,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
>> @@ -5751,7 +5751,7 @@ bridge("br0")
>>  
>>  Final flow: <cleared>
>>  Megaflow: <cleared>
>> -Datapath actions: drop
>> +Datapath actions: drop:pipeline-drop
>>  ])
>>  
>>  dnl Now, try again without megaflows:
>> @@ -5772,7 +5772,7 @@ bridge("br0")
>>  
>>  Final flow: <cleared>
>>  Megaflow: <cleared>
>> -Datapath actions: drop
>> +Datapath actions: drop:pipeline-drop
>>  ])
>>  
>>  OVS_VSWITCHD_STOP
>> @@ -7066,7 +7066,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"])
>> @@ -7158,7 +7158,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
>> @@ -7832,21 +7832,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
>> @@ -7869,7 +7869,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
>> @@ -8617,11 +8617,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])
>> @@ -9348,7 +9348,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
>> @@ -9377,16 +9377,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
>> @@ -9711,7 +9711,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)
>> @@ -10398,7 +10398,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])
>> @@ -10490,7 +10490,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 6ae3470..cd19d41 100644
>> --- a/tests/ovs-ofctl.at
>> +++ b/tests/ovs-ofctl.at
>> @@ -2999,7 +2999,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..7c1b551 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,14 @@ 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 coverage/show | grep "drop_action_unsupported_packet_type" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +2
>>  ])
>>  
>>  # Encap(ethernet) on Ethernet frame -> should be droped
>> @@ -587,7 +594,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 +616,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 +777,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 b840dbf..922ba48 100644
>> --- a/tests/testsuite.at
>> +++ b/tests/testsuite.at
>> @@ -82,3 +82,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..385c114 100644
>> --- a/tests/tunnel-push-pop.at
>> +++ b/tests/tunnel-push-pop.at
>> @@ -447,6 +447,27 @@ 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 netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>> +
>> +ovs-appctl time/warp 1200
>> +
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "dp_tunnel_pop_error_drop" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +1
>> +])
>> +
>> +
>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>> +ovs-appctl time/warp 1200
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "drop_action_congestion" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +1
>> +])
>> +
>>  dnl Check GREL3 only accepts non-fragmented packets?
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>  
>> @@ -455,7 +476,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 +531,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 55fb1d3..3646c06 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 netdev-dummy/receive p2 'aa55aa550001f8bc124434b6080045000054ba20000040018486010103580101037001004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>> +sleep 2
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "dp_invalid_port_drop" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +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
>>
Ben Pfaff Jan. 23, 2019, 8:27 p.m. UTC | #12
On Wed, Jan 23, 2019 at 09:52:02AM +0000, Anju Thomas wrote:
> On Mon, Jan 21, 2019 at 07:01:33AM +0000, Anju Thomas wrote:
> > On Thu, Jan 17, 2019 at 04:49:20AM +0000, Anju Thomas 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.
> > 
> > Thanks for the patch!  I agree with your motivations--it is useful to understand why packets are dropped.  I have some comments to add to Ilya's.
> > 
> > It looks like the drop actions that this formats in
> > format_odp_drop_action() can't necessarily be parsed by 
> > odp_actions_from_string().  Usually we expect this.  (Probably the syntax should be adjusted to make parsing more straightforward.) <Anju>  odp_actions_from string is used to convert string to action for parsing .But we are using the drop action reason only fo display. We do not want to provide an option for its parsing. Is my understanding correct.
> 
> Why not?  Ordinarily, anything that can be output can be input.  It is useful for testing to be able to input datapath actions.
> 
> Agreed. Ideally yes. But in this case the error are set due to some error during translation like *bridge not found 
> *too many resubmits 
> *recursion too deep 
> *too many resubmits 
> *sack too deeo
> *no recirculation context
> *too many mpls labels
> * invalid tunnel metadata
> * unsupported packet type
> * ecn mismatch at tunnel decapsulation
> * forwarding disabled
> * pipeline drop
> 
> Except pipeline drop I don’t understand how it will make sense to inject others as input . Can you explain if I am missing something ?

I don't have an immediate use case for injecting these.

At the very least, I want the output format to be one that is parseable,
whether we decide to actually parse it now or not.
Anju Thomas Jan. 24, 2019, 8:04 a.m. UTC | #13
Thanks Ben and Ilya.
I will incorporate the other changes. 

I just have below comments.

>> @@ -2427,8 +2474,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"))) {
> 
> Why you're adding parse only for this type of drop action ?
> <Anju>The rest are all below error scenarios which we hit during processing and hence I thought perhaps may not make sense as an input?? . Do you think it make sense to do that ?

I think, Ben already answered this questin.
IMHO, that parsing of only one type makes no sence. There should be all
or nothing. As we think that OVS should be able to parse flows it generates,
there should be all.


<Anju> In that case, i propose removing this to ensure no subtypes are parsed.  
Can we agree on this ? In case in future there is a use case,we can revisit this .




> 
> Above counters could be defined inside odp-execute.c. You already have
> 2 other counters there. In this case there will be no need to introduce
> additional 'dp_update_drop_action_counter_cb' callback and pass it around.
> All the required logic will also be moved to odp-execute.c.
> 
> <Anju> But I want the callback to be NULL when called from dpif_execute_with_help

Why? This seems strange to count drops only for actions that do not need
help from the dpif layer.

<Anju>  For now we would want to add only for drop in actual dpdk datapath. We will revist this
Later




Regards
Anju



-----Original Message-----
From: Ilya Maximets [mailto:i.maximets@samsung.com] 
Sent: Wednesday, January 23, 2019 8:27 PM
To: Anju Thomas <anju.thomas@ericsson.com>; dev@openvswitch.org
Cc: Keshav Gupta <keshugupta1@gmail.com>; Stokes, Ian <ian.stokes@intel.com>; Ben Pfaff <blp@ovn.org>
Subject: Re: [ovs-dev,v6] Improved Packet Drop Statistics in OVS

On 23.01.2019 16:53, Anju Thomas wrote:
> Hi Ilya ,
> Some queries w.r.t your comments .
> 
> Regards
> Anju
> 
> -----Original Message-----
> From: Ilya Maximets [mailto:i.maximets@samsung.com] 
> Sent: Friday, January 18, 2019 5:33 PM
> To: Anju Thomas <anju.thomas@ericsson.com>; dev@openvswitch.org
> Cc: Keshav Gupta <keshugupta1@gmail.com>; Stokes, Ian <ian.stokes@intel.com>; Ben Pfaff <blp@ovn.org>
> Subject: Re: [ovs-dev,v6] Improved Packet Drop Statistics in OVS
> 
> Hi.
> Thanks for working on this.
> Some general comments:
> 
> 1. This patch consists of two separate features:
>    - Reporting the drop reason in flow dumps.
>    - Counters for different drop types.
>    I still think that there should be patch-set of two separate patches.
>    This will simplify the review and allow to apply them separately/speed
>    up accepting.
> 
> 2. There are some issues with the patch formatting. The commit message is
>    indented like in the output of 'git show' command.  So, it's double
>    shifted after applying in git. Please remove the additional indentation.
>    You probably should start using 'git format-patch'.
> 
>    Second issue:
> 
>    Applying: Improved Packet Drop Statistics in OVS
>    .git/rebase-apply/patch:415: new blank line at EOF.
>    +
>    warning: 1 line adds whitespace errors.
> 
>    One more thing is that for better readability it's a common practice to
>    limit the width of lines in commit-message to 72 characters. For example,
>    you may strip not important fields of the dump-flows output. Like this:
> 
>        recirc_id(0),in_port(5),<...>, actions:drop:recursion too deep
> 
> 
> Other comments inline.
> 
> <Anju> I use 
> a)git format-patch -1 -s 
> b)  ./utilities/checkpatch.py -1 <patch name>
> 
> Are these the correct parameters that I should be using ?

Commands looks fine. If the patch looks same in your git repo, I'd suggest you
to re-format the patch by hands.

Also, I just spotted that you have a lot of duplicated sign-offs in the patch.

> 
> Best regards, Ilya Maximets. 
> 
> On 17.01.2019 7:49, Anju Thomas 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. Display these drops in ovs-appctl coverage/show
>>     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.
>>
>>      The idea is to use the coverage infrastructure to maintain the drops
>>
>>     $ ovs-appctl dpctl/dump-flows netdev at ovs-netdev
>>     flow-dump from pmd on cpu core: 0
>>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:12, bytes:1176, used:0.884s, actions:drop:recursion too deep
>>
>>     $ ovs-appctl dpif/dump-flows br-int
>>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:25, bytes:2450, used:5.008s, actions:drop:recursion too deep
>>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0806), packets:7, bytes:294, used:0.009s, actions:drop:recursion too deep
>>
>>    In subsequent commits, we are planning to see if we can use this infrastructure to create a
>>    wrapper to clear and display counters as well.
>>
>> Co-authored-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
>> Co-authored-by: Keshav Gupta <keshugupta1@gmail.com>
>> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
>> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
>> Signed-off-by: Keshav Gupta <keshugupta1@gmail.com>
>> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
>> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
>> Signed-off-by: Keshav Gupta <keshugupta1@gmail.com>
>> ---
>>  datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
>>  lib/coverage.c                                    |   6 +-
>>  lib/coverage.h                                    |   3 +-
>>  lib/dpif-netdev.c                                 | 105 +++++++++++-
>>  lib/dpif.c                                        |  10 +-
>>  lib/dpif.h                                        |   3 +
>>  lib/netdev-dpdk.c                                 |   4 +
>>  lib/odp-execute.c                                 |  50 ++++--
>>  lib/odp-execute.h                                 |  10 +-
>>  lib/odp-util.c                                    |  54 +++++-
>>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>>  ofproto/ofproto-dpif-upcall.c                     |   3 +-
>>  ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
>>  ofproto/ofproto-dpif-xlate.h                      |   4 +
>>  ofproto/ofproto-dpif.c                            |  11 +-
>>  ofproto/ofproto-dpif.h                            |   8 +-
>>  tests/automake.mk                                 |   3 +-
>>  tests/bundle.at                                   |   2 +-
>>  tests/classifier.at                               |  10 +-
>>  tests/dpif-netdev.at                              |  17 +-
>>  tests/drop-stats.at                               | 197 ++++++++++++++++++++++
>>  tests/ofproto-dpif.at                             | 118 ++++++-------
>>  tests/ovs-ofctl.at                                |   2 +-
>>  tests/packet-type-aware.at                        |  17 +-
>>  tests/testsuite.at                                |   1 +
>>  tests/tunnel-push-pop.at                          |  26 ++-
>>  tests/tunnel.at                                   |  21 ++-
>>  28 files changed, 695 insertions(+), 107 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 9b087f1..b66b46f 100644
>> --- a/datapath/linux/compat/include/linux/openvswitch.h
>> +++ b/datapath/linux/compat/include/linux/openvswitch.h
>> @@ -819,6 +819,56 @@ struct ovs_action_push_eth {
>>  };
>>  
>>  /**
>> + * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP.
>> + * @OVS_DROP_REASON_OF_PIPELINE: Explicit drop action in the pipeline.
>> + * @OVS_DROP_REASON_BRIDGE_NOT_FOUND: Xlation error generated due to
>> + * unable to determine bridge.
>> + * @OVS_DROP_REASON_RECURSION_TOO_DEEP: Xlation error generated due to
>> + * recursion reached maximum depth.
>> + * @OVS_DROP_REASON_TOO_MANY_RESUBMITS: Xlation error generated due to
>> + * too many resubmits.
>> + * @OVS_DROP_REASON_STACK_TOO_DEEP: Xlation error generated due to stack
>> + * too deep.
>> + * @OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT: Xlation error generated
>> + * due to no recirculation context.
>> + * @OVS_DROP_REASON_RECIRCULATION_CONFLICT: Xlation error generated due to
>> + * conflict in recirculation context.
>> + * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to
>> + * too many mpls labels.
>> + * @OVS_DROP_REASON_INVALID_TUNNEL_METADATA: Xlation error generated due to
>> + * invalid tunnel metadata.
>> + * @OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE: Xlation error generated due to
>> + * unsupported packet type.
>> + * @OVS_DROP_REASON_CONGESTION: Xlation error generated due to ecn mismatch
>> + * during tunnel decapsulation.
>> + * @OVS_DROP_REASON_FORWARDING_DISABLED: Xlation error generated due to
>> + * forwarding is disabled.
>> + */
>> +enum ovs_drop_reason {
>> +	OVS_DROP_REASON_OF_PIPELINE = 0,
>> +	OVS_DROP_REASON_BRIDGE_NOT_FOUND,
>> +	OVS_DROP_REASON_RECURSION_TOO_DEEP,
>> +	OVS_DROP_REASON_TOO_MANY_RESUBMITS,
>> +	OVS_DROP_REASON_STACK_TOO_DEEP,
>> +	OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT,
>> +	OVS_DROP_REASON_RECIRCULATION_CONFLICT,
>> +	OVS_DROP_REASON_TOO_MANY_MPLS_LABELS,
>> +	OVS_DROP_REASON_INVALID_TUNNEL_METADATA,
>> +	OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE,
>> +	OVS_DROP_REASON_CONGESTION,
>> +	OVS_DROP_REASON_FORWARDING_DISABLED,
>> +	OVS_DROP_REASON_MAX,
>> +};
>> +
>> +/*
>> + * struct ovs_action_drop - %OVS_ACTION_ATTR_DROP action argument.
>> + * @drop_reason: Reason for installing drop action.
>> + */
>> +struct ovs_action_drop {
>> +	enum ovs_drop_reason drop_reason;
>> +};
>> +
>> +/**
>>   * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
>>   *
>>   * @OVS_NAT_ATTR_SRC: Flag for Source NAT (mangle source address/port).
>> @@ -938,6 +988,7 @@ enum ovs_action_attr {
>>  	OVS_ACTION_ATTR_POP_NSH,      /* No argument. */
>>  	OVS_ACTION_ATTR_METER,        /* u32 meter number. */
>>  	OVS_ACTION_ATTR_CLONE,        /* Nested OVS_CLONE_ATTR_*.  */
>> +        OVS_ACTION_ATTR_DROP,         /* explicit drop action. */
> 
> This is the linux kernel header. You should use tabs here.
>>  
>>  #ifndef __KERNEL__
>>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
>> diff --git a/lib/coverage.c b/lib/coverage.c
>> index 6cef826..c9acc37 100644
>> --- a/lib/coverage.c
>> +++ b/lib/coverage.c
>> @@ -32,9 +32,7 @@ VLOG_DEFINE_THIS_MODULE(coverage);
>>  static struct coverage_counter **coverage_counters = NULL;
>>  static size_t n_coverage_counters = 0;
>>  static size_t allocated_coverage_counters = 0;
>> -
>> -static struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
> 
> This is leftover from the previous versions. No need to expose
> 'coverage_mutex' in current patch.
>> -
>> +struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
>>  DEFINE_STATIC_PER_THREAD_DATA(long long int, coverage_clear_time, LLONG_MIN);
>>  static long long int coverage_run_time = LLONG_MIN;
>>  
>> @@ -52,7 +50,7 @@ coverage_counter_register(struct coverage_counter* counter)
>>      if (n_coverage_counters >= allocated_coverage_counters) {
>>          coverage_counters = x2nrealloc(coverage_counters,
>>                                         &allocated_coverage_counters,
>> -                                       sizeof(struct coverage_counter*));
>> +                                       sizeof(struct coverage_counter *));
> 
> this change is also not needed.
>>      }
>>      coverage_counters[n_coverage_counters++] = counter;
>>  }
>> diff --git a/lib/coverage.h b/lib/coverage.h
>> index dea990e..45b1dbd 100644
>> --- a/lib/coverage.h
>> +++ b/lib/coverage.h
>> @@ -54,7 +54,8 @@ struct coverage_counter {
>>      unsigned int hr[HR_AVG_LEN];
>>  };
>>  
>> -void coverage_counter_register(struct coverage_counter*);
>> +extern struct ovs_mutex coverage_mutex;
>> +void coverage_counter_register(struct coverage_counter *);
> 
> This is leftover from the previous versions. No need to expose
> 'coverage_mutex' in current patch.
>>  
>>  /* Defines COUNTER.  There must be exactly one such definition at file scope
>>   * within a program. */
>> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
>> index be529b6..8749cba 100644
>> --- a/lib/dpif-netdev.c
>> +++ b/lib/dpif-netdev.c
>> @@ -100,6 +100,30 @@ enum { MAX_METERS = 65536 };    /* Maximum number of meters. */
>>  enum { MAX_BANDS = 8 };         /* Maximum number of bands / meter. */
>>  enum { N_METER_LOCKS = 64 };    /* Maximum number of meters. */
>>  
>> +
>> +COVERAGE_DEFINE(drop_action_of_pipeline);
>> +COVERAGE_DEFINE(drop_action_bridge_not_found);
>> +COVERAGE_DEFINE(drop_action_recursion_too_deep);
>> +COVERAGE_DEFINE(drop_action_too_many_resubmit);
>> +COVERAGE_DEFINE(drop_action_stack_too_deep);
>> +COVERAGE_DEFINE(drop_action_no_recirculation_context);
>> +COVERAGE_DEFINE(drop_action_recirculation_conflict);
>> +COVERAGE_DEFINE(drop_action_too_many_mpls_labels);
>> +COVERAGE_DEFINE(drop_action_invalid_tunnel_metadata);
>> +COVERAGE_DEFINE(drop_action_unsupported_packet_type);
>> +COVERAGE_DEFINE(drop_action_congestion);
>> +COVERAGE_DEFINE(drop_action_forwarding_disabled);
> 
> Above counters could be defined inside odp-execute.c. You already have
> 2 other counters there. In this case there will be no need to introduce
> additional 'dp_update_drop_action_counter_cb' callback and pass it around.
> All the required logic will also be moved to odp-execute.c.
> 
> <Anju> But I want the callback to be NULL when called from dpif_execute_with_help

Why? This seems strange to count drops only for actions that do not need
help from the dpif layer.

> 
>> +COVERAGE_DEFINE(dp_meter_drop);
>> +COVERAGE_DEFINE(dp_upcall_error_drop);
>> +COVERAGE_DEFINE(dp_lock_error_drop);
>> +COVERAGE_DEFINE(dp_userspace_action_error_drop);
>> +COVERAGE_DEFINE(dp_tunnel_push_error_drop);
>> +COVERAGE_DEFINE(dp_tunnel_pop_error_drop);
>> +COVERAGE_DEFINE(dp_recirc_error_drop);
>> +COVERAGE_DEFINE(dp_invalid_port_drop);
>> +COVERAGE_DEFINE(dp_invalid_tnl_port_drop);
>> +COVERAGE_DEFINE(rx_invalid_packet_drop);
> 
> What do you think about renaming above counters to 'datapath_drop_*' ?
> IMHO, it'll be more visually recognizable.
> Like 'datapath_drop_rx_invalid_packet' or 'datapath_drop_upcall_error'.
>  
> <Anju> Do you mean instead of using dp_* to use datapath_*??

I mean using 'datapath_drop_' as a prefix.

>> +
>>  /* Protects against changes to 'dp_netdevs'. */
>>  static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>>  
>> @@ -829,7 +853,6 @@ static inline bool
>>  pmd_perf_metrics_enabled(const struct dp_netdev_pmd_thread *pmd);
>>  static void queue_netdev_flow_del(struct dp_netdev_pmd_thread *pmd,
>>                                    struct dp_netdev_flow *flow);
>> -
> 
> No need to remove an empty line.
> 
>>  static void
>>  emc_cache_init(struct emc_cache *flow_cache)
>>  {
>> @@ -1388,6 +1411,7 @@ dpif_netdev_init(void)
>>      return 0;
>>  }
>>  
>> +
> 
> No need to add an empty line.
>>  static int
>>  dpif_netdev_enumerate(struct sset *all_dps,
>>                        const struct dpif_class *dpif_class)
>> @@ -5563,7 +5587,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, struct dp_packet_batch *packets_,
>>              band = &meter->bands[exceeded_band[j]];
>>              band->packet_count += 1;
>>              band->byte_count += dp_packet_size(packet);
>> -
> Please, keep this empty line for readability.
> 
>> +            COVERAGE_INC(dp_meter_drop);
>>              dp_packet_delete(packet);
>>          } else {
>>              /* Meter accepts packet. */
>> @@ -6320,6 +6344,7 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
>>  
>>          if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) {
>>              dp_packet_delete(packet);
>> +            COVERAGE_INC(rx_invalid_packet_drop);
>>              continue;
>>          }
>>  
>> @@ -6446,6 +6471,7 @@ handle_packet_upcall(struct dp_netdev_pmd_thread *pmd,
>>                               put_actions);
>>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>>          dp_packet_delete(packet);
>> +        COVERAGE_INC(dp_upcall_error_drop);
>>          return error;
>>      }
>>  
>> @@ -6577,6 +6603,7 @@ 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);
>> +                COVERAGE_INC(dp_lock_error_drop);
>>                  upcall_fail_cnt++;
>>              }
>>          }
>> @@ -6846,10 +6873,59 @@ dp_execute_userspace_action(struct dp_netdev_pmd_thread *pmd,
>>                                    actions->data, actions->size);
>>      } else if (should_steal) {
>>          dp_packet_delete(packet);
>> +        COVERAGE_INC(dp_userspace_action_error_drop);
>>      }
>>  }
>>  
>>  static void
>> +dp_update_drop_action_counter_cb(enum ovs_drop_reason drop_reason,
>> +                          int delta)
> 
> Indents are a bit off.
>> +    OVS_NO_THREAD_SAFETY_ANALYSIS
> 
> Is it necessary to disable thread safety analysis ?
> <Anju> Since we are using COVERAGE infra which is defining thread specific data, i thought we might need to use this. Is my understanding incorrect?

Coverage infra is thread safe. And clang doesn't seem to produce any warnings
without this annotation. Thread safety analysis should be disabled only if you
have clang warnings and can not resolve them in other way.

>> +{
>> +   switch (drop_reason) {
>> +   case OVS_DROP_REASON_OF_PIPELINE:
>> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
>> +        break;
>> +   case OVS_DROP_REASON_BRIDGE_NOT_FOUND:
>> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
>> +        break;
>> +   case OVS_DROP_REASON_RECURSION_TOO_DEEP:
>> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
>> +        break;
>> +   case OVS_DROP_REASON_TOO_MANY_RESUBMITS:
>> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
>> +        break;
>> +   case OVS_DROP_REASON_STACK_TOO_DEEP:
>> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
>> +        break;
>> +   case OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT:
>> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
>> +        break;
>> +   case OVS_DROP_REASON_RECIRCULATION_CONFLICT:
>> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
>> +        break;
>> +   case OVS_DROP_REASON_TOO_MANY_MPLS_LABELS:
>> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
>> +        break;
>> +   case OVS_DROP_REASON_INVALID_TUNNEL_METADATA:
>> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
>> +        break;
>> +   case OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE:
>> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
>> +        break;
>> +   case OVS_DROP_REASON_CONGESTION:
>> +        COVERAGE_ADD(drop_action_congestion, delta);
>> +        break;
>> +   case OVS_DROP_REASON_FORWARDING_DISABLED:
>> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
>> +        break;
>> +   case OVS_DROP_REASON_MAX:
>> +   default:
>> +        VLOG_ERR("Invalid Drop reason type:%d",drop_reason);
> 
> We might want the rate limiting for above message to not have it for each packet.
>> +   }
>> +}
>> +
>> +static void
>>  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>>                const struct nlattr *a, bool should_steal)
>>      OVS_NO_THREAD_SAFETY_ANALYSIS
>> @@ -6860,6 +6936,7 @@ 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;
>>  
>>      switch ((enum ovs_action_attr)type) {
>>      case OVS_ACTION_ATTR_OUTPUT:
>> @@ -6901,6 +6978,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>>                  dp_packet_batch_add(&p->output_pkts, packet);
>>              }
>>              return;
>> +        } else {
>> +            COVERAGE_ADD(dp_invalid_port_drop, packets_->count);
>>          }
>>          break;
>>  
>> @@ -6910,10 +6989,13 @@ 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. */
>> +            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
>>              break;
>>          }
>>          dp_packet_batch_apply_cutlen(packets_);
>> -        push_tnl_action(pmd, a, packets_);
>> +        if (push_tnl_action(pmd, a, packets_)) {
>> +            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
>> +        }
>>          return;
>>  
>>      case OVS_ACTION_ATTR_TUNNEL_POP:
>> @@ -6933,7 +7015,12 @@ 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) {
>> +                    COVERAGE_ADD(dp_tunnel_pop_error_drop, packet_dropped);
>> +                }
>>                  if (dp_packet_batch_is_empty(packets_)) {
>>                      return;
>>                  }
>> @@ -6947,7 +7034,11 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>>                  dp_netdev_recirculate(pmd, packets_);
>>                  (*depth)--;
>>                  return;
>> +            } else {
>> +                COVERAGE_ADD(dp_invalid_tnl_port_drop, packets_->count);
>>              }
>> +        } else {
>> +            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
>>          }
>>          break;
>>  
>> @@ -6991,6 +7082,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>>              fat_rwlock_unlock(&dp->upcall_rwlock);
>>  
>>              return;
>> +        } else {
>> +            COVERAGE_ADD(dp_lock_error_drop, packets_->count);
>>          }
>>          break;
>>  
>> @@ -7013,6 +7106,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>>              (*depth)--;
>>  
>>              return;
>> +        } else {
>> +            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
>>          }
> 
> 'Else' condition is not needed here as we're returning inside the 'if'.
> This is actually applicable to most of other places in this function,
> but in this one we already have a warning below without 'else' condition.
>>  
>>          VLOG_WARN("Packet dropped. Max recirculation depth exceeded.");
>> @@ -7167,6 +7262,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();
>>      }
>> @@ -7183,7 +7279,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_action_counter_cb);
> 
> As I already wrote, we'll not need to have a callback function if you'll
> move the 'drop_action_*' counters to odp-execute.c.
>>  }
>>  
>>  struct dp_netdev_ct_dump {
>> diff --git a/lib/dpif.c b/lib/dpif.c
>> index e35f111..21f9f54 100644
>> --- a/lib/dpif.c
>> +++ b/lib/dpif.c
>> @@ -1273,6 +1273,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();
>> @@ -1295,7 +1296,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;
>>  }
>>  
>> @@ -1879,6 +1880,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,
>> @@ -1976,3 +1983,4 @@ dpif_meter_del(struct dpif *dpif, ofproto_meter_id meter_id,
>>      }
>>      return error;
>>  }
>> +
>> diff --git a/lib/dpif.h b/lib/dpif.h
>> index 475d5a6..e799da8 100644
>> --- a/lib/dpif.h
>> +++ b/lib/dpif.h
>> @@ -888,6 +888,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 320422b..0c3bc9c 100644
>> --- a/lib/netdev-dpdk.c
>> +++ b/lib/netdev-dpdk.c
>> @@ -2395,6 +2395,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);
> 
> This is the unrelated bug fix that should be sent as a separate patch.
> In this case we'll be able to apply it and backport to previous versions.
> 
> Adding Ian to CC.
> 
>>          dp_packet_delete_batch(batch, true);
>>          return;
>>      }
>> diff --git a/lib/odp-execute.c b/lib/odp-execute.c
>> index 3b6890e..c0db93f 100644
>> --- a/lib/odp-execute.c
>> +++ b/lib/odp-execute.c
>> @@ -25,6 +25,7 @@
>>  #include <stdlib.h>
>>  #include <string.h>
>>  
>> +#include "coverage.h"
>>  #include "dp-packet.h"
>>  #include "dpif.h"
>>  #include "netlink.h"
>> @@ -37,6 +38,10 @@
>>  #include "csum.h"
>>  #include "conntrack.h"
>>  
>> +COVERAGE_DEFINE(dp_sample_error_drop);
>> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
>> +
>> +
>>  /* Masked copy of an ethernet address. 'src' is already properly masked. */
>>  static void
>>  ether_addr_copy_masked(struct eth_addr *dst, const struct eth_addr src,
>> @@ -575,7 +580,9 @@ 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_action_counter_cb
>> +                     dp_update_drop_action_counter)
>>  {
>>      const struct nlattr *subactions = NULL;
>>      const struct nlattr *a;
>> @@ -589,6 +596,7 @@ 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) {
>> +                    COVERAGE_ADD(dp_sample_error_drop, 1);
>>                      dp_packet_delete(packet);
>>                  }
>>                  return;
>> @@ -616,13 +624,16 @@ 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_action_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_action_counter_cb
>> +                      dp_update_drop_action_counter)
>>  {
>>      if (!steal) {
>>          /* The 'actions' may modify the packet, but the modification
>> @@ -634,11 +645,12 @@ 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);
>> -    }
>> -    else {
>> +                        nl_attr_get_size(actions), dp_execute_action,
>> +                        dp_update_drop_action_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_action_counter);
>>      }
>>  }
>>  
>> @@ -673,6 +685,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,12 +712,15 @@ 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_action_counter_cb
>> +                       dp_update_drop_action_counter)
>>  {
>>      struct dp_packet *packet;
>>      const struct nlattr *a;
>>      unsigned int left;
>>  
>> +
> 
> No need to add an empty line.
> 
>>      NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
>>          int type = nl_attr_type(a);
>>          bool last_action = (left <= NLA_ALIGN(a->nla_len));
>> @@ -822,7 +838,8 @@ 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_action_counter);
>>              }
>>  
>>              if (last_action) {
>> @@ -845,7 +862,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_action_counter);
>>              if (last_action) {
>>                  /* We do not need to free the packets. odp_execute_clone() has
>>                   * stolen them.  */
>> @@ -889,6 +907,7 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
>>                  if (pop_nsh(packet)) {
>>                      dp_packet_batch_refill(batch, packet, i);
>>                  } else {
>> +                    COVERAGE_INC(dp_nsh_decap_error_drop);
>>                      dp_packet_delete(packet);
>>                  }
>>              }
>> @@ -900,6 +919,17 @@ 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_action_counter) {
>> +                 dp_update_drop_action_counter(drop_reason, 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..a84ecbb 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_action_counter_cb) (
>> +                                           enum  ovs_drop_reason drop_reason,
>> +                                           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,7 @@ 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_action_counter_cb
>> +                             dp_update_drop_action_counter_cb);
>>  #endif
>> diff --git a/lib/odp-util.c b/lib/odp-util.c
>> index 0491bed..9754fd5 100644
>> --- a/lib/odp-util.c
>> +++ b/lib/odp-util.c
>> @@ -131,6 +131,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:
>> @@ -345,6 +346,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));
> 
> As I already wrote for the previous versions it'll be good to add
> some visible markers around the drop reason. Spaces in flow dumps
> are usually not used and it could add some complications for the
> parsing scripts. You may use brackets like "drop(stack too deep)"
> or replace the spaces with '-' or '_' like "drop:stack_too_deep".
>> +}
>> +
>>  static void
>>  format_odp_push_nsh_action(struct ds *ds,
>>                             const struct nsh_hdr *nsh_hdr)
>> @@ -1181,6 +1225,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:
>> @@ -2427,8 +2474,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"))) {
> 
> Why you're adding parse only for this type of drop action ?
> <Anju>The rest are all below error scenarios which we hit during processing and hence I thought perhaps may not make sense as an input?? . Do you think it make sense to do that ?

I think, Ben already answered this questin.
IMHO, that parsing of only one type makes no sence. There should be all
or nothing. As we think that OVS should be able to parse flows it generates,
there should be all.

> 
> *no recirculation context 
> *too many mpls labels
> * invalid tunnel metadata
> * unsupported packet type
> * ecn mismatch at tunnel decapsulation
> * forwarding disabled
> * 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 7da3175..69ed7b8 100644
>> --- a/ofproto/ofproto-dpif-sflow.c
>> +++ b/ofproto/ofproto-dpif-sflow.c
>> @@ -1222,6 +1222,7 @@ dpif_sflow_read_actions(const struct flow *flow,
>>          case OVS_ACTION_ATTR_PUSH_NSH:
>>          case OVS_ACTION_ATTR_POP_NSH:
>>          case OVS_ACTION_ATTR_UNSPEC:
>> +        case OVS_ACTION_ATTR_DROP:
>>          case __OVS_ACTION_ATTR_MAX:
>>          default:
>>              break;
>> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
>> index dc30824..b1ce2ec 100644
>> --- a/ofproto/ofproto-dpif-upcall.c
>> +++ b/ofproto/ofproto-dpif-upcall.c
>> @@ -1154,7 +1154,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)
>>  {
>> @@ -1244,6 +1244,7 @@ upcall_xlate(struct udpif *udpif, struct upcall *upcall,
>>      if (upcall->type == MISS_UPCALL) {
>>          upcall->ukey = ukey_create_from_upcall(upcall, wc);
>>      }
>> +    return xerr;
> 
> The only user of this function is 'process_upcall' and it does not check
> the result.
> 
>>  }
>>  
>>  static void
>> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
>> index 839fddd..32c4edf 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);
>>  
>> @@ -5921,6 +5957,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);
> 
> Indents are a bit off.
> 
>> +
>> +}
>> +
>> +static void
>>  put_ct_helper(struct xlate_ctx *ctx,
>>                struct ofpbuf *odp_actions, struct ofpact_conntrack *ofc)
>>  {
>> @@ -7383,6 +7430,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;
>> @@ -7415,6 +7466,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) {
>> @@ -7522,6 +7574,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 0a5a528..bb3f9ff 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 14fe6fc..609226a 100644
>> --- a/ofproto/ofproto-dpif.c
>> +++ b/ofproto/ofproto-dpif.c
>> @@ -827,6 +827,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.
>> @@ -1397,6 +1403,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);
>> @@ -5776,6 +5784,7 @@ ofproto_unixctl_dpif_set_dp_features(struct unixctl_conn *conn,
>>      ds_destroy(&ds);
>>  }
>>  
>> +
> 
> No need to add an empty line.
> 
>>  static void
>>  ofproto_unixctl_init(void)
>>  {
>> @@ -5809,7 +5818,7 @@ ofproto_unixctl_init(void)
>>      unixctl_command_register("dpif/set-dp-features", "bridge", 1, 3 ,
>>                               ofproto_unixctl_dpif_set_dp_features, NULL);
>>  }
>> -
> 
>> +
> 
> Don't drop the ^L character.
> <Anju> What is the significance of this character

Form feeds used in OVS to divide long source files into logical pieces.
This is documented in the coding-style.

>>  static odp_port_t
>>  ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
>>  {
>> 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 *);
>>  
>> +
> 
> No need to add an empty line.
> 
>>  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 92d56b2..22b4722 100644
>> --- a/tests/automake.mk
>> +++ b/tests/automake.mk
>> @@ -108,7 +108,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
> 
> automake.mk uses tabs.
> 
>>  
>>  EXTRA_DIST += $(FUZZ_REGRESSION_TESTS)
>>  FUZZ_REGRESSION_TESTS = \
>> diff --git a/tests/bundle.at b/tests/bundle.at
>> index 0a4eadc..33fe249 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 6915d43..08349f6 100644
>> --- a/tests/dpif-netdev.at
>> +++ b/tests/dpif-netdev.at
>> @@ -1,4 +1,4 @@
>> -AT_BANNER([dpif-netdev])
>> +T_BANNER([dpif-netdev])
> 
> Something wrong here.
> 
>>  
>>  m4_divert_push([PREPARE_TESTS])
>>  [
>> @@ -281,6 +281,7 @@ type=drop rate=1 burst_size=2
>>  ])
>>  
>>  ovs-appctl time/warp 5000
>> +sleep 10
> 
> Do we really need to sleep for so long ?
> 
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>> @@ -291,7 +292,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>> -sleep 1  # wait for forwarders process packets
>> +# wait for forwarders process packets
> 
> Something wrong here too.
> 
>>  
>>  # Meter 1 is measuring packets, allowing one packet per second with
>>  # bursts of one packet, so 4 out of 5 packets should hit the drop
>> @@ -320,7 +321,8 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>> -sleep 1  # wait for forwarders process packets
>> +
>> +sleep 10  # wait for forwarders process packets
>>  
>>  # Meter 1 is measuring packets, allowing one packet per second with
>>  # bursts of one packet, so all 5 of the new packets should hit the drop
>> @@ -337,6 +339,15 @@ meter:2 flow_count:1 packet_in_count:10 byte_in_count:600 duration:0.0s bands:
>>  0: packet_count:5 byte_count:300
>>  ])
>>  
>> +ovs-appctl time/warp 5000
>> +sleep 10
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "dp_meter_drop" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +14
>> +])
>> +
>>  AT_CHECK([cat ovs-vswitchd.log | filter_flow_install | strip_xout_keep_actions], [0], [dnl
>>  recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:meter(0),7
>>  recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:8
>> diff --git a/tests/drop-stats.at b/tests/drop-stats.at
>> new file mode 100644
>> index 0000000..be8b8ae
>> --- /dev/null
>> +++ b/tests/drop-stats.at
>> @@ -0,0 +1,197 @@
>> +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
>> +])
>> +
>> +sleep 1
>> +
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +3
>> +])
>> +
>> +
>> +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
> 
> If possible, it's preferred to use odp format (i.e. declarative) for
> packets definition. This allowes user to see what packet is sent without
> parsing it by external tools.
> Same for all other places.
> 
>> +], [0], [ignore])
>> +
>> +sleep 1
>> +
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +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])
>> +
>> +sleep 1
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "drop_action_recursion_too_deep" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +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)'
>> +
>> +sleep 1
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "drop_action_too_many_resubmit" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +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)'
>> +
>> +sleep 1
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "drop_action_stack_too_deep" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +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])
>> +
>> +sleep 1
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "drop_action_too_many_mpls_labels" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +1
>> +])
>> +
>> +
>> +OVS_VSWITCHD_STOP(["/|WARN|/d"])
>> +AT_CLEANUP
>> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
>> index ded2ef0..298aa9a 100644
>> --- a/tests/ofproto-dpif.at
>> +++ b/tests/ofproto-dpif.at
>> @@ -3496,51 +3496,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" \
>> @@ -3555,8 +3555,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" \
>> @@ -3573,16 +3573,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"
>> @@ -4322,11 +4322,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
>> @@ -4404,8 +4404,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
>> @@ -5751,7 +5751,7 @@ bridge("br0")
>>  
>>  Final flow: <cleared>
>>  Megaflow: <cleared>
>> -Datapath actions: drop
>> +Datapath actions: drop:pipeline-drop
>>  ])
>>  
>>  dnl Now, try again without megaflows:
>> @@ -5772,7 +5772,7 @@ bridge("br0")
>>  
>>  Final flow: <cleared>
>>  Megaflow: <cleared>
>> -Datapath actions: drop
>> +Datapath actions: drop:pipeline-drop
>>  ])
>>  
>>  OVS_VSWITCHD_STOP
>> @@ -7066,7 +7066,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"])
>> @@ -7158,7 +7158,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
>> @@ -7832,21 +7832,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
>> @@ -7869,7 +7869,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
>> @@ -8617,11 +8617,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])
>> @@ -9348,7 +9348,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
>> @@ -9377,16 +9377,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
>> @@ -9711,7 +9711,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)
>> @@ -10398,7 +10398,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])
>> @@ -10490,7 +10490,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 6ae3470..cd19d41 100644
>> --- a/tests/ovs-ofctl.at
>> +++ b/tests/ovs-ofctl.at
>> @@ -2999,7 +2999,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..7c1b551 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,14 @@ 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 coverage/show | grep "drop_action_unsupported_packet_type" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +2
>>  ])
>>  
>>  # Encap(ethernet) on Ethernet frame -> should be droped
>> @@ -587,7 +594,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 +616,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 +777,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 b840dbf..922ba48 100644
>> --- a/tests/testsuite.at
>> +++ b/tests/testsuite.at
>> @@ -82,3 +82,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..385c114 100644
>> --- a/tests/tunnel-push-pop.at
>> +++ b/tests/tunnel-push-pop.at
>> @@ -447,6 +447,27 @@ 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 netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>> +
>> +ovs-appctl time/warp 1200
>> +
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "dp_tunnel_pop_error_drop" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +1
>> +])
>> +
>> +
>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>> +ovs-appctl time/warp 1200
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "drop_action_congestion" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +1
>> +])
>> +
>>  dnl Check GREL3 only accepts non-fragmented packets?
>>  AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>  
>> @@ -455,7 +476,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 +531,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 55fb1d3..3646c06 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 netdev-dummy/receive p2 'aa55aa550001f8bc124434b6080045000054ba20000040018486010103580101037001004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>> +sleep 2
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "dp_invalid_port_drop" | cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +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 Jan. 24, 2019, 1:45 p.m. UTC | #14
On 24.01.2019 11:04, Anju Thomas wrote:
> Thanks Ben and Ilya.
> I will incorporate the other changes. 
> 
> I just have below comments.
> 
>>> @@ -2427,8 +2474,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"))) {
>>
>> Why you're adding parse only for this type of drop action ?
>> <Anju>The rest are all below error scenarios which we hit during processing and hence I thought perhaps may not make sense as an input?? . Do you think it make sense to do that ?
> 
> I think, Ben already answered this questin.
> IMHO, that parsing of only one type makes no sence. There should be all
> or nothing. As we think that OVS should be able to parse flows it generates,
> there should be all.
> 
> 
> <Anju> In that case, i propose removing this to ensure no subtypes are parsed.  
> Can we agree on this ? In case in future there is a use case,we can revisit this .

IMHO, ovs should not fail to parse flows it generates. So, at least, even if
you will not set proper error value in the structure, you need to allow successful
parsing of "drop:.*" strings there. But still fully correct parsing is preferred.
I'd like to leave the decision to Ben.

> 
> 
> 
> 
>>
>> Above counters could be defined inside odp-execute.c. You already have
>> 2 other counters there. In this case there will be no need to introduce
>> additional 'dp_update_drop_action_counter_cb' callback and pass it around.
>> All the required logic will also be moved to odp-execute.c.
>>
>> <Anju> But I want the callback to be NULL when called from dpif_execute_with_help
> 
> Why? This seems strange to count drops only for actions that do not need
> help from the dpif layer.
> 
> <Anju>  For now we would want to add only for drop in actual dpdk datapath. We will revist this
> Later

I still do not understand that. Why not? You're writing a big amount of code
just to avoid counting drops for actions executed with help. If you're
planning to count them in the future you'll need to revert those changes later.
Anyway, these packets are part of the same datapath and should be accounted.

> 
> 
> 
> 
> Regards
> Anju
> 
> 
> 
> -----Original Message-----
> From: Ilya Maximets [mailto:i.maximets@samsung.com] 
> Sent: Wednesday, January 23, 2019 8:27 PM
> To: Anju Thomas <anju.thomas@ericsson.com>; dev@openvswitch.org
> Cc: Keshav Gupta <keshugupta1@gmail.com>; Stokes, Ian <ian.stokes@intel.com>; Ben Pfaff <blp@ovn.org>
> Subject: Re: [ovs-dev,v6] Improved Packet Drop Statistics in OVS
> 
> On 23.01.2019 16:53, Anju Thomas wrote:
>> Hi Ilya ,
>> Some queries w.r.t your comments .
>>
>> Regards
>> Anju
>>
>> -----Original Message-----
>> From: Ilya Maximets [mailto:i.maximets@samsung.com] 
>> Sent: Friday, January 18, 2019 5:33 PM
>> To: Anju Thomas <anju.thomas@ericsson.com>; dev@openvswitch.org
>> Cc: Keshav Gupta <keshugupta1@gmail.com>; Stokes, Ian <ian.stokes@intel.com>; Ben Pfaff <blp@ovn.org>
>> Subject: Re: [ovs-dev,v6] Improved Packet Drop Statistics in OVS
>>
>> Hi.
>> Thanks for working on this.
>> Some general comments:
>>
>> 1. This patch consists of two separate features:
>>    - Reporting the drop reason in flow dumps.
>>    - Counters for different drop types.
>>    I still think that there should be patch-set of two separate patches.
>>    This will simplify the review and allow to apply them separately/speed
>>    up accepting.
>>
>> 2. There are some issues with the patch formatting. The commit message is
>>    indented like in the output of 'git show' command.  So, it's double
>>    shifted after applying in git. Please remove the additional indentation.
>>    You probably should start using 'git format-patch'.
>>
>>    Second issue:
>>
>>    Applying: Improved Packet Drop Statistics in OVS
>>    .git/rebase-apply/patch:415: new blank line at EOF.
>>    +
>>    warning: 1 line adds whitespace errors.
>>
>>    One more thing is that for better readability it's a common practice to
>>    limit the width of lines in commit-message to 72 characters. For example,
>>    you may strip not important fields of the dump-flows output. Like this:
>>
>>        recirc_id(0),in_port(5),<...>, actions:drop:recursion too deep
>>
>>
>> Other comments inline.
>>
>> <Anju> I use 
>> a)git format-patch -1 -s 
>> b)  ./utilities/checkpatch.py -1 <patch name>
>>
>> Are these the correct parameters that I should be using ?
> 
> Commands looks fine. If the patch looks same in your git repo, I'd suggest you
> to re-format the patch by hands.
> 
> Also, I just spotted that you have a lot of duplicated sign-offs in the patch.
> 
>>
>> Best regards, Ilya Maximets. 
>>
>> On 17.01.2019 7:49, Anju Thomas 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. Display these drops in ovs-appctl coverage/show
>>>     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.
>>>
>>>      The idea is to use the coverage infrastructure to maintain the drops
>>>
>>>     $ ovs-appctl dpctl/dump-flows netdev at ovs-netdev
>>>     flow-dump from pmd on cpu core: 0
>>>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:12, bytes:1176, used:0.884s, actions:drop:recursion too deep
>>>
>>>     $ ovs-appctl dpif/dump-flows br-int
>>>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:25, bytes:2450, used:5.008s, actions:drop:recursion too deep
>>>     recirc_id(0),in_port(5),packet_type(ns=0,id=0),eth_type(0x0806), packets:7, bytes:294, used:0.009s, actions:drop:recursion too deep
>>>
>>>    In subsequent commits, we are planning to see if we can use this infrastructure to create a
>>>    wrapper to clear and display counters as well.
>>>
>>> Co-authored-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
>>> Co-authored-by: Keshav Gupta <keshugupta1@gmail.com>
>>> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
>>> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
>>> Signed-off-by: Keshav Gupta <keshugupta1@gmail.com>
>>> Signed-off-by: Anju Thomas <anju.thomas@ericsson.com>
>>> Signed-off-by: Rohith Basavaraja <rohith.basavaraja@gmail.com>
>>> Signed-off-by: Keshav Gupta <keshugupta1@gmail.com>
>>> ---
>>>  datapath/linux/compat/include/linux/openvswitch.h |  51 ++++++
>>>  lib/coverage.c                                    |   6 +-
>>>  lib/coverage.h                                    |   3 +-
>>>  lib/dpif-netdev.c                                 | 105 +++++++++++-
>>>  lib/dpif.c                                        |  10 +-
>>>  lib/dpif.h                                        |   3 +
>>>  lib/netdev-dpdk.c                                 |   4 +
>>>  lib/odp-execute.c                                 |  50 ++++--
>>>  lib/odp-execute.h                                 |  10 +-
>>>  lib/odp-util.c                                    |  54 +++++-
>>>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>>>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>>>  ofproto/ofproto-dpif-upcall.c                     |   3 +-
>>>  ofproto/ofproto-dpif-xlate.c                      |  64 +++++++
>>>  ofproto/ofproto-dpif-xlate.h                      |   4 +
>>>  ofproto/ofproto-dpif.c                            |  11 +-
>>>  ofproto/ofproto-dpif.h                            |   8 +-
>>>  tests/automake.mk                                 |   3 +-
>>>  tests/bundle.at                                   |   2 +-
>>>  tests/classifier.at                               |  10 +-
>>>  tests/dpif-netdev.at                              |  17 +-
>>>  tests/drop-stats.at                               | 197 ++++++++++++++++++++++
>>>  tests/ofproto-dpif.at                             | 118 ++++++-------
>>>  tests/ovs-ofctl.at                                |   2 +-
>>>  tests/packet-type-aware.at                        |  17 +-
>>>  tests/testsuite.at                                |   1 +
>>>  tests/tunnel-push-pop.at                          |  26 ++-
>>>  tests/tunnel.at                                   |  21 ++-
>>>  28 files changed, 695 insertions(+), 107 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 9b087f1..b66b46f 100644
>>> --- a/datapath/linux/compat/include/linux/openvswitch.h
>>> +++ b/datapath/linux/compat/include/linux/openvswitch.h
>>> @@ -819,6 +819,56 @@ struct ovs_action_push_eth {
>>>  };
>>>  
>>>  /**
>>> + * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP.
>>> + * @OVS_DROP_REASON_OF_PIPELINE: Explicit drop action in the pipeline.
>>> + * @OVS_DROP_REASON_BRIDGE_NOT_FOUND: Xlation error generated due to
>>> + * unable to determine bridge.
>>> + * @OVS_DROP_REASON_RECURSION_TOO_DEEP: Xlation error generated due to
>>> + * recursion reached maximum depth.
>>> + * @OVS_DROP_REASON_TOO_MANY_RESUBMITS: Xlation error generated due to
>>> + * too many resubmits.
>>> + * @OVS_DROP_REASON_STACK_TOO_DEEP: Xlation error generated due to stack
>>> + * too deep.
>>> + * @OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT: Xlation error generated
>>> + * due to no recirculation context.
>>> + * @OVS_DROP_REASON_RECIRCULATION_CONFLICT: Xlation error generated due to
>>> + * conflict in recirculation context.
>>> + * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to
>>> + * too many mpls labels.
>>> + * @OVS_DROP_REASON_INVALID_TUNNEL_METADATA: Xlation error generated due to
>>> + * invalid tunnel metadata.
>>> + * @OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE: Xlation error generated due to
>>> + * unsupported packet type.
>>> + * @OVS_DROP_REASON_CONGESTION: Xlation error generated due to ecn mismatch
>>> + * during tunnel decapsulation.
>>> + * @OVS_DROP_REASON_FORWARDING_DISABLED: Xlation error generated due to
>>> + * forwarding is disabled.
>>> + */
>>> +enum ovs_drop_reason {
>>> +	OVS_DROP_REASON_OF_PIPELINE = 0,
>>> +	OVS_DROP_REASON_BRIDGE_NOT_FOUND,
>>> +	OVS_DROP_REASON_RECURSION_TOO_DEEP,
>>> +	OVS_DROP_REASON_TOO_MANY_RESUBMITS,
>>> +	OVS_DROP_REASON_STACK_TOO_DEEP,
>>> +	OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT,
>>> +	OVS_DROP_REASON_RECIRCULATION_CONFLICT,
>>> +	OVS_DROP_REASON_TOO_MANY_MPLS_LABELS,
>>> +	OVS_DROP_REASON_INVALID_TUNNEL_METADATA,
>>> +	OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE,
>>> +	OVS_DROP_REASON_CONGESTION,
>>> +	OVS_DROP_REASON_FORWARDING_DISABLED,
>>> +	OVS_DROP_REASON_MAX,
>>> +};
>>> +
>>> +/*
>>> + * struct ovs_action_drop - %OVS_ACTION_ATTR_DROP action argument.
>>> + * @drop_reason: Reason for installing drop action.
>>> + */
>>> +struct ovs_action_drop {
>>> +	enum ovs_drop_reason drop_reason;
>>> +};
>>> +
>>> +/**
>>>   * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
>>>   *
>>>   * @OVS_NAT_ATTR_SRC: Flag for Source NAT (mangle source address/port).
>>> @@ -938,6 +988,7 @@ enum ovs_action_attr {
>>>  	OVS_ACTION_ATTR_POP_NSH,      /* No argument. */
>>>  	OVS_ACTION_ATTR_METER,        /* u32 meter number. */
>>>  	OVS_ACTION_ATTR_CLONE,        /* Nested OVS_CLONE_ATTR_*.  */
>>> +        OVS_ACTION_ATTR_DROP,         /* explicit drop action. */
>>
>> This is the linux kernel header. You should use tabs here.
>>>  
>>>  #ifndef __KERNEL__
>>>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
>>> diff --git a/lib/coverage.c b/lib/coverage.c
>>> index 6cef826..c9acc37 100644
>>> --- a/lib/coverage.c
>>> +++ b/lib/coverage.c
>>> @@ -32,9 +32,7 @@ VLOG_DEFINE_THIS_MODULE(coverage);
>>>  static struct coverage_counter **coverage_counters = NULL;
>>>  static size_t n_coverage_counters = 0;
>>>  static size_t allocated_coverage_counters = 0;
>>> -
>>> -static struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
>>
>> This is leftover from the previous versions. No need to expose
>> 'coverage_mutex' in current patch.
>>> -
>>> +struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
>>>  DEFINE_STATIC_PER_THREAD_DATA(long long int, coverage_clear_time, LLONG_MIN);
>>>  static long long int coverage_run_time = LLONG_MIN;
>>>  
>>> @@ -52,7 +50,7 @@ coverage_counter_register(struct coverage_counter* counter)
>>>      if (n_coverage_counters >= allocated_coverage_counters) {
>>>          coverage_counters = x2nrealloc(coverage_counters,
>>>                                         &allocated_coverage_counters,
>>> -                                       sizeof(struct coverage_counter*));
>>> +                                       sizeof(struct coverage_counter *));
>>
>> this change is also not needed.
>>>      }
>>>      coverage_counters[n_coverage_counters++] = counter;
>>>  }
>>> diff --git a/lib/coverage.h b/lib/coverage.h
>>> index dea990e..45b1dbd 100644
>>> --- a/lib/coverage.h
>>> +++ b/lib/coverage.h
>>> @@ -54,7 +54,8 @@ struct coverage_counter {
>>>      unsigned int hr[HR_AVG_LEN];
>>>  };
>>>  
>>> -void coverage_counter_register(struct coverage_counter*);
>>> +extern struct ovs_mutex coverage_mutex;
>>> +void coverage_counter_register(struct coverage_counter *);
>>
>> This is leftover from the previous versions. No need to expose
>> 'coverage_mutex' in current patch.
>>>  
>>>  /* Defines COUNTER.  There must be exactly one such definition at file scope
>>>   * within a program. */
>>> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
>>> index be529b6..8749cba 100644
>>> --- a/lib/dpif-netdev.c
>>> +++ b/lib/dpif-netdev.c
>>> @@ -100,6 +100,30 @@ enum { MAX_METERS = 65536 };    /* Maximum number of meters. */
>>>  enum { MAX_BANDS = 8 };         /* Maximum number of bands / meter. */
>>>  enum { N_METER_LOCKS = 64 };    /* Maximum number of meters. */
>>>  
>>> +
>>> +COVERAGE_DEFINE(drop_action_of_pipeline);
>>> +COVERAGE_DEFINE(drop_action_bridge_not_found);
>>> +COVERAGE_DEFINE(drop_action_recursion_too_deep);
>>> +COVERAGE_DEFINE(drop_action_too_many_resubmit);
>>> +COVERAGE_DEFINE(drop_action_stack_too_deep);
>>> +COVERAGE_DEFINE(drop_action_no_recirculation_context);
>>> +COVERAGE_DEFINE(drop_action_recirculation_conflict);
>>> +COVERAGE_DEFINE(drop_action_too_many_mpls_labels);
>>> +COVERAGE_DEFINE(drop_action_invalid_tunnel_metadata);
>>> +COVERAGE_DEFINE(drop_action_unsupported_packet_type);
>>> +COVERAGE_DEFINE(drop_action_congestion);
>>> +COVERAGE_DEFINE(drop_action_forwarding_disabled);
>>
>> Above counters could be defined inside odp-execute.c. You already have
>> 2 other counters there. In this case there will be no need to introduce
>> additional 'dp_update_drop_action_counter_cb' callback and pass it around.
>> All the required logic will also be moved to odp-execute.c.
>>
>> <Anju> But I want the callback to be NULL when called from dpif_execute_with_help
> 
> Why? This seems strange to count drops only for actions that do not need
> help from the dpif layer.
> 
>>
>>> +COVERAGE_DEFINE(dp_meter_drop);
>>> +COVERAGE_DEFINE(dp_upcall_error_drop);
>>> +COVERAGE_DEFINE(dp_lock_error_drop);
>>> +COVERAGE_DEFINE(dp_userspace_action_error_drop);
>>> +COVERAGE_DEFINE(dp_tunnel_push_error_drop);
>>> +COVERAGE_DEFINE(dp_tunnel_pop_error_drop);
>>> +COVERAGE_DEFINE(dp_recirc_error_drop);
>>> +COVERAGE_DEFINE(dp_invalid_port_drop);
>>> +COVERAGE_DEFINE(dp_invalid_tnl_port_drop);
>>> +COVERAGE_DEFINE(rx_invalid_packet_drop);
>>
>> What do you think about renaming above counters to 'datapath_drop_*' ?
>> IMHO, it'll be more visually recognizable.
>> Like 'datapath_drop_rx_invalid_packet' or 'datapath_drop_upcall_error'.
>>  
>> <Anju> Do you mean instead of using dp_* to use datapath_*??
> 
> I mean using 'datapath_drop_' as a prefix.
> 
>>> +
>>>  /* Protects against changes to 'dp_netdevs'. */
>>>  static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>>>  
>>> @@ -829,7 +853,6 @@ static inline bool
>>>  pmd_perf_metrics_enabled(const struct dp_netdev_pmd_thread *pmd);
>>>  static void queue_netdev_flow_del(struct dp_netdev_pmd_thread *pmd,
>>>                                    struct dp_netdev_flow *flow);
>>> -
>>
>> No need to remove an empty line.
>>
>>>  static void
>>>  emc_cache_init(struct emc_cache *flow_cache)
>>>  {
>>> @@ -1388,6 +1411,7 @@ dpif_netdev_init(void)
>>>      return 0;
>>>  }
>>>  
>>> +
>>
>> No need to add an empty line.
>>>  static int
>>>  dpif_netdev_enumerate(struct sset *all_dps,
>>>                        const struct dpif_class *dpif_class)
>>> @@ -5563,7 +5587,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, struct dp_packet_batch *packets_,
>>>              band = &meter->bands[exceeded_band[j]];
>>>              band->packet_count += 1;
>>>              band->byte_count += dp_packet_size(packet);
>>> -
>> Please, keep this empty line for readability.
>>
>>> +            COVERAGE_INC(dp_meter_drop);
>>>              dp_packet_delete(packet);
>>>          } else {
>>>              /* Meter accepts packet. */
>>> @@ -6320,6 +6344,7 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
>>>  
>>>          if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) {
>>>              dp_packet_delete(packet);
>>> +            COVERAGE_INC(rx_invalid_packet_drop);
>>>              continue;
>>>          }
>>>  
>>> @@ -6446,6 +6471,7 @@ handle_packet_upcall(struct dp_netdev_pmd_thread *pmd,
>>>                               put_actions);
>>>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>>>          dp_packet_delete(packet);
>>> +        COVERAGE_INC(dp_upcall_error_drop);
>>>          return error;
>>>      }
>>>  
>>> @@ -6577,6 +6603,7 @@ 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);
>>> +                COVERAGE_INC(dp_lock_error_drop);
>>>                  upcall_fail_cnt++;
>>>              }
>>>          }
>>> @@ -6846,10 +6873,59 @@ dp_execute_userspace_action(struct dp_netdev_pmd_thread *pmd,
>>>                                    actions->data, actions->size);
>>>      } else if (should_steal) {
>>>          dp_packet_delete(packet);
>>> +        COVERAGE_INC(dp_userspace_action_error_drop);
>>>      }
>>>  }
>>>  
>>>  static void
>>> +dp_update_drop_action_counter_cb(enum ovs_drop_reason drop_reason,
>>> +                          int delta)
>>
>> Indents are a bit off.
>>> +    OVS_NO_THREAD_SAFETY_ANALYSIS
>>
>> Is it necessary to disable thread safety analysis ?
>> <Anju> Since we are using COVERAGE infra which is defining thread specific data, i thought we might need to use this. Is my understanding incorrect?
> 
> Coverage infra is thread safe. And clang doesn't seem to produce any warnings
> without this annotation. Thread safety analysis should be disabled only if you
> have clang warnings and can not resolve them in other way.
> 
>>> +{
>>> +   switch (drop_reason) {
>>> +   case OVS_DROP_REASON_OF_PIPELINE:
>>> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
>>> +        break;
>>> +   case OVS_DROP_REASON_BRIDGE_NOT_FOUND:
>>> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
>>> +        break;
>>> +   case OVS_DROP_REASON_RECURSION_TOO_DEEP:
>>> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
>>> +        break;
>>> +   case OVS_DROP_REASON_TOO_MANY_RESUBMITS:
>>> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
>>> +        break;
>>> +   case OVS_DROP_REASON_STACK_TOO_DEEP:
>>> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
>>> +        break;
>>> +   case OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT:
>>> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
>>> +        break;
>>> +   case OVS_DROP_REASON_RECIRCULATION_CONFLICT:
>>> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
>>> +        break;
>>> +   case OVS_DROP_REASON_TOO_MANY_MPLS_LABELS:
>>> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
>>> +        break;
>>> +   case OVS_DROP_REASON_INVALID_TUNNEL_METADATA:
>>> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
>>> +        break;
>>> +   case OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE:
>>> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
>>> +        break;
>>> +   case OVS_DROP_REASON_CONGESTION:
>>> +        COVERAGE_ADD(drop_action_congestion, delta);
>>> +        break;
>>> +   case OVS_DROP_REASON_FORWARDING_DISABLED:
>>> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
>>> +        break;
>>> +   case OVS_DROP_REASON_MAX:
>>> +   default:
>>> +        VLOG_ERR("Invalid Drop reason type:%d",drop_reason);
>>
>> We might want the rate limiting for above message to not have it for each packet.
>>> +   }
>>> +}
>>> +
>>> +static void
>>>  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>>>                const struct nlattr *a, bool should_steal)
>>>      OVS_NO_THREAD_SAFETY_ANALYSIS
>>> @@ -6860,6 +6936,7 @@ 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;
>>>  
>>>      switch ((enum ovs_action_attr)type) {
>>>      case OVS_ACTION_ATTR_OUTPUT:
>>> @@ -6901,6 +6978,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>>>                  dp_packet_batch_add(&p->output_pkts, packet);
>>>              }
>>>              return;
>>> +        } else {
>>> +            COVERAGE_ADD(dp_invalid_port_drop, packets_->count);
>>>          }
>>>          break;
>>>  
>>> @@ -6910,10 +6989,13 @@ 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. */
>>> +            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
>>>              break;
>>>          }
>>>          dp_packet_batch_apply_cutlen(packets_);
>>> -        push_tnl_action(pmd, a, packets_);
>>> +        if (push_tnl_action(pmd, a, packets_)) {
>>> +            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
>>> +        }
>>>          return;
>>>  
>>>      case OVS_ACTION_ATTR_TUNNEL_POP:
>>> @@ -6933,7 +7015,12 @@ 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) {
>>> +                    COVERAGE_ADD(dp_tunnel_pop_error_drop, packet_dropped);
>>> +                }
>>>                  if (dp_packet_batch_is_empty(packets_)) {
>>>                      return;
>>>                  }
>>> @@ -6947,7 +7034,11 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>>>                  dp_netdev_recirculate(pmd, packets_);
>>>                  (*depth)--;
>>>                  return;
>>> +            } else {
>>> +                COVERAGE_ADD(dp_invalid_tnl_port_drop, packets_->count);
>>>              }
>>> +        } else {
>>> +            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
>>>          }
>>>          break;
>>>  
>>> @@ -6991,6 +7082,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>>>              fat_rwlock_unlock(&dp->upcall_rwlock);
>>>  
>>>              return;
>>> +        } else {
>>> +            COVERAGE_ADD(dp_lock_error_drop, packets_->count);
>>>          }
>>>          break;
>>>  
>>> @@ -7013,6 +7106,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>>>              (*depth)--;
>>>  
>>>              return;
>>> +        } else {
>>> +            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
>>>          }
>>
>> 'Else' condition is not needed here as we're returning inside the 'if'.
>> This is actually applicable to most of other places in this function,
>> but in this one we already have a warning below without 'else' condition.
>>>  
>>>          VLOG_WARN("Packet dropped. Max recirculation depth exceeded.");
>>> @@ -7167,6 +7262,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();
>>>      }
>>> @@ -7183,7 +7279,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_action_counter_cb);
>>
>> As I already wrote, we'll not need to have a callback function if you'll
>> move the 'drop_action_*' counters to odp-execute.c.
>>>  }
>>>  
>>>  struct dp_netdev_ct_dump {
>>> diff --git a/lib/dpif.c b/lib/dpif.c
>>> index e35f111..21f9f54 100644
>>> --- a/lib/dpif.c
>>> +++ b/lib/dpif.c
>>> @@ -1273,6 +1273,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();
>>> @@ -1295,7 +1296,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;
>>>  }
>>>  
>>> @@ -1879,6 +1880,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,
>>> @@ -1976,3 +1983,4 @@ dpif_meter_del(struct dpif *dpif, ofproto_meter_id meter_id,
>>>      }
>>>      return error;
>>>  }
>>> +
>>> diff --git a/lib/dpif.h b/lib/dpif.h
>>> index 475d5a6..e799da8 100644
>>> --- a/lib/dpif.h
>>> +++ b/lib/dpif.h
>>> @@ -888,6 +888,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 320422b..0c3bc9c 100644
>>> --- a/lib/netdev-dpdk.c
>>> +++ b/lib/netdev-dpdk.c
>>> @@ -2395,6 +2395,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);
>>
>> This is the unrelated bug fix that should be sent as a separate patch.
>> In this case we'll be able to apply it and backport to previous versions.
>>
>> Adding Ian to CC.
>>
>>>          dp_packet_delete_batch(batch, true);
>>>          return;
>>>      }
>>> diff --git a/lib/odp-execute.c b/lib/odp-execute.c
>>> index 3b6890e..c0db93f 100644
>>> --- a/lib/odp-execute.c
>>> +++ b/lib/odp-execute.c
>>> @@ -25,6 +25,7 @@
>>>  #include <stdlib.h>
>>>  #include <string.h>
>>>  
>>> +#include "coverage.h"
>>>  #include "dp-packet.h"
>>>  #include "dpif.h"
>>>  #include "netlink.h"
>>> @@ -37,6 +38,10 @@
>>>  #include "csum.h"
>>>  #include "conntrack.h"
>>>  
>>> +COVERAGE_DEFINE(dp_sample_error_drop);
>>> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
>>> +
>>> +
>>>  /* Masked copy of an ethernet address. 'src' is already properly masked. */
>>>  static void
>>>  ether_addr_copy_masked(struct eth_addr *dst, const struct eth_addr src,
>>> @@ -575,7 +580,9 @@ 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_action_counter_cb
>>> +                     dp_update_drop_action_counter)
>>>  {
>>>      const struct nlattr *subactions = NULL;
>>>      const struct nlattr *a;
>>> @@ -589,6 +596,7 @@ 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) {
>>> +                    COVERAGE_ADD(dp_sample_error_drop, 1);
>>>                      dp_packet_delete(packet);
>>>                  }
>>>                  return;
>>> @@ -616,13 +624,16 @@ 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_action_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_action_counter_cb
>>> +                      dp_update_drop_action_counter)
>>>  {
>>>      if (!steal) {
>>>          /* The 'actions' may modify the packet, but the modification
>>> @@ -634,11 +645,12 @@ 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);
>>> -    }
>>> -    else {
>>> +                        nl_attr_get_size(actions), dp_execute_action,
>>> +                        dp_update_drop_action_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_action_counter);
>>>      }
>>>  }
>>>  
>>> @@ -673,6 +685,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,12 +712,15 @@ 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_action_counter_cb
>>> +                       dp_update_drop_action_counter)
>>>  {
>>>      struct dp_packet *packet;
>>>      const struct nlattr *a;
>>>      unsigned int left;
>>>  
>>> +
>>
>> No need to add an empty line.
>>
>>>      NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
>>>          int type = nl_attr_type(a);
>>>          bool last_action = (left <= NLA_ALIGN(a->nla_len));
>>> @@ -822,7 +838,8 @@ 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_action_counter);
>>>              }
>>>  
>>>              if (last_action) {
>>> @@ -845,7 +862,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_action_counter);
>>>              if (last_action) {
>>>                  /* We do not need to free the packets. odp_execute_clone() has
>>>                   * stolen them.  */
>>> @@ -889,6 +907,7 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
>>>                  if (pop_nsh(packet)) {
>>>                      dp_packet_batch_refill(batch, packet, i);
>>>                  } else {
>>> +                    COVERAGE_INC(dp_nsh_decap_error_drop);
>>>                      dp_packet_delete(packet);
>>>                  }
>>>              }
>>> @@ -900,6 +919,17 @@ 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_action_counter) {
>>> +                 dp_update_drop_action_counter(drop_reason, 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..a84ecbb 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_action_counter_cb) (
>>> +                                           enum  ovs_drop_reason drop_reason,
>>> +                                           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,7 @@ 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_action_counter_cb
>>> +                             dp_update_drop_action_counter_cb);
>>>  #endif
>>> diff --git a/lib/odp-util.c b/lib/odp-util.c
>>> index 0491bed..9754fd5 100644
>>> --- a/lib/odp-util.c
>>> +++ b/lib/odp-util.c
>>> @@ -131,6 +131,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:
>>> @@ -345,6 +346,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));
>>
>> As I already wrote for the previous versions it'll be good to add
>> some visible markers around the drop reason. Spaces in flow dumps
>> are usually not used and it could add some complications for the
>> parsing scripts. You may use brackets like "drop(stack too deep)"
>> or replace the spaces with '-' or '_' like "drop:stack_too_deep".
>>> +}
>>> +
>>>  static void
>>>  format_odp_push_nsh_action(struct ds *ds,
>>>                             const struct nsh_hdr *nsh_hdr)
>>> @@ -1181,6 +1225,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:
>>> @@ -2427,8 +2474,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"))) {
>>
>> Why you're adding parse only for this type of drop action ?
>> <Anju>The rest are all below error scenarios which we hit during processing and hence I thought perhaps may not make sense as an input?? . Do you think it make sense to do that ?
> 
> I think, Ben already answered this questin.
> IMHO, that parsing of only one type makes no sence. There should be all
> or nothing. As we think that OVS should be able to parse flows it generates,
> there should be all.
> 
>>
>> *no recirculation context 
>> *too many mpls labels
>> * invalid tunnel metadata
>> * unsupported packet type
>> * ecn mismatch at tunnel decapsulation
>> * forwarding disabled
>> * 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 7da3175..69ed7b8 100644
>>> --- a/ofproto/ofproto-dpif-sflow.c
>>> +++ b/ofproto/ofproto-dpif-sflow.c
>>> @@ -1222,6 +1222,7 @@ dpif_sflow_read_actions(const struct flow *flow,
>>>          case OVS_ACTION_ATTR_PUSH_NSH:
>>>          case OVS_ACTION_ATTR_POP_NSH:
>>>          case OVS_ACTION_ATTR_UNSPEC:
>>> +        case OVS_ACTION_ATTR_DROP:
>>>          case __OVS_ACTION_ATTR_MAX:
>>>          default:
>>>              break;
>>> diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
>>> index dc30824..b1ce2ec 100644
>>> --- a/ofproto/ofproto-dpif-upcall.c
>>> +++ b/ofproto/ofproto-dpif-upcall.c
>>> @@ -1154,7 +1154,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)
>>>  {
>>> @@ -1244,6 +1244,7 @@ upcall_xlate(struct udpif *udpif, struct upcall *upcall,
>>>      if (upcall->type == MISS_UPCALL) {
>>>          upcall->ukey = ukey_create_from_upcall(upcall, wc);
>>>      }
>>> +    return xerr;
>>
>> The only user of this function is 'process_upcall' and it does not check
>> the result.
>>
>>>  }
>>>  
>>>  static void
>>> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
>>> index 839fddd..32c4edf 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);
>>>  
>>> @@ -5921,6 +5957,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);
>>
>> Indents are a bit off.
>>
>>> +
>>> +}
>>> +
>>> +static void
>>>  put_ct_helper(struct xlate_ctx *ctx,
>>>                struct ofpbuf *odp_actions, struct ofpact_conntrack *ofc)
>>>  {
>>> @@ -7383,6 +7430,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;
>>> @@ -7415,6 +7466,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) {
>>> @@ -7522,6 +7574,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 0a5a528..bb3f9ff 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 14fe6fc..609226a 100644
>>> --- a/ofproto/ofproto-dpif.c
>>> +++ b/ofproto/ofproto-dpif.c
>>> @@ -827,6 +827,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.
>>> @@ -1397,6 +1403,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);
>>> @@ -5776,6 +5784,7 @@ ofproto_unixctl_dpif_set_dp_features(struct unixctl_conn *conn,
>>>      ds_destroy(&ds);
>>>  }
>>>  
>>> +
>>
>> No need to add an empty line.
>>
>>>  static void
>>>  ofproto_unixctl_init(void)
>>>  {
>>> @@ -5809,7 +5818,7 @@ ofproto_unixctl_init(void)
>>>      unixctl_command_register("dpif/set-dp-features", "bridge", 1, 3 ,
>>>                               ofproto_unixctl_dpif_set_dp_features, NULL);
>>>  }
>>> -
>>
>>> +
>>
>> Don't drop the ^L character.
>> <Anju> What is the significance of this character
> 
> Form feeds used in OVS to divide long source files into logical pieces.
> This is documented in the coding-style.
> 
>>>  static odp_port_t
>>>  ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
>>>  {
>>> 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 *);
>>>  
>>> +
>>
>> No need to add an empty line.
>>
>>>  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 92d56b2..22b4722 100644
>>> --- a/tests/automake.mk
>>> +++ b/tests/automake.mk
>>> @@ -108,7 +108,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
>>
>> automake.mk uses tabs.
>>
>>>  
>>>  EXTRA_DIST += $(FUZZ_REGRESSION_TESTS)
>>>  FUZZ_REGRESSION_TESTS = \
>>> diff --git a/tests/bundle.at b/tests/bundle.at
>>> index 0a4eadc..33fe249 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 6915d43..08349f6 100644
>>> --- a/tests/dpif-netdev.at
>>> +++ b/tests/dpif-netdev.at
>>> @@ -1,4 +1,4 @@
>>> -AT_BANNER([dpif-netdev])
>>> +T_BANNER([dpif-netdev])
>>
>> Something wrong here.
>>
>>>  
>>>  m4_divert_push([PREPARE_TESTS])
>>>  [
>>> @@ -281,6 +281,7 @@ type=drop rate=1 burst_size=2
>>>  ])
>>>  
>>>  ovs-appctl time/warp 5000
>>> +sleep 10
>>
>> Do we really need to sleep for so long ?
>>
>>>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>>>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>>>  AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
>>> @@ -291,7 +292,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
>>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>>> -sleep 1  # wait for forwarders process packets
>>> +# wait for forwarders process packets
>>
>> Something wrong here too.
>>
>>>  
>>>  # Meter 1 is measuring packets, allowing one packet per second with
>>>  # bursts of one packet, so 4 out of 5 packets should hit the drop
>>> @@ -320,7 +321,8 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
>>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>>>  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
>>> -sleep 1  # wait for forwarders process packets
>>> +
>>> +sleep 10  # wait for forwarders process packets
>>>  
>>>  # Meter 1 is measuring packets, allowing one packet per second with
>>>  # bursts of one packet, so all 5 of the new packets should hit the drop
>>> @@ -337,6 +339,15 @@ meter:2 flow_count:1 packet_in_count:10 byte_in_count:600 duration:0.0s bands:
>>>  0: packet_count:5 byte_count:300
>>>  ])
>>>  
>>> +ovs-appctl time/warp 5000
>>> +sleep 10
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "dp_meter_drop" | cut -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +14
>>> +])
>>> +
>>>  AT_CHECK([cat ovs-vswitchd.log | filter_flow_install | strip_xout_keep_actions], [0], [dnl
>>>  recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:meter(0),7
>>>  recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:8
>>> diff --git a/tests/drop-stats.at b/tests/drop-stats.at
>>> new file mode 100644
>>> index 0000000..be8b8ae
>>> --- /dev/null
>>> +++ b/tests/drop-stats.at
>>> @@ -0,0 +1,197 @@
>>> +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
>>> +])
>>> +
>>> +sleep 1
>>> +
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +3
>>> +])
>>> +
>>> +
>>> +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
>>
>> If possible, it's preferred to use odp format (i.e. declarative) for
>> packets definition. This allowes user to see what packet is sent without
>> parsing it by external tools.
>> Same for all other places.
>>
>>> +], [0], [ignore])
>>> +
>>> +sleep 1
>>> +
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +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])
>>> +
>>> +sleep 1
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "drop_action_recursion_too_deep" | cut -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +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)'
>>> +
>>> +sleep 1
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "drop_action_too_many_resubmit" | cut -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +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)'
>>> +
>>> +sleep 1
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "drop_action_stack_too_deep" | cut -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +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])
>>> +
>>> +sleep 1
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "drop_action_too_many_mpls_labels" | cut -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +1
>>> +])
>>> +
>>> +
>>> +OVS_VSWITCHD_STOP(["/|WARN|/d"])
>>> +AT_CLEANUP
>>> diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
>>> index ded2ef0..298aa9a 100644
>>> --- a/tests/ofproto-dpif.at
>>> +++ b/tests/ofproto-dpif.at
>>> @@ -3496,51 +3496,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" \
>>> @@ -3555,8 +3555,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" \
>>> @@ -3573,16 +3573,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"
>>> @@ -4322,11 +4322,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
>>> @@ -4404,8 +4404,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
>>> @@ -5751,7 +5751,7 @@ bridge("br0")
>>>  
>>>  Final flow: <cleared>
>>>  Megaflow: <cleared>
>>> -Datapath actions: drop
>>> +Datapath actions: drop:pipeline-drop
>>>  ])
>>>  
>>>  dnl Now, try again without megaflows:
>>> @@ -5772,7 +5772,7 @@ bridge("br0")
>>>  
>>>  Final flow: <cleared>
>>>  Megaflow: <cleared>
>>> -Datapath actions: drop
>>> +Datapath actions: drop:pipeline-drop
>>>  ])
>>>  
>>>  OVS_VSWITCHD_STOP
>>> @@ -7066,7 +7066,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"])
>>> @@ -7158,7 +7158,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
>>> @@ -7832,21 +7832,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
>>> @@ -7869,7 +7869,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
>>> @@ -8617,11 +8617,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])
>>> @@ -9348,7 +9348,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
>>> @@ -9377,16 +9377,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
>>> @@ -9711,7 +9711,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)
>>> @@ -10398,7 +10398,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])
>>> @@ -10490,7 +10490,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 6ae3470..cd19d41 100644
>>> --- a/tests/ovs-ofctl.at
>>> +++ b/tests/ovs-ofctl.at
>>> @@ -2999,7 +2999,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..7c1b551 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,14 @@ 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 coverage/show | grep "drop_action_unsupported_packet_type" | cut -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +2
>>>  ])
>>>  
>>>  # Encap(ethernet) on Ethernet frame -> should be droped
>>> @@ -587,7 +594,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 +616,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 +777,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 b840dbf..922ba48 100644
>>> --- a/tests/testsuite.at
>>> +++ b/tests/testsuite.at
>>> @@ -82,3 +82,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..385c114 100644
>>> --- a/tests/tunnel-push-pop.at
>>> +++ b/tests/tunnel-push-pop.at
>>> @@ -447,6 +447,27 @@ 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 netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>> +
>>> +ovs-appctl time/warp 1200
>>> +
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "dp_tunnel_pop_error_drop" | cut -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +1
>>> +])
>>> +
>>> +
>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>> +ovs-appctl time/warp 1200
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "drop_action_congestion" | cut -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +1
>>> +])
>>> +
>>>  dnl Check GREL3 only accepts non-fragmented packets?
>>>  AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>>  
>>> @@ -455,7 +476,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 +531,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 55fb1d3..3646c06 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 netdev-dummy/receive p2 'aa55aa550001f8bc124434b6080045000054ba20000040018486010103580101037001004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>> +sleep 2
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "dp_invalid_port_drop" | cut -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +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
>>>
diff mbox series

Patch

diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index 9b087f1..b66b46f 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -819,6 +819,56 @@  struct ovs_action_push_eth {
 };
 
 /**
+ * enum ovs_drop_reason - Reasons for installing %OVS_ACTION_ATTR_DROP.
+ * @OVS_DROP_REASON_OF_PIPELINE: Explicit drop action in the pipeline.
+ * @OVS_DROP_REASON_BRIDGE_NOT_FOUND: Xlation error generated due to
+ * unable to determine bridge.
+ * @OVS_DROP_REASON_RECURSION_TOO_DEEP: Xlation error generated due to
+ * recursion reached maximum depth.
+ * @OVS_DROP_REASON_TOO_MANY_RESUBMITS: Xlation error generated due to
+ * too many resubmits.
+ * @OVS_DROP_REASON_STACK_TOO_DEEP: Xlation error generated due to stack
+ * too deep.
+ * @OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT: Xlation error generated
+ * due to no recirculation context.
+ * @OVS_DROP_REASON_RECIRCULATION_CONFLICT: Xlation error generated due to
+ * conflict in recirculation context.
+ * @OVS_DROP_REASON_TOO_MANY_MPLS_LABELS: Xlation error generated due to
+ * too many mpls labels.
+ * @OVS_DROP_REASON_INVALID_TUNNEL_METADATA: Xlation error generated due to
+ * invalid tunnel metadata.
+ * @OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE: Xlation error generated due to
+ * unsupported packet type.
+ * @OVS_DROP_REASON_CONGESTION: Xlation error generated due to ecn mismatch
+ * during tunnel decapsulation.
+ * @OVS_DROP_REASON_FORWARDING_DISABLED: Xlation error generated due to
+ * forwarding is disabled.
+ */
+enum ovs_drop_reason {
+	OVS_DROP_REASON_OF_PIPELINE = 0,
+	OVS_DROP_REASON_BRIDGE_NOT_FOUND,
+	OVS_DROP_REASON_RECURSION_TOO_DEEP,
+	OVS_DROP_REASON_TOO_MANY_RESUBMITS,
+	OVS_DROP_REASON_STACK_TOO_DEEP,
+	OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT,
+	OVS_DROP_REASON_RECIRCULATION_CONFLICT,
+	OVS_DROP_REASON_TOO_MANY_MPLS_LABELS,
+	OVS_DROP_REASON_INVALID_TUNNEL_METADATA,
+	OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE,
+	OVS_DROP_REASON_CONGESTION,
+	OVS_DROP_REASON_FORWARDING_DISABLED,
+	OVS_DROP_REASON_MAX,
+};
+
+/*
+ * struct ovs_action_drop - %OVS_ACTION_ATTR_DROP action argument.
+ * @drop_reason: Reason for installing drop action.
+ */
+struct ovs_action_drop {
+	enum ovs_drop_reason drop_reason;
+};
+
+/**
  * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
  *
  * @OVS_NAT_ATTR_SRC: Flag for Source NAT (mangle source address/port).
@@ -938,6 +988,7 @@  enum ovs_action_attr {
 	OVS_ACTION_ATTR_POP_NSH,      /* No argument. */
 	OVS_ACTION_ATTR_METER,        /* u32 meter number. */
 	OVS_ACTION_ATTR_CLONE,        /* Nested OVS_CLONE_ATTR_*.  */
+        OVS_ACTION_ATTR_DROP,         /* explicit drop action. */
 
 #ifndef __KERNEL__
 	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
diff --git a/lib/coverage.c b/lib/coverage.c
index 6cef826..c9acc37 100644
--- a/lib/coverage.c
+++ b/lib/coverage.c
@@ -32,9 +32,7 @@  VLOG_DEFINE_THIS_MODULE(coverage);
 static struct coverage_counter **coverage_counters = NULL;
 static size_t n_coverage_counters = 0;
 static size_t allocated_coverage_counters = 0;
-
-static struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
-
+struct ovs_mutex coverage_mutex = OVS_MUTEX_INITIALIZER;
 DEFINE_STATIC_PER_THREAD_DATA(long long int, coverage_clear_time, LLONG_MIN);
 static long long int coverage_run_time = LLONG_MIN;
 
@@ -52,7 +50,7 @@  coverage_counter_register(struct coverage_counter* counter)
     if (n_coverage_counters >= allocated_coverage_counters) {
         coverage_counters = x2nrealloc(coverage_counters,
                                        &allocated_coverage_counters,
-                                       sizeof(struct coverage_counter*));
+                                       sizeof(struct coverage_counter *));
     }
     coverage_counters[n_coverage_counters++] = counter;
 }
diff --git a/lib/coverage.h b/lib/coverage.h
index dea990e..45b1dbd 100644
--- a/lib/coverage.h
+++ b/lib/coverage.h
@@ -54,7 +54,8 @@  struct coverage_counter {
     unsigned int hr[HR_AVG_LEN];
 };
 
-void coverage_counter_register(struct coverage_counter*);
+extern struct ovs_mutex coverage_mutex;
+void coverage_counter_register(struct coverage_counter *);
 
 /* Defines COUNTER.  There must be exactly one such definition at file scope
  * within a program. */
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index be529b6..8749cba 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -100,6 +100,30 @@  enum { MAX_METERS = 65536 };    /* Maximum number of meters. */
 enum { MAX_BANDS = 8 };         /* Maximum number of bands / meter. */
 enum { N_METER_LOCKS = 64 };    /* Maximum number of meters. */
 
+
+COVERAGE_DEFINE(drop_action_of_pipeline);
+COVERAGE_DEFINE(drop_action_bridge_not_found);
+COVERAGE_DEFINE(drop_action_recursion_too_deep);
+COVERAGE_DEFINE(drop_action_too_many_resubmit);
+COVERAGE_DEFINE(drop_action_stack_too_deep);
+COVERAGE_DEFINE(drop_action_no_recirculation_context);
+COVERAGE_DEFINE(drop_action_recirculation_conflict);
+COVERAGE_DEFINE(drop_action_too_many_mpls_labels);
+COVERAGE_DEFINE(drop_action_invalid_tunnel_metadata);
+COVERAGE_DEFINE(drop_action_unsupported_packet_type);
+COVERAGE_DEFINE(drop_action_congestion);
+COVERAGE_DEFINE(drop_action_forwarding_disabled);
+COVERAGE_DEFINE(dp_meter_drop);
+COVERAGE_DEFINE(dp_upcall_error_drop);
+COVERAGE_DEFINE(dp_lock_error_drop);
+COVERAGE_DEFINE(dp_userspace_action_error_drop);
+COVERAGE_DEFINE(dp_tunnel_push_error_drop);
+COVERAGE_DEFINE(dp_tunnel_pop_error_drop);
+COVERAGE_DEFINE(dp_recirc_error_drop);
+COVERAGE_DEFINE(dp_invalid_port_drop);
+COVERAGE_DEFINE(dp_invalid_tnl_port_drop);
+COVERAGE_DEFINE(rx_invalid_packet_drop);
+
 /* Protects against changes to 'dp_netdevs'. */
 static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
 
@@ -829,7 +853,6 @@  static inline bool
 pmd_perf_metrics_enabled(const struct dp_netdev_pmd_thread *pmd);
 static void queue_netdev_flow_del(struct dp_netdev_pmd_thread *pmd,
                                   struct dp_netdev_flow *flow);
-
 static void
 emc_cache_init(struct emc_cache *flow_cache)
 {
@@ -1388,6 +1411,7 @@  dpif_netdev_init(void)
     return 0;
 }
 
+
 static int
 dpif_netdev_enumerate(struct sset *all_dps,
                       const struct dpif_class *dpif_class)
@@ -5563,7 +5587,7 @@  dp_netdev_run_meter(struct dp_netdev *dp, struct dp_packet_batch *packets_,
             band = &meter->bands[exceeded_band[j]];
             band->packet_count += 1;
             band->byte_count += dp_packet_size(packet);
-
+            COVERAGE_INC(dp_meter_drop);
             dp_packet_delete(packet);
         } else {
             /* Meter accepts packet. */
@@ -6320,6 +6344,7 @@  dfc_processing(struct dp_netdev_pmd_thread *pmd,
 
         if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) {
             dp_packet_delete(packet);
+            COVERAGE_INC(rx_invalid_packet_drop);
             continue;
         }
 
@@ -6446,6 +6471,7 @@  handle_packet_upcall(struct dp_netdev_pmd_thread *pmd,
                              put_actions);
     if (OVS_UNLIKELY(error && error != ENOSPC)) {
         dp_packet_delete(packet);
+        COVERAGE_INC(dp_upcall_error_drop);
         return error;
     }
 
@@ -6577,6 +6603,7 @@  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);
+                COVERAGE_INC(dp_lock_error_drop);
                 upcall_fail_cnt++;
             }
         }
@@ -6846,10 +6873,59 @@  dp_execute_userspace_action(struct dp_netdev_pmd_thread *pmd,
                                   actions->data, actions->size);
     } else if (should_steal) {
         dp_packet_delete(packet);
+        COVERAGE_INC(dp_userspace_action_error_drop);
     }
 }
 
 static void
+dp_update_drop_action_counter_cb(enum ovs_drop_reason drop_reason,
+                          int delta)
+    OVS_NO_THREAD_SAFETY_ANALYSIS
+{
+   switch (drop_reason) {
+   case OVS_DROP_REASON_OF_PIPELINE:
+        COVERAGE_ADD(drop_action_of_pipeline, delta);
+        break;
+   case OVS_DROP_REASON_BRIDGE_NOT_FOUND:
+        COVERAGE_ADD(drop_action_bridge_not_found, delta);
+        break;
+   case OVS_DROP_REASON_RECURSION_TOO_DEEP:
+        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
+        break;
+   case OVS_DROP_REASON_TOO_MANY_RESUBMITS:
+        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
+        break;
+   case OVS_DROP_REASON_STACK_TOO_DEEP:
+        COVERAGE_ADD(drop_action_stack_too_deep, delta);
+        break;
+   case OVS_DROP_REASON_NO_RECIRCULATION_CONTEXT:
+        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
+        break;
+   case OVS_DROP_REASON_RECIRCULATION_CONFLICT:
+        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
+        break;
+   case OVS_DROP_REASON_TOO_MANY_MPLS_LABELS:
+        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
+        break;
+   case OVS_DROP_REASON_INVALID_TUNNEL_METADATA:
+        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
+        break;
+   case OVS_DROP_REASON_UNSUPPORTED_PACKET_TYPE:
+        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
+        break;
+   case OVS_DROP_REASON_CONGESTION:
+        COVERAGE_ADD(drop_action_congestion, delta);
+        break;
+   case OVS_DROP_REASON_FORWARDING_DISABLED:
+        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
+        break;
+   case OVS_DROP_REASON_MAX:
+   default:
+        VLOG_ERR("Invalid Drop reason type:%d",drop_reason);
+   }
+}
+
+static void
 dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
               const struct nlattr *a, bool should_steal)
     OVS_NO_THREAD_SAFETY_ANALYSIS
@@ -6860,6 +6936,7 @@  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;
 
     switch ((enum ovs_action_attr)type) {
     case OVS_ACTION_ATTR_OUTPUT:
@@ -6901,6 +6978,8 @@  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
                 dp_packet_batch_add(&p->output_pkts, packet);
             }
             return;
+        } else {
+            COVERAGE_ADD(dp_invalid_port_drop, packets_->count);
         }
         break;
 
@@ -6910,10 +6989,13 @@  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. */
+            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
             break;
         }
         dp_packet_batch_apply_cutlen(packets_);
-        push_tnl_action(pmd, a, packets_);
+        if (push_tnl_action(pmd, a, packets_)) {
+            COVERAGE_ADD(dp_tunnel_push_error_drop, packets_->count);
+        }
         return;
 
     case OVS_ACTION_ATTR_TUNNEL_POP:
@@ -6933,7 +7015,12 @@  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) {
+                    COVERAGE_ADD(dp_tunnel_pop_error_drop, packet_dropped);
+                }
                 if (dp_packet_batch_is_empty(packets_)) {
                     return;
                 }
@@ -6947,7 +7034,11 @@  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
                 dp_netdev_recirculate(pmd, packets_);
                 (*depth)--;
                 return;
+            } else {
+                COVERAGE_ADD(dp_invalid_tnl_port_drop, packets_->count);
             }
+        } else {
+            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
         }
         break;
 
@@ -6991,6 +7082,8 @@  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
             fat_rwlock_unlock(&dp->upcall_rwlock);
 
             return;
+        } else {
+            COVERAGE_ADD(dp_lock_error_drop, packets_->count);
         }
         break;
 
@@ -7013,6 +7106,8 @@  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
             (*depth)--;
 
             return;
+        } else {
+            COVERAGE_ADD(dp_recirc_error_drop, packets_->count);
         }
 
         VLOG_WARN("Packet dropped. Max recirculation depth exceeded.");
@@ -7167,6 +7262,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();
     }
@@ -7183,7 +7279,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_action_counter_cb);
 }
 
 struct dp_netdev_ct_dump {
diff --git a/lib/dpif.c b/lib/dpif.c
index e35f111..21f9f54 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1273,6 +1273,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();
@@ -1295,7 +1296,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;
 }
 
@@ -1879,6 +1880,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,
@@ -1976,3 +1983,4 @@  dpif_meter_del(struct dpif *dpif, ofproto_meter_id meter_id,
     }
     return error;
 }
+
diff --git a/lib/dpif.h b/lib/dpif.h
index 475d5a6..e799da8 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -888,6 +888,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 320422b..0c3bc9c 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -2395,6 +2395,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 3b6890e..c0db93f 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -25,6 +25,7 @@ 
 #include <stdlib.h>
 #include <string.h>
 
+#include "coverage.h"
 #include "dp-packet.h"
 #include "dpif.h"
 #include "netlink.h"
@@ -37,6 +38,10 @@ 
 #include "csum.h"
 #include "conntrack.h"
 
+COVERAGE_DEFINE(dp_sample_error_drop);
+COVERAGE_DEFINE(dp_nsh_decap_error_drop);
+
+
 /* Masked copy of an ethernet address. 'src' is already properly masked. */
 static void
 ether_addr_copy_masked(struct eth_addr *dst, const struct eth_addr src,
@@ -575,7 +580,9 @@  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_action_counter_cb
+                     dp_update_drop_action_counter)
 {
     const struct nlattr *subactions = NULL;
     const struct nlattr *a;
@@ -589,6 +596,7 @@  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) {
+                    COVERAGE_ADD(dp_sample_error_drop, 1);
                     dp_packet_delete(packet);
                 }
                 return;
@@ -616,13 +624,16 @@  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_action_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_action_counter_cb
+                      dp_update_drop_action_counter)
 {
     if (!steal) {
         /* The 'actions' may modify the packet, but the modification
@@ -634,11 +645,12 @@  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);
-    }
-    else {
+                        nl_attr_get_size(actions), dp_execute_action,
+                        dp_update_drop_action_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_action_counter);
     }
 }
 
@@ -673,6 +685,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,12 +712,15 @@  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_action_counter_cb
+                       dp_update_drop_action_counter)
 {
     struct dp_packet *packet;
     const struct nlattr *a;
     unsigned int left;
 
+
     NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
         int type = nl_attr_type(a);
         bool last_action = (left <= NLA_ALIGN(a->nla_len));
@@ -822,7 +838,8 @@  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_action_counter);
             }
 
             if (last_action) {
@@ -845,7 +862,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_action_counter);
             if (last_action) {
                 /* We do not need to free the packets. odp_execute_clone() has
                  * stolen them.  */
@@ -889,6 +907,7 @@  odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
                 if (pop_nsh(packet)) {
                     dp_packet_batch_refill(batch, packet, i);
                 } else {
+                    COVERAGE_INC(dp_nsh_decap_error_drop);
                     dp_packet_delete(packet);
                 }
             }
@@ -900,6 +919,17 @@  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_action_counter) {
+                 dp_update_drop_action_counter(drop_reason, 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..a84ecbb 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_action_counter_cb) (
+                                           enum  ovs_drop_reason drop_reason,
+                                           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,7 @@  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_action_counter_cb
+                             dp_update_drop_action_counter_cb);
 #endif
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 0491bed..9754fd5 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -131,6 +131,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:
@@ -345,6 +346,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)
@@ -1181,6 +1225,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:
@@ -2427,8 +2474,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 7da3175..69ed7b8 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -1222,6 +1222,7 @@  dpif_sflow_read_actions(const struct flow *flow,
         case OVS_ACTION_ATTR_PUSH_NSH:
         case OVS_ACTION_ATTR_POP_NSH:
         case OVS_ACTION_ATTR_UNSPEC:
+        case OVS_ACTION_ATTR_DROP:
         case __OVS_ACTION_ATTR_MAX:
         default:
             break;
diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
index dc30824..b1ce2ec 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -1154,7 +1154,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)
 {
@@ -1244,6 +1244,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
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 839fddd..32c4edf 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);
 
@@ -5921,6 +5957,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)
 {
@@ -7383,6 +7430,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;
@@ -7415,6 +7466,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) {
@@ -7522,6 +7574,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 0a5a528..bb3f9ff 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 14fe6fc..609226a 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -827,6 +827,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.
@@ -1397,6 +1403,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);
@@ -5776,6 +5784,7 @@  ofproto_unixctl_dpif_set_dp_features(struct unixctl_conn *conn,
     ds_destroy(&ds);
 }
 
+
 static void
 ofproto_unixctl_init(void)
 {
@@ -5809,7 +5818,7 @@  ofproto_unixctl_init(void)
     unixctl_command_register("dpif/set-dp-features", "bridge", 1, 3 ,
                              ofproto_unixctl_dpif_set_dp_features, NULL);
 }
-
+
 static odp_port_t
 ofp_port_to_odp_port(const struct ofproto_dpif *ofproto, ofp_port_t ofp_port)
 {
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 92d56b2..22b4722 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -108,7 +108,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
 
 EXTRA_DIST += $(FUZZ_REGRESSION_TESTS)
 FUZZ_REGRESSION_TESTS = \
diff --git a/tests/bundle.at b/tests/bundle.at
index 0a4eadc..33fe249 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 6915d43..08349f6 100644
--- a/tests/dpif-netdev.at
+++ b/tests/dpif-netdev.at
@@ -1,4 +1,4 @@ 
-AT_BANNER([dpif-netdev])
+T_BANNER([dpif-netdev])
 
 m4_divert_push([PREPARE_TESTS])
 [
@@ -281,6 +281,7 @@  type=drop rate=1 burst_size=2
 ])
 
 ovs-appctl time/warp 5000
+sleep 10
 AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
 AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
 AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(7),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)' --len 60])
@@ -291,7 +292,7 @@  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
 AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
 AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
 AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
-sleep 1  # wait for forwarders process packets
+# wait for forwarders process packets
 
 # Meter 1 is measuring packets, allowing one packet per second with
 # bursts of one packet, so 4 out of 5 packets should hit the drop
@@ -320,7 +321,8 @@  AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),packet_type(ns=0,id=0),
 AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
 AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
 AT_CHECK([ovs-appctl netdev-dummy/receive p8 'in_port(8),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.3,dst=10.0.0.4,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)' --len 60])
-sleep 1  # wait for forwarders process packets
+
+sleep 10  # wait for forwarders process packets
 
 # Meter 1 is measuring packets, allowing one packet per second with
 # bursts of one packet, so all 5 of the new packets should hit the drop
@@ -337,6 +339,15 @@  meter:2 flow_count:1 packet_in_count:10 byte_in_count:600 duration:0.0s bands:
 0: packet_count:5 byte_count:300
 ])
 
+ovs-appctl time/warp 5000
+sleep 10
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "dp_meter_drop" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+14
+])
+
 AT_CHECK([cat ovs-vswitchd.log | filter_flow_install | strip_xout_keep_actions], [0], [dnl
 recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:meter(0),7
 recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), actions:8
diff --git a/tests/drop-stats.at b/tests/drop-stats.at
new file mode 100644
index 0000000..be8b8ae
--- /dev/null
+++ b/tests/drop-stats.at
@@ -0,0 +1,197 @@ 
+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
+])
+
+sleep 1
+
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+3
+])
+
+
+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])
+
+sleep 1
+
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "drop_action_of_pipeline" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+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])
+
+sleep 1
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "drop_action_recursion_too_deep" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+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)'
+
+sleep 1
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "drop_action_too_many_resubmit" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+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)'
+
+sleep 1
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "drop_action_stack_too_deep" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+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])
+
+sleep 1
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "drop_action_too_many_mpls_labels" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+1
+])
+
+
+OVS_VSWITCHD_STOP(["/|WARN|/d"])
+AT_CLEANUP
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index ded2ef0..298aa9a 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -3496,51 +3496,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" \
@@ -3555,8 +3555,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" \
@@ -3573,16 +3573,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"
@@ -4322,11 +4322,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
@@ -4404,8 +4404,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
@@ -5751,7 +5751,7 @@  bridge("br0")
 
 Final flow: <cleared>
 Megaflow: <cleared>
-Datapath actions: drop
+Datapath actions: drop:pipeline-drop
 ])
 
 dnl Now, try again without megaflows:
@@ -5772,7 +5772,7 @@  bridge("br0")
 
 Final flow: <cleared>
 Megaflow: <cleared>
-Datapath actions: drop
+Datapath actions: drop:pipeline-drop
 ])
 
 OVS_VSWITCHD_STOP
@@ -7066,7 +7066,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"])
@@ -7158,7 +7158,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
@@ -7832,21 +7832,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
@@ -7869,7 +7869,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
@@ -8617,11 +8617,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])
@@ -9348,7 +9348,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
@@ -9377,16 +9377,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
@@ -9711,7 +9711,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)
@@ -10398,7 +10398,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])
@@ -10490,7 +10490,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 6ae3470..cd19d41 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -2999,7 +2999,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..7c1b551 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,14 @@  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 coverage/show | grep "drop_action_unsupported_packet_type" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+2
 ])
 
 # Encap(ethernet) on Ethernet frame -> should be droped
@@ -587,7 +594,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 +616,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 +777,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 b840dbf..922ba48 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -82,3 +82,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..385c114 100644
--- a/tests/tunnel-push-pop.at
+++ b/tests/tunnel-push-pop.at
@@ -447,6 +447,27 @@  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 netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+
+ovs-appctl time/warp 1200
+
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "dp_tunnel_pop_error_drop" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+1
+])
+
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+ovs-appctl time/warp 1200
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "drop_action_congestion" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+1
+])
+
 dnl Check GREL3 only accepts non-fragmented packets?
 AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
 
@@ -455,7 +476,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 +531,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 55fb1d3..3646c06 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 netdev-dummy/receive p2 'aa55aa550001f8bc124434b6080045000054ba20000040018486010103580101037001004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+sleep 2
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "dp_invalid_port_drop" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+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