fixeria has submitted this change. ( https://gerrit.osmocom.org/c/osmo-trx/+/43612?usp=email )
(
5 is the latest approved patch-set. No files were changed between the latest approved patch-set and the submitted one. )Change subject: proxy: queue BURST.req and dispatch per TDMA frame tick ......................................................................
proxy: queue BURST.req and dispatch per TDMA frame tick
Enqueue incoming BURST.req per endpoint instead of forwarding immediately, and drain the queue on each TDMA clock tick: forward entries whose fn is due, drop late ones, keep the rest waiting.
Change-Id: Ib147983e97638d2a2e487ccf76184912caf0941d Related: OS#6672 --- M proxy/Makefile.am A proxy/include/osmocom/proxy/burst_fwd.h M proxy/include/osmocom/proxy/path_sim.h M proxy/include/osmocom/proxy/trx.h M proxy/src/burst_fwd.c M proxy/src/clck_gen.c M proxy/src/path_sim.c M proxy/src/trx.c 8 files changed, 198 insertions(+), 11 deletions(-)
Approvals: Jenkins Builder: Verified fixeria: Looks good to me, approved laforge: Looks good to me, but someone else must approve
diff --git a/proxy/Makefile.am b/proxy/Makefile.am index d0eb6cf..8fcde9e 100644 --- a/proxy/Makefile.am +++ b/proxy/Makefile.am @@ -3,6 +3,7 @@ $(NULL)
noinst_HEADERS = \ + include/osmocom/proxy/burst_fwd.h \ include/osmocom/proxy/burst_synch.h \ include/osmocom/proxy/clck_gen.h \ include/osmocom/proxy/logging.h \ diff --git a/proxy/include/osmocom/proxy/burst_fwd.h b/proxy/include/osmocom/proxy/burst_fwd.h new file mode 100644 index 0000000..7ecddba --- /dev/null +++ b/proxy/include/osmocom/proxy/burst_fwd.h @@ -0,0 +1,5 @@ +#pragma once + +#include <stdint.h> + +void burst_fwd_dispatch(uint32_t fn); diff --git a/proxy/include/osmocom/proxy/path_sim.h b/proxy/include/osmocom/proxy/path_sim.h index 58bd9bb..25b68a0 100644 --- a/proxy/include/osmocom/proxy/path_sim.h +++ b/proxy/include/osmocom/proxy/path_sim.h @@ -33,6 +33,7 @@ const struct proxy_trx_chan *src, const struct path_sim_cfg *cfg); int path_sim_measure(uint32_t freq_hz, const struct path_sim_cfg *cfg); +void path_sim_fill_nope(struct osmo_trxd_burst_ind *bi, const struct path_sim_cfg *cfg);
struct path_sim_cfg *path_sim_cfg_alloc(void *talloc_ctx);
diff --git a/proxy/include/osmocom/proxy/trx.h b/proxy/include/osmocom/proxy/trx.h index 633c9fa..e3191d6 100644 --- a/proxy/include/osmocom/proxy/trx.h +++ b/proxy/include/osmocom/proxy/trx.h @@ -7,6 +7,7 @@ #include <osmocom/core/logging.h>
#include <osmocom/trx/trxc.h> +#include <osmocom/trx/trxd.h>
#include <osmocom/proxy/path_sim.h>
@@ -23,6 +24,12 @@ struct proxy_trx_ts { struct osmo_trxc_setslot cfg; bool valid; /*!< has SETSLOT been received for this TS? */ + /*! Scratch: this timeslot's Rx burst indication staged for the + * current TDMA frame tick, so dispatch can send it to L1 without + * regard to the order bursts were forwarded in. Reset to NOPE.ind + * at the start of each tick, then a real forwarded burst overwrites + * it if one lands here. See burst_fwd_dispatch() in burst_fwd.c. */ + struct osmo_trxd_burst_ind bi; };
/*! One Mobile Allocation entry: the Rx/Tx frequency pair for one ARFCN. */ @@ -50,6 +57,10 @@ struct path_sim_state path_sim; /*!< RF path simulation state (path_sim.c) */ struct proxy_trx_ts ts[PROXY_TRX_NUM_TS]; /*!< per-timeslot config (SETSLOT) */ struct proxy_trx_fh *fh; /*!< frequency hopping config (SETFH), NULL if disabled */ + /*! Queue of struct msgb, each wrapping a copy of one BURST.req + * (struct osmo_trxd_burst_req) awaiting its TDMA frame tick, sorted + * by fn (ascending). See burst_fwd.c. */ + struct llist_head tx_burst_queue; };
/*! One virtual transceiver endpoint */ diff --git a/proxy/src/burst_fwd.c b/proxy/src/burst_fwd.c index cc64743..f56f316 100644 --- a/proxy/src/burst_fwd.c +++ b/proxy/src/burst_fwd.c @@ -26,7 +26,11 @@ #include <string.h>
#include <osmocom/core/bits.h> +#include <osmocom/core/talloc.h> #include <osmocom/core/linuxlist.h> +#include <osmocom/core/msgb.h> + +#include <osmocom/gsm/gsm0502.h>
#include <osmocom/trx/ep.h>
@@ -34,6 +38,7 @@ #include <osmocom/proxy/trx.h> #include <osmocom/proxy/path_sim.h> #include <osmocom/proxy/burst_synch.h> +#include <osmocom/proxy/burst_fwd.h> #include <osmocom/proxy/logging.h>
#define OSMO_TRXD_F_COMMON_MASK ( \ @@ -43,6 +48,13 @@ OSMO_TRXD_F_TRX_NUM \ )
+/*! The BURST.req copy wrapped in a queued struct msgb + * (see struct proxy_trx_chan::tx_burst_queue). */ +static inline struct osmo_trxd_burst_req *msgb_burst_req(const struct msgb *msg) +{ + return (struct osmo_trxd_burst_req *)msgb_data(msg); +} + static void burst_fwd_to_chan(struct proxy_trx *src, unsigned int src_chan, struct proxy_trx *dst, unsigned int dst_chan, const struct osmo_trxd_burst_req *br) @@ -69,25 +81,22 @@ br, &src->chans[src_chan], g_proxy_ctx->path_sim);
- osmo_trx_ep_send_burst_ind(dst->ep, dst_chan, &bi); + /* Stage the burst rather than sending it right away: dispatch sends + * every dst channel's Rx indications in ascending tn order once all + * sources have been processed for this TDMA frame tick (see + * burst_fwd_dispatch()). */ + dst->chans[dst_chan].ts[br->tn].bi = bi; }
/*! Forward a Tx burst request to every powered-on endpoint/channel * whose Rx frequency matches the source channel's Tx frequency (resolved * per TDMA frame number if frequency hopping (SETFH) is configured). */ -void osmo_trx_ep_rx_burst_req(struct osmo_trx_ep *ep, unsigned int chan, - const struct osmo_trxd_burst_req *br) +static void burst_fwd_burst_req(struct proxy_trx *src, unsigned int chan, + const struct osmo_trxd_burst_req *br) { - struct proxy_trx *src = osmo_trx_ep_get_priv(ep); struct proxy_trx *dst = NULL; uint32_t tx_freq;
- if (!src->powered) { - LOGP_TRXCH(src, chan, DTRXD, LOGL_NOTICE, - "Rx BURST.req while not powered on, dropping\n"); - return; - } - if (src->chans[chan].fh != NULL) proxy_trx_fh_resolve(src->chans[chan].fh, br->fn, NULL, &tx_freq); else @@ -114,3 +123,147 @@ } } } + +/*! Enqueue a Tx burst request for later dispatch by the TDMA clock + * generator: real transceivers typically receive bursts a few frames ahead + * of their actual air time, so forwarding must happen at the matching TDMA + * frame tick, not immediately upon receipt (see burst_fwd_dispatch()). */ +void osmo_trx_ep_rx_burst_req(struct osmo_trx_ep *ep, unsigned int chan, + const struct osmo_trxd_burst_req *br) +{ + struct proxy_trx *src = osmo_trx_ep_get_priv(ep); + struct proxy_trx_chan *c = &src->chans[chan]; + struct msgb *msg, *cur; + + if (!src->powered) { + LOGP_TRXCH(src, chan, DTRXD, LOGL_NOTICE, + "Rx BURST.req while not powered on, dropping\n"); + return; + } + + msg = msgb_alloc_c(src, sizeof(*br), "trxd_burst_req"); + OSMO_ASSERT(msg != NULL); + memcpy(msgb_put(msg, sizeof(*br)), br, sizeof(*br)); + + /* The queue is kept sorted by TDMA FN (ascending) so that dispatch, + * which is timing critical, only ever needs to look at the head of + * the queue instead of scanning it in full on every TDMA frame tick. + * Bursts normally arrive already in ascending FN order (a few frames + * ahead of their air time), so a new entry almost always belongs at + * the tail: scan backwards from there so the common case is O(1). */ + llist_for_each_entry_reverse(cur, &c->tx_burst_queue, list) { + if (gsm0502_fncmp(msgb_burst_req(cur)->fn, br->fn) <= 0) { + llist_add(&msg->list, &cur->list); + return; + } + } + llist_add(&msg->list, &c->tx_burst_queue); +} + +/*! Reset every powered channel's per-tn staging area to NOPE.ind for the + * given TDMA frame number, so real forwarded bursts only need to overwrite + * the entries they land on (see burst_fwd_to_chan()). */ +static void burst_fwd_reset_bi(uint32_t fn) +{ + struct proxy_trx *trx; + + llist_for_each_entry(trx, &g_proxy_ctx->trx_list, list) { + if (!trx->powered) + continue; + + for (unsigned int chan = 0; chan < trx->num_chans; chan++) { + struct proxy_trx_chan *c = &trx->chans[chan]; + + for (unsigned int tn = 0; tn < PROXY_TRX_NUM_TS; tn++) { + c->ts[tn].bi = (struct osmo_trxd_burst_ind){ .fn = fn, .tn = tn }; + path_sim_fill_nope(&c->ts[tn].bi, g_proxy_ctx->path_sim); + } + } + } +} + +/*! Send every powered channel's staged Rx indications (see + * burst_fwd_reset_bi(), burst_fwd_to_chan()) for this TDMA frame tick, + * in ascending tn order. */ +static void burst_fwd_send_bi(void) +{ + struct proxy_trx *trx; + + llist_for_each_entry(trx, &g_proxy_ctx->trx_list, list) { + if (!trx->powered) + continue; + + for (unsigned int chan = 0; chan < trx->num_chans; chan++) { + struct proxy_trx_chan *c = &trx->chans[chan]; + + for (unsigned int tn = 0; tn < PROXY_TRX_NUM_TS; tn++) { + struct proxy_trx_ts *ts = &c->ts[tn]; + struct osmo_trxd_burst_ind *bi = &ts->bi; + + /* unconfigured (no SETSLOT) timeslot: send nothing at all */ + if (!ts->valid) + continue; + + /* TRXDv0 has no MTS field to carry the NOPE.ind flag in */ + if ((bi->flags & OSMO_TRXD_F_NOPE_IND) && + osmo_trx_ep_get_pdu_ver(trx->ep, chan) < 1) + continue; + + osmo_trx_ep_send_burst_ind(trx->ep, chan, bi); + } + + /* flush batched PDUs (no-op below TRXDv2) */ + osmo_trx_ep_send_burst_fin(trx->ep, chan); + } + } +} + +/*! Dispatch every queued BURST.req whose TDMA frame number is due: + * forward those with fn == fn, drop those with fn < fn (arrived too late), + * and leave the rest queued. */ +void burst_fwd_dispatch(uint32_t fn) +{ + struct proxy_trx *trx; + + /* Three passes over this TDMA frame tick: + * 1. pre-fill every dst channel's Rx staging area with NOPE.ind; */ + burst_fwd_reset_bi(fn); + + /* 2. dispatch the per-channel Tx queues, overwriting the staged + * NOPE.ind of whatever real bursts land; */ + llist_for_each_entry(trx, &g_proxy_ctx->trx_list, list) { + if (!trx->powered) + continue; + + for (unsigned int chan = 0; chan < trx->num_chans; chan++) { + struct proxy_trx_chan *c = &trx->chans[chan]; + struct msgb *msg, *msg2; + + /* tx_burst_queue is sorted by fn (ascending), so it's + * enough to consume its head and stop at the first + * entry that is not yet due. */ + llist_for_each_entry_safe(msg, msg2, &c->tx_burst_queue, list) { + struct osmo_trxd_burst_req *br = msgb_burst_req(msg); + int rc = gsm0502_fncmp(br->fn, fn); + + if (rc > 0) /* br->fn is still ahead of fn */ + break; + + llist_del(&msg->list); + + if (OSMO_UNLIKELY(rc < 0)) { /* br->fn is behind fn */ + LOGP_TRXCH(trx, chan, DTRXD, LOGL_ERROR, + "Rx BURST.req for fn=%u too late (now fn=%u), " + "dropping\n", br->fn, fn); + } else { + burst_fwd_burst_req(trx, chan, br); + } + + msgb_free(msg); + } + } + } + + /* 3. send every staged Rx indication out to L1. */ + burst_fwd_send_bi(); +} diff --git a/proxy/src/clck_gen.c b/proxy/src/clck_gen.c index 813530c..ff6c64c 100644 --- a/proxy/src/clck_gen.c +++ b/proxy/src/clck_gen.c @@ -37,6 +37,7 @@ #include <osmocom/proxy/proxy.h> #include <osmocom/proxy/trx.h> #include <osmocom/proxy/clck_gen.h> +#include <osmocom/proxy/burst_fwd.h> #include <osmocom/proxy/logging.h>
/*! Default "IND CLOCK" period, in frames */ @@ -78,7 +79,7 @@ } }
- /* TODO: drive per-frame burst forwarding (burst_queue/burst_fwd) */ + burst_fwd_dispatch(gen->fn);
GSM_TDMA_FN_INC(gen->fn); } diff --git a/proxy/src/path_sim.c b/proxy/src/path_sim.c index 260cce2..1b069ed 100644 --- a/proxy/src/path_sim.c +++ b/proxy/src/path_sim.c @@ -190,6 +190,20 @@ bi->flags |= OSMO_TRXD_F_CI_CB; }
+/*! Fill in a NOPE.ind for a timeslot where no BURST.req was received at all + * this TDMA frame tick (as opposed to path_sim_apply(), which reports on an + * actually forwarded burst that got dropped/muted along the way). Reports + * the configured noise floor, same as path_sim_measure() would for an idle + * frequency. */ +void path_sim_fill_nope(struct osmo_trxd_burst_ind *bi, const struct path_sim_cfg *cfg) +{ + bi->flags |= OSMO_TRXD_F_NOPE_IND | OSMO_TRXD_F_CI_CB; + bi->burst_len = 0; + bi->toa256 = PATH_SIM_TOA256_NOISE_DEFAULT; + bi->rssi = cfg->noise_dbm; + bi->ci_cb = PATH_SIM_CI_NOISE_DEFAULT; +} + static int path_sim_measure_rssi(const struct proxy_trx_chan *tx, const struct path_sim_cfg *cfg) { if (tx->path_sim.flags & PATH_SIM_F_FAKE_RSSI) diff --git a/proxy/src/trx.c b/proxy/src/trx.c index da315d1..96bfb48 100644 --- a/proxy/src/trx.c +++ b/proxy/src/trx.c @@ -135,6 +135,7 @@ path_sim_state_reset(&trx->chans[i].path_sim, trx->tx_power, path_sim_cfg_get_nom_toa256(g_proxy_ctx->path_sim), path_sim_cfg_get_nom_ci_cb(g_proxy_ctx->path_sim)); + INIT_LLIST_HEAD(&trx->chans[i].tx_burst_queue); } }