Signed-off-by: Jarno Rajahalme <jarno.rajaha...@nsn.com> --- lib/dpif-netdev.c | 273 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 236 insertions(+), 37 deletions(-)
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 1f57917..32a0b7a 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -60,6 +60,9 @@ VLOG_DEFINE_THIS_MODULE(dpif_netdev); /* Configuration parameters. */ enum { MAX_PORTS = 256 }; /* Maximum number of ports. */ enum { MAX_FLOWS = 65536 }; /* Maximum number of flows in flow table. */ +enum { MAX_METERS = 65536 }; /* Maximum number of meters. */ +enum { MAX_BANDS = 8 }; /* Maximum number of bands / meter. */ +enum { MAX_DSCP_PREC_LEVEL = 0 }; /* Maximum increase in DSCP value. */ /* Enough headroom to add a vlan tag, plus an extra 2 bytes to allow IP * headers to be aligned on a 4-byte boundary. */ @@ -81,6 +84,34 @@ struct dp_netdev_queue { unsigned int head, tail; }; +/* Set of supported meter flags */ +#define DP_SUPPORTED_METER_FLAGS_MASK \ + (OFPMF13_STATS | OFPMF13_PKTPS | OFPMF13_KBPS | OFPMF13_BURST) + +/* Set of supported meter band types */ +#define DP_SUPPORTED_METER_BAND_TYPES \ + ( 1 << OFPMBT13_DROP ) + +struct dp_meter_band { + struct ofputil_meter_band up; /* type, prec_level, pad, rate, burst_size */ + uint32_t bucket; + uint64_t packet_count; + uint64_t byte_count; +}; + +struct dp_meter { + uint16_t flags; + uint16_t n_bands; + uint64_t used; + uint64_t packet_count; + uint64_t byte_count; + struct dp_meter_band bands[]; +}; + +static bool +dp_netdev_action_meter(void *dp_, struct ofpbuf *packet, uint32_t meter_id, + long long int now); + /* Datapath based on the network device interface from netdev.h. */ struct dp_netdev { const struct dpif_class *class; @@ -100,6 +131,10 @@ struct dp_netdev { struct dp_netdev_port *ports[MAX_PORTS]; struct list port_list; unsigned int serial; + + /* Meters. */ + struct dp_meter *meters[MAX_METERS]; /* meter bands */ + uint32_t meter_free; /* Next free meter */ }; /* A port in a netdev-based datapath. */ @@ -1026,43 +1061,7 @@ dpif_netdev_recv_purge(struct dpif *dpif) struct dpif_netdev *dpif_netdev = dpif_netdev_cast(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) { @@ -1213,7 +1212,207 @@ dp_netdev_execute_actions(struct dp_netdev *dp, { odp_execute_actions(dp, packet, key, actions, actions_len, dp_netdev_output_port, dp_netdev_action_userspace, - NULL); + dp_netdev_action_meter); +} + +/* Meters */ +static void +dpif_netdev_meter_get_features(const struct dpif * dpif OVS_UNUSED, + struct ofputil_meter_features *features) +{ + features->max_meters = MAX_METERS; + features->band_types = DP_SUPPORTED_METER_BAND_TYPES >> 1; + features->capabilities = DP_SUPPORTED_METER_FLAGS_MASK; + features->max_bands = MAX_BANDS; + features->max_color = MAX_DSCP_PREC_LEVEL; +} + +static bool +dp_netdev_action_meter(void *dp_, struct ofpbuf *packet, uint32_t meter_id, + long long int now) +{ + struct dp_netdev *dp = dp_; + struct dp_meter *meter = dp->meters[meter_id]; + struct dp_meter_band *band; + int delta_t = (now - meter->used); /* msec */ + int divider; + uint32_t volume; + int i, band_exceeded_max = -1; + uint32_t band_exceeded_rate = 0; + + meter->used = now; + meter->packet_count += 1; + meter->byte_count += packet->size; + + if (meter->flags & OFPMF13_PKTPS) { + divider = 1000; /* msec * packets per second / 1000 = packets */ + volume = 1; /* Take one packet from the bucket */ + } else { + /* rate in kbps, bucket in bytes */ + divider = 8; /* msec * kbps / 8 = bytes */ + volume = packet->size; /* How much to take from the bucket */ + } + + /* Update all bands and find the one hit with the highest rate */ + for (i = 0; i < meter->n_bands; ++i) { + band = &meter->bands[i]; + /* Extremely large delta_t could wrap the 32-bit bucket around */ + band->bucket += delta_t * band->up.rate / divider; + if (band->bucket > band->up.burst_size) { + band->bucket = band->up.burst_size; + } + if (band->bucket >= volume) { + band->bucket -= volume; + } else if (band->up.rate > band_exceeded_rate) { + band_exceeded_rate = band->up.rate; + band_exceeded_max = i; + } + } + + if (band_exceeded_max < 0) { + return false; /* Do not drop */ + } + + band = &meter->bands[band_exceeded_max]; + band->packet_count += 1; + band->byte_count += packet->size; + + switch (band->up.type) { + case OFPMBT13_DROP: + return true; /* drop */ + case OFPMBT13_DSCP_REMARK: + /* FIXME: NOT IMPLEMETED YET */ + default: + NOT_REACHED(); + } +} + +static inline void +dp_delete_meter(struct dp_netdev *dp, uint32_t meter_id) +{ + if (dp->meters[meter_id]) { + free(dp->meters[meter_id]); + dp->meters[meter_id] = NULL; + } +} + +static int +dpif_netdev_meter_set(struct dpif *dpif, ofproto_meter_id *meter_id, + const struct ofputil_meter_config *config) +{ + struct dp_netdev *dp = get_dp_netdev(dpif); + uint32_t mid = meter_id->uint32; + struct dp_meter *meter; + int i; + + if (mid == UINT32_MAX) { + mid = dp->meter_free; + } + if (mid >= MAX_METERS) { + return EFBIG; /* Meter_id out of range. */ + } + + if (config->flags & ~DP_SUPPORTED_METER_FLAGS_MASK) { + return EBADF; /* Unsupported flags set */ + } + + /* Validate bands */ + if (config->n_bands == 0 || config->n_bands > MAX_BANDS) + return EINVAL; /* Too many bands */ + for (i = 0; i < config->n_bands; ++i) { + switch (config->bands[i].type) { + case OFPMBT13_DROP: + break; + case OFPMBT13_DSCP_REMARK: + default: + return ENODEV; /* Unsupported band type */ + } + } + + /* Allocate meter */ + meter = xzalloc(sizeof *meter + + config->n_bands * sizeof(struct dp_meter_band)); + if (meter) { + /* Free existing meter, if any */ + dp_delete_meter(dp, mid); + + meter->flags = config->flags; + meter->n_bands = config->n_bands; + + /* set up bands */ + for (i = 0; i < config->n_bands; ++i) { + meter->bands[i].up = config->bands[i]; + meter->bands[i].bucket = config->bands[i].burst_size; + } + dp->meters[mid] = meter; + meter_id->uint32 = mid; /* Store on success. */ + + /* Find next free meter */ + if (dp->meter_free == mid) { /* Now taken. */ + do { + if (++mid >= MAX_METERS) { /* Wrap around */ + mid = 0; + } + if (mid == dp->meter_free) { /* Full circle */ + mid = MAX_METERS; + break; + } + } while (dp->meters[mid]); + dp->meter_free = mid; /* Next free meter or MAX_METERS */ + } + return 0; + } + return ENOMEM; +} + +static int +dpif_netdev_meter_get(const struct dpif *dpif, + ofproto_meter_id meter_id_, + struct ofputil_meter_stats *stats) +{ + const struct dp_netdev *dp = get_dp_netdev(dpif); + const struct dp_meter *meter; + uint32_t meter_id = meter_id_.uint32; + + if (meter_id >= MAX_METERS) { + return EFBIG; + } + meter = dp->meters[meter_id]; + if (!meter) { + return ENOENT; + } + if (stats) { + int i = 0; + + stats->packet_in_count = meter->packet_count; + stats->byte_in_count = meter->byte_count; + + for (i = 0; i < stats->n_bands && i < meter->n_bands; ++i) { + stats->bands[i].packet_count = meter->bands[i].packet_count; + stats->bands[i].byte_count = meter->bands[i].byte_count; + } + stats->n_bands = i; + } + return 0; +} + +static int +dpif_netdev_meter_del(struct dpif *dpif, ofproto_meter_id meter_id_, + struct ofputil_meter_stats *stats) +{ + struct dp_netdev *dp = get_dp_netdev(dpif); + int error; + + error = dpif_netdev_meter_get(dpif, meter_id_, stats); + if (!error) { + uint32_t meter_id = meter_id_.uint32; + dp_delete_meter(dp, meter_id); + /* Keep free meter index as low as possible */ + if (meter_id < dp->meter_free) { + dp->meter_free = meter_id; + } + } + return error; } const struct dpif_class dpif_netdev_class = { -- 1.7.10.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev