Signed-off-by: Jarno Rajahalme <jarno.rajaha...@nsn.com>
---
 include/linux/openvswitch.h  |    3 ++
 lib/dpif-linux.c             |   40 +++++++++++++++++++
 lib/dpif-netdev.c            |   43 ++++++++++++++++++++-
 lib/dpif-provider.h          |   28 ++++++++++++++
 lib/dpif.c                   |   87 ++++++++++++++++++++++++++++++++++++++++++
 lib/dpif.h                   |   11 ++++++
 lib/odp-execute.c            |   20 ++++++++--
 lib/odp-execute.h            |    5 ++-
 lib/odp-util.c               |   14 +++++++
 ofproto/ofproto-dpif-xlate.c |   16 +++++++-
 ofproto/ofproto-dpif.c       |   45 ++++++++++++++++------
 11 files changed, 293 insertions(+), 19 deletions(-)

diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h
index e890fd8..ddca0ac 100644
--- a/include/linux/openvswitch.h
+++ b/include/linux/openvswitch.h
@@ -516,6 +516,8 @@ struct ovs_action_push_vlan {
  * indicate the new packet contents This could potentially still be
  * %ETH_P_MPLS_* if the resulting MPLS label stack is not empty.  If there
  * is no MPLS label stack, as determined by ethertype, no action is taken.
+ * @OVS_ACTION_ATTR_METER: Run packet through a meter, which may drop the
+ * packet, or execute further actions on the packet.
  *
  * Only a single header can be set with a single %OVS_ACTION_ATTR_SET.  Not all
  * fields within a header are modifiable, e.g. the IPv4 protocol and fragment
@@ -532,6 +534,7 @@ enum ovs_action_attr {
        OVS_ACTION_ATTR_SAMPLE,       /* Nested OVS_SAMPLE_ATTR_*. */
        OVS_ACTION_ATTR_PUSH_MPLS,    /* struct ovs_action_push_mpls. */
        OVS_ACTION_ATTR_POP_MPLS,     /* __be16 ethertype. */
+       OVS_ACTION_ATTR_METER,        /* u32 meter number. */
        __OVS_ACTION_ATTR_MAX
 };
 
diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c
index 1383b58..3af99ef 100644
--- a/lib/dpif-linux.c
+++ b/lib/dpif-linux.c
@@ -1386,6 +1386,42 @@ dpif_linux_recv_purge(struct dpif *dpif_)
         }
     }
 }
+
+/* Meters */
+static void
+dpif_linux_meter_get_features(const struct dpif * dpif OVS_UNUSED,
+                              struct ofputil_meter_features *features)
+{
+    features->max_meters = 0;
+    features->band_types = 0;
+    features->capabilities = 0;
+    features->max_bands = 0;
+    features->max_color = 0;
+}
+
+static int
+dpif_linux_meter_set(struct dpif *dpif OVS_UNUSED,
+                     ofproto_meter_id *meter_id OVS_UNUSED,
+                     const struct ofputil_meter_config *config OVS_UNUSED)
+{
+    return EFBIG; /* meter_id out of range */
+}
+
+static int
+dpif_linux_meter_get(const struct dpif *dpif OVS_UNUSED,
+                     ofproto_meter_id meter_id OVS_UNUSED,
+                     struct ofputil_meter_stats *stats OVS_UNUSED)
+{
+    return EFBIG; /* meter_id out of range */
+}
+
+static int
+dpif_linux_meter_del(struct dpif *dpif OVS_UNUSED,
+                     ofproto_meter_id meter_id OVS_UNUSED,
+                     struct ofputil_meter_stats *stats OVS_UNUSED)
+{
+    return EFBIG; /* meter_id out of range */
+}
 
 const struct dpif_class dpif_linux_class = {
     "system",
@@ -1422,6 +1458,10 @@ const struct dpif_class dpif_linux_class = {
     dpif_linux_recv,
     dpif_linux_recv_wait,
     dpif_linux_recv_purge,
+    dpif_linux_meter_get_features,
+    dpif_linux_meter_set,
+    dpif_linux_meter_get,
+    dpif_linux_meter_del,
 };
 
 static int
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 52aedb6..1f57917 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -1027,6 +1027,42 @@ dpif_netdev_recv_purge(struct dpif *dpif)
     dp_netdev_purge_queues(dpif_netdev->dp);
 }
 
+/* Meters */
+static void
+dpif_netdev_meter_get_features(const struct dpif * dpif OVS_UNUSED,
+                               struct ofputil_meter_features *features)
+{
+    features->max_meters = 0;
+    features->band_types = 0;
+    features->capabilities = 0;
+    features->max_bands = 0;
+    features->max_color = 0;
+}
+
+static int
+dpif_netdev_meter_set(struct dpif *dpif OVS_UNUSED,
+                      ofproto_meter_id *meter_id OVS_UNUSED,
+                      const struct ofputil_meter_config *config OVS_UNUSED)
+{
+    return EFBIG; /* meter_id out of range */
+}
+
+static int
+dpif_netdev_meter_get(const struct dpif *dpif OVS_UNUSED,
+                      ofproto_meter_id meter_id OVS_UNUSED,
+                      struct ofputil_meter_stats *stats OVS_UNUSED)
+{
+    return EFBIG; /* meter_id out of range */
+}
+
+static int
+dpif_netdev_meter_del(struct dpif *dpif OVS_UNUSED,
+                      ofproto_meter_id meter_id OVS_UNUSED,
+                      struct ofputil_meter_stats *stats OVS_UNUSED)
+{
+    return EFBIG; /* meter_id out of range */
+}
+
 static void
 dp_netdev_flow_used(struct dp_netdev_flow *flow, const struct ofpbuf *packet)
 {
@@ -1176,7 +1212,8 @@ dp_netdev_execute_actions(struct dp_netdev *dp,
                           size_t actions_len)
 {
     odp_execute_actions(dp, packet, key, actions, actions_len,
-                        dp_netdev_output_port, dp_netdev_action_userspace);
+                        dp_netdev_output_port, dp_netdev_action_userspace,
+                        NULL);
 }
 
 const struct dpif_class dpif_netdev_class = {
@@ -1214,6 +1251,10 @@ const struct dpif_class dpif_netdev_class = {
     dpif_netdev_recv,
     dpif_netdev_recv_wait,
     dpif_netdev_recv_purge,
+    dpif_netdev_meter_get_features,
+    dpif_netdev_meter_set,
+    dpif_netdev_meter_get,
+    dpif_netdev_meter_del,
 };
 
 static void
diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
index bea822f..18ca75d 100644
--- a/lib/dpif-provider.h
+++ b/lib/dpif-provider.h
@@ -348,6 +348,34 @@ struct dpif_class {
     /* Throws away any queued upcalls that 'dpif' currently has ready to
      * return. */
     void (*recv_purge)(struct dpif *dpif);
+
+    /* Meters */
+
+    /* Queries 'dpif' for supported meter features.
+     * NULL pointer means no meter features are supported. */
+    void (*meter_get_features)(const struct dpif *,
+                               struct ofputil_meter_features *);
+
+    /* Adds or modifies 'meter' in 'dpif'.   If '*meter_id' is UINT32_MAX,
+     * adds a new meter, otherwise modifies an existing meter.
+     *
+     * If meter is successfully added, sets '*meter_id' to the new meter's
+     * meter id selected by 'dpif'. */
+    int (*meter_set)(struct dpif *, ofproto_meter_id *meter_id,
+                     const struct ofputil_meter_config *);
+
+    /* Queries 'dpif' for meter stats with the given 'meter_id'.
+     * Stores maximum of 'stats->n_bands' meter statistics, returning the
+     * number of band stats returned in 'stats->n_bands' if successful. */
+    int (*meter_get)(const struct dpif *, ofproto_meter_id meter_id,
+                     struct ofputil_meter_stats *);
+
+    /* Removes meter 'meter_id' from 'dpif'. Stores meter and band
+     * statistics (for maximum of 'stats->n_bands', returning the number of
+     * band stats returned in 'stats->n_bands' if successful.
+     * 'stats' may be passed in as NULL if no stats are needed. */
+    int (*meter_del)(struct dpif *, ofproto_meter_id meter_id,
+                     struct ofputil_meter_stats *);
 };
 
 extern const struct dpif_class dpif_linux_class;
diff --git a/lib/dpif.c b/lib/dpif.c
index 6aa52d5..57097cd 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -55,6 +55,9 @@ COVERAGE_DEFINE(dpif_flow_query_list);
 COVERAGE_DEFINE(dpif_flow_query_list_n);
 COVERAGE_DEFINE(dpif_execute);
 COVERAGE_DEFINE(dpif_purge);
+COVERAGE_DEFINE(dpif_meter_set);
+COVERAGE_DEFINE(dpif_meter_get);
+COVERAGE_DEFINE(dpif_meter_del);
 
 static const struct dpif_class *base_dpif_classes[] = {
 #ifdef LINUX_DATAPATH
@@ -1172,6 +1175,90 @@ dpif_recv_wait(struct dpif *dpif)
     dpif->dpif_class->recv_wait(dpif);
 }
 
+/* Meters */
+void
+dpif_meter_get_features(const struct dpif *dpif,
+                        struct ofputil_meter_features *features)
+{
+    memset(features, 0, sizeof *features);
+    if (dpif->dpif_class->meter_get_features) {
+        dpif->dpif_class->meter_get_features(dpif, features);
+    }
+}
+
+/* Adds or modifies 'meter' in 'dpif'.   If '*meter_id' is UINT32_MAX,
+ * adds a new meter, otherwise modifies an existing meter.
+ *
+ * If meter is successfully added, sets '*meter_id' to the new meter's
+ * meter number. */
+int
+dpif_meter_set(struct dpif *dpif, ofproto_meter_id *meter_id,
+               const struct ofputil_meter_config * config)
+{
+    int error;
+
+    COVERAGE_INC(dpif_meter_set);
+
+    error = dpif->dpif_class->meter_set(dpif, meter_id, config);
+    if (!error) {
+        VLOG_DBG_RL(&dpmsg_rl, "%s: DPIF meter %"PRIu32" set",
+                    dpif_name(dpif), meter_id->uint32);
+    } else {
+        VLOG_WARN_RL(&error_rl, "%s: failed to set DPIF meter %"PRIu32": %s",
+                     dpif_name(dpif), meter_id->uint32, strerror(error));
+        meter_id->uint32 = UINT32_MAX;
+    }
+    return error;
+}
+
+int
+dpif_meter_get(const struct dpif *dpif, ofproto_meter_id meter_id,
+               struct ofputil_meter_stats *stats)
+{
+    int error;
+
+    COVERAGE_INC(dpif_meter_get);
+
+    error = dpif->dpif_class->meter_get(dpif, meter_id, stats);
+    if (!error) {
+        VLOG_DBG_RL(&dpmsg_rl, "%s: DPIF meter %"PRIu32" get stats",
+                    dpif_name(dpif), meter_id.uint32);
+    } else {
+        VLOG_WARN_RL(&error_rl,
+                     "%s: failed to get DPIF meter %"PRIu32" stats: %s",
+                     dpif_name(dpif), meter_id.uint32, strerror(error));
+        stats->packet_in_count = ~0;
+        stats->byte_in_count = ~0;
+        stats->n_bands = 0;
+    }
+    return error;
+}
+
+int
+dpif_meter_del(struct dpif *dpif, ofproto_meter_id meter_id,
+               struct ofputil_meter_stats *stats)
+{
+    int error;
+
+    COVERAGE_INC(dpif_meter_del);
+
+    error = dpif->dpif_class->meter_del(dpif, meter_id, stats);
+    if (!error) {
+        VLOG_DBG_RL(&dpmsg_rl, "%s: DPIF meter %"PRIu32" deleted",
+                    dpif_name(dpif), meter_id.uint32);
+    } else {
+        VLOG_WARN_RL(&error_rl,
+                     "%s: failed to delete DPIF meter %"PRIu32": %s",
+                     dpif_name(dpif), meter_id.uint32, strerror(error));
+        if (stats) {
+            stats->packet_in_count = ~0;
+            stats->byte_in_count = ~0;
+            stats->n_bands = 0;
+        }
+    }
+    return error;
+}
+
 /* Obtains the NetFlow engine type and engine ID for 'dpif' into '*engine_type'
  * and '*engine_id', respectively. */
 void
diff --git a/lib/dpif.h b/lib/dpif.h
index fd05b2f..21088ce 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -324,6 +324,7 @@
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
+#include "ofp-util.h"
 #include "openflow/openflow.h"
 #include "netdev.h"
 #include "util.h"
@@ -561,6 +562,16 @@ int dpif_recv(struct dpif *, struct dpif_upcall *, struct 
ofpbuf *);
 void dpif_recv_purge(struct dpif *);
 void dpif_recv_wait(struct dpif *);
 
+/* Meters. */
+void dpif_meter_get_features(const struct dpif *,
+                             struct ofputil_meter_features *);
+int dpif_meter_set(struct dpif *, ofproto_meter_id *meter_id,
+                   const struct ofputil_meter_config *);
+int dpif_meter_get(const struct dpif *, ofproto_meter_id meter_id,
+                   struct ofputil_meter_stats *);
+int dpif_meter_del(struct dpif *, ofproto_meter_id meter_id,
+                   struct ofputil_meter_stats *);
+
 /* Miscellaneous. */
 
 void dpif_get_netflow_ids(const struct dpif *,
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index e6e8c91..d1243cc 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -25,6 +25,7 @@
 #include "ofpbuf.h"
 #include "odp-util.h"
 #include "packets.h"
+#include "timeval.h"
 #include "util.h"
 
 static void
@@ -122,7 +123,9 @@ odp_execute_sample(void *dp, struct ofpbuf *packet, struct 
flow *key,
                                   uint32_t out_port),
                    void (*userspace)(void *dp, struct ofpbuf *packet,
                                      const struct flow *key,
-                                     const struct nlattr *a))
+                                     const struct nlattr *a),
+                   bool (*meter)(void *dp_, struct ofpbuf *packet,
+                                 uint32_t meter_id, long long int now))
 {
     const struct nlattr *subactions = NULL;
     const struct nlattr *a;
@@ -150,7 +153,8 @@ odp_execute_sample(void *dp, struct ofpbuf *packet, struct 
flow *key,
     }
 
     odp_execute_actions(dp, packet, key, nl_attr_get(subactions),
-                        nl_attr_get_size(subactions), output, userspace);
+                        nl_attr_get_size(subactions), output, userspace,
+                        meter);
 }
 
 void
@@ -160,7 +164,9 @@ odp_execute_actions(void *dp, struct ofpbuf *packet, struct 
flow *key,
                                    uint32_t out_port),
                     void (*userspace)(void *dp, struct ofpbuf *packet,
                                       const struct flow *key,
-                                      const struct nlattr *a))
+                                      const struct nlattr *a),
+                   bool (*meter)(void *dp_, struct ofpbuf *packet,
+                                  uint32_t meter_id, long long int now))
 {
     const struct nlattr *a;
     unsigned int left;
@@ -175,6 +181,12 @@ odp_execute_actions(void *dp, struct ofpbuf *packet, 
struct flow *key,
             }
             break;
 
+        case OVS_ACTION_ATTR_METER:
+            if (meter && meter(dp, packet, nl_attr_get_u32(a), time_msec())) {
+                return;  /* Drop */
+            }
+            break;
+
         case OVS_ACTION_ATTR_USERSPACE: {
             const struct nlattr *userdata;
 
@@ -208,7 +220,7 @@ odp_execute_actions(void *dp, struct ofpbuf *packet, struct 
flow *key,
             break;
 
         case OVS_ACTION_ATTR_SAMPLE:
-            odp_execute_sample(dp, packet, key, a, output, userspace);
+            odp_execute_sample(dp, packet, key, a, output, userspace, meter);
             break;
 
         case OVS_ACTION_ATTR_UNSPEC:
diff --git a/lib/odp-execute.h b/lib/odp-execute.h
index 89dd66b..8c4215e 100644
--- a/lib/odp-execute.h
+++ b/lib/odp-execute.h
@@ -18,6 +18,7 @@
 #ifndef EXECUTE_ACTIONS_H
 #define EXECUTE_ACTIONS_H 1
 
+#include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
 
@@ -32,5 +33,7 @@ odp_execute_actions(void *dp, struct ofpbuf *packet, struct 
flow *key,
                                    uint32_t out_port),
                     void (*userspace)(void *dp, struct ofpbuf *packet,
                                       const struct flow *key,
-                                      const struct nlattr *a));
+                                      const struct nlattr *a),
+                   bool (*meter)(void *dp_, struct ofpbuf *packet,
+                                 uint32_t meter_id, long long int now));
 #endif
diff --git a/lib/odp-util.c b/lib/odp-util.c
index c4127ea..57d66ca 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -70,6 +70,7 @@ odp_action_len(uint16_t type)
 
     switch ((enum ovs_action_attr) type) {
     case OVS_ACTION_ATTR_OUTPUT: return sizeof(uint32_t);
+    case OVS_ACTION_ATTR_METER: return sizeof(uint32_t);
     case OVS_ACTION_ATTR_USERSPACE: return -2;
     case OVS_ACTION_ATTR_PUSH_VLAN: return sizeof(struct ovs_action_push_vlan);
     case OVS_ACTION_ATTR_POP_VLAN: return 0;
@@ -370,6 +371,9 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
     }
 
     switch (type) {
+    case OVS_ACTION_ATTR_METER:
+        ds_put_format(ds, "meter(%"PRIu32")", nl_attr_get_u32(a));
+        break;
     case OVS_ACTION_ATTR_OUTPUT:
         ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
         break;
@@ -623,6 +627,16 @@ parse_odp_action(const char *s, const struct simap 
*port_names,
     }
 
     {
+        unsigned long long int meter_id;
+        int n = -1;
+
+        if (sscanf(s, "meter(%lli)%n", &meter_id, &n) > 0 && n > 0) {
+            nl_msg_put_u32(actions, OVS_ACTION_ATTR_METER, meter_id);
+            return n;
+        }
+    }
+
+    {
         double percentage;
         int n = -1;
 
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index c742154..ed724c6 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -1123,7 +1123,7 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
                        &ctx->xout->odp_actions, &ctx->xout->wc);
 
     odp_execute_actions(NULL, packet, &key, ctx->xout->odp_actions.data,
-                        ctx->xout->odp_actions.size, NULL, NULL);
+                        ctx->xout->odp_actions.size, NULL, NULL, NULL);
 
     pin.packet = packet->data;
     pin.packet_len = packet->size;
@@ -1480,6 +1480,18 @@ xlate_sample_action(struct xlate_ctx *ctx,
                         probability, &cookie, sizeof cookie.flow_sample);
 }
 
+
+static void
+xlate_meter_action(struct xlate_ctx *ctx,
+                   struct ofpact_meter *meter)
+{
+    uint32_t dp_mid = ofproto_get_provider_meter_id(&ctx->ofproto->up,
+                                                    meter->meter_id);
+    if (dp_mid != UINT32_MAX) {
+        nl_msg_put_u32(&ctx->xout->odp_actions, OVS_ACTION_ATTR_METER, dp_mid);
+    }
+}
+
 static bool
 may_receive(const struct ofport_dpif *port, struct xlate_ctx *ctx)
 {
@@ -1738,7 +1750,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
             break;
 
         case OFPACT_METER:
-            /* Not implemented yet. */
+            xlate_meter_action(ctx, ofpact_get_METER(a));
             break;
 
         case OFPACT_GOTO_TABLE: {
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 7afce20..db1f828 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -6606,32 +6606,55 @@ vsp_add(struct ofport_dpif *port, uint16_t 
realdev_ofp_port, int vid)
 }
 
 static void
-meter_get_features(const struct ofproto *ofproto_ OVS_UNUSED,
+meter_get_features(const struct ofproto *ofproto_,
                    struct ofputil_meter_features *features)
 {
-    memset(features, 0, sizeof *features);
+    const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+    dpif_meter_get_features(ofproto->backer->dpif, features);
 }
 
 static enum ofperr
-meter_set(struct ofproto *ofproto_ OVS_UNUSED,
-          ofproto_meter_id *meter_id OVS_UNUSED,
-          const struct ofputil_meter_config *config OVS_UNUSED)
+meter_set(struct ofproto *ofproto_, ofproto_meter_id *meter_id,
+          const struct ofputil_meter_config *config)
 {
-    return OFPERR_OFPMMFC_OUT_OF_METERS;
+    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+    switch (dpif_meter_set(ofproto->backer->dpif, meter_id, config)) {
+    case 0:
+        return 0;
+    case EFBIG: /* meter_id out of range */
+    case ENOMEM: /* Cannot allocate meter */
+        return OFPERR_OFPMMFC_OUT_OF_METERS;
+    case EBADF: /* Unsupported flags */
+        return OFPERR_OFPMMFC_BAD_FLAGS;
+    case EINVAL: /* Too many bands */
+        return OFPERR_OFPMMFC_OUT_OF_BANDS;
+    case ENODEV: /* Unsupported band type */
+        return OFPERR_OFPMMFC_BAD_BAND;
+    default:
+        return OFPERR_OFPMMFC_UNKNOWN;
+    }
 }
 
 static enum ofperr
-meter_get(const struct ofproto *ofproto_ OVS_UNUSED,
-          ofproto_meter_id meter_id OVS_UNUSED,
-          struct ofputil_meter_stats *stats OVS_UNUSED)
+meter_get(const struct ofproto *ofproto_, ofproto_meter_id meter_id,
+          struct ofputil_meter_stats *stats)
 {
+    const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+    if (!dpif_meter_get(ofproto->backer->dpif, meter_id, stats)) {
+        return 0;
+    }
     return OFPERR_OFPMMFC_UNKNOWN_METER;
 }
 
 static void
-meter_del(struct ofproto *ofproto_ OVS_UNUSED,
-          ofproto_meter_id meter_id OVS_UNUSED)
+meter_del(struct ofproto *ofproto_, ofproto_meter_id meter_id)
 {
+    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+    dpif_meter_del(ofproto->backer->dpif, meter_id, NULL);
 }
 
 uint32_t
-- 
1.7.10.4

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to