aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJacob Erlbeck <jerlbeck@sysmocom.de>2015-04-02 13:58:09 +0200
committerJacob Erlbeck <jerlbeck@sysmocom.de>2015-04-02 18:14:08 +0200
commitd0261b72dea475c1fccdf00b77d6be6317588232 (patch)
treeb18e461c6584e8fd83484b9d49792c97befc9298 /src
parent0c1c8778dfdda80222d30b903d62156adb6927c4 (diff)
tbf: Force ACK after the last DL LCC frame has been received
If the protocol layers above LLC (e.g. TCP) need an acknowledgement to continue, it can take up to 400ms (single TS) until the MS is polled for Ack/Nack which it can use to request an uplink TBF quickly. The 400ms result from requesting an DL Ack/Nack every 20 RLC blocks until all pending LLC frames have been sent. Especially TCP's slow start mechanism can lead to a high delay at the start of the connection, since the sender will eventually stop after having sent the first packets (up to 4 (RFC2581) or 10 (RFC6928)). This commit modifies append_data() to (re-)start a timer every time it handles an LLC packet and to request an Ack/Nack every time it expires. So if the server ceases to send IP packets, the MS is polled in the assumption, that the server is waiting for an ACK. The following VTY commands are added (pcu node): - queue idle-ack-delay <1-65535> timeout in centiseconds - no queue idle-ack-delay disable this feature (default) A sensible value is 10 (100ms) that at gave promising results when testing locally. Sponsored-by: On-Waves ehf
Diffstat (limited to 'src')
-rw-r--r--src/bts.h1
-rw-r--r--src/pcu_vty.c34
-rw-r--r--src/tbf.cpp1
-rw-r--r--src/tbf.h5
-rw-r--r--src/tbf_dl.cpp35
5 files changed, 75 insertions, 1 deletions
diff --git a/src/bts.h b/src/bts.h
index fae3c5e5..4b7f7e86 100644
--- a/src/bts.h
+++ b/src/bts.h
@@ -119,6 +119,7 @@ struct gprs_rlcmac_bts {
uint8_t force_cs; /* 0=use from BTS 1=use from VTY */
uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
uint32_t llc_discard_csec;
+ uint32_t llc_idle_ack_csec;
uint8_t t3142;
uint8_t t3169;
uint8_t t3191;
diff --git a/src/pcu_vty.c b/src/pcu_vty.c
index 3d9c79a3..00cd6fca 100644
--- a/src/pcu_vty.c
+++ b/src/pcu_vty.c
@@ -100,6 +100,9 @@ static int config_write_pcu(struct vty *vty)
if (bts->llc_discard_csec)
vty_out(vty, " queue hysteresis %d%s", bts->llc_discard_csec,
VTY_NEWLINE);
+ if (bts->llc_idle_ack_csec)
+ vty_out(vty, " queue idle-ack-delay %d%s", bts->llc_idle_ack_csec,
+ VTY_NEWLINE);
if (bts->alloc_algorithm == alloc_algorithm_a)
vty_out(vty, " alloc-algorithm a%s", VTY_NEWLINE);
if (bts->alloc_algorithm == alloc_algorithm_b)
@@ -217,7 +220,7 @@ DEFUN(cfg_pcu_no_queue_lifetime,
DEFUN(cfg_pcu_queue_hysteresis,
cfg_pcu_queue_hysteresis_cmd,
- "queue hysteresis <1-65534>",
+ "queue hysteresis <1-65535>",
QUEUE_STR QUEUE_HYSTERESIS_STR "Hysteresis in centi-seconds")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
@@ -240,6 +243,33 @@ DEFUN(cfg_pcu_no_queue_hysteresis,
return CMD_SUCCESS;
}
+#define QUEUE_IDLE_ACK_STR "Request an ACK after the last DL LLC frame in centi-seconds\n"
+
+DEFUN(cfg_pcu_queue_idle_ack_delay,
+ cfg_pcu_queue_idle_ack_delay_cmd,
+ "queue idle-ack-delay <1-65535>",
+ QUEUE_STR QUEUE_IDLE_ACK_STR "Idle ACK delay in centi-seconds")
+{
+ struct gprs_rlcmac_bts *bts = bts_main_data();
+ uint8_t csec = atoi(argv[0]);
+
+ bts->llc_idle_ack_csec = csec;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_pcu_no_queue_idle_ack_delay,
+ cfg_pcu_no_queue_idle_ack_delay_cmd,
+ "no queue idle-ack-delay",
+ NO_STR QUEUE_STR QUEUE_IDLE_ACK_STR)
+{
+ struct gprs_rlcmac_bts *bts = bts_main_data();
+
+ bts->llc_idle_ack_csec = 0;
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_pcu_alloc,
cfg_pcu_alloc_cmd,
@@ -400,6 +430,8 @@ int pcu_vty_init(const struct log_info *cat)
install_element(PCU_NODE, &cfg_pcu_no_queue_lifetime_cmd);
install_element(PCU_NODE, &cfg_pcu_queue_hysteresis_cmd);
install_element(PCU_NODE, &cfg_pcu_no_queue_hysteresis_cmd);
+ install_element(PCU_NODE, &cfg_pcu_queue_idle_ack_delay_cmd);
+ install_element(PCU_NODE, &cfg_pcu_no_queue_idle_ack_delay_cmd);
install_element(PCU_NODE, &cfg_pcu_alloc_cmd);
install_element(PCU_NODE, &cfg_pcu_two_phase_cmd);
install_element(PCU_NODE, &cfg_pcu_fc_interval_cmd);
diff --git a/src/tbf.cpp b/src/tbf.cpp
index 2c7bd949..9869ff4b 100644
--- a/src/tbf.cpp
+++ b/src/tbf.cpp
@@ -128,6 +128,7 @@ void tbf_free(struct gprs_rlcmac_tbf *tbf)
if (tbf->direction == GPRS_RLCMAC_DL_TBF) {
gprs_rlcmac_dl_tbf *dl_tbf = static_cast<gprs_rlcmac_dl_tbf *>(tbf);
gprs_rlcmac_lost_rep(dl_tbf);
+ dl_tbf->cleanup();
}
LOGP(DRLCMAC, LOGL_INFO, "%s free\n", tbf_name(tbf));
diff --git a/src/tbf.h b/src/tbf.h
index 17ba980e..e835ab6f 100644
--- a/src/tbf.h
+++ b/src/tbf.h
@@ -311,6 +311,8 @@ inline time_t gprs_rlcmac_tbf::created_ts() const
}
struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
+ void cleanup();
+
/* dispatch Unitdata.DL messages */
static int handle(struct gprs_rlcmac_bts *bts,
const uint32_t tlli, const char *imsi, const uint8_t ms_class,
@@ -365,6 +367,9 @@ protected:
int maybe_start_new_window();
bool dl_window_stalled() const;
void reuse_tbf(const uint8_t *data, const uint16_t len);
+ void start_llc_timer();
+
+ struct osmo_timer_list m_llc_timer;
};
struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp
index aeec23f2..e8c5dfea 100644
--- a/src/tbf_dl.cpp
+++ b/src/tbf_dl.cpp
@@ -62,6 +62,38 @@ static inline void tbf_update_ms_class(struct gprs_rlcmac_tbf *tbf,
tbf->ms_class = ms_class;
}
+static void llc_timer_cb(void *_tbf)
+{
+ struct gprs_rlcmac_dl_tbf *tbf = (struct gprs_rlcmac_dl_tbf *)_tbf;
+
+ if (tbf->state_is_not(GPRS_RLCMAC_FLOW))
+ return;
+
+ LOGP(DRLCMAC, LOGL_DEBUG,
+ "%s LLC receive timeout, requesting DL ACK\n", tbf_name(tbf));
+
+ tbf->request_dl_ack();
+}
+
+void gprs_rlcmac_dl_tbf::cleanup()
+{
+ osmo_timer_del(&m_llc_timer);
+}
+
+void gprs_rlcmac_dl_tbf::start_llc_timer()
+{
+ if (bts_data()->llc_idle_ack_csec > 0) {
+ struct timeval tv;
+
+ /* TODO: this ought to be within a constructor */
+ m_llc_timer.data = this;
+ m_llc_timer.cb = &llc_timer_cb;
+
+ csecs_to_timeval(bts_data()->llc_idle_ack_csec, &tv);
+ osmo_timer_schedule(&m_llc_timer, tv.tv_sec, tv.tv_usec);
+ }
+}
+
int gprs_rlcmac_dl_tbf::append_data(const uint8_t ms_class,
const uint16_t pdu_delay_csec,
const uint8_t *data, const uint16_t len)
@@ -80,7 +112,9 @@ int gprs_rlcmac_dl_tbf::append_data(const uint8_t ms_class,
/* it is no longer drained */
m_last_dl_drained_fn = -1;
tbf_update_ms_class(this, ms_class);
+ start_llc_timer();
} else {
+ /* TODO: put this path into an llc_enqueue method */
/* the TBF exists, so we must write it in the queue
* we prepend lifetime in front of PDU */
struct timeval *tv;
@@ -95,6 +129,7 @@ int gprs_rlcmac_dl_tbf::append_data(const uint8_t ms_class,
memcpy(msgb_put(llc_msg, len), data, len);
m_llc.enqueue(llc_msg);
tbf_update_ms_class(this, ms_class);
+ start_llc_timer();
}
return 0;