diff mbox series

[ovs-dev,ovs-dev,v3,5/8] netdev-offload-tc: Implement and register meter offload API for tc

Message ID 20220407091603.31995-6-jianbol@nvidia.com
State Superseded
Headers show
Series Add support for ovs metering with tc offload | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/intel-ovs-compilation success test: success

Commit Message

Jianbo Liu April 7, 2022, 9:16 a.m. UTC
For dpif-netlink, meters are mapped by tc to police actions with
one-to-one relationship. Implement meter offload API to set/get/del
the police action, and a hmap is used to save the mappings.
An id-pool is used to manage all the available police indexes, which
are 0x10000000-0x1fffffff, reserved only for OVS.

Signed-off-by: Jianbo Liu <jianbol@nvidia.com>
---
 lib/netdev-offload-provider.h |   1 +
 lib/netdev-offload-tc.c       | 228 ++++++++++++++++++++++++++++++++++
 lib/netdev.c                  |   1 +
 3 files changed, 230 insertions(+)
diff mbox series

Patch

diff --git a/lib/netdev-offload-provider.h b/lib/netdev-offload-provider.h
index 4c34d3f7f..aac746f53 100644
--- a/lib/netdev-offload-provider.h
+++ b/lib/netdev-offload-provider.h
@@ -124,6 +124,7 @@  int meter_register_offload_api_provider(const struct meter_offload_api *);
 
 #ifdef __linux__
 extern const struct netdev_flow_api netdev_offload_tc;
+extern const struct meter_offload_api meter_offload_tc;
 #endif
 
 #ifdef DPDK_NETDEV
diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
index cff889c82..9364cb530 100644
--- a/lib/netdev-offload-tc.c
+++ b/lib/netdev-offload-tc.c
@@ -21,6 +21,7 @@ 
 
 #include "dpif.h"
 #include "hash.h"
+#include "id-pool.h"
 #include "openvswitch/hmap.h"
 #include "openvswitch/match.h"
 #include "openvswitch/ofpbuf.h"
@@ -2308,3 +2309,230 @@  const struct netdev_flow_api netdev_offload_tc = {
    .flow_get_n_flows = netdev_tc_get_n_flows,
    .init_flow_api = netdev_tc_init_flow_api,
 };
+
+#define METER_POLICE_IDS_BASE 0x10000000
+#define METER_POLICE_IDS_MAX  0x1FFFFFFF
+/* 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 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 %u (%u) with different police (%u)",
+                     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 int
+meter_tc_set_policer(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, "no free police index for meter id %u",
+                         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,
+                     "failed to %s police %u for meter id %u: %s",
+                     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
+meter_tc_get_policer(ofproto_meter_id meter_id,
+                     struct ofputil_meter_stats *stats,
+                     uint16_t max_bands OVS_UNUSED)
+{
+    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,
+                         "failed to get police %u stats for meter %u: %s",
+                         police_index, meter_id.uint32, ovs_strerror(err));
+        }
+    }
+
+    return err;
+}
+
+static int
+meter_tc_del_policer(ofproto_meter_id meter_id,
+                     struct ofputil_meter_stats *stats,
+                     uint16_t max_bands OVS_UNUSED)
+{
+    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,
+                         "failed to del police %u for meter %u: %s",
+                         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
+meter_tc_init_policer(void)
+{
+    meter_police_ids = id_pool_create(METER_POLICE_IDS_BASE,
+                METER_POLICE_IDS_MAX - METER_POLICE_IDS_BASE + 1);
+
+    return 0;
+}
+
+static int
+meter_tc_destroy_policer(void)
+{
+    id_pool_destroy(meter_police_ids);
+
+    return 0;
+}
+
+const struct meter_offload_api meter_offload_tc = {
+    .type = "tc_police",
+    .meter_offload_init = meter_tc_init_policer,
+    .meter_offload_destroy = meter_tc_destroy_policer,
+    .meter_offload_set = meter_tc_set_policer,
+    .meter_offload_get = meter_tc_get_policer,
+    .meter_offload_del = meter_tc_del_policer,
+};
diff --git a/lib/netdev.c b/lib/netdev.c
index 8305f6c42..f647e5de6 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -152,6 +152,7 @@  netdev_initialize(void)
         netdev_vport_tunnel_register();
 
         netdev_register_flow_api_provider(&netdev_offload_tc);
+        meter_register_offload_api_provider(&meter_offload_tc);
 #ifdef HAVE_AF_XDP
         netdev_register_provider(&netdev_afxdp_class);
         netdev_register_provider(&netdev_afxdp_nonpmd_class);