[ovs-dev] [PATCH 06/12] dpif: Meter framework.
Jarno Rajahalme
jrajahalme at nicira.com
Fri Nov 8 18:54:38 UTC 2013
Add DPIF-level support for meters. Allow meter_set to modify the meter
configuration (e.g. set the burst size if unspecified).
Summary:
v2: Rebase.
Signed-off-by: Jarno Rajahalme <jrajahalme at nicira.com>
---
include/linux/openvswitch.h | 3 ++
lib/dpif-linux.c | 40 ++++++++++++++++++++
lib/dpif-netdev.c | 41 ++++++++++++++++++++
lib/dpif-provider.h | 28 ++++++++++++++
lib/dpif.c | 88 +++++++++++++++++++++++++++++++++++++++++++
lib/dpif.h | 11 ++++++
lib/odp-execute.c | 4 ++
lib/odp-util.c | 14 +++++++
ofproto/ofproto-dpif.c | 60 +++++++++++++++++++++++++++--
ofproto/ofproto-provider.h | 2 +-
10 files changed, 286 insertions(+), 5 deletions(-)
diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h
index b429201..50070ad 100644
--- a/include/linux/openvswitch.h
+++ b/include/linux/openvswitch.h
@@ -541,6 +541,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
@@ -557,6 +559,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 d61226b..1556ca2 100644
--- a/lib/dpif-linux.c
+++ b/lib/dpif-linux.c
@@ -1510,7 +1510,43 @@ dpif_linux_recv_purge(struct dpif *dpif_)
}
ovs_mutex_unlock(&dpif->upcall_lock);
}
+
+/* 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,
+ 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",
dpif_linux_enumerate,
@@ -1546,6 +1582,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 ac81461..975fd60 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -1127,6 +1127,42 @@ dpif_netdev_recv_purge(struct dpif *dpif)
ovs_mutex_unlock(&dp_netdev_mutex);
}
+/* 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,
+ 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 *netdev_flow,
const struct ofpbuf *packet)
@@ -1309,6 +1345,7 @@ dp_execute_cb(void *aux_, struct ofpbuf *packet,
userdata);
break;
}
+ case OVS_ACTION_ATTR_METER:
case OVS_ACTION_ATTR_PUSH_VLAN:
case OVS_ACTION_ATTR_POP_VLAN:
case OVS_ACTION_ATTR_PUSH_MPLS:
@@ -1366,6 +1403,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 0c6d8bd..65891c5 100644
--- a/lib/dpif-provider.h
+++ b/lib/dpif-provider.h
@@ -357,6 +357,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,
+ 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 21fc3e3..df4b365 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -55,6 +55,9 @@ COVERAGE_DEFINE(dpif_flow_del);
COVERAGE_DEFINE(dpif_execute);
COVERAGE_DEFINE(dpif_purge);
COVERAGE_DEFINE(dpif_execute_with_help);
+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
@@ -1075,6 +1078,7 @@ dpif_execute_helper_cb(void *aux_, struct ofpbuf *packet,
packet, md, false);
break;
+ case OVS_ACTION_ATTR_METER:
case OVS_ACTION_ATTR_PUSH_VLAN:
case OVS_ACTION_ATTR_POP_VLAN:
case OVS_ACTION_ATTR_PUSH_MPLS:
@@ -1507,3 +1511,87 @@ log_execute_message(struct dpif *dpif, const struct dpif_execute *execute,
free(packet);
}
}
+
+/* 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,
+ 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, ovs_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, ovs_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, ovs_strerror(error));
+ if (stats) {
+ stats->packet_in_count = ~0;
+ stats->byte_in_count = ~0;
+ stats->n_bands = 0;
+ }
+ }
+ return error;
+}
diff --git a/lib/dpif.h b/lib/dpif.h
index 2086ec5..7fd0c9f 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -343,6 +343,7 @@
#include <stddef.h>
#include <stdint.h>
#include "flow.h"
+#include "ofp-util.h"
#include "openflow/openflow.h"
#include "netdev.h"
#include "util.h"
@@ -606,6 +607,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,
+ 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 12f17d4..3c38e95 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -185,6 +185,10 @@ odp_execute_actions(void *dp, struct ofpbuf *packet, struct dpif_metadata *md,
int type = nl_attr_type(a);
switch ((enum ovs_action_attr) type) {
+ case OVS_ACTION_ATTR_METER:
+ /* Not implemented yet. */
+ break;
+
case OVS_ACTION_ATTR_OUTPUT:
case OVS_ACTION_ATTR_USERSPACE:
if (callback) {
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 238c5e1..b3ee0cf 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -74,6 +74,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;
@@ -386,6 +387,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;
@@ -637,6 +641,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.c b/ofproto/ofproto-dpif.c
index ec6d0bc..f2fda01 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -6153,6 +6153,58 @@ odp_port_to_ofp_port(const struct ofproto_dpif *ofproto, odp_port_t odp_port)
}
}
+static void
+meter_get_features(const struct ofproto *ofproto_,
+ struct ofputil_meter_features *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_, ofproto_meter_id *meter_id,
+ struct ofputil_meter_config *config)
+{
+ 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_, 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_, ofproto_meter_id meter_id)
+{
+ struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+ dpif_meter_del(ofproto->backer->dpif, meter_id, NULL);
+}
+
const struct ofproto_class ofproto_dpif_class = {
init,
enumerate_types,
@@ -6223,10 +6275,10 @@ const struct ofproto_class ofproto_dpif_class = {
forward_bpdu_changed,
set_mac_table_config,
set_realdev,
- NULL, /* meter_get_features */
- NULL, /* meter_set */
- NULL, /* meter_get */
- NULL, /* meter_del */
+ meter_get_features,
+ meter_set,
+ meter_get,
+ meter_del,
group_alloc, /* group_alloc */
group_construct, /* group_construct */
group_destruct, /* group_destruct */
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 2844e4c..586e52a 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -1665,7 +1665,7 @@ struct ofproto_class {
* leaving '*id' unchanged. On failure, the existing meter configuration
* is left intact. */
enum ofperr (*meter_set)(struct ofproto *ofproto, ofproto_meter_id *id,
- const struct ofputil_meter_config *config);
+ struct ofputil_meter_config *config);
/* Gets the meter and meter band packet and byte counts for maximum of
* 'stats->n_bands' bands for the meter with provider ID 'id' within
--
1.7.10.4
More information about the dev
mailing list