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

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">refactor lchan counting<br><br>Add chan_counts_for_trx() and chan_counts_for_bts(). Drop<br>bts_count_free_ts() and trx_count_free_ts().<br><br>Rationale:<br><br>The bts_count_free_ts() and trx_count_free_ts() always returned the<br>number of free lchans, not timeslots. Hence, passing the pchan type as<br>argument never really matched the semantics.<br><br>Especially, when looking for free SDCCH, there is no clear match on a<br>gsm_phys_chan_config enum value: SDCCH8_SACCH8C, CCCH_SDCCH4,<br>CCCH_SDCCH4_CBCH, SDCCH8_SACCH8C_CBCH? -- GSM_LCHAN_SDCCH is clear.<br><br>==> Rather count free lchans by enum gsm_chan_t.<br><br>Counting lchans of distinct types required separate iterations for each<br>lchan type.<br><br>==> Rather compose an array of counts for all types, in one go.<br><br>I need to count the amount of free SDCCH lchans in an upcoming patch to<br>implement the performance indicator allAvailableAllocatedSDCCH (cumulate<br>time for which no SDCCH are available).<br><br>To implement allAvailableAllocated{SDCCH,TCH}, I need a count of both<br>the used as well as the total lchans for a type: it does not make sense<br>to flag "all available allocated" if none are ever available.<br><br>To properly count dynamic ts, I need the maximum total that can be<br>possible at any time. And to count currently free lchans, I need the<br>current total. This may seem counter intuitive, but consider, e.g.:<br><br>- Obviously, if a cell has only static TCH/F timeslots, it does not make<br>  sense to flag that all available TCH/H are occupied, because no TCH/H<br>  are available ever. Just stating this as contrast to dyn TS.<br><br>- If a cell has OSMO_DYN timeslots, I *do* want to flag that all TCH/H<br>  are occupied when all dyn timeslots are fully occupied.<br><br>- If those OSMO_DYN however are all used as TCH/F, the current total of<br>  TCH/H becomes zero, and it seems like TCH/H should not be considered.<br><br>- To count the nr of currently free lchans, I need the currently<br>  possible total of lchans and the nr of occupied lchans.<br><br>So return both a maximum total and a current total of lchans. In above<br>example, the maximum total shows that there would be TCH/H possible.<br><br>BTW, it would be nice to keep a chan_counts array on trx, bts and bsc<br>level and update as channels are allocated and released, instead of<br>counting them all over periodically. But it's less error prone this way.<br><br>Related: SYS#4878<br>Change-Id: I2fb48c549186db812b1e9d6b735a92e80f27b8d3<br>---<br>M include/osmocom/bsc/Makefile.am<br>M include/osmocom/bsc/bts.h<br>M include/osmocom/bsc/bts_trx.h<br>A include/osmocom/bsc/chan_counts.h<br>M src/osmo-bsc/Makefile.am<br>M src/osmo-bsc/abis_rsl.c<br>M src/osmo-bsc/bts.c<br>M src/osmo-bsc/bts_trx.c<br>A src/osmo-bsc/chan_counts.c<br>M src/osmo-bsc/handover_decision_2.c<br>10 files changed, 238 insertions(+), 72 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am</span><br><span>index 3bccf44..3ddad45 100644</span><br><span>--- a/include/osmocom/bsc/Makefile.am</span><br><span>+++ b/include/osmocom/bsc/Makefile.am</span><br><span>@@ -16,6 +16,7 @@</span><br><span>      bts_trx.h \</span><br><span>  bts_ipaccess_nanobts_omlattr.h \</span><br><span>     chan_alloc.h \</span><br><span style="color: hsl(120, 100%, 40%);">+        chan_counts.h \</span><br><span>      codec_pref.h \</span><br><span>       ctrl.h \</span><br><span>     debug.h \</span><br><span>diff --git a/include/osmocom/bsc/bts.h b/include/osmocom/bsc/bts.h</span><br><span>index 7e73acd..c887ca8 100644</span><br><span>--- a/include/osmocom/bsc/bts.h</span><br><span>+++ b/include/osmocom/bsc/bts.h</span><br><span>@@ -726,8 +726,6 @@</span><br><span> </span><br><span> void gsm_bts_all_ts_dispatch(struct gsm_bts *bts, uint32_t ts_ev, void *data);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> int gsm_bts_set_system_infos(struct gsm_bts *bts);</span><br><span> </span><br><span> int gsm_bts_set_c0_power_red(struct gsm_bts *bts, const uint8_t red);</span><br><span>diff --git a/include/osmocom/bsc/bts_trx.h b/include/osmocom/bsc/bts_trx.h</span><br><span>index 4d705d0..c8df9d9 100644</span><br><span>--- a/include/osmocom/bsc/bts_trx.h</span><br><span>+++ b/include/osmocom/bsc/bts_trx.h</span><br><span>@@ -95,7 +95,6 @@</span><br><span> bool trx_is_usable(const struct gsm_bts_trx *trx);</span><br><span> </span><br><span> void gsm_trx_all_ts_dispatch(struct gsm_bts_trx *trx, uint32_t ts_ev, void *data);</span><br><span style="color: hsl(0, 100%, 40%);">-int trx_count_free_ts(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan);</span><br><span> bool trx_has_valid_pchan_config(const struct gsm_bts_trx *trx);</span><br><span> </span><br><span> int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx);</span><br><span>diff --git a/include/osmocom/bsc/chan_counts.h b/include/osmocom/bsc/chan_counts.h</span><br><span>new file mode 100644</span><br><span>index 0000000..9f73bc4</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/bsc/chan_counts.h</span><br><span>@@ -0,0 +1,76 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* API to count total, allocated and free channels of all types */</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct gsm_bts;</span><br><span style="color: hsl(120, 100%, 40%);">+struct gsm_bts_trx;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* First array index to typedef chan_counts_arr. */</span><br><span style="color: hsl(120, 100%, 40%);">+enum chan_counts_dim1 {</span><br><span style="color: hsl(120, 100%, 40%);">+       CHAN_COUNTS1_ALL = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ CHAN_COUNTS1_STATIC = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+      CHAN_COUNTS1_DYNAMIC = 2,</span><br><span style="color: hsl(120, 100%, 40%);">+     _CHAN_COUNTS1_NUM</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%);">+/* Second array index to typedef chan_counts_arr. */</span><br><span style="color: hsl(120, 100%, 40%);">+enum chan_counts_dim2 {</span><br><span style="color: hsl(120, 100%, 40%);">+        /* The maximum possible nr of lchans of this type. Counts all dynamic timeslots as if they are fully available</span><br><span style="color: hsl(120, 100%, 40%);">+         * for this type, regardless of the current pchan mode. (For CHAN_COUNTS1_STATIC, of course no dyn TS are counted</span><br><span style="color: hsl(120, 100%, 40%);">+      * at all.) */</span><br><span style="color: hsl(120, 100%, 40%);">+        CHAN_COUNTS2_MAX_TOTAL = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Like MAX_TOTAL, but as soon as dynamic timeslots are switched to a specific pchan kind, current_total shrinks</span><br><span style="color: hsl(120, 100%, 40%);">+       * to count only currently present lchans (used and unused). */</span><br><span style="color: hsl(120, 100%, 40%);">+       CHAN_COUNTS2_CURRENT_TOTAL = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Currently used lchans of this type. To get currently free lchans, calculate CURRENT_TOTAL - ALLOCATED. */</span><br><span style="color: hsl(120, 100%, 40%);">+  CHAN_COUNTS2_ALLOCATED = 2,</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Currently assignable lchans of this type, same as CURRENT_TOTAL - ALLOCATED. */</span><br><span style="color: hsl(120, 100%, 40%);">+    CHAN_COUNTS2_FREE = 3,</span><br><span style="color: hsl(120, 100%, 40%);">+        _CHAN_COUNTS2_NUM</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%);">+struct chan_counts {</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int val[_CHAN_COUNTS1_NUM][_CHAN_COUNTS2_NUM][_GSM_LCHAN_MAX];</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 chan_counts_for_bts(struct chan_counts *bts_counts, const struct gsm_bts *bts);</span><br><span style="color: hsl(120, 100%, 40%);">+void chan_counts_for_trx(struct chan_counts *trx_counts, const struct gsm_bts_trx *trx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static inline void chan_counts_zero(struct chan_counts *counts)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   *counts = (struct chan_counts){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%);">+static inline void chan_counts_dim3_add(struct chan_counts *dst,</span><br><span style="color: hsl(120, 100%, 40%);">+                                     enum chan_counts_dim1 dst_dim1, enum chan_counts_dim2 dst_dim2,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       const struct chan_counts *add,</span><br><span style="color: hsl(120, 100%, 40%);">+                                        enum chan_counts_dim1 add_dim1, enum chan_counts_dim2 add_dim2)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        for (i = 0; i < _GSM_LCHAN_MAX; i++)</span><br><span style="color: hsl(120, 100%, 40%);">+               dst->val[dst_dim1][dst_dim2][i] += add->val[add_dim1][add_dim2][i];</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 inline void chan_counts_dim3_sub(struct chan_counts *dst,</span><br><span style="color: hsl(120, 100%, 40%);">+                                      enum chan_counts_dim1 dst_dim1, enum chan_counts_dim2 dst_dim2,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       const struct chan_counts *sub,</span><br><span style="color: hsl(120, 100%, 40%);">+                                        enum chan_counts_dim1 sub_dim1, enum chan_counts_dim2 sub_dim2)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        for (i = 0; i < _GSM_LCHAN_MAX; i++)</span><br><span style="color: hsl(120, 100%, 40%);">+               dst->val[dst_dim1][dst_dim2][i] -= sub->val[sub_dim1][sub_dim2][i];</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 inline void chan_counts_dim2_add(struct chan_counts *dst, enum chan_counts_dim1 dst_dim1,</span><br><span style="color: hsl(120, 100%, 40%);">+                                      const struct chan_counts *add, enum chan_counts_dim1 add_dim1)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        for (i = 0; i < _CHAN_COUNTS2_NUM; i++)</span><br><span style="color: hsl(120, 100%, 40%);">+            chan_counts_dim3_add(dst, dst_dim1, i, add, add_dim1, i);</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 inline void chan_counts_add(struct chan_counts *dst, const struct chan_counts *add)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        for (i = 0; i < _CHAN_COUNTS1_NUM; i++)</span><br><span style="color: hsl(120, 100%, 40%);">+            chan_counts_dim2_add(dst, i, add, i);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am</span><br><span>index 3d6666e..583fb79 100644</span><br><span>--- a/src/osmo-bsc/Makefile.am</span><br><span>+++ b/src/osmo-bsc/Makefile.am</span><br><span>@@ -57,6 +57,7 @@</span><br><span>    bts_vty.c \</span><br><span>  bts_trx_vty.c \</span><br><span>      chan_alloc.c \</span><br><span style="color: hsl(120, 100%, 40%);">+        chan_counts.c \</span><br><span>      codec_pref.c \</span><br><span>       e1_config.c \</span><br><span>        gsm_04_08_rr.c \</span><br><span>diff --git a/src/osmo-bsc/abis_rsl.c b/src/osmo-bsc/abis_rsl.c</span><br><span>index 423c2b8..c0d621c 100644</span><br><span>--- a/src/osmo-bsc/abis_rsl.c</span><br><span>+++ b/src/osmo-bsc/abis_rsl.c</span><br><span>@@ -56,6 +56,7 @@</span><br><span> #include <osmocom/bsc/smscb.h></span><br><span> #include <osmocom/bsc/bts.h></span><br><span> #include <osmocom/bsc/power_control.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/chan_counts.h></span><br><span> </span><br><span> static void send_lchan_signal(int sig_no, struct gsm_lchan *lchan,</span><br><span>                          struct gsm_meas_rep *resp)</span><br><span>@@ -1946,6 +1947,7 @@</span><br><span> </span><br><span> struct gsm_lchan *_select_sdcch_for_call(struct gsm_bts *bts, const struct chan_rqd *rqd, enum gsm_chan_t lctype)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+        struct chan_counts bts_counts;</span><br><span>       struct gsm_lchan *lchan = NULL;</span><br><span>      int free_tchf, free_tchh;</span><br><span>    bool needs_dyn_switch;</span><br><span>@@ -1957,8 +1959,9 @@</span><br><span>       needs_dyn_switch = lchan->ts->pchan_on_init == GSM_PCHAN_OSMO_DYN &&</span><br><span>                                   lchan->ts->pchan_is != GSM_PCHAN_SDCCH8_SACCH8C;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      free_tchf = bts_count_free_ts(bts, GSM_PCHAN_TCH_F);</span><br><span style="color: hsl(0, 100%, 40%);">-    free_tchh = bts_count_free_ts(bts, GSM_PCHAN_TCH_H);</span><br><span style="color: hsl(120, 100%, 40%);">+  chan_counts_for_bts(&bts_counts, bts);</span><br><span style="color: hsl(120, 100%, 40%);">+    free_tchf = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];</span><br><span style="color: hsl(120, 100%, 40%);">+     free_tchh = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];</span><br><span>    if (free_tchf == 0 && free_tchh == 0) {</span><br><span>              LOG_BTS(bts, DRSL, LOGL_INFO,</span><br><span>                        "CHAN RQD: 0x%x Requesting %s reason=call but no TCH available\n",</span><br><span>diff --git a/src/osmo-bsc/bts.c b/src/osmo-bsc/bts.c</span><br><span>index 83adcc4..5701957 100644</span><br><span>--- a/src/osmo-bsc/bts.c</span><br><span>+++ b/src/osmo-bsc/bts.c</span><br><span>@@ -719,19 +719,6 @@</span><br><span>             gsm_trx_all_ts_dispatch(trx, ts_ev, data);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* Count number of free TS of given pchan type */</span><br><span style="color: hsl(0, 100%, 40%);">-int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        struct gsm_bts_trx *trx;</span><br><span style="color: hsl(0, 100%, 40%);">-        int count = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  llist_for_each_entry(trx, &bts->trx_list, list)</span><br><span style="color: hsl(0, 100%, 40%);">-          count += trx_count_free_ts(trx, pchan);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return count;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> /* set all system information types for a BTS */</span><br><span> int gsm_bts_set_system_infos(struct gsm_bts *bts)</span><br><span> {</span><br><span>diff --git a/src/osmo-bsc/bts_trx.c b/src/osmo-bsc/bts_trx.c</span><br><span>index f30c748..0333f70 100644</span><br><span>--- a/src/osmo-bsc/bts_trx.c</span><br><span>+++ b/src/osmo-bsc/bts_trx.c</span><br><span>@@ -292,54 +292,6 @@</span><br><span>     }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-int trx_count_free_ts(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-      struct gsm_bts_trx_ts *ts;</span><br><span style="color: hsl(0, 100%, 40%);">-      struct gsm_lchan *lchan;</span><br><span style="color: hsl(0, 100%, 40%);">-        int j;</span><br><span style="color: hsl(0, 100%, 40%);">-  int count = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  if (!trx_is_usable(trx))</span><br><span style="color: hsl(0, 100%, 40%);">-                return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       for (j = 0; j < ARRAY_SIZE(trx->ts); j++) {</span><br><span style="color: hsl(0, 100%, 40%);">-               ts = &trx->ts[j];</span><br><span style="color: hsl(0, 100%, 40%);">-                if (!ts_is_usable(ts))</span><br><span style="color: hsl(0, 100%, 40%);">-                  continue;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               if (ts->pchan_is == GSM_PCHAN_PDCH) {</span><br><span style="color: hsl(0, 100%, 40%);">-                        /* Dynamic timeslots in PDCH mode will become TCH if needed. */</span><br><span style="color: hsl(0, 100%, 40%);">-                 switch (ts->pchan_on_init) {</span><br><span style="color: hsl(0, 100%, 40%);">-                 case GSM_PCHAN_TCH_F_PDCH:</span><br><span style="color: hsl(0, 100%, 40%);">-                              if (pchan == GSM_PCHAN_TCH_F)</span><br><span style="color: hsl(0, 100%, 40%);">-                                   count++;</span><br><span style="color: hsl(0, 100%, 40%);">-                                continue;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       case GSM_PCHAN_OSMO_DYN:</span><br><span style="color: hsl(0, 100%, 40%);">-                                if (pchan == GSM_PCHAN_TCH_F)</span><br><span style="color: hsl(0, 100%, 40%);">-                                   count++;</span><br><span style="color: hsl(0, 100%, 40%);">-                                else if (pchan == GSM_PCHAN_TCH_H)</span><br><span style="color: hsl(0, 100%, 40%);">-                                      count += 2;</span><br><span style="color: hsl(0, 100%, 40%);">-                             continue;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                       default:</span><br><span style="color: hsl(0, 100%, 40%);">-                                /* Not dynamic, not applicable. */</span><br><span style="color: hsl(0, 100%, 40%);">-                              continue;</span><br><span style="color: hsl(0, 100%, 40%);">-                       }</span><br><span style="color: hsl(0, 100%, 40%);">-               }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               if (ts->pchan_is != pchan)</span><br><span style="color: hsl(0, 100%, 40%);">-                   continue;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               ts_for_n_lchans(lchan, ts, ts->max_primary_lchans) {</span><br><span style="color: hsl(0, 100%, 40%);">-                 if (lchan_state_is(lchan, LCHAN_ST_UNUSED))</span><br><span style="color: hsl(0, 100%, 40%);">-                             count++;</span><br><span style="color: hsl(0, 100%, 40%);">-                }</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       return count;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> bool trx_has_valid_pchan_config(const struct gsm_bts_trx *trx)</span><br><span> {</span><br><span>        bool combined = false;</span><br><span>diff --git a/src/osmo-bsc/chan_counts.c b/src/osmo-bsc/chan_counts.c</span><br><span>new file mode 100644</span><br><span>index 0000000..99e6e76</span><br><span>--- /dev/null</span><br><span>+++ b/src/osmo-bsc/chan_counts.c</span><br><span>@@ -0,0 +1,142 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* count total, allocated and free channels of all types.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU Affero General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</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%);">+#include <osmocom/gsm/gsm_utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/bts.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/bts_trx.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/lchan_fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/chan_counts.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const unsigned int lchans_per_pchan[_GSM_PCHAN_MAX][_GSM_LCHAN_MAX] = {</span><br><span style="color: hsl(120, 100%, 40%);">+  [GSM_PCHAN_NONE] = {0},</span><br><span style="color: hsl(120, 100%, 40%);">+       [GSM_PCHAN_CCCH] = { [GSM_LCHAN_CCCH] = 1, },</span><br><span style="color: hsl(120, 100%, 40%);">+ [GSM_PCHAN_PDCH] = { [GSM_LCHAN_PDTCH] = 1, },</span><br><span style="color: hsl(120, 100%, 40%);">+        [GSM_PCHAN_CCCH_SDCCH4] = {</span><br><span style="color: hsl(120, 100%, 40%);">+           [GSM_LCHAN_CCCH] = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+         [GSM_LCHAN_SDCCH] = 3,</span><br><span style="color: hsl(120, 100%, 40%);">+        },</span><br><span style="color: hsl(120, 100%, 40%);">+    [GSM_PCHAN_TCH_F] = { [GSM_LCHAN_TCH_F] = 1, },</span><br><span style="color: hsl(120, 100%, 40%);">+       [GSM_PCHAN_TCH_H] = { [GSM_LCHAN_TCH_H] = 2, },</span><br><span style="color: hsl(120, 100%, 40%);">+       [GSM_PCHAN_SDCCH8_SACCH8C] = { [GSM_LCHAN_SDCCH] = 8, },</span><br><span style="color: hsl(120, 100%, 40%);">+      [GSM_PCHAN_CCCH_SDCCH4_CBCH] = {</span><br><span style="color: hsl(120, 100%, 40%);">+              [GSM_LCHAN_CCCH] = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+         [GSM_LCHAN_SDCCH] = 3,</span><br><span style="color: hsl(120, 100%, 40%);">+                [GSM_LCHAN_CBCH] = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+ },</span><br><span style="color: hsl(120, 100%, 40%);">+    [GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = {</span><br><span style="color: hsl(120, 100%, 40%);">+           [GSM_LCHAN_SDCCH] = 8,</span><br><span style="color: hsl(120, 100%, 40%);">+                [GSM_LCHAN_CBCH] = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+ },</span><br><span style="color: hsl(120, 100%, 40%);">+    [GSM_PCHAN_OSMO_DYN] = {</span><br><span style="color: hsl(120, 100%, 40%);">+              [GSM_LCHAN_TCH_F] = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+                [GSM_LCHAN_TCH_H] = 2,</span><br><span style="color: hsl(120, 100%, 40%);">+                [GSM_LCHAN_SDCCH] = 8,</span><br><span style="color: hsl(120, 100%, 40%);">+                [GSM_LCHAN_PDTCH] = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+        },</span><br><span style="color: hsl(120, 100%, 40%);">+    [GSM_PCHAN_TCH_F_PDCH] = {</span><br><span style="color: hsl(120, 100%, 40%);">+            [GSM_LCHAN_TCH_F] = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+                [GSM_LCHAN_PDTCH] = 1,</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%);">+static inline void chan_counts_per_pchan_add(struct chan_counts *dst,</span><br><span style="color: hsl(120, 100%, 40%);">+                                            enum chan_counts_dim1 dim1, enum chan_counts_dim2 dim2,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       enum gsm_phys_chan_config pchan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        for (i = 0; i < _GSM_LCHAN_MAX; i++)</span><br><span style="color: hsl(120, 100%, 40%);">+               dst->val[dim1][dim2][i] += lchans_per_pchan[pchan][i];</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 chan_counts_for_trx(struct chan_counts *trx_counts, const struct gsm_bts_trx *trx)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    const struct gsm_bts_trx_ts *ts;</span><br><span style="color: hsl(120, 100%, 40%);">+      const struct gsm_lchan *lchan;</span><br><span style="color: hsl(120, 100%, 40%);">+        int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      chan_counts_zero(trx_counts);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!trx_is_usable(trx))</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%);">+     for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+             bool ts_is_dynamic;</span><br><span style="color: hsl(120, 100%, 40%);">+           struct chan_counts ts_count = {0};</span><br><span style="color: hsl(120, 100%, 40%);">+            ts = &trx->ts[i];</span><br><span style="color: hsl(120, 100%, 40%);">+              if (!ts_is_usable(ts))</span><br><span style="color: hsl(120, 100%, 40%);">+                        continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           /* Count the full potential nr of lchans for dynamic TS */</span><br><span style="color: hsl(120, 100%, 40%);">+            chan_counts_per_pchan_add(&ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_MAX_TOTAL, ts->pchan_on_init);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           switch (ts->pchan_on_init) {</span><br><span style="color: hsl(120, 100%, 40%);">+               case GSM_PCHAN_TCH_F_PDCH:</span><br><span style="color: hsl(120, 100%, 40%);">+            case GSM_PCHAN_OSMO_DYN:</span><br><span style="color: hsl(120, 100%, 40%);">+                      ts_is_dynamic = true;</span><br><span style="color: hsl(120, 100%, 40%);">+                 break;</span><br><span style="color: hsl(120, 100%, 40%);">+                default:</span><br><span style="color: hsl(120, 100%, 40%);">+                      ts_is_dynamic = false;</span><br><span style="color: hsl(120, 100%, 40%);">+                        break;</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 (ts_is_dynamic && ts->pchan_is == GSM_PCHAN_PDCH) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     /* Dynamic timeslots in PDCH mode can become TCH or SDCCH immediately,</span><br><span style="color: hsl(120, 100%, 40%);">+                         * so set CURRENT_TOTAL = MAX_TOTAL. */</span><br><span style="color: hsl(120, 100%, 40%);">+                       chan_counts_dim3_add(&ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_CURRENT_TOTAL,</span><br><span style="color: hsl(120, 100%, 40%);">+                                          &ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_MAX_TOTAL);</span><br><span style="color: hsl(120, 100%, 40%);">+                } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                      /* Static TS, or dyn TS that are currently fixed on a specific pchan: count lchans for the</span><br><span style="color: hsl(120, 100%, 40%);">+                     * current pchan mode. */</span><br><span style="color: hsl(120, 100%, 40%);">+                     chan_counts_per_pchan_add(&ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_CURRENT_TOTAL, ts->pchan_is);</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%);">+           /* Count currently allocated lchans */</span><br><span style="color: hsl(120, 100%, 40%);">+                ts_for_n_lchans(lchan, ts, ts->max_primary_lchans) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (!lchan_state_is(lchan, LCHAN_ST_UNUSED))</span><br><span style="color: hsl(120, 100%, 40%);">+                          ts_count.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_ALLOCATED][lchan->type]++;</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%);">+           chan_counts_dim3_add(&ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_FREE,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   &ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_CURRENT_TOTAL);</span><br><span style="color: hsl(120, 100%, 40%);">+            chan_counts_dim3_sub(&ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_FREE,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   &ts_count, CHAN_COUNTS1_ALL, CHAN_COUNTS2_ALLOCATED);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+              if (ts_is_dynamic)</span><br><span style="color: hsl(120, 100%, 40%);">+                    chan_counts_dim2_add(trx_counts, CHAN_COUNTS1_DYNAMIC, &ts_count, CHAN_COUNTS1_ALL);</span><br><span style="color: hsl(120, 100%, 40%);">+              else</span><br><span style="color: hsl(120, 100%, 40%);">+                  chan_counts_dim2_add(trx_counts, CHAN_COUNTS1_STATIC, &ts_count, CHAN_COUNTS1_ALL);</span><br><span style="color: hsl(120, 100%, 40%);">+               chan_counts_dim2_add(trx_counts, CHAN_COUNTS1_ALL, &ts_count, CHAN_COUNTS1_ALL);</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%);">+void chan_counts_for_bts(struct chan_counts *bts_counts, const struct gsm_bts *bts)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct gsm_bts_trx *trx;</span><br><span style="color: hsl(120, 100%, 40%);">+      chan_counts_zero(bts_counts);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       llist_for_each_entry(trx, &bts->trx_list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+              struct chan_counts trx_counts;</span><br><span style="color: hsl(120, 100%, 40%);">+                chan_counts_for_trx(&trx_counts, trx);</span><br><span style="color: hsl(120, 100%, 40%);">+            chan_counts_add(bts_counts, &trx_counts);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/src/osmo-bsc/handover_decision_2.c b/src/osmo-bsc/handover_decision_2.c</span><br><span>index e384feb..6730f26 100644</span><br><span>--- a/src/osmo-bsc/handover_decision_2.c</span><br><span>+++ b/src/osmo-bsc/handover_decision_2.c</span><br><span>@@ -42,6 +42,7 @@</span><br><span> #include <osmocom/bsc/timeslot_fsm.h></span><br><span> #include <osmocom/bsc/bts.h></span><br><span> #include <osmocom/bsc/lchan_select.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/chan_counts.h></span><br><span> </span><br><span> #define LOGPHOBTS(bts, level, fmt, args...) \</span><br><span>   LOGP(DHODEC, level, "(BTS %u) " fmt, bts->nr, ## args)</span><br><span>@@ -990,12 +991,15 @@</span><br><span> </span><br><span> static void candidate_set_free_tch(struct ho_candidate *c)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+    struct chan_counts bts_counts;</span><br><span>       struct gsm_lchan *next_lchan;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-       c->current.free_tchf = bts_count_free_ts(c->current.bts, GSM_PCHAN_TCH_F);</span><br><span style="color: hsl(120, 100%, 40%);">+      chan_counts_for_bts(&bts_counts, c->current.bts);</span><br><span style="color: hsl(120, 100%, 40%);">+      c->current.free_tchf = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];</span><br><span>      c->current.min_free_tchf = ho_get_hodec2_tchf_min_slots(c->current.bts->ho);</span><br><span style="color: hsl(0, 100%, 40%);">-   c->current.free_tchh = bts_count_free_ts(c->current.bts, GSM_PCHAN_TCH_H);</span><br><span style="color: hsl(120, 100%, 40%);">+      c->current.free_tchh = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];</span><br><span>      c->current.min_free_tchh = ho_get_hodec2_tchh_min_slots(c->current.bts->ho);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>      switch (c->current.lchan->ts->pchan_is) {</span><br><span>   case GSM_PCHAN_TCH_F:</span><br><span>                c->current.free_tch = c->current.free_tchf;</span><br><span>@@ -1023,9 +1027,10 @@</span><br><span>           break;</span><br><span>       }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   c->target.free_tchf = bts_count_free_ts(c->target.bts, GSM_PCHAN_TCH_F);</span><br><span style="color: hsl(120, 100%, 40%);">+        chan_counts_for_bts(&bts_counts, c->target.bts);</span><br><span style="color: hsl(120, 100%, 40%);">+       c->target.free_tchf = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];</span><br><span>       c->target.min_free_tchf = ho_get_hodec2_tchf_min_slots(c->target.bts->ho);</span><br><span style="color: hsl(0, 100%, 40%);">-     c->target.free_tchh = bts_count_free_ts(c->target.bts, GSM_PCHAN_TCH_H);</span><br><span style="color: hsl(120, 100%, 40%);">+        c->target.free_tchh = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];</span><br><span>       c->target.min_free_tchh = ho_get_hodec2_tchh_min_slots(c->target.bts->ho);</span><br><span> </span><br><span>      /* Would the next TCH/F lchan occupy a dynamic timeslot that currently counts for free TCH/H timeslots? */</span><br><span>@@ -1928,6 +1933,7 @@</span><br><span> </span><br><span> static void bts_congestion_check(struct gsm_bts *bts)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+  struct chan_counts bts_counts;</span><br><span>       int min_free_tchf, min_free_tchh;</span><br><span>    int free_tchf, free_tchh;</span><br><span> </span><br><span>@@ -1955,8 +1961,9 @@</span><br><span>                return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   free_tchf = bts_count_free_ts(bts, GSM_PCHAN_TCH_F);</span><br><span style="color: hsl(0, 100%, 40%);">-    free_tchh = bts_count_free_ts(bts, GSM_PCHAN_TCH_H);</span><br><span style="color: hsl(120, 100%, 40%);">+  chan_counts_for_bts(&bts_counts, bts);</span><br><span style="color: hsl(120, 100%, 40%);">+    free_tchf = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];</span><br><span style="color: hsl(120, 100%, 40%);">+     free_tchh = bts_counts.val[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_H];</span><br><span>    LOGPHOBTS(bts, LOGL_INFO, "Congestion check: (free/want-free) TCH/F=%d/%d TCH/H=%d/%d\n",</span><br><span>            free_tchf, min_free_tchf, free_tchh, min_free_tchh);</span><br><span> </span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-bsc/+/25972">change 25972</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/+/25972"/><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: I2fb48c549186db812b1e9d6b735a92e80f27b8d3 </div>
<div style="display:none"> Gerrit-Change-Number: 25972 </div>
<div style="display:none"> Gerrit-PatchSet: 9 </div>
<div style="display:none"> Gerrit-Owner: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: dexter <pmaier@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: fixeria <vyanitskiy@sysmocom.de> </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-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>