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
diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp
index aeec23f..e8c5dfe 100644
--- a/src/tbf_dl.cpp
+++ b/src/tbf_dl.cpp
@@ -62,6 +62,38 @@
 		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 @@
 		/* 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 @@
 		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;