gbproxy: convert bss_nses from llist_head to hashtable

For the common lookup-by-nsei, this should reduce the computational
complexity significantly.

Depends: libosmocore.git I8ef73a62fe9846ce45058eb21cf999dd3eed5741
Change-Id: Idbb6a362332bb6e3ce22102e7409ae80d0980f44
diff --git a/src/gbproxy/gb_proxy.c b/src/gbproxy/gb_proxy.c
index 4c34941..c37b21a 100644
--- a/src/gbproxy/gb_proxy.c
+++ b/src/gbproxy/gb_proxy.c
@@ -31,6 +31,7 @@
 #include <arpa/inet.h>
 #include <time.h>
 
+#include <osmocom/core/hashtable.h>
 #include <osmocom/core/logging.h>
 #include <osmocom/core/talloc.h>
 #include <osmocom/core/select.h>
@@ -1183,6 +1184,7 @@
 	struct gbproxy_bvc *bvc;
 	unsigned int n_nses = 0;
 	int errctr = GBPROX_GLOB_CTR_PROTO_ERR_SGSN;
+	int i;
 
 	/* FIXME: Handle paging logic to only page each matching NSE */
 
@@ -1203,7 +1205,7 @@
 	} else if (TLVP_PRES_LEN(tp, BSSGP_IE_ROUTEING_AREA, 6)) {
 		errctr = GBPROX_GLOB_CTR_INV_RAI;
 		/* iterate over all bvcs and dispatch the paging to each matching one */
-		llist_for_each_entry(nse, &cfg->bss_nses, list) {
+		hash_for_each(cfg->bss_nses, i, nse, list) {
 			llist_for_each_entry(bvc, &nse->bvcs, list) {
 				if (!memcmp(bvc->ra, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA), 6)) {
 					LOGPNSE(nse, LOGL_INFO, "routing to NSE (RAI match)\n");
@@ -1217,7 +1219,7 @@
 	} else if (TLVP_PRES_LEN(tp, BSSGP_IE_LOCATION_AREA, 5)) {
 		errctr = GBPROX_GLOB_CTR_INV_LAI;
 		/* iterate over all bvcs and dispatch the paging to each matching one */
-		llist_for_each_entry(nse, &cfg->bss_nses, list) {
+		hash_for_each(cfg->bss_nses, i, nse, list) {
 			llist_for_each_entry(bvc, &nse->bvcs, list) {
 				if (!memcmp(bvc->ra, TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA), 5)) {
 					LOGPNSE(nse, LOGL_INFO, "routing to NSE (LAI match)\n");
@@ -1230,7 +1232,7 @@
 		}
 	} else if (TLVP_PRES_LEN(tp, BSSGP_IE_BSS_AREA_ID, 1)) {
 		/* iterate over all bvcs and dispatch the paging to each matching one */
-		llist_for_each_entry(nse, &cfg->bss_nses, list) {
+		hash_for_each(cfg->bss_nses, i, nse, list) {
 			llist_for_each_entry(bvc, &nse->bvcs, list) {
 				LOGPNSE(nse, LOGL_INFO, "routing to NSE (broadcast)\n");
 				gbprox_relay2nse(msg, nse, ns_bvci);
@@ -1263,6 +1265,7 @@
 	struct gbproxy_nse *nse;
 	struct gbproxy_bvc *bvc;
 	uint16_t ptp_bvci;
+	int i;
 
 	if (!TLVP_PRES_LEN(tp, BSSGP_IE_BVCI, 2)) {
 		rate_ctr_inc(&cfg->ctrg->
@@ -1291,7 +1294,7 @@
 	 * from the SGSN.  As the signalling BVCI is shared
 	 * among all the BSS's that we multiplex, it needs to
 	 * be relayed  */
-	llist_for_each_entry(nse, &cfg->bss_nses, list) {
+	hash_for_each(cfg->bss_nses, i, nse, list) {
 		llist_for_each_entry(bvc, &nse->bvcs, list)
 			gbprox_relay2peer(msg, bvc, ns_bvci);
 	}
@@ -1315,6 +1318,7 @@
 	struct msgb *msg;
 	int rc = 0;
 	int cause;
+	int i;
 
 	if (ns_bvci != 0 && ns_bvci != 1) {
 		LOGP(DGPRS, LOGL_NOTICE, "NSE(%05u/SGSN) BVCI=%05u is not "
@@ -1425,7 +1429,7 @@
 		LOGP(DGPRS, LOGL_DEBUG,
 			"NSE(%05u/SGSN) BSSGP %s: broadcasting\n", nsei, bssgp_pdu_str(pdu_type));
 		/* broadcast to all BSS-side bvcs */
-		llist_for_each_entry(nse, &cfg->bss_nses, list) {
+		hash_for_each(cfg->bss_nses, i, nse, list) {
 			gbprox_relay2nse(msg, nse, 0);
 		}
 		break;
@@ -1618,9 +1622,11 @@
 
 void gbprox_reset(struct gbproxy_config *cfg)
 {
-	struct gbproxy_nse *nse, *ntmp;
+	struct gbproxy_nse *nse;
+	struct hlist_node *ntmp;
+	int i;
 
-	llist_for_each_entry_safe(nse, ntmp, &cfg->bss_nses, list) {
+	hash_for_each_safe(cfg->bss_nses, i, ntmp, nse, list) {
 		struct gbproxy_bvc *bvc, *tmp;
 		llist_for_each_entry_safe(bvc, tmp, &nse->bvcs, list)
 			gbproxy_bvc_free(bvc);
@@ -1636,7 +1642,7 @@
 {
 	struct timespec tp;
 
-	INIT_LLIST_HEAD(&cfg->bss_nses);
+	hash_init(cfg->bss_nses);
 	cfg->ctrg = rate_ctr_group_alloc(tall_sgsn_ctx, &global_ctrg_desc, 0);
 	if (!cfg->ctrg) {
 		LOGP(DGPRS, LOGL_ERROR, "Cannot allocate global counter group!\n");
diff --git a/src/gbproxy/gb_proxy_ctrl.c b/src/gbproxy/gb_proxy_ctrl.c
index c3cfddf..8290412 100644
--- a/src/gbproxy/gb_proxy_ctrl.c
+++ b/src/gbproxy/gb_proxy_ctrl.c
@@ -56,6 +56,7 @@
 	struct gprs_ns2_inst *nsi = cfg->nsi;
 	struct gprs_ns2_nse *nse;
 	struct gbproxy_nse *nse_peer;
+	int i;
 
 	cmd->reply = talloc_strdup(cmd, "");
 
@@ -69,7 +70,7 @@
 		gprs_ns2_nse_foreach_nsvc(nse, &ctrl_nsvc_state_cb, cmd);
 
 	/* NS-VCs for BSS peers */
-	llist_for_each_entry(nse_peer, &cfg->bss_nses, list) {
+	hash_for_each(cfg->bss_nses, i, nse_peer, list) {
 		nse = gprs_ns2_nse_by_nsei(nsi, nse_peer->nsei);
 		if (nse)
 			gprs_ns2_nse_foreach_nsvc(nse, &ctrl_nsvc_state_cb, cmd);
@@ -84,10 +85,11 @@
 {
 	struct gbproxy_config *cfg = data;
 	struct gbproxy_nse *nse_peer;
+	int i;
 
 	cmd->reply = talloc_strdup(cmd, "");
 
-	llist_for_each_entry(nse_peer, &cfg->bss_nses, list) {
+	hash_for_each(cfg->bss_nses, i, nse_peer, list) {
 		struct gbproxy_bvc *bvc;
 		llist_for_each_entry(bvc, &nse_peer->bvcs, list) {
 			struct gprs_ra_id raid;
@@ -111,8 +113,9 @@
 	struct gbproxy_config *cfg = data;
 	struct gbproxy_nse *nse_peer;
 	uint32_t count = 0;
+	int i;
 
-	llist_for_each_entry(nse_peer, &cfg->bss_nses, list)
+	hash_for_each(cfg->bss_nses, i, nse_peer, list)
 		count += llist_count(&nse_peer->bvcs);
 
 	cmd->reply = talloc_strdup(cmd, "");
diff --git a/src/gbproxy/gb_proxy_peer.c b/src/gbproxy/gb_proxy_peer.c
index c48a78f..00bff20 100644
--- a/src/gbproxy/gb_proxy_peer.c
+++ b/src/gbproxy/gb_proxy_peer.c
@@ -86,8 +86,9 @@
 struct gbproxy_bvc *gbproxy_bvc_by_bvci(struct gbproxy_config *cfg, uint16_t bvci)
 {
 	struct gbproxy_nse *nse;
+	int i;
 
-	llist_for_each_entry(nse, &cfg->bss_nses, list) {
+	hash_for_each(cfg->bss_nses, i, nse, list) {
 		struct gbproxy_bvc *bvc;
 		llist_for_each_entry(bvc, &nse->bvcs, list) {
 			if (bvc->bvci == bvci)
@@ -102,11 +103,11 @@
 struct gbproxy_bvc *gbproxy_bvc_by_nsei(struct gbproxy_config *cfg,
 					  uint16_t nsei)
 {
-	struct gbproxy_nse *nse;
-	llist_for_each_entry(nse, &cfg->bss_nses, list) {
-		if (nse->nsei == nsei && !llist_empty(&nse->bvcs))
-			return llist_first_entry(&nse->bvcs, struct gbproxy_bvc, list);
-	}
+	struct gbproxy_nse *nse = gbproxy_nse_by_nsei(cfg, nsei);
+
+	if (nse && !llist_empty(&nse->bvcs))
+		return llist_first_entry(&nse->bvcs, struct gbproxy_bvc, list);
+
 	return NULL;
 }
 
@@ -116,8 +117,9 @@
 					 const uint8_t *ra)
 {
 	struct gbproxy_nse *nse;
+	int i;
 
-	llist_for_each_entry(nse, &cfg->bss_nses, list) {
+	hash_for_each(cfg->bss_nses, i, nse, list) {
 		struct gbproxy_bvc *bvc;
 		llist_for_each_entry(bvc, &nse->bvcs, list) {
 			if (!memcmp(bvc->ra, ra, 6))
@@ -134,8 +136,9 @@
 					 const uint8_t *la)
 {
 	struct gbproxy_nse *nse;
+	int i;
 
-	llist_for_each_entry(nse, &cfg->bss_nses, list) {
+	hash_for_each(cfg->bss_nses, i, nse, list) {
 		struct gbproxy_bvc *bvc;
 		llist_for_each_entry(bvc, &nse->bvcs, list) {
 			if (!memcmp(bvc->ra, la, 5))
@@ -151,8 +154,9 @@
 					 const uint8_t *la)
 {
 	struct gbproxy_nse *nse;
+	int i;
 
-	llist_for_each_entry(nse, &cfg->bss_nses, list) {
+	hash_for_each(cfg->bss_nses, i, nse, list) {
 		struct gbproxy_bvc *bvc;
 		llist_for_each_entry(bvc, &nse->bvcs, list) {
 			if (!memcmp(bvc->ra + 3, la + 3, 2))
@@ -268,11 +272,12 @@
  *  \param[in] bvci if 0: remove all BVCs; if != 0: BVCI of the single BVC to clean up */
 int gbproxy_cleanup_bvcs(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci)
 {
-	int counter = 0;
-	struct gbproxy_nse *nse, *ntmp;
+	int i, counter = 0;
+	struct gbproxy_nse *nse;
+	struct hlist_node *ntmp;
 	OSMO_ASSERT(cfg);
 
-	llist_for_each_entry_safe(nse, ntmp, &cfg->bss_nses, list) {
+	hash_for_each_safe(cfg->bss_nses, i, ntmp, nse, list) {
 		struct gbproxy_bvc *bvc, *tmp;
 		if (nse->nsei != nsei)
 			continue;
@@ -300,7 +305,7 @@
 	nse->nsei = nsei;
 	nse->cfg = cfg;
 
-	llist_add(&nse->list, &cfg->bss_nses);
+	hash_add(cfg->bss_nses, &nse->list, nsei);
 
 	INIT_LLIST_HEAD(&nse->bvcs);
 
@@ -313,7 +318,7 @@
 	if (!nse)
 		return;
 
-	llist_del(&nse->list);
+	hash_del(&nse->list);
 
 	llist_for_each_entry_safe(bvc, tmp, &nse->bvcs, list)
 		gbproxy_bvc_free(bvc);
@@ -326,7 +331,7 @@
 	struct gbproxy_nse *nse;
 	OSMO_ASSERT(cfg);
 
-	llist_for_each_entry(nse, &cfg->bss_nses, list) {
+	hash_for_each_possible(cfg->bss_nses, nse, list, nsei) {
 		if (nse->nsei == nsei)
 			return nse;
 	}
diff --git a/src/gbproxy/gb_proxy_vty.c b/src/gbproxy/gb_proxy_vty.c
index e79297d..da8afdc 100644
--- a/src/gbproxy/gb_proxy_vty.c
+++ b/src/gbproxy/gb_proxy_vty.c
@@ -422,13 +422,14 @@
       "Frequency at which the periodic timer is fired (in seconds)\n")
 {
 	struct gbproxy_nse *nse;
+	int i;
 	g_cfg->clean_stale_timer_freq = (unsigned int) atoi(argv[0]);
 
 	/* Re-schedule running timers soon in case prev frequency was really big
 	   and new frequency is desired to be lower. After initial run, periodic
 	   time is used. Use random() to avoid firing timers for all bvcs at
 	   the same time */
-	llist_for_each_entry(nse, &g_cfg->bss_nses, list) {
+	hash_for_each(g_cfg->bss_nses, i, nse, list) {
 		struct gbproxy_bvc *bvc;
 		llist_for_each_entry(bvc, &nse->bvcs, list)
 			osmo_timer_schedule(&bvc->clean_stale_timer,
@@ -445,9 +446,10 @@
 
 {
 	struct gbproxy_nse *nse;
+	int i;
 	g_cfg->clean_stale_timer_freq = 0;
 
-	llist_for_each_entry(nse, &g_cfg->bss_nses, list) {
+	hash_for_each(g_cfg->bss_nses, i, nse, list) {
 		struct gbproxy_bvc *bvc;
 		llist_for_each_entry(bvc, &nse->bvcs, list)
 			osmo_timer_del(&bvc->clean_stale_timer);
@@ -580,11 +582,12 @@
 {
 	struct gbproxy_nse *nse;
 	int show_stats = argc >= 1;
+	int i;
 
 	if (show_stats)
 		vty_out_rate_ctr_group(vty, "", g_cfg->ctrg);
 
-	llist_for_each_entry(nse, &g_cfg->bss_nses, list) {
+	hash_for_each(g_cfg->bss_nses, i, nse, list) {
 		struct gbproxy_bvc *bvc;
 		llist_for_each_entry(bvc, &nse->bvcs, list) {
 			gbprox_vty_print_bvc(vty, bvc);
@@ -602,11 +605,12 @@
 	struct gbproxy_nse *nse;
 	time_t now;
 	struct timespec ts = {0,};
+	int i;
 
 	osmo_clock_gettime(CLOCK_MONOTONIC, &ts);
 	now = ts.tv_sec;
 
-	llist_for_each_entry(nse, &g_cfg->bss_nses, list) {
+	hash_for_each(g_cfg->bss_nses, i, nse, list) {
 		struct gbproxy_bvc *bvc;
 		llist_for_each_entry(bvc, &nse->bvcs, list) {
 			struct gbproxy_link_info *link_info;
@@ -703,8 +707,9 @@
 		} else {
 			struct gbproxy_nse *nse;
 			struct gbproxy_bvc *bvc;
+			int i;
 			counter = 0;
-			llist_for_each_entry(nse, &g_cfg->bss_nses, list) {
+			hash_for_each(g_cfg->bss_nses, i, nse, list) {
 				if (nse->nsei != nsei)
 					continue;
 				llist_for_each_entry(bvc, &nse->bvcs, list) {