aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPau Espin Pedrol <pespin@sysmocom.de>2023-03-13 17:57:42 +0100
committerPau Espin Pedrol <pespin@sysmocom.de>2023-03-15 10:45:13 +0100
commita4fd6d937151c2f321533d20f78c052caa9b6bf1 (patch)
treeeb896a31184a91161115ad8214399d9322f815ae
parent08a7db6cd3c9b09bc566c7536d2b73972d8679a8 (diff)
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
-rw-r--r--include/osmocom/bsc/bsc_subscriber.h6
-rw-r--r--src/osmo-bsc/bsc_subscriber.c74
-rw-r--r--src/osmo-bsc/osmo_bsc_bssap.c8
-rw-r--r--tests/subscr/bsc_subscr_test.c2
4 files changed, 84 insertions, 6 deletions
diff --git a/include/osmocom/bsc/bsc_subscriber.h b/include/osmocom/bsc/bsc_subscriber.h
index 7ec457bf4..1fa00162d 100644
--- a/include/osmocom/bsc/bsc_subscriber.h
+++ b/include/osmocom/bsc/bsc_subscriber.h
@@ -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);
diff --git a/src/osmo-bsc/bsc_subscriber.c b/src/osmo-bsc/bsc_subscriber.c
index 65c5c8764..1f69d442d 100644
--- a/src/osmo-bsc/bsc_subscriber.c
+++ b/src/osmo-bsc/bsc_subscriber.c
@@ -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);
}
diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c
index a7c6ac355..d504ce691 100644
--- a/src/osmo-bsc/osmo_bsc_bssap.c
+++ b/src/osmo-bsc/osmo_bsc_bssap.c
@@ -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) {
diff --git a/tests/subscr/bsc_subscr_test.c b/tests/subscr/bsc_subscr_test.c
index 27dc93e1d..2a0e1eb0c 100644
--- a/tests/subscr/bsc_subscr_test.c
+++ b/tests/subscr/bsc_subscr_test.c
@@ -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 */