diff mbox series

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

Message ID 1551288216-30877-1-git-send-email-anju.thomas@ericsson.com
State Superseded
Headers show
Series [ovs-dev,v9] Improved Packet Drop Statistics in OVS | expand

Commit Message

Anju Thomas Feb. 27, 2019, 9:22 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. Identify and account all the silent packet drop scenarios

2. Display these drops in ovs-appctl coverage/show

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

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 |   1 +
 lib/dpif-netdev.c                                 |  44 ++++-
 lib/dpif.c                                        |   7 +
 lib/dpif.h                                        |   3 +
 lib/odp-execute.c                                 |  81 ++++++++-
 lib/odp-util.c                                    |   9 +
 ofproto/ofproto-dpif-ipfix.c                      |   1 +
 ofproto/ofproto-dpif-sflow.c                      |   1 +
 ofproto/ofproto-dpif-xlate.c                      | 103 +++++++----
 ofproto/ofproto-dpif-xlate.h                      |   3 +
 ofproto/ofproto-dpif.c                            |   8 +
 ofproto/ofproto-dpif.h                            |   7 +-
 tests/automake.mk                                 |   3 +-
 tests/dpif-netdev.at                              |   8 +
 tests/drop-stats.at                               | 197 ++++++++++++++++++++++
 tests/ofproto-dpif.at                             |   2 +-
 tests/testsuite.at                                |   1 +
 tests/tunnel-push-pop.at                          |  28 ++-
 tests/tunnel.at                                   |  14 +-
 19 files changed, 475 insertions(+), 46 deletions(-)
 create mode 100644 tests/drop-stats.at

Comments

Ilya Maximets March 12, 2019, 1:21 p.m. UTC | #1
Hi.

Have you missed my "inline" comments to v8:
https://mail.openvswitch.org/pipermail/ovs-dev/2019-February/356153.html
?

Best regards, Ilya Maximets.

On 27.02.2019 12:22, 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. Identify and account all the silent packet drop scenarios
> 
> 2. Display these drops in ovs-appctl coverage/show
> 
> 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
> 
> 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 |   1 +
>  lib/dpif-netdev.c                                 |  44 ++++-
>  lib/dpif.c                                        |   7 +
>  lib/dpif.h                                        |   3 +
>  lib/odp-execute.c                                 |  81 ++++++++-
>  lib/odp-util.c                                    |   9 +
>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>  ofproto/ofproto-dpif-xlate.c                      | 103 +++++++----
>  ofproto/ofproto-dpif-xlate.h                      |   3 +
>  ofproto/ofproto-dpif.c                            |   8 +
>  ofproto/ofproto-dpif.h                            |   7 +-
>  tests/automake.mk                                 |   3 +-
>  tests/dpif-netdev.at                              |   8 +
>  tests/drop-stats.at                               | 197 ++++++++++++++++++++++
>  tests/ofproto-dpif.at                             |   2 +-
>  tests/testsuite.at                                |   1 +
>  tests/tunnel-push-pop.at                          |  28 ++-
>  tests/tunnel.at                                   |  14 +-
>  19 files changed, 475 insertions(+), 46 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 d5aa09d..e77e9c8 100644
> --- a/datapath/linux/compat/include/linux/openvswitch.h
> +++ b/datapath/linux/compat/include/linux/openvswitch.h
> @@ -946,6 +946,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,         /* Drop action. */
>  
>  #ifndef __KERNEL__
>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index 77ac1d2..acc7913 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -101,6 +101,17 @@ 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(datapath_drop_meter);
> +COVERAGE_DEFINE(datapath_drop_upcall_error);
> +COVERAGE_DEFINE(datapath_drop_lock_error);
> +COVERAGE_DEFINE(datapath_drop_userspace_action_error);
> +COVERAGE_DEFINE(datapath_drop_tunnel_push_error);
> +COVERAGE_DEFINE(datapath_drop_tunnel_pop_error);
> +COVERAGE_DEFINE(datapath_drop_recirc_error);
> +COVERAGE_DEFINE(datapath_drop_invalid_port);
> +COVERAGE_DEFINE(datapath_drop_invalid_tnl_port);
> +COVERAGE_DEFINE(datapath_drop_rx_invalid_packet);
> +
>  /* Protects against changes to 'dp_netdevs'. */
>  static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>  
> @@ -5647,6 +5658,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, struct dp_packet_batch *packets_,
>              band->packet_count += 1;
>              band->byte_count += dp_packet_size(packet);
>  
> +            COVERAGE_INC(datapath_drop_meter);
>              dp_packet_delete(packet);
>          } else {
>              /* Meter accepts packet. */
> @@ -6402,6 +6414,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(datapath_drop_rx_invalid_packet);
>              continue;
>          }
>  
> @@ -6528,6 +6541,7 @@ handle_packet_upcall(struct dp_netdev_pmd_thread *pmd,
>                               put_actions);
>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>          dp_packet_delete(packet);
> +        COVERAGE_INC(datapath_drop_upcall_error);
>          return error;
>      }
>  
> @@ -6659,6 +6673,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(datapath_drop_lock_error);
>                  upcall_fail_cnt++;
>              }
>          }
> @@ -6928,6 +6943,7 @@ dp_execute_userspace_action(struct dp_netdev_pmd_thread *pmd,
>                                    actions->data, actions->size);
>      } else if (should_steal) {
>          dp_packet_delete(packet);
> +        COVERAGE_INC(datapath_drop_userspace_action_error);
>      }
>  }
>  
> @@ -6942,6 +6958,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:
> @@ -6983,6 +7000,9 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                  dp_packet_batch_add(&p->output_pkts, packet);
>              }
>              return;
> +        } else {
> +            COVERAGE_ADD(datapath_drop_invalid_port,
> +                            dp_packet_batch_size(packets_));
>          }
>          break;
>  
> @@ -6992,10 +7012,16 @@ 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(datapath_drop_tunnel_push_error,
> +                            dp_packet_batch_size(packets_));
>              break;
>          }
>          dp_packet_batch_apply_cutlen(packets_);
> -        push_tnl_action(pmd, a, packets_);
> +        packet_count = dp_packet_batch_size(packets_);
> +        if (push_tnl_action(pmd, a, packets_)) {
> +            COVERAGE_ADD(datapath_drop_tunnel_push_error,
> +                            packet_count);
> +        }
>          return;
>  
>      case OVS_ACTION_ATTR_TUNNEL_POP:
> @@ -7015,7 +7041,13 @@ 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(datapath_drop_tunnel_pop_error,
> +                                     packet_dropped);
> +                }
>                  if (dp_packet_batch_is_empty(packets_)) {
>                      return;
>                  }
> @@ -7030,6 +7062,11 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>                  (*depth)--;
>                  return;
>              }
> +            COVERAGE_ADD(datapath_drop_invalid_tnl_port,
> +                            dp_packet_batch_size(packets_));
> +        } else {
> +            COVERAGE_ADD(datapath_drop_recirc_error,
> +                            dp_packet_batch_size(packets_));
>          }
>          break;
>  
> @@ -7074,6 +7111,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>  
>              return;
>          }
> +        COVERAGE_ADD(datapath_drop_lock_error,
> +                        dp_packet_batch_size(packets_));
>          break;
>  
>      case OVS_ACTION_ATTR_RECIRC:
> @@ -7097,6 +7136,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
>              return;
>          }
>  
> +        COVERAGE_ADD(datapath_drop_recirc_error,
> +                        dp_packet_batch_size(packets_));
>          VLOG_WARN("Packet dropped. Max recirculation depth exceeded.");
>          break;
>  
> @@ -7249,6 +7290,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();
>      }
> diff --git a/lib/dpif.c b/lib/dpif.c
> index 457c9bf..d75a012 100644
> --- a/lib/dpif.c
> +++ b/lib/dpif.c
> @@ -1269,6 +1269,7 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
>      case OVS_ACTION_ATTR_POP_NSH:
>      case OVS_ACTION_ATTR_CT_CLEAR:
>      case OVS_ACTION_ATTR_UNSPEC:
> +    case OVS_ACTION_ATTR_DROP:
>      case __OVS_ACTION_ATTR_MAX:
>          OVS_NOT_REACHED();
>      }
> @@ -1874,6 +1875,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,
> 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/odp-execute.c b/lib/odp-execute.c
> index 5d07133..c8ff16b 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"
> @@ -36,6 +37,74 @@
>  #include "util.h"
>  #include "csum.h"
>  #include "conntrack.h"
> +#include "ofproto/ofproto-dpif-xlate.h"
> +#include "openvswitch/vlog.h"
> +
> +VLOG_DEFINE_THIS_MODULE(odp_execute)
> +COVERAGE_DEFINE(dp_sample_error_drop);
> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
> +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);
> +
> +static void
> +dp_update_drop_action_counter(enum xlate_error drop_reason,
> +                                 int delta)
> +{
> +   static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> +
> +   switch (drop_reason) {
> +   case XLATE_OK:
> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
> +        break;
> +   case XLATE_BRIDGE_NOT_FOUND:
> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
> +        break;
> +   case XLATE_RECURSION_TOO_DEEP:
> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
> +        break;
> +   case XLATE_TOO_MANY_RESUBMITS:
> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
> +        break;
> +   case XLATE_STACK_TOO_DEEP:
> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
> +        break;
> +   case XLATE_NO_RECIRCULATION_CONTEXT:
> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
> +        break;
> +   case XLATE_RECIRCULATION_CONFLICT:
> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
> +        break;
> +   case XLATE_TOO_MANY_MPLS_LABELS:
> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
> +        break;
> +   case XLATE_INVALID_TUNNEL_METADATA:
> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
> +        break;
> +   case XLATE_UNSUPPORTED_PACKET_TYPE:
> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
> +        break;
> +   case XLATE_CONGESTION_DROP:
> +        COVERAGE_ADD(drop_action_congestion, delta);
> +        break;
> +   case XLATE_FORWARDING_DISABLED:
> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
> +        break;
> +   case XLATE_MAX:
> +   default:
> +        VLOG_ERR_RL(&rl, "Invalid Drop reason type:%d", drop_reason);
> +   }
> +}
> +
>  
>  /* Masked copy of an ethernet address. 'src' is already properly masked. */
>  static void
> @@ -621,6 +690,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;
> @@ -705,6 +775,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:
> @@ -921,6 +992,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);
>                  }
>              }
> @@ -931,7 +1003,14 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
>                  conntrack_clear(packet);
>              }
>              break;
> -
> +        case OVS_ACTION_ATTR_DROP: {
> +            const enum xlate_error *drop_reason = nl_attr_get(a);
> +            if (*drop_reason < XLATE_MAX) {
> +                 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-util.c b/lib/odp-util.c
> index e893f46..9c3acc1 100644
> --- a/lib/odp-util.c
> +++ b/lib/odp-util.c
> @@ -43,6 +43,7 @@
>  #include "uuid.h"
>  #include "openvswitch/vlog.h"
>  #include "openvswitch/match.h"
> +#include "ofproto/ofproto-dpif-xlate.h"
>  
>  VLOG_DEFINE_THIS_MODULE(odp_util);
>  
> @@ -131,6 +132,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(enum xlate_error);
>  
>      case OVS_ACTION_ATTR_UNSPEC:
>      case __OVS_ACTION_ATTR_MAX:
> @@ -1182,6 +1184,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:
> +        ds_put_cstr(ds, "drop");
> +        break;
>      case OVS_ACTION_ATTR_UNSPEC:
>      case __OVS_ACTION_ATTR_MAX:
>      default:
> @@ -2428,8 +2433,12 @@ odp_actions_from_string(const char *s, const struct simap *port_names,
>                          struct ofpbuf *actions)
>  {
>      size_t old_size;
> +    enum xlate_error drop_action;
>  
>      if (!strcasecmp(s, "drop")) {
> +        drop_action = XLATE_OK;
> +        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 5ea1097..52b328d 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 bc4ffee..0d0a27d 100644
> --- a/ofproto/ofproto-dpif-sflow.c
> +++ b/ofproto/ofproto-dpif-sflow.c
> @@ -1223,6 +1223,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-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index acd4817..95e2f25 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -444,6 +444,12 @@ 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";
> +    case XLATE_MAX:
> +        break;
>      }
>      return "Unknown error";
>  }
> @@ -5921,6 +5927,14 @@ put_ct_label(const struct flow *flow, struct ofpbuf *odp_actions,
>  }
>  
>  static void
> +put_drop_action(struct ofpbuf *odp_actions, enum xlate_error error)
> +{
> +    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_DROP,
> +                      &error, sizeof error);
> +
> +}
> +
> +static void
>  put_ct_helper(struct xlate_ctx *ctx,
>                struct ofpbuf *odp_actions, struct ofpact_conntrack *ofc)
>  {
> @@ -7383,48 +7397,51 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
>          }
>          size_t sample_actions_len = ctx.odp_actions->size;
>  
> -        if (tnl_process_ecn(flow)
> -            && (!in_port || may_receive(in_port, &ctx))) {
> -            const struct ofpact *ofpacts;
> -            size_t ofpacts_len;
> -
> -            if (xin->ofpacts) {
> -                ofpacts = xin->ofpacts;
> -                ofpacts_len = xin->ofpacts_len;
> -            } else if (ctx.rule) {
> -                const struct rule_actions *actions
> -                    = rule_get_actions(&ctx.rule->up);
> -                ofpacts = actions->ofpacts;
> -                ofpacts_len = actions->ofpacts_len;
> -                ctx.rule_cookie = ctx.rule->up.flow_cookie;
> -            } else {
> -                OVS_NOT_REACHED();
> -            }
> +        if (!tnl_process_ecn(flow)) {
> +            ctx.error = XLATE_CONGESTION_DROP;
> +        } else {
> +            if (!in_port || may_receive(in_port, &ctx)) {
> +                const struct ofpact *ofpacts;
> +                size_t ofpacts_len;
> +
> +                if (xin->ofpacts) {
> +                    ofpacts = xin->ofpacts;
> +                    ofpacts_len = xin->ofpacts_len;
> +                } else if (ctx.rule) {
> +                    const struct rule_actions *actions
> +                         = rule_get_actions(&ctx.rule->up);
> +                    ofpacts = actions->ofpacts;
> +                    ofpacts_len = actions->ofpacts_len;
> +                    ctx.rule_cookie = ctx.rule->up.flow_cookie;
> +                } else {
> +                    OVS_NOT_REACHED();
> +                }
>  
> -            mirror_ingress_packet(&ctx);
> -            do_xlate_actions(ofpacts, ofpacts_len, &ctx, true, false);
> -            if (ctx.error) {
> -                goto exit;
> -            }
> +                mirror_ingress_packet(&ctx);
> +                do_xlate_actions(ofpacts, ofpacts_len, &ctx, true, false);
> +                if (ctx.error) {
> +                    goto exit;
> +                }
>  
> -            /* We've let OFPP_NORMAL and the learning action look at the
> -             * packet, so cancel all actions and freezing if forwarding is
> -             * disabled. */
> -            if (in_port && (!xport_stp_forward_state(in_port) ||
> -                            !xport_rstp_forward_state(in_port))) {
> -                ctx.odp_actions->size = sample_actions_len;
> -                ctx_cancel_freeze(&ctx);
> -                ofpbuf_clear(&ctx.action_set);
> -            }
> +                /* We've let OFPP_NORMAL and the learning action look at the
> +                * packet, so cancel all actions and freezing if forwarding is
> +                * disabled. */
> +                if (in_port && (!xport_stp_forward_state(in_port) ||
> +                                !xport_rstp_forward_state(in_port))) {
> +                    ctx.odp_actions->size = sample_actions_len;
> +                    ctx_cancel_freeze(&ctx);
> +                    ofpbuf_clear(&ctx.action_set);
> +                    ctx.error = XLATE_FORWARDING_DISABLED;
> +                }
>  
> -            if (!ctx.freezing) {
> -                xlate_action_set(&ctx);
> -            }
> -            if (ctx.freezing) {
> -                finish_freezing(&ctx);
> +                if (!ctx.freezing) {
> +                    xlate_action_set(&ctx);
> +                }
> +                if (ctx.freezing) {
> +                    finish_freezing(&ctx);
> +                }
>              }
>          }
> -
>          /* Output only fully processed packets. */
>          if (!ctx.freezing
>              && xbridge->has_in_band
> @@ -7522,6 +7539,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..496bdbf 100644
> --- a/ofproto/ofproto-dpif-xlate.h
> +++ b/ofproto/ofproto-dpif-xlate.h
> @@ -216,6 +216,9 @@ enum xlate_error {
>      XLATE_TOO_MANY_MPLS_LABELS,
>      XLATE_INVALID_TUNNEL_METADATA,
>      XLATE_UNSUPPORTED_PACKET_TYPE,
> +    XLATE_CONGESTION_DROP,
> +    XLATE_FORWARDING_DISABLED,
> +    XLATE_MAX,
>  };
>  
>  const char *xlate_strerror(enum xlate_error error);
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index 50c959b..91df1b3 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);
> diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
> index 1a404c8..8d8edca 100644
> --- a/ofproto/ofproto-dpif.h
> +++ b/ofproto/ofproto-dpif.h
> @@ -192,7 +192,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 +364,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..a4da75e 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/dpif-netdev.at b/tests/dpif-netdev.at
> index 6915d43..5221c10 100644
> --- a/tests/dpif-netdev.at
> +++ b/tests/dpif-netdev.at
> @@ -337,6 +337,14 @@ 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
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "datapath_drop_meter" | 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..318bc02
> --- /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
> +])
> +
> +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..2c8cdce 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -9384,7 +9384,7 @@ recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,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
>  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
>  ])
> 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..045b1a7 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 5000
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "datapath_drop_tunnel_pop_error" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
> +sleep 1
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +
> +ovs-appctl time/warp 5000
> +
> +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
> @@ -478,7 +499,7 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  5'], [0], [dnl
>    port  5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=?
>  ])
>  AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 'in_port(6081)'], [0], [dnl
> -tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=3,rule_cookie=0,controller_id=0,max_len=65535))
> +tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=2,rule_cookie=0,controller_id=0,max_len=65535))
>  ])
>  
>  ovs-appctl time/warp 10000
> @@ -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..7bd5b48 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
> +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,16 @@ 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 "datapath_drop_invalid_port" | cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
>  OVS_VSWITCHD_STOP
>  AT_CLEANUP
>  
>
Eelco Chaudron May 24, 2019, 9:46 a.m. UTC | #2
Hi Anju,

Was there ever a follow up on this patch? I only see one response from 
Ilya on this asking about his v8 comments.

Thanks,

Eelco


On 27 Feb 2019, at 10:22, 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. Identify and account all the silent packet drop scenarios
>
> 2. Display these drops in ovs-appctl coverage/show
>
> 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
>
> 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 |   1 +
>  lib/dpif-netdev.c                                 |  44 ++++-
>  lib/dpif.c                                        |   7 +
>  lib/dpif.h                                        |   3 +
>  lib/odp-execute.c                                 |  81 ++++++++-
>  lib/odp-util.c                                    |   9 +
>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>  ofproto/ofproto-dpif-xlate.c                      | 103 +++++++----
>  ofproto/ofproto-dpif-xlate.h                      |   3 +
>  ofproto/ofproto-dpif.c                            |   8 +
>  ofproto/ofproto-dpif.h                            |   7 +-
>  tests/automake.mk                                 |   3 +-
>  tests/dpif-netdev.at                              |   8 +
>  tests/drop-stats.at                               | 197 
> ++++++++++++++++++++++
>  tests/ofproto-dpif.at                             |   2 +-
>  tests/testsuite.at                                |   1 +
>  tests/tunnel-push-pop.at                          |  28 ++-
>  tests/tunnel.at                                   |  14 +-
>  19 files changed, 475 insertions(+), 46 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 d5aa09d..e77e9c8 100644
> --- a/datapath/linux/compat/include/linux/openvswitch.h
> +++ b/datapath/linux/compat/include/linux/openvswitch.h
> @@ -946,6 +946,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,         /* Drop action. */
>
>  #ifndef __KERNEL__
>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
> index 77ac1d2..acc7913 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -101,6 +101,17 @@ 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(datapath_drop_meter);
> +COVERAGE_DEFINE(datapath_drop_upcall_error);
> +COVERAGE_DEFINE(datapath_drop_lock_error);
> +COVERAGE_DEFINE(datapath_drop_userspace_action_error);
> +COVERAGE_DEFINE(datapath_drop_tunnel_push_error);
> +COVERAGE_DEFINE(datapath_drop_tunnel_pop_error);
> +COVERAGE_DEFINE(datapath_drop_recirc_error);
> +COVERAGE_DEFINE(datapath_drop_invalid_port);
> +COVERAGE_DEFINE(datapath_drop_invalid_tnl_port);
> +COVERAGE_DEFINE(datapath_drop_rx_invalid_packet);
> +
>  /* Protects against changes to 'dp_netdevs'. */
>  static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>
> @@ -5647,6 +5658,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, struct 
> dp_packet_batch *packets_,
>              band->packet_count += 1;
>              band->byte_count += dp_packet_size(packet);
>
> +            COVERAGE_INC(datapath_drop_meter);
>              dp_packet_delete(packet);
>          } else {
>              /* Meter accepts packet. */
> @@ -6402,6 +6414,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(datapath_drop_rx_invalid_packet);
>              continue;
>          }
>
> @@ -6528,6 +6541,7 @@ handle_packet_upcall(struct dp_netdev_pmd_thread 
> *pmd,
>                               put_actions);
>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>          dp_packet_delete(packet);
> +        COVERAGE_INC(datapath_drop_upcall_error);
>          return error;
>      }
>
> @@ -6659,6 +6673,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(datapath_drop_lock_error);
>                  upcall_fail_cnt++;
>              }
>          }
> @@ -6928,6 +6943,7 @@ dp_execute_userspace_action(struct 
> dp_netdev_pmd_thread *pmd,
>                                    actions->data, actions->size);
>      } else if (should_steal) {
>          dp_packet_delete(packet);
> +        COVERAGE_INC(datapath_drop_userspace_action_error);
>      }
>  }
>
> @@ -6942,6 +6958,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:
> @@ -6983,6 +7000,9 @@ dp_execute_cb(void *aux_, struct dp_packet_batch 
> *packets_,
>                  dp_packet_batch_add(&p->output_pkts, packet);
>              }
>              return;
> +        } else {
> +            COVERAGE_ADD(datapath_drop_invalid_port,
> +                            dp_packet_batch_size(packets_));
>          }
>          break;
>
> @@ -6992,10 +7012,16 @@ 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(datapath_drop_tunnel_push_error,
> +                            dp_packet_batch_size(packets_));
>              break;
>          }
>          dp_packet_batch_apply_cutlen(packets_);
> -        push_tnl_action(pmd, a, packets_);
> +        packet_count = dp_packet_batch_size(packets_);
> +        if (push_tnl_action(pmd, a, packets_)) {
> +            COVERAGE_ADD(datapath_drop_tunnel_push_error,
> +                            packet_count);
> +        }
>          return;
>
>      case OVS_ACTION_ATTR_TUNNEL_POP:
> @@ -7015,7 +7041,13 @@ 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(datapath_drop_tunnel_pop_error,
> +                                     packet_dropped);
> +                }
>                  if (dp_packet_batch_is_empty(packets_)) {
>                      return;
>                  }
> @@ -7030,6 +7062,11 @@ dp_execute_cb(void *aux_, struct 
> dp_packet_batch *packets_,
>                  (*depth)--;
>                  return;
>              }
> +            COVERAGE_ADD(datapath_drop_invalid_tnl_port,
> +                            dp_packet_batch_size(packets_));
> +        } else {
> +            COVERAGE_ADD(datapath_drop_recirc_error,
> +                            dp_packet_batch_size(packets_));
>          }
>          break;
>
> @@ -7074,6 +7111,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch 
> *packets_,
>
>              return;
>          }
> +        COVERAGE_ADD(datapath_drop_lock_error,
> +                        dp_packet_batch_size(packets_));
>          break;
>
>      case OVS_ACTION_ATTR_RECIRC:
> @@ -7097,6 +7136,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch 
> *packets_,
>              return;
>          }
>
> +        COVERAGE_ADD(datapath_drop_recirc_error,
> +                        dp_packet_batch_size(packets_));
>          VLOG_WARN("Packet dropped. Max recirculation depth 
> exceeded.");
>          break;
>
> @@ -7249,6 +7290,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();
>      }
> diff --git a/lib/dpif.c b/lib/dpif.c
> index 457c9bf..d75a012 100644
> --- a/lib/dpif.c
> +++ b/lib/dpif.c
> @@ -1269,6 +1269,7 @@ dpif_execute_helper_cb(void *aux_, struct 
> dp_packet_batch *packets_,
>      case OVS_ACTION_ATTR_POP_NSH:
>      case OVS_ACTION_ATTR_CT_CLEAR:
>      case OVS_ACTION_ATTR_UNSPEC:
> +    case OVS_ACTION_ATTR_DROP:
>      case __OVS_ACTION_ATTR_MAX:
>          OVS_NOT_REACHED();
>      }
> @@ -1874,6 +1875,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,
> 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/odp-execute.c b/lib/odp-execute.c
> index 5d07133..c8ff16b 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"
> @@ -36,6 +37,74 @@
>  #include "util.h"
>  #include "csum.h"
>  #include "conntrack.h"
> +#include "ofproto/ofproto-dpif-xlate.h"
> +#include "openvswitch/vlog.h"
> +
> +VLOG_DEFINE_THIS_MODULE(odp_execute)
> +COVERAGE_DEFINE(dp_sample_error_drop);
> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
> +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);
> +
> +static void
> +dp_update_drop_action_counter(enum xlate_error drop_reason,
> +                                 int delta)
> +{
> +   static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> +
> +   switch (drop_reason) {
> +   case XLATE_OK:
> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
> +        break;
> +   case XLATE_BRIDGE_NOT_FOUND:
> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
> +        break;
> +   case XLATE_RECURSION_TOO_DEEP:
> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
> +        break;
> +   case XLATE_TOO_MANY_RESUBMITS:
> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
> +        break;
> +   case XLATE_STACK_TOO_DEEP:
> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
> +        break;
> +   case XLATE_NO_RECIRCULATION_CONTEXT:
> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
> +        break;
> +   case XLATE_RECIRCULATION_CONFLICT:
> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
> +        break;
> +   case XLATE_TOO_MANY_MPLS_LABELS:
> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
> +        break;
> +   case XLATE_INVALID_TUNNEL_METADATA:
> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
> +        break;
> +   case XLATE_UNSUPPORTED_PACKET_TYPE:
> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
> +        break;
> +   case XLATE_CONGESTION_DROP:
> +        COVERAGE_ADD(drop_action_congestion, delta);
> +        break;
> +   case XLATE_FORWARDING_DISABLED:
> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
> +        break;
> +   case XLATE_MAX:
> +   default:
> +        VLOG_ERR_RL(&rl, "Invalid Drop reason type:%d", drop_reason);
> +   }
> +}
> +
>
>  /* Masked copy of an ethernet address. 'src' is already properly 
> masked. */
>  static void
> @@ -621,6 +690,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;
> @@ -705,6 +775,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:
> @@ -921,6 +992,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);
>                  }
>              }
> @@ -931,7 +1003,14 @@ odp_execute_actions(void *dp, struct 
> dp_packet_batch *batch, bool steal,
>                  conntrack_clear(packet);
>              }
>              break;
> -
> +        case OVS_ACTION_ATTR_DROP: {
> +            const enum xlate_error *drop_reason = nl_attr_get(a);
> +            if (*drop_reason < XLATE_MAX) {
> +                 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-util.c b/lib/odp-util.c
> index e893f46..9c3acc1 100644
> --- a/lib/odp-util.c
> +++ b/lib/odp-util.c
> @@ -43,6 +43,7 @@
>  #include "uuid.h"
>  #include "openvswitch/vlog.h"
>  #include "openvswitch/match.h"
> +#include "ofproto/ofproto-dpif-xlate.h"
>
>  VLOG_DEFINE_THIS_MODULE(odp_util);
>
> @@ -131,6 +132,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(enum xlate_error);
>
>      case OVS_ACTION_ATTR_UNSPEC:
>      case __OVS_ACTION_ATTR_MAX:
> @@ -1182,6 +1184,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:
> +        ds_put_cstr(ds, "drop");
> +        break;
>      case OVS_ACTION_ATTR_UNSPEC:
>      case __OVS_ACTION_ATTR_MAX:
>      default:
> @@ -2428,8 +2433,12 @@ odp_actions_from_string(const char *s, const 
> struct simap *port_names,
>                          struct ofpbuf *actions)
>  {
>      size_t old_size;
> +    enum xlate_error drop_action;
>
>      if (!strcasecmp(s, "drop")) {
> +        drop_action = XLATE_OK;
> +        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 5ea1097..52b328d 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 bc4ffee..0d0a27d 100644
> --- a/ofproto/ofproto-dpif-sflow.c
> +++ b/ofproto/ofproto-dpif-sflow.c
> @@ -1223,6 +1223,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-xlate.c 
> b/ofproto/ofproto-dpif-xlate.c
> index acd4817..95e2f25 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -444,6 +444,12 @@ 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";
> +    case XLATE_MAX:
> +        break;
>      }
>      return "Unknown error";
>  }
> @@ -5921,6 +5927,14 @@ put_ct_label(const struct flow *flow, struct 
> ofpbuf *odp_actions,
>  }
>
>  static void
> +put_drop_action(struct ofpbuf *odp_actions, enum xlate_error error)
> +{
> +    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_DROP,
> +                      &error, sizeof error);
> +
> +}
> +
> +static void
>  put_ct_helper(struct xlate_ctx *ctx,
>                struct ofpbuf *odp_actions, struct ofpact_conntrack 
> *ofc)
>  {
> @@ -7383,48 +7397,51 @@ xlate_actions(struct xlate_in *xin, struct 
> xlate_out *xout)
>          }
>          size_t sample_actions_len = ctx.odp_actions->size;
>
> -        if (tnl_process_ecn(flow)
> -            && (!in_port || may_receive(in_port, &ctx))) {
> -            const struct ofpact *ofpacts;
> -            size_t ofpacts_len;
> -
> -            if (xin->ofpacts) {
> -                ofpacts = xin->ofpacts;
> -                ofpacts_len = xin->ofpacts_len;
> -            } else if (ctx.rule) {
> -                const struct rule_actions *actions
> -                    = rule_get_actions(&ctx.rule->up);
> -                ofpacts = actions->ofpacts;
> -                ofpacts_len = actions->ofpacts_len;
> -                ctx.rule_cookie = ctx.rule->up.flow_cookie;
> -            } else {
> -                OVS_NOT_REACHED();
> -            }
> +        if (!tnl_process_ecn(flow)) {
> +            ctx.error = XLATE_CONGESTION_DROP;
> +        } else {
> +            if (!in_port || may_receive(in_port, &ctx)) {
> +                const struct ofpact *ofpacts;
> +                size_t ofpacts_len;
> +
> +                if (xin->ofpacts) {
> +                    ofpacts = xin->ofpacts;
> +                    ofpacts_len = xin->ofpacts_len;
> +                } else if (ctx.rule) {
> +                    const struct rule_actions *actions
> +                         = rule_get_actions(&ctx.rule->up);
> +                    ofpacts = actions->ofpacts;
> +                    ofpacts_len = actions->ofpacts_len;
> +                    ctx.rule_cookie = ctx.rule->up.flow_cookie;
> +                } else {
> +                    OVS_NOT_REACHED();
> +                }
>
> -            mirror_ingress_packet(&ctx);
> -            do_xlate_actions(ofpacts, ofpacts_len, &ctx, true, 
> false);
> -            if (ctx.error) {
> -                goto exit;
> -            }
> +                mirror_ingress_packet(&ctx);
> +                do_xlate_actions(ofpacts, ofpacts_len, &ctx, true, 
> false);
> +                if (ctx.error) {
> +                    goto exit;
> +                }
>
> -            /* We've let OFPP_NORMAL and the learning action look at 
> the
> -             * packet, so cancel all actions and freezing if 
> forwarding is
> -             * disabled. */
> -            if (in_port && (!xport_stp_forward_state(in_port) ||
> -                            !xport_rstp_forward_state(in_port))) {
> -                ctx.odp_actions->size = sample_actions_len;
> -                ctx_cancel_freeze(&ctx);
> -                ofpbuf_clear(&ctx.action_set);
> -            }
> +                /* We've let OFPP_NORMAL and the learning action look 
> at the
> +                * packet, so cancel all actions and freezing if 
> forwarding is
> +                * disabled. */
> +                if (in_port && (!xport_stp_forward_state(in_port) ||
> +                                !xport_rstp_forward_state(in_port))) 
> {
> +                    ctx.odp_actions->size = sample_actions_len;
> +                    ctx_cancel_freeze(&ctx);
> +                    ofpbuf_clear(&ctx.action_set);
> +                    ctx.error = XLATE_FORWARDING_DISABLED;
> +                }
>
> -            if (!ctx.freezing) {
> -                xlate_action_set(&ctx);
> -            }
> -            if (ctx.freezing) {
> -                finish_freezing(&ctx);
> +                if (!ctx.freezing) {
> +                    xlate_action_set(&ctx);
> +                }
> +                if (ctx.freezing) {
> +                    finish_freezing(&ctx);
> +                }
>              }
>          }
> -
>          /* Output only fully processed packets. */
>          if (!ctx.freezing
>              && xbridge->has_in_band
> @@ -7522,6 +7539,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..496bdbf 100644
> --- a/ofproto/ofproto-dpif-xlate.h
> +++ b/ofproto/ofproto-dpif-xlate.h
> @@ -216,6 +216,9 @@ enum xlate_error {
>      XLATE_TOO_MANY_MPLS_LABELS,
>      XLATE_INVALID_TUNNEL_METADATA,
>      XLATE_UNSUPPORTED_PACKET_TYPE,
> +    XLATE_CONGESTION_DROP,
> +    XLATE_FORWARDING_DISABLED,
> +    XLATE_MAX,
>  };
>
>  const char *xlate_strerror(enum xlate_error error);
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index 50c959b..91df1b3 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);
> diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
> index 1a404c8..8d8edca 100644
> --- a/ofproto/ofproto-dpif.h
> +++ b/ofproto/ofproto-dpif.h
> @@ -192,7 +192,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 +364,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..a4da75e 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/dpif-netdev.at b/tests/dpif-netdev.at
> index 6915d43..5221c10 100644
> --- a/tests/dpif-netdev.at
> +++ b/tests/dpif-netdev.at
> @@ -337,6 +337,14 @@ 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
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "datapath_drop_meter" | 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..318bc02
> --- /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
> +])
> +
> +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..2c8cdce 100644
> --- a/tests/ofproto-dpif.at
> +++ b/tests/ofproto-dpif.at
> @@ -9384,7 +9384,7 @@ 
> recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,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
>  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
>  ])
> 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..045b1a7 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 5000
> +
> +AT_CHECK([
> +ovs-appctl coverage/show | grep "datapath_drop_tunnel_pop_error" | 
> cut -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
> +
> +sleep 1
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 
> 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +
> +ovs-appctl time/warp 5000
> +
> +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
> @@ -478,7 +499,7 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port 
>  5'], [0], [dnl
>    port  5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, 
> crc=?
>  ])
>  AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 'in_port(6081)'], 
> [0], [dnl
> -tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), 
> packets:0, bytes:0, used:never, 
> actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=3,rule_cookie=0,controller_id=0,max_len=65535))
> +tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), 
> packets:0, bytes:0, used:never, 
> actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=2,rule_cookie=0,controller_id=0,max_len=65535))
>  ])
>
>  ovs-appctl time/warp 10000
> @@ -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..7bd5b48 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
> +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,16 @@ 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 "datapath_drop_invalid_port" | cut 
> -d':' -f2|sed 's/ //'
> +], [0], [dnl
> +1
> +])
>  OVS_VSWITCHD_STOP
>  AT_CLEANUP
>
> -- 
> 1.9.1
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Eelco Chaudron June 3, 2019, 12:52 p.m. UTC | #3
Hi Anju,

Wondering if you got the email below…

Cheers,

Eelco


On 24 May 2019, at 11:46, Eelco Chaudron wrote:

> Hi Anju,
>
> Was there ever a follow up on this patch? I only see one response from 
> Ilya on this asking about his v8 comments.
>
> Thanks,
>
> Eelco
>
>
> On 27 Feb 2019, at 10:22, 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. Identify and account all the silent packet drop scenarios
>>
>> 2. Display these drops in ovs-appctl coverage/show
>>
>> 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
>>
>> 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 |   1 +
>>  lib/dpif-netdev.c                                 |  44 ++++-
>>  lib/dpif.c                                        |   7 +
>>  lib/dpif.h                                        |   3 +
>>  lib/odp-execute.c                                 |  81 ++++++++-
>>  lib/odp-util.c                                    |   9 +
>>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>>  ofproto/ofproto-dpif-xlate.c                      | 103 +++++++----
>>  ofproto/ofproto-dpif-xlate.h                      |   3 +
>>  ofproto/ofproto-dpif.c                            |   8 +
>>  ofproto/ofproto-dpif.h                            |   7 +-
>>  tests/automake.mk                                 |   3 +-
>>  tests/dpif-netdev.at                              |   8 +
>>  tests/drop-stats.at                               | 197 
>> ++++++++++++++++++++++
>>  tests/ofproto-dpif.at                             |   2 +-
>>  tests/testsuite.at                                |   1 +
>>  tests/tunnel-push-pop.at                          |  28 ++-
>>  tests/tunnel.at                                   |  14 +-
>>  19 files changed, 475 insertions(+), 46 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 d5aa09d..e77e9c8 100644
>> --- a/datapath/linux/compat/include/linux/openvswitch.h
>> +++ b/datapath/linux/compat/include/linux/openvswitch.h
>> @@ -946,6 +946,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,         /* Drop action. */
>>
>>  #ifndef __KERNEL__
>>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
>> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
>> index 77ac1d2..acc7913 100644
>> --- a/lib/dpif-netdev.c
>> +++ b/lib/dpif-netdev.c
>> @@ -101,6 +101,17 @@ 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(datapath_drop_meter);
>> +COVERAGE_DEFINE(datapath_drop_upcall_error);
>> +COVERAGE_DEFINE(datapath_drop_lock_error);
>> +COVERAGE_DEFINE(datapath_drop_userspace_action_error);
>> +COVERAGE_DEFINE(datapath_drop_tunnel_push_error);
>> +COVERAGE_DEFINE(datapath_drop_tunnel_pop_error);
>> +COVERAGE_DEFINE(datapath_drop_recirc_error);
>> +COVERAGE_DEFINE(datapath_drop_invalid_port);
>> +COVERAGE_DEFINE(datapath_drop_invalid_tnl_port);
>> +COVERAGE_DEFINE(datapath_drop_rx_invalid_packet);
>> +
>>  /* Protects against changes to 'dp_netdevs'. */
>>  static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>>
>> @@ -5647,6 +5658,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, 
>> struct dp_packet_batch *packets_,
>>              band->packet_count += 1;
>>              band->byte_count += dp_packet_size(packet);
>>
>> +            COVERAGE_INC(datapath_drop_meter);
>>              dp_packet_delete(packet);
>>          } else {
>>              /* Meter accepts packet. */
>> @@ -6402,6 +6414,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(datapath_drop_rx_invalid_packet);
>>              continue;
>>          }
>>
>> @@ -6528,6 +6541,7 @@ handle_packet_upcall(struct 
>> dp_netdev_pmd_thread *pmd,
>>                               put_actions);
>>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>>          dp_packet_delete(packet);
>> +        COVERAGE_INC(datapath_drop_upcall_error);
>>          return error;
>>      }
>>
>> @@ -6659,6 +6673,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(datapath_drop_lock_error);
>>                  upcall_fail_cnt++;
>>              }
>>          }
>> @@ -6928,6 +6943,7 @@ dp_execute_userspace_action(struct 
>> dp_netdev_pmd_thread *pmd,
>>                                    actions->data, actions->size);
>>      } else if (should_steal) {
>>          dp_packet_delete(packet);
>> +        COVERAGE_INC(datapath_drop_userspace_action_error);
>>      }
>>  }
>>
>> @@ -6942,6 +6958,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:
>> @@ -6983,6 +7000,9 @@ dp_execute_cb(void *aux_, struct 
>> dp_packet_batch *packets_,
>>                  dp_packet_batch_add(&p->output_pkts, packet);
>>              }
>>              return;
>> +        } else {
>> +            COVERAGE_ADD(datapath_drop_invalid_port,
>> +                            dp_packet_batch_size(packets_));
>>          }
>>          break;
>>
>> @@ -6992,10 +7012,16 @@ 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(datapath_drop_tunnel_push_error,
>> +                            dp_packet_batch_size(packets_));
>>              break;
>>          }
>>          dp_packet_batch_apply_cutlen(packets_);
>> -        push_tnl_action(pmd, a, packets_);
>> +        packet_count = dp_packet_batch_size(packets_);
>> +        if (push_tnl_action(pmd, a, packets_)) {
>> +            COVERAGE_ADD(datapath_drop_tunnel_push_error,
>> +                            packet_count);
>> +        }
>>          return;
>>
>>      case OVS_ACTION_ATTR_TUNNEL_POP:
>> @@ -7015,7 +7041,13 @@ 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(datapath_drop_tunnel_pop_error,
>> +                                     packet_dropped);
>> +                }
>>                  if (dp_packet_batch_is_empty(packets_)) {
>>                      return;
>>                  }
>> @@ -7030,6 +7062,11 @@ dp_execute_cb(void *aux_, struct 
>> dp_packet_batch *packets_,
>>                  (*depth)--;
>>                  return;
>>              }
>> +            COVERAGE_ADD(datapath_drop_invalid_tnl_port,
>> +                            dp_packet_batch_size(packets_));
>> +        } else {
>> +            COVERAGE_ADD(datapath_drop_recirc_error,
>> +                            dp_packet_batch_size(packets_));
>>          }
>>          break;
>>
>> @@ -7074,6 +7111,8 @@ dp_execute_cb(void *aux_, struct 
>> dp_packet_batch *packets_,
>>
>>              return;
>>          }
>> +        COVERAGE_ADD(datapath_drop_lock_error,
>> +                        dp_packet_batch_size(packets_));
>>          break;
>>
>>      case OVS_ACTION_ATTR_RECIRC:
>> @@ -7097,6 +7136,8 @@ dp_execute_cb(void *aux_, struct 
>> dp_packet_batch *packets_,
>>              return;
>>          }
>>
>> +        COVERAGE_ADD(datapath_drop_recirc_error,
>> +                        dp_packet_batch_size(packets_));
>>          VLOG_WARN("Packet dropped. Max recirculation depth 
>> exceeded.");
>>          break;
>>
>> @@ -7249,6 +7290,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();
>>      }
>> diff --git a/lib/dpif.c b/lib/dpif.c
>> index 457c9bf..d75a012 100644
>> --- a/lib/dpif.c
>> +++ b/lib/dpif.c
>> @@ -1269,6 +1269,7 @@ dpif_execute_helper_cb(void *aux_, struct 
>> dp_packet_batch *packets_,
>>      case OVS_ACTION_ATTR_POP_NSH:
>>      case OVS_ACTION_ATTR_CT_CLEAR:
>>      case OVS_ACTION_ATTR_UNSPEC:
>> +    case OVS_ACTION_ATTR_DROP:
>>      case __OVS_ACTION_ATTR_MAX:
>>          OVS_NOT_REACHED();
>>      }
>> @@ -1874,6 +1875,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,
>> 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/odp-execute.c b/lib/odp-execute.c
>> index 5d07133..c8ff16b 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"
>> @@ -36,6 +37,74 @@
>>  #include "util.h"
>>  #include "csum.h"
>>  #include "conntrack.h"
>> +#include "ofproto/ofproto-dpif-xlate.h"
>> +#include "openvswitch/vlog.h"
>> +
>> +VLOG_DEFINE_THIS_MODULE(odp_execute)
>> +COVERAGE_DEFINE(dp_sample_error_drop);
>> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
>> +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);
>> +
>> +static void
>> +dp_update_drop_action_counter(enum xlate_error drop_reason,
>> +                                 int delta)
>> +{
>> +   static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
>> +
>> +   switch (drop_reason) {
>> +   case XLATE_OK:
>> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
>> +        break;
>> +   case XLATE_BRIDGE_NOT_FOUND:
>> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
>> +        break;
>> +   case XLATE_RECURSION_TOO_DEEP:
>> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
>> +        break;
>> +   case XLATE_TOO_MANY_RESUBMITS:
>> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
>> +        break;
>> +   case XLATE_STACK_TOO_DEEP:
>> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
>> +        break;
>> +   case XLATE_NO_RECIRCULATION_CONTEXT:
>> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
>> +        break;
>> +   case XLATE_RECIRCULATION_CONFLICT:
>> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
>> +        break;
>> +   case XLATE_TOO_MANY_MPLS_LABELS:
>> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
>> +        break;
>> +   case XLATE_INVALID_TUNNEL_METADATA:
>> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
>> +        break;
>> +   case XLATE_UNSUPPORTED_PACKET_TYPE:
>> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
>> +        break;
>> +   case XLATE_CONGESTION_DROP:
>> +        COVERAGE_ADD(drop_action_congestion, delta);
>> +        break;
>> +   case XLATE_FORWARDING_DISABLED:
>> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
>> +        break;
>> +   case XLATE_MAX:
>> +   default:
>> +        VLOG_ERR_RL(&rl, "Invalid Drop reason type:%d", 
>> drop_reason);
>> +   }
>> +}
>> +
>>
>>  /* Masked copy of an ethernet address. 'src' is already properly 
>> masked. */
>>  static void
>> @@ -621,6 +690,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;
>> @@ -705,6 +775,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:
>> @@ -921,6 +992,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);
>>                  }
>>              }
>> @@ -931,7 +1003,14 @@ odp_execute_actions(void *dp, struct 
>> dp_packet_batch *batch, bool steal,
>>                  conntrack_clear(packet);
>>              }
>>              break;
>> -
>> +        case OVS_ACTION_ATTR_DROP: {
>> +            const enum xlate_error *drop_reason = nl_attr_get(a);
>> +            if (*drop_reason < XLATE_MAX) {
>> +                 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-util.c b/lib/odp-util.c
>> index e893f46..9c3acc1 100644
>> --- a/lib/odp-util.c
>> +++ b/lib/odp-util.c
>> @@ -43,6 +43,7 @@
>>  #include "uuid.h"
>>  #include "openvswitch/vlog.h"
>>  #include "openvswitch/match.h"
>> +#include "ofproto/ofproto-dpif-xlate.h"
>>
>>  VLOG_DEFINE_THIS_MODULE(odp_util);
>>
>> @@ -131,6 +132,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(enum xlate_error);
>>
>>      case OVS_ACTION_ATTR_UNSPEC:
>>      case __OVS_ACTION_ATTR_MAX:
>> @@ -1182,6 +1184,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:
>> +        ds_put_cstr(ds, "drop");
>> +        break;
>>      case OVS_ACTION_ATTR_UNSPEC:
>>      case __OVS_ACTION_ATTR_MAX:
>>      default:
>> @@ -2428,8 +2433,12 @@ odp_actions_from_string(const char *s, const 
>> struct simap *port_names,
>>                          struct ofpbuf *actions)
>>  {
>>      size_t old_size;
>> +    enum xlate_error drop_action;
>>
>>      if (!strcasecmp(s, "drop")) {
>> +        drop_action = XLATE_OK;
>> +        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 5ea1097..52b328d 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 bc4ffee..0d0a27d 100644
>> --- a/ofproto/ofproto-dpif-sflow.c
>> +++ b/ofproto/ofproto-dpif-sflow.c
>> @@ -1223,6 +1223,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-xlate.c 
>> b/ofproto/ofproto-dpif-xlate.c
>> index acd4817..95e2f25 100644
>> --- a/ofproto/ofproto-dpif-xlate.c
>> +++ b/ofproto/ofproto-dpif-xlate.c
>> @@ -444,6 +444,12 @@ 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";
>> +    case XLATE_MAX:
>> +        break;
>>      }
>>      return "Unknown error";
>>  }
>> @@ -5921,6 +5927,14 @@ put_ct_label(const struct flow *flow, struct 
>> ofpbuf *odp_actions,
>>  }
>>
>>  static void
>> +put_drop_action(struct ofpbuf *odp_actions, enum xlate_error error)
>> +{
>> +    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_DROP,
>> +                      &error, sizeof error);
>> +
>> +}
>> +
>> +static void
>>  put_ct_helper(struct xlate_ctx *ctx,
>>                struct ofpbuf *odp_actions, struct ofpact_conntrack 
>> *ofc)
>>  {
>> @@ -7383,48 +7397,51 @@ xlate_actions(struct xlate_in *xin, struct 
>> xlate_out *xout)
>>          }
>>          size_t sample_actions_len = ctx.odp_actions->size;
>>
>> -        if (tnl_process_ecn(flow)
>> -            && (!in_port || may_receive(in_port, &ctx))) {
>> -            const struct ofpact *ofpacts;
>> -            size_t ofpacts_len;
>> -
>> -            if (xin->ofpacts) {
>> -                ofpacts = xin->ofpacts;
>> -                ofpacts_len = xin->ofpacts_len;
>> -            } else if (ctx.rule) {
>> -                const struct rule_actions *actions
>> -                    = rule_get_actions(&ctx.rule->up);
>> -                ofpacts = actions->ofpacts;
>> -                ofpacts_len = actions->ofpacts_len;
>> -                ctx.rule_cookie = ctx.rule->up.flow_cookie;
>> -            } else {
>> -                OVS_NOT_REACHED();
>> -            }
>> +        if (!tnl_process_ecn(flow)) {
>> +            ctx.error = XLATE_CONGESTION_DROP;
>> +        } else {
>> +            if (!in_port || may_receive(in_port, &ctx)) {
>> +                const struct ofpact *ofpacts;
>> +                size_t ofpacts_len;
>> +
>> +                if (xin->ofpacts) {
>> +                    ofpacts = xin->ofpacts;
>> +                    ofpacts_len = xin->ofpacts_len;
>> +                } else if (ctx.rule) {
>> +                    const struct rule_actions *actions
>> +                         = rule_get_actions(&ctx.rule->up);
>> +                    ofpacts = actions->ofpacts;
>> +                    ofpacts_len = actions->ofpacts_len;
>> +                    ctx.rule_cookie = ctx.rule->up.flow_cookie;
>> +                } else {
>> +                    OVS_NOT_REACHED();
>> +                }
>>
>> -            mirror_ingress_packet(&ctx);
>> -            do_xlate_actions(ofpacts, ofpacts_len, &ctx, true, 
>> false);
>> -            if (ctx.error) {
>> -                goto exit;
>> -            }
>> +                mirror_ingress_packet(&ctx);
>> +                do_xlate_actions(ofpacts, ofpacts_len, &ctx, true, 
>> false);
>> +                if (ctx.error) {
>> +                    goto exit;
>> +                }
>>
>> -            /* We've let OFPP_NORMAL and the learning action look at 
>> the
>> -             * packet, so cancel all actions and freezing if 
>> forwarding is
>> -             * disabled. */
>> -            if (in_port && (!xport_stp_forward_state(in_port) ||
>> -                            !xport_rstp_forward_state(in_port))) {
>> -                ctx.odp_actions->size = sample_actions_len;
>> -                ctx_cancel_freeze(&ctx);
>> -                ofpbuf_clear(&ctx.action_set);
>> -            }
>> +                /* We've let OFPP_NORMAL and the learning action 
>> look at the
>> +                * packet, so cancel all actions and freezing if 
>> forwarding is
>> +                * disabled. */
>> +                if (in_port && (!xport_stp_forward_state(in_port) ||
>> +                                !xport_rstp_forward_state(in_port))) 
>> {
>> +                    ctx.odp_actions->size = sample_actions_len;
>> +                    ctx_cancel_freeze(&ctx);
>> +                    ofpbuf_clear(&ctx.action_set);
>> +                    ctx.error = XLATE_FORWARDING_DISABLED;
>> +                }
>>
>> -            if (!ctx.freezing) {
>> -                xlate_action_set(&ctx);
>> -            }
>> -            if (ctx.freezing) {
>> -                finish_freezing(&ctx);
>> +                if (!ctx.freezing) {
>> +                    xlate_action_set(&ctx);
>> +                }
>> +                if (ctx.freezing) {
>> +                    finish_freezing(&ctx);
>> +                }
>>              }
>>          }
>> -
>>          /* Output only fully processed packets. */
>>          if (!ctx.freezing
>>              && xbridge->has_in_band
>> @@ -7522,6 +7539,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..496bdbf 100644
>> --- a/ofproto/ofproto-dpif-xlate.h
>> +++ b/ofproto/ofproto-dpif-xlate.h
>> @@ -216,6 +216,9 @@ enum xlate_error {
>>      XLATE_TOO_MANY_MPLS_LABELS,
>>      XLATE_INVALID_TUNNEL_METADATA,
>>      XLATE_UNSUPPORTED_PACKET_TYPE,
>> +    XLATE_CONGESTION_DROP,
>> +    XLATE_FORWARDING_DISABLED,
>> +    XLATE_MAX,
>>  };
>>
>>  const char *xlate_strerror(enum xlate_error error);
>> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
>> index 50c959b..91df1b3 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);
>> diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
>> index 1a404c8..8d8edca 100644
>> --- a/ofproto/ofproto-dpif.h
>> +++ b/ofproto/ofproto-dpif.h
>> @@ -192,7 +192,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 +364,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..a4da75e 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/dpif-netdev.at b/tests/dpif-netdev.at
>> index 6915d43..5221c10 100644
>> --- a/tests/dpif-netdev.at
>> +++ b/tests/dpif-netdev.at
>> @@ -337,6 +337,14 @@ 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
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "datapath_drop_meter" | 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..318bc02
>> --- /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
>> +])
>> +
>> +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..2c8cdce 100644
>> --- a/tests/ofproto-dpif.at
>> +++ b/tests/ofproto-dpif.at
>> @@ -9384,7 +9384,7 @@ 
>> recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,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
>>  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
>>  ])
>> 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..045b1a7 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 5000
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "datapath_drop_tunnel_pop_error" | 
>> cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +1
>> +])
>> +
>> +sleep 1
>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 
>> 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>> +
>> +ovs-appctl time/warp 5000
>> +
>> +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
>> @@ -478,7 +499,7 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 
>> 'port  5'], [0], [dnl
>>    port  5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, 
>> crc=?
>>  ])
>>  AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 'in_port(6081)'], 
>> [0], [dnl
>> -tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), 
>> packets:0, bytes:0, used:never, 
>> actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=3,rule_cookie=0,controller_id=0,max_len=65535))
>> +tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), 
>> packets:0, bytes:0, used:never, 
>> actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=2,rule_cookie=0,controller_id=0,max_len=65535))
>>  ])
>>
>>  ovs-appctl time/warp 10000
>> @@ -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..7bd5b48 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
>> +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,16 @@ 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 "datapath_drop_invalid_port" | cut 
>> -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +1
>> +])
>>  OVS_VSWITCHD_STOP
>>  AT_CLEANUP
>>
>> -- 
>> 1.9.1
>>
>> _______________________________________________
>> dev mailing list
>> dev@openvswitch.org
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Anju Thomas June 4, 2019, 3:42 a.m. UTC | #4
Hi Eelco,
Apart from Ilya's comments I have not received any comments for v8. I have addressed those comment in v9.

Regards
Anju

-----Original Message-----
From: Eelco Chaudron <echaudro@redhat.com> 
Sent: Monday, June 3, 2019 6:23 PM
To: Anju Thomas <anju.thomas@ericsson.com>
Cc: dev@openvswitch.org
Subject: Re: [ovs-dev] [PATCH v9] Improved Packet Drop Statistics in OVS

Hi Anju,

Wondering if you got the email below…

Cheers,

Eelco


On 24 May 2019, at 11:46, Eelco Chaudron wrote:

> Hi Anju,
>
> Was there ever a follow up on this patch? I only see one response from 
> Ilya on this asking about his v8 comments.
>
> Thanks,
>
> Eelco
>
>
> On 27 Feb 2019, at 10:22, 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. Identify and account all the silent packet drop scenarios
>>
>> 2. Display these drops in ovs-appctl coverage/show
>>
>> 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
>>
>> 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 |   1 +
>>  lib/dpif-netdev.c                                 |  44 ++++-
>>  lib/dpif.c                                        |   7 +
>>  lib/dpif.h                                        |   3 +
>>  lib/odp-execute.c                                 |  81 ++++++++-
>>  lib/odp-util.c                                    |   9 +
>>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>>  ofproto/ofproto-dpif-xlate.c                      | 103 +++++++----
>>  ofproto/ofproto-dpif-xlate.h                      |   3 +
>>  ofproto/ofproto-dpif.c                            |   8 +
>>  ofproto/ofproto-dpif.h                            |   7 +-
>>  tests/automake.mk                                 |   3 +-
>>  tests/dpif-netdev.at                              |   8 +
>>  tests/drop-stats.at                               | 197 
>> ++++++++++++++++++++++
>>  tests/ofproto-dpif.at                             |   2 +-
>>  tests/testsuite.at                                |   1 +
>>  tests/tunnel-push-pop.at                          |  28 ++-
>>  tests/tunnel.at                                   |  14 +-
>>  19 files changed, 475 insertions(+), 46 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 d5aa09d..e77e9c8 100644
>> --- a/datapath/linux/compat/include/linux/openvswitch.h
>> +++ b/datapath/linux/compat/include/linux/openvswitch.h
>> @@ -946,6 +946,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,         /* Drop action. */
>>
>>  #ifndef __KERNEL__
>>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
>> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 
>> 77ac1d2..acc7913 100644
>> --- a/lib/dpif-netdev.c
>> +++ b/lib/dpif-netdev.c
>> @@ -101,6 +101,17 @@ 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(datapath_drop_meter);
>> +COVERAGE_DEFINE(datapath_drop_upcall_error);
>> +COVERAGE_DEFINE(datapath_drop_lock_error);
>> +COVERAGE_DEFINE(datapath_drop_userspace_action_error);
>> +COVERAGE_DEFINE(datapath_drop_tunnel_push_error);
>> +COVERAGE_DEFINE(datapath_drop_tunnel_pop_error);
>> +COVERAGE_DEFINE(datapath_drop_recirc_error);
>> +COVERAGE_DEFINE(datapath_drop_invalid_port);
>> +COVERAGE_DEFINE(datapath_drop_invalid_tnl_port);
>> +COVERAGE_DEFINE(datapath_drop_rx_invalid_packet);
>> +
>>  /* Protects against changes to 'dp_netdevs'. */  static struct 
>> ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>>
>> @@ -5647,6 +5658,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, 
>> struct dp_packet_batch *packets_,
>>              band->packet_count += 1;
>>              band->byte_count += dp_packet_size(packet);
>>
>> +            COVERAGE_INC(datapath_drop_meter);
>>              dp_packet_delete(packet);
>>          } else {
>>              /* Meter accepts packet. */ @@ -6402,6 +6414,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(datapath_drop_rx_invalid_packet);
>>              continue;
>>          }
>>
>> @@ -6528,6 +6541,7 @@ handle_packet_upcall(struct 
>> dp_netdev_pmd_thread *pmd,
>>                               put_actions);
>>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>>          dp_packet_delete(packet);
>> +        COVERAGE_INC(datapath_drop_upcall_error);
>>          return error;
>>      }
>>
>> @@ -6659,6 +6673,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(datapath_drop_lock_error);
>>                  upcall_fail_cnt++;
>>              }
>>          }
>> @@ -6928,6 +6943,7 @@ dp_execute_userspace_action(struct
>> dp_netdev_pmd_thread *pmd,
>>                                    actions->data, actions->size);
>>      } else if (should_steal) {
>>          dp_packet_delete(packet);
>> +        COVERAGE_INC(datapath_drop_userspace_action_error);
>>      }
>>  }
>>
>> @@ -6942,6 +6958,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:
>> @@ -6983,6 +7000,9 @@ dp_execute_cb(void *aux_, struct 
>> dp_packet_batch *packets_,
>>                  dp_packet_batch_add(&p->output_pkts, packet);
>>              }
>>              return;
>> +        } else {
>> +            COVERAGE_ADD(datapath_drop_invalid_port,
>> +                            dp_packet_batch_size(packets_));
>>          }
>>          break;
>>
>> @@ -6992,10 +7012,16 @@ 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(datapath_drop_tunnel_push_error,
>> +                            dp_packet_batch_size(packets_));
>>              break;
>>          }
>>          dp_packet_batch_apply_cutlen(packets_);
>> -        push_tnl_action(pmd, a, packets_);
>> +        packet_count = dp_packet_batch_size(packets_);
>> +        if (push_tnl_action(pmd, a, packets_)) {
>> +            COVERAGE_ADD(datapath_drop_tunnel_push_error,
>> +                            packet_count);
>> +        }
>>          return;
>>
>>      case OVS_ACTION_ATTR_TUNNEL_POP:
>> @@ -7015,7 +7041,13 @@ 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(datapath_drop_tunnel_pop_error,
>> +                                     packet_dropped);
>> +                }
>>                  if (dp_packet_batch_is_empty(packets_)) {
>>                      return;
>>                  }
>> @@ -7030,6 +7062,11 @@ dp_execute_cb(void *aux_, struct 
>> dp_packet_batch *packets_,
>>                  (*depth)--;
>>                  return;
>>              }
>> +            COVERAGE_ADD(datapath_drop_invalid_tnl_port,
>> +                            dp_packet_batch_size(packets_));
>> +        } else {
>> +            COVERAGE_ADD(datapath_drop_recirc_error,
>> +                            dp_packet_batch_size(packets_));
>>          }
>>          break;
>>
>> @@ -7074,6 +7111,8 @@ dp_execute_cb(void *aux_, struct 
>> dp_packet_batch *packets_,
>>
>>              return;
>>          }
>> +        COVERAGE_ADD(datapath_drop_lock_error,
>> +                        dp_packet_batch_size(packets_));
>>          break;
>>
>>      case OVS_ACTION_ATTR_RECIRC:
>> @@ -7097,6 +7136,8 @@ dp_execute_cb(void *aux_, struct 
>> dp_packet_batch *packets_,
>>              return;
>>          }
>>
>> +        COVERAGE_ADD(datapath_drop_recirc_error,
>> +                        dp_packet_batch_size(packets_));
>>          VLOG_WARN("Packet dropped. Max recirculation depth 
>> exceeded.");
>>          break;
>>
>> @@ -7249,6 +7290,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();
>>      }
>> diff --git a/lib/dpif.c b/lib/dpif.c
>> index 457c9bf..d75a012 100644
>> --- a/lib/dpif.c
>> +++ b/lib/dpif.c
>> @@ -1269,6 +1269,7 @@ dpif_execute_helper_cb(void *aux_, struct 
>> dp_packet_batch *packets_,
>>      case OVS_ACTION_ATTR_POP_NSH:
>>      case OVS_ACTION_ATTR_CT_CLEAR:
>>      case OVS_ACTION_ATTR_UNSPEC:
>> +    case OVS_ACTION_ATTR_DROP:
>>      case __OVS_ACTION_ATTR_MAX:
>>          OVS_NOT_REACHED();
>>      }
>> @@ -1874,6 +1875,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, 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/odp-execute.c b/lib/odp-execute.c index 
>> 5d07133..c8ff16b 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"
>> @@ -36,6 +37,74 @@
>>  #include "util.h"
>>  #include "csum.h"
>>  #include "conntrack.h"
>> +#include "ofproto/ofproto-dpif-xlate.h"
>> +#include "openvswitch/vlog.h"
>> +
>> +VLOG_DEFINE_THIS_MODULE(odp_execute)
>> +COVERAGE_DEFINE(dp_sample_error_drop);
>> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
>> +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);
>> +
>> +static void
>> +dp_update_drop_action_counter(enum xlate_error drop_reason,
>> +                                 int delta) {
>> +   static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
>> +
>> +   switch (drop_reason) {
>> +   case XLATE_OK:
>> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
>> +        break;
>> +   case XLATE_BRIDGE_NOT_FOUND:
>> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
>> +        break;
>> +   case XLATE_RECURSION_TOO_DEEP:
>> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
>> +        break;
>> +   case XLATE_TOO_MANY_RESUBMITS:
>> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
>> +        break;
>> +   case XLATE_STACK_TOO_DEEP:
>> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
>> +        break;
>> +   case XLATE_NO_RECIRCULATION_CONTEXT:
>> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
>> +        break;
>> +   case XLATE_RECIRCULATION_CONFLICT:
>> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
>> +        break;
>> +   case XLATE_TOO_MANY_MPLS_LABELS:
>> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
>> +        break;
>> +   case XLATE_INVALID_TUNNEL_METADATA:
>> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
>> +        break;
>> +   case XLATE_UNSUPPORTED_PACKET_TYPE:
>> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
>> +        break;
>> +   case XLATE_CONGESTION_DROP:
>> +        COVERAGE_ADD(drop_action_congestion, delta);
>> +        break;
>> +   case XLATE_FORWARDING_DISABLED:
>> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
>> +        break;
>> +   case XLATE_MAX:
>> +   default:
>> +        VLOG_ERR_RL(&rl, "Invalid Drop reason type:%d",
>> drop_reason);
>> +   }
>> +}
>> +
>>
>>  /* Masked copy of an ethernet address. 'src' is already properly 
>> masked. */  static void @@ -621,6 +690,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;
>> @@ -705,6 +775,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:
>> @@ -921,6 +992,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);
>>                  }
>>              }
>> @@ -931,7 +1003,14 @@ odp_execute_actions(void *dp, struct 
>> dp_packet_batch *batch, bool steal,
>>                  conntrack_clear(packet);
>>              }
>>              break;
>> -
>> +        case OVS_ACTION_ATTR_DROP: {
>> +            const enum xlate_error *drop_reason = nl_attr_get(a);
>> +            if (*drop_reason < XLATE_MAX) {
>> +                 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-util.c b/lib/odp-util.c index e893f46..9c3acc1 
>> 100644
>> --- a/lib/odp-util.c
>> +++ b/lib/odp-util.c
>> @@ -43,6 +43,7 @@
>>  #include "uuid.h"
>>  #include "openvswitch/vlog.h"
>>  #include "openvswitch/match.h"
>> +#include "ofproto/ofproto-dpif-xlate.h"
>>
>>  VLOG_DEFINE_THIS_MODULE(odp_util);
>>
>> @@ -131,6 +132,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(enum xlate_error);
>>
>>      case OVS_ACTION_ATTR_UNSPEC:
>>      case __OVS_ACTION_ATTR_MAX:
>> @@ -1182,6 +1184,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:
>> +        ds_put_cstr(ds, "drop");
>> +        break;
>>      case OVS_ACTION_ATTR_UNSPEC:
>>      case __OVS_ACTION_ATTR_MAX:
>>      default:
>> @@ -2428,8 +2433,12 @@ odp_actions_from_string(const char *s, const 
>> struct simap *port_names,
>>                          struct ofpbuf *actions)  {
>>      size_t old_size;
>> +    enum xlate_error drop_action;
>>
>>      if (!strcasecmp(s, "drop")) {
>> +        drop_action = XLATE_OK;
>> +        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 5ea1097..52b328d 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 bc4ffee..0d0a27d 100644
>> --- a/ofproto/ofproto-dpif-sflow.c
>> +++ b/ofproto/ofproto-dpif-sflow.c
>> @@ -1223,6 +1223,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-xlate.c 
>> b/ofproto/ofproto-dpif-xlate.c
>> index acd4817..95e2f25 100644
>> --- a/ofproto/ofproto-dpif-xlate.c
>> +++ b/ofproto/ofproto-dpif-xlate.c
>> @@ -444,6 +444,12 @@ 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";
>> +    case XLATE_MAX:
>> +        break;
>>      }
>>      return "Unknown error";
>>  }
>> @@ -5921,6 +5927,14 @@ put_ct_label(const struct flow *flow, struct 
>> ofpbuf *odp_actions,
>>  }
>>
>>  static void
>> +put_drop_action(struct ofpbuf *odp_actions, enum xlate_error error)
>> +{
>> +    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_DROP,
>> +                      &error, sizeof error);
>> +
>> +}
>> +
>> +static void
>>  put_ct_helper(struct xlate_ctx *ctx,
>>                struct ofpbuf *odp_actions, struct ofpact_conntrack 
>> *ofc)
>>  {
>> @@ -7383,48 +7397,51 @@ xlate_actions(struct xlate_in *xin, struct 
>> xlate_out *xout)
>>          }
>>          size_t sample_actions_len = ctx.odp_actions->size;
>>
>> -        if (tnl_process_ecn(flow)
>> -            && (!in_port || may_receive(in_port, &ctx))) {
>> -            const struct ofpact *ofpacts;
>> -            size_t ofpacts_len;
>> -
>> -            if (xin->ofpacts) {
>> -                ofpacts = xin->ofpacts;
>> -                ofpacts_len = xin->ofpacts_len;
>> -            } else if (ctx.rule) {
>> -                const struct rule_actions *actions
>> -                    = rule_get_actions(&ctx.rule->up);
>> -                ofpacts = actions->ofpacts;
>> -                ofpacts_len = actions->ofpacts_len;
>> -                ctx.rule_cookie = ctx.rule->up.flow_cookie;
>> -            } else {
>> -                OVS_NOT_REACHED();
>> -            }
>> +        if (!tnl_process_ecn(flow)) {
>> +            ctx.error = XLATE_CONGESTION_DROP;
>> +        } else {
>> +            if (!in_port || may_receive(in_port, &ctx)) {
>> +                const struct ofpact *ofpacts;
>> +                size_t ofpacts_len;
>> +
>> +                if (xin->ofpacts) {
>> +                    ofpacts = xin->ofpacts;
>> +                    ofpacts_len = xin->ofpacts_len;
>> +                } else if (ctx.rule) {
>> +                    const struct rule_actions *actions
>> +                         = rule_get_actions(&ctx.rule->up);
>> +                    ofpacts = actions->ofpacts;
>> +                    ofpacts_len = actions->ofpacts_len;
>> +                    ctx.rule_cookie = ctx.rule->up.flow_cookie;
>> +                } else {
>> +                    OVS_NOT_REACHED();
>> +                }
>>
>> -            mirror_ingress_packet(&ctx);
>> -            do_xlate_actions(ofpacts, ofpacts_len, &ctx, true, 
>> false);
>> -            if (ctx.error) {
>> -                goto exit;
>> -            }
>> +                mirror_ingress_packet(&ctx);
>> +                do_xlate_actions(ofpacts, ofpacts_len, &ctx, true, 
>> false);
>> +                if (ctx.error) {
>> +                    goto exit;
>> +                }
>>
>> -            /* We've let OFPP_NORMAL and the learning action look at 
>> the
>> -             * packet, so cancel all actions and freezing if 
>> forwarding is
>> -             * disabled. */
>> -            if (in_port && (!xport_stp_forward_state(in_port) ||
>> -                            !xport_rstp_forward_state(in_port))) {
>> -                ctx.odp_actions->size = sample_actions_len;
>> -                ctx_cancel_freeze(&ctx);
>> -                ofpbuf_clear(&ctx.action_set);
>> -            }
>> +                /* We've let OFPP_NORMAL and the learning action 
>> look at the
>> +                * packet, so cancel all actions and freezing if 
>> forwarding is
>> +                * disabled. */
>> +                if (in_port && (!xport_stp_forward_state(in_port) ||
>> +                                !xport_rstp_forward_state(in_port))) 
>> {
>> +                    ctx.odp_actions->size = sample_actions_len;
>> +                    ctx_cancel_freeze(&ctx);
>> +                    ofpbuf_clear(&ctx.action_set);
>> +                    ctx.error = XLATE_FORWARDING_DISABLED;
>> +                }
>>
>> -            if (!ctx.freezing) {
>> -                xlate_action_set(&ctx);
>> -            }
>> -            if (ctx.freezing) {
>> -                finish_freezing(&ctx);
>> +                if (!ctx.freezing) {
>> +                    xlate_action_set(&ctx);
>> +                }
>> +                if (ctx.freezing) {
>> +                    finish_freezing(&ctx);
>> +                }
>>              }
>>          }
>> -
>>          /* Output only fully processed packets. */
>>          if (!ctx.freezing
>>              && xbridge->has_in_band
>> @@ -7522,6 +7539,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..496bdbf 100644
>> --- a/ofproto/ofproto-dpif-xlate.h
>> +++ b/ofproto/ofproto-dpif-xlate.h
>> @@ -216,6 +216,9 @@ enum xlate_error {
>>      XLATE_TOO_MANY_MPLS_LABELS,
>>      XLATE_INVALID_TUNNEL_METADATA,
>>      XLATE_UNSUPPORTED_PACKET_TYPE,
>> +    XLATE_CONGESTION_DROP,
>> +    XLATE_FORWARDING_DISABLED,
>> +    XLATE_MAX,
>>  };
>>
>>  const char *xlate_strerror(enum xlate_error error);
>> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
>> index 50c959b..91df1b3 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);
>> diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
>> index 1a404c8..8d8edca 100644
>> --- a/ofproto/ofproto-dpif.h
>> +++ b/ofproto/ofproto-dpif.h
>> @@ -192,7 +192,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 +364,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..a4da75e 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/dpif-netdev.at b/tests/dpif-netdev.at
>> index 6915d43..5221c10 100644
>> --- a/tests/dpif-netdev.at
>> +++ b/tests/dpif-netdev.at
>> @@ -337,6 +337,14 @@ 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
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "datapath_drop_meter" | 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..318bc02
>> --- /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
>> +])
>> +
>> +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..2c8cdce 100644
>> --- a/tests/ofproto-dpif.at
>> +++ b/tests/ofproto-dpif.at
>> @@ -9384,7 +9384,7 @@ 
>> recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,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
>>  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
>>  ])
>> 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..045b1a7 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 5000
>> +
>> +AT_CHECK([
>> +ovs-appctl coverage/show | grep "datapath_drop_tunnel_pop_error" | 
>> cut -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +1
>> +])
>> +
>> +sleep 1
>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 
>> 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>> +
>> +ovs-appctl time/warp 5000
>> +
>> +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
>> @@ -478,7 +499,7 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 
>> 'port  5'], [0], [dnl
>>    port  5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, 
>> crc=?
>>  ])
>>  AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 'in_port(6081)'], 
>> [0], [dnl
>> -tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), 
>> packets:0, bytes:0, used:never, 
>> actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=3,rule_cookie=0,controller_id=0,max_len=65535))
>> +tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), 
>> packets:0, bytes:0, used:never, 
>> actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=2,rule_cookie=0,controller_id=0,max_len=65535))
>>  ])
>>
>>  ovs-appctl time/warp 10000
>> @@ -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..7bd5b48 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
>> +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,16 @@ 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 "datapath_drop_invalid_port" | cut 
>> -d':' -f2|sed 's/ //'
>> +], [0], [dnl
>> +1
>> +])
>>  OVS_VSWITCHD_STOP
>>  AT_CLEANUP
>>
>> -- 
>> 1.9.1
>>
>> _______________________________________________
>> dev mailing list
>> dev@openvswitch.org
>> https://protect2.fireeye.com/url?k=b1bddc0f-ed69d7a4-b1bd9c94-864b0d136b87-d394ae6811e94bab&q=1&u=https%3A%2F%2Fmail.openvswitch.org%2Fmailman%2Flistinfo%2Fovs-dev
Eelco Chaudron June 4, 2019, 6:35 a.m. UTC | #5
Hi Anju,

Can you rebase this patch set on the latest master branch? If you do 
this I will review/test it.

Cheers,

Eelco



On 4 Jun 2019, at 5:42, Anju Thomas wrote:

> Hi Eelco,
> Apart from Ilya's comments I have not received any comments for v8. I 
> have addressed those comment in v9.
>
> Regards
> Anju
>
> -----Original Message-----
> From: Eelco Chaudron <echaudro@redhat.com>
> Sent: Monday, June 3, 2019 6:23 PM
> To: Anju Thomas <anju.thomas@ericsson.com>
> Cc: dev@openvswitch.org
> Subject: Re: [ovs-dev] [PATCH v9] Improved Packet Drop Statistics in 
> OVS
>
> Hi Anju,
>
> Wondering if you got the email below…
>
> Cheers,
>
> Eelco
>
>
> On 24 May 2019, at 11:46, Eelco Chaudron wrote:
>
>> Hi Anju,
>>
>> Was there ever a follow up on this patch? I only see one response 
>> from
>> Ilya on this asking about his v8 comments.
>>
>> Thanks,
>>
>> Eelco
>>
>>
>> On 27 Feb 2019, at 10:22, 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. Identify and account all the silent packet drop scenarios
>>>
>>> 2. Display these drops in ovs-appctl coverage/show
>>>
>>> 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
>>>
>>> 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 |   1 +
>>>  lib/dpif-netdev.c                                 |  44 ++++-
>>>  lib/dpif.c                                        |   7 +
>>>  lib/dpif.h                                        |   3 +
>>>  lib/odp-execute.c                                 |  81 ++++++++-
>>>  lib/odp-util.c                                    |   9 +
>>>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>>>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>>>  ofproto/ofproto-dpif-xlate.c                      | 103 +++++++----
>>>  ofproto/ofproto-dpif-xlate.h                      |   3 +
>>>  ofproto/ofproto-dpif.c                            |   8 +
>>>  ofproto/ofproto-dpif.h                            |   7 +-
>>>  tests/automake.mk                                 |   3 +-
>>>  tests/dpif-netdev.at                              |   8 +
>>>  tests/drop-stats.at                               | 197
>>> ++++++++++++++++++++++
>>>  tests/ofproto-dpif.at                             |   2 +-
>>>  tests/testsuite.at                                |   1 +
>>>  tests/tunnel-push-pop.at                          |  28 ++-
>>>  tests/tunnel.at                                   |  14 +-
>>>  19 files changed, 475 insertions(+), 46 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 d5aa09d..e77e9c8 100644
>>> --- a/datapath/linux/compat/include/linux/openvswitch.h
>>> +++ b/datapath/linux/compat/include/linux/openvswitch.h
>>> @@ -946,6 +946,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,         /* Drop action. */
>>>
>>>  #ifndef __KERNEL__
>>>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
>>> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index
>>> 77ac1d2..acc7913 100644
>>> --- a/lib/dpif-netdev.c
>>> +++ b/lib/dpif-netdev.c
>>> @@ -101,6 +101,17 @@ 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(datapath_drop_meter);
>>> +COVERAGE_DEFINE(datapath_drop_upcall_error);
>>> +COVERAGE_DEFINE(datapath_drop_lock_error);
>>> +COVERAGE_DEFINE(datapath_drop_userspace_action_error);
>>> +COVERAGE_DEFINE(datapath_drop_tunnel_push_error);
>>> +COVERAGE_DEFINE(datapath_drop_tunnel_pop_error);
>>> +COVERAGE_DEFINE(datapath_drop_recirc_error);
>>> +COVERAGE_DEFINE(datapath_drop_invalid_port);
>>> +COVERAGE_DEFINE(datapath_drop_invalid_tnl_port);
>>> +COVERAGE_DEFINE(datapath_drop_rx_invalid_packet);
>>> +
>>>  /* Protects against changes to 'dp_netdevs'. */  static struct
>>> ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>>>
>>> @@ -5647,6 +5658,7 @@ dp_netdev_run_meter(struct dp_netdev *dp,
>>> struct dp_packet_batch *packets_,
>>>              band->packet_count += 1;
>>>              band->byte_count += dp_packet_size(packet);
>>>
>>> +            COVERAGE_INC(datapath_drop_meter);
>>>              dp_packet_delete(packet);
>>>          } else {
>>>              /* Meter accepts packet. */ @@ -6402,6 +6414,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(datapath_drop_rx_invalid_packet);
>>>              continue;
>>>          }
>>>
>>> @@ -6528,6 +6541,7 @@ handle_packet_upcall(struct
>>> dp_netdev_pmd_thread *pmd,
>>>                               put_actions);
>>>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>>>          dp_packet_delete(packet);
>>> +        COVERAGE_INC(datapath_drop_upcall_error);
>>>          return error;
>>>      }
>>>
>>> @@ -6659,6 +6673,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(datapath_drop_lock_error);
>>>                  upcall_fail_cnt++;
>>>              }
>>>          }
>>> @@ -6928,6 +6943,7 @@ dp_execute_userspace_action(struct
>>> dp_netdev_pmd_thread *pmd,
>>>                                    actions->data, actions->size);
>>>      } else if (should_steal) {
>>>          dp_packet_delete(packet);
>>> +        COVERAGE_INC(datapath_drop_userspace_action_error);
>>>      }
>>>  }
>>>
>>> @@ -6942,6 +6958,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:
>>> @@ -6983,6 +7000,9 @@ dp_execute_cb(void *aux_, struct
>>> dp_packet_batch *packets_,
>>>                  dp_packet_batch_add(&p->output_pkts, packet);
>>>              }
>>>              return;
>>> +        } else {
>>> +            COVERAGE_ADD(datapath_drop_invalid_port,
>>> +                            dp_packet_batch_size(packets_));
>>>          }
>>>          break;
>>>
>>> @@ -6992,10 +7012,16 @@ 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(datapath_drop_tunnel_push_error,
>>> +                            dp_packet_batch_size(packets_));
>>>              break;
>>>          }
>>>          dp_packet_batch_apply_cutlen(packets_);
>>> -        push_tnl_action(pmd, a, packets_);
>>> +        packet_count = dp_packet_batch_size(packets_);
>>> +        if (push_tnl_action(pmd, a, packets_)) {
>>> +            COVERAGE_ADD(datapath_drop_tunnel_push_error,
>>> +                            packet_count);
>>> +        }
>>>          return;
>>>
>>>      case OVS_ACTION_ATTR_TUNNEL_POP:
>>> @@ -7015,7 +7041,13 @@ 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(datapath_drop_tunnel_pop_error,
>>> +                                     packet_dropped);
>>> +                }
>>>                  if (dp_packet_batch_is_empty(packets_)) {
>>>                      return;
>>>                  }
>>> @@ -7030,6 +7062,11 @@ dp_execute_cb(void *aux_, struct
>>> dp_packet_batch *packets_,
>>>                  (*depth)--;
>>>                  return;
>>>              }
>>> +            COVERAGE_ADD(datapath_drop_invalid_tnl_port,
>>> +                            dp_packet_batch_size(packets_));
>>> +        } else {
>>> +            COVERAGE_ADD(datapath_drop_recirc_error,
>>> +                            dp_packet_batch_size(packets_));
>>>          }
>>>          break;
>>>
>>> @@ -7074,6 +7111,8 @@ dp_execute_cb(void *aux_, struct
>>> dp_packet_batch *packets_,
>>>
>>>              return;
>>>          }
>>> +        COVERAGE_ADD(datapath_drop_lock_error,
>>> +                        dp_packet_batch_size(packets_));
>>>          break;
>>>
>>>      case OVS_ACTION_ATTR_RECIRC:
>>> @@ -7097,6 +7136,8 @@ dp_execute_cb(void *aux_, struct
>>> dp_packet_batch *packets_,
>>>              return;
>>>          }
>>>
>>> +        COVERAGE_ADD(datapath_drop_recirc_error,
>>> +                        dp_packet_batch_size(packets_));
>>>          VLOG_WARN("Packet dropped. Max recirculation depth
>>> exceeded.");
>>>          break;
>>>
>>> @@ -7249,6 +7290,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();
>>>      }
>>> diff --git a/lib/dpif.c b/lib/dpif.c
>>> index 457c9bf..d75a012 100644
>>> --- a/lib/dpif.c
>>> +++ b/lib/dpif.c
>>> @@ -1269,6 +1269,7 @@ dpif_execute_helper_cb(void *aux_, struct
>>> dp_packet_batch *packets_,
>>>      case OVS_ACTION_ATTR_POP_NSH:
>>>      case OVS_ACTION_ATTR_CT_CLEAR:
>>>      case OVS_ACTION_ATTR_UNSPEC:
>>> +    case OVS_ACTION_ATTR_DROP:
>>>      case __OVS_ACTION_ATTR_MAX:
>>>          OVS_NOT_REACHED();
>>>      }
>>> @@ -1874,6 +1875,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, 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/odp-execute.c b/lib/odp-execute.c index
>>> 5d07133..c8ff16b 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"
>>> @@ -36,6 +37,74 @@
>>>  #include "util.h"
>>>  #include "csum.h"
>>>  #include "conntrack.h"
>>> +#include "ofproto/ofproto-dpif-xlate.h"
>>> +#include "openvswitch/vlog.h"
>>> +
>>> +VLOG_DEFINE_THIS_MODULE(odp_execute)
>>> +COVERAGE_DEFINE(dp_sample_error_drop);
>>> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
>>> +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);
>>> +
>>> +static void
>>> +dp_update_drop_action_counter(enum xlate_error drop_reason,
>>> +                                 int delta) {
>>> +   static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
>>> +
>>> +   switch (drop_reason) {
>>> +   case XLATE_OK:
>>> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
>>> +        break;
>>> +   case XLATE_BRIDGE_NOT_FOUND:
>>> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
>>> +        break;
>>> +   case XLATE_RECURSION_TOO_DEEP:
>>> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
>>> +        break;
>>> +   case XLATE_TOO_MANY_RESUBMITS:
>>> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
>>> +        break;
>>> +   case XLATE_STACK_TOO_DEEP:
>>> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
>>> +        break;
>>> +   case XLATE_NO_RECIRCULATION_CONTEXT:
>>> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
>>> +        break;
>>> +   case XLATE_RECIRCULATION_CONFLICT:
>>> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
>>> +        break;
>>> +   case XLATE_TOO_MANY_MPLS_LABELS:
>>> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
>>> +        break;
>>> +   case XLATE_INVALID_TUNNEL_METADATA:
>>> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
>>> +        break;
>>> +   case XLATE_UNSUPPORTED_PACKET_TYPE:
>>> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
>>> +        break;
>>> +   case XLATE_CONGESTION_DROP:
>>> +        COVERAGE_ADD(drop_action_congestion, delta);
>>> +        break;
>>> +   case XLATE_FORWARDING_DISABLED:
>>> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
>>> +        break;
>>> +   case XLATE_MAX:
>>> +   default:
>>> +        VLOG_ERR_RL(&rl, "Invalid Drop reason type:%d",
>>> drop_reason);
>>> +   }
>>> +}
>>> +
>>>
>>>  /* Masked copy of an ethernet address. 'src' is already properly
>>> masked. */  static void @@ -621,6 +690,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;
>>> @@ -705,6 +775,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:
>>> @@ -921,6 +992,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);
>>>                  }
>>>              }
>>> @@ -931,7 +1003,14 @@ odp_execute_actions(void *dp, struct
>>> dp_packet_batch *batch, bool steal,
>>>                  conntrack_clear(packet);
>>>              }
>>>              break;
>>> -
>>> +        case OVS_ACTION_ATTR_DROP: {
>>> +            const enum xlate_error *drop_reason = nl_attr_get(a);
>>> +            if (*drop_reason < XLATE_MAX) {
>>> +                 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-util.c b/lib/odp-util.c index e893f46..9c3acc1
>>> 100644
>>> --- a/lib/odp-util.c
>>> +++ b/lib/odp-util.c
>>> @@ -43,6 +43,7 @@
>>>  #include "uuid.h"
>>>  #include "openvswitch/vlog.h"
>>>  #include "openvswitch/match.h"
>>> +#include "ofproto/ofproto-dpif-xlate.h"
>>>
>>>  VLOG_DEFINE_THIS_MODULE(odp_util);
>>>
>>> @@ -131,6 +132,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(enum xlate_error);
>>>
>>>      case OVS_ACTION_ATTR_UNSPEC:
>>>      case __OVS_ACTION_ATTR_MAX:
>>> @@ -1182,6 +1184,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:
>>> +        ds_put_cstr(ds, "drop");
>>> +        break;
>>>      case OVS_ACTION_ATTR_UNSPEC:
>>>      case __OVS_ACTION_ATTR_MAX:
>>>      default:
>>> @@ -2428,8 +2433,12 @@ odp_actions_from_string(const char *s, const
>>> struct simap *port_names,
>>>                          struct ofpbuf *actions)  {
>>>      size_t old_size;
>>> +    enum xlate_error drop_action;
>>>
>>>      if (!strcasecmp(s, "drop")) {
>>> +        drop_action = XLATE_OK;
>>> +        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 5ea1097..52b328d 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 bc4ffee..0d0a27d 100644
>>> --- a/ofproto/ofproto-dpif-sflow.c
>>> +++ b/ofproto/ofproto-dpif-sflow.c
>>> @@ -1223,6 +1223,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-xlate.c
>>> b/ofproto/ofproto-dpif-xlate.c
>>> index acd4817..95e2f25 100644
>>> --- a/ofproto/ofproto-dpif-xlate.c
>>> +++ b/ofproto/ofproto-dpif-xlate.c
>>> @@ -444,6 +444,12 @@ 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";
>>> +    case XLATE_MAX:
>>> +        break;
>>>      }
>>>      return "Unknown error";
>>>  }
>>> @@ -5921,6 +5927,14 @@ put_ct_label(const struct flow *flow, struct
>>> ofpbuf *odp_actions,
>>>  }
>>>
>>>  static void
>>> +put_drop_action(struct ofpbuf *odp_actions, enum xlate_error error)
>>> +{
>>> +    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_DROP,
>>> +                      &error, sizeof error);
>>> +
>>> +}
>>> +
>>> +static void
>>>  put_ct_helper(struct xlate_ctx *ctx,
>>>                struct ofpbuf *odp_actions, struct ofpact_conntrack
>>> *ofc)
>>>  {
>>> @@ -7383,48 +7397,51 @@ xlate_actions(struct xlate_in *xin, struct
>>> xlate_out *xout)
>>>          }
>>>          size_t sample_actions_len = ctx.odp_actions->size;
>>>
>>> -        if (tnl_process_ecn(flow)
>>> -            && (!in_port || may_receive(in_port, &ctx))) {
>>> -            const struct ofpact *ofpacts;
>>> -            size_t ofpacts_len;
>>> -
>>> -            if (xin->ofpacts) {
>>> -                ofpacts = xin->ofpacts;
>>> -                ofpacts_len = xin->ofpacts_len;
>>> -            } else if (ctx.rule) {
>>> -                const struct rule_actions *actions
>>> -                    = rule_get_actions(&ctx.rule->up);
>>> -                ofpacts = actions->ofpacts;
>>> -                ofpacts_len = actions->ofpacts_len;
>>> -                ctx.rule_cookie = ctx.rule->up.flow_cookie;
>>> -            } else {
>>> -                OVS_NOT_REACHED();
>>> -            }
>>> +        if (!tnl_process_ecn(flow)) {
>>> +            ctx.error = XLATE_CONGESTION_DROP;
>>> +        } else {
>>> +            if (!in_port || may_receive(in_port, &ctx)) {
>>> +                const struct ofpact *ofpacts;
>>> +                size_t ofpacts_len;
>>> +
>>> +                if (xin->ofpacts) {
>>> +                    ofpacts = xin->ofpacts;
>>> +                    ofpacts_len = xin->ofpacts_len;
>>> +                } else if (ctx.rule) {
>>> +                    const struct rule_actions *actions
>>> +                         = rule_get_actions(&ctx.rule->up);
>>> +                    ofpacts = actions->ofpacts;
>>> +                    ofpacts_len = actions->ofpacts_len;
>>> +                    ctx.rule_cookie = ctx.rule->up.flow_cookie;
>>> +                } else {
>>> +                    OVS_NOT_REACHED();
>>> +                }
>>>
>>> -            mirror_ingress_packet(&ctx);
>>> -            do_xlate_actions(ofpacts, ofpacts_len, &ctx, true,
>>> false);
>>> -            if (ctx.error) {
>>> -                goto exit;
>>> -            }
>>> +                mirror_ingress_packet(&ctx);
>>> +                do_xlate_actions(ofpacts, ofpacts_len, &ctx, true,
>>> false);
>>> +                if (ctx.error) {
>>> +                    goto exit;
>>> +                }
>>>
>>> -            /* We've let OFPP_NORMAL and the learning action look 
>>> at
>>> the
>>> -             * packet, so cancel all actions and freezing if
>>> forwarding is
>>> -             * disabled. */
>>> -            if (in_port && (!xport_stp_forward_state(in_port) ||
>>> -                            !xport_rstp_forward_state(in_port))) {
>>> -                ctx.odp_actions->size = sample_actions_len;
>>> -                ctx_cancel_freeze(&ctx);
>>> -                ofpbuf_clear(&ctx.action_set);
>>> -            }
>>> +                /* We've let OFPP_NORMAL and the learning action
>>> look at the
>>> +                * packet, so cancel all actions and freezing if
>>> forwarding is
>>> +                * disabled. */
>>> +                if (in_port && (!xport_stp_forward_state(in_port) 
>>> ||
>>> +                                
>>> !xport_rstp_forward_state(in_port)))
>>> {
>>> +                    ctx.odp_actions->size = sample_actions_len;
>>> +                    ctx_cancel_freeze(&ctx);
>>> +                    ofpbuf_clear(&ctx.action_set);
>>> +                    ctx.error = XLATE_FORWARDING_DISABLED;
>>> +                }
>>>
>>> -            if (!ctx.freezing) {
>>> -                xlate_action_set(&ctx);
>>> -            }
>>> -            if (ctx.freezing) {
>>> -                finish_freezing(&ctx);
>>> +                if (!ctx.freezing) {
>>> +                    xlate_action_set(&ctx);
>>> +                }
>>> +                if (ctx.freezing) {
>>> +                    finish_freezing(&ctx);
>>> +                }
>>>              }
>>>          }
>>> -
>>>          /* Output only fully processed packets. */
>>>          if (!ctx.freezing
>>>              && xbridge->has_in_band
>>> @@ -7522,6 +7539,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..496bdbf 100644
>>> --- a/ofproto/ofproto-dpif-xlate.h
>>> +++ b/ofproto/ofproto-dpif-xlate.h
>>> @@ -216,6 +216,9 @@ enum xlate_error {
>>>      XLATE_TOO_MANY_MPLS_LABELS,
>>>      XLATE_INVALID_TUNNEL_METADATA,
>>>      XLATE_UNSUPPORTED_PACKET_TYPE,
>>> +    XLATE_CONGESTION_DROP,
>>> +    XLATE_FORWARDING_DISABLED,
>>> +    XLATE_MAX,
>>>  };
>>>
>>>  const char *xlate_strerror(enum xlate_error error);
>>> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
>>> index 50c959b..91df1b3 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);
>>> diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
>>> index 1a404c8..8d8edca 100644
>>> --- a/ofproto/ofproto-dpif.h
>>> +++ b/ofproto/ofproto-dpif.h
>>> @@ -192,7 +192,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 +364,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..a4da75e 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/dpif-netdev.at b/tests/dpif-netdev.at
>>> index 6915d43..5221c10 100644
>>> --- a/tests/dpif-netdev.at
>>> +++ b/tests/dpif-netdev.at
>>> @@ -337,6 +337,14 @@ 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
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "datapath_drop_meter" | 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..318bc02
>>> --- /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
>>> +])
>>> +
>>> +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..2c8cdce 100644
>>> --- a/tests/ofproto-dpif.at
>>> +++ b/tests/ofproto-dpif.at
>>> @@ -9384,7 +9384,7 @@
>>> recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,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
>>>  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
>>>  ])
>>> 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..045b1a7 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 5000
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "datapath_drop_tunnel_pop_error" |
>>> cut -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +1
>>> +])
>>> +
>>> +sleep 1
>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>> +
>>> +ovs-appctl time/warp 5000
>>> +
>>> +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
>>> @@ -478,7 +499,7 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep
>>> 'port  5'], [0], [dnl
>>>    port  5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?,
>>> crc=?
>>>  ])
>>>  AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 
>>> 'in_port(6081)'],
>>> [0], [dnl
>>> -tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no),
>>> packets:0, bytes:0, used:never,
>>> actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=3,rule_cookie=0,controller_id=0,max_len=65535))
>>> +tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no),
>>> packets:0, bytes:0, used:never,
>>> actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=2,rule_cookie=0,controller_id=0,max_len=65535))
>>>  ])
>>>
>>>  ovs-appctl time/warp 10000
>>> @@ -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..7bd5b48 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
>>> +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,16 @@ 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 "datapath_drop_invalid_port" | cut
>>> -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +1
>>> +])
>>>  OVS_VSWITCHD_STOP
>>>  AT_CLEANUP
>>>
>>> -- 
>>> 1.9.1
>>>
>>> _______________________________________________
>>> dev mailing list
>>> dev@openvswitch.org
>>> https://protect2.fireeye.com/url?k=b1bddc0f-ed69d7a4-b1bd9c94-864b0d136b87-d394ae6811e94bab&q=1&u=https%3A%2F%2Fmail.openvswitch.org%2Fmailman%2Flistinfo%2Fovs-dev
Ilya Maximets June 4, 2019, 7:03 a.m. UTC | #6
> Hi Eelco,
> Apart from Ilya's comments I have not received any comments for v8. I have addressed those comment in v9.

Hi Anju.
I wrote about my comments to v8 just because you didn't reply or address a bunch
of them in v9. There was comments about using dp_packet_batch_size(), renaming
suggestion for sample and nsh drop counters, suggested ecn handling code snippet,
maybe something else.

Best regards, Ilya Maximets.

> 
> Regards
> Anju
> 
> -----Original Message-----
> From: Eelco Chaudron <echaudro at redhat.com> 
> Sent: Monday, June 3, 2019 6:23 PM
> To: Anju Thomas <anju.thomas at ericsson.com>
> Cc: dev at openvswitch.org
> Subject: Re: [ovs-dev] [PATCH v9] Improved Packet Drop Statistics in OVS
> 
> Hi Anju,
> 
> Wondering if you got the email below…
> 
> Cheers,
> 
> Eelco
> 
> 
> On 24 May 2019, at 11:46, Eelco Chaudron wrote:
> 
>> Hi Anju,
>>
>> Was there ever a follow up on this patch? I only see one response from 
>> Ilya on this asking about his v8 comments.
>>
>> Thanks,
>>
>> Eelco
>>
>>
>> On 27 Feb 2019, at 10:22, 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. Identify and account all the silent packet drop scenarios
>>>
>>> 2. Display these drops in ovs-appctl coverage/show
>>>
>>> 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
>>>
>>> Co-authored-by: Rohith Basavaraja <rohith.basavaraja at gmail.com>
>>> Co-authored-by: Keshav Gupta <keshugupta1 at gmail.com>
>>> Signed-off-by: Anju Thomas <anju.thomas at ericsson.com>
>>> Signed-off-by: Rohith Basavaraja <rohith.basavaraja at gmail.com>
>>> Signed-off-by: Keshav Gupta <keshugupta1 at gmail.com>
>>> ---
>>>  datapath/linux/compat/include/linux/openvswitch.h |   1 +
>>>  lib/dpif-netdev.c                                 |  44 ++++-
>>>  lib/dpif.c                                        |   7 +
>>>  lib/dpif.h                                        |   3 +
>>>  lib/odp-execute.c                                 |  81 ++++++++-
>>>  lib/odp-util.c                                    |   9 +
>>>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>>>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>>>  ofproto/ofproto-dpif-xlate.c                      | 103 +++++++----
>>>  ofproto/ofproto-dpif-xlate.h                      |   3 +
>>>  ofproto/ofproto-dpif.c                            |   8 +
>>>  ofproto/ofproto-dpif.h                            |   7 +-
>>>  tests/automake.mk                                 |   3 +-
>>>  tests/dpif-netdev.at                              |   8 +
>>>  tests/drop-stats.at                               | 197 
>>> ++++++++++++++++++++++
>>>  tests/ofproto-dpif.at                             |   2 +-
>>>  tests/testsuite.at                                |   1 +
>>>  tests/tunnel-push-pop.at                          |  28 ++-
>>>  tests/tunnel.at                                   |  14 +-
>>>  19 files changed, 475 insertions(+), 46 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 d5aa09d..e77e9c8 100644
>>> --- a/datapath/linux/compat/include/linux/openvswitch.h
>>> +++ b/datapath/linux/compat/include/linux/openvswitch.h
>>> @@ -946,6 +946,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,         /* Drop action. */
>>>
>>>  #ifndef __KERNEL__
>>>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
>>> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 
>>> 77ac1d2..acc7913 100644
>>> --- a/lib/dpif-netdev.c
>>> +++ b/lib/dpif-netdev.c
>>> @@ -101,6 +101,17 @@ 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(datapath_drop_meter);
>>> +COVERAGE_DEFINE(datapath_drop_upcall_error);
>>> +COVERAGE_DEFINE(datapath_drop_lock_error);
>>> +COVERAGE_DEFINE(datapath_drop_userspace_action_error);
>>> +COVERAGE_DEFINE(datapath_drop_tunnel_push_error);
>>> +COVERAGE_DEFINE(datapath_drop_tunnel_pop_error);
>>> +COVERAGE_DEFINE(datapath_drop_recirc_error);
>>> +COVERAGE_DEFINE(datapath_drop_invalid_port);
>>> +COVERAGE_DEFINE(datapath_drop_invalid_tnl_port);
>>> +COVERAGE_DEFINE(datapath_drop_rx_invalid_packet);
>>> +
>>>  /* Protects against changes to 'dp_netdevs'. */  static struct 
>>> ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>>>
>>> @@ -5647,6 +5658,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, 
>>> struct dp_packet_batch *packets_,
>>>              band->packet_count += 1;
>>>              band->byte_count += dp_packet_size(packet);
>>>
>>> +            COVERAGE_INC(datapath_drop_meter);
>>>              dp_packet_delete(packet);
>>>          } else {
>>>              /* Meter accepts packet. */ @@ -6402,6 +6414,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(datapath_drop_rx_invalid_packet);
>>>              continue;
>>>          }
>>>
>>> @@ -6528,6 +6541,7 @@ handle_packet_upcall(struct 
>>> dp_netdev_pmd_thread *pmd,
>>>                               put_actions);
>>>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>>>          dp_packet_delete(packet);
>>> +        COVERAGE_INC(datapath_drop_upcall_error);
>>>          return error;
>>>      }
>>>
>>> @@ -6659,6 +6673,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(datapath_drop_lock_error);
>>>                  upcall_fail_cnt++;
>>>              }
>>>          }
>>> @@ -6928,6 +6943,7 @@ dp_execute_userspace_action(struct
>>> dp_netdev_pmd_thread *pmd,
>>>                                    actions->data, actions->size);
>>>      } else if (should_steal) {
>>>          dp_packet_delete(packet);
>>> +        COVERAGE_INC(datapath_drop_userspace_action_error);
>>>      }
>>>  }
>>>
>>> @@ -6942,6 +6958,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:
>>> @@ -6983,6 +7000,9 @@ dp_execute_cb(void *aux_, struct 
>>> dp_packet_batch *packets_,
>>>                  dp_packet_batch_add(&p->output_pkts, packet);
>>>              }
>>>              return;
>>> +        } else {
>>> +            COVERAGE_ADD(datapath_drop_invalid_port,
>>> +                            dp_packet_batch_size(packets_));
>>>          }
>>>          break;
>>>
>>> @@ -6992,10 +7012,16 @@ 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(datapath_drop_tunnel_push_error,
>>> +                            dp_packet_batch_size(packets_));
>>>              break;
>>>          }
>>>          dp_packet_batch_apply_cutlen(packets_);
>>> -        push_tnl_action(pmd, a, packets_);
>>> +        packet_count = dp_packet_batch_size(packets_);
>>> +        if (push_tnl_action(pmd, a, packets_)) {
>>> +            COVERAGE_ADD(datapath_drop_tunnel_push_error,
>>> +                            packet_count);
>>> +        }
>>>          return;
>>>
>>>      case OVS_ACTION_ATTR_TUNNEL_POP:
>>> @@ -7015,7 +7041,13 @@ 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(datapath_drop_tunnel_pop_error,
>>> +                                     packet_dropped);
>>> +                }
>>>                  if (dp_packet_batch_is_empty(packets_)) {
>>>                      return;
>>>                  }
>>> @@ -7030,6 +7062,11 @@ dp_execute_cb(void *aux_, struct 
>>> dp_packet_batch *packets_,
>>>                  (*depth)--;
>>>                  return;
>>>              }
>>> +            COVERAGE_ADD(datapath_drop_invalid_tnl_port,
>>> +                            dp_packet_batch_size(packets_));
>>> +        } else {
>>> +            COVERAGE_ADD(datapath_drop_recirc_error,
>>> +                            dp_packet_batch_size(packets_));
>>>          }
>>>          break;
>>>
>>> @@ -7074,6 +7111,8 @@ dp_execute_cb(void *aux_, struct 
>>> dp_packet_batch *packets_,
>>>
>>>              return;
>>>          }
>>> +        COVERAGE_ADD(datapath_drop_lock_error,
>>> +                        dp_packet_batch_size(packets_));
>>>          break;
>>>
>>>      case OVS_ACTION_ATTR_RECIRC:
>>> @@ -7097,6 +7136,8 @@ dp_execute_cb(void *aux_, struct 
>>> dp_packet_batch *packets_,
>>>              return;
>>>          }
>>>
>>> +        COVERAGE_ADD(datapath_drop_recirc_error,
>>> +                        dp_packet_batch_size(packets_));
>>>          VLOG_WARN("Packet dropped. Max recirculation depth 
>>> exceeded.");
>>>          break;
>>>
>>> @@ -7249,6 +7290,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();
>>>      }
>>> diff --git a/lib/dpif.c b/lib/dpif.c
>>> index 457c9bf..d75a012 100644
>>> --- a/lib/dpif.c
>>> +++ b/lib/dpif.c
>>> @@ -1269,6 +1269,7 @@ dpif_execute_helper_cb(void *aux_, struct 
>>> dp_packet_batch *packets_,
>>>      case OVS_ACTION_ATTR_POP_NSH:
>>>      case OVS_ACTION_ATTR_CT_CLEAR:
>>>      case OVS_ACTION_ATTR_UNSPEC:
>>> +    case OVS_ACTION_ATTR_DROP:
>>>      case __OVS_ACTION_ATTR_MAX:
>>>          OVS_NOT_REACHED();
>>>      }
>>> @@ -1874,6 +1875,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, 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/odp-execute.c b/lib/odp-execute.c index 
>>> 5d07133..c8ff16b 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"
>>> @@ -36,6 +37,74 @@
>>>  #include "util.h"
>>>  #include "csum.h"
>>>  #include "conntrack.h"
>>> +#include "ofproto/ofproto-dpif-xlate.h"
>>> +#include "openvswitch/vlog.h"
>>> +
>>> +VLOG_DEFINE_THIS_MODULE(odp_execute)
>>> +COVERAGE_DEFINE(dp_sample_error_drop);
>>> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
>>> +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);
>>> +
>>> +static void
>>> +dp_update_drop_action_counter(enum xlate_error drop_reason,
>>> +                                 int delta) {
>>> +   static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
>>> +
>>> +   switch (drop_reason) {
>>> +   case XLATE_OK:
>>> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
>>> +        break;
>>> +   case XLATE_BRIDGE_NOT_FOUND:
>>> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
>>> +        break;
>>> +   case XLATE_RECURSION_TOO_DEEP:
>>> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
>>> +        break;
>>> +   case XLATE_TOO_MANY_RESUBMITS:
>>> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
>>> +        break;
>>> +   case XLATE_STACK_TOO_DEEP:
>>> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
>>> +        break;
>>> +   case XLATE_NO_RECIRCULATION_CONTEXT:
>>> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
>>> +        break;
>>> +   case XLATE_RECIRCULATION_CONFLICT:
>>> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
>>> +        break;
>>> +   case XLATE_TOO_MANY_MPLS_LABELS:
>>> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
>>> +        break;
>>> +   case XLATE_INVALID_TUNNEL_METADATA:
>>> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
>>> +        break;
>>> +   case XLATE_UNSUPPORTED_PACKET_TYPE:
>>> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
>>> +        break;
>>> +   case XLATE_CONGESTION_DROP:
>>> +        COVERAGE_ADD(drop_action_congestion, delta);
>>> +        break;
>>> +   case XLATE_FORWARDING_DISABLED:
>>> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
>>> +        break;
>>> +   case XLATE_MAX:
>>> +   default:
>>> +        VLOG_ERR_RL(&rl, "Invalid Drop reason type:%d",
>>> drop_reason);
>>> +   }
>>> +}
>>> +
>>>
>>>  /* Masked copy of an ethernet address. 'src' is already properly 
>>> masked. */  static void @@ -621,6 +690,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;
>>> @@ -705,6 +775,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:
>>> @@ -921,6 +992,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);
>>>                  }
>>>              }
>>> @@ -931,7 +1003,14 @@ odp_execute_actions(void *dp, struct 
>>> dp_packet_batch *batch, bool steal,
>>>                  conntrack_clear(packet);
>>>              }
>>>              break;
>>> -
>>> +        case OVS_ACTION_ATTR_DROP: {
>>> +            const enum xlate_error *drop_reason = nl_attr_get(a);
>>> +            if (*drop_reason < XLATE_MAX) {
>>> +                 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-util.c b/lib/odp-util.c index e893f46..9c3acc1 
>>> 100644
>>> --- a/lib/odp-util.c
>>> +++ b/lib/odp-util.c
>>> @@ -43,6 +43,7 @@
>>>  #include "uuid.h"
>>>  #include "openvswitch/vlog.h"
>>>  #include "openvswitch/match.h"
>>> +#include "ofproto/ofproto-dpif-xlate.h"
>>>
>>>  VLOG_DEFINE_THIS_MODULE(odp_util);
>>>
>>> @@ -131,6 +132,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(enum xlate_error);
>>>
>>>      case OVS_ACTION_ATTR_UNSPEC:
>>>      case __OVS_ACTION_ATTR_MAX:
>>> @@ -1182,6 +1184,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:
>>> +        ds_put_cstr(ds, "drop");
>>> +        break;
>>>      case OVS_ACTION_ATTR_UNSPEC:
>>>      case __OVS_ACTION_ATTR_MAX:
>>>      default:
>>> @@ -2428,8 +2433,12 @@ odp_actions_from_string(const char *s, const 
>>> struct simap *port_names,
>>>                          struct ofpbuf *actions)  {
>>>      size_t old_size;
>>> +    enum xlate_error drop_action;
>>>
>>>      if (!strcasecmp(s, "drop")) {
>>> +        drop_action = XLATE_OK;
>>> +        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 5ea1097..52b328d 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 bc4ffee..0d0a27d 100644
>>> --- a/ofproto/ofproto-dpif-sflow.c
>>> +++ b/ofproto/ofproto-dpif-sflow.c
>>> @@ -1223,6 +1223,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-xlate.c 
>>> b/ofproto/ofproto-dpif-xlate.c
>>> index acd4817..95e2f25 100644
>>> --- a/ofproto/ofproto-dpif-xlate.c
>>> +++ b/ofproto/ofproto-dpif-xlate.c
>>> @@ -444,6 +444,12 @@ 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";
>>> +    case XLATE_MAX:
>>> +        break;
>>>      }
>>>      return "Unknown error";
>>>  }
>>> @@ -5921,6 +5927,14 @@ put_ct_label(const struct flow *flow, struct 
>>> ofpbuf *odp_actions,
>>>  }
>>>
>>>  static void
>>> +put_drop_action(struct ofpbuf *odp_actions, enum xlate_error error)
>>> +{
>>> +    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_DROP,
>>> +                      &error, sizeof error);
>>> +
>>> +}
>>> +
>>> +static void
>>>  put_ct_helper(struct xlate_ctx *ctx,
>>>                struct ofpbuf *odp_actions, struct ofpact_conntrack 
>>> *ofc)
>>>  {
>>> @@ -7383,48 +7397,51 @@ xlate_actions(struct xlate_in *xin, struct 
>>> xlate_out *xout)
>>>          }
>>>          size_t sample_actions_len = ctx.odp_actions->size;
>>>
>>> -        if (tnl_process_ecn(flow)
>>> -            && (!in_port || may_receive(in_port, &ctx))) {
>>> -            const struct ofpact *ofpacts;
>>> -            size_t ofpacts_len;
>>> -
>>> -            if (xin->ofpacts) {
>>> -                ofpacts = xin->ofpacts;
>>> -                ofpacts_len = xin->ofpacts_len;
>>> -            } else if (ctx.rule) {
>>> -                const struct rule_actions *actions
>>> -                    = rule_get_actions(&ctx.rule->up);
>>> -                ofpacts = actions->ofpacts;
>>> -                ofpacts_len = actions->ofpacts_len;
>>> -                ctx.rule_cookie = ctx.rule->up.flow_cookie;
>>> -            } else {
>>> -                OVS_NOT_REACHED();
>>> -            }
>>> +        if (!tnl_process_ecn(flow)) {
>>> +            ctx.error = XLATE_CONGESTION_DROP;
>>> +        } else {
>>> +            if (!in_port || may_receive(in_port, &ctx)) {
>>> +                const struct ofpact *ofpacts;
>>> +                size_t ofpacts_len;
>>> +
>>> +                if (xin->ofpacts) {
>>> +                    ofpacts = xin->ofpacts;
>>> +                    ofpacts_len = xin->ofpacts_len;
>>> +                } else if (ctx.rule) {
>>> +                    const struct rule_actions *actions
>>> +                         = rule_get_actions(&ctx.rule->up);
>>> +                    ofpacts = actions->ofpacts;
>>> +                    ofpacts_len = actions->ofpacts_len;
>>> +                    ctx.rule_cookie = ctx.rule->up.flow_cookie;
>>> +                } else {
>>> +                    OVS_NOT_REACHED();
>>> +                }
>>>
>>> -            mirror_ingress_packet(&ctx);
>>> -            do_xlate_actions(ofpacts, ofpacts_len, &ctx, true, 
>>> false);
>>> -            if (ctx.error) {
>>> -                goto exit;
>>> -            }
>>> +                mirror_ingress_packet(&ctx);
>>> +                do_xlate_actions(ofpacts, ofpacts_len, &ctx, true, 
>>> false);
>>> +                if (ctx.error) {
>>> +                    goto exit;
>>> +                }
>>>
>>> -            /* We've let OFPP_NORMAL and the learning action look at 
>>> the
>>> -             * packet, so cancel all actions and freezing if 
>>> forwarding is
>>> -             * disabled. */
>>> -            if (in_port && (!xport_stp_forward_state(in_port) ||
>>> -                            !xport_rstp_forward_state(in_port))) {
>>> -                ctx.odp_actions->size = sample_actions_len;
>>> -                ctx_cancel_freeze(&ctx);
>>> -                ofpbuf_clear(&ctx.action_set);
>>> -            }
>>> +                /* We've let OFPP_NORMAL and the learning action 
>>> look at the
>>> +                * packet, so cancel all actions and freezing if 
>>> forwarding is
>>> +                * disabled. */
>>> +                if (in_port && (!xport_stp_forward_state(in_port) ||
>>> +                                !xport_rstp_forward_state(in_port))) 
>>> {
>>> +                    ctx.odp_actions->size = sample_actions_len;
>>> +                    ctx_cancel_freeze(&ctx);
>>> +                    ofpbuf_clear(&ctx.action_set);
>>> +                    ctx.error = XLATE_FORWARDING_DISABLED;
>>> +                }
>>>
>>> -            if (!ctx.freezing) {
>>> -                xlate_action_set(&ctx);
>>> -            }
>>> -            if (ctx.freezing) {
>>> -                finish_freezing(&ctx);
>>> +                if (!ctx.freezing) {
>>> +                    xlate_action_set(&ctx);
>>> +                }
>>> +                if (ctx.freezing) {
>>> +                    finish_freezing(&ctx);
>>> +                }
>>>              }
>>>          }
>>> -
>>>          /* Output only fully processed packets. */
>>>          if (!ctx.freezing
>>>              && xbridge->has_in_band
>>> @@ -7522,6 +7539,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..496bdbf 100644
>>> --- a/ofproto/ofproto-dpif-xlate.h
>>> +++ b/ofproto/ofproto-dpif-xlate.h
>>> @@ -216,6 +216,9 @@ enum xlate_error {
>>>      XLATE_TOO_MANY_MPLS_LABELS,
>>>      XLATE_INVALID_TUNNEL_METADATA,
>>>      XLATE_UNSUPPORTED_PACKET_TYPE,
>>> +    XLATE_CONGESTION_DROP,
>>> +    XLATE_FORWARDING_DISABLED,
>>> +    XLATE_MAX,
>>>  };
>>>
>>>  const char *xlate_strerror(enum xlate_error error);
>>> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
>>> index 50c959b..91df1b3 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);
>>> diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
>>> index 1a404c8..8d8edca 100644
>>> --- a/ofproto/ofproto-dpif.h
>>> +++ b/ofproto/ofproto-dpif.h
>>> @@ -192,7 +192,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 +364,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..a4da75e 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/dpif-netdev.at b/tests/dpif-netdev.at
>>> index 6915d43..5221c10 100644
>>> --- a/tests/dpif-netdev.at
>>> +++ b/tests/dpif-netdev.at
>>> @@ -337,6 +337,14 @@ 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
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "datapath_drop_meter" | 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..318bc02
>>> --- /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
>>> +])
>>> +
>>> +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..2c8cdce 100644
>>> --- a/tests/ofproto-dpif.at
>>> +++ b/tests/ofproto-dpif.at
>>> @@ -9384,7 +9384,7 @@ 
>>> recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=
>>>  # are wildcarded.
>>>  AT_CHECK([grep '\(modify\)\|\(flow_add\)' ovs-vswitchd.log | 
>>> strip_ufid ], [0], [dnl
>>>  dpif_netdev|DBG|flow_add: 
>>> recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x1234), 
>>> actions:100
>>> -dpif|DBG|dummy at ovs-dummy: put[[modify]] 
>>> skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234)
>>> +dpif|DBG|dummy at ovs-dummy: put[[modify]] 
>>> skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234), 
>>> actions:drop
>>>  dpif|DBG|dummy at ovs-dummy: put[[modify]] 
>>> skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0/0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(ns=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x1234), 
>>> actions:100
>>>  dpif_netdev|DBG|flow_add: 
>>> recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,pcp=7/0x0),encap(eth_type(0x1234)), 
>>> actions:drop
>>>  ])
>>> 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..045b1a7 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 5000
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "datapath_drop_tunnel_pop_error" | 
>>> cut -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +1
>>> +])
>>> +
>>> +sleep 1
>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 
>>> 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>> +
>>> +ovs-appctl time/warp 5000
>>> +
>>> +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
>>> @@ -478,7 +499,7 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 
>>> 'port  5'], [0], [dnl
>>>    port  5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, 
>>> crc=?
>>>  ])
>>>  AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 'in_port(6081)'], 
>>> [0], [dnl
>>> -tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), 
>>> packets:0, bytes:0, used:never, 
>>> actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=3,rule_cookie=0,controller_id=0,max_len=65535))
>>> +tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), 
>>> packets:0, bytes:0, used:never, 
>>> actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=2,rule_cookie=0,controller_id=0,max_len=65535))
>>>  ])
>>>
>>>  ovs-appctl time/warp 10000
>>> @@ -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..7bd5b48 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
>>> +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,16 @@ 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 "datapath_drop_invalid_port" | cut 
>>> -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +1
>>> +])
>>>  OVS_VSWITCHD_STOP
>>>  AT_CLEANUP
>>>
>>> -- 
>>> 1.9.1
>>>
>>> _______________________________________________
>>> dev mailing list
>>> dev at openvswitch.org
>>> https://protect2.fireeye.com/url?k=b1bddc0f-ed69d7a4-b1bd9c94-864b0d136b87-d394ae6811e94bab&q=1&u=https%3A%2F%2Fmail.openvswitch.org%2Fmailman%2Flistinfo%2Fovs-dev
Anju Thomas June 6, 2019, 2:28 p.m. UTC | #7
Hi Ilya,
Yes, I have addressed those comments in v9.

Regards  & Thanks
Anju

-----Original Message-----
From: Ilya Maximets <i.maximets@samsung.com> 
Sent: Tuesday, June 4, 2019 12:33 PM
To: ovs-dev@openvswitch.org; Anju Thomas <anju.thomas@ericsson.com>
Cc: Eelco Chaudron <echaudro@redhat.com>
Subject: Re: Re: [ovs-dev] [PATCH v9] Improved Packet Drop Statistics in OVS

> Hi Eelco,
> Apart from Ilya's comments I have not received any comments for v8. I have addressed those comment in v9.

Hi Anju.
I wrote about my comments to v8 just because you didn't reply or address a bunch of them in v9. There was comments about using dp_packet_batch_size(), renaming suggestion for sample and nsh drop counters, suggested ecn handling code snippet, maybe something else.

Best regards, Ilya Maximets.

> 
> Regards
> Anju
> 
> -----Original Message-----
> From: Eelco Chaudron <echaudro at redhat.com>
> Sent: Monday, June 3, 2019 6:23 PM
> To: Anju Thomas <anju.thomas at ericsson.com>
> Cc: dev at openvswitch.org
> Subject: Re: [ovs-dev] [PATCH v9] Improved Packet Drop Statistics in 
> OVS
> 
> Hi Anju,
> 
> Wondering if you got the email below…
> 
> Cheers,
> 
> Eelco
> 
> 
> On 24 May 2019, at 11:46, Eelco Chaudron wrote:
> 
>> Hi Anju,
>>
>> Was there ever a follow up on this patch? I only see one response 
>> from Ilya on this asking about his v8 comments.
>>
>> Thanks,
>>
>> Eelco
>>
>>
>> On 27 Feb 2019, at 10:22, 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. Identify and account all the silent packet drop scenarios
>>>
>>> 2. Display these drops in ovs-appctl coverage/show
>>>
>>> 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
>>>
>>> Co-authored-by: Rohith Basavaraja <rohith.basavaraja at gmail.com>
>>> Co-authored-by: Keshav Gupta <keshugupta1 at gmail.com>
>>> Signed-off-by: Anju Thomas <anju.thomas at ericsson.com>
>>> Signed-off-by: Rohith Basavaraja <rohith.basavaraja at gmail.com>
>>> Signed-off-by: Keshav Gupta <keshugupta1 at gmail.com>
>>> ---
>>>  datapath/linux/compat/include/linux/openvswitch.h |   1 +
>>>  lib/dpif-netdev.c                                 |  44 ++++-
>>>  lib/dpif.c                                        |   7 +
>>>  lib/dpif.h                                        |   3 +
>>>  lib/odp-execute.c                                 |  81 ++++++++-
>>>  lib/odp-util.c                                    |   9 +
>>>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>>>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>>>  ofproto/ofproto-dpif-xlate.c                      | 103 +++++++----
>>>  ofproto/ofproto-dpif-xlate.h                      |   3 +
>>>  ofproto/ofproto-dpif.c                            |   8 +
>>>  ofproto/ofproto-dpif.h                            |   7 +-
>>>  tests/automake.mk                                 |   3 +-
>>>  tests/dpif-netdev.at                              |   8 +
>>>  tests/drop-stats.at                               | 197 
>>> ++++++++++++++++++++++
>>>  tests/ofproto-dpif.at                             |   2 +-
>>>  tests/testsuite.at                                |   1 +
>>>  tests/tunnel-push-pop.at                          |  28 ++-
>>>  tests/tunnel.at                                   |  14 +-
>>>  19 files changed, 475 insertions(+), 46 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 d5aa09d..e77e9c8 100644
>>> --- a/datapath/linux/compat/include/linux/openvswitch.h
>>> +++ b/datapath/linux/compat/include/linux/openvswitch.h
>>> @@ -946,6 +946,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,         /* Drop action. */
>>>
>>>  #ifndef __KERNEL__
>>>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
>>> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index
>>> 77ac1d2..acc7913 100644
>>> --- a/lib/dpif-netdev.c
>>> +++ b/lib/dpif-netdev.c
>>> @@ -101,6 +101,17 @@ 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(datapath_drop_meter);
>>> +COVERAGE_DEFINE(datapath_drop_upcall_error);
>>> +COVERAGE_DEFINE(datapath_drop_lock_error);
>>> +COVERAGE_DEFINE(datapath_drop_userspace_action_error);
>>> +COVERAGE_DEFINE(datapath_drop_tunnel_push_error);
>>> +COVERAGE_DEFINE(datapath_drop_tunnel_pop_error);
>>> +COVERAGE_DEFINE(datapath_drop_recirc_error);
>>> +COVERAGE_DEFINE(datapath_drop_invalid_port);
>>> +COVERAGE_DEFINE(datapath_drop_invalid_tnl_port);
>>> +COVERAGE_DEFINE(datapath_drop_rx_invalid_packet);
>>> +
>>>  /* Protects against changes to 'dp_netdevs'. */  static struct 
>>> ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>>>
>>> @@ -5647,6 +5658,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, 
>>> struct dp_packet_batch *packets_,
>>>              band->packet_count += 1;
>>>              band->byte_count += dp_packet_size(packet);
>>>
>>> +            COVERAGE_INC(datapath_drop_meter);
>>>              dp_packet_delete(packet);
>>>          } else {
>>>              /* Meter accepts packet. */ @@ -6402,6 +6414,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(datapath_drop_rx_invalid_packet);
>>>              continue;
>>>          }
>>>
>>> @@ -6528,6 +6541,7 @@ handle_packet_upcall(struct 
>>> dp_netdev_pmd_thread *pmd,
>>>                               put_actions);
>>>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>>>          dp_packet_delete(packet);
>>> +        COVERAGE_INC(datapath_drop_upcall_error);
>>>          return error;
>>>      }
>>>
>>> @@ -6659,6 +6673,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(datapath_drop_lock_error);
>>>                  upcall_fail_cnt++;
>>>              }
>>>          }
>>> @@ -6928,6 +6943,7 @@ dp_execute_userspace_action(struct
>>> dp_netdev_pmd_thread *pmd,
>>>                                    actions->data, actions->size);
>>>      } else if (should_steal) {
>>>          dp_packet_delete(packet);
>>> +        COVERAGE_INC(datapath_drop_userspace_action_error);
>>>      }
>>>  }
>>>
>>> @@ -6942,6 +6958,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:
>>> @@ -6983,6 +7000,9 @@ dp_execute_cb(void *aux_, struct 
>>> dp_packet_batch *packets_,
>>>                  dp_packet_batch_add(&p->output_pkts, packet);
>>>              }
>>>              return;
>>> +        } else {
>>> +            COVERAGE_ADD(datapath_drop_invalid_port,
>>> +                            dp_packet_batch_size(packets_));
>>>          }
>>>          break;
>>>
>>> @@ -6992,10 +7012,16 @@ 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(datapath_drop_tunnel_push_error,
>>> +                            dp_packet_batch_size(packets_));
>>>              break;
>>>          }
>>>          dp_packet_batch_apply_cutlen(packets_);
>>> -        push_tnl_action(pmd, a, packets_);
>>> +        packet_count = dp_packet_batch_size(packets_);
>>> +        if (push_tnl_action(pmd, a, packets_)) {
>>> +            COVERAGE_ADD(datapath_drop_tunnel_push_error,
>>> +                            packet_count);
>>> +        }
>>>          return;
>>>
>>>      case OVS_ACTION_ATTR_TUNNEL_POP:
>>> @@ -7015,7 +7041,13 @@ 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(datapath_drop_tunnel_pop_error,
>>> +                                     packet_dropped);
>>> +                }
>>>                  if (dp_packet_batch_is_empty(packets_)) {
>>>                      return;
>>>                  }
>>> @@ -7030,6 +7062,11 @@ dp_execute_cb(void *aux_, struct 
>>> dp_packet_batch *packets_,
>>>                  (*depth)--;
>>>                  return;
>>>              }
>>> +            COVERAGE_ADD(datapath_drop_invalid_tnl_port,
>>> +                            dp_packet_batch_size(packets_));
>>> +        } else {
>>> +            COVERAGE_ADD(datapath_drop_recirc_error,
>>> +                            dp_packet_batch_size(packets_));
>>>          }
>>>          break;
>>>
>>> @@ -7074,6 +7111,8 @@ dp_execute_cb(void *aux_, struct 
>>> dp_packet_batch *packets_,
>>>
>>>              return;
>>>          }
>>> +        COVERAGE_ADD(datapath_drop_lock_error,
>>> +                        dp_packet_batch_size(packets_));
>>>          break;
>>>
>>>      case OVS_ACTION_ATTR_RECIRC:
>>> @@ -7097,6 +7136,8 @@ dp_execute_cb(void *aux_, struct 
>>> dp_packet_batch *packets_,
>>>              return;
>>>          }
>>>
>>> +        COVERAGE_ADD(datapath_drop_recirc_error,
>>> +                        dp_packet_batch_size(packets_));
>>>          VLOG_WARN("Packet dropped. Max recirculation depth 
>>> exceeded.");
>>>          break;
>>>
>>> @@ -7249,6 +7290,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();
>>>      }
>>> diff --git a/lib/dpif.c b/lib/dpif.c index 457c9bf..d75a012 100644
>>> --- a/lib/dpif.c
>>> +++ b/lib/dpif.c
>>> @@ -1269,6 +1269,7 @@ dpif_execute_helper_cb(void *aux_, struct 
>>> dp_packet_batch *packets_,
>>>      case OVS_ACTION_ATTR_POP_NSH:
>>>      case OVS_ACTION_ATTR_CT_CLEAR:
>>>      case OVS_ACTION_ATTR_UNSPEC:
>>> +    case OVS_ACTION_ATTR_DROP:
>>>      case __OVS_ACTION_ATTR_MAX:
>>>          OVS_NOT_REACHED();
>>>      }
>>> @@ -1874,6 +1875,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, 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/odp-execute.c b/lib/odp-execute.c index 
>>> 5d07133..c8ff16b 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"
>>> @@ -36,6 +37,74 @@
>>>  #include "util.h"
>>>  #include "csum.h"
>>>  #include "conntrack.h"
>>> +#include "ofproto/ofproto-dpif-xlate.h"
>>> +#include "openvswitch/vlog.h"
>>> +
>>> +VLOG_DEFINE_THIS_MODULE(odp_execute)
>>> +COVERAGE_DEFINE(dp_sample_error_drop);
>>> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
>>> +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);
>>> +
>>> +static void
>>> +dp_update_drop_action_counter(enum xlate_error drop_reason,
>>> +                                 int delta) {
>>> +   static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
>>> +
>>> +   switch (drop_reason) {
>>> +   case XLATE_OK:
>>> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
>>> +        break;
>>> +   case XLATE_BRIDGE_NOT_FOUND:
>>> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
>>> +        break;
>>> +   case XLATE_RECURSION_TOO_DEEP:
>>> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
>>> +        break;
>>> +   case XLATE_TOO_MANY_RESUBMITS:
>>> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
>>> +        break;
>>> +   case XLATE_STACK_TOO_DEEP:
>>> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
>>> +        break;
>>> +   case XLATE_NO_RECIRCULATION_CONTEXT:
>>> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
>>> +        break;
>>> +   case XLATE_RECIRCULATION_CONFLICT:
>>> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
>>> +        break;
>>> +   case XLATE_TOO_MANY_MPLS_LABELS:
>>> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
>>> +        break;
>>> +   case XLATE_INVALID_TUNNEL_METADATA:
>>> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
>>> +        break;
>>> +   case XLATE_UNSUPPORTED_PACKET_TYPE:
>>> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
>>> +        break;
>>> +   case XLATE_CONGESTION_DROP:
>>> +        COVERAGE_ADD(drop_action_congestion, delta);
>>> +        break;
>>> +   case XLATE_FORWARDING_DISABLED:
>>> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
>>> +        break;
>>> +   case XLATE_MAX:
>>> +   default:
>>> +        VLOG_ERR_RL(&rl, "Invalid Drop reason type:%d",
>>> drop_reason);
>>> +   }
>>> +}
>>> +
>>>
>>>  /* Masked copy of an ethernet address. 'src' is already properly 
>>> masked. */  static void @@ -621,6 +690,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;
>>> @@ -705,6 +775,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:
>>> @@ -921,6 +992,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);
>>>                  }
>>>              }
>>> @@ -931,7 +1003,14 @@ odp_execute_actions(void *dp, struct 
>>> dp_packet_batch *batch, bool steal,
>>>                  conntrack_clear(packet);
>>>              }
>>>              break;
>>> -
>>> +        case OVS_ACTION_ATTR_DROP: {
>>> +            const enum xlate_error *drop_reason = nl_attr_get(a);
>>> +            if (*drop_reason < XLATE_MAX) {
>>> +                 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-util.c b/lib/odp-util.c index e893f46..9c3acc1
>>> 100644
>>> --- a/lib/odp-util.c
>>> +++ b/lib/odp-util.c
>>> @@ -43,6 +43,7 @@
>>>  #include "uuid.h"
>>>  #include "openvswitch/vlog.h"
>>>  #include "openvswitch/match.h"
>>> +#include "ofproto/ofproto-dpif-xlate.h"
>>>
>>>  VLOG_DEFINE_THIS_MODULE(odp_util);
>>>
>>> @@ -131,6 +132,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(enum xlate_error);
>>>
>>>      case OVS_ACTION_ATTR_UNSPEC:
>>>      case __OVS_ACTION_ATTR_MAX:
>>> @@ -1182,6 +1184,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:
>>> +        ds_put_cstr(ds, "drop");
>>> +        break;
>>>      case OVS_ACTION_ATTR_UNSPEC:
>>>      case __OVS_ACTION_ATTR_MAX:
>>>      default:
>>> @@ -2428,8 +2433,12 @@ odp_actions_from_string(const char *s, const 
>>> struct simap *port_names,
>>>                          struct ofpbuf *actions)  {
>>>      size_t old_size;
>>> +    enum xlate_error drop_action;
>>>
>>>      if (!strcasecmp(s, "drop")) {
>>> +        drop_action = XLATE_OK;
>>> +        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 5ea1097..52b328d 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 bc4ffee..0d0a27d 100644
>>> --- a/ofproto/ofproto-dpif-sflow.c
>>> +++ b/ofproto/ofproto-dpif-sflow.c
>>> @@ -1223,6 +1223,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-xlate.c 
>>> b/ofproto/ofproto-dpif-xlate.c index acd4817..95e2f25 100644
>>> --- a/ofproto/ofproto-dpif-xlate.c
>>> +++ b/ofproto/ofproto-dpif-xlate.c
>>> @@ -444,6 +444,12 @@ 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";
>>> +    case XLATE_MAX:
>>> +        break;
>>>      }
>>>      return "Unknown error";
>>>  }
>>> @@ -5921,6 +5927,14 @@ put_ct_label(const struct flow *flow, struct 
>>> ofpbuf *odp_actions,  }
>>>
>>>  static void
>>> +put_drop_action(struct ofpbuf *odp_actions, enum xlate_error error) 
>>> +{
>>> +    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_DROP,
>>> +                      &error, sizeof error);
>>> +
>>> +}
>>> +
>>> +static void
>>>  put_ct_helper(struct xlate_ctx *ctx,
>>>                struct ofpbuf *odp_actions, struct ofpact_conntrack
>>> *ofc)
>>>  {
>>> @@ -7383,48 +7397,51 @@ xlate_actions(struct xlate_in *xin, struct 
>>> xlate_out *xout)
>>>          }
>>>          size_t sample_actions_len = ctx.odp_actions->size;
>>>
>>> -        if (tnl_process_ecn(flow)
>>> -            && (!in_port || may_receive(in_port, &ctx))) {
>>> -            const struct ofpact *ofpacts;
>>> -            size_t ofpacts_len;
>>> -
>>> -            if (xin->ofpacts) {
>>> -                ofpacts = xin->ofpacts;
>>> -                ofpacts_len = xin->ofpacts_len;
>>> -            } else if (ctx.rule) {
>>> -                const struct rule_actions *actions
>>> -                    = rule_get_actions(&ctx.rule->up);
>>> -                ofpacts = actions->ofpacts;
>>> -                ofpacts_len = actions->ofpacts_len;
>>> -                ctx.rule_cookie = ctx.rule->up.flow_cookie;
>>> -            } else {
>>> -                OVS_NOT_REACHED();
>>> -            }
>>> +        if (!tnl_process_ecn(flow)) {
>>> +            ctx.error = XLATE_CONGESTION_DROP;
>>> +        } else {
>>> +            if (!in_port || may_receive(in_port, &ctx)) {
>>> +                const struct ofpact *ofpacts;
>>> +                size_t ofpacts_len;
>>> +
>>> +                if (xin->ofpacts) {
>>> +                    ofpacts = xin->ofpacts;
>>> +                    ofpacts_len = xin->ofpacts_len;
>>> +                } else if (ctx.rule) {
>>> +                    const struct rule_actions *actions
>>> +                         = rule_get_actions(&ctx.rule->up);
>>> +                    ofpacts = actions->ofpacts;
>>> +                    ofpacts_len = actions->ofpacts_len;
>>> +                    ctx.rule_cookie = ctx.rule->up.flow_cookie;
>>> +                } else {
>>> +                    OVS_NOT_REACHED();
>>> +                }
>>>
>>> -            mirror_ingress_packet(&ctx);
>>> -            do_xlate_actions(ofpacts, ofpacts_len, &ctx, true, 
>>> false);
>>> -            if (ctx.error) {
>>> -                goto exit;
>>> -            }
>>> +                mirror_ingress_packet(&ctx);
>>> +                do_xlate_actions(ofpacts, ofpacts_len, &ctx, true,
>>> false);
>>> +                if (ctx.error) {
>>> +                    goto exit;
>>> +                }
>>>
>>> -            /* We've let OFPP_NORMAL and the learning action look at 
>>> the
>>> -             * packet, so cancel all actions and freezing if 
>>> forwarding is
>>> -             * disabled. */
>>> -            if (in_port && (!xport_stp_forward_state(in_port) ||
>>> -                            !xport_rstp_forward_state(in_port))) {
>>> -                ctx.odp_actions->size = sample_actions_len;
>>> -                ctx_cancel_freeze(&ctx);
>>> -                ofpbuf_clear(&ctx.action_set);
>>> -            }
>>> +                /* We've let OFPP_NORMAL and the learning action
>>> look at the
>>> +                * packet, so cancel all actions and freezing if
>>> forwarding is
>>> +                * disabled. */
>>> +                if (in_port && (!xport_stp_forward_state(in_port) ||
>>> +                                
>>> + !xport_rstp_forward_state(in_port)))
>>> {
>>> +                    ctx.odp_actions->size = sample_actions_len;
>>> +                    ctx_cancel_freeze(&ctx);
>>> +                    ofpbuf_clear(&ctx.action_set);
>>> +                    ctx.error = XLATE_FORWARDING_DISABLED;
>>> +                }
>>>
>>> -            if (!ctx.freezing) {
>>> -                xlate_action_set(&ctx);
>>> -            }
>>> -            if (ctx.freezing) {
>>> -                finish_freezing(&ctx);
>>> +                if (!ctx.freezing) {
>>> +                    xlate_action_set(&ctx);
>>> +                }
>>> +                if (ctx.freezing) {
>>> +                    finish_freezing(&ctx);
>>> +                }
>>>              }
>>>          }
>>> -
>>>          /* Output only fully processed packets. */
>>>          if (!ctx.freezing
>>>              && xbridge->has_in_band @@ -7522,6 +7539,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..496bdbf 100644
>>> --- a/ofproto/ofproto-dpif-xlate.h
>>> +++ b/ofproto/ofproto-dpif-xlate.h
>>> @@ -216,6 +216,9 @@ enum xlate_error {
>>>      XLATE_TOO_MANY_MPLS_LABELS,
>>>      XLATE_INVALID_TUNNEL_METADATA,
>>>      XLATE_UNSUPPORTED_PACKET_TYPE,
>>> +    XLATE_CONGESTION_DROP,
>>> +    XLATE_FORWARDING_DISABLED,
>>> +    XLATE_MAX,
>>>  };
>>>
>>>  const char *xlate_strerror(enum xlate_error error); diff --git 
>>> a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 
>>> 50c959b..91df1b3 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); diff 
>>> --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h index 
>>> 1a404c8..8d8edca 100644
>>> --- a/ofproto/ofproto-dpif.h
>>> +++ b/ofproto/ofproto-dpif.h
>>> @@ -192,7 +192,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 +364,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..a4da75e 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/dpif-netdev.at b/tests/dpif-netdev.at index 
>>> 6915d43..5221c10 100644
>>> --- a/tests/dpif-netdev.at
>>> +++ b/tests/dpif-netdev.at
>>> @@ -337,6 +337,14 @@ 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
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "datapath_drop_meter" | 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..318bc02
>>> --- /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
>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e
>>> 08006f200a4d0001fc509a5800000000271502000000000010111213141516171819
>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>> +    ovs-appctl netdev-dummy/receive p1
>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e
>>> 08006f200a4d0001fc509a5800000000271502000000000010111213141516171819
>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>> +    ovs-appctl netdev-dummy/receive p1
>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e
>>> 08006f200a4d0001fc509a5800000000271502000000000010111213141516171819
>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>> +], [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),ipv
>>> +4(frag=no),
>>> packets:2, bytes:196, used:0.0, actions: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
>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e
>>> 08006f200a4d0001fc509a5800000000271502000000000010111213141516171819
>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>> +], [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
>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e
>>> 08006f200a4d0001fc509a5800000000271502000000000010111213141516171819
>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>> +], [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
>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e
>>> 08006f200a4d0001fc509a5800000000271502000000000010111213141516171819
>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>> +], [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..2c8cdce 100644
>>> --- a/tests/ofproto-dpif.at
>>> +++ b/tests/ofproto-dpif.at
>>> @@ -9384,7 +9384,7 @@
>>> recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan
>>> (vid=99,pcp=
>>>  # are wildcarded.
>>>  AT_CHECK([grep '\(modify\)\|\(flow_add\)' ovs-vswitchd.log | 
>>> strip_ufid ], [0], [dnl
>>>  dpif_netdev|DBG|flow_add: 
>>> recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x1234),
>>> actions:100
>>> -dpif|DBG|dummy at ovs-dummy: put[[modify]]
>>> skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0
>>> /0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(n
>>> s=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:0
>>> 0:00:0a/00:00:00:00:00:00),eth_type(0x1234)
>>> +dpif|DBG|dummy at ovs-dummy: put[[modify]]
>>> skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0
>>> /0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(n
>>> s=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:0
>>> 0:00:0a/00:00:00:00:00:00),eth_type(0x1234),
>>> actions:drop
>>>  dpif|DBG|dummy at ovs-dummy: put[[modify]] 
>>> skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0
>>> /0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(n
>>> s=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:0
>>> 0: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
>>>  ])
>>> 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..045b1a7 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
>>> 'aa55aa550000001b213cab6408004500007079464000402fba600101025c0101025
>>> 820000800000001c845000054ba200000400184861e0000011e00000200004227e75
>>> 400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1
>>> f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>> +
>>> +ovs-appctl time/warp 5000
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "datapath_drop_tunnel_pop_error" |
>>> cut -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +1
>>> +])
>>> +
>>> +sleep 1
>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>> 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025
>>> 820000800000001c845000054ba200000400184861e0000011e00000200004227e75
>>> 400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1
>>> f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>> +
>>> +ovs-appctl time/warp 5000
>>> +
>>> +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
>>> 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025
>>> 820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba2000004001848
>>> 61e0000011e00000200004227e75400030af3195500000000f265010000000000101
>>> 112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323
>>> 334353637'])
>>>
>>> @@ -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 @@ -478,7 
>>> +499,7 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  5'], 
>>> [0], [dnl
>>>    port  5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, 
>>> crc=?
>>>  ])
>>>  AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 
>>> 'in_port(6081)'], [0], [dnl 
>>> -tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,t
>>> ype=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+k
>>> ey)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x08
>>> 00),ipv4(frag=no),
>>> packets:0, bytes:0, used:never,
>>> actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation
>>> =0,recirc_id=3,rule_cookie=0,controller_id=0,max_len=65535))
>>> +tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,t
>>> +ype=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+
>>> +key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x
>>> +0800),ipv4(frag=no),
>>> packets:0, bytes:0, used:never,
>>> actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation
>>> =0,recirc_id=2,rule_cookie=0,controller_id=0,max_len=65535))
>>>  ])
>>>
>>>  ovs-appctl time/warp 10000
>>> @@ -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..7bd5b48 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),e
>>> th(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv
>>> 4(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
>>> +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,16 @@ 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
>>> 'aa55aa550001f8bc124434b6080045000054ba20000040018486010103580101037
>>> 001004227e75400030af3195500000000f2650100000000001011121314151617181
>>> 91a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>> +
>>> +sleep 2
>>> +
>>> +AT_CHECK([
>>> +ovs-appctl coverage/show | grep "datapath_drop_invalid_port" | cut
>>> -d':' -f2|sed 's/ //'
>>> +], [0], [dnl
>>> +1
>>> +])
>>>  OVS_VSWITCHD_STOP
>>>  AT_CLEANUP
>>>
>>> --
>>> 1.9.1
>>>
>>> _______________________________________________
>>> dev mailing list
>>> dev at openvswitch.org
>>> https://protect2.fireeye.com/url?k=b1bddc0f-ed69d7a4-b1bd9c94-864b0d
>>> 136b87-d394ae6811e94bab&q=1&u=https%3A%2F%2Fmail.openvswitch.org%2Fm
>>> ailman%2Flistinfo%2Fovs-dev
Ilya Maximets June 6, 2019, 2:47 p.m. UTC | #8
On 06.06.2019 17:28, Anju Thomas wrote:
> Hi Ilya,
> Yes, I have addressed those comments in v9.

That is really strange. Maybe you've sent the wrong version?

That is what I (and everyone else) received:
  https://mail.openvswitch.org/pipermail/ovs-dev/2019-February/356743.html
It's the same code as down below.

You may simply grep for following lines:

COVERAGE_DEFINE(dp_sample_error_drop);   <-- I asked to rename this.

if (tnl_process_ecn(flow)                <-- Here started a big diff that
                                             that I suggested to replace with
                                             a smaller one.

packet_count = packets_->count;          <-- Here I suggested to use
                                             dp_packet_batch_size().

Please, re-check.
Here is my review for v8, just in case:
https://mail.openvswitch.org/pipermail/ovs-dev/2019-February/356153.html

Best regards, Ilya Maximets.


> 
> Regards  & Thanks
> Anju
> 
> -----Original Message-----
> From: Ilya Maximets <i.maximets@samsung.com> 
> Sent: Tuesday, June 4, 2019 12:33 PM
> To: ovs-dev@openvswitch.org; Anju Thomas <anju.thomas@ericsson.com>
> Cc: Eelco Chaudron <echaudro@redhat.com>
> Subject: Re: Re: [ovs-dev] [PATCH v9] Improved Packet Drop Statistics in OVS
> 
>> Hi Eelco,
>> Apart from Ilya's comments I have not received any comments for v8. I have addressed those comment in v9.
> 
> Hi Anju.
> I wrote about my comments to v8 just because you didn't reply or address a bunch of them in v9. There was comments about using dp_packet_batch_size(), renaming suggestion for sample and nsh drop counters, suggested ecn handling code snippet, maybe something else.
> 
> Best regards, Ilya Maximets.
> 
>>
>> Regards
>> Anju
>>
>> -----Original Message-----
>> From: Eelco Chaudron <echaudro at redhat.com>
>> Sent: Monday, June 3, 2019 6:23 PM
>> To: Anju Thomas <anju.thomas at ericsson.com>
>> Cc: dev at openvswitch.org
>> Subject: Re: [ovs-dev] [PATCH v9] Improved Packet Drop Statistics in 
>> OVS
>>
>> Hi Anju,
>>
>> Wondering if you got the email below…
>>
>> Cheers,
>>
>> Eelco
>>
>>
>> On 24 May 2019, at 11:46, Eelco Chaudron wrote:
>>
>>> Hi Anju,
>>>
>>> Was there ever a follow up on this patch? I only see one response 
>>> from Ilya on this asking about his v8 comments.
>>>
>>> Thanks,
>>>
>>> Eelco
>>>
>>>
>>> On 27 Feb 2019, at 10:22, 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. Identify and account all the silent packet drop scenarios
>>>>
>>>> 2. Display these drops in ovs-appctl coverage/show
>>>>
>>>> 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
>>>>
>>>> Co-authored-by: Rohith Basavaraja <rohith.basavaraja at gmail.com>
>>>> Co-authored-by: Keshav Gupta <keshugupta1 at gmail.com>
>>>> Signed-off-by: Anju Thomas <anju.thomas at ericsson.com>
>>>> Signed-off-by: Rohith Basavaraja <rohith.basavaraja at gmail.com>
>>>> Signed-off-by: Keshav Gupta <keshugupta1 at gmail.com>
>>>> ---
>>>>  datapath/linux/compat/include/linux/openvswitch.h |   1 +
>>>>  lib/dpif-netdev.c                                 |  44 ++++-
>>>>  lib/dpif.c                                        |   7 +
>>>>  lib/dpif.h                                        |   3 +
>>>>  lib/odp-execute.c                                 |  81 ++++++++-
>>>>  lib/odp-util.c                                    |   9 +
>>>>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>>>>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>>>>  ofproto/ofproto-dpif-xlate.c                      | 103 +++++++----
>>>>  ofproto/ofproto-dpif-xlate.h                      |   3 +
>>>>  ofproto/ofproto-dpif.c                            |   8 +
>>>>  ofproto/ofproto-dpif.h                            |   7 +-
>>>>  tests/automake.mk                                 |   3 +-
>>>>  tests/dpif-netdev.at                              |   8 +
>>>>  tests/drop-stats.at                               | 197 
>>>> ++++++++++++++++++++++
>>>>  tests/ofproto-dpif.at                             |   2 +-
>>>>  tests/testsuite.at                                |   1 +
>>>>  tests/tunnel-push-pop.at                          |  28 ++-
>>>>  tests/tunnel.at                                   |  14 +-
>>>>  19 files changed, 475 insertions(+), 46 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 d5aa09d..e77e9c8 100644
>>>> --- a/datapath/linux/compat/include/linux/openvswitch.h
>>>> +++ b/datapath/linux/compat/include/linux/openvswitch.h
>>>> @@ -946,6 +946,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,         /* Drop action. */
>>>>
>>>>  #ifndef __KERNEL__
>>>>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
>>>> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index
>>>> 77ac1d2..acc7913 100644
>>>> --- a/lib/dpif-netdev.c
>>>> +++ b/lib/dpif-netdev.c
>>>> @@ -101,6 +101,17 @@ 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(datapath_drop_meter);
>>>> +COVERAGE_DEFINE(datapath_drop_upcall_error);
>>>> +COVERAGE_DEFINE(datapath_drop_lock_error);
>>>> +COVERAGE_DEFINE(datapath_drop_userspace_action_error);
>>>> +COVERAGE_DEFINE(datapath_drop_tunnel_push_error);
>>>> +COVERAGE_DEFINE(datapath_drop_tunnel_pop_error);
>>>> +COVERAGE_DEFINE(datapath_drop_recirc_error);
>>>> +COVERAGE_DEFINE(datapath_drop_invalid_port);
>>>> +COVERAGE_DEFINE(datapath_drop_invalid_tnl_port);
>>>> +COVERAGE_DEFINE(datapath_drop_rx_invalid_packet);
>>>> +
>>>>  /* Protects against changes to 'dp_netdevs'. */  static struct 
>>>> ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>>>>
>>>> @@ -5647,6 +5658,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, 
>>>> struct dp_packet_batch *packets_,
>>>>              band->packet_count += 1;
>>>>              band->byte_count += dp_packet_size(packet);
>>>>
>>>> +            COVERAGE_INC(datapath_drop_meter);
>>>>              dp_packet_delete(packet);
>>>>          } else {
>>>>              /* Meter accepts packet. */ @@ -6402,6 +6414,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(datapath_drop_rx_invalid_packet);
>>>>              continue;
>>>>          }
>>>>
>>>> @@ -6528,6 +6541,7 @@ handle_packet_upcall(struct 
>>>> dp_netdev_pmd_thread *pmd,
>>>>                               put_actions);
>>>>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>>>>          dp_packet_delete(packet);
>>>> +        COVERAGE_INC(datapath_drop_upcall_error);
>>>>          return error;
>>>>      }
>>>>
>>>> @@ -6659,6 +6673,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(datapath_drop_lock_error);
>>>>                  upcall_fail_cnt++;
>>>>              }
>>>>          }
>>>> @@ -6928,6 +6943,7 @@ dp_execute_userspace_action(struct
>>>> dp_netdev_pmd_thread *pmd,
>>>>                                    actions->data, actions->size);
>>>>      } else if (should_steal) {
>>>>          dp_packet_delete(packet);
>>>> +        COVERAGE_INC(datapath_drop_userspace_action_error);
>>>>      }
>>>>  }
>>>>
>>>> @@ -6942,6 +6958,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:
>>>> @@ -6983,6 +7000,9 @@ dp_execute_cb(void *aux_, struct 
>>>> dp_packet_batch *packets_,
>>>>                  dp_packet_batch_add(&p->output_pkts, packet);
>>>>              }
>>>>              return;
>>>> +        } else {
>>>> +            COVERAGE_ADD(datapath_drop_invalid_port,
>>>> +                            dp_packet_batch_size(packets_));
>>>>          }
>>>>          break;
>>>>
>>>> @@ -6992,10 +7012,16 @@ 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(datapath_drop_tunnel_push_error,
>>>> +                            dp_packet_batch_size(packets_));
>>>>              break;
>>>>          }
>>>>          dp_packet_batch_apply_cutlen(packets_);
>>>> -        push_tnl_action(pmd, a, packets_);
>>>> +        packet_count = dp_packet_batch_size(packets_);
>>>> +        if (push_tnl_action(pmd, a, packets_)) {
>>>> +            COVERAGE_ADD(datapath_drop_tunnel_push_error,
>>>> +                            packet_count);
>>>> +        }
>>>>          return;
>>>>
>>>>      case OVS_ACTION_ATTR_TUNNEL_POP:
>>>> @@ -7015,7 +7041,13 @@ 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(datapath_drop_tunnel_pop_error,
>>>> +                                     packet_dropped);
>>>> +                }
>>>>                  if (dp_packet_batch_is_empty(packets_)) {
>>>>                      return;
>>>>                  }
>>>> @@ -7030,6 +7062,11 @@ dp_execute_cb(void *aux_, struct 
>>>> dp_packet_batch *packets_,
>>>>                  (*depth)--;
>>>>                  return;
>>>>              }
>>>> +            COVERAGE_ADD(datapath_drop_invalid_tnl_port,
>>>> +                            dp_packet_batch_size(packets_));
>>>> +        } else {
>>>> +            COVERAGE_ADD(datapath_drop_recirc_error,
>>>> +                            dp_packet_batch_size(packets_));
>>>>          }
>>>>          break;
>>>>
>>>> @@ -7074,6 +7111,8 @@ dp_execute_cb(void *aux_, struct 
>>>> dp_packet_batch *packets_,
>>>>
>>>>              return;
>>>>          }
>>>> +        COVERAGE_ADD(datapath_drop_lock_error,
>>>> +                        dp_packet_batch_size(packets_));
>>>>          break;
>>>>
>>>>      case OVS_ACTION_ATTR_RECIRC:
>>>> @@ -7097,6 +7136,8 @@ dp_execute_cb(void *aux_, struct 
>>>> dp_packet_batch *packets_,
>>>>              return;
>>>>          }
>>>>
>>>> +        COVERAGE_ADD(datapath_drop_recirc_error,
>>>> +                        dp_packet_batch_size(packets_));
>>>>          VLOG_WARN("Packet dropped. Max recirculation depth 
>>>> exceeded.");
>>>>          break;
>>>>
>>>> @@ -7249,6 +7290,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();
>>>>      }
>>>> diff --git a/lib/dpif.c b/lib/dpif.c index 457c9bf..d75a012 100644
>>>> --- a/lib/dpif.c
>>>> +++ b/lib/dpif.c
>>>> @@ -1269,6 +1269,7 @@ dpif_execute_helper_cb(void *aux_, struct 
>>>> dp_packet_batch *packets_,
>>>>      case OVS_ACTION_ATTR_POP_NSH:
>>>>      case OVS_ACTION_ATTR_CT_CLEAR:
>>>>      case OVS_ACTION_ATTR_UNSPEC:
>>>> +    case OVS_ACTION_ATTR_DROP:
>>>>      case __OVS_ACTION_ATTR_MAX:
>>>>          OVS_NOT_REACHED();
>>>>      }
>>>> @@ -1874,6 +1875,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, 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/odp-execute.c b/lib/odp-execute.c index 
>>>> 5d07133..c8ff16b 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"
>>>> @@ -36,6 +37,74 @@
>>>>  #include "util.h"
>>>>  #include "csum.h"
>>>>  #include "conntrack.h"
>>>> +#include "ofproto/ofproto-dpif-xlate.h"
>>>> +#include "openvswitch/vlog.h"
>>>> +
>>>> +VLOG_DEFINE_THIS_MODULE(odp_execute)
>>>> +COVERAGE_DEFINE(dp_sample_error_drop);
>>>> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
>>>> +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);
>>>> +
>>>> +static void
>>>> +dp_update_drop_action_counter(enum xlate_error drop_reason,
>>>> +                                 int delta) {
>>>> +   static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
>>>> +
>>>> +   switch (drop_reason) {
>>>> +   case XLATE_OK:
>>>> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
>>>> +        break;
>>>> +   case XLATE_BRIDGE_NOT_FOUND:
>>>> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
>>>> +        break;
>>>> +   case XLATE_RECURSION_TOO_DEEP:
>>>> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
>>>> +        break;
>>>> +   case XLATE_TOO_MANY_RESUBMITS:
>>>> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
>>>> +        break;
>>>> +   case XLATE_STACK_TOO_DEEP:
>>>> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
>>>> +        break;
>>>> +   case XLATE_NO_RECIRCULATION_CONTEXT:
>>>> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
>>>> +        break;
>>>> +   case XLATE_RECIRCULATION_CONFLICT:
>>>> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
>>>> +        break;
>>>> +   case XLATE_TOO_MANY_MPLS_LABELS:
>>>> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
>>>> +        break;
>>>> +   case XLATE_INVALID_TUNNEL_METADATA:
>>>> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
>>>> +        break;
>>>> +   case XLATE_UNSUPPORTED_PACKET_TYPE:
>>>> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
>>>> +        break;
>>>> +   case XLATE_CONGESTION_DROP:
>>>> +        COVERAGE_ADD(drop_action_congestion, delta);
>>>> +        break;
>>>> +   case XLATE_FORWARDING_DISABLED:
>>>> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
>>>> +        break;
>>>> +   case XLATE_MAX:
>>>> +   default:
>>>> +        VLOG_ERR_RL(&rl, "Invalid Drop reason type:%d",
>>>> drop_reason);
>>>> +   }
>>>> +}
>>>> +
>>>>
>>>>  /* Masked copy of an ethernet address. 'src' is already properly 
>>>> masked. */  static void @@ -621,6 +690,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;
>>>> @@ -705,6 +775,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:
>>>> @@ -921,6 +992,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);
>>>>                  }
>>>>              }
>>>> @@ -931,7 +1003,14 @@ odp_execute_actions(void *dp, struct 
>>>> dp_packet_batch *batch, bool steal,
>>>>                  conntrack_clear(packet);
>>>>              }
>>>>              break;
>>>> -
>>>> +        case OVS_ACTION_ATTR_DROP: {
>>>> +            const enum xlate_error *drop_reason = nl_attr_get(a);
>>>> +            if (*drop_reason < XLATE_MAX) {
>>>> +                 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-util.c b/lib/odp-util.c index e893f46..9c3acc1
>>>> 100644
>>>> --- a/lib/odp-util.c
>>>> +++ b/lib/odp-util.c
>>>> @@ -43,6 +43,7 @@
>>>>  #include "uuid.h"
>>>>  #include "openvswitch/vlog.h"
>>>>  #include "openvswitch/match.h"
>>>> +#include "ofproto/ofproto-dpif-xlate.h"
>>>>
>>>>  VLOG_DEFINE_THIS_MODULE(odp_util);
>>>>
>>>> @@ -131,6 +132,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(enum xlate_error);
>>>>
>>>>      case OVS_ACTION_ATTR_UNSPEC:
>>>>      case __OVS_ACTION_ATTR_MAX:
>>>> @@ -1182,6 +1184,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:
>>>> +        ds_put_cstr(ds, "drop");
>>>> +        break;
>>>>      case OVS_ACTION_ATTR_UNSPEC:
>>>>      case __OVS_ACTION_ATTR_MAX:
>>>>      default:
>>>> @@ -2428,8 +2433,12 @@ odp_actions_from_string(const char *s, const 
>>>> struct simap *port_names,
>>>>                          struct ofpbuf *actions)  {
>>>>      size_t old_size;
>>>> +    enum xlate_error drop_action;
>>>>
>>>>      if (!strcasecmp(s, "drop")) {
>>>> +        drop_action = XLATE_OK;
>>>> +        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 5ea1097..52b328d 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 bc4ffee..0d0a27d 100644
>>>> --- a/ofproto/ofproto-dpif-sflow.c
>>>> +++ b/ofproto/ofproto-dpif-sflow.c
>>>> @@ -1223,6 +1223,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-xlate.c 
>>>> b/ofproto/ofproto-dpif-xlate.c index acd4817..95e2f25 100644
>>>> --- a/ofproto/ofproto-dpif-xlate.c
>>>> +++ b/ofproto/ofproto-dpif-xlate.c
>>>> @@ -444,6 +444,12 @@ 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";
>>>> +    case XLATE_MAX:
>>>> +        break;
>>>>      }
>>>>      return "Unknown error";
>>>>  }
>>>> @@ -5921,6 +5927,14 @@ put_ct_label(const struct flow *flow, struct 
>>>> ofpbuf *odp_actions,  }
>>>>
>>>>  static void
>>>> +put_drop_action(struct ofpbuf *odp_actions, enum xlate_error error) 
>>>> +{
>>>> +    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_DROP,
>>>> +                      &error, sizeof error);
>>>> +
>>>> +}
>>>> +
>>>> +static void
>>>>  put_ct_helper(struct xlate_ctx *ctx,
>>>>                struct ofpbuf *odp_actions, struct ofpact_conntrack
>>>> *ofc)
>>>>  {
>>>> @@ -7383,48 +7397,51 @@ xlate_actions(struct xlate_in *xin, struct 
>>>> xlate_out *xout)
>>>>          }
>>>>          size_t sample_actions_len = ctx.odp_actions->size;
>>>>
>>>> -        if (tnl_process_ecn(flow)
>>>> -            && (!in_port || may_receive(in_port, &ctx))) {
>>>> -            const struct ofpact *ofpacts;
>>>> -            size_t ofpacts_len;
>>>> -
>>>> -            if (xin->ofpacts) {
>>>> -                ofpacts = xin->ofpacts;
>>>> -                ofpacts_len = xin->ofpacts_len;
>>>> -            } else if (ctx.rule) {
>>>> -                const struct rule_actions *actions
>>>> -                    = rule_get_actions(&ctx.rule->up);
>>>> -                ofpacts = actions->ofpacts;
>>>> -                ofpacts_len = actions->ofpacts_len;
>>>> -                ctx.rule_cookie = ctx.rule->up.flow_cookie;
>>>> -            } else {
>>>> -                OVS_NOT_REACHED();
>>>> -            }
>>>> +        if (!tnl_process_ecn(flow)) {
>>>> +            ctx.error = XLATE_CONGESTION_DROP;
>>>> +        } else {
>>>> +            if (!in_port || may_receive(in_port, &ctx)) {
>>>> +                const struct ofpact *ofpacts;
>>>> +                size_t ofpacts_len;
>>>> +
>>>> +                if (xin->ofpacts) {
>>>> +                    ofpacts = xin->ofpacts;
>>>> +                    ofpacts_len = xin->ofpacts_len;
>>>> +                } else if (ctx.rule) {
>>>> +                    const struct rule_actions *actions
>>>> +                         = rule_get_actions(&ctx.rule->up);
>>>> +                    ofpacts = actions->ofpacts;
>>>> +                    ofpacts_len = actions->ofpacts_len;
>>>> +                    ctx.rule_cookie = ctx.rule->up.flow_cookie;
>>>> +                } else {
>>>> +                    OVS_NOT_REACHED();
>>>> +                }
>>>>
>>>> -            mirror_ingress_packet(&ctx);
>>>> -            do_xlate_actions(ofpacts, ofpacts_len, &ctx, true, 
>>>> false);
>>>> -            if (ctx.error) {
>>>> -                goto exit;
>>>> -            }
>>>> +                mirror_ingress_packet(&ctx);
>>>> +                do_xlate_actions(ofpacts, ofpacts_len, &ctx, true,
>>>> false);
>>>> +                if (ctx.error) {
>>>> +                    goto exit;
>>>> +                }
>>>>
>>>> -            /* We've let OFPP_NORMAL and the learning action look at 
>>>> the
>>>> -             * packet, so cancel all actions and freezing if 
>>>> forwarding is
>>>> -             * disabled. */
>>>> -            if (in_port && (!xport_stp_forward_state(in_port) ||
>>>> -                            !xport_rstp_forward_state(in_port))) {
>>>> -                ctx.odp_actions->size = sample_actions_len;
>>>> -                ctx_cancel_freeze(&ctx);
>>>> -                ofpbuf_clear(&ctx.action_set);
>>>> -            }
>>>> +                /* We've let OFPP_NORMAL and the learning action
>>>> look at the
>>>> +                * packet, so cancel all actions and freezing if
>>>> forwarding is
>>>> +                * disabled. */
>>>> +                if (in_port && (!xport_stp_forward_state(in_port) ||
>>>> +                                
>>>> + !xport_rstp_forward_state(in_port)))
>>>> {
>>>> +                    ctx.odp_actions->size = sample_actions_len;
>>>> +                    ctx_cancel_freeze(&ctx);
>>>> +                    ofpbuf_clear(&ctx.action_set);
>>>> +                    ctx.error = XLATE_FORWARDING_DISABLED;
>>>> +                }
>>>>
>>>> -            if (!ctx.freezing) {
>>>> -                xlate_action_set(&ctx);
>>>> -            }
>>>> -            if (ctx.freezing) {
>>>> -                finish_freezing(&ctx);
>>>> +                if (!ctx.freezing) {
>>>> +                    xlate_action_set(&ctx);
>>>> +                }
>>>> +                if (ctx.freezing) {
>>>> +                    finish_freezing(&ctx);
>>>> +                }
>>>>              }
>>>>          }
>>>> -
>>>>          /* Output only fully processed packets. */
>>>>          if (!ctx.freezing
>>>>              && xbridge->has_in_band @@ -7522,6 +7539,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..496bdbf 100644
>>>> --- a/ofproto/ofproto-dpif-xlate.h
>>>> +++ b/ofproto/ofproto-dpif-xlate.h
>>>> @@ -216,6 +216,9 @@ enum xlate_error {
>>>>      XLATE_TOO_MANY_MPLS_LABELS,
>>>>      XLATE_INVALID_TUNNEL_METADATA,
>>>>      XLATE_UNSUPPORTED_PACKET_TYPE,
>>>> +    XLATE_CONGESTION_DROP,
>>>> +    XLATE_FORWARDING_DISABLED,
>>>> +    XLATE_MAX,
>>>>  };
>>>>
>>>>  const char *xlate_strerror(enum xlate_error error); diff --git 
>>>> a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 
>>>> 50c959b..91df1b3 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); diff 
>>>> --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h index 
>>>> 1a404c8..8d8edca 100644
>>>> --- a/ofproto/ofproto-dpif.h
>>>> +++ b/ofproto/ofproto-dpif.h
>>>> @@ -192,7 +192,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 +364,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..a4da75e 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/dpif-netdev.at b/tests/dpif-netdev.at index 
>>>> 6915d43..5221c10 100644
>>>> --- a/tests/dpif-netdev.at
>>>> +++ b/tests/dpif-netdev.at
>>>> @@ -337,6 +337,14 @@ 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
>>>> +
>>>> +AT_CHECK([
>>>> +ovs-appctl coverage/show | grep "datapath_drop_meter" | 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..318bc02
>>>> --- /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
>>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e
>>>> 08006f200a4d0001fc509a5800000000271502000000000010111213141516171819
>>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>>> +    ovs-appctl netdev-dummy/receive p1
>>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e
>>>> 08006f200a4d0001fc509a5800000000271502000000000010111213141516171819
>>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>>> +    ovs-appctl netdev-dummy/receive p1
>>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e
>>>> 08006f200a4d0001fc509a5800000000271502000000000010111213141516171819
>>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>>> +], [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),ipv
>>>> +4(frag=no),
>>>> packets:2, bytes:196, used:0.0, actions: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
>>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e
>>>> 08006f200a4d0001fc509a5800000000271502000000000010111213141516171819
>>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>>> +], [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
>>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e
>>>> 08006f200a4d0001fc509a5800000000271502000000000010111213141516171819
>>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>>> +], [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
>>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1e
>>>> 08006f200a4d0001fc509a5800000000271502000000000010111213141516171819
>>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>>> +], [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..2c8cdce 100644
>>>> --- a/tests/ofproto-dpif.at
>>>> +++ b/tests/ofproto-dpif.at
>>>> @@ -9384,7 +9384,7 @@
>>>> recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan
>>>> (vid=99,pcp=
>>>>  # are wildcarded.
>>>>  AT_CHECK([grep '\(modify\)\|\(flow_add\)' ovs-vswitchd.log | 
>>>> strip_ufid ], [0], [dnl
>>>>  dpif_netdev|DBG|flow_add: 
>>>> recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x1234),
>>>> actions:100
>>>> -dpif|DBG|dummy at ovs-dummy: put[[modify]]
>>>> skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0
>>>> /0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(n
>>>> s=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:0
>>>> 0:00:0a/00:00:00:00:00:00),eth_type(0x1234)
>>>> +dpif|DBG|dummy at ovs-dummy: put[[modify]]
>>>> skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0
>>>> /0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(n
>>>> s=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:0
>>>> 0:00:0a/00:00:00:00:00:00),eth_type(0x1234),
>>>> actions:drop
>>>>  dpif|DBG|dummy at ovs-dummy: put[[modify]] 
>>>> skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(0
>>>> /0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(n
>>>> s=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:0
>>>> 0: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
>>>>  ])
>>>> 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..045b1a7 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
>>>> 'aa55aa550000001b213cab6408004500007079464000402fba600101025c0101025
>>>> 820000800000001c845000054ba200000400184861e0000011e00000200004227e75
>>>> 400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1
>>>> f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>>> +
>>>> +ovs-appctl time/warp 5000
>>>> +
>>>> +AT_CHECK([
>>>> +ovs-appctl coverage/show | grep "datapath_drop_tunnel_pop_error" |
>>>> cut -d':' -f2|sed 's/ //'
>>>> +], [0], [dnl
>>>> +1
>>>> +])
>>>> +
>>>> +sleep 1
>>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>>> 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025
>>>> 820000800000001c845000054ba200000400184861e0000011e00000200004227e75
>>>> 400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1
>>>> f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>>> +
>>>> +ovs-appctl time/warp 5000
>>>> +
>>>> +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
>>>> 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025
>>>> 820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba2000004001848
>>>> 61e0000011e00000200004227e75400030af3195500000000f265010000000000101
>>>> 112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323
>>>> 334353637'])
>>>>
>>>> @@ -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 @@ -478,7 
>>>> +499,7 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  5'], 
>>>> [0], [dnl
>>>>    port  5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, 
>>>> crc=?
>>>>  ])
>>>>  AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 
>>>> 'in_port(6081)'], [0], [dnl 
>>>> -tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,t
>>>> ype=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+k
>>>> ey)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x08
>>>> 00),ipv4(frag=no),
>>>> packets:0, bytes:0, used:never,
>>>> actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation
>>>> =0,recirc_id=3,rule_cookie=0,controller_id=0,max_len=65535))
>>>> +tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,t
>>>> +ype=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+
>>>> +key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x
>>>> +0800),ipv4(frag=no),
>>>> packets:0, bytes:0, used:never,
>>>> actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation
>>>> =0,recirc_id=2,rule_cookie=0,controller_id=0,max_len=65535))
>>>>  ])
>>>>
>>>>  ovs-appctl time/warp 10000
>>>> @@ -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..7bd5b48 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),e
>>>> th(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv
>>>> 4(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
>>>> +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,16 @@ 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
>>>> 'aa55aa550001f8bc124434b6080045000054ba20000040018486010103580101037
>>>> 001004227e75400030af3195500000000f2650100000000001011121314151617181
>>>> 91a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>>> +
>>>> +sleep 2
>>>> +
>>>> +AT_CHECK([
>>>> +ovs-appctl coverage/show | grep "datapath_drop_invalid_port" | cut
>>>> -d':' -f2|sed 's/ //'
>>>> +], [0], [dnl
>>>> +1
>>>> +])
>>>>  OVS_VSWITCHD_STOP
>>>>  AT_CLEANUP
>>>>
>>>> --
>>>> 1.9.1
>>>>
>>>> _______________________________________________
>>>> dev mailing list
>>>> dev at openvswitch.org
>>>> https://protect2.fireeye.com/url?k=b1bddc0f-ed69d7a4-b1bd9c94-864b0d
>>>> 136b87-d394ae6811e94bab&q=1&u=https%3A%2F%2Fmail.openvswitch.org%2Fm
>>>> ailman%2Flistinfo%2Fovs-dev
Anju Thomas June 7, 2019, 3:10 a.m. UTC | #9
Hi,
Oh.. You are right I did send the wrong version. Let me send the latest version.

Regards
Anju

-----Original Message-----
From: Ilya Maximets <i.maximets@samsung.com> 
Sent: Thursday, June 6, 2019 8:18 PM
To: Anju Thomas <anju.thomas@ericsson.com>; ovs-dev@openvswitch.org
Cc: Eelco Chaudron <echaudro@redhat.com>
Subject: Re: [ovs-dev] [PATCH v9] Improved Packet Drop Statistics in OVS

On 06.06.2019 17:28, Anju Thomas wrote:
> Hi Ilya,
> Yes, I have addressed those comments in v9.

That is really strange. Maybe you've sent the wrong version?

That is what I (and everyone else) received:
  https://mail.openvswitch.org/pipermail/ovs-dev/2019-February/356743.html
It's the same code as down below.

You may simply grep for following lines:

COVERAGE_DEFINE(dp_sample_error_drop);   <-- I asked to rename this.

if (tnl_process_ecn(flow)                <-- Here started a big diff that
                                             that I suggested to replace with
                                             a smaller one.

packet_count = packets_->count;          <-- Here I suggested to use
                                             dp_packet_batch_size().

Please, re-check.
Here is my review for v8, just in case:
https://mail.openvswitch.org/pipermail/ovs-dev/2019-February/356153.html

Best regards, Ilya Maximets.


> 
> Regards  & Thanks
> Anju
> 
> -----Original Message-----
> From: Ilya Maximets <i.maximets@samsung.com>
> Sent: Tuesday, June 4, 2019 12:33 PM
> To: ovs-dev@openvswitch.org; Anju Thomas <anju.thomas@ericsson.com>
> Cc: Eelco Chaudron <echaudro@redhat.com>
> Subject: Re: Re: [ovs-dev] [PATCH v9] Improved Packet Drop Statistics 
> in OVS
> 
>> Hi Eelco,
>> Apart from Ilya's comments I have not received any comments for v8. I have addressed those comment in v9.
> 
> Hi Anju.
> I wrote about my comments to v8 just because you didn't reply or address a bunch of them in v9. There was comments about using dp_packet_batch_size(), renaming suggestion for sample and nsh drop counters, suggested ecn handling code snippet, maybe something else.
> 
> Best regards, Ilya Maximets.
> 
>>
>> Regards
>> Anju
>>
>> -----Original Message-----
>> From: Eelco Chaudron <echaudro at redhat.com>
>> Sent: Monday, June 3, 2019 6:23 PM
>> To: Anju Thomas <anju.thomas at ericsson.com>
>> Cc: dev at openvswitch.org
>> Subject: Re: [ovs-dev] [PATCH v9] Improved Packet Drop Statistics in 
>> OVS
>>
>> Hi Anju,
>>
>> Wondering if you got the email below…
>>
>> Cheers,
>>
>> Eelco
>>
>>
>> On 24 May 2019, at 11:46, Eelco Chaudron wrote:
>>
>>> Hi Anju,
>>>
>>> Was there ever a follow up on this patch? I only see one response 
>>> from Ilya on this asking about his v8 comments.
>>>
>>> Thanks,
>>>
>>> Eelco
>>>
>>>
>>> On 27 Feb 2019, at 10:22, 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. Identify and account all the silent packet drop scenarios
>>>>
>>>> 2. Display these drops in ovs-appctl coverage/show
>>>>
>>>> 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-th
>>>> e
>>>> -
>>>> data-plane-in-ovs-82280329
>>>>
>>>> Co-authored-by: Rohith Basavaraja <rohith.basavaraja at gmail.com>
>>>> Co-authored-by: Keshav Gupta <keshugupta1 at gmail.com>
>>>> Signed-off-by: Anju Thomas <anju.thomas at ericsson.com>
>>>> Signed-off-by: Rohith Basavaraja <rohith.basavaraja at gmail.com>
>>>> Signed-off-by: Keshav Gupta <keshugupta1 at gmail.com>
>>>> ---
>>>>  datapath/linux/compat/include/linux/openvswitch.h |   1 +
>>>>  lib/dpif-netdev.c                                 |  44 ++++-
>>>>  lib/dpif.c                                        |   7 +
>>>>  lib/dpif.h                                        |   3 +
>>>>  lib/odp-execute.c                                 |  81 ++++++++-
>>>>  lib/odp-util.c                                    |   9 +
>>>>  ofproto/ofproto-dpif-ipfix.c                      |   1 +
>>>>  ofproto/ofproto-dpif-sflow.c                      |   1 +
>>>>  ofproto/ofproto-dpif-xlate.c                      | 103 +++++++----
>>>>  ofproto/ofproto-dpif-xlate.h                      |   3 +
>>>>  ofproto/ofproto-dpif.c                            |   8 +
>>>>  ofproto/ofproto-dpif.h                            |   7 +-
>>>>  tests/automake.mk                                 |   3 +-
>>>>  tests/dpif-netdev.at                              |   8 +
>>>>  tests/drop-stats.at                               | 197 
>>>> ++++++++++++++++++++++
>>>>  tests/ofproto-dpif.at                             |   2 +-
>>>>  tests/testsuite.at                                |   1 +
>>>>  tests/tunnel-push-pop.at                          |  28 ++-
>>>>  tests/tunnel.at                                   |  14 +-
>>>>  19 files changed, 475 insertions(+), 46 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 d5aa09d..e77e9c8 100644
>>>> --- a/datapath/linux/compat/include/linux/openvswitch.h
>>>> +++ b/datapath/linux/compat/include/linux/openvswitch.h
>>>> @@ -946,6 +946,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,         /* Drop action. */
>>>>
>>>>  #ifndef __KERNEL__
>>>>  	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
>>>> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index
>>>> 77ac1d2..acc7913 100644
>>>> --- a/lib/dpif-netdev.c
>>>> +++ b/lib/dpif-netdev.c
>>>> @@ -101,6 +101,17 @@ 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(datapath_drop_meter);
>>>> +COVERAGE_DEFINE(datapath_drop_upcall_error);
>>>> +COVERAGE_DEFINE(datapath_drop_lock_error);
>>>> +COVERAGE_DEFINE(datapath_drop_userspace_action_error);
>>>> +COVERAGE_DEFINE(datapath_drop_tunnel_push_error);
>>>> +COVERAGE_DEFINE(datapath_drop_tunnel_pop_error);
>>>> +COVERAGE_DEFINE(datapath_drop_recirc_error);
>>>> +COVERAGE_DEFINE(datapath_drop_invalid_port);
>>>> +COVERAGE_DEFINE(datapath_drop_invalid_tnl_port);
>>>> +COVERAGE_DEFINE(datapath_drop_rx_invalid_packet);
>>>> +
>>>>  /* Protects against changes to 'dp_netdevs'. */  static struct 
>>>> ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
>>>>
>>>> @@ -5647,6 +5658,7 @@ dp_netdev_run_meter(struct dp_netdev *dp, 
>>>> struct dp_packet_batch *packets_,
>>>>              band->packet_count += 1;
>>>>              band->byte_count += dp_packet_size(packet);
>>>>
>>>> +            COVERAGE_INC(datapath_drop_meter);
>>>>              dp_packet_delete(packet);
>>>>          } else {
>>>>              /* Meter accepts packet. */ @@ -6402,6 +6414,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(datapath_drop_rx_invalid_packet);
>>>>              continue;
>>>>          }
>>>>
>>>> @@ -6528,6 +6541,7 @@ handle_packet_upcall(struct 
>>>> dp_netdev_pmd_thread *pmd,
>>>>                               put_actions);
>>>>      if (OVS_UNLIKELY(error && error != ENOSPC)) {
>>>>          dp_packet_delete(packet);
>>>> +        COVERAGE_INC(datapath_drop_upcall_error);
>>>>          return error;
>>>>      }
>>>>
>>>> @@ -6659,6 +6673,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(datapath_drop_lock_error);
>>>>                  upcall_fail_cnt++;
>>>>              }
>>>>          }
>>>> @@ -6928,6 +6943,7 @@ dp_execute_userspace_action(struct
>>>> dp_netdev_pmd_thread *pmd,
>>>>                                    actions->data, actions->size);
>>>>      } else if (should_steal) {
>>>>          dp_packet_delete(packet);
>>>> +        COVERAGE_INC(datapath_drop_userspace_action_error);
>>>>      }
>>>>  }
>>>>
>>>> @@ -6942,6 +6958,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:
>>>> @@ -6983,6 +7000,9 @@ dp_execute_cb(void *aux_, struct 
>>>> dp_packet_batch *packets_,
>>>>                  dp_packet_batch_add(&p->output_pkts, packet);
>>>>              }
>>>>              return;
>>>> +        } else {
>>>> +            COVERAGE_ADD(datapath_drop_invalid_port,
>>>> +                            dp_packet_batch_size(packets_));
>>>>          }
>>>>          break;
>>>>
>>>> @@ -6992,10 +7012,16 @@ 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(datapath_drop_tunnel_push_error,
>>>> +                            dp_packet_batch_size(packets_));
>>>>              break;
>>>>          }
>>>>          dp_packet_batch_apply_cutlen(packets_);
>>>> -        push_tnl_action(pmd, a, packets_);
>>>> +        packet_count = dp_packet_batch_size(packets_);
>>>> +        if (push_tnl_action(pmd, a, packets_)) {
>>>> +            COVERAGE_ADD(datapath_drop_tunnel_push_error,
>>>> +                            packet_count);
>>>> +        }
>>>>          return;
>>>>
>>>>      case OVS_ACTION_ATTR_TUNNEL_POP:
>>>> @@ -7015,7 +7041,13 @@ 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(datapath_drop_tunnel_pop_error,
>>>> +                                     packet_dropped);
>>>> +                }
>>>>                  if (dp_packet_batch_is_empty(packets_)) {
>>>>                      return;
>>>>                  }
>>>> @@ -7030,6 +7062,11 @@ dp_execute_cb(void *aux_, struct 
>>>> dp_packet_batch *packets_,
>>>>                  (*depth)--;
>>>>                  return;
>>>>              }
>>>> +            COVERAGE_ADD(datapath_drop_invalid_tnl_port,
>>>> +                            dp_packet_batch_size(packets_));
>>>> +        } else {
>>>> +            COVERAGE_ADD(datapath_drop_recirc_error,
>>>> +                            dp_packet_batch_size(packets_));
>>>>          }
>>>>          break;
>>>>
>>>> @@ -7074,6 +7111,8 @@ dp_execute_cb(void *aux_, struct 
>>>> dp_packet_batch *packets_,
>>>>
>>>>              return;
>>>>          }
>>>> +        COVERAGE_ADD(datapath_drop_lock_error,
>>>> +                        dp_packet_batch_size(packets_));
>>>>          break;
>>>>
>>>>      case OVS_ACTION_ATTR_RECIRC:
>>>> @@ -7097,6 +7136,8 @@ dp_execute_cb(void *aux_, struct 
>>>> dp_packet_batch *packets_,
>>>>              return;
>>>>          }
>>>>
>>>> +        COVERAGE_ADD(datapath_drop_recirc_error,
>>>> +                        dp_packet_batch_size(packets_));
>>>>          VLOG_WARN("Packet dropped. Max recirculation depth 
>>>> exceeded.");
>>>>          break;
>>>>
>>>> @@ -7249,6 +7290,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();
>>>>      }
>>>> diff --git a/lib/dpif.c b/lib/dpif.c index 457c9bf..d75a012 100644
>>>> --- a/lib/dpif.c
>>>> +++ b/lib/dpif.c
>>>> @@ -1269,6 +1269,7 @@ dpif_execute_helper_cb(void *aux_, struct 
>>>> dp_packet_batch *packets_,
>>>>      case OVS_ACTION_ATTR_POP_NSH:
>>>>      case OVS_ACTION_ATTR_CT_CLEAR:
>>>>      case OVS_ACTION_ATTR_UNSPEC:
>>>> +    case OVS_ACTION_ATTR_DROP:
>>>>      case __OVS_ACTION_ATTR_MAX:
>>>>          OVS_NOT_REACHED();
>>>>      }
>>>> @@ -1874,6 +1875,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, 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/odp-execute.c b/lib/odp-execute.c index 
>>>> 5d07133..c8ff16b 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"
>>>> @@ -36,6 +37,74 @@
>>>>  #include "util.h"
>>>>  #include "csum.h"
>>>>  #include "conntrack.h"
>>>> +#include "ofproto/ofproto-dpif-xlate.h"
>>>> +#include "openvswitch/vlog.h"
>>>> +
>>>> +VLOG_DEFINE_THIS_MODULE(odp_execute)
>>>> +COVERAGE_DEFINE(dp_sample_error_drop);
>>>> +COVERAGE_DEFINE(dp_nsh_decap_error_drop);
>>>> +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);
>>>> +
>>>> +static void
>>>> +dp_update_drop_action_counter(enum xlate_error drop_reason,
>>>> +                                 int delta) {
>>>> +   static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
>>>> +
>>>> +   switch (drop_reason) {
>>>> +   case XLATE_OK:
>>>> +        COVERAGE_ADD(drop_action_of_pipeline, delta);
>>>> +        break;
>>>> +   case XLATE_BRIDGE_NOT_FOUND:
>>>> +        COVERAGE_ADD(drop_action_bridge_not_found, delta);
>>>> +        break;
>>>> +   case XLATE_RECURSION_TOO_DEEP:
>>>> +        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
>>>> +        break;
>>>> +   case XLATE_TOO_MANY_RESUBMITS:
>>>> +        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
>>>> +        break;
>>>> +   case XLATE_STACK_TOO_DEEP:
>>>> +        COVERAGE_ADD(drop_action_stack_too_deep, delta);
>>>> +        break;
>>>> +   case XLATE_NO_RECIRCULATION_CONTEXT:
>>>> +        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
>>>> +        break;
>>>> +   case XLATE_RECIRCULATION_CONFLICT:
>>>> +        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
>>>> +        break;
>>>> +   case XLATE_TOO_MANY_MPLS_LABELS:
>>>> +        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
>>>> +        break;
>>>> +   case XLATE_INVALID_TUNNEL_METADATA:
>>>> +        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
>>>> +        break;
>>>> +   case XLATE_UNSUPPORTED_PACKET_TYPE:
>>>> +        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
>>>> +        break;
>>>> +   case XLATE_CONGESTION_DROP:
>>>> +        COVERAGE_ADD(drop_action_congestion, delta);
>>>> +        break;
>>>> +   case XLATE_FORWARDING_DISABLED:
>>>> +        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
>>>> +        break;
>>>> +   case XLATE_MAX:
>>>> +   default:
>>>> +        VLOG_ERR_RL(&rl, "Invalid Drop reason type:%d",
>>>> drop_reason);
>>>> +   }
>>>> +}
>>>> +
>>>>
>>>>  /* Masked copy of an ethernet address. 'src' is already properly 
>>>> masked. */  static void @@ -621,6 +690,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;
>>>> @@ -705,6 +775,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:
>>>> @@ -921,6 +992,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);
>>>>                  }
>>>>              }
>>>> @@ -931,7 +1003,14 @@ odp_execute_actions(void *dp, struct 
>>>> dp_packet_batch *batch, bool steal,
>>>>                  conntrack_clear(packet);
>>>>              }
>>>>              break;
>>>> -
>>>> +        case OVS_ACTION_ATTR_DROP: {
>>>> +            const enum xlate_error *drop_reason = nl_attr_get(a);
>>>> +            if (*drop_reason < XLATE_MAX) {
>>>> +                 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-util.c b/lib/odp-util.c index e893f46..9c3acc1
>>>> 100644
>>>> --- a/lib/odp-util.c
>>>> +++ b/lib/odp-util.c
>>>> @@ -43,6 +43,7 @@
>>>>  #include "uuid.h"
>>>>  #include "openvswitch/vlog.h"
>>>>  #include "openvswitch/match.h"
>>>> +#include "ofproto/ofproto-dpif-xlate.h"
>>>>
>>>>  VLOG_DEFINE_THIS_MODULE(odp_util);
>>>>
>>>> @@ -131,6 +132,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(enum xlate_error);
>>>>
>>>>      case OVS_ACTION_ATTR_UNSPEC:
>>>>      case __OVS_ACTION_ATTR_MAX:
>>>> @@ -1182,6 +1184,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:
>>>> +        ds_put_cstr(ds, "drop");
>>>> +        break;
>>>>      case OVS_ACTION_ATTR_UNSPEC:
>>>>      case __OVS_ACTION_ATTR_MAX:
>>>>      default:
>>>> @@ -2428,8 +2433,12 @@ odp_actions_from_string(const char *s, const 
>>>> struct simap *port_names,
>>>>                          struct ofpbuf *actions)  {
>>>>      size_t old_size;
>>>> +    enum xlate_error drop_action;
>>>>
>>>>      if (!strcasecmp(s, "drop")) {
>>>> +        drop_action = XLATE_OK;
>>>> +        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 5ea1097..52b328d 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 bc4ffee..0d0a27d 100644
>>>> --- a/ofproto/ofproto-dpif-sflow.c
>>>> +++ b/ofproto/ofproto-dpif-sflow.c
>>>> @@ -1223,6 +1223,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-xlate.c 
>>>> b/ofproto/ofproto-dpif-xlate.c index acd4817..95e2f25 100644
>>>> --- a/ofproto/ofproto-dpif-xlate.c
>>>> +++ b/ofproto/ofproto-dpif-xlate.c
>>>> @@ -444,6 +444,12 @@ 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";
>>>> +    case XLATE_MAX:
>>>> +        break;
>>>>      }
>>>>      return "Unknown error";
>>>>  }
>>>> @@ -5921,6 +5927,14 @@ put_ct_label(const struct flow *flow, struct 
>>>> ofpbuf *odp_actions,  }
>>>>
>>>>  static void
>>>> +put_drop_action(struct ofpbuf *odp_actions, enum xlate_error 
>>>> +error) {
>>>> +    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_DROP,
>>>> +                      &error, sizeof error);
>>>> +
>>>> +}
>>>> +
>>>> +static void
>>>>  put_ct_helper(struct xlate_ctx *ctx,
>>>>                struct ofpbuf *odp_actions, struct ofpact_conntrack
>>>> *ofc)
>>>>  {
>>>> @@ -7383,48 +7397,51 @@ xlate_actions(struct xlate_in *xin, struct 
>>>> xlate_out *xout)
>>>>          }
>>>>          size_t sample_actions_len = ctx.odp_actions->size;
>>>>
>>>> -        if (tnl_process_ecn(flow)
>>>> -            && (!in_port || may_receive(in_port, &ctx))) {
>>>> -            const struct ofpact *ofpacts;
>>>> -            size_t ofpacts_len;
>>>> -
>>>> -            if (xin->ofpacts) {
>>>> -                ofpacts = xin->ofpacts;
>>>> -                ofpacts_len = xin->ofpacts_len;
>>>> -            } else if (ctx.rule) {
>>>> -                const struct rule_actions *actions
>>>> -                    = rule_get_actions(&ctx.rule->up);
>>>> -                ofpacts = actions->ofpacts;
>>>> -                ofpacts_len = actions->ofpacts_len;
>>>> -                ctx.rule_cookie = ctx.rule->up.flow_cookie;
>>>> -            } else {
>>>> -                OVS_NOT_REACHED();
>>>> -            }
>>>> +        if (!tnl_process_ecn(flow)) {
>>>> +            ctx.error = XLATE_CONGESTION_DROP;
>>>> +        } else {
>>>> +            if (!in_port || may_receive(in_port, &ctx)) {
>>>> +                const struct ofpact *ofpacts;
>>>> +                size_t ofpacts_len;
>>>> +
>>>> +                if (xin->ofpacts) {
>>>> +                    ofpacts = xin->ofpacts;
>>>> +                    ofpacts_len = xin->ofpacts_len;
>>>> +                } else if (ctx.rule) {
>>>> +                    const struct rule_actions *actions
>>>> +                         = rule_get_actions(&ctx.rule->up);
>>>> +                    ofpacts = actions->ofpacts;
>>>> +                    ofpacts_len = actions->ofpacts_len;
>>>> +                    ctx.rule_cookie = ctx.rule->up.flow_cookie;
>>>> +                } else {
>>>> +                    OVS_NOT_REACHED();
>>>> +                }
>>>>
>>>> -            mirror_ingress_packet(&ctx);
>>>> -            do_xlate_actions(ofpacts, ofpacts_len, &ctx, true, 
>>>> false);
>>>> -            if (ctx.error) {
>>>> -                goto exit;
>>>> -            }
>>>> +                mirror_ingress_packet(&ctx);
>>>> +                do_xlate_actions(ofpacts, ofpacts_len, &ctx, true,
>>>> false);
>>>> +                if (ctx.error) {
>>>> +                    goto exit;
>>>> +                }
>>>>
>>>> -            /* We've let OFPP_NORMAL and the learning action look at 
>>>> the
>>>> -             * packet, so cancel all actions and freezing if 
>>>> forwarding is
>>>> -             * disabled. */
>>>> -            if (in_port && (!xport_stp_forward_state(in_port) ||
>>>> -                            !xport_rstp_forward_state(in_port))) {
>>>> -                ctx.odp_actions->size = sample_actions_len;
>>>> -                ctx_cancel_freeze(&ctx);
>>>> -                ofpbuf_clear(&ctx.action_set);
>>>> -            }
>>>> +                /* We've let OFPP_NORMAL and the learning action
>>>> look at the
>>>> +                * packet, so cancel all actions and freezing if
>>>> forwarding is
>>>> +                * disabled. */
>>>> +                if (in_port && (!xport_stp_forward_state(in_port) 
>>>> + ||
>>>> +                                
>>>> + !xport_rstp_forward_state(in_port)))
>>>> {
>>>> +                    ctx.odp_actions->size = sample_actions_len;
>>>> +                    ctx_cancel_freeze(&ctx);
>>>> +                    ofpbuf_clear(&ctx.action_set);
>>>> +                    ctx.error = XLATE_FORWARDING_DISABLED;
>>>> +                }
>>>>
>>>> -            if (!ctx.freezing) {
>>>> -                xlate_action_set(&ctx);
>>>> -            }
>>>> -            if (ctx.freezing) {
>>>> -                finish_freezing(&ctx);
>>>> +                if (!ctx.freezing) {
>>>> +                    xlate_action_set(&ctx);
>>>> +                }
>>>> +                if (ctx.freezing) {
>>>> +                    finish_freezing(&ctx);
>>>> +                }
>>>>              }
>>>>          }
>>>> -
>>>>          /* Output only fully processed packets. */
>>>>          if (!ctx.freezing
>>>>              && xbridge->has_in_band @@ -7522,6 +7539,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..496bdbf 100644
>>>> --- a/ofproto/ofproto-dpif-xlate.h
>>>> +++ b/ofproto/ofproto-dpif-xlate.h
>>>> @@ -216,6 +216,9 @@ enum xlate_error {
>>>>      XLATE_TOO_MANY_MPLS_LABELS,
>>>>      XLATE_INVALID_TUNNEL_METADATA,
>>>>      XLATE_UNSUPPORTED_PACKET_TYPE,
>>>> +    XLATE_CONGESTION_DROP,
>>>> +    XLATE_FORWARDING_DISABLED,
>>>> +    XLATE_MAX,
>>>>  };
>>>>
>>>>  const char *xlate_strerror(enum xlate_error error); diff --git 
>>>> a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index
>>>> 50c959b..91df1b3 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); diff 
>>>> --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h index 
>>>> 1a404c8..8d8edca 100644
>>>> --- a/ofproto/ofproto-dpif.h
>>>> +++ b/ofproto/ofproto-dpif.h
>>>> @@ -192,7 +192,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 +364,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..a4da75e 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/dpif-netdev.at b/tests/dpif-netdev.at index
>>>> 6915d43..5221c10 100644
>>>> --- a/tests/dpif-netdev.at
>>>> +++ b/tests/dpif-netdev.at
>>>> @@ -337,6 +337,14 @@ 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
>>>> +
>>>> +AT_CHECK([
>>>> +ovs-appctl coverage/show | grep "datapath_drop_meter" | 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),ipv
>>>> 4
>>>> (frag=no),
>>>> actions:meter(0),7
>>>>  
>>>> recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth_type(0x0800),ipv
>>>> 4
>>>> (frag=no),
>>>> actions:8
>>>> diff --git a/tests/drop-stats.at b/tests/drop-stats.at new file 
>>>> mode
>>>> 100644 index 0000000..318bc02
>>>> --- /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
>>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1
>>>> e
>>>> 08006f200a4d0001fc509a580000000027150200000000001011121314151617181
>>>> 9
>>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>>> +    ovs-appctl netdev-dummy/receive p1
>>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1
>>>> e
>>>> 08006f200a4d0001fc509a580000000027150200000000001011121314151617181
>>>> 9
>>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>>> +    ovs-appctl netdev-dummy/receive p1
>>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1
>>>> e
>>>> 08006f200a4d0001fc509a580000000027150200000000001011121314151617181
>>>> 9
>>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>>> +], [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),ip
>>>> +v
>>>> +4(frag=no),
>>>> packets:2, bytes:196, used:0.0, actions: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
>>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1
>>>> e
>>>> 08006f200a4d0001fc509a580000000027150200000000001011121314151617181
>>>> 9
>>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>>> +], [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
>>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1
>>>> e
>>>> 08006f200a4d0001fc509a580000000027150200000000001011121314151617181
>>>> 9
>>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>>> +], [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
>>>> 1e2ce92a669e3a6dd2099cab0800450000548a53400040011addc0a80a0ac0a80a1
>>>> e
>>>> 08006f200a4d0001fc509a580000000027150200000000001011121314151617181
>>>> 9
>>>> 1a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
>>>> +], [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..2c8cdce 100644
>>>> --- a/tests/ofproto-dpif.at
>>>> +++ b/tests/ofproto-dpif.at
>>>> @@ -9384,7 +9384,7 @@
>>>> recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vla
>>>> n
>>>> (vid=99,pcp=
>>>>  # are wildcarded.
>>>>  AT_CHECK([grep '\(modify\)\|\(flow_add\)' ovs-vswitchd.log | 
>>>> strip_ufid ], [0], [dnl
>>>>  dpif_netdev|DBG|flow_add: 
>>>> recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x1234),
>>>> actions:100
>>>> -dpif|DBG|dummy at ovs-dummy: put[[modify]]
>>>> skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(
>>>> 0 
>>>> /0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(
>>>> n
>>>> s=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:
>>>> 0
>>>> 0:00:0a/00:00:00:00:00:00),eth_type(0x1234)
>>>> +dpif|DBG|dummy at ovs-dummy: put[[modify]]
>>>> skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(
>>>> 0 
>>>> /0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(
>>>> n
>>>> s=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:
>>>> 0 0:00:0a/00:00:00:00:00:00),eth_type(0x1234),
>>>> actions:drop
>>>>  dpif|DBG|dummy at ovs-dummy: put[[modify]]
>>>> skb_priority(0/0),skb_mark(0/0),ct_state(0/0),ct_zone(0/0),ct_mark(
>>>> 0 
>>>> /0),ct_label(0/0),recirc_id(0),dp_hash(0/0),in_port(1),packet_type(
>>>> n
>>>> s=0,id=0),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:
>>>> 0 0: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),vla
>>>> n (vid=99,pcp=7/0x0),encap(eth_type(0x1234)),
>>>> actions:drop
>>>>  ])
>>>> 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..045b1a7 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
>>>> 'aa55aa550000001b213cab6408004500007079464000402fba600101025c010102
>>>> 5
>>>> 820000800000001c845000054ba200000400184861e0000011e00000200004227e7
>>>> 5
>>>> 400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e
>>>> 1
>>>> f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>>> +
>>>> +ovs-appctl time/warp 5000
>>>> +
>>>> +AT_CHECK([
>>>> +ovs-appctl coverage/show | grep "datapath_drop_tunnel_pop_error" |
>>>> cut -d':' -f2|sed 's/ //'
>>>> +], [0], [dnl
>>>> +1
>>>> +])
>>>> +
>>>> +sleep 1
>>>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
>>>> 'aa55aa550000001b213cab6408004503007079464000402fba600101025c010102
>>>> 5
>>>> 820000800000001c845000054ba200000400184861e0000011e00000200004227e7
>>>> 5
>>>> 400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e
>>>> 1
>>>> f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>>> +
>>>> +ovs-appctl time/warp 5000
>>>> +
>>>> +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
>>>> 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c010102
>>>> 5
>>>> 820000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184
>>>> 8
>>>> 61e0000011e00000200004227e75400030af3195500000000f26501000000000010
>>>> 1
>>>> 112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132
>>>> 3
>>>> 334353637'])
>>>>
>>>> @@ -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 @@ -478,7
>>>> +499,7 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  5'],
>>>> [0], [dnl
>>>>    port  5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, 
>>>> crc=?
>>>>  ])
>>>>  AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 
>>>> 'in_port(6081)'], [0], [dnl 
>>>> -tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,
>>>> t 
>>>> ype=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+
>>>> k
>>>> ey)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0
>>>> 8
>>>> 00),ipv4(frag=no),
>>>> packets:0, bytes:0, used:never,
>>>> actions:userspace(pid=0,controller(reason=1,dont_send=0,continuatio
>>>> n
>>>> =0,recirc_id=3,rule_cookie=0,controller_id=0,max_len=65535))
>>>> +tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,
>>>> +t 
>>>> +ype=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum
>>>> ++ 
>>>> +key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0
>>>> +x
>>>> +0800),ipv4(frag=no),
>>>> packets:0, bytes:0, used:never,
>>>> actions:userspace(pid=0,controller(reason=1,dont_send=0,continuatio
>>>> n
>>>> =0,recirc_id=2,rule_cookie=0,controller_id=0,max_len=65535))
>>>>  ])
>>>>
>>>>  ovs-appctl time/warp 10000
>>>> @@ -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..7bd5b48 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),
>>>> e 
>>>> th(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ip
>>>> v 
>>>> 4(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
>>>> +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,16 @@ 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
>>>> 'aa55aa550001f8bc124434b6080045000054ba2000004001848601010358010103
>>>> 7
>>>> 001004227e75400030af3195500000000f265010000000000101112131415161718
>>>> 1
>>>> 91a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>>>> +
>>>> +sleep 2
>>>> +
>>>> +AT_CHECK([
>>>> +ovs-appctl coverage/show | grep "datapath_drop_invalid_port" | cut
>>>> -d':' -f2|sed 's/ //'
>>>> +], [0], [dnl
>>>> +1
>>>> +])
>>>>  OVS_VSWITCHD_STOP
>>>>  AT_CLEANUP
>>>>
>>>> --
>>>> 1.9.1
>>>>
>>>> _______________________________________________
>>>> dev mailing list
>>>> dev at openvswitch.org
>>>> https://protect2.fireeye.com/url?k=b1bddc0f-ed69d7a4-b1bd9c94-864b0
>>>> d 
>>>> 136b87-d394ae6811e94bab&q=1&u=https%3A%2F%2Fmail.openvswitch.org%2F
>>>> m
>>>> ailman%2Flistinfo%2Fovs-dev
diff mbox series

Patch

diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index d5aa09d..e77e9c8 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -946,6 +946,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,         /* Drop action. */
 
 #ifndef __KERNEL__
 	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 77ac1d2..acc7913 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -101,6 +101,17 @@  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(datapath_drop_meter);
+COVERAGE_DEFINE(datapath_drop_upcall_error);
+COVERAGE_DEFINE(datapath_drop_lock_error);
+COVERAGE_DEFINE(datapath_drop_userspace_action_error);
+COVERAGE_DEFINE(datapath_drop_tunnel_push_error);
+COVERAGE_DEFINE(datapath_drop_tunnel_pop_error);
+COVERAGE_DEFINE(datapath_drop_recirc_error);
+COVERAGE_DEFINE(datapath_drop_invalid_port);
+COVERAGE_DEFINE(datapath_drop_invalid_tnl_port);
+COVERAGE_DEFINE(datapath_drop_rx_invalid_packet);
+
 /* Protects against changes to 'dp_netdevs'. */
 static struct ovs_mutex dp_netdev_mutex = OVS_MUTEX_INITIALIZER;
 
@@ -5647,6 +5658,7 @@  dp_netdev_run_meter(struct dp_netdev *dp, struct dp_packet_batch *packets_,
             band->packet_count += 1;
             band->byte_count += dp_packet_size(packet);
 
+            COVERAGE_INC(datapath_drop_meter);
             dp_packet_delete(packet);
         } else {
             /* Meter accepts packet. */
@@ -6402,6 +6414,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(datapath_drop_rx_invalid_packet);
             continue;
         }
 
@@ -6528,6 +6541,7 @@  handle_packet_upcall(struct dp_netdev_pmd_thread *pmd,
                              put_actions);
     if (OVS_UNLIKELY(error && error != ENOSPC)) {
         dp_packet_delete(packet);
+        COVERAGE_INC(datapath_drop_upcall_error);
         return error;
     }
 
@@ -6659,6 +6673,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(datapath_drop_lock_error);
                 upcall_fail_cnt++;
             }
         }
@@ -6928,6 +6943,7 @@  dp_execute_userspace_action(struct dp_netdev_pmd_thread *pmd,
                                   actions->data, actions->size);
     } else if (should_steal) {
         dp_packet_delete(packet);
+        COVERAGE_INC(datapath_drop_userspace_action_error);
     }
 }
 
@@ -6942,6 +6958,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:
@@ -6983,6 +7000,9 @@  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
                 dp_packet_batch_add(&p->output_pkts, packet);
             }
             return;
+        } else {
+            COVERAGE_ADD(datapath_drop_invalid_port,
+                            dp_packet_batch_size(packets_));
         }
         break;
 
@@ -6992,10 +7012,16 @@  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(datapath_drop_tunnel_push_error,
+                            dp_packet_batch_size(packets_));
             break;
         }
         dp_packet_batch_apply_cutlen(packets_);
-        push_tnl_action(pmd, a, packets_);
+        packet_count = dp_packet_batch_size(packets_);
+        if (push_tnl_action(pmd, a, packets_)) {
+            COVERAGE_ADD(datapath_drop_tunnel_push_error,
+                            packet_count);
+        }
         return;
 
     case OVS_ACTION_ATTR_TUNNEL_POP:
@@ -7015,7 +7041,13 @@  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(datapath_drop_tunnel_pop_error,
+                                     packet_dropped);
+                }
                 if (dp_packet_batch_is_empty(packets_)) {
                     return;
                 }
@@ -7030,6 +7062,11 @@  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
                 (*depth)--;
                 return;
             }
+            COVERAGE_ADD(datapath_drop_invalid_tnl_port,
+                            dp_packet_batch_size(packets_));
+        } else {
+            COVERAGE_ADD(datapath_drop_recirc_error,
+                            dp_packet_batch_size(packets_));
         }
         break;
 
@@ -7074,6 +7111,8 @@  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
 
             return;
         }
+        COVERAGE_ADD(datapath_drop_lock_error,
+                        dp_packet_batch_size(packets_));
         break;
 
     case OVS_ACTION_ATTR_RECIRC:
@@ -7097,6 +7136,8 @@  dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
             return;
         }
 
+        COVERAGE_ADD(datapath_drop_recirc_error,
+                        dp_packet_batch_size(packets_));
         VLOG_WARN("Packet dropped. Max recirculation depth exceeded.");
         break;
 
@@ -7249,6 +7290,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();
     }
diff --git a/lib/dpif.c b/lib/dpif.c
index 457c9bf..d75a012 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1269,6 +1269,7 @@  dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
     case OVS_ACTION_ATTR_POP_NSH:
     case OVS_ACTION_ATTR_CT_CLEAR:
     case OVS_ACTION_ATTR_UNSPEC:
+    case OVS_ACTION_ATTR_DROP:
     case __OVS_ACTION_ATTR_MAX:
         OVS_NOT_REACHED();
     }
@@ -1874,6 +1875,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,
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/odp-execute.c b/lib/odp-execute.c
index 5d07133..c8ff16b 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"
@@ -36,6 +37,74 @@ 
 #include "util.h"
 #include "csum.h"
 #include "conntrack.h"
+#include "ofproto/ofproto-dpif-xlate.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(odp_execute)
+COVERAGE_DEFINE(dp_sample_error_drop);
+COVERAGE_DEFINE(dp_nsh_decap_error_drop);
+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);
+
+static void
+dp_update_drop_action_counter(enum xlate_error drop_reason,
+                                 int delta)
+{
+   static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+   switch (drop_reason) {
+   case XLATE_OK:
+        COVERAGE_ADD(drop_action_of_pipeline, delta);
+        break;
+   case XLATE_BRIDGE_NOT_FOUND:
+        COVERAGE_ADD(drop_action_bridge_not_found, delta);
+        break;
+   case XLATE_RECURSION_TOO_DEEP:
+        COVERAGE_ADD(drop_action_recursion_too_deep, delta);
+        break;
+   case XLATE_TOO_MANY_RESUBMITS:
+        COVERAGE_ADD(drop_action_too_many_resubmit, delta);
+        break;
+   case XLATE_STACK_TOO_DEEP:
+        COVERAGE_ADD(drop_action_stack_too_deep, delta);
+        break;
+   case XLATE_NO_RECIRCULATION_CONTEXT:
+        COVERAGE_ADD(drop_action_no_recirculation_context, delta);
+        break;
+   case XLATE_RECIRCULATION_CONFLICT:
+        COVERAGE_ADD(drop_action_recirculation_conflict, delta);
+        break;
+   case XLATE_TOO_MANY_MPLS_LABELS:
+        COVERAGE_ADD(drop_action_too_many_mpls_labels, delta);
+        break;
+   case XLATE_INVALID_TUNNEL_METADATA:
+        COVERAGE_ADD(drop_action_invalid_tunnel_metadata, delta);
+        break;
+   case XLATE_UNSUPPORTED_PACKET_TYPE:
+        COVERAGE_ADD(drop_action_unsupported_packet_type, delta);
+        break;
+   case XLATE_CONGESTION_DROP:
+        COVERAGE_ADD(drop_action_congestion, delta);
+        break;
+   case XLATE_FORWARDING_DISABLED:
+        COVERAGE_ADD(drop_action_forwarding_disabled, delta);
+        break;
+   case XLATE_MAX:
+   default:
+        VLOG_ERR_RL(&rl, "Invalid Drop reason type:%d", drop_reason);
+   }
+}
+
 
 /* Masked copy of an ethernet address. 'src' is already properly masked. */
 static void
@@ -621,6 +690,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;
@@ -705,6 +775,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:
@@ -921,6 +992,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);
                 }
             }
@@ -931,7 +1003,14 @@  odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
                 conntrack_clear(packet);
             }
             break;
-
+        case OVS_ACTION_ATTR_DROP: {
+            const enum xlate_error *drop_reason = nl_attr_get(a);
+            if (*drop_reason < XLATE_MAX) {
+                 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-util.c b/lib/odp-util.c
index e893f46..9c3acc1 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -43,6 +43,7 @@ 
 #include "uuid.h"
 #include "openvswitch/vlog.h"
 #include "openvswitch/match.h"
+#include "ofproto/ofproto-dpif-xlate.h"
 
 VLOG_DEFINE_THIS_MODULE(odp_util);
 
@@ -131,6 +132,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(enum xlate_error);
 
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
@@ -1182,6 +1184,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:
+        ds_put_cstr(ds, "drop");
+        break;
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
     default:
@@ -2428,8 +2433,12 @@  odp_actions_from_string(const char *s, const struct simap *port_names,
                         struct ofpbuf *actions)
 {
     size_t old_size;
+    enum xlate_error drop_action;
 
     if (!strcasecmp(s, "drop")) {
+        drop_action = XLATE_OK;
+        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 5ea1097..52b328d 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 bc4ffee..0d0a27d 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -1223,6 +1223,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-xlate.c b/ofproto/ofproto-dpif-xlate.c
index acd4817..95e2f25 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -444,6 +444,12 @@  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";
+    case XLATE_MAX:
+        break;
     }
     return "Unknown error";
 }
@@ -5921,6 +5927,14 @@  put_ct_label(const struct flow *flow, struct ofpbuf *odp_actions,
 }
 
 static void
+put_drop_action(struct ofpbuf *odp_actions, enum xlate_error error)
+{
+    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_DROP,
+                      &error, sizeof error);
+
+}
+
+static void
 put_ct_helper(struct xlate_ctx *ctx,
               struct ofpbuf *odp_actions, struct ofpact_conntrack *ofc)
 {
@@ -7383,48 +7397,51 @@  xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
         }
         size_t sample_actions_len = ctx.odp_actions->size;
 
-        if (tnl_process_ecn(flow)
-            && (!in_port || may_receive(in_port, &ctx))) {
-            const struct ofpact *ofpacts;
-            size_t ofpacts_len;
-
-            if (xin->ofpacts) {
-                ofpacts = xin->ofpacts;
-                ofpacts_len = xin->ofpacts_len;
-            } else if (ctx.rule) {
-                const struct rule_actions *actions
-                    = rule_get_actions(&ctx.rule->up);
-                ofpacts = actions->ofpacts;
-                ofpacts_len = actions->ofpacts_len;
-                ctx.rule_cookie = ctx.rule->up.flow_cookie;
-            } else {
-                OVS_NOT_REACHED();
-            }
+        if (!tnl_process_ecn(flow)) {
+            ctx.error = XLATE_CONGESTION_DROP;
+        } else {
+            if (!in_port || may_receive(in_port, &ctx)) {
+                const struct ofpact *ofpacts;
+                size_t ofpacts_len;
+
+                if (xin->ofpacts) {
+                    ofpacts = xin->ofpacts;
+                    ofpacts_len = xin->ofpacts_len;
+                } else if (ctx.rule) {
+                    const struct rule_actions *actions
+                         = rule_get_actions(&ctx.rule->up);
+                    ofpacts = actions->ofpacts;
+                    ofpacts_len = actions->ofpacts_len;
+                    ctx.rule_cookie = ctx.rule->up.flow_cookie;
+                } else {
+                    OVS_NOT_REACHED();
+                }
 
-            mirror_ingress_packet(&ctx);
-            do_xlate_actions(ofpacts, ofpacts_len, &ctx, true, false);
-            if (ctx.error) {
-                goto exit;
-            }
+                mirror_ingress_packet(&ctx);
+                do_xlate_actions(ofpacts, ofpacts_len, &ctx, true, false);
+                if (ctx.error) {
+                    goto exit;
+                }
 
-            /* We've let OFPP_NORMAL and the learning action look at the
-             * packet, so cancel all actions and freezing if forwarding is
-             * disabled. */
-            if (in_port && (!xport_stp_forward_state(in_port) ||
-                            !xport_rstp_forward_state(in_port))) {
-                ctx.odp_actions->size = sample_actions_len;
-                ctx_cancel_freeze(&ctx);
-                ofpbuf_clear(&ctx.action_set);
-            }
+                /* We've let OFPP_NORMAL and the learning action look at the
+                * packet, so cancel all actions and freezing if forwarding is
+                * disabled. */
+                if (in_port && (!xport_stp_forward_state(in_port) ||
+                                !xport_rstp_forward_state(in_port))) {
+                    ctx.odp_actions->size = sample_actions_len;
+                    ctx_cancel_freeze(&ctx);
+                    ofpbuf_clear(&ctx.action_set);
+                    ctx.error = XLATE_FORWARDING_DISABLED;
+                }
 
-            if (!ctx.freezing) {
-                xlate_action_set(&ctx);
-            }
-            if (ctx.freezing) {
-                finish_freezing(&ctx);
+                if (!ctx.freezing) {
+                    xlate_action_set(&ctx);
+                }
+                if (ctx.freezing) {
+                    finish_freezing(&ctx);
+                }
             }
         }
-
         /* Output only fully processed packets. */
         if (!ctx.freezing
             && xbridge->has_in_band
@@ -7522,6 +7539,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..496bdbf 100644
--- a/ofproto/ofproto-dpif-xlate.h
+++ b/ofproto/ofproto-dpif-xlate.h
@@ -216,6 +216,9 @@  enum xlate_error {
     XLATE_TOO_MANY_MPLS_LABELS,
     XLATE_INVALID_TUNNEL_METADATA,
     XLATE_UNSUPPORTED_PACKET_TYPE,
+    XLATE_CONGESTION_DROP,
+    XLATE_FORWARDING_DISABLED,
+    XLATE_MAX,
 };
 
 const char *xlate_strerror(enum xlate_error error);
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 50c959b..91df1b3 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);
diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
index 1a404c8..8d8edca 100644
--- a/ofproto/ofproto-dpif.h
+++ b/ofproto/ofproto-dpif.h
@@ -192,7 +192,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 +364,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..a4da75e 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/dpif-netdev.at b/tests/dpif-netdev.at
index 6915d43..5221c10 100644
--- a/tests/dpif-netdev.at
+++ b/tests/dpif-netdev.at
@@ -337,6 +337,14 @@  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
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "datapath_drop_meter" | 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..318bc02
--- /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
+])
+
+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..2c8cdce 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -9384,7 +9384,7 @@  recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=99,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
 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
 ])
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..045b1a7 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 5000
+
+AT_CHECK([
+ovs-appctl coverage/show | grep "datapath_drop_tunnel_pop_error" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+1
+])
+
+sleep 1
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004503007079464000402fba600101025c0101025820000800000001c845000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
+
+ovs-appctl time/warp 5000
+
+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
@@ -478,7 +499,7 @@  AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  5'], [0], [dnl
   port  5: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=?
 ])
 AT_CHECK([ovs-appctl dpif/dump-flows int-br | grep 'in_port(6081)'], [0], [dnl
-tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=3,rule_cookie=0,controller_id=0,max_len=65535))
+tunnel(tun_id=0x7b,src=1.1.2.92,dst=1.1.2.88,geneve({class=0xffff,type=0x80,len=4,0xa/0xf}{class=0xffff,type=0,len=4}),flags(-df-csum+key)),recirc_id(0),in_port(6081),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:0, bytes:0, used:never, actions:userspace(pid=0,controller(reason=1,dont_send=0,continuation=0,recirc_id=2,rule_cookie=0,controller_id=0,max_len=65535))
 ])
 
 ovs-appctl time/warp 10000
@@ -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..7bd5b48 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
+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,16 @@  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 "datapath_drop_invalid_port" | cut -d':' -f2|sed 's/ //'
+], [0], [dnl
+1
+])
 OVS_VSWITCHD_STOP
 AT_CLEANUP