From d39b8410590f7183c78acb6ded8f6e7257257681 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 8 Jul 2020 19:11:58 +0700 Subject: trxcon/scheduler: check TDMA frame order, drop out of order bursts When running together with fake_trx.py (mostly used back-end), it is currently possible that Downlink bursts are received in a wrong order if more than one transceiver is configured (multi-trx mode). This is how it looks like: DTRXD DEBUG trx_if.c:612 RX burst tn=3 fn=629 rssi=-86 toa=0 DSCHD DEBUG sched_lchan_tchf.c:60 Traffic received on TCH/F: fn=629 ts=3 bid=1 DTRXD DEBUG trx_if.c:612 RX burst tn=3 fn=630 rssi=-86 toa=0 DSCHD DEBUG sched_lchan_tchf.c:60 Traffic received on TCH/F: fn=630 ts=3 bid=2 DTRXD DEBUG trx_if.c:612 RX burst tn=3 fn=631 rssi=-86 toa=0 DSCHD DEBUG sched_lchan_tchf.c:60 Traffic received on TCH/F: fn=631 ts=3 bid=3 DTRXD DEBUG trx_if.c:612 RX burst tn=3 fn=633 (!) rssi=-86 toa=0 DSCHD NOTICE sched_trx.c:663 Substituting (!) lost TDMA frame 632 on TCH/F DSCHD DEBUG sched_lchan_tchf.c:60 Traffic received on TCH/F: fn=632 ts=3 bid=0 DSCHD DEBUG sched_lchan_tchf.c:60 Traffic received on TCH/F: fn=633 ts=3 bid=1 DTRXD DEBUG trx_if.c:612 RX burst tn=3 fn=632 (!) rssi=-86 toa=0 DTRXD NOTICE sched_trx.c:640 Too many (>104) contiguous TDMA frames elapsed (2715647) since the last processed fn=633 (current fn=632) so here a burst with TDMA fn=633 was received earlier than a burst with TDMA fn=632. The burst loss detection logic considered the latter one as lost, and substituted it with a dummy burst. When finally the out-of-order burst with TDMA fn=632 was received, we got the large number of allegedly elapsed frames: ((632 + 2715648) - 633) % 2715648 == 2715647 Given that late bursts get substituted, the best thing we can do is to reject them and log an error. Passing them to the logical channel handler (again) might lead to undefined behaviour. Change-Id: I873c8555ea2ca190b1689227bb0fdcba87188772 Related: OS#4658, OS#4546 --- src/host/trxcon/sched_trx.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 9195b336..0025e0cc 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -625,7 +625,7 @@ static int subst_frame_loss(struct trx_lchan_state *lchan, { const struct trx_multiframe *mf; const struct trx_frame *fp; - unsigned int elapsed, i; + int elapsed, i; /* Wait until at least one TDMA frame is processed */ if (lchan->tdma.num_proc == 0) @@ -634,10 +634,28 @@ static int subst_frame_loss(struct trx_lchan_state *lchan, /* Short alias for the current multiframe */ mf = lchan->ts->mf_layout; - /* How many frames elapsed since the last one? */ - elapsed = GSM_TDMA_FN_SUB(fn, lchan->tdma.last_proc); + /* Calculate how many frames elapsed since the last received one. + * The algorithm is based on GSM::FNDelta() from osmo-trx. */ + elapsed = fn - lchan->tdma.last_proc; + if (elapsed >= GSM_TDMA_HYPERFRAME / 2) + elapsed -= GSM_TDMA_HYPERFRAME; + else if (elapsed < -GSM_TDMA_HYPERFRAME / 2) + elapsed += GSM_TDMA_HYPERFRAME; + + /* Check TDMA frame order (wrong order is possible with fake_trx.py, see OS#4658) */ + if (elapsed < 0) { + /* This burst has already been substituted by a dummy burst (all bits set to zero), + * so better drop it. Otherwise we risk to get undefined behavior in handler(). */ + LOGP(DSCHD, LOGL_ERROR, "(%s) Rx burst with fn=%u older than the last " + "processed fn=%u (see OS#4658) => dropping\n", + trx_lchan_desc[lchan->type].name, + fn, lchan->tdma.last_proc); + return -EALREADY; + } + + /* Check how many frames we (potentially) need to compensate */ if (elapsed > mf->period) { - LOGP(DSCHD, LOGL_NOTICE, "Too many (>%u) contiguous TDMA frames elapsed (%u) " + LOGP(DSCHD, LOGL_NOTICE, "Too many (>%u) contiguous TDMA frames elapsed (%d) " "since the last processed fn=%u (current %u)\n", mf->period, elapsed, lchan->tdma.last_proc, fn); return -EIO; @@ -687,6 +705,7 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, trx_lchan_rx_func *handler; enum trx_lchan_type chan; uint8_t offset, bid; + int rc; /* Check whether required timeslot is allocated and configured */ ts = trx->ts_list[tn]; @@ -720,7 +739,9 @@ int sched_trx_handle_rx_burst(struct trx_instance *trx, uint8_t tn, return 0; /* Compensate lost TDMA frames (if any) */ - subst_frame_loss(lchan, handler, fn); + rc = subst_frame_loss(lchan, handler, fn); + if (rc == -EALREADY) + return rc; /* Perform A5/X decryption if required */ if (lchan->a5.algo) -- cgit v1.2.3