Introduce hashtable to lookup chain_id

This lookup was taking ages specially when UPF already managed thousands
of sessions.

Related: SYS#6398
Change-Id: I7df8fd945eedbda98bd08e9fb2f382e0f55c2983
This commit is contained in:
Pau Espin Pedrol
2025-01-31 18:55:55 +01:00
parent c27e317b21
commit bede7f130f
7 changed files with 74 additions and 88 deletions

View File

@@ -64,6 +64,9 @@ struct up_gtp_action {
void *handle;
};
struct up_gtp_action *up_gtp_action_alloc(void *ctx, struct up_session *session, enum up_gtp_action_kind kind, struct llist_head *dst);
void up_gtp_action_free(struct up_gtp_action *a);
int up_gtp_action_cmp(const struct up_gtp_action *a, const struct up_gtp_action *b);
int up_gtp_action_enable(struct up_gtp_action *a);

View File

@@ -115,6 +115,8 @@ struct g_upf {
int priority_pre;
int priority_post;
uint32_t next_chain_id_state;
/* hashtable of (struct upf_nft_tun)->node_by_chain_id: */
DECLARE_HASHTABLE(nft_tun_by_chain_id, 10);
} tunmap;
struct {

View File

@@ -24,10 +24,12 @@
#pragma once
#include <stdint.h>
#include <osmocom/core/hashtable.h>
#include <osmocom/upf/upf_tun.h>
struct upf_nft_tun {
struct hlist_node node_by_chain_id; /* item in g_upf->tunmap.nft_tun_by_chain_id */
struct upf_tun tun;
uint32_t chain_id;
};

View File

@@ -198,3 +198,33 @@ char *up_gtp_action_to_str_c(void *ctx, const struct up_gtp_action *a)
{
OSMO_NAME_C_IMPL(ctx, 128, "ERROR", up_gtp_action_to_str_buf, a)
}
struct up_gtp_action *up_gtp_action_alloc(void *ctx, struct up_session *session, enum up_gtp_action_kind kind, struct llist_head *dst)
{
struct up_gtp_action *a = talloc_zero(ctx, struct up_gtp_action);
OSMO_ASSERT(a);
a->session = session;
a->kind = kind;
if (kind == UP_GTP_U_TUNMAP) {
INIT_HLIST_NODE(&a->tunmap.access.node_by_chain_id);
INIT_HLIST_NODE(&a->tunmap.core.node_by_chain_id);
}
llist_add_tail(&a->entry, dst);
return a;
}
void up_gtp_action_free(struct up_gtp_action *a)
{
if (!a)
return;
up_gtp_action_disable(a);
llist_del(&a->entry);
if (a->kind == UP_GTP_U_TUNMAP) {
if (!hlist_unhashed(&a->tunmap.access.node_by_chain_id))
hash_del(&a->tunmap.access.node_by_chain_id);
if (!hlist_unhashed(&a->tunmap.core.node_by_chain_id))
hash_del(&a->tunmap.core.node_by_chain_id);
}
talloc_free(a);
}

View File

@@ -824,9 +824,7 @@ static void up_session_established_onleave(struct osmo_fsm_inst *fi, uint32_t ne
/* Shut down all active GTP rules */
while ((a = llist_first_entry_or_null(&session->active_gtp_actions, struct up_gtp_action, entry))) {
up_gtp_action_disable(a);
llist_del(&a->entry);
talloc_free(a);
up_gtp_action_free(a);
}
}
@@ -1202,31 +1200,14 @@ static void add_gtp_action_tunend(void *ctx, struct llist_head *dst, struct pdr
talloc_free(rpdr->inactive_reason);
rpdr->inactive_reason = NULL;
a = talloc(ctx, struct up_gtp_action);
OSMO_ASSERT(a);
*a = (struct up_gtp_action){
.session = session,
.pdr_access = pdr->desc.pdr_id,
.pdr_core = rpdr->desc.pdr_id,
.kind = UP_GTP_U_TUNEND,
.tunend = {
.access = {
.local = {
.addr = pdr->local_f_teid->fixed.ip_addr.v4,
.teid = pdr->local_f_teid->fixed.teid,
},
.remote = {
.addr = rfar_forw->outer_header_creation.ip_addr.v4,
.teid = rfar_forw->outer_header_creation.teid,
},
},
.core = {
.ue_local_addr = rpdr->desc.pdi.ue_ip_address.ip_addr.v4,
},
},
};
llist_add_tail(&a->entry, dst);
a = up_gtp_action_alloc(ctx, session, UP_GTP_U_TUNEND, dst);
a->pdr_access = pdr->desc.pdr_id;
a->pdr_core = rpdr->desc.pdr_id;
a->tunend.access.local.addr = pdr->local_f_teid->fixed.ip_addr.v4;
a->tunend.access.local.teid = pdr->local_f_teid->fixed.teid;
a->tunend.access.remote.addr = rfar_forw->outer_header_creation.ip_addr.v4;
a->tunend.access.remote.teid = rfar_forw->outer_header_creation.teid;
a->tunend.core.ue_local_addr = rpdr->desc.pdi.ue_ip_address.ip_addr.v4;
}
/* A GTP tunnel on Access side, mapping to another GTP tunnel on Core side and vice versa.
@@ -1316,38 +1297,17 @@ static void add_gtp_action_tunmap(void *ctx, struct llist_head *dst, struct pdr
talloc_free(rpdr->inactive_reason);
rpdr->inactive_reason = NULL;
a = talloc(ctx, struct up_gtp_action);
OSMO_ASSERT(a);
*a = (struct up_gtp_action){
.session = session,
.pdr_access = pdr->desc.pdr_id,
.pdr_core = rpdr->desc.pdr_id,
.kind = UP_GTP_U_TUNMAP,
.tunmap = {
.access.tun = {
.local = {
.addr = pdr->local_f_teid->fixed.ip_addr.v4,
.teid = pdr->local_f_teid->fixed.teid,
},
.remote = {
.addr = rfar_forw->outer_header_creation.ip_addr.v4,
.teid = rfar_forw->outer_header_creation.teid,
},
},
.core.tun = {
.local = {
.addr = rpdr->local_f_teid->fixed.ip_addr.v4,
.teid = rpdr->local_f_teid->fixed.teid,
},
.remote = {
.addr = far_forw->outer_header_creation.ip_addr.v4,
.teid = far_forw->outer_header_creation.teid,
},
},
},
};
llist_add_tail(&a->entry, dst);
a = up_gtp_action_alloc(ctx, session, UP_GTP_U_TUNMAP, dst);
a->pdr_access = pdr->desc.pdr_id;
a->pdr_core = rpdr->desc.pdr_id;
a->tunmap.access.tun.local.addr = pdr->local_f_teid->fixed.ip_addr.v4;
a->tunmap.access.tun.local.teid = pdr->local_f_teid->fixed.teid;
a->tunmap.access.tun.remote.addr = rfar_forw->outer_header_creation.ip_addr.v4;
a->tunmap.access.tun.remote.teid = rfar_forw->outer_header_creation.teid;
a->tunmap.core.tun.local.addr = rpdr->local_f_teid->fixed.ip_addr.v4;
a->tunmap.core.tun.local.teid = rpdr->local_f_teid->fixed.teid;
a->tunmap.core.tun.remote.addr = far_forw->outer_header_creation.ip_addr.v4;
a->tunmap.core.tun.remote.teid = far_forw->outer_header_creation.teid;
}
/* Analyse all PDRs and FARs and find configurations that match either a GTP encaps/decaps or a GTP forward rule. Add to
@@ -1472,9 +1432,7 @@ static enum osmo_pfcp_cause setup_gtp_actions(struct up_session *session, struct
continue;
LOGPFSML(session->fi, LOGL_DEBUG, "disabling: %s\n", up_gtp_action_to_str_c(OTC_SELECT, a));
up_gtp_action_disable(a);
llist_del(&a->entry);
talloc_free(a);
up_gtp_action_free(a);
}
/* Set up all GTP tunnels requested in the session setup, but not active yet */

View File

@@ -78,6 +78,7 @@ void g_upf_alloc(void *ctx)
INIT_LLIST_HEAD(&g_upf->tunend.vty_cfg.devs);
INIT_LLIST_HEAD(&g_upf->tunend.devs);
INIT_LLIST_HEAD(&g_upf->netinst);
hash_init(g_upf->tunmap.nft_tun_by_chain_id);
hash_init(g_upf->gtp.pdrs_by_local_f_teid);
}
@@ -175,6 +176,17 @@ static uint32_t upf_next_chain_id_inc(void)
return g_upf->tunmap.next_chain_id_state;
}
static bool upf_is_chain_id_in_use(uint32_t chain_id)
{
struct upf_nft_tun *nft_tun;
hash_for_each_possible(g_upf->tunmap.nft_tun_by_chain_id, nft_tun, node_by_chain_id, chain_id) {
if (nft_tun->chain_id != chain_id)
continue;
return true;
}
return false;
}
/* Return an unused chain_id, or 0 if none is found with sane effort. */
uint32_t upf_next_chain_id(void)
{
@@ -182,36 +194,14 @@ uint32_t upf_next_chain_id(void)
/* Make sure the new chain_id is not used anywhere */
for (sanity = 2342; sanity; sanity--) {
struct up_peer *peer;
uint32_t chain_id = upf_next_chain_id_inc();
bool taken = false;
if (!g_upf->pfcp.ep)
return chain_id;
llist_for_each_entry(peer, &g_upf->pfcp.ep->peers, entry) {
struct up_session *session;
int bkt;
hash_for_each(peer->sessions_by_up_seid, bkt, session, node_by_up_seid) {
struct up_gtp_action *a;
llist_for_each_entry(a, &session->active_gtp_actions, entry) {
if (a->kind != UP_GTP_U_TUNMAP)
continue;
if (a->tunmap.access.chain_id == chain_id
|| a->tunmap.core.chain_id == chain_id) {
taken = true;
break;
}
}
if (taken)
break;
}
if (taken)
break;
}
if (!taken)
return chain_id;
if (upf_is_chain_id_in_use(chain_id))
continue;
return chain_id;
}
/* finding a chain_id became insane, return invalid = 0 */

View File

@@ -489,6 +489,7 @@ static int upf_nft_tunmap_ensure_chain_id(struct upf_nft_tun *tun)
tun->chain_id = upf_next_chain_id();
if (!tun->chain_id)
return -ENOSPC;
hash_add(g_upf->tunmap.nft_tun_by_chain_id, &tun->node_by_chain_id, tun->chain_id);
return 0;
}