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

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">trx_toolkit/transceiver.py: implement the transmit burst queue<br><br>In order to reflect the UL/DL delay caused by the premature burst<br>scheduling (a.k.a. 'fn-advance') in a virtual environment, the<br>Transceiver implementation now queues all to be transmitted bursts,<br>so they remain in the queue until the appropriate time of transmission.<br><br>The API user is supposed to call recv_data_msg() in order to obtain<br>a L12TRX message on the TRXD (data) inteface, so it gets queued by<br>this function.  Then, to ensure the timeous transmission, the user<br>of this implementation needs to call clck_tick() on each TDMA<br>frame.  Both functions are thread-safe (queue mutex).<br><br>In a multi-trx configuration, the use of queue additionally ensures<br>proper burst aggregation on multiple TRXD connections, so all L12TRX<br>messages are guaranteed to be sent in the right order, i.e. with<br>monolithically-increasing TDMA frame numbers.<br><br>Of course, this change increases the overall CPU usage, given that<br>each transceiver gets its own queue, and we need to serve them all<br>on every TDMA frame.  According to my measurements, when running<br>test cases from ttcn3-bts-test, the average load is ~50% higher<br>than what it used to be.  Still not significantly high, though.<br><br>Change-Id: Ie66ef9667dc8d156ad578ce324941a816c07c105<br>Related: OS#4658, OS#4546<br>---<br>M src/target/trx_toolkit/fake_trx.py<br>M src/target/trx_toolkit/transceiver.py<br>2 files changed, 75 insertions(+), 4 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/target/trx_toolkit/fake_trx.py b/src/target/trx_toolkit/fake_trx.py</span><br><span>index 27c2b88..b487a93 100755</span><br><span>--- a/src/target/trx_toolkit/fake_trx.py</span><br><span>+++ b/src/target/trx_toolkit/fake_trx.py</span><br><span>@@ -388,6 +388,8 @@</span><br><span> </span><br><span>                 # Init shared clock generator</span><br><span>                self.clck_gen = CLCKGen([])</span><br><span style="color: hsl(120, 100%, 40%);">+           # This method will be called on each TDMA frame</span><br><span style="color: hsl(120, 100%, 40%);">+               self.clck_gen.clck_handler = self.clck_handler</span><br><span> </span><br><span>           # Power measurement emulation</span><br><span>                # Noise: -120 .. -105</span><br><span>@@ -456,14 +458,18 @@</span><br><span>                        for trx in self.trx_list.trx_list:</span><br><span>                           # DATA interface</span><br><span>                             if trx.data_if.sock in r_event:</span><br><span style="color: hsl(0, 100%, 40%);">-                                 msg = trx.recv_data_msg()</span><br><span style="color: hsl(0, 100%, 40%);">-                                       if msg is not None:</span><br><span style="color: hsl(0, 100%, 40%);">-                                             self.burst_fwd.forward_msg(trx, msg)</span><br><span style="color: hsl(120, 100%, 40%);">+                                  trx.recv_data_msg()</span><br><span> </span><br><span>                              # CTRL interface</span><br><span>                             if trx.ctrl_if.sock in r_event:</span><br><span>                                      trx.ctrl_if.handle_rx()</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+   # This method will be called by the clock thread</span><br><span style="color: hsl(120, 100%, 40%);">+      def clck_handler(self, fn):</span><br><span style="color: hsl(120, 100%, 40%);">+           # We assume that this list is immutable at run-time</span><br><span style="color: hsl(120, 100%, 40%);">+           for trx in self.trx_list.trx_list:</span><br><span style="color: hsl(120, 100%, 40%);">+                    trx.clck_tick(self.burst_fwd, fn)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  def shutdown(self):</span><br><span>          log.info("Shutting down...")</span><br><span> </span><br><span>diff --git a/src/target/trx_toolkit/transceiver.py b/src/target/trx_toolkit/transceiver.py</span><br><span>index 474834d..3c1ddc8 100644</span><br><span>--- a/src/target/trx_toolkit/transceiver.py</span><br><span>+++ b/src/target/trx_toolkit/transceiver.py</span><br><span>@@ -24,6 +24,7 @@</span><br><span> # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span><br><span> </span><br><span> import logging as log</span><br><span style="color: hsl(120, 100%, 40%);">+import threading</span><br><span> </span><br><span> from ctrl_if_trx import CTRLInterfaceTRX</span><br><span> from data_if import DATAInterface</span><br><span>@@ -123,6 +124,37 @@</span><br><span>  Transceiver and all its timeslots, so using in for the BTS side</span><br><span>      does not make any sense (imagine BCCH hopping together with DCCH).</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+        == The transmit burst queue</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ According to 3GPP 45.002, the time difference between Uplink and</span><br><span style="color: hsl(120, 100%, 40%);">+      Downlink corresponds to three TDMA timeslot periods.  However,</span><br><span style="color: hsl(120, 100%, 40%);">+        in general the L1 implementations (such as osmo-bts-trx and trxcon)</span><br><span style="color: hsl(120, 100%, 40%);">+   never schedule to be transmitted bursts for the current TDMA frame</span><br><span style="color: hsl(120, 100%, 40%);">+    immediately.  Instead, they are being scheduled prematurely.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        The rationale is that both transceiver and the L1 implementation</span><br><span style="color: hsl(120, 100%, 40%);">+      are separete processes that are not perfectly synchronized in time.</span><br><span style="color: hsl(120, 100%, 40%);">+   Moreover, the transceiver needs some time to prepare a burst for</span><br><span style="color: hsl(120, 100%, 40%);">+      transmission.  This is why the time difference between Uplink and</span><br><span style="color: hsl(120, 100%, 40%);">+     Downlink is actually much higher on practice (20 TDMA frame periods</span><br><span style="color: hsl(120, 100%, 40%);">+   by default, at the moment of writing this patch).</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   In order to reflect that delay in a virtual environment, this</span><br><span style="color: hsl(120, 100%, 40%);">+ implementation, just like a normal transceiver (e.g. osmo-trx),</span><br><span style="color: hsl(120, 100%, 40%);">+       queues all to be transmitted (L12TRX) bursts, so hey remain in</span><br><span style="color: hsl(120, 100%, 40%);">+        the transmit queue until the appropriate time of transmission.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      The API user is supposed to call recv_data_msg() in order to obtain</span><br><span style="color: hsl(120, 100%, 40%);">+   a L12TRX message on the TRXD (data) inteface, so it gets queued by</span><br><span style="color: hsl(120, 100%, 40%);">+    this function.  Then, to ensure the timeous transmission, the user</span><br><span style="color: hsl(120, 100%, 40%);">+    of this implementation needs to call clck_tick() on each TDMA</span><br><span style="color: hsl(120, 100%, 40%);">+ frame.  Both functions are thread-safe (queue mutex).</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       In a multi-trx configuration, the use of queue additionally ensures</span><br><span style="color: hsl(120, 100%, 40%);">+   proper burst aggregation on multiple TRXD connections, so all L12TRX</span><br><span style="color: hsl(120, 100%, 40%);">+  messages are guaranteed to be sent in the right order, i.e. with</span><br><span style="color: hsl(120, 100%, 40%);">+      monolithically-increasing TDMA frame numbers.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>      """</span><br><span> </span><br><span>       def __init__(self, bind_addr, remote_addr, base_port, name = None,</span><br><span>@@ -178,6 +210,10 @@</span><br><span>            # List of child transceivers</span><br><span>                 self.child_trx_list = TRXList()</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+           # Tx (L12TRX) burst queue and mutex</span><br><span style="color: hsl(120, 100%, 40%);">+           self._tx_queue_lock = threading.Lock()</span><br><span style="color: hsl(120, 100%, 40%);">+                self._tx_queue = []</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>        def __str__(self):</span><br><span>           desc = "%s:%d" % (self.remote_addr, self.base_port)</span><br><span>                if self.child_idx > 0:</span><br><span>@@ -234,10 +270,12 @@</span><br><span>                            trx.running = True</span><br><span>                   elif event == "POWEROFF":</span><br><span>                          trx.running = False</span><br><span style="color: hsl(120, 100%, 40%);">+                           trx.tx_queue_clear()</span><br><span>                                 trx.disable_fh()</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-            # Reset frequency hopping parameters</span><br><span style="color: hsl(120, 100%, 40%);">+          # Reset frequency hopping parameters, clear the queue</span><br><span>                if event == "POWEROFF":</span><br><span style="color: hsl(120, 100%, 40%);">+                     self.tx_queue_clear()</span><br><span>                        self.disable_fh()</span><br><span> </span><br><span>                # Trigger clock generator if required</span><br><span>@@ -275,8 +313,35 @@</span><br><span>                                 "configured => dropping..." % (self, msg.desc_hdr()))</span><br><span>                   return None</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+               # Enque the message, it will be sent later</span><br><span style="color: hsl(120, 100%, 40%);">+            self.tx_queue_append(msg)</span><br><span>            return msg</span><br><span> </span><br><span>       def handle_data_msg(self, msg):</span><br><span>              # TODO: make legacy mode configurable (via argv?)</span><br><span>            self.data_if.send_msg(msg, legacy = True)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   def tx_queue_append(self, msg):</span><br><span style="color: hsl(120, 100%, 40%);">+               with self._tx_queue_lock:</span><br><span style="color: hsl(120, 100%, 40%);">+                     self._tx_queue.append(msg)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  def tx_queue_clear(self):</span><br><span style="color: hsl(120, 100%, 40%);">+             with self._tx_queue_lock:</span><br><span style="color: hsl(120, 100%, 40%);">+                     # TODO: Python3: self._tx_queue.clear()</span><br><span style="color: hsl(120, 100%, 40%);">+                       del self._tx_queue[:]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       def clck_tick(self, fwd, fn):</span><br><span style="color: hsl(120, 100%, 40%);">+         if not self.running:</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%);">+              self._tx_queue_lock.acquire()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               for msg in self._tx_queue:</span><br><span style="color: hsl(120, 100%, 40%);">+                    if msg.fn == fn:</span><br><span style="color: hsl(120, 100%, 40%);">+                              fwd.forward_msg(self, msg)</span><br><span style="color: hsl(120, 100%, 40%);">+                    elif msg.fn < fn:</span><br><span style="color: hsl(120, 100%, 40%);">+                          log.warning("(%s) Stale TRXD message (fn=%u): %s"</span><br><span style="color: hsl(120, 100%, 40%);">+                                   % (self, fn, msg.desc_hdr()))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               self._tx_queue = [msg for msg in self._tx_queue if msg.fn > fn]</span><br><span style="color: hsl(120, 100%, 40%);">+            self._tx_queue_lock.release()</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmocom-bb/+/19228">change 19228</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/osmocom-bb/+/19228"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmocom-bb </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Ie66ef9667dc8d156ad578ce324941a816c07c105 </div>
<div style="display:none"> Gerrit-Change-Number: 19228 </div>
<div style="display:none"> Gerrit-PatchSet: 3 </div>
<div style="display:none"> Gerrit-Owner: fixeria <vyanitskiy@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </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: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>