ms: Use osmo_use_count to track references

Change-Id: Ib65629224e6bd5683bb9192ba4354e965e8d39ec
diff --git a/src/gprs_ms.c b/src/gprs_ms.c
index b9a1b76..f65ffb8 100644
--- a/src/gprs_ms.c
+++ b/src/gprs_ms.c
@@ -61,6 +61,40 @@
 	return (int64_t)(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000;
 }
 
+static void ms_update_status(struct GprsMs *ms);
+
+static int ms_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
+{
+	struct GprsMs *ms = e->use_count->talloc_object;
+	int32_t total;
+	int level;
+	char buf[1024];
+
+	if (!e->use)
+		return -EINVAL;
+
+	total = osmo_use_count_total(&ms->use_count);
+
+	if (total == 0
+	    || (total == 1 && old_use_count == 0 && e->count == 1))
+		level = LOGL_INFO;
+	else
+		level = LOGL_DEBUG;
+
+
+	LOGPSRC(DRLCMAC, level, file, line, "%s: %s %s: now used by %s\n",
+		ms_name(ms),
+		(e->count - old_use_count) > 0 ? "+" : "-", e->use,
+		(osmo_use_count_to_str_buf(buf, sizeof(buf), &ms->use_count), buf));
+
+	if (e->count < 0)
+		return -ERANGE;
+
+	if (total == 0)
+		ms_update_status(ms);
+	return 0;
+}
+
 void gprs_default_cb_ms_idle(struct GprsMs *ms)
 {
 	if (ms_is_idle(ms))
@@ -84,7 +118,7 @@
 
 	if (ms->timer.data) {
 		ms->timer.data = NULL;
-		ms_unref(ms);
+		ms_unref(ms, MS_USE_RELEASE_TIMER);
 	}
 }
 
@@ -125,6 +159,11 @@
 	ms->is_idle = true;
 	INIT_LLIST_HEAD(&ms->old_tbfs);
 
+	ms->use_count = (struct osmo_use_count){
+		.talloc_object = ms,
+		.use_cb = ms_use_cb,
+	};
+
 	int codel_interval = LLC_CODEL_USE_DEFAULT;
 
 	LOGP(DRLCMAC, LOGL_INFO, "Creating MS object\n");
@@ -204,7 +243,7 @@
 
 static void ms_update_status(struct GprsMs *ms)
 {
-	if (ms->ref > 0)
+	if (osmo_use_count_total(&ms->use_count) > 0)
 		return;
 
 	if (ms_is_idle(ms) && !ms->is_idle) {
@@ -220,20 +259,6 @@
 	}
 }
 
-struct GprsMs *ms_ref(struct GprsMs *ms)
-{
-	ms->ref += 1;
-	return ms;
-}
-
-void ms_unref(struct GprsMs *ms)
-{
-	OSMO_ASSERT(ms->ref >= 0);
-	ms->ref -= 1;
-	if (ms->ref == 0)
-		ms_update_status(ms);
-}
-
 static void ms_release_timer_start(struct GprsMs *ms)
 {
 	/* Immediate free():
@@ -252,8 +277,10 @@
 
 	LOGPMS(ms, DRLCMAC, LOGL_DEBUG, "Schedule MS release in %u secs\n", ms->delay);
 
-	if (!ms->timer.data)
-		ms->timer.data = ms_ref(ms);
+	if (!ms->timer.data) {
+		ms_ref(ms, MS_USE_RELEASE_TIMER);
+		ms->timer.data = ms;
+	}
 
 	osmo_timer_schedule(&ms->timer, ms->delay, 0);
 }
@@ -267,7 +294,7 @@
 
 	osmo_timer_del(&ms->timer);
 	ms->timer.data = NULL;
-	ms_unref(ms);
+	ms_unref(ms, MS_USE_RELEASE_TIMER);
 }
 
 void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode)
@@ -328,7 +355,7 @@
 
 	LOGPMS(ms, DRLCMAC, LOGL_INFO, "Attaching UL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
 
-	ms_ref(ms);
+	ms_ref(ms, __func__);
 
 	if (ms->ul_tbf)
 		llist_add_tail(tbf_ms_list(ul_tbf_as_tbf(ms->ul_tbf)), &ms->old_tbfs);
@@ -338,7 +365,7 @@
 	if (tbf)
 		ms_release_timer_stop(ms);
 
-	ms_unref(ms);
+	ms_unref(ms, __func__);
 }
 
 static void ms_attach_dl_tbf(struct GprsMs *ms, struct gprs_rlcmac_dl_tbf *tbf)
@@ -348,7 +375,7 @@
 
 	LOGPMS(ms, DRLCMAC, LOGL_INFO, "Attaching DL TBF: %s\n", tbf_name((struct gprs_rlcmac_tbf *)tbf));
 
-	ms_ref(ms);
+	ms_ref(ms, __func__);
 
 	if (ms->dl_tbf)
 		llist_add_tail(tbf_ms_list(dl_tbf_as_tbf(ms->dl_tbf)), &ms->old_tbfs);
@@ -358,7 +385,7 @@
 	if (tbf)
 		ms_release_timer_stop(ms);
 
-	ms_unref(ms);
+	ms_unref(ms, __func__);
 }
 
 void ms_attach_tbf(struct GprsMs *ms, struct gprs_rlcmac_tbf *tbf)
@@ -452,7 +479,7 @@
 {
 	char old_ms_name[128];
 	OSMO_ASSERT(old_ms != ms);
-	ms_ref(old_ms);
+	ms_ref(old_ms, __func__);
 
 	ms_name_buf(old_ms, old_ms_name, sizeof(old_ms_name));
 
@@ -478,7 +505,7 @@
 
 	ms_reset(old_ms);
 
-	ms_unref(old_ms);
+	ms_unref(old_ms, __func__);
 }
 
 /* Apply changes to the TLLI directly, used interally by functions below: */
diff --git a/src/gprs_ms.h b/src/gprs_ms.h
index d976235..123db0e 100644
--- a/src/gprs_ms.h
+++ b/src/gprs_ms.h
@@ -31,6 +31,7 @@
 #include <osmocom/core/timer.h>
 #include <osmocom/core/linuxlist.h>
 #include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/use_count.h>
 
 #include <osmocom/gsm/protocol/gsm_23_003.h>
 #include <osmocom/gsm/gsm48.h>
@@ -84,7 +85,7 @@
 	struct osmo_timer_list llc_timer;
 
 	bool is_idle;
-	int ref;
+	struct osmo_use_count use_count;
 	struct osmo_timer_list timer;
 	unsigned delay;
 
@@ -107,8 +108,6 @@
 void ms_set_first_common_ts(struct GprsMs *ms, struct gprs_rlcmac_pdch *pdch);
 void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
 			   uint8_t ul_slots, uint8_t dl_slots);
-struct GprsMs *ms_ref(struct GprsMs *ms);
-void ms_unref(struct GprsMs *ms);
 void ms_set_mode(struct GprsMs *ms, enum mcs_kind mode);
 void ms_set_ms_class(struct GprsMs *ms, uint8_t ms_class_);
 void ms_set_egprs_ms_class(struct GprsMs *ms, uint8_t ms_class_);
@@ -157,7 +156,9 @@
 
 static inline bool ms_is_idle(const struct GprsMs *ms)
 {
-	return !ms->ul_tbf && !ms->dl_tbf && !ms->ref && llist_empty(&ms->old_tbfs);
+	return !ms->ul_tbf && !ms->dl_tbf &&
+		llist_empty(&ms->old_tbfs) &&
+		osmo_use_count_total(&ms->use_count) == 0;
 }
 
 static inline struct gprs_llc_queue *ms_llc_queue(struct GprsMs *ms)
@@ -250,6 +251,12 @@
 	return ms->current_trx;
 }
 
+#define MS_USE_RELEASE_TIMER "release_timer"
+#define ms_ref(ms, use) \
+	OSMO_ASSERT(osmo_use_count_get_put(&(ms)->use_count, use, 1) == 0)
+#define ms_unref(ms, use) \
+	OSMO_ASSERT(osmo_use_count_get_put(&(ms)->use_count, use, -1) == 0)
+
 #define LOGPMS(ms, category, level, fmt, args...) \
 	LOGP(category, level, "%s " fmt, ms_name(ms), ## args)
 
diff --git a/src/pdch.cpp b/src/pdch.cpp
index 1c39ceb..4495e21 100644
--- a/src/pdch.cpp
+++ b/src/pdch.cpp
@@ -697,7 +697,7 @@
 	 * temporarily, in order to avoid it being freed if we free any of its
 	 * resources (TBF). */
 	OSMO_ASSERT(ms);
-	ms_ref(ms);
+	ms_ref(ms, __func__);
 
 
 	switch (item->type) {
@@ -843,7 +843,7 @@
 	/* schedule uplink assignment */
 	osmo_fsm_inst_dispatch(new_ul_tbf->ul_ass_fsm.fi, TBF_UL_ASS_EV_SCHED_ASS, NULL);
 return_unref:
-	ms_unref(ms);
+	ms_unref(ms, __func__);
 	return;
 }
 
diff --git a/src/tbf_ul_fsm.c b/src/tbf_ul_fsm.c
index a7d24ec..8778e32 100644
--- a/src/tbf_ul_fsm.c
+++ b/src/tbf_ul_fsm.c
@@ -227,7 +227,7 @@
 		/* Ref the MS, otherwise it may be freed after ul_tbf is
 		 * detached when sending event below. */
 		ms = tbf_ms(ctx->tbf);
-		ms_ref(ms);
+		ms_ref(ms, __func__);
 		/* UL TBF ACKed our transmitted UL ACK/NACK with final Ack
 		 * Indicator set to '1'. We can free the TBF right away, the MS
 		 * also just released its TBF on its side. */
@@ -242,7 +242,7 @@
 		 * now. */
 		if (!new_ul_tbf_requested && ms_need_dl_tbf(ms))
 			ms_new_dl_tbf_assigned_on_pch(ms);
-		ms_unref(ms);
+		ms_unref(ms, __func__);
 		break;
 	case TBF_EV_MAX_N3103:
 		ctx->T_release = 3169;