bsc_subscriber: Optimize lookup of bsub by TMSI

It was found that on a busy osmo-bsc process (>1000 concurrent calls
spead over different BTSs), a good amount of time is spent iterating the
subscribers list trying to find a subscriber based on a TMSI (1.60% of
total CPU time used by osmo-bsc).

This patch introduces a new rbtree under struct bsc_subscr_store which
allows storing all the busbs ordered by TMSI.
This way, lookup time changes O(N) -> O(log(N)), at the expense of
increased insert/deletion time O(1) -> O(log(N)).

Related: SYS#6200
Change-Id: If27429e715ef4b327177e427249e68321a6e83cc
This commit is contained in:
Pau Espin 2023-03-13 17:57:42 +01:00
parent 08a7db6cd3
commit a4fd6d9371
4 changed files with 84 additions and 6 deletions

View File

@ -5,6 +5,7 @@
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/linuxrbtree.h>
#include <osmocom/core/use_count.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/gsm/gsm48.h>
@ -14,6 +15,8 @@ struct gsm_bts;
struct bsc_subscr_store {
struct llist_head bsub_list; /* list containing "struct bsc_subscr" */
/* rbtree root of 'struct bsc_subscr', ordered by tmsi */
struct rb_root bsub_tree_tmsi;
};
struct bsc_subscr_store *bsc_subscr_store_alloc(void *ctx);
@ -21,6 +24,8 @@ struct bsc_subscr_store *bsc_subscr_store_alloc(void *ctx);
struct bsc_subscr {
struct bsc_subscr_store *store; /* backpointer to "struct bsc_subscr_store" */
struct llist_head entry; /* entry in (struct bsc_subscr_store)->bsub_list */
/* entry in (struct bsc_subscr_store)->bsub_tree_tmsi. Inserted if tmsi != GSM_RESERVED_TMSI: */
struct rb_node node_tmsi;
struct osmo_use_count use_count;
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
@ -49,6 +54,7 @@ struct bsc_subscr *bsc_subscr_find_by_imsi(struct bsc_subscr_store *bsubst,
const char *imsi,
const char *use_token);
int bsc_subscr_set_tmsi(struct bsc_subscr *bsub, uint32_t tmsi);
void bsc_subscr_set_imsi(struct bsc_subscr *bsub, const char *imsi);
void bsc_subscr_set_imei(struct bsc_subscr *bsub, const char *imei);

View File

@ -138,20 +138,81 @@ static struct bsc_subscr *bsc_subscr_find_by_tmsi(struct bsc_subscr_store *bsubs
uint32_t tmsi,
const char *use_token)
{
const struct rb_node *node = bsubst->bsub_tree_tmsi.rb_node;
struct bsc_subscr *bsub;
if (tmsi == GSM_RESERVED_TMSI)
return NULL;
llist_for_each_entry(bsub, &bsubst->bsub_list, entry) {
if (bsub->tmsi == tmsi) {
while (node) {
bsub = container_of(node, struct bsc_subscr, node_tmsi);
if (tmsi < bsub->tmsi)
node = node->rb_left;
else if (tmsi > bsub->tmsi)
node = node->rb_right;
else {
bsc_subscr_get(bsub, use_token);
return bsub;
}
}
return NULL;
}
static int bsc_subscr_store_insert_bsub_tmsi(struct bsc_subscr *bsub)
{
struct bsc_subscr_store *bsubst = bsub->store;
struct rb_node **n = &(bsubst->bsub_tree_tmsi.rb_node);
struct rb_node *parent = NULL;
OSMO_ASSERT(bsub->tmsi != GSM_RESERVED_TMSI);
while (*n) {
struct bsc_subscr *it;
it = container_of(*n, struct bsc_subscr, node_tmsi);
parent = *n;
if (bsub->tmsi < it->tmsi) {
n = &((*n)->rb_left);
} else if (bsub->tmsi > it->tmsi) {
n = &((*n)->rb_right);
} else {
LOGP(DMSC, LOGL_ERROR, "Trying to reserve already reserved tmsi %u\n", bsub->tmsi);
return -EEXIST;
}
}
rb_link_node(&bsub->node_tmsi, parent, n);
rb_insert_color(&bsub->node_tmsi, &bsubst->bsub_tree_tmsi);
return 0;
}
int bsc_subscr_set_tmsi(struct bsc_subscr *bsub, uint32_t tmsi)
{
int rc = 0;
if (!bsub)
return -EINVAL;
if (bsub->tmsi == tmsi)
return 0;
/* bsub was already inserted, remove and re-insert with new tmsi */
if (bsub->tmsi != GSM_RESERVED_TMSI)
rb_erase(&bsub->node_tmsi, &bsub->store->bsub_tree_tmsi);
bsub->tmsi = tmsi;
/* If new tmsi is set, insert bsub into rbtree: */
if (bsub->tmsi != GSM_RESERVED_TMSI) {
if ((rc = bsc_subscr_store_insert_bsub_tmsi(bsub)) < 0)
bsub->tmsi = GSM_RESERVED_TMSI;
}
return rc;
}
void bsc_subscr_set_imsi(struct bsc_subscr *bsub, const char *imsi)
{
if (!bsub)
@ -209,7 +270,10 @@ struct bsc_subscr *bsc_subscr_find_or_create_by_tmsi(struct bsc_subscr_store *bs
bsub = bsc_subscr_alloc(bsubst);
if (!bsub)
return NULL;
bsub->tmsi = tmsi;
if (bsc_subscr_set_tmsi(bsub, tmsi) < 0) {
bsc_subscr_free(bsub);
return NULL;
}
bsc_subscr_get(bsub, use_token);
return bsub;
}
@ -268,6 +332,10 @@ const char *bsc_subscr_id(struct bsc_subscr *bsub)
static void bsc_subscr_free(struct bsc_subscr *bsub)
{
OSMO_ASSERT(llist_empty(&bsub->active_paging_requests));
if (bsub->tmsi != GSM_RESERVED_TMSI)
rb_erase(&bsub->node_tmsi, &bsub->store->bsub_tree_tmsi);
llist_del(&bsub->entry);
talloc_free(bsub);
}

View File

@ -352,8 +352,12 @@ int bsc_paging_start(struct bsc_paging_params *params)
return -EINVAL;
}
}
if (params->tmsi != GSM_RESERVED_TMSI)
params->bsub->tmsi = params->tmsi;
if (params->tmsi != GSM_RESERVED_TMSI) {
if (bsc_subscr_set_tmsi(params->bsub, params->tmsi) < 0) {
LOG_PAGING(params, DMSC, LOGL_ERROR, "Paging request failed: Could not set TMSI on subscriber\n");
return -EINVAL;
}
}
log_set_context(LOG_CTX_BSC_SUBSCR, params->bsub);
switch (params->cil.id_discr) {

View File

@ -75,7 +75,7 @@ static void test_bsc_subscr(void)
/* Allocate entry 2 */
s2 = bsc_subscr_find_or_create_by_imsi(bsc_subscribers, imsi2, BSUB_USE);
s2->tmsi = 0x73517351;
bsc_subscr_set_tmsi(s2, 0x73517351);
VERBOSE_ASSERT(llist_count(&bsc_subscribers->bsub_list), == 2, "%d");
/* Allocate entry 3 */