tbf: Add MS object management to TBF code

This commit adds MS object creation and cleanup to the TBF related
code. MS objects are created when a TBF that has been "anonymous" so
far gets associated with a TLLI. When a TBF is replaced by another,
the old TBF is detached and the new one is attached to the MS. When
all TBFs have been detached, the MS object gets deleted.

The TBF related code should not call attach_tbf/detach_tbf directly
but use set_ms instead to make sure, that the references are updated
properly. GprsMs::detach_tbf also calls set_ms(NULL) on the detached
TBF object.

The MS object is not really used yet, the focus is still on object
creation, TBF association, and cleanup.

Ticket: #1674
Sponsored-by: On-Waves ehf
diff --git a/src/bts.cpp b/src/bts.cpp
index 9886cf6..d027993 100644
--- a/src/bts.cpp
+++ b/src/bts.cpp
@@ -748,6 +748,12 @@
 		return;
 	}
 	tbf->update_tlli(tlli);
+
+	if (tbf->new_tbf())
+		tbf->new_tbf()->update_ms(tlli);
+	else
+		tbf->update_ms(tlli);
+
 	LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] %s Packet Control Ack\n", tbf_name(tbf));
 	tbf->poll_state = GPRS_RLCMAC_POLL_NONE;
 
diff --git a/src/gprs_ms.cpp b/src/gprs_ms.cpp
index db29d20..d9d74f4 100644
--- a/src/gprs_ms.cpp
+++ b/src/gprs_ms.cpp
@@ -67,6 +67,16 @@
 GprsMs::~GprsMs()
 {
 	LOGP(DRLCMAC, LOGL_INFO, "Destroying MS object, TLLI = 0x%08x\n", tlli());
+
+	if (m_ul_tbf) {
+		m_ul_tbf->set_ms(NULL);
+		m_ul_tbf = NULL;
+	}
+
+	if (m_dl_tbf) {
+		m_dl_tbf->set_ms(NULL);
+		m_dl_tbf = NULL;
+	}
 }
 
 void* GprsMs::operator new(size_t size)
@@ -148,6 +158,9 @@
 	LOGP(DRLCMAC, LOGL_INFO, "Detaching TBF from MS object, TLLI = 0x%08x, TBF = %s\n",
 		tlli(), tbf->name());
 
+	if (tbf->ms() == this)
+		tbf->set_ms(NULL);
+
 	update_status();
 }
 
diff --git a/src/tbf.cpp b/src/tbf.cpp
index b1377f0..89072e4 100644
--- a/src/tbf.cpp
+++ b/src/tbf.cpp
@@ -26,6 +26,7 @@
 #include <gprs_rlcmac.h>
 #include <gprs_debug.h>
 #include <gprs_bssgp_pcu.h>
+#include <gprs_ms.h>
 #include <decoding.h>
 
 extern "C" {
@@ -60,16 +61,43 @@
 				tbf_name(this), tbf_name(tbf));
 			return;
 		}
-		if (m_new_tbf != this)
+		if (m_new_tbf != this) {
 			LOGP(DRLCMAC, LOGL_NOTICE,
 				"%s m_new_tbf is already assigned to %s, "
 				"overwriting the old value with %s\n",
 				tbf_name(this), tbf_name(m_new_tbf), tbf_name(tbf));
+		}
 		/* Detach from other TBF */
 		m_new_tbf->m_old_tbf = NULL;
 	}
 	m_new_tbf = tbf;
 	tbf->m_old_tbf = this;
+
+	if (!tbf->ms())
+		tbf->set_ms(ms());
+}
+
+void gprs_rlcmac_tbf::set_ms(GprsMs *ms)
+{
+	if (m_ms == ms)
+		return;
+
+	if (m_ms)
+		m_ms->detach_tbf(this);
+
+	m_ms = ms;
+
+	if (m_ms)
+		m_ms->attach_tbf(this);
+}
+
+void gprs_rlcmac_tbf::update_ms(uint32_t tlli)
+{
+	if (!ms())
+		/* TODO: access the container instead when that is implemented */
+		set_ms(new GprsMs(tlli));
+	else
+		ms()->set_tlli(tlli);
 }
 
 gprs_rlcmac_ul_tbf *tbf_alloc_ul(struct gprs_rlcmac_bts *bts,
@@ -102,6 +130,7 @@
 	tbf->set_state(GPRS_RLCMAC_ASSIGN);
 	tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH);
 	tbf_timer_start(tbf, 3169, bts->t3169, 0);
+	tbf->update_ms(tlli);
 
 	return tbf;
 }
@@ -168,6 +197,8 @@
 				"tbf->m_old_tbf->m_new_tbf != tbf\n",
 				tbf_name(tbf));
 		}
+
+		tbf->m_old_tbf = NULL;
 	}
 
 	if (tbf->m_new_tbf) {
@@ -183,8 +214,13 @@
 				"tbf->m_new_tbf->m_old_tbf != tbf\n",
 				tbf_name(tbf));
 		}
+
+		tbf->m_new_tbf = NULL;
 	}
 
+	if (tbf->ms())
+		tbf->set_ms(NULL);
+
 	LOGP(DRLCMAC, LOGL_DEBUG, "********** TBF ends here **********\n");
 	talloc_free(tbf);
 }
@@ -438,6 +474,12 @@
 	llist_add(&tbf->list.list, &bts->ul_tbfs);
 	tbf->bts->tbf_ul_created();
 
+	if (old_tbf && old_tbf->ms())
+		tbf->set_ms(old_tbf->ms());
+
+	if (tbf->ms())
+		tbf->ms()->attach_ul_tbf(tbf);
+
 	return tbf;
 }
 
@@ -477,6 +519,12 @@
 	gettimeofday(&tbf->m_bw.dl_bw_tv, NULL);
 	gettimeofday(&tbf->m_bw.dl_loss_tv, NULL);
 
+	if (old_tbf && old_tbf->ms())
+		tbf->set_ms(old_tbf->ms());
+
+	if (tbf->ms())
+		tbf->ms()->attach_dl_tbf(tbf);
+
 	return tbf;
 }
 
@@ -811,6 +859,7 @@
 		return 0;
 	}
 	update_tlli(new_tlli);
+	update_ms(new_tlli);
 	LOGP(DRLCMACUL, LOGL_INFO, "Decoded premier TLLI=0x%08x of "
 		"UL DATA TFI=%d.\n", tlli(), rh->tfi);
 	if ((dl_tbf = bts->dl_tbf_by_tlli(tlli()))) {
diff --git a/src/tbf.h b/src/tbf.h
index bfe2875..43b0a27 100644
--- a/src/tbf.h
+++ b/src/tbf.h
@@ -30,6 +30,7 @@
 struct bssgp_bvc_ctx;
 struct rlc_ul_header;
 struct msgb;
+class GprsMs;
 
 /*
  * TBF instance
@@ -122,6 +123,9 @@
 	struct msgb *create_dl_ass(uint32_t fn);
 	struct msgb *create_ul_ass(uint32_t fn);
 
+	GprsMs *ms();
+	void set_ms(GprsMs *ms);
+
 	uint8_t tsc() const;
 
 	int rlcmac_diag();
@@ -139,6 +143,9 @@
 	bool is_tlli_valid() const;
 	void tlli_mark_valid();
 
+	/** MS updating */
+	void update_ms(uint32_t tlli);
+
 	uint8_t tfi() const;
 
 	const char *imsi() const;
@@ -229,6 +236,7 @@
 
 	static const char *tbf_state_name[6];
 
+	class GprsMs *m_ms;
 private:
 	mutable char m_name_buf[60];
 };
@@ -280,6 +288,11 @@
 	state = new_state;
 }
 
+inline GprsMs *gprs_rlcmac_tbf::ms()
+{
+	return m_ms;
+}
+
 inline uint32_t gprs_rlcmac_tbf::tlli() const
 {
 	return m_tlli;
diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp
index 0aa22f9..aec1535 100644
--- a/src/tbf_dl.cpp
+++ b/src/tbf_dl.cpp
@@ -199,6 +199,7 @@
 	dl_tbf->m_tlli = tlli;
 	dl_tbf->m_tlli_valid = 1;
 	dl_tbf->ta = ta;
+	dl_tbf->update_ms(tlli);
 
 	LOGP(DRLCMAC, LOGL_DEBUG, "%s [DOWNLINK] START\n", tbf_name(dl_tbf));
 
@@ -793,6 +794,7 @@
 	new_tbf->m_tlli_valid = m_tlli_valid;
 	new_tbf->ta = ta;
 	new_tbf->assign_imsi(m_imsi);
+	new_tbf->update_ms(m_tlli);
 
 	/* Copy over all data to the new TBF */
 	new_tbf->m_llc.put_frame(data, len);