fixeria has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/osmo-trx/+/43116?usp=email )


Change subject: Transceiver52M: implement TRXDv2 support
......................................................................

Transceiver52M: implement TRXDv2 support

TRXDv2 was proposed and implemented in osmo-bts a while ago, but
osmo-trx was still stuck at TRXDv1.  This patch implements TRXDv2
and UL/DL burst batching using libosmo-trx API.

DL: driveTxPriorityQueue() now loops over osmo_trxd_burst_req_parse()
to handle datagrams potentially containing multiple batched BURST.req,
dispatching each via the new handleBurstReq().

UL: accumulate BURST.ind PDUs for the same FN into one msgb per channel
and flush as a single datagram once the next frame's PDU arrives or
the channel stops;  TRXDv0/v1 channels keep sending one PDU per
datagram via sendBurstInd().  Batching is unconditional for now;
making it configurable is left as a follow-up.

Change-Id: Ie6a33e3980b3ca4ab05a3d4221d5a8ba0132233d
Related: OS#5283
---
M Transceiver52M/Transceiver.cpp
M Transceiver52M/Transceiver.h
2 files changed, 172 insertions(+), 52 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-trx refs/changes/16/43116/1

diff --git a/Transceiver52M/Transceiver.cpp b/Transceiver52M/Transceiver.cpp
index 20a568a..2753c47 100644
--- a/Transceiver52M/Transceiver.cpp
+++ b/Transceiver52M/Transceiver.cpp
@@ -56,6 +56,13 @@
 /* Number of running values use in noise average */
 #define NOISE_CNT                      20

+/* Worst-case size of a batched TRXDv2 datagram (either direction): up to 8
+ * timeslots per frame, each sized as generously as a lone PDU
+ * (OSMO_TRXD_MSG_BUF_SIZE already covers header + max burst payload for one
+ * PDU; PDUs after the first omit the 4-byte FN, so this is a safe
+ * over-estimate, not a tight bound). */
+#define TRX_TRXD_BATCH_BUF_SIZE                (8 * OSMO_TRXD_MSG_BUF_SIZE)
+

 static void dispatch_trx_rate_ctr_change(TransceiverState *state, unsigned int 
chan) {
         thread_enable_cancel(false);
@@ -142,7 +149,9 @@
     mCtrlSockets(mChans), mClockSocket(-1),
     mTxPriorityQueues(mChans), mReceiveFIFO(mChans),
     mRxServiceLoopThreads(mChans), mRxLowerLoopThread(nullptr), 
mTxLowerLoopThread(nullptr),
-    mTxPriorityQueueServiceLoopThreads(mChans), 
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
+    mTxPriorityQueueServiceLoopThreads(mChans),
+    mBurstIndBatch(mChans), mBurstIndBatchFn(mChans),
+    mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
     mOn(false),mForceClockInterface(false), mTxFreq(0.0), mRxFreq(0.0), 
mTSC(0), mMaxExpectedDelayAB(0),
     mMaxExpectedDelayNB(0), mWriteBurstToDiskMask(0), mVersionTRXD(mChans), 
mStates(mChans)
 {
@@ -168,6 +177,7 @@
     mTxPriorityQueues[i].clear();
     if (mDataSockets[i] >= 0)
       close(mDataSockets[i]);
+    msgb_free(mBurstIndBatch[i]);
   }
 }

@@ -366,6 +376,9 @@
     delete mRxServiceLoopThreads[i];
     delete mTxPriorityQueueServiceLoopThreads[i];

+    /* Rx thread is joined, so no concurrent access to the batch anymore */
+    flushBurstIndBatch(i);
+
     mTxPriorityQueues[i].clear();
   }

@@ -1080,29 +1093,9 @@
   return 0;
 }

-bool Transceiver::driveTxPriorityQueue(size_t chan)
+bool Transceiver::handleBurstReq(size_t chan, const struct osmo_trxd_burst_req 
*br)
 {
-  char buffer[OSMO_TRXD_MSG_BUF_SIZE];
-  int msgLen;
-  struct osmo_trxd_parse_state st;
-  struct osmo_trxd_burst_req br;
-  int rc;
-
-  // check data socket
-  msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
-  if (msgLen <= 0) {
-    LOGCHAN(chan, DTRXDDL, NOTICE) << "mDataSockets read(" << 
mDataSockets[chan] << ") failed: " << msgLen;
-    return false;
-  }
-
-  osmo_trxd_parse_state_init(&st);
-  rc = osmo_trxd_burst_req_parse(&st, &br, (const uint8_t *) buffer, msgLen);
-  if (rc < 0) {
-    LOGCHAN(chan, DTRXDDL, ERROR) << "failed to parse BURST.req (rc=" << rc << 
")";
-    return false;
-  }
-
-  switch (br.burst_len) {
+  switch (br->burst_len) {
   case gSlotLen: /* GSM burst */
     break;
   case EDGE_BURST_NBITS: /* EDGE burst */
@@ -1113,19 +1106,19 @@
     break;
   default:
     LOGCHAN(chan, DTRXDDL, ERROR) << "badly formatted packet on GSM->TRX 
interface (burst_len="
-                                  << br.burst_len << ")";
+                                  << br->burst_len << ")";
     return false;
   }

-  LOGCHAN(chan, DTRXDDL, DEBUG) << "Rx TRXD message: fn=" << br.fn << ", tn=" 
<< unsigned(br.tn)
-                                << ", burst_len=" << br.burst_len;
+  LOGCHAN(chan, DTRXDDL, DEBUG) << "Rx TRXD message: fn=" << br->fn << ", tn=" 
<< unsigned(br->tn)
+                                << ", burst_len=" << br->burst_len;

   TransceiverState *state = &mStates[chan];
-  GSM::Time currTime = GSM::Time(br.fn, br.tn);
+  GSM::Time currTime = GSM::Time(br->fn, br->tn);

   /* Verify proper FN order in DL stream */
-  if (state->first_dl_fn_rcv[br.tn]) {
-    int32_t delta = GSM::FNDelta(currTime.FN(), 
state->last_dl_time_rcv[br.tn].FN());
+  if (state->first_dl_fn_rcv[br->tn]) {
+    int32_t delta = GSM::FNDelta(currTime.FN(), 
state->last_dl_time_rcv[br->tn].FN());
     if (delta == 1) {
         /* usual expected scenario, continue code flow */
     } else if (delta == 0) {
@@ -1135,7 +1128,7 @@
       return true;
     } else if (delta < 0) {
       LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with previous FN " << 
currTime
-                                     << " vs last " << 
state->last_dl_time_rcv[br.tn];
+                                     << " vs last " << 
state->last_dl_time_rcv[br->tn];
        state->ctrs.tx_trxd_fn_outoforder++;
        dispatch_trx_rate_ctr_change(state, chan);
        /* Allow adding radio vector below, since it gets sorted in the queue */
@@ -1145,25 +1138,66 @@
          * setups. Also, osmo-trx supports optionally filling empty bursts on
          * its own. In that case bts-trx is not obliged to submit all bursts. 
*/
       LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with future FN " << currTime
-                                     << " vs last " << 
state->last_dl_time_rcv[br.tn]
+                                     << " vs last " << 
state->last_dl_time_rcv[br->tn]
                                      << ", " << delta - 1 << " FN lost";
       state->ctrs.tx_trxd_fn_skipped += delta - 1;
       dispatch_trx_rate_ctr_change(state, chan);
     }
     if (delta > 0)
-      state->last_dl_time_rcv[br.tn] = currTime;
+      state->last_dl_time_rcv[br->tn] = currTime;
   } else { /* Initial check, simply store state */
-    state->first_dl_fn_rcv[br.tn] = true;
-    state->last_dl_time_rcv[br.tn] = currTime;
+    state->first_dl_fn_rcv[br->tn] = true;
+    state->last_dl_time_rcv[br->tn] = currTime;
   }

-  BitVector newBurst(br.burst_len);
+  BitVector newBurst(br->burst_len);
   BitVector::iterator itr = newBurst.begin();
-  const ubit_t *bufferItr = br.burst;
+  const ubit_t *bufferItr = br->burst;
   while (itr < newBurst.end())
     *itr++ = *bufferItr++;

-  addRadioVector(chan, newBurst, br.att, currTime);
+  addRadioVector(chan, newBurst, br->att, currTime);
+
+  return true;
+}
+
+bool Transceiver::driveTxPriorityQueue(size_t chan)
+{
+  char buffer[TRX_TRXD_BATCH_BUF_SIZE];
+  int msgLen;
+  struct osmo_trxd_parse_state st;
+  struct osmo_trxd_burst_req br;
+  const uint8_t *pos;
+  size_t remain;
+  int rc;
+
+  // check data socket
+  msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
+  if (msgLen <= 0) {
+    LOGCHAN(chan, DTRXDDL, NOTICE) << "mDataSockets read(" << 
mDataSockets[chan] << ") failed: " << msgLen;
+    return false;
+  }
+
+  /* Normally a single PDU per datagram (TRXDv0/v1, or unbatched TRXDv2),
+   * but the BTS side may also send us a TRXDv2 batch (multiple PDUs for
+   * the same FN, one per timeslot): keep parsing while BATCH.ind is set. */
+  osmo_trxd_parse_state_init(&st);
+  pos = (const uint8_t *) buffer;
+  remain = (size_t) msgLen;
+
+  do {
+    rc = osmo_trxd_burst_req_parse(&st, &br, pos, remain);
+    if (rc < 0) {
+      LOGCHAN(chan, DTRXDDL, ERROR) << "failed to parse BURST.req (rc=" << rc 
<< ")";
+      return false;
+    }
+
+    if (!handleBurstReq(chan, &br))
+      return false;
+
+    pos += rc;
+    remain -= rc;
+  } while (br.flags & OSMO_TRXD_F_BATCH_IND);

   return true;
 }
@@ -1213,28 +1247,17 @@
     << " bits: "   << os;
 }

-bool Transceiver::driveReceiveFIFO(size_t chan)
+
+bool Transceiver::sendBurstInd(size_t chan, const struct osmo_trxd_burst_ind 
*bi)
 {
-  struct osmo_trxd_burst_ind bi;
   struct msgb *msg;
   int rc;

-  if ((rc = pullRadioVector(chan, &bi)) < 0) {
-    if (rc == -ENOENT) { /* timeslot off, continue processing */
-      LOGCHAN(chan, DTRXDUL, DEBUG) << unsigned(bi.tn) << ":" << bi.fn << " 
timeslot is off";
-      return true;
-    }
-    return false; /* other errors: we want to stop the process */
-  }
-
-  if (!(bi.flags & OSMO_TRXD_F_NOPE_IND) && log_check_level(DTRXDUL, 
LOGL_DEBUG))
-    logRxBurst(chan, &bi);
-
   msg = msgb_alloc(OSMO_TRXD_MSG_BUF_SIZE, "trxd_burst_ind");
   if (!msg)
     return false;

-  rc = osmo_trxd_burst_ind_build(msg, mVersionTRXD[chan], &bi);
+  rc = osmo_trxd_burst_ind_build(msg, mVersionTRXD[chan], bi);
   if (rc < 0) {
     LOGCHAN(chan, DTRXDUL, ERROR) << "failed to build BURST.ind (rc=" << rc << 
")";
     msgb_free(msg);
@@ -1252,6 +1275,81 @@
   return true;
 }

+bool Transceiver::flushBurstIndBatch(size_t chan)
+{
+  struct msgb *msg = mBurstIndBatch[chan];
+  int rc;
+
+  if (!msg || msgb_length(msg) == 0)
+    return true; /* nothing accumulated */
+
+  osmo_trxd_build_fin(msg, mVersionTRXD[chan]);
+
+  rc = write(mDataSockets[chan], msgb_data(msg), msgb_length(msg));
+  msgb_trim(msg, 0);
+  if (rc < 0) {
+    LOGCHAN(chan, DTRXDUL, NOTICE) << "mDataSockets write(" << 
mDataSockets[chan] << ") failed: " << rc;
+    return false;
+  }
+
+  return true;
+}
+
+bool Transceiver::queueBurstIndBatched(size_t chan, const struct 
osmo_trxd_burst_ind *bi)
+{
+  struct msgb *msg = mBurstIndBatch[chan];
+  int rc;
+
+  if (msg && msgb_length(msg) > 0 && mBurstIndBatchFn[chan] != bi->fn) {
+    /* previous frame's batch is complete (a PDU for a new FN showed up) */
+    if (!flushBurstIndBatch(chan))
+      return false;
+  }
+
+  if (!msg) {
+    msg = msgb_alloc(TRX_TRXD_BATCH_BUF_SIZE, "trxd_burst_ind_batch");
+    if (!msg)
+      return false;
+    mBurstIndBatch[chan] = msg;
+  }
+
+  if (msgb_length(msg) == 0)
+    mBurstIndBatchFn[chan] = bi->fn;
+
+  rc = osmo_trxd_burst_ind_build(msg, mVersionTRXD[chan], bi);
+  if (rc < 0) {
+    LOGCHAN(chan, DTRXDUL, ERROR) << "failed to build batched BURST.ind (rc=" 
<< rc << ")";
+    msgb_trim(msg, 0);
+    return false;
+  }
+
+  return true;
+}
+
+bool Transceiver::driveReceiveFIFO(size_t chan)
+{
+  struct osmo_trxd_burst_ind bi;
+  int rc;
+
+  if ((rc = pullRadioVector(chan, &bi)) < 0) {
+    if (rc == -ENOENT) { /* timeslot off, continue processing */
+      LOGCHAN(chan, DTRXDUL, DEBUG) << unsigned(bi.tn) << ":" << bi.fn << " 
timeslot is off";
+      return true;
+    }
+    return false; /* other errors: we want to stop the process */
+  }
+
+  if (!(bi.flags & OSMO_TRXD_F_NOPE_IND) && log_check_level(DTRXDUL, 
LOGL_DEBUG))
+    logRxBurst(chan, &bi);
+
+  /* TODO: make batching configurable via VTY, so it can be disabled even
+   * when TRXDv2 is negotiated (e.g. to trade datagram count for latency) */
+  if (mVersionTRXD[chan] < 2)
+    return sendBurstInd(chan, &bi);
+
+  return queueBurstIndBatched(chan, &bi);
+}
+
 void Transceiver::driveTxFIFO()
 {

diff --git a/Transceiver52M/Transceiver.h b/Transceiver52M/Transceiver.h
index 8d27db9..e540c9f 100644
--- a/Transceiver52M/Transceiver.h
+++ b/Transceiver52M/Transceiver.h
@@ -44,7 +44,7 @@
 extern Transceiver *transceiver;

 /* The latest TRXD header format version advertised/accepted by this TRX 
implementation */
-#define TRX_DATA_FORMAT_VER    1
+#define TRX_DATA_FORMAT_VER    2

 /** Channel descriptor for transceiver object and channel number pair */
 struct TrxChanThParams {
@@ -186,6 +186,15 @@
   Thread *mTxLowerLoopThread;                   ///< thread to push bursts 
into transmit FIFO
   std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to 
process transmit bursts from GSM core

+  /* TRXDv2 uplink PDU batching (see osmo-gsm-manuals trx_if.adoc, combination
+   * "b"): all BURST.ind PDUs for one TDMA frame on a given channel are
+   * accumulated into a single datagram, flushed once the next frame's PDU
+   * arrives (i.e. keyed off a change in FN, not off tn==7, since not every
+   * timeslot is necessarily active). Batching across channels (TRXN) is not
+   * supported. Unused (kept NULL) for channels negotiated at TRXDv0/v1. */
+  std::vector<struct msgb *> mBurstIndBatch;
+  std::vector<uint32_t> mBurstIndBatchFn;
+
   GSM::Time mTransmitLatency;             ///< latency between basestation 
clock and transmit deadline clock
   GSM::Time mLatencyUpdateTime;           ///< last time latency was updated
   GSM::Time mTransmitDeadlineClock;       ///< deadline for pushing bursts 
into transmit FIFO
@@ -208,6 +217,19 @@
   /** Pull and demodulate a burst from the receive FIFO */
   int pullRadioVector(size_t chan, struct osmo_trxd_burst_ind *ind);

+  /** Send one BURST.ind PDU as its own datagram (TRXDv0/v1) */
+  bool sendBurstInd(size_t chan, const struct osmo_trxd_burst_ind *bi);
+
+  /** Append one BURST.ind PDU to the per-channel TRXDv2 batch, flushing the
+   *  previous frame's batch first if this PDU belongs to a new frame */
+  bool queueBurstIndBatched(size_t chan, const struct osmo_trxd_burst_ind *bi);
+
+  /** Write out and reset the accumulated TRXDv2 batch for a channel, if any */
+  bool flushBurstIndBatch(size_t chan);
+
+  /** Handle one parsed BURST.req PDU (one out of a batch, or a lone one) */
+  bool handleBurstReq(size_t chan, const struct osmo_trxd_burst_req *br);
+
   /** Set modulus for specific timeslot */
   void setModulus(size_t timeslot, size_t chan);


--
To view, visit https://gerrit.osmocom.org/c/osmo-trx/+/43116?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: osmo-trx
Gerrit-Branch: master
Gerrit-Change-Id: Ie6a33e3980b3ca4ab05a3d4221d5a8ba0132233d
Gerrit-Change-Number: 43116
Gerrit-PatchSet: 1
Gerrit-Owner: fixeria <[email protected]>

Reply via email to