tbf: Implement delayed release of a downlink TBF

Currently a DL TBF is immediately closed, when the LLC queue is
drained. This will lead to a new DL assignment if data is received
afterwards. In addition, it is not possible to keep the PACCH open
to poll the MS for UL establishment requests there.

GSM 44.060, 9.3.1a suggests to delay the release of an inactive TBF
for some time (max 5s).

This commit mainly changes create_new_bsn() to send LLC dummy
commands as filler if no LLC data is available until keep_open()
returns false. The keep_open() functions returns true unless a
configurable time has passed after the LLC data store drained. By
default, that time is not set which causes keep_open() to always
return false, so that delayed release is effectively disabled.

The following VTY commands are added:
  - dl-tbf-idle-time <1-5000>    to set the delay in ms
  - no dl-tbf-idle-time          to disable delayed release

Ticket: #556
Sponsored-by: On-Waves ehf
This commit is contained in:
Jacob Erlbeck 2015-03-19 11:22:38 +01:00
parent cbb1e70554
commit 3bed5d11d2
6 changed files with 97 additions and 5 deletions

View File

@ -133,6 +133,7 @@ struct gprs_rlcmac_bts {
uint32_t alloc_algorithm_curst; /* options to customize algorithm */
uint8_t force_two_phase;
uint8_t alpha, gamma;
uint32_t dl_tbf_idle_msec; /* hold time for idle DL TBFs */
/* TBF handling, make private or move into TBFController */
/* list of uplink TBFs */

21
src/pcu_utils.h Normal file
View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
inline int msecs_to_frames(int msecs) {
return (msecs * (1024 * 1000 / 4615)) / 1024;
}

View File

@ -105,6 +105,9 @@ static int config_write_pcu(struct vty *vty)
vty_out(vty, " two-phase-access%s", VTY_NEWLINE);
vty_out(vty, " alpha %d%s", bts->alpha, VTY_NEWLINE);
vty_out(vty, " gamma %d%s", bts->gamma * 2, VTY_NEWLINE);
if (bts->dl_tbf_idle_msec)
vty_out(vty, " dl-tbf-idle-time %d%s", bts->dl_tbf_idle_msec,
VTY_NEWLINE);
return CMD_SUCCESS;
}
@ -287,6 +290,31 @@ DEFUN(show_bts_stats,
return CMD_SUCCESS;
}
#define IDLE_TIME_STR "keep an idle DL TBF alive for the time given\n"
DEFUN(cfg_pcu_dl_tbf_idle_time,
cfg_pcu_dl_tbf_idle_time_cmd,
"dl-tbf-idle-time <1-5000>",
IDLE_TIME_STR "idle time in msec")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->dl_tbf_idle_msec = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_dl_tbf_idle_time,
cfg_pcu_no_dl_tbf_idle_time_cmd,
"no dl-tbf-idle-time",
NO_STR IDLE_TIME_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->dl_tbf_idle_msec = 0;
return CMD_SUCCESS;
}
DEFUN(show_tbf,
show_tbf_cmd,
"show tbf all",
@ -343,6 +371,8 @@ int pcu_vty_init(const struct log_info *cat)
install_element(PCU_NODE, &cfg_pcu_fc_interval_cmd);
install_element(PCU_NODE, &cfg_pcu_alpha_cmd);
install_element(PCU_NODE, &cfg_pcu_gamma_cmd);
install_element(PCU_NODE, &cfg_pcu_dl_tbf_idle_time_cmd);
install_element(PCU_NODE, &cfg_pcu_no_dl_tbf_idle_time_cmd);
install_element(PCU_NODE, &ournode_end_cmd);
install_element_ve(&show_bts_stats_cmd);

View File

@ -471,6 +471,7 @@ struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts,
tbf->bts->tbf_dl_created();
tbf->m_last_dl_poll_fn = -1;
tbf->m_last_dl_drained_fn = -1;
gettimeofday(&tbf->m_bw.dl_bw_tv, NULL);
gettimeofday(&tbf->m_bw.dl_loss_tv, NULL);

View File

@ -326,6 +326,8 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
bool need_control_ts() const;
bool have_data() const;
int frames_since_last_poll(unsigned fn) const;
int frames_since_last_drain(unsigned fn) const;
bool keep_open(unsigned fn) const;
bool is_control_ts(uint8_t ts) const {
return ts == control_ts;
@ -344,6 +346,7 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
uint8_t m_wait_confirm; /* wait for CCCH IMM.ASS cnf */
bool m_dl_ack_requested;
int32_t m_last_dl_poll_fn;
int32_t m_last_dl_drained_fn;
struct {
struct timeval dl_bw_tv; /* timestamp for dl bw calculation */

View File

@ -27,6 +27,8 @@
#include <gprs_bssgp_pcu.h>
#include <decoding.h>
#include "pcu_utils.h"
extern "C" {
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
@ -74,6 +76,8 @@ int gprs_rlcmac_dl_tbf::append_data(const uint8_t ms_class,
} else if (!have_data()) {
m_llc.put_frame(data, len);
bts->llc_frame_sched();
/* it is no longer drained */
m_last_dl_drained_fn = -1;
tbf_update_ms_class(this, ms_class);
} else {
/* the TBF exists, so we must write it in the queue
@ -354,14 +358,19 @@ struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t
* space-1 octets */
m_llc.put_dummy_frame(space - 1);
/* The data just drained, store the current fn */
if (m_last_dl_drained_fn < 0)
m_last_dl_drained_fn = fn;
/* It is not clear, when the next real data will
* arrive, so request a DL ack/nack now */
request_dl_ack();
LOGP(DRLCMACDL, LOGL_DEBUG,
"-- Empty chunk, "
"added LLC dummy command of size %d\n",
m_llc.frame_length());
"added LLC dummy command of size %d, "
"drained_since=%d\n",
m_llc.frame_length(), frames_since_last_drain(fn));
}
chunk = m_llc.chunk_size();
@ -380,7 +389,8 @@ struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t
break;
}
/* if FINAL chunk would fit precisely in space left */
if (chunk == space && llist_empty(&m_llc.queue)) {
if (chunk == space && llist_empty(&m_llc.queue) && !keep_open(fn))
{
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
"would exactly fit into space (%d): because "
"this is a final block, we don't add length "
@ -424,6 +434,7 @@ struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t
/* return data block as message */
break;
}
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d is less "
"than remaining space (%d): add length header to "
"to delimit LLC frame\n", chunk, space);
@ -456,17 +467,19 @@ struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t
m_llc.put_frame(msg->data, msg->len);
bts->llc_frame_sched();
msgb_free(msg);
m_last_dl_drained_fn = -1;
}
/* if we have more data and we have space left */
if (space > 0 && m_llc.frame_length()) {
if (space > 0 && (m_llc.frame_length() || keep_open(fn))) {
li->m = 1; /* we indicate more frames to follow */
continue;
}
/* if we don't have more LLC frames */
if (!m_llc.frame_length()) {
if (!m_llc.frame_length() && !keep_open(fn)) {
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we "
"done.\n");
li->e = 1; /* we cannot extend */
rh->fbi = 1; /* we indicate final block */
request_dl_ack();
set_state(GPRS_RLCMAC_FINISHED);
@ -771,3 +784,26 @@ int gprs_rlcmac_dl_tbf::frames_since_last_poll(unsigned fn) const
return wrapped - 2715648;
}
int gprs_rlcmac_dl_tbf::frames_since_last_drain(unsigned fn) const
{
unsigned wrapped;
if (m_last_dl_drained_fn < 0)
return -1;
wrapped = (fn + 2715648 - m_last_dl_drained_fn) % 2715648;
if (wrapped < 2715648/2)
return wrapped;
else
return wrapped - 2715648;
}
bool gprs_rlcmac_dl_tbf::keep_open(unsigned fn) const
{
int keep_time_frames;
if (bts_data()->dl_tbf_idle_msec <= 0)
return false;
keep_time_frames = msecs_to_frames(bts_data()->dl_tbf_idle_msec);
return frames_since_last_drain(fn) <= keep_time_frames;
}