@@ -37,6 +37,7 @@
#include "dpif-provider.h"
#include "fat-rwlock.h"
#include "flow.h"
+#include "id-pool.h"
#include "netdev-linux.h"
#include "netdev-offload.h"
#include "netdev-provider.h"
@@ -122,6 +123,23 @@ static void
dpif_netlink_unixctl_dispatch_mode(struct unixctl_conn *conn, int argc,
const char *argv[], void *aux);
+#define METER_POLICE_IDS_BASE 50000
+#define METER_POLICE_IDS_MAX 59999
+/* Protects below meter ids pool and hashmaps. */
+static struct ovs_mutex meter_mutex = OVS_MUTEX_INITIALIZER;
+static struct id_pool *meter_police_ids;
+static struct hmap meter_id_to_police_idx OVS_GUARDED_BY(meter_mutex)
+ = HMAP_INITIALIZER(&meter_id_to_police_idx);
+
+struct meter_id_to_police_idx_data {
+ struct hmap_node meter_id_node;
+ uint32_t meter_id;
+ uint32_t police_idx;
+};
+
+static int
+meter_id_lookup(uint32_t meter_id, uint32_t *police_idx);
+
struct dpif_netlink_flow {
/* Generic Netlink header. */
uint8_t cmd;
@@ -704,6 +722,7 @@ dpif_netlink_destroy(struct dpif *dpif_)
struct dpif_netlink *dpif = dpif_netlink_cast(dpif_);
struct dpif_netlink_dp dp;
+ id_pool_destroy(meter_police_ids);
dpif_netlink_dp_init(&dp);
dp.cmd = OVS_DP_CMD_DEL;
dp.dp_ifindex = dpif->dp_ifindex;
@@ -3921,6 +3940,96 @@ dpif_netlink_ct_timeout_policy_dump_done(struct dpif *dpif OVS_UNUSED,
* zero. Check for that condition and disable meters on those kernels. */
static bool probe_broken_meters(struct dpif *);
+static struct meter_id_to_police_idx_data *
+meter_id_find_locked(uint32_t meter_id)
+ OVS_REQUIRES(meter_mutex)
+{
+ struct meter_id_to_police_idx_data *data;
+ size_t hash = hash_int(meter_id, 0);
+
+ HMAP_FOR_EACH_WITH_HASH (data, meter_id_node, hash,
+ &meter_id_to_police_idx) {
+ if (data->meter_id == meter_id) {
+ return data;
+ }
+ }
+
+ return NULL;
+}
+
+static int
+meter_id_lookup(uint32_t meter_id, uint32_t *police_idx)
+{
+ struct meter_id_to_police_idx_data *data;
+ int ret = 0;
+
+ ovs_mutex_lock(&meter_mutex);
+ data = meter_id_find_locked(meter_id);
+ if (data) {
+ *police_idx = data->police_idx;
+ } else {
+ ret = ENOENT;
+ }
+ ovs_mutex_unlock(&meter_mutex);
+
+ return ret;
+}
+
+static void
+meter_id_insert(uint32_t meter_id, uint32_t police_idx)
+{
+ struct meter_id_to_police_idx_data *data;
+
+ ovs_mutex_lock(&meter_mutex);
+ data = meter_id_find_locked(meter_id);
+ if (!data) {
+ data = xzalloc(sizeof *data);
+ data->meter_id = meter_id;
+ data->police_idx = police_idx;
+ hmap_insert(&meter_id_to_police_idx, &data->meter_id_node,
+ hash_int(meter_id, 0));
+ } else {
+ VLOG_WARN_RL(&error_rl,
+ "try to insert meter %d (%d) with different police (%d)",
+ meter_id, data->police_idx, police_idx);
+ }
+ ovs_mutex_unlock(&meter_mutex);
+}
+
+static void
+meter_id_remove(uint32_t meter_id)
+{
+ struct meter_id_to_police_idx_data *data;
+
+ ovs_mutex_lock(&meter_mutex);
+ data = meter_id_find_locked(meter_id);
+ if (data) {
+ hmap_remove(&meter_id_to_police_idx, &data->meter_id_node);
+ free(data);
+ }
+ ovs_mutex_unlock(&meter_mutex);
+}
+
+static bool
+meter_alloc_police_index(uint32_t *police_index)
+{
+ bool ret;
+
+ ovs_mutex_lock(&meter_mutex);
+ ret = id_pool_alloc_id(meter_police_ids, police_index);
+ ovs_mutex_unlock(&meter_mutex);
+
+ return ret;
+}
+
+static void
+meter_free_police_index(uint32_t police_index)
+{
+ ovs_mutex_lock(&meter_mutex);
+ id_pool_free_id(meter_police_ids, police_index);
+ ovs_mutex_unlock(&meter_mutex);
+}
+
static void
dpif_netlink_meter_init(struct dpif_netlink *dpif, struct ofpbuf *buf,
void *stub, size_t size, uint32_t command)
@@ -4107,14 +4216,75 @@ dpif_netlink_meter_set__(struct dpif *dpif_, ofproto_meter_id meter_id,
}
static int
+dpif_netlink_meter_set_policer(struct dpif *dpif_, ofproto_meter_id meter_id,
+ struct ofputil_meter_config *config)
+{
+ uint32_t police_index;
+ uint32_t rate, burst;
+ bool add_policer;
+ int err;
+
+ ovs_assert(config->bands != NULL);
+
+ rate = config->bands[0].rate;
+ if (config->flags & OFPMF13_BURST) {
+ burst = config->bands[0].burst_size;
+ } else {
+ burst = config->bands[0].rate;
+ }
+
+ add_policer = (meter_id_lookup(meter_id.uint32, &police_index) == ENOENT);
+ if (add_policer) {
+ if (!meter_alloc_police_index(&police_index)) {
+ VLOG_WARN_RL(&error_rl, "%s: no free police index for meter id %d",
+ dpif_name(dpif_), meter_id.uint32);
+ return ENOENT;
+ }
+ }
+
+ err = tc_add_policer_action(police_index,
+ (config->flags & OFPMF13_KBPS) ? rate : 0,
+ (config->flags & OFPMF13_KBPS) ? burst : 0,
+ (config->flags & OFPMF13_PKTPS) ? rate : 0,
+ (config->flags & OFPMF13_PKTPS) ? burst : 0,
+ !add_policer);
+ if (err) {
+ VLOG_WARN_RL(&error_rl,
+ "%s: failed to %s police %d for meter id %d: %s",
+ dpif_name(dpif_), add_policer ? "add" : "modify",
+ police_index, meter_id.uint32, ovs_strerror(err));
+ goto err_add_policer;
+ }
+
+ if (add_policer) {
+ meter_id_insert(meter_id.uint32, police_index);
+ }
+
+ return 0;
+
+err_add_policer:
+ if (add_policer) {
+ meter_free_police_index(police_index);
+ }
+ return err;
+}
+
+static int
dpif_netlink_meter_set(struct dpif *dpif_, ofproto_meter_id meter_id,
struct ofputil_meter_config *config)
{
+ int err;
+
if (probe_broken_meters(dpif_)) {
return ENOMEM;
}
- return dpif_netlink_meter_set__(dpif_, meter_id, config);
+ err = dpif_netlink_meter_set__(dpif_, meter_id, config);
+ if (!err && netdev_is_flow_api_enabled()) {
+ dpif_netlink_meter_set_policer(dpif_, meter_id, config);
+ }
+
+ return err;
}
/* Retrieve statistics and/or delete meter 'meter_id'. Statistics are
@@ -4202,19 +4372,77 @@ dpif_netlink_meter_get_stats(const struct dpif *dpif_,
}
static int
+dpif_netlink_meter_get_policer(const struct dpif *dpif,
+ ofproto_meter_id meter_id,
+ struct ofputil_meter_stats *stats)
+{
+ uint32_t police_index;
+ int err = 0;
+
+ if (!meter_id_lookup(meter_id.uint32, &police_index)) {
+ err = tc_get_policer_action(police_index, stats);
+ if (err) {
+ VLOG_WARN_RL(&error_rl,
+ "%s: failed to get police %d stats for meter %d: %s",
+ dpif_name(dpif), police_index, meter_id.uint32,
+ ovs_strerror(err));
+ }
+ }
+
+ return err;
+}
+
+static int
dpif_netlink_meter_get(const struct dpif *dpif, ofproto_meter_id meter_id,
struct ofputil_meter_stats *stats, uint16_t max_bands)
{
- return dpif_netlink_meter_get_stats(dpif, meter_id, stats, max_bands,
- OVS_METER_CMD_GET);
+ int err;
+
+ err = dpif_netlink_meter_get_stats(dpif, meter_id, stats, max_bands,
+ OVS_METER_CMD_GET);
+ if (!err && netdev_is_flow_api_enabled()) {
+ dpif_netlink_meter_get_policer(dpif, meter_id, stats);
+ }
+
+ return err;
+}
+
+static int
+dpif_netlink_meter_del_policer(struct dpif *dpif, ofproto_meter_id meter_id,
+ struct ofputil_meter_stats *stats)
+{
+ uint32_t police_index;
+ int err = 0;
+
+ if (!meter_id_lookup(meter_id.uint32, &police_index)) {
+ err = tc_del_policer_action(police_index, stats);
+ if (err) {
+ VLOG_WARN_RL(&error_rl,
+ "%s: failed to del police %d for meter %d: %s",
+ dpif_name(dpif), police_index,
+ meter_id.uint32, ovs_strerror(err));
+ } else {
+ meter_free_police_index(police_index);
+ }
+ meter_id_remove(meter_id.uint32);
+ }
+
+ return err;
}
static int
dpif_netlink_meter_del(struct dpif *dpif, ofproto_meter_id meter_id,
struct ofputil_meter_stats *stats, uint16_t max_bands)
{
- return dpif_netlink_meter_get_stats(dpif, meter_id, stats, max_bands,
- OVS_METER_CMD_DEL);
+ int err;
+
+ err = dpif_netlink_meter_get_stats(dpif, meter_id, stats,
+ max_bands, OVS_METER_CMD_DEL);
+ if (!err && netdev_is_flow_api_enabled()) {
+ dpif_netlink_meter_del_policer(dpif, meter_id, stats);
+ }
+
+ return err;
}
static bool
@@ -4256,6 +4484,8 @@ probe_broken_meters(struct dpif *dpif)
static bool broken_meters = false;
if (ovsthread_once_start(&once)) {
+ meter_police_ids = id_pool_create(METER_POLICE_IDS_BASE,
+ METER_POLICE_IDS_MAX - METER_POLICE_IDS_BASE + 1);
broken_meters = probe_broken_meters__(dpif);
ovsthread_once_done(&once);
}