diff --git a/src/bts.h b/src/bts.h index 1a6d259a..ec17ef21 100644 --- a/src/bts.h +++ b/src/bts.h @@ -36,6 +36,9 @@ extern "C" { #include +#define LLC_CODEL_DISABLE 0 +#define LLC_CODEL_USE_DEFAULT (-1) + struct BTS; struct GprsMs; @@ -152,6 +155,7 @@ struct gprs_rlcmac_bts { uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */ uint32_t llc_discard_csec; uint32_t llc_idle_ack_csec; + uint32_t llc_codel_interval_msec; /* 0=disabled, -1=use default interval */ uint8_t t3142; uint8_t t3169; uint8_t t3191; diff --git a/src/gprs_ms.cpp b/src/gprs_ms.cpp index b36c61db..e0023e3d 100644 --- a/src/gprs_ms.cpp +++ b/src/gprs_ms.cpp @@ -24,6 +24,7 @@ #include "bts.h" #include "tbf.h" #include "gprs_debug.h" +#include "gprs_codel.h" #include @@ -32,6 +33,8 @@ extern "C" { #include } +#define GPRS_CODEL_SLOW_INTERVAL_MS 2000 + extern void *tall_pcu_ctx; static int64_t now_msec() @@ -102,8 +105,11 @@ GprsMs::GprsMs(BTS *bts, uint32_t tlli) : m_nack_rate_dl(0), m_reserved_dl_slots(0), m_reserved_ul_slots(0), - m_current_trx(NULL) + m_current_trx(NULL), + m_codel_state(NULL) { + int codel_interval = LLC_CODEL_USE_DEFAULT; + LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli); m_imsi[0] = 0; @@ -118,6 +124,16 @@ GprsMs::GprsMs(BTS *bts, uint32_t tlli) : m_current_cs_dl = m_bts->bts_data()->initial_cs_dl; if (m_current_cs_dl < 1) m_current_cs_dl = 1; + + codel_interval = m_bts->bts_data()->llc_codel_interval_msec; + } + + if (codel_interval) { + if (codel_interval == LLC_CODEL_USE_DEFAULT) + codel_interval = GPRS_CODEL_SLOW_INTERVAL_MS; + m_codel_state = talloc(this, struct gprs_codel); + gprs_codel_init(m_codel_state); + gprs_codel_set_interval(m_codel_state, codel_interval); } m_last_cs_not_low = now_msec(); } diff --git a/src/gprs_ms.h b/src/gprs_ms.h index 1a135cbb..185ffd23 100644 --- a/src/gprs_ms.h +++ b/src/gprs_ms.h @@ -23,6 +23,7 @@ struct gprs_rlcmac_tbf; struct gprs_rlcmac_dl_tbf; struct gprs_rlcmac_ul_tbf; +struct gprs_codel; #include "cxx_linuxlist.h" #include "llc.h" @@ -92,6 +93,7 @@ public: gprs_llc_queue *llc_queue(); const gprs_llc_queue *llc_queue() const; + gprs_codel *codel_state() const; void set_timeout(unsigned secs); @@ -157,6 +159,8 @@ private: uint8_t m_reserved_dl_slots; uint8_t m_reserved_ul_slots; gprs_rlcmac_trx *m_current_trx; + + struct gprs_codel *m_codel_state; }; inline uint32_t GprsMs::tlli() const @@ -207,6 +211,11 @@ inline const gprs_llc_queue *GprsMs::llc_queue() const return &m_llc_queue; } +inline gprs_codel *GprsMs::codel_state() const +{ + return m_codel_state; +} + inline unsigned GprsMs::nack_rate_dl() const { return m_nack_rate_dl; diff --git a/src/pcu_vty.c b/src/pcu_vty.c index ed708f8d..94f89b56 100644 --- a/src/pcu_vty.c +++ b/src/pcu_vty.c @@ -116,6 +116,14 @@ static int config_write_pcu(struct vty *vty) if (bts->llc_idle_ack_csec) vty_out(vty, " queue idle-ack-delay %d%s", bts->llc_idle_ack_csec, VTY_NEWLINE); + 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) @@ -419,6 +427,46 @@ DEFUN(cfg_pcu_no_queue_hysteresis, return CMD_SUCCESS; } +#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, @@ -776,6 +824,9 @@ 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_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); diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp index cdd02bae..4739a500 100644 --- a/src/tbf_dl.cpp +++ b/src/tbf_dl.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "pcu_utils.h" @@ -36,6 +37,7 @@ extern "C" { #include #include +#include /* After sending these frames, we poll for ack/nack. */ #define POLL_ACK_AFTER_FRAMES 20 @@ -249,6 +251,13 @@ struct msgb *gprs_rlcmac_dl_tbf::llc_dequeue(bssgp_bvc_ctx *bctx) gprs_bssgp_update_queue_delay(tv_recv, &tv_now); + if (ms() && ms()->codel_state()) { + int bytes = llc_queue()->octets(); + if (gprs_codel_control(ms()->codel_state(), + tv_recv, &tv_now, bytes)) + goto drop_frame; + } + /* Is the age below the low water mark? */ if (!gprs_llc_queue::is_frame_expired(&tv_now2, tv_disc)) break; @@ -274,6 +283,7 @@ struct msgb *gprs_rlcmac_dl_tbf::llc_dequeue(bssgp_bvc_ctx *bctx) } bts->llc_timedout_frame(); +drop_frame: frames++; octets += msg->len; msgb_free(msg);