aboutsummaryrefslogtreecommitdiffstats
path: root/src/tbf_dl.cpp
diff options
context:
space:
mode:
authorJacob Erlbeck <jerlbeck@sysmocom.de>2016-02-02 18:12:46 +0100
committerJacob Erlbeck <jerlbeck@sysmocom.de>2016-02-08 00:45:40 +0100
commitd6752491e1facccf3cb022a6701532379a1f4e0c (patch)
tree0570cabe506535367fb71b2f2d9d1da064bdd0db /src/tbf_dl.cpp
parentbe314d9a54578d1eb31ab84f5de9bc16f8e892e9 (diff)
edge: Send a second BSN block in an RLC message if possible
Currently only one BSN block is encoded in each RLC data message, even if MSC7-9 are used, which transport two independant (except for a max BSN delta of 512) BSN blocks. In that case, the same block is just put twice into the same message. The current create_dl_acked_block(fn, ts) method handles block selection (resend, new BSN, dummy block, ...) and restart handling in one method and is too complex to extend it accordingly. Therefore this commit move the block selection/creation handling into a new method (take_next_bsn) which delivers the next BSN along with a hint, whether it may be combined with another block. In that case, the function can be called a second time (this time with a valid previous BSN, that's the one returned by the first call) to get the second BSN. The real block generation method create_dl_acked_block(fn, ts, index, index2) is then called with both BSNs as indices (the second must be -1, if there is only one BSN). Note that every BSN returned by take_next_bsn should be passed to create_dl_acked_block to avoid state inconsistencies. Sponsored-by: On-Waves ehf
Diffstat (limited to 'src/tbf_dl.cpp')
-rw-r--r--src/tbf_dl.cpp157
1 files changed, 104 insertions, 53 deletions
diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp
index 21b2ba13..e2521f84 100644
--- a/src/tbf_dl.cpp
+++ b/src/tbf_dl.cpp
@@ -326,81 +326,136 @@ drop_frame:
return msg;
}
-/*
- * Create DL data block
- * The messages are fragmented and forwarded as data blocks.
- */
-struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(uint32_t fn, uint8_t ts)
+bool gprs_rlcmac_dl_tbf::restart_bsn_cycle()
{
- LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink (V(A)==%d .. "
- "V(S)==%d)\n", tbf_name(this),
- m_window.v_a(), m_window.v_s());
+ /* If V(S) == V(A) and finished state, we would have received
+ * acknowledgement of all transmitted block. In this case we would
+ * have transmitted the final block, and received ack from MS. But in
+ * this case we did not receive the final ack indication from MS. This
+ * should never happen if MS works correctly.
+ */
+ if (m_window.window_empty()) {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- MS acked all blocks\n");
+ return false;
+ }
-do_resend:
- /* check if there is a block with negative acknowledgement */
- int resend_bsn = m_window.resend_needed();
- if (resend_bsn >= 0) {
- LOGP(DRLCMACDL, LOGL_DEBUG, "- Resending BSN %d\n", resend_bsn);
- /* re-send block with negative aknowlegement */
- m_window.m_v_b.mark_unacked(resend_bsn);
- bts->rlc_resent();
- return create_dl_acked_block(fn, ts, resend_bsn);
+ /* cycle through all unacked blocks */
+ int resend = m_window.mark_for_resend();
+
+ /* At this point there should be at least one unacked block
+ * to be resent. If not, this is an software error. */
+ if (resend == 0) {
+ LOGP(DRLCMACDL, LOGL_ERROR, "Software error: "
+ "There are no unacknowledged blocks, but V(A) "
+ " != V(S). PLEASE FIX!\n");
+ return false;
}
- /* if the window has stalled, or transfer is complete,
- * send an unacknowledged block */
- if (state_is(GPRS_RLCMAC_FINISHED)) {
+ return true;
+}
+
+int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn,
+ int previous_bsn, GprsCodingScheme *next_cs)
+{
+ int bsn;
+ GprsCodingScheme cs2;
+ GprsCodingScheme force_cs;
+
+ bsn = m_window.resend_needed();
+
+ if (previous_bsn >= 0) {
+ force_cs = m_rlc.block(previous_bsn)->cs;
+ if (!force_cs.isEgprs())
+ return -1;
+ }
+
+ if (bsn >= 0) {
+ if (previous_bsn == bsn)
+ return -1;
+
+ if (previous_bsn >= 0 &&
+ m_window.mod_sns(bsn - previous_bsn) > RLC_EGPRS_MAX_BSN_DELTA)
+ return -1;
+
+ cs2 = m_rlc.block(bsn)->cs;
+ if (force_cs && !cs2.isCombinable(force_cs))
+ return -1;
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- Resending BSN %d\n", bsn);
+ /* re-send block with negative aknowlegement */
+ m_window.m_v_b.mark_unacked(bsn);
+ bts->rlc_resent();
+ } else if (state_is(GPRS_RLCMAC_FINISHED)) {
LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
"because all blocks have been transmitted.\n",
m_window.v_a());
bts->rlc_restarted();
+ if (restart_bsn_cycle())
+ return take_next_bsn(fn, previous_bsn, next_cs);
} else if (dl_window_stalled()) {
LOGP(DRLCMACDL, LOGL_NOTICE, "- Restarting at BSN %d, "
- "because all window is stalled.\n",
+ "because the window is stalled.\n",
m_window.v_a());
bts->rlc_stalled();
+ if (restart_bsn_cycle())
+ return take_next_bsn(fn, previous_bsn, next_cs);
} else if (have_data()) {
/* New blocks may be send */
- return create_new_bsn(fn, ts);
+ cs2 = force_cs ? force_cs : current_cs();
+ LOGP(DRLCMACDL, LOGL_DEBUG,
+ "- Sending new block at BSN %d, CS=%s\n",
+ m_window.v_s(), cs2.name());
+
+ bsn = create_new_bsn(fn, cs2);
} else if (!m_window.window_empty()) {
LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
"because all blocks have been transmitted (FLOW).\n",
m_window.v_a());
bts->rlc_restarted();
+ if (restart_bsn_cycle())
+ return take_next_bsn(fn, previous_bsn, next_cs);
} else {
/* Nothing left to send, create dummy LLC commands */
- return create_new_bsn(fn, ts);
+ LOGP(DRLCMACDL, LOGL_DEBUG,
+ "- Sending new dummy block at BSN %d, CS=%s\n",
+ m_window.v_s(), current_cs().name());
+ bsn = create_new_bsn(fn, current_cs());
+ /* Don't send a second block */
}
- /* If V(S) == V(A) and finished state, we would have received
- * acknowledgement of all transmitted block. In this case we
- * would have transmitted the final block, and received ack
- * from MS. But in this case we did not receive the final ack
- * indication from MS. This should never happen if MS works
- * correctly. */
- if (m_window.window_empty()) {
- LOGP(DRLCMACDL, LOGL_DEBUG, "- MS acked all blocks, "
- "so we re-transmit final block!\n");
+ if (bsn < 0) {
/* we just send final block again */
- int16_t index = m_window.v_s_mod(-1);
+ LOGP(DRLCMACDL, LOGL_DEBUG,
+ "- Nothing else to send, Re-transmit final block!\n");
+ bsn = m_window.v_s_mod(-1);
bts->rlc_resent();
- return create_dl_acked_block(fn, ts, index);
}
- /* cycle through all unacked blocks */
- int resend = m_window.mark_for_resend();
+ *next_cs = cs2;
- /* At this point there should be at least one unacked block
- * to be resent. If not, this is an software error. */
- if (resend == 0) {
- LOGP(DRLCMACDL, LOGL_ERROR, "Software error: "
- "There are no unacknowledged blocks, but V(A) "
- " != V(S). PLEASE FIX!\n");
- /* we just send final block again */
- int16_t index = m_window.v_s_mod(-1);
- return create_dl_acked_block(fn, ts, index);
- }
- goto do_resend;
+ return bsn;
+}
+
+/*
+ * Create DL data block
+ * The messages are fragmented and forwarded as data blocks.
+ */
+struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(uint32_t fn, uint8_t ts)
+{
+ int bsn, bsn2 = -1;
+ GprsCodingScheme cs, next_cs;
+
+ LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink (V(A)==%d .. "
+ "V(S)==%d)\n", tbf_name(this),
+ m_window.v_a(), m_window.v_s());
+
+ bsn = take_next_bsn(fn, -1, &next_cs);
+ if (bsn < 0)
+ return NULL;
+
+ if (next_cs.numDataBlocks() > 1)
+ bsn2 = take_next_bsn(fn, bsn, &next_cs);
+
+ return create_dl_acked_block(fn, ts, bsn, bsn2);
}
void gprs_rlcmac_dl_tbf::schedule_next_frame()
@@ -425,12 +480,11 @@ void gprs_rlcmac_dl_tbf::schedule_next_frame()
m_last_dl_drained_fn = -1;
}
-struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t ts)
+int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs)
{
uint8_t *data;
gprs_rlc_data *rlc_data;
const uint16_t bsn = m_window.v_s();
- GprsCodingScheme cs = current_cs();
gprs_rlc_data_block_info *rdbi;
int num_chunks = 0;
int write_offset = 0;
@@ -439,9 +493,6 @@ struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t
if (m_llc.frame_length() == 0)
schedule_next_frame();
- LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d, CS=%s\n",
- m_window.v_s(), cs.name());
-
OSMO_ASSERT(cs.isValid());
/* length of usable data block (single data unit w/o header) */
@@ -514,7 +565,7 @@ struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t
m_window.m_v_b.mark_unacked(bsn);
m_window.increment_send();
- return create_dl_acked_block(fn, ts, bsn);
+ return bsn;
}
struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(