@@ -729,13 +729,14 @@ enum ovs_ct_attr {
* fields within a header are modifiable, e.g. the IPv4 protocol and fragment
* type may not be changed.
*
- *
* @OVS_ACTION_ATTR_SET_TO_MASKED: Kernel internal masked set action translated
* from the @OVS_ACTION_ATTR_SET.
* @OVS_ACTION_ATTR_TUNNEL_PUSH: Push tunnel header described by struct
* ovs_action_push_tnl.
* @OVS_ACTION_ATTR_TUNNEL_POP: Lookup tunnel port by port-no passed and pop
* tunnel header.
+ * @OVS_ACTION_ATTR_METER: Run packet through a meter, which may drop the
+ * packet, or modify the packet (e.g., change the DSCP field).
*/
enum ovs_action_attr {
@@ -757,8 +758,9 @@ enum ovs_action_attr {
OVS_ACTION_ATTR_CT, /* Nested OVS_CT_ATTR_* . */
#ifndef __KERNEL__
- OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/
- OVS_ACTION_ATTR_TUNNEL_POP, /* u32 port number. */
+ OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl. */
+ OVS_ACTION_ATTR_TUNNEL_POP, /* u32 port number. */
+ OVS_ACTION_ATTR_METER, /* u32 meter number. */
#endif
__OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted
* from userspace. */
@@ -2714,6 +2714,46 @@ 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,
+ uint16_t n_bands 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,
+ uint16_t n_bands OVS_UNUSED)
+{
+ return EFBIG; /* meter_id out of range */
+}
+
+
static void
dpif_netdev_disable_upcall(struct dpif *dpif)
OVS_NO_THREAD_SAFETY_ANALYSIS
@@ -3619,6 +3659,7 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
VLOG_WARN("Cannot execute conntrack action in userspace.");
break;
+ case OVS_ACTION_ATTR_METER:
case OVS_ACTION_ATTR_PUSH_VLAN:
case OVS_ACTION_ATTR_POP_VLAN:
case OVS_ACTION_ATTR_PUSH_MPLS:
@@ -3687,6 +3728,10 @@ const struct dpif_class dpif_netdev_class = {
dpif_netdev_enable_upcall,
dpif_netdev_disable_upcall,
dpif_netdev_get_datapath_version,
+ dpif_netdev_meter_get_features,
+ dpif_netdev_meter_set,
+ dpif_netdev_meter_get,
+ dpif_netdev_meter_del,
};
static void
@@ -2279,6 +2279,46 @@ dpif_netlink_get_datapath_version(void)
return version_str;
}
+
+/* Meters */
+static void
+dpif_netlink_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_netlink_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_netlink_meter_get(const struct dpif *dpif OVS_UNUSED,
+ ofproto_meter_id meter_id OVS_UNUSED,
+ struct ofputil_meter_stats *stats OVS_UNUSED,
+ uint16_t n_bands OVS_UNUSED)
+{
+ return EFBIG; /* meter_id out of range */
+}
+
+static int
+dpif_netlink_meter_del(struct dpif *dpif OVS_UNUSED,
+ ofproto_meter_id meter_id OVS_UNUSED,
+ struct ofputil_meter_stats *stats OVS_UNUSED,
+ uint16_t n_bands OVS_UNUSED)
+{
+ return EFBIG; /* meter_id out of range */
+}
+
+
const struct dpif_class dpif_netlink_class = {
"system",
NULL, /* init */
@@ -2319,6 +2359,10 @@ const struct dpif_class dpif_netlink_class = {
NULL, /* enable_upcall */
NULL, /* disable_upcall */
dpif_netlink_get_datapath_version, /* get_datapath_version */
+ dpif_netlink_meter_get_features,
+ dpif_netlink_meter_set,
+ dpif_netlink_meter_get,
+ dpif_netlink_meter_del,
};
static int
@@ -390,6 +390,35 @@ struct dpif_class {
/* Get datapath version. Caller is responsible for freeing the string
* returned. */
char *(*get_datapath_version)(void);
+
+ /* 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 '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 *, uint16_t n_bands);
+
+ /* Removes meter 'meter_id' from 'dpif'. Stores meter and band statistics
+ * (for maximum of '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, in which case 'n_bands' must be passed in as
+ * zero. */
+ int (*meter_del)(struct dpif *, ofproto_meter_id meter_id,
+ struct ofputil_meter_stats *, uint16_t n_bands);
};
extern const struct dpif_class dpif_netlink_class;
@@ -63,6 +63,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[] = {
#if defined(__linux__) || defined(_WIN32)
@@ -1137,6 +1140,7 @@ dpif_execute_helper_cb(void *aux_, struct dp_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:
@@ -1719,3 +1723,87 @@ dpif_supports_tnl_push_pop(const struct dpif *dpif)
{
return dpif_is_netdev(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 identified by 'meter_id' 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, uint16_t n_bands)
+{
+ int error;
+
+ COVERAGE_INC(dpif_meter_get);
+
+ error = dpif->dpif_class->meter_get(dpif, meter_id, stats, n_bands);
+ 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, uint16_t n_bands)
+{
+ int error;
+
+ COVERAGE_INC(dpif_meter_del);
+
+ error = dpif->dpif_class->meter_del(dpif, meter_id, stats, n_bands);
+ 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;
+}
@@ -390,6 +390,7 @@
#include <stdint.h>
#include "netdev.h"
#include "dp-packet.h"
+#include "ofp-util.h"
#include "openflow/openflow.h"
#include "ovs-numa.h"
#include "packets.h"
@@ -847,6 +848,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 *, uint16_t n_bands);
+int dpif_meter_del(struct dpif *, ofproto_meter_id meter_id,
+ struct ofputil_meter_stats *, uint16_t n_bands);
+
/* Miscellaneous. */
void dpif_get_netflow_ids(const struct dpif *,
@@ -493,6 +493,7 @@ requires_datapath_assistance(const struct nlattr *a)
case OVS_ACTION_ATTR_USERSPACE:
case OVS_ACTION_ATTR_RECIRC:
case OVS_ACTION_ATTR_CT:
+ case OVS_ACTION_ATTR_METER:
return true;
case OVS_ACTION_ATTR_SET:
@@ -623,6 +624,10 @@ odp_execute_actions(void *dp, struct dp_packet **packets, int cnt, bool steal,
}
break;
+ case OVS_ACTION_ATTR_METER:
+ /* Not implemented yet. */
+ break;
+
case OVS_ACTION_ATTR_OUTPUT:
case OVS_ACTION_ATTR_TUNNEL_PUSH:
case OVS_ACTION_ATTR_TUNNEL_POP:
@@ -108,6 +108,7 @@ odp_action_len(uint16_t type)
case OVS_ACTION_ATTR_OUTPUT: return sizeof(uint32_t);
case OVS_ACTION_ATTR_TUNNEL_PUSH: return ATTR_LEN_VARIABLE;
case OVS_ACTION_ATTR_TUNNEL_POP: return sizeof(uint32_t);
+ case OVS_ACTION_ATTR_METER: return sizeof(uint32_t);
case OVS_ACTION_ATTR_USERSPACE: return ATTR_LEN_VARIABLE;
case OVS_ACTION_ATTR_PUSH_VLAN: return sizeof(struct ovs_action_push_vlan);
case OVS_ACTION_ATTR_POP_VLAN: return 0;
@@ -618,6 +619,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;
@@ -1245,6 +1249,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;
@@ -1142,6 +1142,7 @@ dpif_sflow_read_actions(const struct flow *flow,
case OVS_ACTION_ATTR_RECIRC:
case OVS_ACTION_ATTR_HASH:
case OVS_ACTION_ATTR_CT:
+ case OVS_ACTION_ATTR_METER:
break;
case OVS_ACTION_ATTR_SET_MASKED:
@@ -5661,6 +5661,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, uint16_t n_bands)
+{
+ const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+
+ if (!dpif_meter_get(ofproto->backer->dpif, meter_id, stats, n_bands)) {
+ 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, 0);
+}
+
const struct ofproto_class ofproto_dpif_class = {
init,
enumerate_types,
@@ -5746,10 +5798,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 */
@@ -1713,16 +1713,17 @@ 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
- * 'ofproto'. The caller fills in the other stats values. The band stats
- * are copied to memory at 'stats->bands' provided by the caller. The
- * number of returned band stats is returned in 'stats->n_bands'. */
+ * 'n_bands' bands for the meter with provider ID 'id' within 'ofproto'.
+ * The caller fills in the other stats values. The band stats are copied
+ * to memory at 'stats->bands' provided by the caller. The number of
+ * returned band stats is returned in 'stats->n_bands'. */
enum ofperr (*meter_get)(const struct ofproto *ofproto,
ofproto_meter_id id,
- struct ofputil_meter_stats *stats);
+ struct ofputil_meter_stats *stats,
+ uint16_t n_bands);
/* Deletes a meter, making the 'ofproto_meter_id' invalid for any
* further calls. */
@@ -6000,7 +6000,7 @@ handle_meter_request(struct ofconn *ofconn, const struct ofp_header *request,
if (!ofproto->ofproto_class->meter_get(ofproto,
meter->provider_meter_id,
- &stats)) {
+ &stats, meter->n_bands)) {
ofputil_append_meter_stats(&replies, &stats);
}
} else { /* type == OFPTYPE_METER_CONFIG_REQUEST */
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 <jarno@ovn.org> --- datapath/linux/compat/include/linux/openvswitch.h | 8 ++- lib/dpif-netdev.c | 45 ++++++++++++ lib/dpif-netlink.c | 44 ++++++++++++ lib/dpif-provider.h | 29 ++++++++ lib/dpif.c | 88 +++++++++++++++++++++++ lib/dpif.h | 11 +++ lib/odp-execute.c | 5 ++ lib/odp-util.c | 14 ++++ ofproto/ofproto-dpif-sflow.c | 1 + ofproto/ofproto-dpif.c | 60 ++++++++++++++-- ofproto/ofproto-provider.h | 13 ++-- ofproto/ofproto.c | 2 +- 12 files changed, 306 insertions(+), 14 deletions(-)