<p>Harald Welte <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/12085">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Harald Welte: Looks good to me, approved
  Jenkins Builder: Verified

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">SigProcLib: Improve Vector buffer allocation mess<br><br>Original issue: In order to use SSE instructions, 16-byte aligned memory<br>chunks are needed, and C++ version < C++11 doesn't provide for a native<br>new/delete store. For that reason, memalign() must be used in the<br>implementation of convolve_h_alloc() for some buffers.<br>On the other side, The C++ code relies on C++ "new T[]" operator to<br>allocate a chunk of memory containing an array of class instances. As<br>classes are complex types, they cannot be allocated through C structures<br>(calling malloc). Experimentally can be seen too that it's unreliable<br>and the process will crash during startup if malloc() is used and then a<br>Complex<> deferred from it.<br><br>Previous implementation allowed for use of convolve_h_alloc or new[]<br>based on how the (signal)Vector is called, because then the buffer is<br>not going to be managed internally. But that's unreliable since resize()<br>calling resize() on it could use "delete" operator on a malloc'ed<br>buffer, and end up having a new new[] allocated buffer. It was also<br>found that some of the callers were actually leaking memory through ASan (because the<br>buffer is not managed by the Vector instance).<br><br>IMHO best option would be to rewrite all this code using C structures<br>and malloc/free exclusively, since it would make all this cod eeasier to<br>maintain.<br><br>But for now, let's extend the Vector class to allow specifying an<br>external alloc/free function and let the Vector instance take care of<br>the ownership of the buffer in all scenarios.<br><br>Change-Id: Ie484a4762a7f77fe1b105188ea03a6f025730b82<br>---<br>M CommonLibs/Vector.h<br>M Transceiver52M/arch/common/convolve.h<br>M Transceiver52M/arch/common/convolve_base.c<br>M Transceiver52M/sigProcLib.cpp<br>M Transceiver52M/signalVector.cpp<br>M Transceiver52M/signalVector.h<br>6 files changed, 52 insertions(+), 47 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/CommonLibs/Vector.h b/CommonLibs/Vector.h</span><br><span>index 9119683..4c96b78 100644</span><br><span>--- a/CommonLibs/Vector.h</span><br><span>+++ b/CommonLibs/Vector.h</span><br><span>@@ -32,11 +32,14 @@</span><br><span> #include <string.h></span><br><span> #include <iostream></span><br><span> #include <assert.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdlib.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> // We cant use Logger.h in this file...</span><br><span> extern int gVectorDebug;</span><br><span> #define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;}</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+typedef void (*vector_free_func)(void* wData);</span><br><span style="color: hsl(120, 100%, 40%);">+typedef void *(*vector_alloc_func)(size_t newSize);</span><br><span> </span><br><span> /**</span><br><span>       A simplified Vector template with aliases.</span><br><span>@@ -60,6 +63,8 @@</span><br><span>       T* mData;               ///< allocated data block, if any</span><br><span>         T* mStart;              ///< start of useful data</span><br><span>         T* mEnd;                ///< end of useful data + 1</span><br><span style="color: hsl(120, 100%, 40%);">+        vector_alloc_func mAllocFunc; ///< function used to alloc new mData during resize.</span><br><span style="color: hsl(120, 100%, 40%);">+ vector_free_func mFreeFunc; ///< function used to free mData.</span><br><span> </span><br><span>         public:</span><br><span> </span><br><span>@@ -85,9 +90,19 @@</span><br><span>     /** Change the size of the Vector, discarding content. */</span><br><span>    void resize(size_t newSize)</span><br><span>  {</span><br><span style="color: hsl(0, 100%, 40%);">-               if (mData!=NULL) delete[] mData;</span><br><span style="color: hsl(120, 100%, 40%);">+              if (mData!=NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (mFreeFunc)</span><br><span style="color: hsl(120, 100%, 40%);">+                                mFreeFunc(mData);</span><br><span style="color: hsl(120, 100%, 40%);">+                     else</span><br><span style="color: hsl(120, 100%, 40%);">+                          delete[] mData;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span>            if (newSize==0) mData=NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-             else mData = new T[newSize];</span><br><span style="color: hsl(120, 100%, 40%);">+          else {</span><br><span style="color: hsl(120, 100%, 40%);">+                        if (mAllocFunc)</span><br><span style="color: hsl(120, 100%, 40%);">+                               mData = (T*) mAllocFunc(newSize);</span><br><span style="color: hsl(120, 100%, 40%);">+                     else</span><br><span style="color: hsl(120, 100%, 40%);">+                          mData = new T[newSize];</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span>            mStart = mData;</span><br><span>              mEnd = mStart + newSize;</span><br><span>     }</span><br><span>@@ -116,29 +131,31 @@</span><br><span>    //@{</span><br><span> </span><br><span>     /** Build an empty Vector of a given size. */</span><br><span style="color: hsl(0, 100%, 40%);">-   Vector(size_t wSize=0):mData(NULL) { resize(wSize); }</span><br><span style="color: hsl(120, 100%, 40%);">+ Vector(size_t wSize=0, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+            :mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)</span><br><span style="color: hsl(120, 100%, 40%);">+    { resize(wSize); }</span><br><span> </span><br><span>       /** Build a Vector by moving another. */</span><br><span>     Vector(Vector<T>&& other)</span><br><span style="color: hsl(0, 100%, 40%);">-         :mData(other.mData),mStart(other.mStart),mEnd(other.mEnd)</span><br><span style="color: hsl(120, 100%, 40%);">+             :mData(other.mData),mStart(other.mStart),mEnd(other.mEnd), mAllocFunc(other.mAllocFunc),  mFreeFunc(other.mFreeFunc)</span><br><span>         { other.mData=NULL; }</span><br><span> </span><br><span>    /** Build a Vector by copying another. */</span><br><span style="color: hsl(0, 100%, 40%);">-       Vector(const Vector<T>& other):mData(NULL) { clone(other); }</span><br><span style="color: hsl(120, 100%, 40%);">+        Vector(const Vector<T>& other):mData(NULL), mAllocFunc(other.mAllocFunc), mFreeFunc(other.mFreeFunc) { clone(other); }</span><br><span> </span><br><span>         /** Build a Vector with explicit values. */</span><br><span style="color: hsl(0, 100%, 40%);">-     Vector(T* wData, T* wStart, T* wEnd)</span><br><span style="color: hsl(0, 100%, 40%);">-            :mData(wData),mStart(wStart),mEnd(wEnd)</span><br><span style="color: hsl(120, 100%, 40%);">+       Vector(T* wData, T* wStart, T* wEnd, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+              :mData(wData),mStart(wStart),mEnd(wEnd), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)</span><br><span>        { }</span><br><span> </span><br><span>      /** Build a vector from an existing block, NOT to be deleted upon destruction. */</span><br><span style="color: hsl(0, 100%, 40%);">-       Vector(T* wStart, size_t span)</span><br><span style="color: hsl(0, 100%, 40%);">-          :mData(NULL),mStart(wStart),mEnd(wStart+span)</span><br><span style="color: hsl(120, 100%, 40%);">+ Vector(T* wStart, size_t span, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+            :mData(NULL),mStart(wStart),mEnd(wStart+span),mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)</span><br><span>   { }</span><br><span> </span><br><span>      /** Build a Vector by concatenation. */</span><br><span style="color: hsl(0, 100%, 40%);">- Vector(const Vector<T>& other1, const Vector<T>& other2)</span><br><span style="color: hsl(0, 100%, 40%);">-            :mData(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+  Vector(const Vector<T>& other1, const Vector<T>& other2, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+              :mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc)</span><br><span>   {</span><br><span>            resize(other1.size()+other2.size());</span><br><span>                 memcpy(mStart, other1.mStart, other1.bytes());</span><br><span>@@ -162,6 +179,8 @@</span><br><span>                 mData=other.mData;</span><br><span>           mStart=other.mStart;</span><br><span>                 mEnd=other.mEnd;</span><br><span style="color: hsl(120, 100%, 40%);">+              mAllocFunc=other.mAllocFunc;</span><br><span style="color: hsl(120, 100%, 40%);">+          mFreeFunc=other.mFreeFunc;</span><br><span>           other.mData=NULL;</span><br><span>    }</span><br><span> </span><br><span>diff --git a/Transceiver52M/arch/common/convolve.h b/Transceiver52M/arch/common/convolve.h</span><br><span>index 43db577..095b04c 100644</span><br><span>--- a/Transceiver52M/arch/common/convolve.h</span><br><span>+++ b/Transceiver52M/arch/common/convolve.h</span><br><span>@@ -1,7 +1,7 @@</span><br><span> #ifndef _CONVOLVE_H_</span><br><span> #define _CONVOLVE_H_</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-void *convolve_h_alloc(int num);</span><br><span style="color: hsl(120, 100%, 40%);">+void *convolve_h_alloc(size_t num);</span><br><span> </span><br><span> int convolve_real(const float *x, int x_len,</span><br><span>                  const float *h, int h_len,</span><br><span>diff --git a/Transceiver52M/arch/common/convolve_base.c b/Transceiver52M/arch/common/convolve_base.c</span><br><span>index 71453a1..2eb7124 100644</span><br><span>--- a/Transceiver52M/arch/common/convolve_base.c</span><br><span>+++ b/Transceiver52M/arch/common/convolve_base.c</span><br><span>@@ -146,7 +146,7 @@</span><br><span> }</span><br><span> </span><br><span> /* Aligned filter tap allocation */</span><br><span style="color: hsl(0, 100%, 40%);">-void *convolve_h_alloc(int len)</span><br><span style="color: hsl(120, 100%, 40%);">+void *convolve_h_alloc(size_t len)</span><br><span> {</span><br><span> #ifdef HAVE_SSE3</span><br><span>  return memalign(16, len * 2 * sizeof(float));</span><br><span>diff --git a/Transceiver52M/sigProcLib.cpp b/Transceiver52M/sigProcLib.cpp</span><br><span>index 28c4ded..f720828 100644</span><br><span>--- a/Transceiver52M/sigProcLib.cpp</span><br><span>+++ b/Transceiver52M/sigProcLib.cpp</span><br><span>@@ -84,14 +84,13 @@</span><br><span>  * perform 16-byte memory alignment required by many SSE instructions.</span><br><span>  */</span><br><span> struct CorrelationSequence {</span><br><span style="color: hsl(0, 100%, 40%);">-  CorrelationSequence() : sequence(NULL), buffer(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+  CorrelationSequence() : sequence(NULL)</span><br><span>   {</span><br><span>   }</span><br><span> </span><br><span>   ~CorrelationSequence()</span><br><span>   {</span><br><span>     delete sequence;</span><br><span style="color: hsl(0, 100%, 40%);">-    free(buffer);</span><br><span>   }</span><br><span> </span><br><span>   signalVector *sequence;</span><br><span>@@ -106,8 +105,7 @@</span><br><span>  * for SSE instructions.</span><br><span>  */</span><br><span> struct PulseSequence {</span><br><span style="color: hsl(0, 100%, 40%);">-  PulseSequence() : c0(NULL), c1(NULL), c0_inv(NULL), empty(NULL),</span><br><span style="color: hsl(0, 100%, 40%);">-                    c0_buffer(NULL), c1_buffer(NULL), c0_inv_buffer(NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+  PulseSequence() : c0(NULL), c1(NULL), c0_inv(NULL), empty(NULL)</span><br><span>   {</span><br><span>   }</span><br><span> </span><br><span>@@ -117,17 +115,12 @@</span><br><span>     delete c1;</span><br><span>     delete c0_inv;</span><br><span>     delete empty;</span><br><span style="color: hsl(0, 100%, 40%);">-    free(c0_buffer);</span><br><span style="color: hsl(0, 100%, 40%);">-    free(c1_buffer);</span><br><span>   }</span><br><span> </span><br><span>   signalVector *c0;</span><br><span>   signalVector *c1;</span><br><span>   signalVector *c0_inv;</span><br><span>   signalVector *empty;</span><br><span style="color: hsl(0, 100%, 40%);">-  void *c0_buffer;</span><br><span style="color: hsl(0, 100%, 40%);">-  void *c1_buffer;</span><br><span style="color: hsl(0, 100%, 40%);">-  void *c0_inv_buffer;</span><br><span> };</span><br><span> </span><br><span> static CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};</span><br><span>@@ -340,7 +333,7 @@</span><br><span>   if (y && (len > y->size()))</span><br><span>     return NULL;</span><br><span>   if (!y) {</span><br><span style="color: hsl(0, 100%, 40%);">-    y = new signalVector(len);</span><br><span style="color: hsl(120, 100%, 40%);">+    y = new signalVector(len, convolve_h_alloc, free);</span><br><span>     alloc = true;</span><br><span>   }</span><br><span> </span><br><span>@@ -403,8 +396,7 @@</span><br><span>   if (!pulse)</span><br><span>     return false;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  pulse->c0_inv_buffer = convolve_h_alloc(5);</span><br><span style="color: hsl(0, 100%, 40%);">-  pulse->c0_inv = new signalVector((complex *) pulse->c0_inv_buffer, 0, 5);</span><br><span style="color: hsl(120, 100%, 40%);">+  pulse->c0_inv = new signalVector((complex *) convolve_h_alloc(5), 0, 5, convolve_h_alloc, free);</span><br><span>   pulse->c0_inv->isReal(true);</span><br><span>   pulse->c0_inv->setAligned(false);</span><br><span> </span><br><span>@@ -433,9 +425,7 @@</span><br><span>     return false;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  pulse->c1_buffer = convolve_h_alloc(len);</span><br><span style="color: hsl(0, 100%, 40%);">-  pulse->c1 = new signalVector((complex *)</span><br><span style="color: hsl(0, 100%, 40%);">-                                  pulse->c1_buffer, 0, len);</span><br><span style="color: hsl(120, 100%, 40%);">+  pulse->c1 = new signalVector((complex *) convolve_h_alloc(len), 0, len, convolve_h_alloc, free);</span><br><span>   pulse->c1->isReal(true);</span><br><span> </span><br><span>   /* Enable alignment for SSE usage */</span><br><span>@@ -489,8 +479,7 @@</span><br><span>     len = 4;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  pulse->c0_buffer = convolve_h_alloc(len);</span><br><span style="color: hsl(0, 100%, 40%);">-  pulse->c0 = new signalVector((complex *) pulse->c0_buffer, 0, len);</span><br><span style="color: hsl(120, 100%, 40%);">+  pulse->c0 = new signalVector((complex *) convolve_h_alloc(len), 0, len, convolve_h_alloc, free);</span><br><span>   pulse->c0->isReal(true);</span><br><span> </span><br><span>   /* Enable alingnment for SSE usage */</span><br><span>@@ -1019,7 +1008,7 @@</span><br><span> </span><br><span>   for (int i = 0; i < DELAYFILTS; i++) {</span><br><span>     data = (complex *) convolve_h_alloc(h_len);</span><br><span style="color: hsl(0, 100%, 40%);">-    h = new signalVector(data, 0, h_len);</span><br><span style="color: hsl(120, 100%, 40%);">+    h = new signalVector(data, 0, h_len, convolve_h_alloc, free);</span><br><span>     h->setAligned(true);</span><br><span>     h->isReal(true);</span><br><span> </span><br><span>@@ -1263,7 +1252,7 @@</span><br><span> </span><br><span>   /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */</span><br><span>   data = (complex *) convolve_h_alloc(midMidamble->size());</span><br><span style="color: hsl(0, 100%, 40%);">-  _midMidamble = new signalVector(data, 0, midMidamble->size());</span><br><span style="color: hsl(120, 100%, 40%);">+  _midMidamble = new signalVector(data, 0, midMidamble->size(), convolve_h_alloc, free);</span><br><span>   _midMidamble->setAligned(true);</span><br><span>   midMidamble->copyTo(*_midMidamble);</span><br><span> </span><br><span>@@ -1274,7 +1263,6 @@</span><br><span>   }</span><br><span> </span><br><span>   gMidambles[tsc] = new CorrelationSequence;</span><br><span style="color: hsl(0, 100%, 40%);">-  gMidambles[tsc]->buffer = data;</span><br><span>   gMidambles[tsc]->sequence = _midMidamble;</span><br><span>   gMidambles[tsc]->gain = peakDetect(*autocorr, &toa, NULL);</span><br><span> </span><br><span>@@ -1319,13 +1307,12 @@</span><br><span>   conjugateVector(*midamble);</span><br><span> </span><br><span>   data = (complex *) convolve_h_alloc(midamble->size());</span><br><span style="color: hsl(0, 100%, 40%);">-  _midamble = new signalVector(data, 0, midamble->size());</span><br><span style="color: hsl(120, 100%, 40%);">+  _midamble = new signalVector(data, 0, midamble->size(), convolve_h_alloc, free);</span><br><span>   _midamble->setAligned(true);</span><br><span>   midamble->copyTo(*_midamble);</span><br><span> </span><br><span>   /* Channel gain is an empirically measured value */</span><br><span>   seq = new CorrelationSequence;</span><br><span style="color: hsl(0, 100%, 40%);">-  seq->buffer = data;</span><br><span>   seq->sequence = _midamble;</span><br><span>   seq->gain = Complex<float>(-19.6432, 19.5006) / 1.18;</span><br><span>   seq->toa = 0;</span><br><span>@@ -1360,7 +1347,7 @@</span><br><span> </span><br><span>   /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */</span><br><span>   data = (complex *) convolve_h_alloc(seq1->size());</span><br><span style="color: hsl(0, 100%, 40%);">-  _seq1 = new signalVector(data, 0, seq1->size());</span><br><span style="color: hsl(120, 100%, 40%);">+  _seq1 = new signalVector(data, 0, seq1->size(), convolve_h_alloc, free);</span><br><span>   _seq1->setAligned(true);</span><br><span>   seq1->copyTo(*_seq1);</span><br><span> </span><br><span>@@ -1372,7 +1359,6 @@</span><br><span> </span><br><span>   *seq = new CorrelationSequence;</span><br><span>   (*seq)->sequence = _seq1;</span><br><span style="color: hsl(0, 100%, 40%);">-  (*seq)->buffer = data;</span><br><span>   (*seq)->gain = peakDetect(*autocorr, &toa, NULL);</span><br><span> </span><br><span>   /* For 1 sps only</span><br><span>diff --git a/Transceiver52M/signalVector.cpp b/Transceiver52M/signalVector.cpp</span><br><span>index fc8157e..710eda5 100644</span><br><span>--- a/Transceiver52M/signalVector.cpp</span><br><span>+++ b/Transceiver52M/signalVector.cpp</span><br><span>@@ -1,20 +1,20 @@</span><br><span> #include "signalVector.h"</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-signalVector::signalVector(size_t size)</span><br><span style="color: hsl(0, 100%, 40%);">-       : Vector<complex>(size),</span><br><span style="color: hsl(120, 100%, 40%);">+signalVector::signalVector(size_t size, vector_alloc_func wAllocFunc, vector_free_func wFreeFunc)</span><br><span style="color: hsl(120, 100%, 40%);">+     : Vector<complex>(size, wAllocFunc, wFreeFunc),</span><br><span>          real(false), aligned(false), symmetry(NONE)</span><br><span> {</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-signalVector::signalVector(size_t size, size_t start)</span><br><span style="color: hsl(0, 100%, 40%);">-        : Vector<complex>(size + start),</span><br><span style="color: hsl(120, 100%, 40%);">+signalVector::signalVector(size_t size, size_t start, vector_alloc_func wAllocFunc, vector_free_func wFreeFunc)</span><br><span style="color: hsl(120, 100%, 40%);">+       : Vector<complex>(size + start, wAllocFunc, wFreeFunc),</span><br><span>          real(false), aligned(false), symmetry(NONE)</span><br><span> {</span><br><span>   mStart = mData + start;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-signalVector::signalVector(complex *data, size_t start, size_t span)</span><br><span style="color: hsl(0, 100%, 40%);">-    : Vector<complex>(NULL, data + start, data + start + span),</span><br><span style="color: hsl(120, 100%, 40%);">+signalVector::signalVector(complex *data, size_t start, size_t span, vector_alloc_func wAllocFunc, vector_free_func wFreeFunc)</span><br><span style="color: hsl(120, 100%, 40%);">+     : Vector<complex>(data, data + start, data + start + span, wAllocFunc, wFreeFunc),</span><br><span>       real(false), aligned(false), symmetry(NONE)</span><br><span> {</span><br><span> }</span><br><span>diff --git a/Transceiver52M/signalVector.h b/Transceiver52M/signalVector.h</span><br><span>index 83f141e..d9486af 100644</span><br><span>--- a/Transceiver52M/signalVector.h</span><br><span>+++ b/Transceiver52M/signalVector.h</span><br><span>@@ -13,13 +13,13 @@</span><br><span> class signalVector: public Vector<complex> {</span><br><span> public:</span><br><span>      /** Default constructor */</span><br><span style="color: hsl(0, 100%, 40%);">-      signalVector(size_t size = 0);</span><br><span style="color: hsl(120, 100%, 40%);">+        signalVector(size_t size = 0, vector_alloc_func wAllocFunc = NULL, vector_free_func wFreeFunc = NULL);</span><br><span> </span><br><span>   /** Construct with head room */</span><br><span style="color: hsl(0, 100%, 40%);">- signalVector(size_t size, size_t start);</span><br><span style="color: hsl(120, 100%, 40%);">+      signalVector(size_t size, size_t start, vector_alloc_func wAllocFunc = NULL, vector_free_func wFreeFunc = NULL);</span><br><span> </span><br><span>         /** Construct from existing buffer data (buffer not managed) */</span><br><span style="color: hsl(0, 100%, 40%);">- signalVector(complex *data, size_t start, size_t span);</span><br><span style="color: hsl(120, 100%, 40%);">+       signalVector(complex *data, size_t start, size_t span, vector_alloc_func wAllocFunc = NULL, vector_free_func wFreeFunc = NULL);</span><br><span> </span><br><span>  /** Construct by from existing vector */</span><br><span>     signalVector(const signalVector &vector);</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/12085">change 12085</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/12085"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-trx </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: Ie484a4762a7f77fe1b105188ea03a6f025730b82 </div>
<div style="display:none"> Gerrit-Change-Number: 12085 </div>
<div style="display:none"> Gerrit-PatchSet: 3 </div>
<div style="display:none"> Gerrit-Owner: Pau Espin Pedrol <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Harald Welte <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder (1000002) </div>
<div style="display:none"> Gerrit-CC: Vadim Yanitskiy <axilirator@gmail.com> </div>