Move out of alloc_algo code modifying the data model

This way the alloc_algo() becomes idempotent, simplifying implementation
of new alloc_algos as well as rolling back if allocation fails (for
instance because some resource is exhausted at the time).

For now the code applying the results is moved to tbf::alloc_algo(), but
it will eventually get out of tbf code, so that the MS object is
responsible for running it. As a result, there's no even need to create
TBF object before trying to allocate resources, which will help furher in
rollback operations described above.

Change-Id: I5ffd00f5f80bde4b73b78db44896f65e70e12b20
diff --git a/src/alloc_algo.cpp b/src/alloc_algo.cpp
index bc44cbf..c026590 100644
--- a/src/alloc_algo.cpp
+++ b/src/alloc_algo.cpp
@@ -214,29 +214,6 @@
 	return min_ts;
 }
 
-static void attach_tbf_to_pdch(struct gprs_rlcmac_pdch *pdch,
-	struct gprs_rlcmac_tbf *tbf)
-{
-	if (tbf->pdch[pdch->ts_no])
-		tbf->pdch[pdch->ts_no]->detach_tbf(tbf);
-
-	tbf->pdch[pdch->ts_no] = pdch;
-	pdch->attach_tbf(tbf);
-}
-
-static void assign_uplink_tbf_usf(struct gprs_rlcmac_pdch *pdch, struct gprs_rlcmac_ul_tbf *tbf, uint8_t tfi, int8_t usf)
-{
-	tbf->m_tfi = tfi;
-	tbf->m_usf[pdch->ts_no] = usf;
-	attach_tbf_to_pdch(pdch, tbf);
-}
-
-static void assign_dlink_tbf(struct gprs_rlcmac_pdch *pdch, struct gprs_rlcmac_dl_tbf *tbf, uint8_t tfi)
-{
-	tbf->m_tfi = tfi;
-	attach_tbf_to_pdch(pdch, tbf);
-}
-
 static int find_trx(const struct alloc_resources_req *req)
 {
 	unsigned trx_no;
@@ -331,11 +308,12 @@
  * Assign single slot for uplink and downlink
  *
  *  \param[in] req Contains all the requested params
+ *  \param[out] res The resolution/response for the allocation request
  *  \returns negative error code or 0 on success
  */
-int alloc_algorithm_a(const struct alloc_resources_req *req)
+int alloc_algorithm_a(const struct alloc_resources_req *req,
+		      struct alloc_resources_res *res)
 {
-	struct gprs_rlcmac_pdch *pdch;
 	int ts = -1;
 	uint8_t ul_slots, dl_slots;
 	int trx_no;
@@ -388,26 +366,15 @@
 		return -EBUSY;
 	}
 
-	pdch = &trx->pdch[ts];
+	res->trx = trx;
+	res->first_common_ts = &trx->pdch[ts];
+	res->reserved_ul_slots = 1 << ts;
+	res->reserved_dl_slots = 1 << ts;
+	res->ass_slots_mask = 1 << ts;
+	res->upgrade_to_multislot = false;
+	res->tfi = tfi;
+	res->usf[ts] = usf;
 
-	/* The allocation will be successful, so the system state and tbf/ms
-	 * may be modified from now on. */
-	if (req->direction == GPRS_RLCMAC_UL_TBF) {
-		struct gprs_rlcmac_ul_tbf *ul_tbf = tbf_as_ul_tbf(req->tbf);
-		LOGPSL(req, LOGL_DEBUG, "Assign uplink TS=%d TFI=%d USF=%d\n", ts, tfi, usf);
-		assign_uplink_tbf_usf(pdch, ul_tbf, tfi, usf);
-	} else {
-		struct gprs_rlcmac_dl_tbf *dl_tbf = tbf_as_dl_tbf(req->tbf);
-		LOGPSL(req, LOGL_DEBUG, "Assign downlink TS=%d TFI=%d\n", ts, tfi);
-		assign_dlink_tbf(pdch, dl_tbf, tfi);
-	}
-
-	req->tbf->trx = trx;
-	/* the only one TS is the common TS */
-	ms_set_reserved_slots(req->ms, trx, 1 << ts, 1 << ts);
-	ms_set_first_common_ts(req->ms, pdch);
-
-	req->tbf->upgrade_to_multislot = false;
 	bts_do_rate_ctr_inc(req->bts, CTR_TBF_ALLOC_ALGO_A);
 	return 0;
 }
@@ -766,76 +733,17 @@
 	return ul_slots;
 }
 
-/*! Update MS' reserved timeslots
- *
- *  \param[in,out] trx Pointer to TRX struct
- *  \param[in,out] ms_ Pointer to MS object
- *  \param[in] tbf_ Pointer to TBF struct
- *  \param[in] res_ul_slots Newly reserved UL slots
- *  \param[in] res_dl_slots Newly reserved DL slots
- */
-static void update_ms_reserved_slots(gprs_rlcmac_trx *trx, GprsMs *ms, uint8_t res_ul_slots, uint8_t res_dl_slots)
-{
-	if (res_ul_slots == ms_reserved_ul_slots(ms) && res_dl_slots == ms_reserved_dl_slots(ms))
-		return;
-
-	/* The reserved slots have changed, update the MS */
-	ms_set_reserved_slots(ms, trx, res_ul_slots, res_dl_slots);
-}
-
-/*! Assign given UL timeslots to UL TBF
- *
- *  \param[in,out] ul_tbf Pointer to UL TBF struct
- *  \param[in,out] trx Pointer to TRX object
- *  \param[in] ul_slots Set of slots to be assigned
- *  \param[in] tfi selected TFI
- *  \param[in] usf selected USF
- */
-static void assign_ul_tbf_slots(struct gprs_rlcmac_ul_tbf *ul_tbf, gprs_rlcmac_trx *trx, uint8_t ul_slots, int tfi,
-				int *usf)
-{
-	uint8_t ts;
-
-	for (ts = 0; ts < 8; ts++) {
-		if (!(ul_slots & (1 << ts)))
-			continue;
-
-		OSMO_ASSERT(usf[ts] >= 0);
-
-		LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning UL TS %u\n", ts);
-		assign_uplink_tbf_usf(&trx->pdch[ts], ul_tbf, tfi, usf[ts]);
-	}
-}
-
-/*! Assign given DL timeslots to DL TBF
- *
- *  \param[in,out] dl_tbf Pointer to DL TBF struct
- *  \param[in,out] trx Pointer to TRX object
- *  \param[in] ul_slots Set of slots to be assigned
- *  \param[in] tfi selected TFI
- */
-static void assign_dl_tbf_slots(struct gprs_rlcmac_dl_tbf *dl_tbf, gprs_rlcmac_trx *trx, uint8_t dl_slots, int tfi)
-{
-	uint8_t ts;
-
-	for (ts = 0; ts < 8; ts++) {
-		if (!(dl_slots & (1 << ts)))
-			continue;
-
-		LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning DL TS %u\n", ts);
-		assign_dlink_tbf(&trx->pdch[ts], dl_tbf, tfi);
-	}
-}
-
 /*! Slot Allocation: Algorithm B
  *
  * Assign as many downlink slots as possible.
  * Assign one uplink slot. (With free USF)
  *
  *  \param[in] req Contains all the requested params
+ *  \param[out] res The resolution/response for the allocation request
  *  \returns negative error code or 0 on success
  */
-int alloc_algorithm_b(const struct alloc_resources_req *req)
+int alloc_algorithm_b(const struct alloc_resources_req *req,
+		      struct alloc_resources_res *res)
 {
 	uint8_t dl_slots;
 	uint8_t ul_slots;
@@ -845,15 +753,18 @@
 	uint8_t slotcount = 0;
 	uint8_t reserve_count = 0, trx_no;
 	int first_ts;
-	int usf[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
 	int rc;
 	int tfi;
+	unsigned int i;
 	gprs_rlcmac_trx *trx;
 	char slot_info[9] = { 0 };
 	struct gprs_rlcmac_pdch *first_common_ts = ms_first_common_ts(req->ms);
 
 	LOGPAL(req, "B", LOGL_DEBUG, "Alloc start\n");
 
+	for (i = 0; i < ARRAY_SIZE(res->usf); i++)
+		res->usf[i] = -1;
+
 	/* Step 1: Get current state from the MS object */
 
 	reserved_dl_slots = ms_reserved_dl_slots(req->ms);
@@ -889,7 +800,7 @@
 		dl_slots = rc;
 		count_slots(dl_slots, reserved_dl_slots, &slotcount, &reserve_count);
 	} else {
-		rc = allocate_usf(trx, rc, dl_slots, usf);
+		rc = allocate_usf(trx, rc, dl_slots, &res->usf[0]);
 		if (rc < 0)
 			return rc;
 
@@ -912,30 +823,27 @@
 	}
 	first_common_ts = &trx->pdch[first_common_tn];
 
+	res->trx = trx;
+	res->first_common_ts = first_common_ts;
+	res->reserved_ul_slots = reserved_ul_slots;
+	res->reserved_dl_slots = reserved_dl_slots;
+	res->tfi = tfi;
+	/* res->usf is already filled in above */
 	if (req->single && slotcount) {
-		req->tbf->upgrade_to_multislot = (reserve_count > slotcount);
+		res->upgrade_to_multislot = (reserve_count > slotcount);
 		LOGPAL(req, "B", LOGL_INFO, "using single slot at TS %d\n", first_ts);
 	} else {
-		req->tbf->upgrade_to_multislot = false;
+		res->upgrade_to_multislot = false;
 		LOGPAL(req, "B", LOGL_INFO, "using %d slots\n", slotcount);
 	}
 
 	ts_format(slot_info, dl_slots, ul_slots);
 	LOGP(DRLCMAC, LOGL_DEBUG, "- Available DL/UL slots: (TS=0)\"%s\"(TS=7)\n", slot_info);
 
-	/* The allocation will be successful, so the system state and tbf/ms
-	 * may be modified from now on. */
-
-	/* Step 4: Update MS and TBF and really allocate the resources */
-
-	update_ms_reserved_slots(trx, req->ms, reserved_ul_slots, reserved_dl_slots);
-	ms_set_first_common_ts(req->ms, first_common_ts);
-	req->tbf->trx = trx;
-
 	if (req->direction == GPRS_RLCMAC_DL_TBF)
-		assign_dl_tbf_slots(tbf_as_dl_tbf(req->tbf), trx, dl_slots, tfi);
+		res->ass_slots_mask = dl_slots;
 	else
-		assign_ul_tbf_slots(tbf_as_ul_tbf(req->tbf), trx, ul_slots, tfi, usf);
+		res->ass_slots_mask = ul_slots;
 
 	bts_do_rate_ctr_inc(req->bts, CTR_TBF_ALLOC_ALGO_B);
 
@@ -951,9 +859,11 @@
  * goal is to provide the highest possible bandwidth per MS.
  *
  *  \param[in] req Contains all the requested params
+ *  \param[out] res The resolution/response for the allocation request
  *  \returns negative error code or 0 on success
  */
-int alloc_algorithm_dynamic(const struct alloc_resources_req *req)
+int alloc_algorithm_dynamic(const struct alloc_resources_req *req,
+			    struct alloc_resources_res *res)
 {
 	int rc;
 
@@ -965,7 +875,7 @@
 	}
 
 	if (!req->bts->multislot_disabled) {
-		rc = alloc_algorithm_b(req);
+		rc = alloc_algorithm_b(req, res);
 		if (rc >= 0)
 			return rc;
 
@@ -974,7 +884,7 @@
 		req->bts->multislot_disabled = 1;
 	}
 
-	return alloc_algorithm_a(req);
+	return alloc_algorithm_a(req, res);
 }
 
 int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts, uint8_t ms_class)
diff --git a/src/alloc_algo.h b/src/alloc_algo.h
index f55f278..7fbe945 100644
--- a/src/alloc_algo.h
+++ b/src/alloc_algo.h
@@ -32,23 +32,34 @@
 	/* BTS where to allocate resources */
 	struct gprs_rlcmac_bts *bts;
 	/* MS for which to allocate resources */
-	struct GprsMs *ms;
+	const struct GprsMs *ms;
 	/* Direction of the TBF for which we are allocating resources */
 	enum gprs_rlcmac_tbf_direction direction;
 	/* Whether to allocate only a single (1) TS */
 	bool single;
 	/* Whether to allocate on a specific TRX (>=0) or not (-1) */
 	int8_t use_trx;
-	/* FIXME: this will be removed in the future, tbf struct will be filled
-	 * in by caller of alloc_algorithm(). */
-	struct gprs_rlcmac_tbf *tbf;
 };
 
-int alloc_algorithm_a(const struct alloc_resources_req *req);
+struct alloc_resources_res {
+	struct gprs_rlcmac_trx *trx;
+	struct gprs_rlcmac_pdch *first_common_ts;
+	uint8_t reserved_ul_slots;
+	uint8_t reserved_dl_slots;
+	uint8_t ass_slots_mask;
+	bool upgrade_to_multislot;
+	uint8_t tfi;
+	int usf[8];
+};
 
-int alloc_algorithm_b(const struct alloc_resources_req *req);
+int alloc_algorithm_a(const struct alloc_resources_req *req,
+		      struct alloc_resources_res *res);
 
-int alloc_algorithm_dynamic(const struct alloc_resources_req *req);
+int alloc_algorithm_b(const struct alloc_resources_req *req,
+		      struct alloc_resources_res *res);
+
+int alloc_algorithm_dynamic(const struct alloc_resources_req *req,
+			    struct alloc_resources_res *res);
 int gprs_alloc_max_dl_slots_per_ms(const struct gprs_rlcmac_bts *bts, uint8_t ms_class);
 
 #ifdef __cplusplus
diff --git a/src/gprs_pcu.h b/src/gprs_pcu.h
index b4c9f7d..956f0e7 100644
--- a/src/gprs_pcu.h
+++ b/src/gprs_pcu.h
@@ -67,8 +67,10 @@
 struct GprsMs;
 struct gprs_rlcmac_tbf;
 struct alloc_resources_req;
+struct alloc_resources_res;
 
-typedef int (*alloc_algorithm_func_t)(const struct alloc_resources_req *req);
+typedef int (*alloc_algorithm_func_t)(const struct alloc_resources_req *req,
+				      struct alloc_resources_res *res);
 
 struct gprs_pcu {
 
diff --git a/src/tbf.cpp b/src/tbf.cpp
index 0b6fe58..921611d 100644
--- a/src/tbf.cpp
+++ b/src/tbf.cpp
@@ -572,6 +572,33 @@
 	}
 }
 
+int gprs_rlcmac_tbf::alloc_algorithm(const struct alloc_resources_req *req)
+{
+	int rc;
+
+	/* select algorithm */
+	struct alloc_resources_res res = {};
+	rc = the_pcu->alloc_algorithm(req, &res);
+	if (rc < 0)
+		return rc;
+
+	/* The allocation will be successful, so the system state and tbf/ms
+	 * may be modified from now on. */
+
+	/* Update MS, really allocate the resources */
+	if (res.reserved_ul_slots != ms_reserved_ul_slots(req->ms) ||
+	    res.reserved_dl_slots != ms_reserved_dl_slots(req->ms)) {
+		/* The reserved slots have changed, update the MS */
+		ms_set_reserved_slots(ms(), res.trx, res.reserved_ul_slots, res.reserved_dl_slots);
+	}
+	ms_set_first_common_ts(ms(), res.first_common_ts);
+
+	/* Assign TRX,TS,TFI,USF to TBF: */
+	this->apply_allocated_resources(&res);
+
+	return 0;
+}
+
 int gprs_rlcmac_tbf::setup(int8_t use_trx, bool single_slot)
 {
 	int rc;
@@ -581,11 +608,10 @@
 		.direction = this->direction,
 		.single = single_slot,
 		.use_trx = use_trx,
-		.tbf = this,
 	};
 
 	/* select algorithm */
-	rc = the_pcu->alloc_algorithm(&req);
+	rc = this->alloc_algorithm(&req);
 	/* if no resource */
 	if (rc < 0) {
 		LOGPTBF(this, LOGL_NOTICE,
diff --git a/src/tbf.h b/src/tbf.h
index 3f58cd2..ba2a755 100644
--- a/src/tbf.h
+++ b/src/tbf.h
@@ -27,6 +27,7 @@
 
 struct bssgp_bvc_ctx;
 struct gprs_rlcmac_bts;
+struct alloc_resources_req;
 
 #endif
 
@@ -162,6 +163,7 @@
 	virtual ~gprs_rlcmac_tbf();
 
 	virtual gprs_rlc_window *window() = 0;
+	virtual void apply_allocated_resources(const struct alloc_resources_res *res) = 0;
 
 	int setup(int8_t use_trx, bool single_slot);
 	bool state_is(enum tbf_fsm_states rhs) const;
@@ -212,6 +214,8 @@
 	/* attempt to make things a bit more fair */
 	void rotate_in_list();
 
+	int alloc_algorithm(const struct alloc_resources_req *req);
+
 	enum gprs_rlcmac_tbf_direction direction;
 	struct gprs_rlcmac_trx *trx;
 	struct gprs_rlcmac_pdch *control_ts; /* timeslot control messages and polling */
diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp
index cb01222..4af9ae6 100644
--- a/src/tbf_dl.cpp
+++ b/src/tbf_dl.cpp
@@ -418,6 +418,28 @@
 	return create_dl_acked_block(fn, pdch, bsn, bsn2);
 }
 
+void gprs_rlcmac_dl_tbf::apply_allocated_resources(const struct alloc_resources_res *res)
+{
+	uint8_t ts;
+
+	this->trx = res->trx;
+	this->upgrade_to_multislot = res->upgrade_to_multislot;
+
+	for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
+		struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
+		OSMO_ASSERT(!this->pdch[pdch->ts_no]);
+		if (!(res->ass_slots_mask & (1 << ts)))
+			continue;
+		LOGPTBFDL(this, LOGL_DEBUG, "Assigning TS=%u TFI=%d\n",
+			  ts, res->tfi);
+
+		this->m_tfi = res->tfi;
+
+		this->pdch[pdch->ts_no] = pdch;
+		pdch->attach_tbf(this);
+	}
+}
+
 /* old_tbf (UL TBF or DL TBF) will send a Pkt Dl Ass on PACCH to assign tbf.
  * Note: It is possible that "tbf == old_tbf" if the TBF is being updated. This can
  * happen when we first assign over PCH (only single slot is possible) and we want
@@ -472,9 +494,8 @@
 		.direction = tbf_direction(tbf),
 		.single = false,
 		.use_trx = -1,
-		.tbf = tbf,
 	};
-	rc = the_pcu->alloc_algorithm(&req);
+	rc = dl_tbf->alloc_algorithm(&req);
 	/* if no resource */
 	if (rc < 0) {
 		LOGPTBFDL(dl_tbf, LOGL_ERROR, "No resources allocated during upgrade to multislot!\n");
diff --git a/src/tbf_dl.h b/src/tbf_dl.h
index 4fdfbed..90dfb12 100644
--- a/src/tbf_dl.h
+++ b/src/tbf_dl.h
@@ -46,6 +46,7 @@
 	gprs_rlcmac_dl_tbf(struct gprs_rlcmac_bts *bts, GprsMs *ms);
 	~gprs_rlcmac_dl_tbf();
 	gprs_rlc_window *window();
+	void apply_allocated_resources(const struct alloc_resources_res *res);
 
 	int rcvd_dl_ack(bool final_ack, unsigned first_bsn, struct bitvec *rbb);
 	struct msgb *create_dl_acked_block(uint32_t fn, const gprs_rlcmac_pdch *pdch,
diff --git a/src/tbf_ul.cpp b/src/tbf_ul.cpp
index 16ef304..47347f9 100644
--- a/src/tbf_ul.cpp
+++ b/src/tbf_ul.cpp
@@ -29,6 +29,7 @@
 #include <gprs_ms.h>
 #include <llc.h>
 #include "pcu_utils.h"
+#include "alloc_algo.h"
 
 extern "C" {
 #include <osmocom/core/msgb.h>
@@ -704,6 +705,30 @@
 	return &m_window;
 }
 
+void gprs_rlcmac_ul_tbf::apply_allocated_resources(const struct alloc_resources_res *res)
+{
+	uint8_t ts;
+
+	this->trx = res->trx;
+	this->upgrade_to_multislot = res->upgrade_to_multislot;
+
+	for (ts = 0; ts < ARRAY_SIZE(trx->pdch); ts++) {
+		struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts];
+		OSMO_ASSERT(!this->pdch[pdch->ts_no]);
+		if (!(res->ass_slots_mask & (1 << ts)))
+			continue;
+		LOGPTBFUL(this, LOGL_DEBUG, "Assigning TS=%u TFI=%d USF=%u\n",
+			  ts, res->tfi, res->usf[ts]);
+		OSMO_ASSERT(res->usf[ts] >= 0);
+
+		this->m_tfi = res->tfi;
+		this->m_usf[pdch->ts_no] = res->usf[ts];
+
+		this->pdch[pdch->ts_no] = pdch;
+		pdch->attach_tbf(this);
+	}
+}
+
 void gprs_rlcmac_ul_tbf::usf_timeout()
 {
 	if (n_inc(N3101))
diff --git a/src/tbf_ul.h b/src/tbf_ul.h
index aa54c8f..5ef8465 100644
--- a/src/tbf_ul.h
+++ b/src/tbf_ul.h
@@ -60,6 +60,7 @@
 	gprs_rlcmac_ul_tbf(struct gprs_rlcmac_bts *bts, GprsMs *ms);
 	~gprs_rlcmac_ul_tbf();
 	gprs_rlc_window *window();
+	void apply_allocated_resources(const struct alloc_resources_res *res);
 	/* blocks were acked */
 	int rcv_data_block_acknowledged(
 		const struct gprs_rlc_data_info *rlc,