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]>