From ee5be3a0095b4bae430032d35e3ad0716eff34ab Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 20 Dec 2017 17:31:13 +0100 Subject: TBF: implement independent T31xx timers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously TBF got single timer so the pending timer was automatically cancelled when new one was scheduled. Let's make it more robust by implementing independent T31 xx timers from 3GPP TS 44.060 ยง13.2 with corresponding start/stop functions and counters. The semantics of the timers is preserved as before: pending timers are restarted unconditionally. It might be neecessary to change this later on after spec review. N. B. T0: used for assign/reject timeouts, have to be properly attributed and documented first. Change-Id: I0305873ca47534f53441247217881da59625e1f7 Related: OS#2407 --- src/bts.cpp | 2 +- src/tbf.cpp | 211 ++++++++++++++++++++++++++++++++++---------------- src/tbf.h | 25 +++++- src/tbf_dl.cpp | 14 ++-- src/tbf_ul.cpp | 2 +- tests/tbf/TbfTest.cpp | 4 +- 6 files changed, 179 insertions(+), 79 deletions(-) diff --git a/src/bts.cpp b/src/bts.cpp index b2af7aac..9e8a6c83 100644 --- a/src/bts.cpp +++ b/src/bts.cpp @@ -687,7 +687,7 @@ int BTS::rcv_rach(uint16_t ra, uint32_t Fn, int16_t qta, uint8_t is_11bit, tbf->set_ta(ta); tbf->set_state(GPRS_RLCMAC_FLOW); tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH); - tbf_timer_start(tbf, 3169, m_bts.t3169, 0, "RACH (new UL-TBF)"); + tbf->t_start(T3169, m_bts.t3169, 0, "RACH (new UL-TBF)", true); LOGPTBF(tbf, LOGL_DEBUG, "[UPLINK] START\n"); LOGPTBF(tbf, LOGL_DEBUG, "RX: [PCU <- BTS] RACH " "qbit-ta=%d ra=0x%02x, Fn=%d " diff --git a/src/tbf.cpp b/src/tbf.cpp index 18059ace..a5eedd93 100644 --- a/src/tbf.cpp +++ b/src/tbf.cpp @@ -32,6 +32,7 @@ extern "C" { #include +#include #include #include } @@ -58,6 +59,14 @@ const struct value_string gprs_rlcmac_tbf_ul_ass_state_names[] = { { 0, NULL } }; +static const struct value_string tbf_timers_names[] = { + OSMO_VALUE_STRING(T3169), + OSMO_VALUE_STRING(T3191), + OSMO_VALUE_STRING(T3193), + OSMO_VALUE_STRING(T3195), + { 0, NULL } +}; + static const struct rate_ctr_desc tbf_ctr_description[] = { { "rlc.nacked", "RLC Nacked " }, }; @@ -182,6 +191,7 @@ gprs_rlcmac_tbf::gprs_rlcmac_tbf(BTS *bts_, gprs_rlcmac_tbf_direction dir) : * Just set them to 0 like talloc_zero did */ memset(&pdch, 0, sizeof(pdch)); memset(&timer, 0, sizeof(timer)); + memset(&T31, 0, sizeof(T31)); memset(&m_rlc, 0, sizeof(m_rlc)); memset(&gsm_timer, 0, sizeof(gsm_timer)); @@ -323,7 +333,7 @@ void gprs_rlcmac_tbf::merge_and_clear_ms(GprsMs *old_ms) /* Clean up the old MS object */ /* TODO: Use timer? */ - if (old_ms->ul_tbf() && old_ms->ul_tbf()->T == 0) { + if (old_ms->ul_tbf() && !old_ms->ul_tbf()->timers_pending(T_MAX)) { if (old_ms->ul_tbf() == this) { LOGP(DRLCMAC, LOGL_ERROR, "%s is referred by the old MS " @@ -334,7 +344,7 @@ void gprs_rlcmac_tbf::merge_and_clear_ms(GprsMs *old_ms) tbf_free(old_ms->ul_tbf()); } } - if (old_ms->dl_tbf() && old_ms->dl_tbf()->T == 0) { + if (old_ms->dl_tbf() && !old_ms->dl_tbf()->timers_pending(T_MAX)) { if (old_ms->dl_tbf() == this) { LOGP(DRLCMAC, LOGL_ERROR, "%s is referred by the old MS " @@ -393,7 +403,7 @@ gprs_rlcmac_ul_tbf *tbf_alloc_ul(struct gprs_rlcmac_bts *bts, tbf->m_contention_resolution_done = 1; tbf->set_state(GPRS_RLCMAC_ASSIGN); tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH); - tbf_timer_start(tbf, 3169, bts->t3169, 0, "allocation (UL-TBF)"); + tbf->t_start(T3169, bts->t3169, 0, "allocation (UL-TBF)", true); tbf->update_ms(tlli, GPRS_RLCMAC_UL_TBF); OSMO_ASSERT(tbf->ms()); @@ -462,6 +472,7 @@ void tbf_free(struct gprs_rlcmac_tbf *tbf) get_value_string(gprs_rlcmac_tbf_dl_ass_state_names, tbf->dl_ass_state)); tbf->stop_timer("freeing TBF"); + tbf->stop_timers("freeing TBF"); /* TODO: Could/Should generate bssgp_tx_llc_discarded */ tbf_unlink_pdch(tbf); llist_del(&tbf->list()); @@ -547,9 +558,40 @@ void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T, osmo_timer_schedule(&tbf->timer, seconds, microseconds); } -void gprs_rlcmac_tbf::stop_t3191() +void gprs_rlcmac_tbf::t_stop(enum tbf_timers t, const char *reason) { - return stop_timer("T3191"); + if (t >= T_MAX) { + LOGPTBF(this, LOGL_ERROR, "attempting to stop unknown timer %s [%s]\n", + get_value_string(tbf_timers_names, t), reason); + } + + if (osmo_timer_pending(&T31[t])) { + LOGPTBF(this, LOGL_DEBUG, "stopping timer %s [%s]\n", + get_value_string(tbf_timers_names, t), reason); + osmo_timer_del(&T31[t]); + } +} + +/* check if any of T31xx timer(s) are pending */ +bool gprs_rlcmac_tbf::timers_pending(enum tbf_timers t) +{ + uint8_t i; + + if (t != T_MAX) + return osmo_timer_pending(&T31[t]); + + for (i = T3169; i < T_MAX; i++) + if (osmo_timer_pending(&T31[i])) + return true; + + return false; +} + +void gprs_rlcmac_tbf::stop_timers(const char *reason) +{ + uint8_t i; + for (i = 0; i < T_MAX; i++) + t_stop((enum tbf_timers)i, reason); } void gprs_rlcmac_tbf::stop_timer(const char *reason) @@ -561,6 +603,60 @@ void gprs_rlcmac_tbf::stop_timer(const char *reason) } } +static inline void tbf_timeout_free(struct gprs_rlcmac_tbf *tbf, enum tbf_timers t, bool run_diag) +{ + LOGPTBF(tbf, LOGL_NOTICE, "%s timeout expired, freeing TBF\n", + get_value_string(tbf_timers_names, t)); + + if (run_diag) + tbf->rlcmac_diag(); + + tbf_free(tbf); +} + +#define T_CBACK(t, diag) static void cb_##t(void *_tbf) { tbf_timeout_free((struct gprs_rlcmac_tbf *)_tbf, t, diag); } + +T_CBACK(T3169, true) +T_CBACK(T3191, true) +T_CBACK(T3193, false) +T_CBACK(T3195, true) + +void gprs_rlcmac_tbf::t_start(enum tbf_timers t, uint32_t sec, uint32_t microsec, const char *reason, bool force) +{ + if (t >= T_MAX) { + LOGPTBF(this, LOGL_ERROR, "attempting to start unknown timer %s [%s]\n", + get_value_string(tbf_timers_names, t), reason); + } + + if (!force && osmo_timer_pending(&T31[t])) + return; + + LOGPTBF(this, LOGL_DEBUG, "%sstarting timer %s [%s] with %u sec. %u microsec.\n", + osmo_timer_pending(&T31[t]) ? "re" : "", get_value_string(tbf_timers_names, t), reason, sec, microsec); + + T31[t].data = this; + + switch(t) { + case T3169: + T31[t].cb = cb_T3169; + break; + case T3191: + T31[t].cb = cb_T3191; + break; + case T3193: + T31[t].cb = cb_T3193; + break; + case T3195: + T31[t].cb = cb_T3195; + break; + default: + LOGPTBF(this, LOGL_ERROR, "attempting to set callback for unknown timer %s [%s]\n", + get_value_string(tbf_timers_names, t), reason); + } + + osmo_timer_schedule(&T31[t], sec, microsec); +} + int gprs_rlcmac_tbf::check_polling(uint32_t fn, uint8_t ts, uint32_t *poll_fn_, unsigned int *rrbp_) { @@ -658,7 +754,7 @@ void gprs_rlcmac_tbf::poll_timeout() "- N3103 exceeded\n"); bts->pkt_ul_ack_nack_poll_failed(); ul_tbf->set_state(GPRS_RLCMAC_RELEASING); - tbf_timer_start(ul_tbf, 3169, ul_tbf->bts->bts_data()->t3169, 0, "MAX N3103 reached"); + ul_tbf->t_start(T3169, ul_tbf->bts->bts_data()->t3169, 0, "MAX N3103 reached", false); return; } /* reschedule UL ack */ @@ -680,7 +776,7 @@ void gprs_rlcmac_tbf::poll_timeout() if (n3105 == bts_data()->n3105) { LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); set_state(GPRS_RLCMAC_RELEASING); - tbf_timer_start(this, 3195, bts_data()->t3195, 0, "MAX N3105 reached"); + t_start(T3195, bts_data()->t3195, 0, "MAX N3105 reached", true); bts->rlc_ass_failed(); bts->pua_poll_failed(); return; @@ -702,7 +798,7 @@ void gprs_rlcmac_tbf::poll_timeout() if (n3105 == bts->bts_data()->n3105) { LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); set_state(GPRS_RLCMAC_RELEASING); - tbf_timer_start(this, 3195, bts_data()->t3195, 0, "MAX N3105 reached"); + t_start(T3195, bts_data()->t3195, 0, "MAX N3105 reached", true); bts->rlc_ass_failed(); bts->pda_poll_failed(); return; @@ -728,7 +824,7 @@ void gprs_rlcmac_tbf::poll_timeout() if (dl_tbf->n3105 == dl_tbf->bts->bts_data()->n3105) { LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n"); dl_tbf->set_state(GPRS_RLCMAC_RELEASING); - tbf_timer_start(dl_tbf, 3195, dl_tbf->bts_data()->t3195, 0, "MAX N3105 reached"); + dl_tbf->t_start(T3195, dl_tbf->bts_data()->t3195, 0, "MAX N3105 reached", true); bts->pkt_dl_ack_nack_poll_failed(); bts->rlc_ack_failed(); return; @@ -999,64 +1095,47 @@ void gprs_rlcmac_tbf::handle_timeout() { LOGPTBF(this, LOGL_DEBUG, "timer %u expired.\n", T); - switch (T) { - case 0: /* assignment */ - if ((state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH))) { - if (state_is(GPRS_RLCMAC_ASSIGN)) { - LOGPTBF(this, LOGL_NOTICE, - "releasing due to PACCH assignment timeout.\n"); - tbf_free(this); - return; - } else - LOGPTBF(this, LOGL_ERROR, - "Error: TBF is not in assign state\n"); - } - if ((state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))) { - gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(this); - dl_tbf->m_wait_confirm = 0; - if (dl_tbf->state_is(GPRS_RLCMAC_ASSIGN)) { - tbf_assign_control_ts(dl_tbf); - - if (!dl_tbf->upgrade_to_multislot) { - /* change state to FLOW, so scheduler - * will start transmission */ - dl_tbf->set_state(GPRS_RLCMAC_FLOW); - break; - } - - /* This tbf can be upgraded to use multiple DL - * timeslots and now that there is already one - * slot assigned send another DL assignment via - * PDCH. */ - - /* keep to flags */ - dl_tbf->state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; - - dl_tbf->update(); - - dl_tbf->trigger_ass(dl_tbf); - } else - LOGPTBF(dl_tbf, LOGL_NOTICE, - "Continue flow after IMM.ASS confirm\n"); - } - break; - case 3169: - case 3191: - case 3195: - LOGPTBF(this, LOGL_NOTICE, "T%d timeout during " - "transsmission\n", T); - rlcmac_diag(); - /* fall through */ - case 3193: - LOGP(DRLCMAC, LOGL_DEBUG, - "%s will be freed due to timeout\n", tbf_name(this)); - /* free TBF */ - tbf_free(this); + if (T) { + LOGPTBF(this, LOGL_ERROR, "%s timer expired in unknown mode: %u\n", T); return; - break; - default: - LOGP(DRLCMAC, LOGL_ERROR, - "%s timer expired in unknown mode: %u\n", tbf_name(this), T); + } + + /* assignment */ + if ((state_flags & (1 << GPRS_RLCMAC_FLAG_PACCH))) { + if (state_is(GPRS_RLCMAC_ASSIGN)) { + LOGPTBF(this, LOGL_NOTICE, "releasing due to PACCH assignment timeout.\n"); + tbf_free(this); + return; + } else + LOGPTBF(this, LOGL_ERROR, "Error: TBF is not in assign state\n"); + } + + if ((state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))) { + gprs_rlcmac_dl_tbf *dl_tbf = as_dl_tbf(this); + dl_tbf->m_wait_confirm = 0; + if (dl_tbf->state_is(GPRS_RLCMAC_ASSIGN)) { + tbf_assign_control_ts(dl_tbf); + + if (!dl_tbf->upgrade_to_multislot) { + /* change state to FLOW, so scheduler + * will start transmission */ + dl_tbf->set_state(GPRS_RLCMAC_FLOW); + return; + } + + /* This tbf can be upgraded to use multiple DL + * timeslots and now that there is already one + * slot assigned send another DL assignment via + * PDCH. */ + + /* keep to flags */ + dl_tbf->state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; + + dl_tbf->update(); + + dl_tbf->trigger_ass(dl_tbf); + } else + LOGPTBF(dl_tbf, LOGL_NOTICE, "Continue flow after IMM.ASS confirm\n"); } } diff --git a/src/tbf.h b/src/tbf.h index 15d414d4..06c9f60c 100644 --- a/src/tbf.h +++ b/src/tbf.h @@ -138,6 +138,24 @@ enum tbf_egprs_ul_counters { #define LOGPTBFUL(tbf, level, fmt, args...) LOGP(DRLCMACUL, level, "%s " fmt, tbf_name(tbf), ## args) #define LOGPTBFDL(tbf, level, fmt, args...) LOGP(DRLCMACDL, level, "%s " fmt, tbf_name(tbf), ## args) +enum tbf_timers { + /* Wait for reuse of USF and TFI(s) after the MS uplink assignment for this TBF is invalid. */ + T3169, + + /* Wait for reuse of TFI(s) after sending of the last RLC Data Block on this TBF. + Wait for reuse of TFI(s) after sending the PACKET TBF RELEASE for an MBMS radio bearer. */ + T3191, + + /* Wait for reuse of TFI(s) after reception of the final PACKET DOWNLINK ACK/NACK from the + MS for this TBF. */ + T3193, + + /* Wait for reuse of TFI(s) when there is no response from the MS + (radio failure or cell change) for this TBF/MBMS radio bearer. */ + T3195, + T_MAX +}; + #define GPRS_RLCMAC_FLAG_CCCH 0 /* assignment on CCCH */ #define GPRS_RLCMAC_FLAG_PACCH 1 /* assignment on PACCH */ #define GPRS_RLCMAC_FLAG_UL_DATA 2 /* uplink data received */ @@ -177,7 +195,10 @@ struct gprs_rlcmac_tbf { int update(); void handle_timeout(); void stop_timer(const char *reason); - void stop_t3191(); + void stop_timers(const char *reason); + bool timers_pending(enum tbf_timers t); + void t_stop(enum tbf_timers t, const char *reason); + void t_start(enum tbf_timers t, uint32_t sec, uint32_t microsec, const char *reason, bool force); int establish_dl_tbf_on_pacch(); int check_polling(uint32_t fn, uint8_t ts, @@ -305,7 +326,7 @@ private: LListHead m_list; LListHead m_ms_list; bool m_egprs_enabled; - + struct osmo_timer_list T31[T_MAX]; mutable char m_name_buf[60]; }; diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp index 0bcb67b1..b0439892 100644 --- a/src/tbf_dl.cpp +++ b/src/tbf_dl.cpp @@ -216,9 +216,9 @@ int gprs_rlcmac_dl_tbf::handle(struct gprs_rlcmac_bts *bts, } /* Clean up the old MS object */ /* TODO: Put this into a separate function, use timer? */ - if (ms_old->ul_tbf() && ms_old->ul_tbf()->T == 0) + if (ms_old->ul_tbf() && !ms_old->ul_tbf()->timers_pending(T_MAX)) tbf_free(ms_old->ul_tbf()); - if (ms_old->dl_tbf() && ms_old->dl_tbf()->T == 0) + if (ms_old->dl_tbf() && !ms_old->dl_tbf()->timers_pending(T_MAX)) tbf_free(ms_old->dl_tbf()); ms->merge_old_ms(ms_old); @@ -485,6 +485,7 @@ void gprs_rlcmac_dl_tbf::trigger_ass(struct gprs_rlcmac_tbf *old_tbf) { /* stop pending timer */ stop_timer("assignment (DL-TBF)"); + stop_timers("assignment (DL-TBF)"); /* check for downlink tbf: */ if (old_tbf) { @@ -651,7 +652,7 @@ bool gprs_rlcmac_dl_tbf::handle_ack_nack() /* reset N3105 */ n3105 = 0; - stop_t3191(); + t_stop(T3191, "ACK/NACK received"); poll_state = GPRS_RLCMAC_POLL_NONE; return ack_recovered; @@ -854,7 +855,7 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block( m_tx_counter = 0; /* start timer whenever we send the final block */ if (is_final) - tbf_timer_start(this, 3191, bts_data()->t3191, 0, "final block (DL-TBF)"); + t_start(T3191, bts_data()->t3191, 0, "final block (DL-TBF)", true); clear_poll_timeout_flag(); @@ -1116,9 +1117,8 @@ int gprs_rlcmac_dl_tbf::release() set_state(GPRS_RLCMAC_WAIT_RELEASE); /* start T3193 */ - tbf_timer_start(this, 3193, - bts_data()->t3193_msec / 1000, - (bts_data()->t3193_msec % 1000) * 1000, "release (DL-TBF)"); + t_start(T3193, bts_data()->t3193_msec / 1000, (bts_data()->t3193_msec % 1000) * 1000, + "release (DL-TBF)", true); /* reset rlc states */ m_tx_counter = 0; diff --git a/src/tbf_ul.cpp b/src/tbf_ul.cpp index 09ee1f04..8e4e57e8 100644 --- a/src/tbf_ul.cpp +++ b/src/tbf_ul.cpp @@ -191,7 +191,7 @@ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged( unsigned int block_idx; /* restart T3169 */ - tbf_timer_start(this, 3169, bts_data()->t3169, 0, "acked (data)"); + t_start(T3169, bts_data()->t3169, 0, "acked (data)", true); /* Increment RX-counter */ this->m_rx_counter++; diff --git a/tests/tbf/TbfTest.cpp b/tests/tbf/TbfTest.cpp index d4b51fe2..18bbc76e 100644 --- a/tests/tbf/TbfTest.cpp +++ b/tests/tbf/TbfTest.cpp @@ -51,9 +51,9 @@ static void check_tbf(gprs_rlcmac_tbf *tbf) { OSMO_ASSERT(tbf); if (tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE)) - OSMO_ASSERT(tbf->T == 3191 || tbf->T == 3193); + OSMO_ASSERT(tbf->timers_pending(T3191) || tbf->timers_pending(T3193)); if (tbf->state_is(GPRS_RLCMAC_RELEASING)) - OSMO_ASSERT(tbf->T != 0); + OSMO_ASSERT(tbf->timers_pending(T_MAX)); } static void test_tbf_base() -- cgit v1.2.3