tbf: Use plain old data structure (PODS) for llist management

The PODS struct has a back pointer to access the actual object.
llist_pods_for_each_entry traverses the list of struct llist_pods and
makes the entry available (through the back pointer).

Ticket: SYS#389
Sponsored-by: On-Waves ehf
diff --git a/src/bts.cpp b/src/bts.cpp
index ff1a065..77c3f3e 100644
--- a/src/bts.cpp
+++ b/src/bts.cpp
@@ -138,6 +138,7 @@
 {
 	uint8_t l, trx, ts, any_tbf = 0;
 	struct gprs_rlcmac_tbf *tbf;
+	struct llist_pods *lpods;
 	struct gprs_rlcmac_paging *pag;
 	uint8_t slot_mask[8];
 	int8_t first_ts; /* must be signed */
@@ -158,7 +159,7 @@
 	 * Don't mark, if TBF uses a different slot that is already marked. */
 	memset(slot_mask, 0, sizeof(slot_mask));
 	for (l = 0; tbfs_lists[l]; l++) {
-		llist_for_each_entry(tbf, tbfs_lists[l], list) {
+		llist_pods_for_each_entry(tbf, tbfs_lists[l], list, lpods) {
 			first_ts = -1;
 			for (ts = 0; ts < 8; ts++) {
 				if (tbf->pdch[ts]) {
@@ -232,15 +233,16 @@
 gprs_rlcmac_tbf *BTS::tbf_by_tlli(uint32_t tlli, enum gprs_rlcmac_tbf_direction dir)
 {
 	struct gprs_rlcmac_tbf *tbf;
+	struct llist_pods *lpods;
 	if (dir == GPRS_RLCMAC_UL_TBF) {
-		llist_for_each_entry(tbf, &m_bts.ul_tbfs, list) {
+		llist_pods_for_each_entry(tbf, &m_bts.ul_tbfs, list, lpods) {
 			OSMO_ASSERT(tbf->direction == dir);
 			if (tbf->state_is_not(GPRS_RLCMAC_RELEASING)
 			 && tbf->tlli() == tlli && tbf->is_tlli_valid())
 				return tbf;
 		}
 	} else {
-		llist_for_each_entry(tbf, &m_bts.dl_tbfs, list) {
+		llist_pods_for_each_entry(tbf, &m_bts.dl_tbfs, list, lpods) {
 			OSMO_ASSERT(tbf->direction == dir);
 			if (tbf->state_is_not(GPRS_RLCMAC_RELEASING)
 			 && tbf->tlli() == tlli)
@@ -253,10 +255,11 @@
 gprs_rlcmac_dl_tbf *BTS::dl_tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts)
 {
 	struct gprs_rlcmac_dl_tbf *tbf;
+	struct llist_pods *lpods;
 
 	/* only one TBF can poll on specific TS/FN, because scheduler can only
 	 * schedule one downlink control block (with polling) at a FN per TS */
-	llist_for_each_entry(tbf, &m_bts.dl_tbfs, list) {
+	llist_pods_for_each_entry(tbf, &m_bts.dl_tbfs, list, lpods) {
 		if (tbf->state_is_not(GPRS_RLCMAC_RELEASING)
 		 && tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
 		 && tbf->poll_fn == fn && tbf->trx->trx_no == trx
@@ -270,10 +273,11 @@
 gprs_rlcmac_ul_tbf *BTS::ul_tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts)
 {
 	struct gprs_rlcmac_ul_tbf *tbf;
+	struct llist_pods *lpods;
 
 	/* only one TBF can poll on specific TS/FN, because scheduler can only
 	 * schedule one downlink control block (with polling) at a FN per TS */
-	llist_for_each_entry(tbf, &m_bts.ul_tbfs, list) {
+	llist_pods_for_each_entry(tbf, &m_bts.ul_tbfs, list, lpods) {
 		if (tbf->state_is_not(GPRS_RLCMAC_RELEASING)
 		 && tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
 		 && tbf->poll_fn == fn && tbf->trx->trx_no == trx
@@ -1042,8 +1046,9 @@
 		enum gprs_rlcmac_tbf_direction dir)
 {
 	gprs_rlcmac_tbf *tbf;
+	struct llist_pods *lpods;
 
-	llist_for_each_entry(tbf, tbf_list, list) {
+	llist_pods_for_each_entry(tbf, tbf_list, list, lpods) {
 		OSMO_ASSERT(tbf->direction == dir);
 		if (tbf->tfi() != tfi)
 			continue;
diff --git a/src/gprs_rlcmac_sched.cpp b/src/gprs_rlcmac_sched.cpp
index 92e97ec..dab38d8 100644
--- a/src/gprs_rlcmac_sched.cpp
+++ b/src/gprs_rlcmac_sched.cpp
@@ -31,6 +31,7 @@
 		    struct gprs_rlcmac_tbf **ul_ack_tbf)
 {
 	struct gprs_rlcmac_tbf *tbf;
+	struct llist_pods *lpods;
 	uint32_t poll_fn;
 
 	/* check special TBF for events */
@@ -38,7 +39,7 @@
 	if ((block_nr % 3) == 2)
 		poll_fn ++;
 	poll_fn = poll_fn % 2715648;
-	llist_for_each_entry(tbf, &bts->ul_tbfs, list) {
+	llist_pods_for_each_entry(tbf, &bts->ul_tbfs, list, lpods) {
 		/* this trx, this ts */
 		if (tbf->trx->trx_no != trx || tbf->control_ts != ts)
 			continue;
@@ -54,7 +55,7 @@
 			*ul_ass_tbf = tbf;
 #warning "Is this supposed to be fair? The last TBF for each wins? Maybe use llist_add_tail and skip once we have all states?"
 	}
-	llist_for_each_entry(tbf, &bts->dl_tbfs, list) {
+	llist_pods_for_each_entry(tbf, &bts->dl_tbfs, list, lpods) {
 		/* this trx, this ts */
 		if (tbf->trx->trx_no != trx || tbf->control_ts != ts)
 			continue;
diff --git a/src/poll_controller.cpp b/src/poll_controller.cpp
index 0f46472..dea75ed 100644
--- a/src/poll_controller.cpp
+++ b/src/poll_controller.cpp
@@ -33,10 +33,11 @@
 	struct gprs_rlcmac_bts *bts = m_bts.bts_data();
 	struct gprs_rlcmac_tbf *tbf;
 	struct gprs_rlcmac_sba *sba, *sba2;
+	struct llist_pods *lpods;
 	uint32_t elapsed;
 
 	/* check for poll timeout */
-	llist_for_each_entry(tbf, &bts->ul_tbfs, list) {
+	llist_pods_for_each_entry(tbf, &bts->ul_tbfs, list, lpods) {
 		if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
 			elapsed = (frame_number + 2715648 - tbf->poll_fn)
 								% 2715648;
@@ -44,7 +45,7 @@
 				tbf->poll_timeout();
 		}
 	}
-	llist_for_each_entry(tbf, &bts->dl_tbfs, list) {
+	llist_pods_for_each_entry(tbf, &bts->dl_tbfs, list, lpods) {
 		if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
 			elapsed = (frame_number + 2715648 - tbf->poll_fn)
 								% 2715648;
diff --git a/src/tbf.cpp b/src/tbf.cpp
index 1201642..2257018 100644
--- a/src/tbf.cpp
+++ b/src/tbf.cpp
@@ -286,7 +286,7 @@
 	#warning "TODO: Could/Should generate  bssgp_tx_llc_discarded"
 	tbf->m_llc.clear(tbf->bts);
 	tbf_unlink_pdch(tbf);
-	llist_del(&tbf->list);
+	llist_del(&tbf->list.list);
 
 	if (tbf->direction == GPRS_RLCMAC_UL_TBF)
 		tbf->bts->tbf_ul_freed();
@@ -491,6 +491,9 @@
 	if (!tbf)
 		return -1;
 
+	/* Back pointer for PODS llist compatibility */
+	tbf->list.back = tbf;
+
 	tbf->m_created_ts = time(NULL);
 	tbf->bts = bts->bts;
 	tbf->m_tfi = tfi;
@@ -548,7 +551,7 @@
 		return NULL;
 	}
 
-	llist_add(&tbf->list, &bts->ul_tbfs);
+	llist_add(&tbf->list.list, &bts->ul_tbfs);
 	tbf->bts->tbf_ul_created();
 
 	return tbf;
@@ -581,7 +584,7 @@
 		return NULL;
 	}
 
-	llist_add(&tbf->list, &bts->dl_tbfs);
+	llist_add(&tbf->list.list, &bts->dl_tbfs);
 	tbf->bts->tbf_dl_created();
 
 	return tbf;
@@ -1805,11 +1808,11 @@
 
 void gprs_rlcmac_tbf::rotate_in_list()
 {
-	llist_del(&list);
+	llist_del(&list.list);
 	if (direction == GPRS_RLCMAC_UL_TBF)
-		llist_add(&list, &bts->bts_data()->ul_tbfs);
+		llist_add(&list.list, &bts->bts_data()->ul_tbfs);
 	else
-		llist_add(&list, &bts->bts_data()->dl_tbfs);
+		llist_add(&list.list, &bts->bts_data()->dl_tbfs);
 }
 
 uint8_t gprs_rlcmac_tbf::tsc() const
@@ -1819,7 +1822,7 @@
 
 void tbf_print_vty_info(struct vty *vty, llist_head *ltbf)
 {
-	gprs_rlcmac_tbf *tbf = llist_entry(ltbf, gprs_rlcmac_tbf, list);
+	gprs_rlcmac_tbf *tbf = llist_pods_entry(ltbf, gprs_rlcmac_tbf);
 
 	vty_out(vty, "TBF: TFI=%d TLLI=0x%08x (%s) DIR=%s IMSI=%s%s", tbf->tfi(),
 			tbf->tlli(), tbf->is_tlli_valid() ? "valid" : "invalid",
diff --git a/src/tbf.h b/src/tbf.h
index 4d20987..95355bd 100644
--- a/src/tbf.h
+++ b/src/tbf.h
@@ -84,6 +84,28 @@
 #define GPRS_RLCMAC_FLAG_TO_DL_ASS	7
 #define GPRS_RLCMAC_FLAG_TO_MASK	0xf0 /* timeout bits */
 
+struct llist_pods {
+	struct llist_head list;
+	void *back;
+};
+
+#define llist_pods_entry(ptr, type) \
+	((type *)(container_of(ptr, struct llist_pods, list)->back))
+
+/**
+ * llist_pods_for_each_entry - like llist_for_each_entry, but uses
+ * struct llist_pods ->back to access the entry.
+ * This is necessary for non-PODS classes because container_of is
+ * not guaranteed to work anymore. */
+#define llist_pods_for_each_entry(pos, head, member, lpods)			\
+	for (lpods = llist_entry((head)->next, typeof(struct llist_pods), list), \
+		     pos = ((typeof(pos))lpods->back),	\
+		     prefetch(pos->member.list.next);				\
+	     &lpods->list != (head);					\
+	     lpods = llist_entry(lpods->list.next, typeof(struct llist_pods), list), \
+		     pos = ((typeof(pos))lpods->back),\
+		     prefetch(pos->member.list.next))
+
 struct gprs_rlcmac_tbf {
 
 	static void free_all(struct gprs_rlcmac_trx *trx);
@@ -143,7 +165,7 @@
 	/* attempt to make things a bit more fair */
 	void rotate_in_list();
 
-	struct llist_head list;
+	struct llist_pods list;
 	uint32_t state_flags;
 	enum gprs_rlcmac_tbf_direction direction;
 	struct gprs_rlcmac_trx *trx;