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 <jrajaha...@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 c405747..856456c 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -5030,6 +5030,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, @@ -5104,10 +5156,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 06e2b68..9ba156d 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -1577,7 +1577,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 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev