bts: Add counter availablePDCHAllocatedTime

We basically want to probe whether it's possible to allocate TBFs, or
whether we know it will fail due to all main resources being already in
use (TFI, USF).

Having bts_all_pdch_allocated() return false doesn't mean though that an
MS will be able to allocate a TBF for sure. That's because further
restrictions are applied based on MS: whether it was already attached to
a specific TRX, whether the ms_class allows for a certain multislot
combination, etc. However, it should provide a general idea on whether
for sure the PCU is unable to provide more allocations. More fine
grained state about failures can still be followed by looking at
tbf:alloc:failed:* rate counters.

Related: SYS#4878
Depends: Iabb17a08e6e1a86f168cdb008fba05ecd4776bdd (libosmocore)
Change-Id: Ie0f0c451558817bddc3fe1a0f0df531f14c9f1d3
diff --git a/src/bts.cpp b/src/bts.cpp
index 6fabc90..05a9cc0 100644
--- a/src/bts.cpp
+++ b/src/bts.cpp
@@ -83,6 +83,20 @@
 	{ .T=3191, .default_val=5,   .unit=OSMO_TDEF_S,  .desc="Reuse of TFI(s) after sending (1) last RLC Data Block on TBF(s), or (2) PACKET TBF RELEASE for an MBMS radio bearer", .val=0 },
 	{ .T=3193, .default_val=100, .unit=OSMO_TDEF_MS, .desc="Reuse of TFI(s) after reception of final PACKET DOWNLINK ACK/NACK from MS for TBF", .val=0 },
 	{ .T=3195, .default_val=5,   .unit=OSMO_TDEF_S,  .desc="Reuse of TFI(s) upon no response from the MS (radio failure or cell change) for TBF/MBMS radio bearer", .val=0 },
+	{ .T = -16, .default_val = 1000, .unit = OSMO_TDEF_MS,
+		.desc = "Granularity for *:all_allocated rate counters: amount of milliseconds that one counter increment"
+			" represents. See also X17, X18" },
+	{ .T = -17, .default_val = 0, .unit = OSMO_TDEF_MS,
+		.desc = "Rounding threshold for *:all_allocated rate counters: round up to the next counter increment"
+			" after this many milliseconds. If set to half of X16 (or 0), employ the usual round() behavior:"
+			" round up after half of a granularity period. If set to 1, behave like ceil(): already"
+			" increment the counter immediately when all channels are allocated. If set >= X16, behave like"
+			" floor(): only increment after a full X16 period of all channels being occupied."
+			" See also X16, X18" },
+	{ .T = -18, .default_val = 60000, .unit = OSMO_TDEF_MS,
+		.desc = "Forget-sum period for *:all_allocated rate counters:"
+			" after this amount of idle time, forget internally cumulated time remainders. Zero to always"
+			" keep remainders. See also X16, X17." },
 	{ .T=0, .default_val=0, .unit=OSMO_TDEF_S, .desc=NULL, .val=0 } /* empty item at the end */
 };
 
@@ -92,6 +106,7 @@
  * the code below.
  */
 static const struct rate_ctr_desc bts_ctr_description[] = {
+	{ "pdch:all_allocated",		"Cumulative counter of seconds where all enabled PDCH resources were allocated"},
 	{ "tbf:dl:alloc",		"TBF DL Allocated     "},
 	{ "tbf:dl:freed",		"TBF DL Freed         "},
 	{ "tbf:dl:aborted",		"TBF DL Aborted       "},
@@ -231,6 +246,8 @@
 	bts->ms_store->cleanup();
 	delete bts->ms_store;
 
+	osmo_time_cc_cleanup(&bts->all_allocated_pdch);
+
 	if (bts->ratectrs) {
 		rate_ctr_group_free(bts->ratectrs);
 		bts->ratectrs = NULL;
@@ -300,6 +317,16 @@
 	bts->statg = osmo_stat_item_group_alloc(tall_pcu_ctx, &bts_statg_desc, 0);
 	OSMO_ASSERT(bts->statg);
 
+	osmo_time_cc_init(&bts->all_allocated_pdch);
+	struct osmo_time_cc_cfg *cc_cfg = &bts->all_allocated_pdch.cfg;
+	cc_cfg->gran_usec = 1*1000000,
+	cc_cfg->forget_sum_usec = 60*1000000,
+	cc_cfg->rate_ctr = rate_ctr_group_get_ctr(bts->ratectrs, CTR_PDCH_ALL_ALLOCATED),
+	cc_cfg->T_gran = -16,
+	cc_cfg->T_round_threshold = -17,
+	cc_cfg->T_forget_sum = -18,
+	cc_cfg->T_defs = T_defs_bts,
+
 	llist_add_tail(&bts->list, &pcu->bts_list);
 
 	INIT_LLIST_HEAD(&bts->pch_timer);
@@ -1371,3 +1398,20 @@
 	 * B.2 Closed loop control */
 	return 0;
 }
+
+/* Used by counter availablePDCHAllocatedTime, TS 52.402 B.2.1.45 "All available PDCH allocated time" */
+bool bts_all_pdch_allocated(const struct gprs_rlcmac_bts *bts)
+{
+	unsigned trx_no, ts_no;
+	for (trx_no = 0; trx_no < ARRAY_SIZE(bts->trx); trx_no++) {
+		const struct gprs_rlcmac_trx *trx = &bts->trx[trx_no];
+		for (ts_no = 0; ts_no < ARRAY_SIZE(trx->pdch); ts_no++) {
+			const struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts_no];
+			if (!pdch_is_enabled(pdch))
+				continue;
+			if(!pdch_is_full(pdch))
+				return false;
+		}
+	}
+	return true;
+}