2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 14:25:26 +00:00

netdev-offload-tc: Implement meter offload API for tc

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>
Acked-by: Eelco Chaudron <echaudro@redhat.com>
Signed-off-by: Simon Horman <simon.horman@corigine.com>
This commit is contained in:
Jianbo Liu
2022-07-08 03:06:26 +00:00
committed by Simon Horman
parent 5c039ddc64
commit 4c226944f7

View File

@@ -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"
@@ -44,6 +45,7 @@
VLOG_DEFINE_THIS_MODULE(netdev_offload_tc);
static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5);
static struct vlog_rate_limit warn_rl = VLOG_RATE_LIMIT_INIT(10, 2);
static struct hmap ufid_to_tc = HMAP_INITIALIZER(&ufid_to_tc);
static struct hmap tc_to_ufid = HMAP_INITIALIZER(&tc_to_ufid);
@@ -62,6 +64,22 @@ struct chain_node {
uint32_t chain;
};
struct meter_police_mapping_data {
struct hmap_node meter_id_node;
uint32_t meter_id;
uint32_t police_idx;
};
#define METER_POLICE_IDS_BASE 0x10000000
#define METER_POLICE_IDS_MAX 0x1FFFFFFF
/* Protects below meter police ids pool. */
static struct ovs_mutex meter_police_ids_mutex = OVS_MUTEX_INITIALIZER;
static struct id_pool *meter_police_ids OVS_GUARDED_BY(meter_police_ids_mutex);
/* Protects below meter hashmaps. */
static struct ovs_mutex meter_mutex = OVS_MUTEX_INITIALIZER;
static struct hmap meter_id_to_police_idx OVS_GUARDED_BY(meter_mutex)
= HMAP_INITIALIZER(&meter_id_to_police_idx);
static bool
is_internal_port(const char *type)
{
@@ -2286,6 +2304,12 @@ netdev_tc_init_flow_api(struct netdev *netdev)
probe_multi_mask_per_prio(ifindex);
probe_ct_state_support(ifindex);
ovs_mutex_lock(&meter_police_ids_mutex);
meter_police_ids = id_pool_create(METER_POLICE_IDS_BASE,
METER_POLICE_IDS_MAX - METER_POLICE_IDS_BASE + 1);
ovs_mutex_unlock(&meter_police_ids_mutex);
ovsthread_once_done(&once);
}
@@ -2302,6 +2326,181 @@ netdev_tc_init_flow_api(struct netdev *netdev)
return 0;
}
static struct meter_police_mapping_data *
meter_id_find_locked(uint32_t meter_id)
OVS_REQUIRES(meter_mutex)
{
struct meter_police_mapping_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_police_mapping_data *data;
ovs_mutex_lock(&meter_mutex);
data = meter_id_find_locked(meter_id);
if (data) {
*police_idx = data->police_idx;
}
ovs_mutex_unlock(&meter_mutex);
return data ? 0 : ENOENT;
}
static void
meter_id_insert(uint32_t meter_id, uint32_t police_idx)
{
struct meter_police_mapping_data *data;
ovs_mutex_lock(&meter_mutex);
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));
ovs_mutex_unlock(&meter_mutex);
}
static void
meter_id_remove(uint32_t meter_id)
{
struct meter_police_mapping_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_police_ids_mutex);
ret = id_pool_alloc_id(meter_police_ids, police_index);
ovs_mutex_unlock(&meter_police_ids_mutex);
return ret;
}
static void
meter_free_police_index(uint32_t police_index)
{
ovs_mutex_lock(&meter_police_ids_mutex);
id_pool_free_id(meter_police_ids, police_index);
ovs_mutex_unlock(&meter_police_ids_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;
if (!config->bands || config->n_bands < 1 ||
config->bands[0].type != OFPMBT13_DROP) {
return 0;
}
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(&warn_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(&warn_rl,
"Failed to %s police %u for meter id %u: %s",
add_policer ? "add" : "modify",
police_index, meter_id.uint32, ovs_strerror(err));
}
if (add_policer) {
if (!err) {
meter_id_insert(meter_id.uint32, police_index);
} else {
meter_free_police_index(police_index);
}
}
return err;
}
static int
meter_tc_get_policer(ofproto_meter_id meter_id,
struct ofputil_meter_stats *stats)
{
uint32_t police_index;
int err = ENOENT;
if (!meter_id_lookup(meter_id.uint32, &police_index)) {
err = tc_get_policer_action(police_index, stats);
if (err) {
VLOG_WARN_RL(&warn_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)
{
uint32_t police_index;
int err = ENOENT;
if (!meter_id_lookup(meter_id.uint32, &police_index)) {
err = tc_del_policer_action(police_index, stats);
if (err && err != ENOENT) {
VLOG_ERR_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;
}
const struct netdev_flow_api netdev_offload_tc = {
.type = "linux_tc",
.flow_flush = netdev_tc_flow_flush,
@@ -2312,5 +2511,8 @@ const struct netdev_flow_api netdev_offload_tc = {
.flow_get = netdev_tc_flow_get,
.flow_del = netdev_tc_flow_del,
.flow_get_n_flows = netdev_tc_get_n_flows,
.meter_set = meter_tc_set_policer,
.meter_get = meter_tc_get_policer,
.meter_del = meter_tc_del_policer,
.init_flow_api = netdev_tc_init_flow_api,
};