[ovs-dev] [PATCH 4/6] dpif: Meter framework.
Jarno Rajahalme
jrajahalme at nicira.com
Tue Aug 5 23:38:55 UTC 2014
Add DPIF-level infrastructure for meters. Allow meter_set to modify
the meter configuration (e.g. set the burst size if unspecified).
Signed-off-by: Jarno Rajahalme <jrajahalme at nicira.com>
---
datapath/linux/compat/include/linux/openvswitch.h | 3 +
lib/dpif-linux.c | 40 ++++++++++
lib/dpif-netdev.c | 43 ++++++++++
lib/dpif-provider.h | 28 +++++++
lib/dpif.c | 88 +++++++++++++++++++++
lib/dpif.h | 11 +++
lib/odp-execute.c | 5 ++
lib/odp-util.c | 14 ++++
ofproto/ofproto-dpif.c | 60 +++++++++++++-
ofproto/ofproto-provider.h | 2 +-
10 files changed, 289 insertions(+), 5 deletions(-)
diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index 271a14e..64761b4 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -590,6 +590,8 @@ struct ovs_action_hash {
* 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 modify the packet (e.g., change the DSCP field).
*
* 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
@@ -608,6 +610,7 @@ enum ovs_action_attr {
OVS_ACTION_ATTR_HASH, /* struct ovs_action_hash. */
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 aa01cef..ebbc73c 100644
--- a/lib/dpif-linux.c
+++ b/lib/dpif-linux.c
@@ -1907,7 +1907,43 @@ dpif_linux_recv_purge(struct dpif *dpif_)
dpif_linux_recv_purge__(dpif);
fat_rwlock_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,
@@ -1948,6 +1984,10 @@ const struct dpif_class dpif_linux_class = {
NULL, /* register_upcall_cb */
NULL, /* enable_upcall */
NULL, /* disable_upcall */
+ 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 c637d9f..c662260 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -1715,6 +1715,44 @@ dp_netdev_disable_upcall(struct dp_netdev *dp)
fat_rwlock_wrlock(&dp->upcall_rwlock);
}
+
+/* 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
dpif_netdev_disable_upcall(struct dpif *dpif)
OVS_NO_THREAD_SAFETY_ANALYSIS
@@ -2163,6 +2201,7 @@ dp_execute_cb(void *aux_, struct dpif_packet **packets, int cnt,
}
break;
+ case OVS_ACTION_ATTR_METER:
case OVS_ACTION_ATTR_PUSH_VLAN:
case OVS_ACTION_ATTR_POP_VLAN:
case OVS_ACTION_ATTR_PUSH_MPLS:
@@ -2227,6 +2266,10 @@ const struct dpif_class dpif_netdev_class = {
dpif_netdev_register_upcall_cb,
dpif_netdev_enable_upcall,
dpif_netdev_disable_upcall,
+ 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 bf24a9d..b06ab81 100644
--- a/lib/dpif-provider.h
+++ b/lib/dpif-provider.h
@@ -427,6 +427,34 @@ struct dpif_class {
/* Disables upcalls if 'dpif' directly executes upcall functions. */
void (*disable_upcall)(struct 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 43141df..ce941e0 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -57,6 +57,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__
@@ -1117,6 +1120,7 @@ dpif_execute_helper_cb(void *aux_, struct dpif_packet **packets, int cnt,
}
case OVS_ACTION_ATTR_HASH:
+ case OVS_ACTION_ATTR_METER:
case OVS_ACTION_ATTR_PUSH_VLAN:
case OVS_ACTION_ATTR_POP_VLAN:
case OVS_ACTION_ATTR_PUSH_MPLS:
@@ -1651,3 +1655,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 f4a2a9e..92e4fc1 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -389,6 +389,7 @@
#include <stddef.h>
#include <stdint.h>
#include "netdev.h"
+#include "ofp-util.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
#include "packets.h"
@@ -685,6 +686,16 @@ void dpif_disable_upcall(struct dpif *);
void dpif_print_packet(struct dpif *, struct dpif_upcall *);
+/* 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 e1e9b57..fd6644d 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -214,6 +214,11 @@ odp_execute_actions__(void *dp, struct dpif_packet **packets, int cnt,
int type = nl_attr_type(a);
switch ((enum ovs_action_attr) type) {
+
+ case OVS_ACTION_ATTR_METER:
+ /* Not implemented yet. */
+ break;
+
/* These only make sense in the context of a datapath. */
case OVS_ACTION_ATTR_OUTPUT:
case OVS_ACTION_ATTR_USERSPACE:
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 162d85a..8bc80b2 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;
@@ -423,6 +424,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;
@@ -670,6 +674,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 3622ee7..bc56d08 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -5046,6 +5046,58 @@ ofproto_dpif_delete_internal_flow(struct ofproto_dpif *ofproto,
return 0;
}
+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,
@@ -5121,10 +5173,10 @@ const struct ofproto_class ofproto_dpif_class = {
set_mcast_snooping,
set_mcast_snooping_port,
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 fa29eee..8ce7b68 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -1599,7 +1599,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