<p>neels has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/osmo-bsc/+/25972">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">refactor lchan counting<br><br>Add trx_count_lchans() and bts_count_lchans(). Drop bts_count_free_ts()<br>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/bts.h<br>M include/osmocom/bsc/bts_trx.h<br>M include/osmocom/bsc/gsm_data.h<br>M src/osmo-bsc/abis_rsl.c<br>M src/osmo-bsc/bts.c<br>M src/osmo-bsc/bts_trx.c<br>M src/osmo-bsc/gsm_data.c<br>M src/osmo-bsc/handover_decision_2.c<br>8 files changed, 265 insertions(+), 50 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/osmo-bsc refs/changes/72/25972/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/bsc/bts.h b/include/osmocom/bsc/bts.h</span><br><span>index 68654f2..700033c 100644</span><br><span>--- a/include/osmocom/bsc/bts.h</span><br><span>+++ b/include/osmocom/bsc/bts.h</span><br><span>@@ -712,7 +712,7 @@</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(120, 100%, 40%);">+void bts_count_lchans(chan_counts_t bts_counts, struct gsm_bts *bts);</span><br><span> </span><br><span> int gsm_bts_set_system_infos(struct gsm_bts *bts);</span><br><span> </span><br><span>diff --git a/include/osmocom/bsc/bts_trx.h b/include/osmocom/bsc/bts_trx.h</span><br><span>index 4d705d0..ca6e8b5 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,7 @@</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 style="color: hsl(120, 100%, 40%);">+void trx_count_lchans(chan_counts_t counts, const struct gsm_bts_trx *trx);</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/gsm_data.h b/include/osmocom/bsc/gsm_data.h</span><br><span>index cb56028..3d2c901 100644</span><br><span>--- a/include/osmocom/bsc/gsm_data.h</span><br><span>+++ b/include/osmocom/bsc/gsm_data.h</span><br><span>@@ -1135,6 +1135,7 @@</span><br><span> </span><br><span> uint8_t pchan_subslots(enum gsm_phys_chan_config pchan);</span><br><span> uint8_t pchan_subslots_vamos(enum gsm_phys_chan_config pchan);</span><br><span style="color: hsl(120, 100%, 40%);">+int pchan_subslots_for_lchant(enum gsm_phys_chan_config pchan, enum gsm_chan_t type);</span><br><span> bool ts_is_tch(struct gsm_bts_trx_ts *ts);</span><br><span> </span><br><span> struct gsm_lchan *gsm_lchan_vamos_to_primary(const struct gsm_lchan *lchan_vamos);</span><br><span>@@ -1410,4 +1411,73 @@</span><br><span> </span><br><span> enum rsl_cmod_spd chan_mode_to_rsl_cmod_spd(enum gsm48_chan_mode chan_mode);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* First array index to typedef chan_counts_t. */</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_N</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%);">+extern const struct value_string chan_count1_strs[];</span><br><span style="color: hsl(120, 100%, 40%);">+static inline const char *chan_count1_str(enum chan_counts_dim1 val)</span><br><span style="color: hsl(120, 100%, 40%);">+{ return get_value_string(chan_count1_strs, val); }</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_t. */</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_N</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%);">+extern const struct value_string chan_count2_strs[];</span><br><span style="color: hsl(120, 100%, 40%);">+static inline const char *chan_count2_str(enum chan_counts_dim2 val)</span><br><span style="color: hsl(120, 100%, 40%);">+{ return get_value_string(chan_count2_strs, val); }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+typedef unsigned int chan_counts_dim3 [_GSM_LCHAN_MAX];</span><br><span style="color: hsl(120, 100%, 40%);">+typedef unsigned int chan_counts_dim2 [CHAN_COUNTS2_N][_GSM_LCHAN_MAX];</span><br><span style="color: hsl(120, 100%, 40%);">+typedef unsigned int chan_counts_t [CHAN_COUNTS1_N][CHAN_COUNTS2_N][_GSM_LCHAN_MAX];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static inline void chan_counts_zero(chan_counts_t counts)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       memset(counts, 0, sizeof(chan_counts_t));</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(chan_counts_dim3 dst, const chan_counts_dim3 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 < _GSM_LCHAN_MAX; i++)</span><br><span style="color: hsl(120, 100%, 40%);">+               dst[i] += add[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(chan_counts_dim3 dst, const chan_counts_dim3 sub)</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[i] = (sub[i] < dst[i]) ? dst[i] - sub[i] : 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_dim2_add(chan_counts_dim2 dst, const chan_counts_dim2 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_COUNTS2_N; i++)</span><br><span style="color: hsl(120, 100%, 40%);">+               chan_counts_dim3_add(dst[i], add[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(chan_counts_t dst, const chan_counts_t 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_N; 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 style="color: hsl(120, 100%, 40%);">+</span><br><span> #endif /* _GSM_DATA_H */</span><br><span>diff --git a/src/osmo-bsc/abis_rsl.c b/src/osmo-bsc/abis_rsl.c</span><br><span>index 0e2ffc6..f539b07 100644</span><br><span>--- a/src/osmo-bsc/abis_rsl.c</span><br><span>+++ b/src/osmo-bsc/abis_rsl.c</span><br><span>@@ -1958,6 +1958,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%);">+ chan_counts_t 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>@@ -1969,8 +1970,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%);">+  bts_count_lchans(bts_counts, bts);</span><br><span style="color: hsl(120, 100%, 40%);">+    free_tchf = bts_counts[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];</span><br><span style="color: hsl(120, 100%, 40%);">+ free_tchh = bts_counts[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 21ee01a..2e4b520 100644</span><br><span>--- a/src/osmo-bsc/bts.c</span><br><span>+++ b/src/osmo-bsc/bts.c</span><br><span>@@ -719,17 +719,16 @@</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(120, 100%, 40%);">+void bts_count_lchans(chan_counts_t bts_counts, struct gsm_bts *bts)</span><br><span> {</span><br><span>   struct gsm_bts_trx *trx;</span><br><span style="color: hsl(0, 100%, 40%);">-        int count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+        chan_counts_zero(bts_counts);</span><br><span> </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(120, 100%, 40%);">+ llist_for_each_entry(trx, &bts->trx_list, list) {</span><br><span style="color: hsl(120, 100%, 40%);">+              chan_counts_t trx_counts;</span><br><span style="color: hsl(120, 100%, 40%);">+             trx_count_lchans(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> }</span><br><span> </span><br><span> /* set all system information types for a BTS */</span><br><span>diff --git a/src/osmo-bsc/bts_trx.c b/src/osmo-bsc/bts_trx.c</span><br><span>index f30c748..523a7c4 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,52 +292,94 @@</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(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%);">+void trx_count_lchans(chan_counts_t ret, const struct gsm_bts_trx *trx)</span><br><span> {</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(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(ret);</span><br><span> </span><br><span>   if (!trx_is_usable(trx))</span><br><span style="color: hsl(0, 100%, 40%);">-                return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span> </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(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%);">+           chan_counts_dim2 ts_count = {0};</span><br><span style="color: hsl(120, 100%, 40%);">+              ts = &trx->ts[i];</span><br><span>             if (!ts_is_usable(ts))</span><br><span>                       continue;</span><br><span> </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(120, 100%, 40%);">+             /* Count the full potential nr of lchans for dynamic TS */</span><br><span style="color: hsl(120, 100%, 40%);">+            chan_counts_dim3_add(ts_count[CHAN_COUNTS2_MAX_TOTAL], lchans_per_pchan[ts->pchan_on_init]);</span><br><span> </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(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>               }</span><br><span> </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(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_COUNTS2_CURRENT_TOTAL], ts_count[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_dim3_add(ts_count[CHAN_COUNTS2_CURRENT_TOTAL], lchans_per_pchan[ts->pchan_is]);</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+         /* Count currently allocated lchans */</span><br><span>               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(120, 100%, 40%);">+                      if (!lchan_state_is(lchan, LCHAN_ST_UNUSED))</span><br><span style="color: hsl(120, 100%, 40%);">+                          ts_count[CHAN_COUNTS2_ALLOCATED][lchan->type]++;</span><br><span>          }</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   return count;</span><br><span style="color: hsl(120, 100%, 40%);">+         chan_counts_dim3_add(ts_count[CHAN_COUNTS2_FREE], ts_count[CHAN_COUNTS2_CURRENT_TOTAL]);</span><br><span style="color: hsl(120, 100%, 40%);">+              chan_counts_dim3_sub(ts_count[CHAN_COUNTS2_FREE], ts_count[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(ret[CHAN_COUNTS1_DYNAMIC], ts_count);</span><br><span style="color: hsl(120, 100%, 40%);">+            else</span><br><span style="color: hsl(120, 100%, 40%);">+                  chan_counts_dim2_add(ret[CHAN_COUNTS1_STATIC], ts_count);</span><br><span style="color: hsl(120, 100%, 40%);">+             chan_counts_dim2_add(ret[CHAN_COUNTS1_ALL], ts_count);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span> }</span><br><span> </span><br><span> bool trx_has_valid_pchan_config(const struct gsm_bts_trx *trx)</span><br><span>diff --git a/src/osmo-bsc/gsm_data.c b/src/osmo-bsc/gsm_data.c</span><br><span>index 0134e34..9a5128a 100644</span><br><span>--- a/src/osmo-bsc/gsm_data.c</span><br><span>+++ b/src/osmo-bsc/gsm_data.c</span><br><span>@@ -646,6 +646,102 @@</span><br><span>        return subslots_per_pchan_vamos[pchan];</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! Return the maximum nr of lchans of the given type possible in the given pchan config.</span><br><span style="color: hsl(120, 100%, 40%);">+ * TODO: include possible VAMOS secondary lchans? */</span><br><span style="color: hsl(120, 100%, 40%);">+int pchan_subslots_for_lchant(enum gsm_phys_chan_config pchan, enum gsm_chan_t type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    switch (pchan) {</span><br><span style="color: hsl(120, 100%, 40%);">+      default:</span><br><span style="color: hsl(120, 100%, 40%);">+      case GSM_PCHAN_UNKNOWN:</span><br><span style="color: hsl(120, 100%, 40%);">+       case GSM_PCHAN_NONE:</span><br><span style="color: hsl(120, 100%, 40%);">+          return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     case GSM_PCHAN_CCCH:</span><br><span style="color: hsl(120, 100%, 40%);">+          switch (type) {</span><br><span style="color: hsl(120, 100%, 40%);">+               case GSM_LCHAN_CCCH:</span><br><span style="color: hsl(120, 100%, 40%);">+                  return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+             default:</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%);">+     case GSM_PCHAN_CCCH_SDCCH4:</span><br><span style="color: hsl(120, 100%, 40%);">+           switch (type) {</span><br><span style="color: hsl(120, 100%, 40%);">+               case GSM_LCHAN_CCCH:</span><br><span style="color: hsl(120, 100%, 40%);">+                  return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+             case GSM_LCHAN_SDCCH:</span><br><span style="color: hsl(120, 100%, 40%);">+                 return 4;</span><br><span style="color: hsl(120, 100%, 40%);">+             default:</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%);">+     case GSM_PCHAN_TCH_F:</span><br><span style="color: hsl(120, 100%, 40%);">+         switch (type) {</span><br><span style="color: hsl(120, 100%, 40%);">+               case GSM_LCHAN_TCH_F:</span><br><span style="color: hsl(120, 100%, 40%);">+                 return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+             default:</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%);">+     case GSM_PCHAN_TCH_H:</span><br><span style="color: hsl(120, 100%, 40%);">+         switch (type) {</span><br><span style="color: hsl(120, 100%, 40%);">+               case GSM_LCHAN_TCH_H:</span><br><span style="color: hsl(120, 100%, 40%);">+                 return 2;</span><br><span style="color: hsl(120, 100%, 40%);">+             default:</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%);">+     case GSM_PCHAN_SDCCH8_SACCH8C:</span><br><span style="color: hsl(120, 100%, 40%);">+                switch (type) {</span><br><span style="color: hsl(120, 100%, 40%);">+               case GSM_LCHAN_SDCCH:</span><br><span style="color: hsl(120, 100%, 40%);">+                 return 8;</span><br><span style="color: hsl(120, 100%, 40%);">+             default:</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%);">+     case GSM_PCHAN_PDCH:</span><br><span style="color: hsl(120, 100%, 40%);">+          switch (type) {</span><br><span style="color: hsl(120, 100%, 40%);">+               case GSM_LCHAN_PDTCH:</span><br><span style="color: hsl(120, 100%, 40%);">+                 return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+             default:</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%);">+     case GSM_PCHAN_TCH_F_PDCH:</span><br><span style="color: hsl(120, 100%, 40%);">+            switch (type) {</span><br><span style="color: hsl(120, 100%, 40%);">+               case GSM_LCHAN_PDTCH:</span><br><span style="color: hsl(120, 100%, 40%);">+         case GSM_LCHAN_TCH_F:</span><br><span style="color: hsl(120, 100%, 40%);">+                 return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+             default:</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%);">+     case GSM_PCHAN_CCCH_SDCCH4_CBCH:</span><br><span style="color: hsl(120, 100%, 40%);">+              switch (type) {</span><br><span style="color: hsl(120, 100%, 40%);">+               case GSM_LCHAN_CCCH:</span><br><span style="color: hsl(120, 100%, 40%);">+          case GSM_LCHAN_CBCH:</span><br><span style="color: hsl(120, 100%, 40%);">+                  return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+             case GSM_LCHAN_SDCCH:</span><br><span style="color: hsl(120, 100%, 40%);">+                 return 4;</span><br><span style="color: hsl(120, 100%, 40%);">+             default:</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%);">+     case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:</span><br><span style="color: hsl(120, 100%, 40%);">+           switch (type) {</span><br><span style="color: hsl(120, 100%, 40%);">+               case GSM_LCHAN_SDCCH:</span><br><span style="color: hsl(120, 100%, 40%);">+                 return 8;</span><br><span style="color: hsl(120, 100%, 40%);">+             case GSM_LCHAN_CBCH:</span><br><span style="color: hsl(120, 100%, 40%);">+                  return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+             default:</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%);">+     case GSM_PCHAN_OSMO_DYN:</span><br><span style="color: hsl(120, 100%, 40%);">+              switch (type) {</span><br><span style="color: hsl(120, 100%, 40%);">+               case GSM_LCHAN_SDCCH:</span><br><span style="color: hsl(120, 100%, 40%);">+                 return 8;</span><br><span style="color: hsl(120, 100%, 40%);">+             case GSM_LCHAN_TCH_F:</span><br><span style="color: hsl(120, 100%, 40%);">+                 return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+             case GSM_LCHAN_TCH_H:</span><br><span style="color: hsl(120, 100%, 40%);">+                 return 2;</span><br><span style="color: hsl(120, 100%, 40%);">+             case GSM_LCHAN_PDTCH:</span><br><span style="color: hsl(120, 100%, 40%);">+                 return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+             default:</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%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static bool pchan_is_tch(enum gsm_phys_chan_config pchan)</span><br><span> {</span><br><span>     switch (pchan) {</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 d08173c..387561c 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>@@ -990,12 +990,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%);">+  chan_counts_t 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%);">+      bts_count_lchans(bts_counts, c->current.bts);</span><br><span style="color: hsl(120, 100%, 40%);">+      c->current.free_tchf = bts_counts[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[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 +1026,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%);">+        bts_count_lchans(bts_counts, c->target.bts);</span><br><span style="color: hsl(120, 100%, 40%);">+       c->target.free_tchf = bts_counts[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[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 +1932,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%);">+  chan_counts_t 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 +1960,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%);">+  bts_count_lchans(bts_counts, bts);</span><br><span style="color: hsl(120, 100%, 40%);">+    free_tchf = bts_counts[CHAN_COUNTS1_ALL][CHAN_COUNTS2_FREE][GSM_LCHAN_TCH_F];</span><br><span style="color: hsl(120, 100%, 40%);">+ free_tchh = bts_counts[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: 1 </div>
<div style="display:none"> Gerrit-Owner: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>