[MERGED] osmo-bsc[master]: HO: introduce ho decision callbacks

Neels Hofmeyr gerrit-no-reply at lists.osmocom.org
Mon Feb 19 16:30:58 UTC 2018


Neels Hofmeyr has submitted this change and it was merged.

Change subject: HO: introduce ho decision callbacks
......................................................................


HO: introduce ho decision callbacks

Instead of reacting on S_LCHAN* signals in the handover decision code,
introduce callbacks for the handover decision to be invoked by handover_logic.c
at the appropriate time.

The rationale is explained in a comment to struct handover_decision_callbacks,
quoting:

"
All events that are interesting for handover decision are actually communicated
by S_LCHAN_* signals, so theoretically, each handover algorithm could evaluate
those.  However, handover_logic.c cleans up handover operation state upon
receiving some of these signals. To allow a handover decision algorithm to take
advantage of e.g. the struct bsc_handover before it is discarded, the handover
decision event handler needs to be invoked before handover_logic.c discards the
state. For example, if the handover decision wants to place a penalty timer
upon a handover failure, it still needs to know which target cell the handover
failed for; handover_logic.c erases that knowledge on handover failure, since
it needs to clean up the lchan's handover state.

The most explicit and safest way to ensure the correct order of event handling
is to invoke the handover decision algorithm's actions from handover_logic.c
itself, before cleaning up. This struct provides the callback functions for
this purpose.

For consistency, also handle signals in this way that aren't actually in danger
of interference from handover_logic.c (which also saves repeated lookup of
handover state for lchans). Thus, handover decision algorithms should not
register any signal handler at all.
"

Also:
- Publish struct bsc_handover to use it as argument to above callbacks.
- Add enum hodec_id to struct bsc_handover, to be able to signal the
  appropriate hodec algorithm per event.
- Add hodec_id argument to bsc_handover_start*() to be placed in the
  bsc_handover struct.
- Publish the LOGPHO logging macros in handover.h along with struct
  bsc_handover, convenient for logging in callback implementations.

Replace handover_decision.c's signal handler with a registered
handover_decision_callbacks instance.

(Upcoming handover_decision_2 will use all of the callbacks introduced here.)

Change-Id: Id5b64504007fe03e0406a4b395cd0359232b77d2
---
M include/osmocom/bsc/handover.h
M src/libbsc/bsc_vty.c
M src/libbsc/handover_decision.c
M src/libbsc/handover_logic.c
4 files changed, 131 insertions(+), 57 deletions(-)

Approvals:
  Harald Welte: Looks good to me, approved
  Jenkins Builder: Verified



diff --git a/include/osmocom/bsc/handover.h b/include/osmocom/bsc/handover.h
index bbb448c..0fbfaf6 100644
--- a/include/osmocom/bsc/handover.h
+++ b/include/osmocom/bsc/handover.h
@@ -1,12 +1,91 @@
 #pragma once
 
+#include <stdint.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/timer.h>
+
 struct gsm_lchan;
 struct gsm_bts;
 struct gsm_subscriber_connection;
 
-int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *new_bts,
+#define LOGPHOLCHANTOLCHAN(old_lchan, new_lchan, level, fmt, args...) \
+	LOGP(DHODEC, level, "(BTS %u trx %u arfcn %u ts %u lchan %u %s)->(BTS %u trx %u arfcn %u ts %u lchan %u %s) (subscr %s) " fmt, \
+	     old_lchan->ts->trx->bts->nr, \
+	     old_lchan->ts->trx->nr, \
+	     old_lchan->ts->trx->arfcn, \
+	     old_lchan->ts->nr, \
+	     old_lchan->nr, \
+	     gsm_pchan_name(old_lchan->ts->pchan), \
+	     new_lchan->ts->trx->bts->nr, \
+	     new_lchan->ts->trx->nr, \
+	     new_lchan->ts->trx->arfcn, \
+	     new_lchan->ts->nr, \
+	     new_lchan->nr, \
+	     gsm_pchan_name(new_lchan->ts->pchan), \
+	     bsc_subscr_name(old_lchan->conn? old_lchan->conn->bsub : NULL), \
+	     ## args)
+
+#define LOGPHO(struct_bsc_handover, level, fmt, args ...) \
+	LOGPHOLCHANTOLCHAN(struct_bsc_handover->old_lchan, struct_bsc_handover->new_lchan, level, fmt, ## args)
+
+enum hodec_id {
+	HODEC_NONE,
+	HODEC1 = 1,
+	HODEC2 = 2,
+};
+
+struct bsc_handover {
+	struct llist_head list;
+
+	enum hodec_id from_hodec_id;
+
+	struct gsm_lchan *old_lchan;
+	struct gsm_lchan *new_lchan;
+
+	struct osmo_timer_list T3103;
+
+	uint8_t ho_ref;
+
+	bool inter_cell;
+	bool async;
+};
+
+int bsc_handover_start(enum hodec_id from_hodec_id, struct gsm_lchan *old_lchan, struct gsm_bts *new_bts,
 		       enum gsm_chan_t new_lchan_type);
 void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan);
 struct gsm_lchan *bsc_handover_pending(struct gsm_lchan *new_lchan);
 
 int bsc_ho_count(struct gsm_bts *bts, bool inter_cell);
+
+/* Handover decision algorithms' actions to take on incoming handover-relevant events.
+ *
+ * All events that are interesting for handover decision are actually communicated by S_LCHAN_* signals,
+ * so theoretically, each handover algorithm could evaluate those.  However, handover_logic.c cleans up
+ * handover operation state upon receiving some of these signals. To allow a handover decision algorithm
+ * to take advantage of e.g. the struct bsc_handover before it is discarded, the handover decision event
+ * handler needs to be invoked before handover_logic.c discards the state. For example, if the handover
+ * decision wants to place a penalty timer upon a handover failure, it still needs to know which target
+ * cell the handover failed for; handover_logic.c erases that knowledge on handover failure, since it
+ * needs to clean up the lchan's handover state.
+ *
+ * The most explicit and safest way to ensure the correct order of event handling is to invoke the
+ * handover decision algorithm's actions from handover_logic.c itself, before cleaning up. This struct
+ * provides the callback functions for this purpose.
+ *
+ * For consistency, also handle signals in this way that aren't actually in danger of interference from
+ * handover_logic.c (which also saves repeated lookup of handover state for lchans). Thus, handover
+ * decision algorithms should not register any signal handler at all.
+ */
+struct handover_decision_callbacks {
+	struct llist_head entry;
+
+	int hodec_id;
+
+	void (*on_measurement_report)(struct gsm_meas_rep *mr);
+	void (*on_ho_chan_activ_nack)(struct bsc_handover *ho);
+	void (*on_ho_failure)(struct bsc_handover *ho);
+};
+
+void handover_decision_callbacks_register(struct handover_decision_callbacks *hdc);
+struct handover_decision_callbacks *handover_decision_callbacks_get(int hodec_id);
diff --git a/src/libbsc/bsc_vty.c b/src/libbsc/bsc_vty.c
index 3ce310a..36c849d 100644
--- a/src/libbsc/bsc_vty.c
+++ b/src/libbsc/bsc_vty.c
@@ -1481,7 +1481,7 @@
 	} else
 		LOGP(DHO, LOGL_NOTICE, "%s (ARFCN %u) --> BTS %u Manually triggering Handover from VTY\n",
 		     gsm_lchan_name(from_lchan), from_lchan->ts->trx->arfcn, to_bts->nr);
-	rc = bsc_handover_start(from_lchan, to_bts, from_lchan->type);
+	rc = bsc_handover_start(HODEC_NONE, from_lchan, to_bts, from_lchan->type);
 	if (rc) {
 		vty_out(vty, "bsc_handover_start() returned %d=%s%s", rc,
 			strerror(-rc), VTY_NEWLINE);
diff --git a/src/libbsc/handover_decision.c b/src/libbsc/handover_decision.c
index e677b1f..887c299 100644
--- a/src/libbsc/handover_decision.c
+++ b/src/libbsc/handover_decision.c
@@ -70,7 +70,7 @@
 	}
 
 	/* and actually try to handover to that cell */
-	return bsc_handover_start(lchan, new_bts, lchan->type);
+	return bsc_handover_start(HODEC1, lchan, new_bts, lchan->type);
 }
 
 /* did we get a RXLEV for a given cell in the given report? */
@@ -257,7 +257,7 @@
 
 /* process an already parsed measurement report and decide if we want to
  * attempt a handover */
-static void process_meas_rep(struct gsm_meas_rep *mr)
+static void on_measurement_report(struct gsm_meas_rep *mr)
 {
 	struct gsm_bts *bts = mr->lchan->ts->trx->bts;
 	enum meas_rep_field dlev, dqual;
@@ -332,25 +332,12 @@
 		attempt_handover(mr);
 }
 
-static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal,
-			   void *handler_data, void *signal_data)
-{
-	struct lchan_signal_data *lchan_data;
-
-	if (subsys != SS_LCHAN)
-		return 0;
-
-	lchan_data = signal_data;
-	switch (signal) {
-	case S_LCHAN_MEAS_REP:
-		process_meas_rep(lchan_data->mr);
-		break;
-	}
-
-	return 0;
-}
+struct handover_decision_callbacks hodec1_callbacks = {
+	.hodec_id = HODEC1,
+	.on_measurement_report = on_measurement_report,
+};
 
 void handover_decision_1_init(void)
 {
-	osmo_signal_register_handler(SS_LCHAN, ho_dec_sig_cb, NULL);
+	handover_decision_callbacks_register(&hodec1_callbacks);
 }
diff --git a/src/libbsc/handover_logic.c b/src/libbsc/handover_logic.c
index 3dd7227..483c76b 100644
--- a/src/libbsc/handover_logic.c
+++ b/src/libbsc/handover_logic.c
@@ -39,42 +39,10 @@
 #include <osmocom/bsc/bsc_subscriber.h>
 #include <osmocom/bsc/gsm_04_08_utils.h>
 #include <osmocom/bsc/handover.h>
-
-#define LOGPHOLCHANTOLCHAN(lchan, new_lchan, level, fmt, args...) \
-	LOGP(DHODEC, level, "(BTS %u trx %u arfcn %u ts %u lchan %u %s)->(BTS %u trx %u arfcn %u ts %u lchan %u %s) (subscr %s) " fmt, \
-	     lchan->ts->trx->bts->nr, \
-	     lchan->ts->trx->nr, \
-	     lchan->ts->trx->arfcn, \
-	     lchan->ts->nr, \
-	     lchan->nr, \
-	     gsm_pchan_name(lchan->ts->pchan), \
-	     new_lchan->ts->trx->bts->nr, \
-	     new_lchan->ts->trx->nr, \
-	     new_lchan->ts->trx->arfcn, \
-	     new_lchan->ts->nr, \
-	     new_lchan->nr, \
-	     gsm_pchan_name(new_lchan->ts->pchan), \
-	     bsc_subscr_name(lchan->conn->bsub), \
-	     ## args)
-
-#define LOGPHO(struct_bsc_handover, level, fmt, args ...) \
-	LOGPHOLCHANTOLCHAN(struct_bsc_handover->old_lchan, struct_bsc_handover->new_lchan, level, fmt, ## args)
-
-struct bsc_handover {
-	struct llist_head list;
-
-	struct gsm_lchan *old_lchan;
-	struct gsm_lchan *new_lchan;
-
-	struct osmo_timer_list T3103;
-
-	uint8_t ho_ref;
-
-	bool inter_cell;
-	bool async;
-};
+#include <osmocom/bsc/handover_cfg.h>
 
 static LLIST_HEAD(bsc_handovers);
+static LLIST_HEAD(handover_decision_callbacks);
 
 static void handover_free(struct bsc_handover *ho)
 {
@@ -110,7 +78,7 @@
 /*! Hand over the specified logical channel to the specified new BTS and possibly change the lchan type.
  * This is the main entry point for the actual handover algorithm, after the decision whether to initiate
  * HO to a specific BTS. To not change the lchan type, pass old_lchan->type. */
-int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *new_bts,
+int bsc_handover_start(enum hodec_id from_hodec_id, struct gsm_lchan *old_lchan, struct gsm_bts *new_bts,
 		       enum gsm_chan_t new_lchan_type)
 {
 	struct gsm_network *network;
@@ -160,6 +128,7 @@
 		lchan_free(new_lchan);
 		return -ENOMEM;
 	}
+	ho->from_hodec_id = from_hodec_id;
 	ho->old_lchan = old_lchan;
 	ho->new_lchan = new_lchan;
 	ho->ho_ref = ho_ref++;
@@ -282,12 +251,17 @@
 static int ho_chan_activ_nack(struct gsm_lchan *new_lchan)
 {
 	struct bsc_handover *ho;
+	struct handover_decision_callbacks *hdc;
 
 	ho = bsc_ho_by_new_lchan(new_lchan);
 	if (!ho) {
 		LOGP(DHO, LOGL_INFO, "ACT NACK: unable to find HO record\n");
 		return -ENODEV;
 	}
+
+	hdc = handover_decision_callbacks_get(ho->from_hodec_id);
+	if (hdc && hdc->on_ho_chan_activ_nack)
+		hdc->on_ho_chan_activ_nack(ho);
 
 	new_lchan->conn->ho_lchan = NULL;
 	new_lchan->conn = NULL;
@@ -341,12 +315,17 @@
 	struct gsm_network *net = old_lchan->ts->trx->bts->network;
 	struct bsc_handover *ho;
 	struct gsm_lchan *new_lchan;
+	struct handover_decision_callbacks *hdc;
 
 	ho = bsc_ho_by_old_lchan(old_lchan);
 	if (!ho) {
 		LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
 		return -ENODEV;
 	}
+
+	hdc = handover_decision_callbacks_get(ho->from_hodec_id);
+	if (hdc && hdc->on_ho_failure)
+		hdc->on_ho_failure(ho);
 
 	rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_FAILED]);
 
@@ -383,6 +362,18 @@
 	return 0;
 }
 
+static int ho_meas_rep(struct gsm_meas_rep *mr)
+{
+	struct handover_decision_callbacks *hdc;
+	enum hodec_id hodec_id = ho_get_algorithm(mr->lchan->ts->trx->bts->ho);
+
+	hdc = handover_decision_callbacks_get(hodec_id);
+	if (!hdc || !hdc->on_measurement_report)
+		return 0;
+	hdc->on_measurement_report(mr);
+	return 0;
+}
+
 static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal,
 			   void *handler_data, void *signal_data)
 {
@@ -404,6 +395,8 @@
 			return ho_gsm48_ho_compl(lchan);
 		case S_LCHAN_HANDOVER_FAIL:
 			return ho_gsm48_ho_fail(lchan);
+		case S_LCHAN_MEAS_REP:
+			return ho_meas_rep(lchan_data->mr);
 		}
 		break;
 	default:
@@ -445,3 +438,18 @@
 
 	return count;
 }
+
+void handover_decision_callbacks_register(struct handover_decision_callbacks *hdc)
+{
+	llist_add_tail(&hdc->entry, &handover_decision_callbacks);
+}
+
+struct handover_decision_callbacks *handover_decision_callbacks_get(int hodec_id)
+{
+	struct handover_decision_callbacks *hdc;
+	llist_for_each_entry(hdc, &handover_decision_callbacks, entry) {
+		if (hdc->hodec_id == hodec_id)
+			return hdc;
+	}
+	return NULL;
+}

-- 
To view, visit https://gerrit.osmocom.org/6497
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Id5b64504007fe03e0406a4b395cd0359232b77d2
Gerrit-PatchSet: 4
Gerrit-Project: osmo-bsc
Gerrit-Branch: master
Gerrit-Owner: Neels Hofmeyr <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr at sysmocom.de>


More information about the gerrit-log mailing list