osmo-pcu/src/pcu_vty.c

1315 lines
36 KiB
C
Raw Normal View History

2012-07-12 12:31:57 +00:00
/* OsmoBTS VTY interface */
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <osmocom/core/tdef.h>
#include <osmocom/vty/tdef_vty.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/misc.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/pcu/pcuif_proto.h>
2012-07-12 12:31:57 +00:00
#include "pcu_vty.h"
#include "gprs_rlcmac.h"
#include <pdch.h>
#include "bts.h"
#include "tbf.h"
#include "pcu_vty_functions.h"
extern void *tall_pcu_ctx;
static const struct value_string pcu_gsmtap_categ_names[] = {
{ PCU_GSMTAP_C_DL_UNKNOWN, "dl-unknown" },
{ PCU_GSMTAP_C_DL_DUMMY, "dl-dummy" },
{ PCU_GSMTAP_C_DL_CTRL, "dl-ctrl" },
{ PCU_GSMTAP_C_DL_DATA_GPRS, "dl-data-gprs" },
{ PCU_GSMTAP_C_DL_DATA_EGPRS, "dl-data-egprs" },
{ PCU_GSMTAP_C_DL_PTCCH, "dl-ptcch" },
{ PCU_GSMTAP_C_DL_AGCH, "dl-agch" },
{ PCU_GSMTAP_C_DL_PCH, "dl-pch" },
{ PCU_GSMTAP_C_UL_UNKNOWN, "ul-unknown" },
{ PCU_GSMTAP_C_UL_DUMMY, "ul-dummy" },
{ PCU_GSMTAP_C_UL_CTRL, "ul-ctrl" },
{ PCU_GSMTAP_C_UL_DATA_GPRS, "ul-data-gprs" },
{ PCU_GSMTAP_C_UL_DATA_EGPRS, "ul-data-egprs" },
{ PCU_GSMTAP_C_UL_RACH, "ul-rach" },
{ 0, NULL }
};
static const struct value_string pcu_gsmtap_categ_help[] = {
{ PCU_GSMTAP_C_DL_UNKNOWN, "Unknown / Unparseable / Erroneous Downlink Blocks" },
{ PCU_GSMTAP_C_DL_DUMMY, "Downlink Dummy Blocks" },
{ PCU_GSMTAP_C_DL_CTRL, "Downlink Control Blocks" },
{ PCU_GSMTAP_C_DL_DATA_GPRS, "Downlink Data Blocks (GPRS)" },
{ PCU_GSMTAP_C_DL_DATA_EGPRS, "Downlink Data Blocks (EGPRS)" },
{ PCU_GSMTAP_C_DL_PTCCH, "Downlink PTCCH Blocks" },
{ PCU_GSMTAP_C_DL_AGCH, "Downlink AGCH Blocks" },
{ PCU_GSMTAP_C_DL_PCH, "Downlink PCH Blocks" },
{ PCU_GSMTAP_C_UL_UNKNOWN, "Unknown / Unparseable / Erroneous Downlink Blocks" },
{ PCU_GSMTAP_C_UL_DUMMY, "Uplink Dummy Blocks" },
{ PCU_GSMTAP_C_UL_CTRL, "Uplink Control Blocks" },
{ PCU_GSMTAP_C_UL_DATA_GPRS, "Uplink Data Blocks (GPRS)" },
{ PCU_GSMTAP_C_UL_DATA_EGPRS, "Uplink Data Blocks (EGPRS)" },
{ PCU_GSMTAP_C_UL_RACH, "Uplink RACH Bursts" },
{ 0, NULL }
};
DEFUN(cfg_pcu_gsmtap_categ, cfg_pcu_gsmtap_categ_cmd, "HIDDEN", "HIDDEN")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
int categ;
categ = get_string_value(pcu_gsmtap_categ_names, argv[0]);
if (categ < 0)
return CMD_WARNING;
bts->gsmtap_categ_mask |= (1 << categ);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_gsmtap_categ, cfg_pcu_no_gsmtap_categ_cmd, "HIDDEN", "HIDDEN")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
int categ;
categ = get_string_value(pcu_gsmtap_categ_names, argv[0]);
if (categ < 0)
return CMD_WARNING;
bts->gsmtap_categ_mask &= ~(1 << categ);
return CMD_SUCCESS;
}
int pcu_vty_go_parent(struct vty *vty)
2012-07-12 12:31:57 +00:00
{
switch (vty->node) {
#if 0
case TRX_NODE:
vty->node = PCU_NODE;
2012-07-12 12:31:57 +00:00
{
struct gsm_bts_trx *trx = vty->index;
vty->index = trx->bts;
}
break;
#endif
default:
vty->node = CONFIG_NODE;
}
return (enum node_type) vty->node;
2012-07-12 12:31:57 +00:00
}
int pcu_vty_is_config_node(struct vty *vty, int node)
{
switch (node) {
case PCU_NODE:
2012-07-12 12:31:57 +00:00
return 1;
default:
return 0;
}
}
static struct cmd_node pcu_node = {
(enum node_type) PCU_NODE,
"%s(config-pcu)# ",
1,
};
static int config_write_pcu(struct vty *vty)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
unsigned int i;
vty_out(vty, "pcu%s", VTY_NEWLINE);
if (bts->egprs_enabled)
vty_out(vty, " egprs only%s", VTY_NEWLINE);
vty_out(vty, " flow-control-interval %d%s", bts->fc_interval,
VTY_NEWLINE);
if (bts->fc_bvc_bucket_size)
vty_out(vty, " flow-control force-bvc-bucket-size %d%s",
bts->fc_bvc_bucket_size, VTY_NEWLINE);
if (bts->fc_bvc_leak_rate)
vty_out(vty, " flow-control force-bvc-leak-rate %d%s",
bts->fc_bvc_leak_rate, VTY_NEWLINE);
if (bts->fc_ms_bucket_size)
vty_out(vty, " flow-control force-ms-bucket-size %d%s",
bts->fc_ms_bucket_size, VTY_NEWLINE);
if (bts->fc_ms_leak_rate)
vty_out(vty, " flow-control force-ms-leak-rate %d%s",
bts->fc_ms_leak_rate, VTY_NEWLINE);
if (bts->force_cs) {
if (bts->initial_cs_ul == bts->initial_cs_dl)
vty_out(vty, " cs %d%s", bts->initial_cs_dl,
VTY_NEWLINE);
else
vty_out(vty, " cs %d %d%s", bts->initial_cs_dl,
bts->initial_cs_ul, VTY_NEWLINE);
}
if (bts->max_cs_dl && bts->max_cs_ul) {
if (bts->max_cs_ul == bts->max_cs_dl)
vty_out(vty, " cs max %d%s", bts->max_cs_dl,
VTY_NEWLINE);
else
vty_out(vty, " cs max %d %d%s", bts->max_cs_dl,
bts->max_cs_ul, VTY_NEWLINE);
}
if (bts->cs_adj_enabled)
vty_out(vty, " cs threshold %d %d%s",
bts->cs_adj_lower_limit, bts->cs_adj_upper_limit,
VTY_NEWLINE);
else
vty_out(vty, " no cs threshold%s", VTY_NEWLINE);
if (bts->cs_downgrade_threshold)
vty_out(vty, " cs downgrade-threshold %d%s",
bts->cs_downgrade_threshold, VTY_NEWLINE);
else
vty_out(vty, " no cs downgrade-threshold%s", VTY_NEWLINE);
vty_out(vty, " cs link-quality-ranges cs1 %d cs2 %d %d cs3 %d %d cs4 %d%s",
bts->cs_lqual_ranges[0].high,
bts->cs_lqual_ranges[1].low,
bts->cs_lqual_ranges[1].high,
bts->cs_lqual_ranges[2].low,
bts->cs_lqual_ranges[2].high,
bts->cs_lqual_ranges[3].low,
VTY_NEWLINE);
vty_out(vty, " mcs link-quality-ranges mcs1 %d mcs2 %d %d mcs3 %d %d mcs4 %d %d mcs5 %d %d mcs6 %d %d mcs7 %d %d mcs8 %d %d mcs9 %d%s",
bts->mcs_lqual_ranges[0].high,
bts->mcs_lqual_ranges[1].low,
bts->mcs_lqual_ranges[1].high,
bts->mcs_lqual_ranges[2].low,
bts->mcs_lqual_ranges[2].high,
bts->mcs_lqual_ranges[3].low,
bts->mcs_lqual_ranges[3].high,
bts->mcs_lqual_ranges[4].low,
bts->mcs_lqual_ranges[4].high,
bts->mcs_lqual_ranges[5].low,
bts->mcs_lqual_ranges[5].high,
bts->mcs_lqual_ranges[6].low,
bts->mcs_lqual_ranges[6].high,
bts->mcs_lqual_ranges[7].low,
bts->mcs_lqual_ranges[7].high,
bts->mcs_lqual_ranges[8].low,
VTY_NEWLINE);
if (bts->initial_mcs_dl != 1 && bts->initial_mcs_ul != 1) {
if (bts->initial_mcs_ul == bts->initial_mcs_dl)
vty_out(vty, " mcs %d%s", bts->initial_mcs_dl,
VTY_NEWLINE);
else
vty_out(vty, " mcs %d %d%s", bts->initial_mcs_dl,
bts->initial_mcs_ul, VTY_NEWLINE);
}
if (bts->max_mcs_dl && bts->max_mcs_ul) {
if (bts->max_mcs_ul == bts->max_mcs_dl)
vty_out(vty, " mcs max %d%s", bts->max_mcs_dl,
VTY_NEWLINE);
else
vty_out(vty, " mcs max %d %d%s", bts->max_mcs_dl,
bts->max_mcs_ul, VTY_NEWLINE);
}
vty_out(vty, " window-size %d %d%s", bts->ws_base, bts->ws_pdch,
VTY_NEWLINE);
if (bts->dl_arq_type)
vty_out(vty, " egprs dl arq-type arq2%s",
VTY_NEWLINE);
if (bts->force_llc_lifetime == 0xffff)
vty_out(vty, " queue lifetime infinite%s", VTY_NEWLINE);
else if (bts->force_llc_lifetime)
vty_out(vty, " queue lifetime %d%s", bts->force_llc_lifetime,
VTY_NEWLINE);
tbf: Use a hysteresis when discarding DL LLC frames Currently single LLC blocks are discarded when the PDU lifetime expires. If an IP packet has been fragmented either on the IP or on the LLC layer and is therefore distributed over several LLC frames, the kept fragments are transmitted and then discarded by the MS because of the missing PDU. This can cause massive IP packet loss when there are many fragmented packets (e.g. when trying 'ping -s1800' or if the GGSN chops downlink IP packets into several SNDCP packets). On the other hand, discarding too many packets might disturb the congestion handling of TCP. Dropping plain TCP ACKs might also hinder flow control and congestion avoidance. This commit adds a hysteresis algorithm to the LLC discard loop. If an LLC message's age reaches the high water mark, further message's with an age above the low water mark are discarded, too. This is aborted, if a GMM, a non-UI, or a small message is detected. In these cases, that message is kept. The following VTY commands are added (pcu config node): - queue hysteresis <1-65535> set the difference between high (lifetime) and low watermark in centiseconds - no queue hysteresis disable this feature (default) Since the SGSN will most probably send all fragments of a single N-PDU without much delay between them, a value slightly above the average transmission delay jitter between SGSN and PCU is probably a sensible value to discard all fragments of a single IP packet. This is an experimental feature that might be replaced by more advanced means of active queue management in the future. Sponsored-by: On-Waves ehf
2015-03-20 11:02:42 +00:00
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);
llc: Use CoDel to drop packages from the LLC queue Currently packets are only dropped if they have reached their maximum life time. This leads to LLC queues being constantly filled under load, increasing the latency up to the maximum life time. This kind of bufferbloat hinders TCP's congestion avoidance algorithms. To keep the queues short, the CoDel active queue management algorithm can be used. This commit changes to llc_dequeue method to apply the CoDel algorithm to selectively drop LLC frames before they passed to the TBF layer to be encoded in BSNs. This feature is currently disabled by default. The CoDel state is managed per MS since the LLC queues are also kept in the MS objects. Note that there is still some buffering in the TBF objects, in the worst case (CS4) 3.5kByte + LLC-MTU octets are stored there. The resulting additional packet delay is not (yet) taken into account for CoDel. Also note that configuration changes are applied to new MS objects only. The following VTY commands are added to the 'pcu' node: - queue codel activates CoDel, the interval is selected by the implementation - queue codel interval <1-1000> activates CoDel with a fixed interval given in centiseconds (10ms-10s) - no queue codel deactivates CoDel Which interval value to use is still an open issue. For high speed links (e.g. Ethernet), CoDel suggests 100ms. For slower links, the expected RTT is recommended. The current implementation uses a default value of 2000ms. Measurements: Note that the following measurements depend on several other factors, most notably the interaction with the SGSN's flow control. They are just examples to give an idea how CoDel might influence some parameters. The measurements have been done with a single E71, first with a running ping only (Idle), then with an additional TCP download of a 360k file (Busy). The CoDel interval was set to 1s. - Idle : ping ~400ms, avg queue delay 0ms, dropped 0 - Busy, No CoDel: ping ~6s, avg queue delay 4-6s, dropped 0, scheduled 948, duration 54s - Busy, CoDel: ping 500-1500ms, avg queue delay ~600ms, dropped 77, scheduled 1040, duration 60s More measurements with two MS downloading in parallel (two independant measurements per case). - Busy, No CoDel: dropped 0, scheduled 1883, duration 121s dropped 19, scheduled 2003, duration 133s - Busy, CoDel: dropped 22, scheduled 1926, duration 116s dropped 22, scheduled 1955, duration 108s Sponsored-by: On-Waves ehf
2015-07-17 14:39:09 +00:00
if (bts->llc_codel_interval_msec == LLC_CODEL_USE_DEFAULT)
vty_out(vty, " queue codel%s", VTY_NEWLINE);
else if (bts->llc_codel_interval_msec == LLC_CODEL_DISABLE)
vty_out(vty, " no queue codel%s", VTY_NEWLINE);
else
vty_out(vty, " queue codel interval %d%s",
bts->llc_codel_interval_msec/10, 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)
vty_out(vty, " alloc-algorithm b%s", VTY_NEWLINE);
if (bts->alloc_algorithm == alloc_algorithm_dynamic)
vty_out(vty, " alloc-algorithm dynamic%s", VTY_NEWLINE);
if (bts->force_two_phase)
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_preemptive_retransmission)
vty_out(vty, " no dl-tbf-preemptive-retransmission%s", VTY_NEWLINE);
if (strcmp(bts->pcu_sock_path, PCU_SOCK_DEFAULT))
vty_out(vty, " pcu-socket %s%s", bts->pcu_sock_path, VTY_NEWLINE);
for (i = 0; i < 32; i++) {
unsigned int cs = (1 << i);
if (bts->gsmtap_categ_mask & cs) {
vty_out(vty, " gsmtap-category %s%s",
get_value_string(pcu_gsmtap_categ_names, i), VTY_NEWLINE);
}
}
if (bts->gb_dialect_sns)
vty_out(vty, " gb-dialect ip-sns%s", VTY_NEWLINE);
else
vty_out(vty, " gb-dialect classic%s", VTY_NEWLINE);
osmo_tdef_vty_write(vty, bts->T_defs_pcu, " timer ");
return CMD_SUCCESS;
}
/* per-BTS configuration */
DEFUN(cfg_pcu,
cfg_pcu_cmd,
"pcu",
"BTS specific configure")
{
vty->node = PCU_NODE;
return CMD_SUCCESS;
}
#define EGPRS_STR "EGPRS configuration\n"
DEFUN(cfg_pcu_egprs,
cfg_pcu_egprs_cmd,
"egprs only",
EGPRS_STR "Use EGPRS and disable plain GPRS\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->egprs_enabled = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_egprs,
cfg_pcu_no_egprs_cmd,
"no egprs",
NO_STR EGPRS_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->egprs_enabled = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_fc_interval,
cfg_pcu_fc_interval_cmd,
"flow-control-interval <1-10>",
"Interval between sending subsequent Flow Control PDUs\n"
"Interval time in seconds\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_interval = atoi(argv[0]);
return CMD_SUCCESS;
}
#define FC_STR "BSSGP Flow Control configuration\n"
#define FC_BMAX_STR(who) "Force a fixed value for the " who " bucket size\n"
#define FC_LR_STR(who) "Force a fixed value for the " who " leak rate\n"
DEFUN(cfg_pcu_fc_bvc_bucket_size,
cfg_pcu_fc_bvc_bucket_size_cmd,
"flow-control force-bvc-bucket-size <1-6553500>",
FC_STR FC_BMAX_STR("BVC") "Bucket size in octets\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_bvc_bucket_size = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_fc_bvc_bucket_size,
cfg_pcu_no_fc_bvc_bucket_size_cmd,
"no flow-control force-bvc-bucket-size",
NO_STR FC_STR FC_BMAX_STR("BVC"))
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_bvc_bucket_size = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_fc_bvc_leak_rate,
cfg_pcu_fc_bvc_leak_rate_cmd,
"flow-control force-bvc-leak-rate <1-6553500>",
FC_STR FC_LR_STR("BVC") "Leak rate in bit/s\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_bvc_leak_rate = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_fc_bvc_leak_rate,
cfg_pcu_no_fc_bvc_leak_rate_cmd,
"no flow-control force-bvc-leak-rate",
NO_STR FC_STR FC_LR_STR("BVC"))
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_bvc_leak_rate = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_fc_ms_bucket_size,
cfg_pcu_fc_ms_bucket_size_cmd,
"flow-control force-ms-bucket-size <1-6553500>",
FC_STR FC_BMAX_STR("default MS") "Bucket size in octets\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_ms_bucket_size = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_fc_ms_bucket_size,
cfg_pcu_no_fc_ms_bucket_size_cmd,
"no flow-control force-ms-bucket-size",
NO_STR FC_STR FC_BMAX_STR("default MS"))
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_ms_bucket_size = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_fc_ms_leak_rate,
cfg_pcu_fc_ms_leak_rate_cmd,
"flow-control force-ms-leak-rate <1-6553500>",
FC_STR FC_LR_STR("default MS") "Leak rate in bit/s\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_ms_leak_rate = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_fc_ms_leak_rate,
cfg_pcu_no_fc_ms_leak_rate_cmd,
"no flow-control force-ms-leak-rate",
NO_STR FC_STR FC_LR_STR("default MS"))
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_ms_leak_rate = 0;
return CMD_SUCCESS;
}
#define FC_BTIME_STR "Set target downlink maximum queueing time (only affects the advertised bucket size)\n"
DEFUN(cfg_pcu_fc_bucket_time,
cfg_pcu_fc_bucket_time_cmd,
"flow-control bucket-time <1-65534>",
FC_STR FC_BTIME_STR "Time in centi-seconds\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_bucket_time = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_fc_bucket_time,
cfg_pcu_no_fc_bucket_time_cmd,
"no flow-control bucket-time",
NO_STR FC_STR FC_BTIME_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_bucket_time = 0;
return CMD_SUCCESS;
}
#define CS_STR "Coding Scheme configuration\n"
DEFUN(cfg_pcu_cs,
cfg_pcu_cs_cmd,
"cs <1-4> [<1-4>]",
CS_STR
"Initial CS value to be used (overrides BTS config)\n"
"Use a different initial CS value for the uplink")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t cs = atoi(argv[0]);
bts->force_cs = 1;
bts->initial_cs_dl = cs;
if (argc > 1)
bts->initial_cs_ul = atoi(argv[1]);
else
bts->initial_cs_ul = cs;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_cs,
cfg_pcu_no_cs_cmd,
"no cs",
NO_STR CS_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->force_cs = 0;
return CMD_SUCCESS;
}
#define CS_MAX_STR "Set maximum values for adaptive CS selection (overrides BTS config)\n"
DEFUN(cfg_pcu_cs_max,
cfg_pcu_cs_max_cmd,
"cs max <1-4> [<1-4>]",
CS_STR
CS_MAX_STR
"Maximum CS value to be used\n"
"Use a different maximum CS value for the uplink")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t cs = atoi(argv[0]);
bts->max_cs_dl = cs;
if (argc > 1)
bts->max_cs_ul = atoi(argv[1]);
else
bts->max_cs_ul = cs;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_cs_max,
cfg_pcu_no_cs_max_cmd,
"no cs max",
NO_STR CS_STR CS_MAX_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->max_cs_dl = 0;
bts->max_cs_ul = 0;
return CMD_SUCCESS;
}
#define MCS_STR "Modulation and Coding Scheme configuration (EGPRS)\n"
DEFUN(cfg_pcu_mcs,
cfg_pcu_mcs_cmd,
"mcs <1-9> [<1-9>]",
MCS_STR
"Initial MCS value to be used (default 1)\n"
"Use a different initial MCS value for the uplink")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t cs = atoi(argv[0]);
bts->initial_mcs_dl = cs;
if (argc > 1)
bts->initial_mcs_ul = atoi(argv[1]);
else
bts->initial_mcs_ul = cs;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_mcs,
cfg_pcu_no_mcs_cmd,
"no mcs",
NO_STR MCS_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->initial_mcs_dl = 1;
bts->initial_mcs_ul = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_mcs_max,
cfg_pcu_mcs_max_cmd,
"mcs max <1-9> [<1-9>]",
MCS_STR
CS_MAX_STR
"Maximum MCS value to be used\n"
"Use a different maximum MCS value for the uplink")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t mcs = atoi(argv[0]);
bts->max_mcs_dl = mcs;
if (argc > 1)
bts->max_mcs_ul = atoi(argv[1]);
else
bts->max_mcs_ul = mcs;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_mcs_max,
cfg_pcu_no_mcs_max_cmd,
"no mcs max",
NO_STR MCS_STR CS_MAX_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->max_mcs_dl = 0;
bts->max_mcs_ul = 0;
return CMD_SUCCESS;
}
#define DL_STR "downlink specific configuration\n"
DEFUN(cfg_pcu_dl_arq_type,
cfg_pcu_dl_arq_cmd,
"egprs dl arq-type (spb|arq2)",
EGPRS_STR DL_STR "ARQ options\n"
"enable SPB(ARQ1) support\n"
"enable ARQ2 support")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
if (!strcmp(argv[0], "arq2"))
bts->dl_arq_type = 1;
else
bts->dl_arq_type = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_window_size,
cfg_pcu_window_size_cmd,
"window-size <0-1024> [<0-256>]",
"Window size configuration (b + N_PDCH * f)\n"
"Base value (b)\n"
"Factor for number of PDCH (f)")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint16_t b = atoi(argv[0]);
bts->ws_base = b;
if (argc > 1) {
uint16_t f = atoi(argv[1]);
bts->ws_pdch = f;
}
return CMD_SUCCESS;
}
#define QUEUE_STR "Packet queue options\n"
#define LIFETIME_STR "Set lifetime limit of LLC frame in centi-seconds " \
"(overrides the value given by SGSN)\n"
DEFUN(cfg_pcu_queue_lifetime,
cfg_pcu_queue_lifetime_cmd,
"queue lifetime <1-65534>",
QUEUE_STR LIFETIME_STR "Lifetime in centi-seconds")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint16_t csec = atoi(argv[0]);
bts->force_llc_lifetime = csec;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_queue_lifetime_inf,
cfg_pcu_queue_lifetime_inf_cmd,
"queue lifetime infinite",
QUEUE_STR LIFETIME_STR "Infinite lifetime")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->force_llc_lifetime = 0xffff;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_queue_lifetime,
cfg_pcu_no_queue_lifetime_cmd,
"no queue lifetime",
NO_STR QUEUE_STR "Disable lifetime limit of LLC frame (use value given "
"by SGSN)\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->force_llc_lifetime = 0;
return CMD_SUCCESS;
}
tbf: Use a hysteresis when discarding DL LLC frames Currently single LLC blocks are discarded when the PDU lifetime expires. If an IP packet has been fragmented either on the IP or on the LLC layer and is therefore distributed over several LLC frames, the kept fragments are transmitted and then discarded by the MS because of the missing PDU. This can cause massive IP packet loss when there are many fragmented packets (e.g. when trying 'ping -s1800' or if the GGSN chops downlink IP packets into several SNDCP packets). On the other hand, discarding too many packets might disturb the congestion handling of TCP. Dropping plain TCP ACKs might also hinder flow control and congestion avoidance. This commit adds a hysteresis algorithm to the LLC discard loop. If an LLC message's age reaches the high water mark, further message's with an age above the low water mark are discarded, too. This is aborted, if a GMM, a non-UI, or a small message is detected. In these cases, that message is kept. The following VTY commands are added (pcu config node): - queue hysteresis <1-65535> set the difference between high (lifetime) and low watermark in centiseconds - no queue hysteresis disable this feature (default) Since the SGSN will most probably send all fragments of a single N-PDU without much delay between them, a value slightly above the average transmission delay jitter between SGSN and PCU is probably a sensible value to discard all fragments of a single IP packet. This is an experimental feature that might be replaced by more advanced means of active queue management in the future. Sponsored-by: On-Waves ehf
2015-03-20 11:02:42 +00:00
#define QUEUE_HYSTERESIS_STR "Set lifetime hysteresis of LLC frame in centi-seconds " \
"(continue discarding until lifetime-hysteresis is reached)\n"
DEFUN(cfg_pcu_queue_hysteresis,
cfg_pcu_queue_hysteresis_cmd,
"queue hysteresis <1-65535>",
tbf: Use a hysteresis when discarding DL LLC frames Currently single LLC blocks are discarded when the PDU lifetime expires. If an IP packet has been fragmented either on the IP or on the LLC layer and is therefore distributed over several LLC frames, the kept fragments are transmitted and then discarded by the MS because of the missing PDU. This can cause massive IP packet loss when there are many fragmented packets (e.g. when trying 'ping -s1800' or if the GGSN chops downlink IP packets into several SNDCP packets). On the other hand, discarding too many packets might disturb the congestion handling of TCP. Dropping plain TCP ACKs might also hinder flow control and congestion avoidance. This commit adds a hysteresis algorithm to the LLC discard loop. If an LLC message's age reaches the high water mark, further message's with an age above the low water mark are discarded, too. This is aborted, if a GMM, a non-UI, or a small message is detected. In these cases, that message is kept. The following VTY commands are added (pcu config node): - queue hysteresis <1-65535> set the difference between high (lifetime) and low watermark in centiseconds - no queue hysteresis disable this feature (default) Since the SGSN will most probably send all fragments of a single N-PDU without much delay between them, a value slightly above the average transmission delay jitter between SGSN and PCU is probably a sensible value to discard all fragments of a single IP packet. This is an experimental feature that might be replaced by more advanced means of active queue management in the future. Sponsored-by: On-Waves ehf
2015-03-20 11:02:42 +00:00
QUEUE_STR QUEUE_HYSTERESIS_STR "Hysteresis in centi-seconds")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint16_t csec = atoi(argv[0]);
tbf: Use a hysteresis when discarding DL LLC frames Currently single LLC blocks are discarded when the PDU lifetime expires. If an IP packet has been fragmented either on the IP or on the LLC layer and is therefore distributed over several LLC frames, the kept fragments are transmitted and then discarded by the MS because of the missing PDU. This can cause massive IP packet loss when there are many fragmented packets (e.g. when trying 'ping -s1800' or if the GGSN chops downlink IP packets into several SNDCP packets). On the other hand, discarding too many packets might disturb the congestion handling of TCP. Dropping plain TCP ACKs might also hinder flow control and congestion avoidance. This commit adds a hysteresis algorithm to the LLC discard loop. If an LLC message's age reaches the high water mark, further message's with an age above the low water mark are discarded, too. This is aborted, if a GMM, a non-UI, or a small message is detected. In these cases, that message is kept. The following VTY commands are added (pcu config node): - queue hysteresis <1-65535> set the difference between high (lifetime) and low watermark in centiseconds - no queue hysteresis disable this feature (default) Since the SGSN will most probably send all fragments of a single N-PDU without much delay between them, a value slightly above the average transmission delay jitter between SGSN and PCU is probably a sensible value to discard all fragments of a single IP packet. This is an experimental feature that might be replaced by more advanced means of active queue management in the future. Sponsored-by: On-Waves ehf
2015-03-20 11:02:42 +00:00
bts->llc_discard_csec = csec;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_queue_hysteresis,
cfg_pcu_no_queue_hysteresis_cmd,
"no queue hysteresis",
NO_STR QUEUE_STR QUEUE_HYSTERESIS_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->llc_discard_csec = 0;
return CMD_SUCCESS;
}
llc: Use CoDel to drop packages from the LLC queue Currently packets are only dropped if they have reached their maximum life time. This leads to LLC queues being constantly filled under load, increasing the latency up to the maximum life time. This kind of bufferbloat hinders TCP's congestion avoidance algorithms. To keep the queues short, the CoDel active queue management algorithm can be used. This commit changes to llc_dequeue method to apply the CoDel algorithm to selectively drop LLC frames before they passed to the TBF layer to be encoded in BSNs. This feature is currently disabled by default. The CoDel state is managed per MS since the LLC queues are also kept in the MS objects. Note that there is still some buffering in the TBF objects, in the worst case (CS4) 3.5kByte + LLC-MTU octets are stored there. The resulting additional packet delay is not (yet) taken into account for CoDel. Also note that configuration changes are applied to new MS objects only. The following VTY commands are added to the 'pcu' node: - queue codel activates CoDel, the interval is selected by the implementation - queue codel interval <1-1000> activates CoDel with a fixed interval given in centiseconds (10ms-10s) - no queue codel deactivates CoDel Which interval value to use is still an open issue. For high speed links (e.g. Ethernet), CoDel suggests 100ms. For slower links, the expected RTT is recommended. The current implementation uses a default value of 2000ms. Measurements: Note that the following measurements depend on several other factors, most notably the interaction with the SGSN's flow control. They are just examples to give an idea how CoDel might influence some parameters. The measurements have been done with a single E71, first with a running ping only (Idle), then with an additional TCP download of a 360k file (Busy). The CoDel interval was set to 1s. - Idle : ping ~400ms, avg queue delay 0ms, dropped 0 - Busy, No CoDel: ping ~6s, avg queue delay 4-6s, dropped 0, scheduled 948, duration 54s - Busy, CoDel: ping 500-1500ms, avg queue delay ~600ms, dropped 77, scheduled 1040, duration 60s More measurements with two MS downloading in parallel (two independant measurements per case). - Busy, No CoDel: dropped 0, scheduled 1883, duration 121s dropped 19, scheduled 2003, duration 133s - Busy, CoDel: dropped 22, scheduled 1926, duration 116s dropped 22, scheduled 1955, duration 108s Sponsored-by: On-Waves ehf
2015-07-17 14:39:09 +00:00
#define QUEUE_CODEL_STR "Set CoDel queue management\n"
DEFUN(cfg_pcu_queue_codel,
cfg_pcu_queue_codel_cmd,
"queue codel",
QUEUE_STR QUEUE_CODEL_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->llc_codel_interval_msec = LLC_CODEL_USE_DEFAULT;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_queue_codel_interval,
cfg_pcu_queue_codel_interval_cmd,
"queue codel interval <1-1000>",
QUEUE_STR QUEUE_CODEL_STR "Specify interval\n" "Interval in centi-seconds")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint16_t csec = atoi(argv[0]);
bts->llc_codel_interval_msec = 10*csec;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_queue_codel,
cfg_pcu_no_queue_codel_cmd,
"no queue codel",
NO_STR QUEUE_STR QUEUE_CODEL_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->llc_codel_interval_msec = LLC_CODEL_DISABLE;
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();
uint16_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;
}
tbf: Use a hysteresis when discarding DL LLC frames Currently single LLC blocks are discarded when the PDU lifetime expires. If an IP packet has been fragmented either on the IP or on the LLC layer and is therefore distributed over several LLC frames, the kept fragments are transmitted and then discarded by the MS because of the missing PDU. This can cause massive IP packet loss when there are many fragmented packets (e.g. when trying 'ping -s1800' or if the GGSN chops downlink IP packets into several SNDCP packets). On the other hand, discarding too many packets might disturb the congestion handling of TCP. Dropping plain TCP ACKs might also hinder flow control and congestion avoidance. This commit adds a hysteresis algorithm to the LLC discard loop. If an LLC message's age reaches the high water mark, further message's with an age above the low water mark are discarded, too. This is aborted, if a GMM, a non-UI, or a small message is detected. In these cases, that message is kept. The following VTY commands are added (pcu config node): - queue hysteresis <1-65535> set the difference between high (lifetime) and low watermark in centiseconds - no queue hysteresis disable this feature (default) Since the SGSN will most probably send all fragments of a single N-PDU without much delay between them, a value slightly above the average transmission delay jitter between SGSN and PCU is probably a sensible value to discard all fragments of a single IP packet. This is an experimental feature that might be replaced by more advanced means of active queue management in the future. Sponsored-by: On-Waves ehf
2015-03-20 11:02:42 +00:00
DEFUN(cfg_pcu_alloc,
cfg_pcu_alloc_cmd,
"alloc-algorithm (a|b|dynamic)",
"Select slot allocation algorithm to use when assigning timeslots on "
"PACCH\n"
"Single slot is assigned only\n"
"Multiple slots are assigned for semi-duplex operation\n"
"Dynamically select the algorithm based on the system state\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
switch (argv[0][0]) {
case 'a':
bts->alloc_algorithm = alloc_algorithm_a;
break;
case 'b':
bts->alloc_algorithm = alloc_algorithm_b;
break;
default:
bts->alloc_algorithm = alloc_algorithm_dynamic;
break;
}
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_two_phase,
cfg_pcu_two_phase_cmd,
"two-phase-access",
"Force two phase access when MS requests single phase access\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->force_two_phase = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_two_phase,
cfg_pcu_no_two_phase_cmd,
"no two-phase-access",
NO_STR "Only use two phase access when requested my MS\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->force_two_phase = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_alpha,
cfg_pcu_alpha_cmd,
"alpha <0-10>",
"Alpha parameter for MS power control in units of 0.1 (see TS 05.08) "
"NOTE: Be sure to set Alpha value at System information 13 too.\n"
"Alpha in units of 0.1\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->alpha = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_gamma,
cfg_pcu_gamma_cmd,
"gamma <0-62>",
"Gamma parameter for MS power control in units of dB (see TS 05.08)\n"
"Gamma in even unit of dBs\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->gamma = atoi(argv[0]) / 2;
return CMD_SUCCESS;
}
DEFUN(show_bts_stats,
show_bts_stats_cmd,
"show bts statistics",
SHOW_STR "BTS related functionality\nStatistics\n")
{
vty_out_rate_ctr_group(vty, "", bts_main_data_stats());
return CMD_SUCCESS;
}
#define IDLE_TIME_STR "keep an idle DL TBF alive for the time given\n"
DEFUN_DEPRECATED(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")
{
vty_out(vty, "%% 'dl-tbf-idle-time' is now deprecated: use 'timer X2031 <val>' instead%s", VTY_NEWLINE);
struct gprs_rlcmac_bts *bts = bts_main_data();
if (osmo_tdef_set(bts->T_defs_pcu, -2031, atoi(argv[0]), OSMO_TDEF_MS) < 0)
return CMD_WARNING;
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(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)
{
vty_out(vty, "%% 'no dl-tbf-idle-time' is now deprecated: use 'timer X2031 0' instead%s", VTY_NEWLINE);
struct gprs_rlcmac_bts *bts = bts_main_data();
if (osmo_tdef_set(bts->T_defs_pcu, -2031, 0, OSMO_TDEF_MS) < 0)
return CMD_WARNING;
return CMD_SUCCESS;
}
#define RETRANSMISSION_STR "retransmit blocks even before the MS had a chance to receive them (better throughput," \
" less readable traces)"
DEFUN(cfg_pcu_dl_tbf_preemptive_retransmission,
cfg_pcu_dl_tbf_preemptive_retransmission_cmd,
"dl-tbf-preemptive-retransmission",
RETRANSMISSION_STR " (enabled by default)")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->dl_tbf_preemptive_retransmission = true;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_dl_tbf_preemptive_retransmission,
cfg_pcu_no_dl_tbf_preemptive_retransmission_cmd,
"no dl-tbf-preemptive-retransmission",
NO_STR RETRANSMISSION_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->dl_tbf_preemptive_retransmission = false;
return CMD_SUCCESS;
}
#define MS_IDLE_TIME_STR "keep an idle MS object alive for the time given\n"
DEFUN_DEPRECATED(cfg_pcu_ms_idle_time,
cfg_pcu_ms_idle_time_cmd,
"ms-idle-time <1-7200>",
MS_IDLE_TIME_STR "idle time in sec")
{
vty_out(vty, "%% 'ms-idle-time' is now deprecated: use 'timer X2030 <val>' instead%s", VTY_NEWLINE);
struct gprs_rlcmac_bts *bts = bts_main_data();
if (osmo_tdef_set(bts->T_defs_pcu, -2030, atoi(argv[0]), OSMO_TDEF_S) < 0)
return CMD_WARNING;
return CMD_SUCCESS;
}
DEFUN_DEPRECATED(cfg_pcu_no_ms_idle_time,
cfg_pcu_no_ms_idle_time_cmd,
"no ms-idle-time",
NO_STR MS_IDLE_TIME_STR)
{
vty_out(vty, "%% 'no ms-idle-time' is now deprecated: use 'timer X2030 0' instead%s", VTY_NEWLINE);
struct gprs_rlcmac_bts *bts = bts_main_data();
if (osmo_tdef_set(bts->T_defs_pcu, -2030, 0, OSMO_TDEF_S) < 0)
return CMD_WARNING;
return CMD_SUCCESS;
}
#define CS_ERR_LIMITS_STR "set thresholds for error rate based downlink (M)CS adjustment\n"
DEFUN(cfg_pcu_cs_err_limits,
cfg_pcu_cs_err_limits_cmd,
"cs threshold <0-100> <0-100>",
CS_STR CS_ERR_LIMITS_STR "lower limit in %\n" "upper limit in %\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t lower_limit = atoi(argv[0]);
uint8_t upper_limit = atoi(argv[1]);
if (lower_limit > upper_limit) {
vty_out(vty,
"The lower limit must be less than or equal to the "
"upper limit.%s", VTY_NEWLINE);
return CMD_WARNING;
}
bts->cs_adj_enabled = 1;
bts->cs_adj_upper_limit = upper_limit;
bts->cs_adj_lower_limit = lower_limit;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_cs_err_limits,
cfg_pcu_no_cs_err_limits_cmd,
"no cs threshold",
NO_STR CS_STR CS_ERR_LIMITS_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->cs_adj_enabled = 0;
bts->cs_adj_upper_limit = 100;
bts->cs_adj_lower_limit = 0;
return CMD_SUCCESS;
}
#define CS_DOWNGRADE_STR "set threshold for data size based downlink (M)CS downgrade\n"
DEFUN(cfg_pcu_cs_downgrade_thrsh,
cfg_pcu_cs_downgrade_thrsh_cmd,
"cs downgrade-threshold <1-10000>",
CS_STR CS_DOWNGRADE_STR "downgrade if less octets left\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->cs_downgrade_threshold = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_no_cs_downgrade_thrsh,
cfg_pcu_no_cs_downgrade_thrsh_cmd,
"no cs downgrade-threshold",
NO_STR CS_STR CS_DOWNGRADE_STR)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->cs_downgrade_threshold = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_cs_lqual_ranges,
cfg_pcu_cs_lqual_ranges_cmd,
"cs link-quality-ranges cs1 <0-35> cs2 <0-35> <0-35> cs3 <0-35> <0-35> cs4 <0-35>",
CS_STR "Set link quality ranges for each uplink CS\n"
"Set quality range for CS-1 (high value only)\n"
"CS-1 high (dB)\n"
"Set quality range for CS-2\n"
"CS-2 low (dB)\n"
"CS-2 high (dB)\n"
"Set quality range for CS-3\n"
"CS-3 low (dB)\n"
"CS-3 high (dB)\n"
"Set quality range for CS-4 (low value only)\n"
"CS-4 low (dB)\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t cs1_high = atoi(argv[0]);
uint8_t cs2_low = atoi(argv[1]);
uint8_t cs2_high = atoi(argv[2]);
uint8_t cs3_low = atoi(argv[3]);
uint8_t cs3_high = atoi(argv[4]);
uint8_t cs4_low = atoi(argv[5]);
bts->cs_lqual_ranges[0].high = cs1_high;
bts->cs_lqual_ranges[1].low = cs2_low;
bts->cs_lqual_ranges[1].high = cs2_high;
bts->cs_lqual_ranges[2].low = cs3_low;
bts->cs_lqual_ranges[2].high = cs3_high;
bts->cs_lqual_ranges[3].low = cs4_low;
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_mcs_lqual_ranges,
cfg_pcu_mcs_lqual_ranges_cmd,
"mcs link-quality-ranges mcs1 <0-35> mcs2 <0-35> <0-35> mcs3 <0-35> <0-35> mcs4 <0-35> <0-35> mcs5 <0-35> <0-35> mcs6 <0-35> <0-35> mcs7 <0-35> <0-35> mcs8 <0-35> <0-35> mcs9 <0-35>",
CS_STR "Set link quality ranges for each uplink MCS\n"
"Set quality range for MCS-1 (high value only)\n"
"MCS-1 high (dB)\n"
"Set quality range for MCS-2\n"
"MCS-2 high (dB)\n"
"MCS-2 low (dB)\n"
"Set quality range for MCS-3\n"
"MCS-3 high (dB)\n"
"MCS-3 low (dB)\n"
"Set quality range for MCS-4\n"
"MCS-4 high (dB)\n"
"MCS-4 low (dB)\n"
"Set quality range for MCS-5\n"
"MCS-5 high (dB)\n"
"MCS-5 low (dB)\n"
"Set quality range for MCS-6\n"
"MCS-6 low (dB)\n"
"MCS-6 high (dB)\n"
"Set quality range for MCS-7\n"
"MCS-7 low (dB)\n"
"MCS-7 high (dB)\n"
"Set quality range for MCS-8\n"
"MCS-8 low (dB)\n"
"MCS-8 high (dB)\n"
"Set quality range for MCS-9 (low value only)\n"
"MCS-9 low (dB)\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->mcs_lqual_ranges[0].high = atoi(argv[0]);
bts->mcs_lqual_ranges[1].low = atoi(argv[1]);
bts->mcs_lqual_ranges[1].high = atoi(argv[2]);
bts->mcs_lqual_ranges[2].low = atoi(argv[3]);
bts->mcs_lqual_ranges[2].high = atoi(argv[4]);
bts->mcs_lqual_ranges[3].low = atoi(argv[5]);
bts->mcs_lqual_ranges[3].high = atoi(argv[6]);
bts->mcs_lqual_ranges[4].low = atoi(argv[7]);
bts->mcs_lqual_ranges[4].high = atoi(argv[8]);
bts->mcs_lqual_ranges[5].low = atoi(argv[9]);
bts->mcs_lqual_ranges[5].high = atoi(argv[10]);
bts->mcs_lqual_ranges[6].low = atoi(argv[11]);
bts->mcs_lqual_ranges[6].high = atoi(argv[12]);
bts->mcs_lqual_ranges[7].low = atoi(argv[13]);
bts->mcs_lqual_ranges[7].high = atoi(argv[14]);
bts->mcs_lqual_ranges[8].low = atoi(argv[15]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_sock,
cfg_pcu_sock_cmd,
"pcu-socket PATH",
"Configure the osmo-bts PCU socket file/path name\n"
"Path of the socket to connect to\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
if (vty->type != VTY_FILE)
vty_out(vty, "Changing PCU socket path at run-time has no effect%s", VTY_NEWLINE);
if (bts->pcu_sock_path) {
talloc_free(bts->pcu_sock_path);
}
bts->pcu_sock_path = talloc_strdup(tall_pcu_ctx, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_pcu_gb_dialect,
cfg_pcu_gb_dialect_cmd,
"gb-dialect (classic|ip-sns)",
"Select which Gb interface dialect to use\n"
"Classic Gb interface with NS-{RESET,BLOCK,UNBLOCK} and static configuration\n"
"Modern Gb interface with IP-SNS (Sub Network Service) and dynamic configuration\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
if (!strcmp(argv[0], "ip-sns"))
bts->gb_dialect_sns = true;
else
bts->gb_dialect_sns = false;
return CMD_SUCCESS;
}
DEFUN(show_bts_timer, show_bts_timer_cmd,
"show bts-timer " OSMO_TDEF_VTY_ARG_T_OPTIONAL,
SHOW_STR "Show BTS controlled timers\n"
OSMO_TDEF_VTY_DOC_T)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
const char *T_arg = argc > 0 ? argv[0] : NULL;
return osmo_tdef_vty_show_cmd(vty, bts->T_defs_bts, T_arg, NULL);
}
DEFUN(show_timer, show_timer_cmd,
"show timer " OSMO_TDEF_VTY_ARG_T_OPTIONAL,
SHOW_STR "Show PCU timers\n"
OSMO_TDEF_VTY_DOC_T)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
const char *T_arg = argc > 0 ? argv[0] : NULL;
return osmo_tdef_vty_show_cmd(vty, bts->T_defs_pcu, T_arg, NULL);
}
DEFUN(cfg_pcu_timer, cfg_pcu_timer_cmd,
"timer " OSMO_TDEF_VTY_ARG_SET_OPTIONAL,
"Configure or show PCU timers\n"
OSMO_TDEF_VTY_DOC_SET)
{
struct gprs_rlcmac_bts *bts = bts_main_data();
/* If any arguments are missing, redirect to 'show' */
if (argc < 2)
return show_timer(self, vty, argc, argv);
return osmo_tdef_vty_set_cmd(vty, bts->T_defs_pcu, argv);
}
DEFUN(show_tbf,
show_tbf_cmd,
"show tbf (all|ccch|pacch)",
SHOW_STR "information about TBFs\n"
"All TBFs\n"
"TBFs allocated via CCCH\n"
"TBFs allocated via PACCH\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
uint32_t flags = UINT32_MAX;
if (argv[0][0] == 'c')
flags = (1 << GPRS_RLCMAC_FLAG_CCCH);
else if (argv[0][0] == 'p')
flags = (1 << GPRS_RLCMAC_FLAG_PACCH);
return pcu_vty_show_tbf_all(vty, bts, flags);
}
DEFUN(show_ms_all,
show_ms_all_cmd,
"show ms all",
SHOW_STR "information about MSs\n" "All TBFs\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
return pcu_vty_show_ms_all(vty, bts);
}
DEFUN(show_ms_tlli,
show_ms_tlli_cmd,
"show ms tlli TLLI",
SHOW_STR "information about MSs\n" "Select MS by TLLI\n" "TLLI as hex\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
char *endp = NULL;
unsigned long long tlli = strtoll(argv[0], &endp, 16);
if ((endp != NULL && *endp != 0) || tlli > 0xffffffffULL) {
vty_out(vty, "Invalid TLLI.%s", VTY_NEWLINE);
return CMD_WARNING;
}
return pcu_vty_show_ms_by_tlli(vty, bts, (uint32_t)tlli);
}
DEFUN(show_ms_imsi,
show_ms_imsi_cmd,
"show ms imsi IMSI",
SHOW_STR "information about MSs\n" "Select MS by IMSI\n" "IMSI\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
return pcu_vty_show_ms_by_imsi(vty, bts, argv[0]);
}
2012-07-12 12:31:57 +00:00
static const char pcu_copyright[] =
2013-01-17 11:24:29 +00:00
"Copyright (C) 2012 by Ivan Kluchnikov <kluchnikovi@gmail.com> and \r\n"
" Andreas Eversberg <jolly@eversberg.eu>\r\n"
2012-07-12 12:31:57 +00:00
"License GNU GPL version 2 or later\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n";
struct vty_app_info pcu_vty_info = {
.name = "Osmo-PCU",
.version = PACKAGE_VERSION,
.copyright = pcu_copyright,
.go_parent_cb = pcu_vty_go_parent,
.is_config_node = pcu_vty_is_config_node,
};
int pcu_vty_init(void)
2012-07-12 12:31:57 +00:00
{
// install_element_ve(&show_pcu_cmd);
cfg_pcu_gsmtap_categ_cmd.string = vty_cmd_string_from_valstr(tall_pcu_ctx, pcu_gsmtap_categ_names,
"gsmtap-category (",
"|",")", VTY_DO_LOWER);
cfg_pcu_gsmtap_categ_cmd.doc = vty_cmd_string_from_valstr(tall_pcu_ctx, pcu_gsmtap_categ_help,
"GSMTAP Category\n",
"\n", "", 0);
cfg_pcu_no_gsmtap_categ_cmd.string = vty_cmd_string_from_valstr(tall_pcu_ctx, pcu_gsmtap_categ_names,
"no gsmtap-category (",
"|",")", VTY_DO_LOWER);
cfg_pcu_no_gsmtap_categ_cmd.doc = vty_cmd_string_from_valstr(tall_pcu_ctx, pcu_gsmtap_categ_help,
NO_STR "GSMTAP Category\n",
"\n", "", 0);
logging_vty_add_cmds();
osmo_stats_vty_add_cmds();
2012-07-12 12:31:57 +00:00
install_node(&pcu_node, config_write_pcu);
install_element(CONFIG_NODE, &cfg_pcu_cmd);
install_element(PCU_NODE, &cfg_pcu_egprs_cmd);
install_element(PCU_NODE, &cfg_pcu_no_egprs_cmd);
install_element(PCU_NODE, &cfg_pcu_no_two_phase_cmd);
install_element(PCU_NODE, &cfg_pcu_cs_cmd);
install_element(PCU_NODE, &cfg_pcu_no_cs_cmd);
install_element(PCU_NODE, &cfg_pcu_cs_max_cmd);
install_element(PCU_NODE, &cfg_pcu_no_cs_max_cmd);
install_element(PCU_NODE, &cfg_pcu_cs_err_limits_cmd);
install_element(PCU_NODE, &cfg_pcu_no_cs_err_limits_cmd);
install_element(PCU_NODE, &cfg_pcu_cs_downgrade_thrsh_cmd);
install_element(PCU_NODE, &cfg_pcu_no_cs_downgrade_thrsh_cmd);
install_element(PCU_NODE, &cfg_pcu_cs_lqual_ranges_cmd);
install_element(PCU_NODE, &cfg_pcu_mcs_lqual_ranges_cmd);
install_element(PCU_NODE, &cfg_pcu_mcs_cmd);
install_element(PCU_NODE, &cfg_pcu_dl_arq_cmd);
install_element(PCU_NODE, &cfg_pcu_no_mcs_cmd);
install_element(PCU_NODE, &cfg_pcu_mcs_max_cmd);
install_element(PCU_NODE, &cfg_pcu_no_mcs_max_cmd);
install_element(PCU_NODE, &cfg_pcu_window_size_cmd);
install_element(PCU_NODE, &cfg_pcu_queue_lifetime_cmd);
install_element(PCU_NODE, &cfg_pcu_queue_lifetime_inf_cmd);
install_element(PCU_NODE, &cfg_pcu_no_queue_lifetime_cmd);
tbf: Use a hysteresis when discarding DL LLC frames Currently single LLC blocks are discarded when the PDU lifetime expires. If an IP packet has been fragmented either on the IP or on the LLC layer and is therefore distributed over several LLC frames, the kept fragments are transmitted and then discarded by the MS because of the missing PDU. This can cause massive IP packet loss when there are many fragmented packets (e.g. when trying 'ping -s1800' or if the GGSN chops downlink IP packets into several SNDCP packets). On the other hand, discarding too many packets might disturb the congestion handling of TCP. Dropping plain TCP ACKs might also hinder flow control and congestion avoidance. This commit adds a hysteresis algorithm to the LLC discard loop. If an LLC message's age reaches the high water mark, further message's with an age above the low water mark are discarded, too. This is aborted, if a GMM, a non-UI, or a small message is detected. In these cases, that message is kept. The following VTY commands are added (pcu config node): - queue hysteresis <1-65535> set the difference between high (lifetime) and low watermark in centiseconds - no queue hysteresis disable this feature (default) Since the SGSN will most probably send all fragments of a single N-PDU without much delay between them, a value slightly above the average transmission delay jitter between SGSN and PCU is probably a sensible value to discard all fragments of a single IP packet. This is an experimental feature that might be replaced by more advanced means of active queue management in the future. Sponsored-by: On-Waves ehf
2015-03-20 11:02:42 +00:00
install_element(PCU_NODE, &cfg_pcu_queue_hysteresis_cmd);
install_element(PCU_NODE, &cfg_pcu_no_queue_hysteresis_cmd);
llc: Use CoDel to drop packages from the LLC queue Currently packets are only dropped if they have reached their maximum life time. This leads to LLC queues being constantly filled under load, increasing the latency up to the maximum life time. This kind of bufferbloat hinders TCP's congestion avoidance algorithms. To keep the queues short, the CoDel active queue management algorithm can be used. This commit changes to llc_dequeue method to apply the CoDel algorithm to selectively drop LLC frames before they passed to the TBF layer to be encoded in BSNs. This feature is currently disabled by default. The CoDel state is managed per MS since the LLC queues are also kept in the MS objects. Note that there is still some buffering in the TBF objects, in the worst case (CS4) 3.5kByte + LLC-MTU octets are stored there. The resulting additional packet delay is not (yet) taken into account for CoDel. Also note that configuration changes are applied to new MS objects only. The following VTY commands are added to the 'pcu' node: - queue codel activates CoDel, the interval is selected by the implementation - queue codel interval <1-1000> activates CoDel with a fixed interval given in centiseconds (10ms-10s) - no queue codel deactivates CoDel Which interval value to use is still an open issue. For high speed links (e.g. Ethernet), CoDel suggests 100ms. For slower links, the expected RTT is recommended. The current implementation uses a default value of 2000ms. Measurements: Note that the following measurements depend on several other factors, most notably the interaction with the SGSN's flow control. They are just examples to give an idea how CoDel might influence some parameters. The measurements have been done with a single E71, first with a running ping only (Idle), then with an additional TCP download of a 360k file (Busy). The CoDel interval was set to 1s. - Idle : ping ~400ms, avg queue delay 0ms, dropped 0 - Busy, No CoDel: ping ~6s, avg queue delay 4-6s, dropped 0, scheduled 948, duration 54s - Busy, CoDel: ping 500-1500ms, avg queue delay ~600ms, dropped 77, scheduled 1040, duration 60s More measurements with two MS downloading in parallel (two independant measurements per case). - Busy, No CoDel: dropped 0, scheduled 1883, duration 121s dropped 19, scheduled 2003, duration 133s - Busy, CoDel: dropped 22, scheduled 1926, duration 116s dropped 22, scheduled 1955, duration 108s Sponsored-by: On-Waves ehf
2015-07-17 14:39:09 +00:00
install_element(PCU_NODE, &cfg_pcu_queue_codel_cmd);
install_element(PCU_NODE, &cfg_pcu_queue_codel_interval_cmd);
install_element(PCU_NODE, &cfg_pcu_no_queue_codel_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);
install_element(PCU_NODE, &cfg_pcu_fc_bucket_time_cmd);
install_element(PCU_NODE, &cfg_pcu_no_fc_bucket_time_cmd);
install_element(PCU_NODE, &cfg_pcu_fc_bvc_bucket_size_cmd);
install_element(PCU_NODE, &cfg_pcu_no_fc_bvc_bucket_size_cmd);
install_element(PCU_NODE, &cfg_pcu_fc_bvc_leak_rate_cmd);
install_element(PCU_NODE, &cfg_pcu_no_fc_bvc_leak_rate_cmd);
install_element(PCU_NODE, &cfg_pcu_fc_ms_bucket_size_cmd);
install_element(PCU_NODE, &cfg_pcu_no_fc_ms_bucket_size_cmd);
install_element(PCU_NODE, &cfg_pcu_fc_ms_leak_rate_cmd);
install_element(PCU_NODE, &cfg_pcu_no_fc_ms_leak_rate_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, &cfg_pcu_dl_tbf_preemptive_retransmission_cmd);
install_element(PCU_NODE, &cfg_pcu_no_dl_tbf_preemptive_retransmission_cmd);
install_element(PCU_NODE, &cfg_pcu_ms_idle_time_cmd);
install_element(PCU_NODE, &cfg_pcu_no_ms_idle_time_cmd);
install_element(PCU_NODE, &cfg_pcu_gsmtap_categ_cmd);
install_element(PCU_NODE, &cfg_pcu_no_gsmtap_categ_cmd);
install_element(PCU_NODE, &cfg_pcu_sock_cmd);
install_element(PCU_NODE, &cfg_pcu_gb_dialect_cmd);
install_element(PCU_NODE, &cfg_pcu_timer_cmd);
install_element_ve(&show_bts_stats_cmd);
install_element_ve(&show_tbf_cmd);
install_element_ve(&show_ms_all_cmd);
install_element_ve(&show_ms_tlli_cmd);
install_element_ve(&show_ms_imsi_cmd);
install_element_ve(&show_bts_timer_cmd);
install_element_ve(&show_timer_cmd);
2012-07-12 12:31:57 +00:00
return 0;
}