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