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

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">hodec2: to balance congestion, use overload percent<br><br>For balancing load across congested cells and across congested TCH/*<br>kinds, instead of comparing the number of lchans above the configured<br>congestion threshold, compare the percent of lchans of overload.<br><br>In short, using a percentage prevents cells with less min-free-slots to<br>fill up 100% while neighbor cells still may have several free lchans<br>available.<br><br>An obvious example of why this is desirable is illustrated by<br>test_balance_congestion_by_percentage.ho_vty:<br><br>Cell A has min-free-slots 2, and has all slots occupied.<br>Cell B has min-free-slots 4, and has 2 slots remaining free.<br><br>If we count congested lchans as in current master: cell A has a<br>congestion count of 2: two more lchans in use than "allowed". If we move<br>one lchan over to cell B, it ends up with a congestion count of 3, which<br>is worse than 2. So when counting lchans, we decide that cell A should<br>remain full.<br><br>Instead, when comparing percentage of remaining lchans, we would see<br>that cell A is loaded 100% above congestion (2 of 2 remaining lchans in<br>use), but when moving one lchan to cell B, that would only be 75% loaded<br>above its treshold (3 of 4 remaining lchans in use). So a percentage<br>comparison would cause a handover to cell B.<br><br>Related: SYS#5259<br>Change-Id: I55234c6c99eb02ceee52be0d7388bea14304930f<br>---<br>M doc/manuals/chapters/handover.adoc<br>M src/osmo-bsc/handover_decision_2.c<br>M tests/handover/test_balance_congestion_by_percentage.ho_vty<br>M tests/handover/test_balance_congestion_tchf_tchh.ho_vty<br>4 files changed, 57 insertions(+), 23 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/doc/manuals/chapters/handover.adoc b/doc/manuals/chapters/handover.adoc</span><br><span>index 4de744b..56e9aea 100644</span><br><span>--- a/doc/manuals/chapters/handover.adoc</span><br><span>+++ b/doc/manuals/chapters/handover.adoc</span><br><span>@@ -511,9 +511,11 @@</span><br><span> cells. Every time, the one MS that will suffer the least RXLEV loss while still</span><br><span> reducing congestion will be instructed to move first.</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-If a cell and its neighbors are all loaded past their `min-free-slots`</span><br><span style="color: hsl(0, 100%, 40%);">-settings, the algorithmic aim is equal load: a load-based handover will never</span><br><span style="color: hsl(0, 100%, 40%);">-cause the target cell to be more congested than the source cell.</span><br><span style="color: hsl(120, 100%, 40%);">+If a cell and its neighbors are all loaded past their `min-free-slots` settings,</span><br><span style="color: hsl(120, 100%, 40%);">+the algorithmic aim is to improve the percentage of load above the</span><br><span style="color: hsl(120, 100%, 40%);">+`min-free-slots` setting: a load-based handover always requires the target cell</span><br><span style="color: hsl(120, 100%, 40%);">+to have a lower load percentage after handover than the source cell had before</span><br><span style="color: hsl(120, 100%, 40%);">+handover.</span><br><span> </span><br><span> The min-free-slots setting is a tradeoff between immediate voice service</span><br><span> availability and optimal reception levels. A sane choice could be:</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 c265f5f..0362c9b 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>@@ -25,6 +25,7 @@</span><br><span> #include <stdbool.h></span><br><span> #include <errno.h></span><br><span> #include <limits.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <math.h></span><br><span> </span><br><span> #include <osmocom/bsc/debug.h></span><br><span> #include <osmocom/bsc/gsm_data.h></span><br><span>@@ -387,6 +388,26 @@</span><br><span>   return false;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#define LOAD_PRECISION 6</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Return a number representing overload, i.e. the fraction of lchans used above the congestion threshold.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Think of it as a percentage of used lchans above congestion, just represented in a fixed-point fraction with N</span><br><span style="color: hsl(120, 100%, 40%);">+ * decimal digits of fractional part. If there is no congestion (free_tch >= min_free_tch), return 0.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int32_t load_above_congestion(int free_tch, int min_free_tch)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     int32_t v;</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(free_tch >= 0);</span><br><span style="color: hsl(120, 100%, 40%);">+        /* Avoid division by zero when no congestion threshold is set, and return zero overload when there is no</span><br><span style="color: hsl(120, 100%, 40%);">+       * congestion. */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (free_tch >= min_free_tch)</span><br><span style="color: hsl(120, 100%, 40%);">+              return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     v = min_free_tch - free_tch;</span><br><span style="color: hsl(120, 100%, 40%);">+  v *= pow(10, LOAD_PRECISION);</span><br><span style="color: hsl(120, 100%, 40%);">+ v /= min_free_tch;</span><br><span style="color: hsl(120, 100%, 40%);">+    return v;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*</span><br><span>  * Check what requirements the given cell fulfills.</span><br><span>  * A bit mask of fulfilled requirements is returned.</span><br><span>@@ -443,7 +464,7 @@</span><br><span> {</span><br><span>       uint8_t requirement = 0;</span><br><span>     unsigned int penalty_time;</span><br><span style="color: hsl(0, 100%, 40%);">-      int current_overbooked;</span><br><span style="color: hsl(120, 100%, 40%);">+       int32_t current_overbooked;</span><br><span>  c->requirements = 0;</span><br><span> </span><br><span>  /* Requirement A */</span><br><span>@@ -626,17 +647,25 @@</span><br><span> </span><br><span>      /* Requirement C */</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- /* the nr of lchans surpassing congestion on the target cell must be <= the lchans surpassing congestion on the</span><br><span style="color: hsl(0, 100%, 40%);">-       * current cell _after_ handover/assignment */</span><br><span style="color: hsl(0, 100%, 40%);">-  current_overbooked = c->current.min_free_tch - c->current.free_tch;</span><br><span style="color: hsl(120, 100%, 40%);">+     /* the load percentage above congestion on the target cell *after* HO must be < the load percentage above</span><br><span style="color: hsl(120, 100%, 40%);">+   * congestion on the current cell, hence the - 1 on the target. */</span><br><span style="color: hsl(120, 100%, 40%);">+    current_overbooked = load_above_congestion(c->current.free_tch, c->current.min_free_tch);</span><br><span>      if (requirement & REQUIREMENT_A_TCHF) {</span><br><span style="color: hsl(0, 100%, 40%);">-             int target_overbooked = c->target.min_free_tchf - c->target.free_tchf;</span><br><span style="color: hsl(0, 100%, 40%);">-            if (target_overbooked + 1 <= current_overbooked - 1)</span><br><span style="color: hsl(120, 100%, 40%);">+               int32_t target_overbooked = load_above_congestion(c->target.free_tchf - 1, c->target.min_free_tchf);</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG,</span><br><span style="color: hsl(120, 100%, 40%);">+                            "current overbooked = %s%%, TCH/F target overbooked after HO = %s%%\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                             osmo_int_to_float_str_c(OTC_SELECT, current_overbooked, LOAD_PRECISION - 2),</span><br><span style="color: hsl(120, 100%, 40%);">+                          osmo_int_to_float_str_c(OTC_SELECT, target_overbooked, LOAD_PRECISION - 2));</span><br><span style="color: hsl(120, 100%, 40%);">+         if (target_overbooked < current_overbooked)</span><br><span>                       requirement |= REQUIREMENT_C_TCHF;</span><br><span>   }</span><br><span>    if (requirement & REQUIREMENT_A_TCHH) {</span><br><span style="color: hsl(0, 100%, 40%);">-             int target_overbooked = c->target.min_free_tchh - c->target.free_tchh;</span><br><span style="color: hsl(0, 100%, 40%);">-            if (target_overbooked + 1 <= current_overbooked - 1)</span><br><span style="color: hsl(120, 100%, 40%);">+               int32_t target_overbooked = load_above_congestion(c->target.free_tchh - 1, c->target.min_free_tchh);</span><br><span style="color: hsl(120, 100%, 40%);">+            LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG,</span><br><span style="color: hsl(120, 100%, 40%);">+                            "current overbooked = %s%%, TCH/H target overbooked after HO = %s%%\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                             osmo_int_to_float_str_c(OTC_SELECT, current_overbooked, LOAD_PRECISION - 2),</span><br><span style="color: hsl(120, 100%, 40%);">+                          osmo_int_to_float_str_c(OTC_SELECT, target_overbooked, LOAD_PRECISION - 2));</span><br><span style="color: hsl(120, 100%, 40%);">+         if (target_overbooked < current_overbooked)</span><br><span>                       requirement |= REQUIREMENT_C_TCHH;</span><br><span>   }</span><br><span> </span><br><span>diff --git a/tests/handover/test_balance_congestion_by_percentage.ho_vty b/tests/handover/test_balance_congestion_by_percentage.ho_vty</span><br><span>index e00636e..09d2151 100644</span><br><span>--- a/tests/handover/test_balance_congestion_by_percentage.ho_vty</span><br><span>+++ b/tests/handover/test_balance_congestion_by_percentage.ho_vty</span><br><span>@@ -29,6 +29,7 @@</span><br><span> meas-rep lchan * * * * rxlev 40 rxqual 0 ta 0 neighbors 30</span><br><span> expect-no-chan</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-# bts 0 is full, but by counting lchans above congestion, it should remain full.</span><br><span style="color: hsl(120, 100%, 40%);">+# bts 0 is full, by target_overbooked_after_ho==75% < current_overbooked_before_ho==100%, a congestion balancing to bts</span><br><span style="color: hsl(120, 100%, 40%);">+# 1 is performed.</span><br><span> congestion-check</span><br><span style="color: hsl(0, 100%, 40%);">-expect-no-chan</span><br><span style="color: hsl(120, 100%, 40%);">+expect-ho from lchan 0 0 1 0 to lchan 1 0 5 0</span><br><span>diff --git a/tests/handover/test_balance_congestion_tchf_tchh.ho_vty b/tests/handover/test_balance_congestion_tchf_tchh.ho_vty</span><br><span>index 7f9039f..62f07bf 100644</span><br><span>--- a/tests/handover/test_balance_congestion_tchf_tchh.ho_vty</span><br><span>+++ b/tests/handover/test_balance_congestion_tchf_tchh.ho_vty</span><br><span>@@ -6,46 +6,48 @@</span><br><span> </span><br><span> create-bts trx-count 1 timeslots     c+s4    TCH/F   TCH/F   TCH/F   TCH/F   TCH/H   TCH/H   TCH/H</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-# both TCH/H and TCH/F have one lchan above congestion, nothing happens</span><br><span style="color: hsl(120, 100%, 40%);">+# both TCH/H and TCH/F have one lchan = 33% above congestion, nothing happens</span><br><span> set-ts-use trx 0 0 states            *       TCH/F   TCH/F   -       -       TCH/HH  TCH/HH  -</span><br><span> meas-rep lchan * * * * rxlev 10 rxqual 0 ta 0</span><br><span> congestion-check</span><br><span> expect-no-chan</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-# TCH/F = +1, TCH/H = +2 above congestion. Moving a TCH/H to TCH/F would just reverse the situation to F=+2 H=+1. Nothing happens.</span><br><span style="color: hsl(120, 100%, 40%);">+# TCH/F = +1 = 33%, TCH/H = +2 = 66% above congestion.</span><br><span style="color: hsl(120, 100%, 40%);">+# Moving a TCH/H to TCH/F would just reverse the situation to F=+2=66%. Nothing happens.</span><br><span> set-ts-use trx 0 0 states           *       TCH/F   TCH/F   -       -       TCH/HH  TCH/HH  TCH/H-</span><br><span> meas-rep lchan * * * * rxlev 10 rxqual 0 ta 0</span><br><span> congestion-check</span><br><span> expect-no-chan</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-# F=+1 H=+3. Balance to F=+2 H=+2</span><br><span style="color: hsl(120, 100%, 40%);">+# F=+1=33% H=+3=100%. Balance to F=+2=66% (which is < 100%) and H=+2=66%</span><br><span> set-ts-use trx 0 0 states            *       TCH/F   TCH/F   -       -       TCH/HH  TCH/HH  TCH/HH</span><br><span> meas-rep lchan * * * * rxlev 10 rxqual 0 ta 0</span><br><span> congestion-check</span><br><span> expect-ho from lchan 0 0 5 0 to lchan 0 0 3 0</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-# Now the exact same thing, just with different min-free-slots settings for</span><br><span style="color: hsl(0, 100%, 40%);">-# tch/f vs tch/h</span><br><span style="color: hsl(120, 100%, 40%);">+# Now similar load percentages, just with different min-free-slots settings for tch/f vs tch/h.</span><br><span> </span><br><span> network</span><br><span>  handover2 min-free-slots tch/f 3</span><br><span>  handover2 min-free-slots tch/h 5</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-# both TCH/H and TCH/F have one lchan above congestion, nothing happens</span><br><span style="color: hsl(120, 100%, 40%);">+# TCH/F has 1/3 = 33%, TCH/H has 1/5 = 20% overload.</span><br><span style="color: hsl(120, 100%, 40%);">+# Moving one to TCH/H would mean 40% overload on TCH/H, which is above the current TCH/F of 33%.</span><br><span style="color: hsl(120, 100%, 40%);">+# Nothing happens.</span><br><span> set-ts-use trx 0 0 states               *       TCH/F   TCH/F   -       -       TCH/HH  -       -</span><br><span> meas-rep lchan * * * * rxlev 20 rxqual 0 ta 0</span><br><span> congestion-check</span><br><span> expect-no-chan</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-# TCH/F = +1, TCH/H = +2 above congestion. Moving a TCH/H to TCH/F would just</span><br><span style="color: hsl(0, 100%, 40%);">-# reverse the situation to F=+2 H=+1. Nothing happens.</span><br><span style="color: hsl(120, 100%, 40%);">+# TCH/F = +1 = 33%, TCH/H = +2 = 40% above congestion. Moving a TCH/H to TCH/F would result</span><br><span style="color: hsl(120, 100%, 40%);">+# in F=+2=66%>40%. Nothing happens.</span><br><span> set-ts-use trx 0 0 states               *       TCH/F   TCH/F   -       -       TCH/HH  TCH/H-  -</span><br><span> meas-rep lchan * * * * rxlev 20 rxqual 0 ta 0</span><br><span> congestion-check</span><br><span> expect-no-chan</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-# F=+1 H=+3. Balance to F=+2 H=+2</span><br><span style="color: hsl(0, 100%, 40%);">-set-ts-use trx 0 0 states          *       TCH/F   TCH/F   -       -       TCH/HH  TCH/HH  -</span><br><span style="color: hsl(120, 100%, 40%);">+# F=+1=33% H=+4=80%. Balance to F=+2=66%<80% and H=+3=60%</span><br><span style="color: hsl(120, 100%, 40%);">+set-ts-use trx 0 0 states              *       TCH/F   TCH/F   -       -       TCH/HH  TCH/HH  TCH/H-</span><br><span> meas-rep lchan * * * * rxlev 20 rxqual 0 ta 0</span><br><span> congestion-check</span><br><span> expect-ho from lchan 0 0 5 0 to lchan 0 0 3 0</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-bsc/+/22085">change 22085</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/+/22085"/><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: I55234c6c99eb02ceee52be0d7388bea14304930f </div>
<div style="display:none"> Gerrit-Change-Number: 22085 </div>
<div style="display:none"> Gerrit-PatchSet: 7 </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: 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>