<p>laforge <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-bsc/+/19793">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  laforge: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">abis_rsl: prioritize emergency calls over regular calls<br><br>when an emergency call arrives while all TCH are busy, the BSC should<br>pick an arbitrary (preferably the longest lasting) call / lchan and<br>release it in favor of the incoming emergancy call.<br><br>The release of the existing call is a process that can not be done<br>synchronously while the ChanRQD is handled sonce multiple messages are<br>exchanged between BTS and MSC and multiple FSMs need to do their work.<br><br>To be able to release one lchan while handling a ChanRQD a queue is<br>implemented in which the incomming channel requests are collected. If<br>an emergency call is established while all channels are busy, an<br>arbitrary lchan is picked and freed. When freeing the lchan is done,<br>the queue is checked again and the emergency call is put on the free<br>lchan (TCH/H or TCH/F).<br><br>Change-Id: If8651265928797dbda9f528b544931dcfa4a0b36<br>Related: OS#4549<br>---<br>M include/osmocom/bsc/abis_rsl.h<br>M include/osmocom/bsc/bts.h<br>M include/osmocom/bsc/lchan_fsm.h<br>M include/osmocom/bsc/lchan_select.h<br>M src/osmo-bsc/abis_rsl.c<br>M src/osmo-bsc/bts.c<br>M src/osmo-bsc/lchan_fsm.c<br>M src/osmo-bsc/lchan_select.c<br>8 files changed, 284 insertions(+), 50 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/bsc/abis_rsl.h b/include/osmocom/bsc/abis_rsl.h</span><br><span>index 2611a3d..9879497 100644</span><br><span>--- a/include/osmocom/bsc/abis_rsl.h</span><br><span>+++ b/include/osmocom/bsc/abis_rsl.h</span><br><span>@@ -118,5 +118,7 @@</span><br><span> </span><br><span> int rsl_tx_rf_chan_release(struct gsm_lchan *lchan);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+void abis_rsl_chan_rqd_queue_poll(struct gsm_bts *bts);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #endif /* RSL_MT_H */</span><br><span> </span><br><span>diff --git a/include/osmocom/bsc/bts.h b/include/osmocom/bsc/bts.h</span><br><span>index a50c70e..7f36904 100644</span><br><span>--- a/include/osmocom/bsc/bts.h</span><br><span>+++ b/include/osmocom/bsc/bts.h</span><br><span>@@ -634,6 +634,7 @@</span><br><span>     struct osmo_timer_list etws_timer;      /* when to stop ETWS PN */</span><br><span> </span><br><span>       struct llist_head oml_fail_rep;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct llist_head chan_rqd_queue;</span><br><span> };</span><br><span> </span><br><span> #define GSM_BTS_SI2Q(bts, i)   (struct gsm48_system_information_type_2quater *)((bts)->si_buf[SYSINFO_TYPE_2quater][i])</span><br><span>diff --git a/include/osmocom/bsc/lchan_fsm.h b/include/osmocom/bsc/lchan_fsm.h</span><br><span>index df3ed22..9fe7db1 100644</span><br><span>--- a/include/osmocom/bsc/lchan_fsm.h</span><br><span>+++ b/include/osmocom/bsc/lchan_fsm.h</span><br><span>@@ -74,3 +74,5 @@</span><br><span> void lchan_forget_conn(struct gsm_lchan *lchan);</span><br><span> </span><br><span> void lchan_set_last_error(struct gsm_lchan *lchan, const char *fmt, ...);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void lchan_fsm_skip_error(struct gsm_lchan *lchan);</span><br><span>diff --git a/include/osmocom/bsc/lchan_select.h b/include/osmocom/bsc/lchan_select.h</span><br><span>index 865181b..41e7015 100644</span><br><span>--- a/include/osmocom/bsc/lchan_select.h</span><br><span>+++ b/include/osmocom/bsc/lchan_select.h</span><br><span>@@ -4,3 +4,4 @@</span><br><span> struct gsm_lchan *lchan_select_by_type(struct gsm_bts *bts, enum gsm_chan_t type);</span><br><span> struct gsm_lchan *lchan_select_by_chan_mode(struct gsm_bts *bts,</span><br><span>                                      enum gsm48_chan_mode chan_mode, enum channel_rate chan_rate);</span><br><span style="color: hsl(120, 100%, 40%);">+struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts, enum gsm_chan_t type);</span><br><span>diff --git a/src/osmo-bsc/abis_rsl.c b/src/osmo-bsc/abis_rsl.c</span><br><span>index 03d53c5..74cc760 100644</span><br><span>--- a/src/osmo-bsc/abis_rsl.c</span><br><span>+++ b/src/osmo-bsc/abis_rsl.c</span><br><span>@@ -1330,77 +1330,258 @@</span><br><span>     return rsl_send_imm_ass_rej(bts, rqd_ref, wait_ind);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct chan_rqd {</span><br><span style="color: hsl(120, 100%, 40%);">+      struct llist_head entry;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct gsm_bts *bts;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct gsm48_req_ref ref;</span><br><span style="color: hsl(120, 100%, 40%);">+     enum gsm_chreq_reason_t reason;</span><br><span style="color: hsl(120, 100%, 40%);">+       uint8_t ta;</span><br><span style="color: hsl(120, 100%, 40%);">+   /* set to true to mark that the release of the release_lchan is in progress */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct gsm_lchan *release_lchan;</span><br><span style="color: hsl(120, 100%, 40%);">+      time_t timestamp;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Handle packet channel rach requests */</span><br><span style="color: hsl(0, 100%, 40%);">-static int rsl_rx_pchan_rqd(struct msgb *msg, struct gsm_bts *bts)</span><br><span style="color: hsl(120, 100%, 40%);">+static int rsl_rx_pchan_rqd(struct chan_rqd *rqd)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-  struct gsm48_req_ref *rqd_ref;</span><br><span style="color: hsl(0, 100%, 40%);">-  struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg);</span><br><span style="color: hsl(0, 100%, 40%);">-      rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1];</span><br><span style="color: hsl(0, 100%, 40%);">-    uint8_t ra = rqd_ref->ra;</span><br><span>         uint8_t t1, t2, t3;</span><br><span>  uint32_t fn;</span><br><span>         uint8_t rqd_ta;</span><br><span>      uint8_t is_11bit;</span><br><span> </span><br><span>        /* Process rach request and forward contained information to PCU */</span><br><span style="color: hsl(0, 100%, 40%);">-     if (ra == 0x7F) {</span><br><span style="color: hsl(120, 100%, 40%);">+     if (rqd->ref.ra == 0x7F) {</span><br><span>                is_11bit = 1;</span><br><span> </span><br><span>            /* FIXME: Also handle 11 bit rach requests */</span><br><span style="color: hsl(0, 100%, 40%);">-           LOGP(DRSL, LOGL_ERROR, "BTS %d eleven bit access burst not supported yet!\n",bts->nr);</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGP(DRSL, LOGL_ERROR, "BTS %d eleven bit access burst not supported yet!\n",rqd->bts->nr);</span><br><span>          return -EINVAL;</span><br><span>      } else {</span><br><span>             is_11bit = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-           t1 = rqd_ref->t1;</span><br><span style="color: hsl(0, 100%, 40%);">-            t2 = rqd_ref->t2;</span><br><span style="color: hsl(0, 100%, 40%);">-            t3 = rqd_ref->t3_low | (rqd_ref->t3_high << 3);</span><br><span style="color: hsl(120, 100%, 40%);">+           t1 = rqd->ref.t1;</span><br><span style="color: hsl(120, 100%, 40%);">+          t2 = rqd->ref.t2;</span><br><span style="color: hsl(120, 100%, 40%);">+          t3 = rqd->ref.t3_low | (rqd->ref.t3_high << 3);</span><br><span>          fn = (51 * ((t3-t2) % 26) + t3 + 51 * 26 * t1);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-         rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2];</span><br><span style="color: hsl(120, 100%, 40%);">+            rqd_ta = rqd->ta;</span><br><span>         }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   return pcu_tx_rach_ind(bts, rqd_ta, ra, fn, is_11bit,</span><br><span style="color: hsl(120, 100%, 40%);">+ return pcu_tx_rach_ind(rqd->bts, rqd_ta, rqd->ref.ra, fn, is_11bit,</span><br><span>                           GSM_L1_BURST_TYPE_ACCESS_0);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* Protect against RACH DoS attack: If an excessive amount of RACH requests queues up it is likely that the current BTS</span><br><span style="color: hsl(120, 100%, 40%);">+ * is under RACH DoS attack. To prevent excessive memory usage, remove all expired or at least one of the oldest channel</span><br><span style="color: hsl(120, 100%, 40%);">+ * requests from the queue to prevent the queue from growing indefinetly. */</span><br><span style="color: hsl(120, 100%, 40%);">+static void reduce_rach_dos(struct gsm_bts *bts)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    int rlt = gsm_bts_get_radio_link_timeout(bts);</span><br><span style="color: hsl(120, 100%, 40%);">+        time_t timestamp_current = time(NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+        struct chan_rqd *rqd;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct chan_rqd *rqd_tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+     unsigned int rqd_count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Drop all expired channel requests in the list */</span><br><span style="color: hsl(120, 100%, 40%);">+   llist_for_each_entry_safe(rqd, rqd_tmp, &bts->chan_rqd_queue, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+         /* If the channel request is older than the radio link timeout we drop it. This also means that the</span><br><span style="color: hsl(120, 100%, 40%);">+            * queue is under its overflow limit again. */</span><br><span style="color: hsl(120, 100%, 40%);">+                if (timestamp_current - rqd->timestamp > rlt)</span><br><span style="color: hsl(120, 100%, 40%);">+                   llist_del(&rqd->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+                else</span><br><span style="color: hsl(120, 100%, 40%);">+                  rqd_count++;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* If we find more than 255 (256) unexpired channel requests in the queue it is very likely that there is a</span><br><span style="color: hsl(120, 100%, 40%);">+    * problem with RACH dos on this BTS. We drop the first entry in the list to clip the growth of the list. */</span><br><span style="color: hsl(120, 100%, 40%);">+  if (rqd_count > 255) {</span><br><span style="color: hsl(120, 100%, 40%);">+             LOG_BTS(bts, DRSL, LOGL_INFO, "CHAN RQD: more than 255 queued RACH requests -- RACH DoS attack?\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                llist_del(&llist_first_entry(&bts->chan_rqd_queue, struct chan_rqd, entry)->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* MS has requested a channel on the RACH */</span><br><span> static int rsl_rx_chan_rqd(struct msgb *msg)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-     struct lchan_activate_info info;</span><br><span>     struct e1inp_sign_link *sign_link = msg->dst;</span><br><span>     struct gsm_bts *bts = sign_link->trx->bts;</span><br><span>     struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg);</span><br><span style="color: hsl(0, 100%, 40%);">-      struct gsm48_req_ref *rqd_ref;</span><br><span style="color: hsl(0, 100%, 40%);">-  enum gsm_chan_t lctype;</span><br><span style="color: hsl(0, 100%, 40%);">- enum gsm_chreq_reason_t chreq_reason;</span><br><span style="color: hsl(0, 100%, 40%);">-   struct gsm_lchan *lchan;</span><br><span style="color: hsl(0, 100%, 40%);">-        uint8_t rqd_ta;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct chan_rqd *rqd;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       reduce_rach_dos(bts);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       rqd = talloc_zero(bts, struct chan_rqd);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(rqd);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   rqd->bts = bts;</span><br><span style="color: hsl(120, 100%, 40%);">+    rqd->timestamp = time(NULL);</span><br><span> </span><br><span>  /* parse request reference to be used in immediate assign */</span><br><span style="color: hsl(0, 100%, 40%);">-    if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE)</span><br><span style="color: hsl(120, 100%, 40%);">+      if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE) {</span><br><span style="color: hsl(120, 100%, 40%);">+            talloc_free(rqd);</span><br><span>            return -EINVAL;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1];</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     memcpy(&rqd->ref, &rqd_hdr->data[1], sizeof(rqd->ref));</span><br><span> </span><br><span>         /* parse access delay and use as TA */</span><br><span style="color: hsl(0, 100%, 40%);">-  if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY)</span><br><span style="color: hsl(120, 100%, 40%);">+  if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY) {</span><br><span style="color: hsl(120, 100%, 40%);">+                talloc_free(rqd);</span><br><span>            return -EINVAL;</span><br><span style="color: hsl(0, 100%, 40%);">- rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2];</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     rqd->ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2];</span><br><span> </span><br><span>   /* Determine channel request cause code */</span><br><span style="color: hsl(0, 100%, 40%);">-      chreq_reason = get_reason_by_chreq(rqd_ref->ra, bts->network->neci);</span><br><span style="color: hsl(120, 100%, 40%);">+ rqd->reason = get_reason_by_chreq(rqd->ref.ra, bts->network->neci);</span><br><span>      LOG_BTS(bts, DRSL, LOGL_INFO, "CHAN RQD: reason: %s (ra=0x%02x, neci=0x%02x, chreq_reason=0x%02x)\n",</span><br><span style="color: hsl(0, 100%, 40%);">-         get_value_string(gsm_chreq_descs, chreq_reason), rqd_ref->ra, bts->network->neci, chreq_reason);</span><br><span style="color: hsl(120, 100%, 40%);">+             get_value_string(gsm_chreq_descs, rqd->reason), rqd->ref.ra, bts->network->neci, rqd->reason);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-       /* Handle PDCH related rach requests (in case of BSC-co-located-PCU */</span><br><span style="color: hsl(0, 100%, 40%);">-  if (chreq_reason == GSM_CHREQ_REASON_PDCH)</span><br><span style="color: hsl(0, 100%, 40%);">-              return rsl_rx_pchan_rqd(msg, bts);</span><br><span style="color: hsl(120, 100%, 40%);">+    rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CHREQ_TOTAL]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Enqueue request */</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_add_tail(&rqd->entry, &bts->chan_rqd_queue);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    /* Forward the request directly. Most request will be finished with one attempt so no queuing will be</span><br><span style="color: hsl(120, 100%, 40%);">+  * necessary. */</span><br><span style="color: hsl(120, 100%, 40%);">+      abis_rsl_chan_rqd_queue_poll(bts);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Find any busy TCH/H or TCH/F lchan */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct gsm_lchan *get_any_lchan(struct gsm_bts *bts)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      int trx_nr;</span><br><span style="color: hsl(120, 100%, 40%);">+   int ts_nr;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct gsm_bts_trx *trx;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct gsm_bts_trx_ts *ts;</span><br><span style="color: hsl(120, 100%, 40%);">+    struct gsm_lchan *lchan_est = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct gsm_lchan *lchan_any = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct gsm_lchan *lchan;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) {</span><br><span style="color: hsl(120, 100%, 40%);">+             trx = gsm_bts_trx_num(bts, trx_nr);</span><br><span style="color: hsl(120, 100%, 40%);">+           for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ts = &trx->ts[ts_nr];</span><br><span style="color: hsl(120, 100%, 40%);">+                  ts_for_each_lchan(lchan, ts) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                if (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                 if (bts->chan_alloc_reverse) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                             if (lchan->fi->state == LCHAN_ST_ESTABLISHED)</span><br><span style="color: hsl(120, 100%, 40%);">+                                                   lchan_est = lchan;</span><br><span style="color: hsl(120, 100%, 40%);">+                                            else</span><br><span style="color: hsl(120, 100%, 40%);">+                                                  lchan_any = lchan;</span><br><span style="color: hsl(120, 100%, 40%);">+                                    } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                                              if (lchan->fi->state == LCHAN_ST_ESTABLISHED) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                 if (!lchan_est)</span><br><span style="color: hsl(120, 100%, 40%);">+                                                               lchan_est = lchan;</span><br><span style="color: hsl(120, 100%, 40%);">+                                            } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                                                      if (!lchan_any)</span><br><span style="color: hsl(120, 100%, 40%);">+                                                               lchan_any = lchan;</span><br><span style="color: hsl(120, 100%, 40%);">+                                            }</span><br><span style="color: hsl(120, 100%, 40%);">+                                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                             }</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (lchan_est)</span><br><span style="color: hsl(120, 100%, 40%);">+                return lchan_est;</span><br><span style="color: hsl(120, 100%, 40%);">+     else if (lchan_any)</span><br><span style="color: hsl(120, 100%, 40%);">+           return lchan_any;</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Ensure that an incoming emergency call gets priority, if all voice channels are busy, terminate one regular call.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Return true if freeing of a busy lchan is in progress, but not done yet, return false when done (either successfully</span><br><span style="color: hsl(120, 100%, 40%);">+ * or unsuccessfully). */</span><br><span style="color: hsl(120, 100%, 40%);">+static bool force_free_lchan_for_emergency(struct chan_rqd *rqd)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      /* If the request is not about an emergency call, we may exit early, without doing anything. */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (rqd->reason != GSM_CHREQ_REASON_EMERG)</span><br><span style="color: hsl(120, 100%, 40%);">+              return false;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* First check the situation on the BTS, if we have TCH/H or TCH/F resources available for another (EMERGENCY)</span><br><span style="color: hsl(120, 100%, 40%);">+         * call. If yes, then no (further) action has to be carried out. */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (lchan_avail_by_type(rqd->bts, GSM_LCHAN_TCH_F)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE,</span><br><span style="color: hsl(120, 100%, 40%);">+                       "CHAN RQD/EMERGENCY-PRIORITY: at least one TCH/F is (now) available!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (lchan_avail_by_type(rqd->bts, GSM_LCHAN_TCH_H)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE,</span><br><span style="color: hsl(120, 100%, 40%);">+                       "CHAN RQD/EMERGENCY-PRIORITY: at least one TCH/H is (now) available!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* No free TCH/F or TCH/H was found, we now select one of the busy lchans and initate a release on that lchan.</span><br><span style="color: hsl(120, 100%, 40%);">+         * This will take a short amount of time. We need to come back and check regulary to see if we managed to</span><br><span style="color: hsl(120, 100%, 40%);">+      * free up another lchan. */</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!rqd->release_lchan) {</span><br><span style="color: hsl(120, 100%, 40%);">+         /* Pick any busy TCH/F or TCH/H lchan and inititate a channel</span><br><span style="color: hsl(120, 100%, 40%);">+          * release to make room for the incoming emergency call */</span><br><span style="color: hsl(120, 100%, 40%);">+            rqd->release_lchan = get_any_lchan(rqd->bts);</span><br><span style="color: hsl(120, 100%, 40%);">+           if (!rqd->release_lchan) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 /* It can not happen that we first find out that there</span><br><span style="color: hsl(120, 100%, 40%);">+                         * is no TCH/H or TCH/F available and at the same time</span><br><span style="color: hsl(120, 100%, 40%);">+                         * we ware unable to find any busy TCH/H or TCH/F. In</span><br><span style="color: hsl(120, 100%, 40%);">+                  * this case, the BTS probably does not have any</span><br><span style="color: hsl(120, 100%, 40%);">+                       * voice channels configured? */</span><br><span style="color: hsl(120, 100%, 40%);">+                      LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE,</span><br><span style="color: hsl(120, 100%, 40%);">+                               "CHAN RQD/EMERGENCY-PRIORITY: no TCH/H or TCH/F available - check VTY config!\n");</span><br><span style="color: hsl(120, 100%, 40%);">+                  return false;</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE,</span><br><span style="color: hsl(120, 100%, 40%);">+                       "CHAN RQD/EMERGENCY-PRIORITY: inducing termination of lchan %s (state:%s) in favor of incoming EMERGENCY CALL!\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                  gsm_lchan_name(rqd->release_lchan), osmo_fsm_inst_state_name(rqd->release_lchan->fi));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             lchan_release(rqd->release_lchan, !!(rqd->release_lchan->conn), true, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+    } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* BTS is shutting down, give up... */</span><br><span style="color: hsl(120, 100%, 40%);">+                if (rqd->release_lchan->ts->fi->state == TS_ST_NOT_INITIALIZED)</span><br><span style="color: hsl(120, 100%, 40%);">+                   return false;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               OSMO_ASSERT(rqd->release_lchan->fi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          LOG_BTS(rqd->bts, DRSL, LOGL_NOTICE,</span><br><span style="color: hsl(120, 100%, 40%);">+                       "CHAN RQD/EMERGENCY-PRIORITY: still terminating lchan %s (state:%s) in favor of incoming EMERGENCY CALL!\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                        gsm_lchan_name(rqd->release_lchan), osmo_fsm_inst_state_name(rqd->release_lchan->fi));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             /* If the channel was released in error (not established), the</span><br><span style="color: hsl(120, 100%, 40%);">+                 * lchan FSM automatically blocks the LCHAN for a short time.</span><br><span style="color: hsl(120, 100%, 40%);">+          * This is not acceptable in an emergency situation, so we skip</span><br><span style="color: hsl(120, 100%, 40%);">+                * this waiting period. */</span><br><span style="color: hsl(120, 100%, 40%);">+            if (rqd->release_lchan->fi->state == LCHAN_ST_WAIT_AFTER_ERROR)</span><br><span style="color: hsl(120, 100%, 40%);">+                      lchan_fsm_skip_error(rqd->release_lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* We are still in the process of releasing a busy lchan in favvor of the incoming emergency call. */</span><br><span style="color: hsl(120, 100%, 40%);">+ return true;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void abis_rsl_chan_rqd_queue_poll(struct gsm_bts *bts)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct lchan_activate_info info;</span><br><span style="color: hsl(120, 100%, 40%);">+      enum gsm_chan_t lctype;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gsm_lchan *lchan = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct chan_rqd *rqd;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       rqd = llist_first_entry_or_null(&bts->chan_rqd_queue, struct chan_rqd, entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!rqd)</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Handle PDCH related rach requests (in case of BSC-co-located-PCU) */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (rqd->reason == GSM_CHREQ_REASON_PDCH) {</span><br><span style="color: hsl(120, 100%, 40%);">+                rsl_rx_pchan_rqd(rqd);</span><br><span style="color: hsl(120, 100%, 40%);">+                return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Ensure that emergency calls will get priority over regular calls, however releasing</span><br><span style="color: hsl(120, 100%, 40%);">+         * lchan in favor of an emergency call may take some time, so we exit here. The lchan_fsm</span><br><span style="color: hsl(120, 100%, 40%);">+      * will poll again when an lchan becomes available. */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (force_free_lchan_for_emergency(rqd))</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</span><br><span> </span><br><span>  /* determine channel type (SDCCH/TCH_F/TCH_H) based on</span><br><span>        * request reference RA */</span><br><span style="color: hsl(0, 100%, 40%);">-      lctype = get_ctype_by_chreq(bts->network, rqd_ref->ra);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CHREQ_TOTAL]);</span><br><span style="color: hsl(120, 100%, 40%);">+     lctype = get_ctype_by_chreq(bts->network, rqd->ref.ra);</span><br><span> </span><br><span>    /* check availability / allocate channel</span><br><span>      *</span><br><span>@@ -1410,49 +1591,60 @@</span><br><span>          * - If there is still no channel available, try a TCH/F.</span><br><span>     *</span><br><span>    */</span><br><span style="color: hsl(0, 100%, 40%);">-     if (chreq_reason == GSM_CHREQ_REASON_EMERG) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (rqd->reason == GSM_CHREQ_REASON_EMERG) {</span><br><span>              if (bts->si_common.rach_control.t2 & 0x4) {</span><br><span>                   LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD: MS attempts EMERGENCY CALL although EMERGENCY CALLS "</span><br><span>                           "are not allowed in sysinfo (spec violation by MS!)\n");</span><br><span style="color: hsl(0, 100%, 40%);">-                      rsl_tx_imm_ass_rej(bts, rqd_ref);</span><br><span style="color: hsl(0, 100%, 40%);">-                       return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+                       rsl_tx_imm_ass_rej(bts, &rqd->ref);</span><br><span style="color: hsl(120, 100%, 40%);">+                    llist_del(&rqd->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+                        talloc_free(rqd);</span><br><span style="color: hsl(120, 100%, 40%);">+                     return;</span><br><span>              }</span><br><span>    }</span><br><span style="color: hsl(0, 100%, 40%);">-       lchan = lchan_select_by_type(bts, GSM_LCHAN_SDCCH);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Emergency calls will be put on a free TCH/H or TCH/F directly in the code below, all other channel requests</span><br><span style="color: hsl(120, 100%, 40%);">+         * will get an SDCCH first (if possible). */</span><br><span style="color: hsl(120, 100%, 40%);">+  if (rqd->reason != GSM_CHREQ_REASON_EMERG)</span><br><span style="color: hsl(120, 100%, 40%);">+         lchan = lchan_select_by_type(bts, GSM_LCHAN_SDCCH);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>        if (!lchan) {</span><br><span>                LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD: no resources for %s 0x%x, retrying with %s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                     gsm_lchant_name(GSM_LCHAN_SDCCH), rqd_ref->ra, gsm_lchant_name(GSM_LCHAN_TCH_H));</span><br><span style="color: hsl(120, 100%, 40%);">+                  gsm_lchant_name(GSM_LCHAN_SDCCH), rqd->ref.ra, gsm_lchant_name(GSM_LCHAN_TCH_H));</span><br><span>                 lchan = lchan_select_by_type(bts, GSM_LCHAN_TCH_H);</span><br><span>  }</span><br><span>    if (!lchan) {</span><br><span>                LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD: no resources for %s 0x%x, retrying with %s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                     gsm_lchant_name(GSM_LCHAN_SDCCH), rqd_ref->ra, gsm_lchant_name(GSM_LCHAN_TCH_F));</span><br><span style="color: hsl(120, 100%, 40%);">+                  gsm_lchant_name(GSM_LCHAN_SDCCH), rqd->ref.ra, gsm_lchant_name(GSM_LCHAN_TCH_F));</span><br><span>                 lchan = lchan_select_by_type(bts, GSM_LCHAN_TCH_F);</span><br><span>  }</span><br><span>    if (!lchan) {</span><br><span>                LOG_BTS(bts, DRSL, LOGL_NOTICE, "CHAN RQD: no resources for %s 0x%x\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                       gsm_lchant_name(lctype), rqd_ref->ra);</span><br><span style="color: hsl(120, 100%, 40%);">+                     gsm_lchant_name(lctype), rqd->ref.ra);</span><br><span>            rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CHREQ_NO_CHANNEL]);</span><br><span style="color: hsl(0, 100%, 40%);">-          rsl_tx_imm_ass_rej(bts, rqd_ref);</span><br><span style="color: hsl(0, 100%, 40%);">-               return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             rsl_tx_imm_ass_rej(bts, &rqd->ref);</span><br><span style="color: hsl(120, 100%, 40%);">+            llist_del(&rqd->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+                talloc_free(rqd);</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span>      }</span><br><span> </span><br><span>        /* save the RACH data as we need it after the CHAN ACT ACK */</span><br><span>        lchan->rqd_ref = talloc_zero(bts, struct gsm48_req_ref);</span><br><span>  OSMO_ASSERT(lchan->rqd_ref);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     *(lchan->rqd_ref) = *rqd_ref;</span><br><span style="color: hsl(0, 100%, 40%);">-        lchan->rqd_ta = rqd_ta;</span><br><span style="color: hsl(120, 100%, 40%);">+    *(lchan->rqd_ref) = rqd->ref;</span><br><span style="color: hsl(120, 100%, 40%);">+   lchan->rqd_ta = rqd->ta;</span><br><span> </span><br><span>   LOG_LCHAN(lchan, LOGL_DEBUG, "MS: Channel Request: reason=%s ra=0x%02x ta=%d\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                gsm_chreq_name(chreq_reason), rqd_ref->ra, rqd_ta);</span><br><span style="color: hsl(120, 100%, 40%);">+                gsm_chreq_name(rqd->reason), rqd->ref.ra, rqd->ta);</span><br><span>       info = (struct lchan_activate_info){</span><br><span>                 .activ_for = FOR_MS_CHANNEL_REQUEST,</span><br><span>                 .chan_mode = GSM48_CMODE_SIGN,</span><br><span>       };</span><br><span> </span><br><span>       lchan_activate(lchan, &info);</span><br><span style="color: hsl(0, 100%, 40%);">-       return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_del(&rqd->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+        talloc_free(rqd);</span><br><span style="color: hsl(120, 100%, 40%);">+     return;</span><br><span> }</span><br><span> </span><br><span> int rsl_tx_imm_assignment(struct gsm_lchan *lchan)</span><br><span>diff --git a/src/osmo-bsc/bts.c b/src/osmo-bsc/bts.c</span><br><span>index a17143d..ce1b200 100644</span><br><span>--- a/src/osmo-bsc/bts.c</span><br><span>+++ b/src/osmo-bsc/bts.c</span><br><span>@@ -263,6 +263,7 @@</span><br><span>    INIT_LLIST_HEAD(&bts->loc_list);</span><br><span>      INIT_LLIST_HEAD(&bts->local_neighbors);</span><br><span>       INIT_LLIST_HEAD(&bts->oml_fail_rep);</span><br><span style="color: hsl(120, 100%, 40%);">+   INIT_LLIST_HEAD(&bts->chan_rqd_queue);</span><br><span> </span><br><span>    /* Enable all codecs by default. These get reset to a more fine grained selection IF a</span><br><span>        * 'codec-support' config appears in the config file (see bsc_vty.c). */</span><br><span>diff --git a/src/osmo-bsc/lchan_fsm.c b/src/osmo-bsc/lchan_fsm.c</span><br><span>index 115c3da..65d77c4 100644</span><br><span>--- a/src/osmo-bsc/lchan_fsm.c</span><br><span>+++ b/src/osmo-bsc/lchan_fsm.c</span><br><span>@@ -413,8 +413,24 @@</span><br><span> static void lchan_fsm_unused_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span> {</span><br><span>     struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct gsm_bts *bts = lchan->ts->trx->bts;</span><br><span>  lchan_reset(lchan);</span><br><span>  osmo_fsm_inst_dispatch(lchan->ts->fi, TS_EV_LCHAN_UNUSED, lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Poll the channel request queue, so that waiting calls can make use of the lchan that just</span><br><span style="color: hsl(120, 100%, 40%);">+   * has become unused now. */</span><br><span style="color: hsl(120, 100%, 40%);">+  abis_rsl_chan_rqd_queue_poll(bts);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_fsm_wait_after_error_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct gsm_bts *bts = lchan->ts->trx->bts;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* We also need to poll the channel request queue when the FSM enters the WAIT_AFTER_ERROR</span><br><span style="color: hsl(120, 100%, 40%);">+     * state. In case of an emergency call the channel request queue will skip the waiting</span><br><span style="color: hsl(120, 100%, 40%);">+         * period. */</span><br><span style="color: hsl(120, 100%, 40%);">+ abis_rsl_chan_rqd_queue_poll(bts);</span><br><span> }</span><br><span> </span><br><span> /* Configure the multirate setting on this channel. */</span><br><span>@@ -1429,6 +1445,7 @@</span><br><span>        },</span><br><span>   [LCHAN_ST_WAIT_AFTER_ERROR] = {</span><br><span>              .name = "WAIT_AFTER_ERROR",</span><br><span style="color: hsl(120, 100%, 40%);">+         .onenter = lchan_fsm_wait_after_error_onenter,</span><br><span>               .in_event_mask = 0</span><br><span>                   | S(LCHAN_EV_RTP_RELEASED) /* ignore late lchan_rtp_fsm release events */</span><br><span>                    ,</span><br><span>@@ -1496,6 +1513,13 @@</span><br><span>   }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+void lchan_fsm_skip_error(struct gsm_lchan *lchan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_fsm_inst *fi = lchan->fi;</span><br><span style="color: hsl(120, 100%, 40%);">+      if (fi->state == LCHAN_ST_WAIT_AFTER_ERROR)</span><br><span style="color: hsl(120, 100%, 40%);">+                lchan_fsm_state_chg(LCHAN_ST_UNUSED);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int lchan_fsm_timer_cb(struct osmo_fsm_inst *fi)</span><br><span> {</span><br><span>   struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span>diff --git a/src/osmo-bsc/lchan_select.c b/src/osmo-bsc/lchan_select.c</span><br><span>index d2dba1b..6d3caac 100644</span><br><span>--- a/src/osmo-bsc/lchan_select.c</span><br><span>+++ b/src/osmo-bsc/lchan_select.c</span><br><span>@@ -162,15 +162,12 @@</span><br><span>       return lchan_select_by_type(bts, type);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/* Return a matching lchan from a specific BTS that is currently available. The next logical step is</span><br><span style="color: hsl(0, 100%, 40%);">- * lchan_activate() on it, which would possibly cause dynamic timeslot pchan switching, taken care of by</span><br><span style="color: hsl(0, 100%, 40%);">- * the lchan and timeslot FSMs. */</span><br><span style="color: hsl(0, 100%, 40%);">-struct gsm_lchan *lchan_select_by_type(struct gsm_bts *bts, enum gsm_chan_t type)</span><br><span style="color: hsl(120, 100%, 40%);">+struct gsm_lchan *lchan_avail_by_type(struct gsm_bts *bts, enum gsm_chan_t type)</span><br><span> {</span><br><span>       struct gsm_lchan *lchan = NULL;</span><br><span>      enum gsm_phys_chan_config first, first_cbch, second, second_cbch;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   LOG_BTS(bts, DRLL, LOGL_DEBUG, "lchan_select_by_type(%s)\n", gsm_lchant_name(type));</span><br><span style="color: hsl(120, 100%, 40%);">+        LOG_BTS(bts, DRLL, LOGL_DEBUG, "lchan_avail_by_type(%s)\n", gsm_lchant_name(type));</span><br><span> </span><br><span>    switch (type) {</span><br><span>      case GSM_LCHAN_SDCCH:</span><br><span>@@ -231,6 +228,20 @@</span><br><span>                 LOG_BTS(bts, DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type);</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ return lchan;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Return a matching lchan from a specific BTS that is currently available. The next logical step is</span><br><span style="color: hsl(120, 100%, 40%);">+ * lchan_activate() on it, which would possibly cause dynamic timeslot pchan switching, taken care of by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the lchan and timeslot FSMs. */</span><br><span style="color: hsl(120, 100%, 40%);">+struct gsm_lchan *lchan_select_by_type(struct gsm_bts *bts, enum gsm_chan_t type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct gsm_lchan *lchan = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     lchan = lchan_avail_by_type(bts, type);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     LOG_BTS(bts, DRLL, LOGL_DEBUG, "lchan_select_by_type(%s)\n", gsm_lchant_name(type));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>     if (lchan) {</span><br><span>                 lchan->type = type;</span><br><span>               LOG_LCHAN(lchan, LOGL_INFO, "Selected\n");</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-bsc/+/19793">change 19793</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-bsc/+/19793"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-bsc </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: If8651265928797dbda9f528b544931dcfa4a0b36 </div>
<div style="display:none"> Gerrit-Change-Number: 19793 </div>
<div style="display:none"> Gerrit-PatchSet: 5 </div>
<div style="display:none"> Gerrit-Owner: dexter <pmaier@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>