chan_alloc: Change Channel Release to release SAPIs, then the channel
Currently every SAPI release indication will trigger the channel. It
was possible that we had SAPI=3 and SAPI=0 allocated and we tried to
release the channel by sending a RF Channel Release, the BTS answered
with a RF Channel Release ACK but also sent the SAPI Release Indication
which triggered a channel release here. So it was possible that we
would have released a newly allocated channel because of the SAPI
release of the old connection.
This code now works by releasing all SAPIs from highest to lowest,
then sending a SACH Deactivate and finally releasing the channel. This
approach is in use on the on-waves/bsc-master.
diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h
index 4a33410..0b732d9 100644
--- a/openbsc/include/openbsc/abis_rsl.h
+++ b/openbsc/include/openbsc/abis_rsl.h
@@ -75,6 +75,7 @@
/* to be provided by external code */
int abis_rsl_sendmsg(struct msgb *msg);
int rsl_deact_sacch(struct gsm_lchan *lchan);
+int rsl_lchan_rll_release(struct gsm_lchan *lchan, u_int8_t link_id);
/* BCCH related code */
int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf);
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index e276e1e..14d8014 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -278,6 +278,8 @@
/* Established data link layer services */
u_int8_t sapis[8];
+ int sach_deact;
+ int release_reason;
/* GSM Random Access data */
struct gsm48_req_ref *rqd_ref;
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index 7b19d7f..7068422 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -1356,11 +1356,21 @@
static void rsl_handle_release(struct gsm_lchan *lchan)
{
+ int sapi;
struct gsm_bts *bts;
+
+ /* maybe we have only brought down one RLL */
if (lchan->state != LCHAN_S_REL_REQ)
- LOGP(DRSL, LOGL_ERROR, "RF release on %s but state %s\n",
- gsm_lchan_name(lchan),
- gsm_lchans_name(lchan->state));
+ return;
+
+ for (sapi = 0; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) {
+ if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED)
+ continue;
+ LOGP(DRSL, LOGL_NOTICE, "%s waiting for SAPI=%d to be released.\n",
+ gsm_lchan_name(lchan), sapi);
+ return;
+ }
+
/* wait a bit to send the RF Channel Release */
@@ -1422,6 +1432,7 @@
rll_indication(msg->lchan, rllh->link_id,
BSC_RLLR_IND_REL_IND);
rsl_handle_release(msg->lchan);
+ rsl_lchan_rll_release(msg->lchan, rllh->link_id);
break;
case RSL_MT_REL_CONF:
/* BTS informs us of having received UA from MS,
@@ -1429,6 +1440,7 @@
DEBUGPC(DRLL, "RELEASE CONFIRMATION\n");
msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED;
rsl_handle_release(msg->lchan);
+ rsl_lchan_rll_release(msg->lchan, rllh->link_id);
break;
case RSL_MT_ERROR_IND:
rc = rsl_rx_rll_err_ind(msg);
diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c
index b061fc7..5325dc0 100644
--- a/openbsc/src/chan_alloc.c
+++ b/openbsc/src/chan_alloc.c
@@ -328,6 +328,9 @@
lchan->conn = NULL;
}
+ lchan->sach_deact = 0;
+ lchan->release_reason = 0;
+
/* FIXME: ts_free() the timeslot, if we're the last logical
* channel using it */
}
@@ -352,18 +355,62 @@
lchan->state = LCHAN_S_NONE;
}
+/* release the next allocated SAPI or return 0 */
+static int _lchan_release_next_sapi(struct gsm_lchan *lchan)
+{
+ int sapi;
+
+ for (sapi = 1; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) {
+ u_int8_t link_id;
+ if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED)
+ continue;
+
+ link_id = sapi;
+ if (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H)
+ link_id |= 0x40;
+ rsl_release_request(lchan, link_id, lchan->release_reason);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Drive the release process of the lchan */
+static void _lchan_handle_release(struct gsm_lchan *lchan)
+{
+ /* Ask for SAPI != 0 to be freed first and stop if we need to wait */
+ if (_lchan_release_next_sapi(lchan) == 0)
+ return;
+
+ if (lchan->sach_deact) {
+ gsm48_send_rr_release(lchan);
+ return;
+ }
+
+ rsl_release_request(lchan, 0, lchan->release_reason);
+ rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ);
+}
+
+/* called from abis rsl */
+int rsl_lchan_rll_release(struct gsm_lchan *lchan, u_int8_t link_id)
+{
+ if (lchan->state != LCHAN_S_REL_REQ)
+ return -1;
+
+ if ((link_id & 0x7) != 0)
+ _lchan_handle_release(lchan);
+ return 0;
+}
/* Consider releasing the channel now */
int lchan_release(struct gsm_lchan *lchan, int sach_deact, int reason)
{
- /* Assume we have GSM04.08 running and send a release */
- if (sach_deact) {
- gsm48_send_rr_release(lchan);
- }
-
- DEBUGP(DRLL, "%s Recycling Channel\n", gsm_lchan_name(lchan));
+ DEBUGP(DRLL, "%s starting release sequence\n", gsm_lchan_name(lchan));
rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ);
- rsl_release_request(lchan, 0, 0);
+
+ lchan->release_reason = reason;
+ lchan->sach_deact = sach_deact;
+ _lchan_handle_release(lchan);
return 1;
}