Avoid losing DL-TBF during MS merge

It is desired to free pending DL-TBF under some scenarios, which somehow
are related to those where we call the ms_merge() procedure since it's at
time an MS can be identified when coming from packet-idle state.

Until now, the freeing of those DL-TBFs happen because the ms_merge()
procedure doesn't migrate DL-TBF from "old_ms" to "ms". This was done
manually under the cases where it was deemed necessary before calling
the ms_merge() procedure 8because old_ms and its tbfs are gone after
returning from it).
This logic, though convinient for the specific cases at hand, is quite
confusing for readers and program execution since one would expect the
ms merge to, well, merge everything.

Therefore, this patch changes the ms_merge() logic to always merge the
DL-TBF, and only under the specific cases where it makes sense to free
it, do so explicitly after MS merge, where all the info has been updated
and united.
2 code paths are now explicitly freeing the existing DL-TBF when needed:
- TBF_EV_FIRST_UL_DATA_RECVD: 1st data (containing TLLI) is
  received from MS, hence identifyng the MS and potentially having been
  merged with some old MS which used to have a DL-TBF, most probably in
  process of being assigned through CCCH (PCH). This event is triggered
  during MS using 1phase-access, and we should drop the exsitng DL-TBF
  because MS just came from packet-idle mode (CCCH).
- rcv_resource_request(): PktResourceRequest is received at an scheduled
  SBA, meaning the MS is doing 2phase-access, meaning MS also came from
  packet-idle mode (CCCH), so previous DL-TBF can be dropped.

Related: OS#5700
Change-Id: I29f4ffa904d79d58275c6596cc5ef6790b6e68c6
diff --git a/src/gprs_ms.c b/src/gprs_ms.c
index 10d761d..f233967 100644
--- a/src/gprs_ms.c
+++ b/src/gprs_ms.c
@@ -452,6 +452,14 @@
 
 	llc_queue_move_and_merge(&ms->llc_queue, &old_ms->llc_queue);
 
+	if (!ms_dl_tbf(ms) && ms_dl_tbf(old_ms)) {
+		struct gprs_rlcmac_dl_tbf *dl_tbf = ms_dl_tbf(old_ms);
+		LOGPTBFDL(dl_tbf, LOGL_NOTICE,
+			  "Merge MS: Move DL TBF: %s => %s\n",
+			  old_ms_name, ms_name(ms));
+		/* Move the DL TBF to the new MS */
+		tbf_set_ms(dl_tbf_as_tbf(dl_tbf), ms);
+	}
 
 	/* Clean up the old MS object */
 	/* TODO: Use timer? */
diff --git a/src/pdch.cpp b/src/pdch.cpp
index e979cf0..e9b9852 100644
--- a/src/pdch.cpp
+++ b/src/pdch.cpp
@@ -657,6 +657,7 @@
 
 	if (request->ID.UnionType) {
 		struct gprs_rlcmac_ul_tbf *ul_tbf = NULL;
+		struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
 		uint32_t tlli = request->ID.u.TLLI;
 
 		GprsMs *ms = bts_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
@@ -693,8 +694,17 @@
 				tbf_free(ul_tbf);
 				ul_tbf = NULL;
 			}
-			/* MS seized the PDCH answering on the SBA: */
-			bts_do_rate_ctr_inc(bts, CTR_IMMEDIATE_ASSIGN_UL_TBF_CONTENTION_RESOLUTION_SUCCESS);
+			/* Similarly, it is for sure not using any DL-TBF. We
+			* still may have some because we were trying to assign a
+			* DL-TBF over CCCH when the MS proactively requested for a
+			* UL-TBF. In that case we'll need to re-assigna new DL-TBF through PACCH when contention resolution is done: */
+			if ((dl_tbf = ms_dl_tbf(ms))) {
+				/* Get rid of previous finished UL TBF before providing a new one */
+				LOGPTBFDL(dl_tbf, LOGL_NOTICE,
+					  "Got PACKET RESOURCE REQ while DL-TBF pending, killing it\n");
+				tbf_free(dl_tbf);
+				dl_tbf = NULL;
+			}
 			break;
 		case PDCH_ULC_NODE_TBF_POLL:
 			if (item->tbf_poll.poll_tbf->direction != GPRS_RLCMAC_UL_TBF) {
diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp
index c63af58..cc809da 100644
--- a/src/tbf_dl.cpp
+++ b/src/tbf_dl.cpp
@@ -191,7 +191,6 @@
 		  const uint16_t delay_csec,
 		  const uint8_t *data, const uint16_t len)
 {
-	struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
 	int rc;
 	GprsMs *ms, *ms_old;
 
@@ -208,20 +207,8 @@
 			LOGP(DTBF, LOGL_NOTICE,
 			     "There is a new MS object for the same MS: (0x%08x, '%s') -> (0x%08x, '%s')\n",
 			     ms_tlli(ms_old), ms_imsi(ms_old), ms_tlli(ms), ms_imsi(ms));
-
-			ms_ref(ms_old);
-
-			if (!ms_dl_tbf(ms) && ms_dl_tbf(ms_old)) {
-				LOGP(DTBF, LOGL_NOTICE,
-				     "IMSI %s, old TBF %s: moving DL TBF to new MS object\n",
-				     imsi ? : "unknown", ms_dl_tbf(ms_old)->name());
-				dl_tbf = ms_dl_tbf(ms_old);
-				/* Move the DL TBF to the new MS */
-				dl_tbf->set_ms(ms);
-			}
 			ms_merge_and_clear_ms(ms, ms_old);
-
-			ms_unref(ms_old);
+			/* old_ms may no longer be available here */
 		}
 	}
 
diff --git a/src/tbf_fsm.c b/src/tbf_fsm.c
index b236d27..9ddbd69 100644
--- a/src/tbf_fsm.c
+++ b/src/tbf_fsm.c
@@ -208,6 +208,7 @@
 {
 	struct tbf_fsm_ctx *ctx = (struct tbf_fsm_ctx *)fi->priv;
 	struct GprsMs *ms = tbf_ms(ctx->tbf);
+	struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
 
 	switch (event) {
 	case TBF_EV_FIRST_UL_DATA_RECVD:
@@ -217,6 +218,17 @@
 		 * comprises the TLLI value that identifies the mobile station and the
 		 * TFI value associated with the TBF." */
 		bts_pch_timer_stop(ms->bts, ms);
+		/* We may still have some DL-TBF waiting for assignment in PCH,
+		 * which clearly won't happen since the MS is on PDCH now. Get rid
+		 * of it, it will be re-assigned on PACCH when contention
+		 * resolution at the MS side is done (1st UL ACK/NACK sent) */
+		if ((dl_tbf = ms_dl_tbf(ms))) {
+			/* Get rid of previous finished UL TBF before providing a new one */
+			LOGPTBFDL(dl_tbf, LOGL_NOTICE,
+					"Got first UL data while DL-TBF pending, killing it\n");
+			tbf_free(dl_tbf_as_tbf(dl_tbf));
+			dl_tbf = NULL;
+		}
 		break;
 	case TBF_EV_DL_ACKNACK_MISS:
 		OSMO_ASSERT(tbf_direction(ctx->tbf) == GPRS_RLCMAC_DL_TBF);