alloc: Make alloc_algorithm_dynamic stateful

Currently there is no persistent state being used in
alloc_algorithm_dynamic. So algorithm B is even used in persistent
high usage scenarios. If there are many active TBFs, multislot
assigments are not fair, because MS of a "higher" multislot class get
higher troughputs. On the other hand, as long as all PDCH are busy no
bandwidth will be wasted even if all MS use algorithm A.

This commit modifies alloc_algorithm_dynamic to disable algorithm B
when that call fails. It then keeps it disabled until there is a
single PDCH which is idle (it is considered idle, if there is at most
one active DL TBF assigned to it).

Sponsored-by: On-Waves ehf
diff --git a/src/gprs_rlcmac_ts_alloc.cpp b/src/gprs_rlcmac_ts_alloc.cpp
index e2c2a0f..3714b30 100644
--- a/src/gprs_rlcmac_ts_alloc.cpp
+++ b/src/gprs_rlcmac_ts_alloc.cpp
@@ -28,6 +28,9 @@
 #include <errno.h>
 #include <values.h>
 
+/* Consider a PDCH as idle if has at most this number of TBFs assigned to it */
+#define PDCH_IDLE_TBF_THRESH	1
+
 /* 3GPP TS 05.02 Annex B.1 */
 
 #define MS_NA	255 /* N/A */
@@ -363,6 +366,30 @@
 	return -EBUSY;
 }
 
+static struct gprs_rlcmac_pdch * find_idle_pdch(BTS *bts)
+{
+	unsigned trx_no;
+	unsigned ts;
+	struct gprs_rlcmac_bts *bts_data = bts->bts_data();
+
+	/* Find the first PDCH with an unused DL TS */
+	for (trx_no = 0; trx_no < ARRAY_SIZE(bts_data->trx); trx_no += 1) {
+		struct gprs_rlcmac_trx *trx = &bts_data->trx[trx_no];
+		for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
+			struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
+			if (!pdch->is_enabled())
+				continue;
+
+			if (pdch->num_tbfs(GPRS_RLCMAC_DL_TBF) > PDCH_IDLE_TBF_THRESH)
+				continue;
+
+			return pdch;
+		}
+	}
+
+	return NULL;
+}
+
 static int tfi_find_free(BTS *bts, const GprsMs *ms,
 	enum gprs_rlcmac_tbf_direction dir, int use_trx, int *trx_no_)
 {
@@ -1030,9 +1057,22 @@
 {
 	int rc;
 
-	rc = alloc_algorithm_b(bts, ms_, tbf_, cust, single, use_trx);
-	if (rc >= 0)
-		return rc;
+	/* Reset load_is_high if there is at least one idle PDCH */
+	if (bts->multislot_disabled) {
+		bts->multislot_disabled = find_idle_pdch(bts->bts) == NULL;
+		if (!bts->multislot_disabled)
+			LOGP(DRLCMAC, LOGL_DEBUG, "Enabling algorithm B\n");
+	}
+
+	if (!bts->multislot_disabled) {
+		rc = alloc_algorithm_b(bts, ms_, tbf_, cust, single, use_trx);
+		if (rc >= 0)
+			return rc;
+
+		if (!bts->multislot_disabled)
+			LOGP(DRLCMAC, LOGL_DEBUG, "Disabling algorithm B\n");
+		bts->multislot_disabled = 1;
+	}
 
 	rc = alloc_algorithm_a(bts, ms_, tbf_, cust, single, use_trx);
 	return rc;