gprs_rlc_ul_window: Mark received BSNs falling out of the V(N)/RBB when V(R) is raised

This is a port of libosmo-gprs.git commit 49535bec37caddcea5718ed4e526c477c71f1b3a applied to osmo-pcu.

Problem this patch is fixing:
The current RLCMAC code is never invalidating the BSNs which have
been received after they are not needed.
This is somehow workaround by the following check when eg BSN wraps
around 127->0 and the BSN=0 is received:
""""
rcv_data_block_acknowledged()
	m_window.m_v_n.is_received(rdbi->bsn)
		/* Offset to the end of the received window */
		offset_v_r = (m_v_r - 1 - bsn) & mod_sns();
		return m_v_n.is_received(bsn) && offset_v_r < ws();
"""

The first m_v_n_is_received() would return true because the BSN has
never been invalidated, which is wrong, but fortunately the extra check
"offset_v_r < ws()" returns > ws() since:
v_r=0, bsn=0: "(0 - 1 - 0) & 127" ----> 0xffff & 0x007f ---> 0x007f > 64

So thanks to the above the BSN is considered not received, but looking
at the V(N) array would say the opposite. Hence, this commit aims at
fixing the V(N) array to contain proper values (marked "invalid") for
BSNs becoming out of the window when it rolls forward.

Explanation of the solution:
The V(N) array contains the status of the previous WS (64 in GPRS) data
blocks. This array is used to construct the RRB signaled to the peer
during PKT UL ACK/NACK messages together with the SSN (start sequence
number), which in our case is mainly V(R), aka one block higher than the
highest received block in the rx window.
Hence, whenever PKT UL ACK/NACK is transmitted, it contains an RRB
ranging [V(R)-1,...V(R)-WS)] mod SNS (SNS=128 in GPRS).

The receive window is basically [V(Q) <= BSN < V(R)] mod SNS, as is of
size 64.
The V(R) is increased whenever a highest new block arrives which is in the
receive window (guaranteeing it will be increased to at most V(Q)+64.

Since we are only announcing state of blocks from V(R)..V(R)-WS, and
blocks received which are before that BSN are dropped since don't fall
inside the rx window, we can securely mark as invalid those blocks
falling behind V(R)-WS whenever we increase V(R).

Related: OS#6102
Change-Id: I5ef4dcb0c5eac07a892114897f9e4f565b1dcc2c
This commit is contained in:
Pau Espin 2023-07-25 17:43:36 +02:00
parent ff33597e4b
commit c1278288b5
3 changed files with 15 additions and 3 deletions

View File

@ -37,8 +37,14 @@ void gprs_rlc_ul_window::raise_v_r(const uint16_t bsn)
/* Positive offset, so raise. */
if (offset_v_r < (sns() >> 1)) {
while (offset_v_r--) {
if (offset_v_r) /* all except the received block */
m_v_n.mark_missing(v_r());
const uint16_t _v_r = v_r();
const uint16_t bsn_no_longer_in_ws = mod_sns(_v_r - ws());
LOGP(DRLCMACUL, LOGL_DEBUG, "- Mark BSN %u as INVALID\n", bsn_no_longer_in_ws);
m_v_n.mark_invalid(bsn_no_longer_in_ws);
if (offset_v_r) {/* all except the received block */
LOGP(DRLCMACUL, LOGL_DEBUG, "- Mark BSN %u as MISSING\n", _v_r);
m_v_n.mark_missing(_v_r);
}
raise_v_r_to(1);
}
LOGP(DRLCMACUL, LOGL_DEBUG, "- Raising V(R) to %d\n", v_r());

View File

@ -32,6 +32,7 @@ struct gprs_rlc_v_n {
void mark_received(int bsn);
void mark_missing(int bsn);
void mark_invalid(int bsn);
bool is_received(int bsn) const;
@ -53,6 +54,11 @@ inline void gprs_rlc_v_n::mark_missing(int bsn)
return mark(bsn, GPRS_RLC_UL_BSN_MISSING);
}
inline void gprs_rlc_v_n::mark_invalid(int bsn)
{
return mark(bsn, GPRS_RLC_UL_BSN_INVALID);
}
inline bool gprs_rlc_v_n::is_received(int bsn) const
{
return is_state(bsn, GPRS_RLC_UL_BSN_RECEIVED);

View File

@ -303,7 +303,7 @@ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(
rdbi->bsn,
m_window.v_q(), m_window.mod_sns(m_window.v_q() + ws - 1));
continue;
} else if (m_window.is_received(rdbi->bsn)) {
} else if (m_window.m_v_n.is_received(rdbi->bsn)) {
LOGPTBFUL(this, LOGL_DEBUG,
"BSN %d already received\n", rdbi->bsn);
continue;