mirror of https://github.com/PentHertz/srsLTE.git
sched,bugfix - fix bitmask formatting. fix msg3 adaptive retx pdcch allocation
This commit is contained in:
parent
7447fefd19
commit
d1483dc0f8
|
@ -362,7 +362,7 @@ struct formatter<srslte::bounded_bitset<N, reversed> > {
|
||||||
auto parse(ParseContext& ctx) -> decltype(ctx.begin())
|
auto parse(ParseContext& ctx) -> decltype(ctx.begin())
|
||||||
{
|
{
|
||||||
auto it = ctx.begin();
|
auto it = ctx.begin();
|
||||||
while (*it != '\0' and *it != '}') {
|
while (it != ctx.end() and *it != '}') {
|
||||||
if (*it == 'x') {
|
if (*it == 'x') {
|
||||||
mode = hexadecimal;
|
mode = hexadecimal;
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,7 @@ public:
|
||||||
dl_ctrl_alloc_t alloc_dl_ctrl(uint32_t aggr_lvl, alloc_type_t alloc_type);
|
dl_ctrl_alloc_t alloc_dl_ctrl(uint32_t aggr_lvl, alloc_type_t alloc_type);
|
||||||
alloc_outcome_t alloc_dl_data(sched_ue* user, const rbgmask_t& user_mask, bool has_pusch_grant);
|
alloc_outcome_t alloc_dl_data(sched_ue* user, const rbgmask_t& user_mask, bool has_pusch_grant);
|
||||||
bool reserve_dl_rbgs(uint32_t start_rbg, uint32_t end_rbg);
|
bool reserve_dl_rbgs(uint32_t start_rbg, uint32_t end_rbg);
|
||||||
alloc_outcome_t alloc_ul_data(sched_ue* user, prb_interval alloc, bool needs_pdcch);
|
alloc_outcome_t alloc_ul_data(sched_ue* user, prb_interval alloc, bool needs_pdcch, bool strict = true);
|
||||||
alloc_outcome_t reserve_ul_prbs(const prbmask_t& prbmask, bool strict);
|
alloc_outcome_t reserve_ul_prbs(const prbmask_t& prbmask, bool strict);
|
||||||
alloc_outcome_t reserve_ul_prbs(prb_interval alloc, bool strict);
|
alloc_outcome_t reserve_ul_prbs(prb_interval alloc, bool strict);
|
||||||
bool find_ul_alloc(uint32_t L, prb_interval* alloc) const;
|
bool find_ul_alloc(uint32_t L, prb_interval* alloc) const;
|
||||||
|
@ -174,15 +174,15 @@ public:
|
||||||
uint32_t pid;
|
uint32_t pid;
|
||||||
};
|
};
|
||||||
struct ul_alloc_t {
|
struct ul_alloc_t {
|
||||||
enum type_t { NEWTX, NOADAPT_RETX, ADAPT_RETX, MSG3, MSG3_RETX };
|
enum type_t { NEWTX, NOADAPT_RETX, ADAPT_RETX };
|
||||||
|
bool is_msg3;
|
||||||
size_t dci_idx;
|
size_t dci_idx;
|
||||||
type_t type;
|
type_t type;
|
||||||
uint16_t rnti;
|
uint16_t rnti;
|
||||||
prb_interval alloc;
|
prb_interval alloc;
|
||||||
int msg3_mcs = -1;
|
int msg3_mcs = -1;
|
||||||
bool is_retx() const { return type == NOADAPT_RETX or type == ADAPT_RETX; }
|
bool is_retx() const { return type == NOADAPT_RETX or type == ADAPT_RETX; }
|
||||||
bool is_msg3() const { return type == MSG3; }
|
bool needs_pdcch() const { return (type == NEWTX and not is_msg3) or type == ADAPT_RETX; }
|
||||||
bool needs_pdcch() const { return type == NEWTX or type == ADAPT_RETX; }
|
|
||||||
};
|
};
|
||||||
struct pending_msg3_t {
|
struct pending_msg3_t {
|
||||||
uint16_t rnti = 0;
|
uint16_t rnti = 0;
|
||||||
|
@ -212,7 +212,8 @@ public:
|
||||||
|
|
||||||
// UL alloc methods
|
// UL alloc methods
|
||||||
alloc_outcome_t alloc_msg3(sched_ue* user, const sched_interface::dl_sched_rar_grant_t& rargrant);
|
alloc_outcome_t alloc_msg3(sched_ue* user, const sched_interface::dl_sched_rar_grant_t& rargrant);
|
||||||
alloc_outcome_t alloc_ul(sched_ue* user, prb_interval alloc, ul_alloc_t::type_t alloc_type, int msg3_mcs = -1);
|
alloc_outcome_t
|
||||||
|
alloc_ul(sched_ue* user, prb_interval alloc, ul_alloc_t::type_t alloc_type, bool is_msg3 = false, int msg3_mcs = -1);
|
||||||
bool reserve_ul_prbs(const prbmask_t& ulmask, bool strict) { return tti_alloc.reserve_ul_prbs(ulmask, strict); }
|
bool reserve_ul_prbs(const prbmask_t& ulmask, bool strict) { return tti_alloc.reserve_ul_prbs(ulmask, strict); }
|
||||||
bool alloc_phich(sched_ue* user, sched_interface::ul_sched_res_t* ul_sf_result);
|
bool alloc_phich(sched_ue* user, sched_interface::ul_sched_res_t* ul_sf_result);
|
||||||
|
|
||||||
|
@ -232,6 +233,7 @@ public:
|
||||||
bool is_dl_alloc(uint16_t rnti) const;
|
bool is_dl_alloc(uint16_t rnti) const;
|
||||||
bool is_ul_alloc(uint16_t rnti) const;
|
bool is_ul_alloc(uint16_t rnti) const;
|
||||||
uint32_t get_enb_cc_idx() const { return cc_cfg->enb_cc_idx; }
|
uint32_t get_enb_cc_idx() const { return cc_cfg->enb_cc_idx; }
|
||||||
|
const sched_cell_params_t* get_cc_cfg() const { return cc_cfg; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ctrl_code_t alloc_dl_ctrl(uint32_t aggr_lvl, uint32_t tbs_bytes, uint16_t rnti);
|
ctrl_code_t alloc_dl_ctrl(uint32_t aggr_lvl, uint32_t tbs_bytes, uint16_t rnti);
|
||||||
|
|
|
@ -232,7 +232,7 @@ alloc_outcome_t sf_grid_t::alloc_dl_data(sched_ue* user, const rbgmask_t& user_m
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
alloc_outcome_t sf_grid_t::alloc_ul_data(sched_ue* user, prb_interval alloc, bool needs_pdcch)
|
alloc_outcome_t sf_grid_t::alloc_ul_data(sched_ue* user, prb_interval alloc, bool needs_pdcch, bool strict)
|
||||||
{
|
{
|
||||||
if (alloc.stop() > ul_mask.size()) {
|
if (alloc.stop() > ul_mask.size()) {
|
||||||
return alloc_outcome_t::ERROR;
|
return alloc_outcome_t::ERROR;
|
||||||
|
@ -240,7 +240,7 @@ alloc_outcome_t sf_grid_t::alloc_ul_data(sched_ue* user, prb_interval alloc, boo
|
||||||
|
|
||||||
prbmask_t newmask(ul_mask.size());
|
prbmask_t newmask(ul_mask.size());
|
||||||
newmask.fill(alloc.start(), alloc.stop());
|
newmask.fill(alloc.start(), alloc.stop());
|
||||||
if ((ul_mask & newmask).any()) {
|
if (strict and (ul_mask & newmask).any()) {
|
||||||
return alloc_outcome_t::RB_COLLISION;
|
return alloc_outcome_t::RB_COLLISION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,7 +503,7 @@ std::pair<alloc_outcome_t, uint32_t> sf_sched::alloc_rar(uint32_t aggr_lvl, cons
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ret.first != alloc_outcome_t::SUCCESS) {
|
if (ret.first != alloc_outcome_t::SUCCESS) {
|
||||||
logger.info("SCHED: Failed to allocate RAR due to lack of RBs");
|
logger.info("SCHED: RAR allocation postponed due to lack of RBs");
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -606,7 +606,8 @@ alloc_outcome_t sf_sched::alloc_dl_user(sched_ue* user, const rbgmask_t& user_ma
|
||||||
return alloc_outcome_t::SUCCESS;
|
return alloc_outcome_t::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
alloc_outcome_t sf_sched::alloc_ul(sched_ue* user, prb_interval alloc, ul_alloc_t::type_t alloc_type, int msg3_mcs)
|
alloc_outcome_t
|
||||||
|
sf_sched::alloc_ul(sched_ue* user, prb_interval alloc, ul_alloc_t::type_t alloc_type, bool is_msg3, int msg3_mcs)
|
||||||
{
|
{
|
||||||
if (ul_data_allocs.size() >= sched_interface::MAX_DATA_LIST) {
|
if (ul_data_allocs.size() >= sched_interface::MAX_DATA_LIST) {
|
||||||
logger.warning("SCHED: Maximum number of UL allocations reached");
|
logger.warning("SCHED: Maximum number of UL allocations reached");
|
||||||
|
@ -620,25 +621,21 @@ alloc_outcome_t sf_sched::alloc_ul(sched_ue* user, prb_interval alloc, ul_alloc_
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if there is no collision with measGap
|
// Check if there is no collision with measGap
|
||||||
bool needs_pdcch = alloc_type == ul_alloc_t::ADAPT_RETX or alloc_type == ul_alloc_t::NEWTX;
|
bool needs_pdcch = alloc_type == ul_alloc_t::ADAPT_RETX or (alloc_type == ul_alloc_t::NEWTX and not is_msg3);
|
||||||
if (not user->pusch_enabled(srslte::tti_point{get_tti_rx()}, cc_cfg->enb_cc_idx, needs_pdcch)) {
|
if (not user->pusch_enabled(srslte::tti_point{get_tti_rx()}, cc_cfg->enb_cc_idx, needs_pdcch)) {
|
||||||
return alloc_outcome_t::MEASGAP_COLLISION;
|
return alloc_outcome_t::MEASGAP_COLLISION;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate RBGs and DCI space
|
// Allocate RBGs and DCI space
|
||||||
alloc_outcome_t ret;
|
bool allow_pucch_collision = cc_cfg->nof_prb() == 6 and is_msg3;
|
||||||
if (alloc_type != ul_alloc_t::MSG3 and alloc_type != ul_alloc_t::MSG3_RETX) {
|
alloc_outcome_t ret = tti_alloc.alloc_ul_data(user, alloc, needs_pdcch, not allow_pucch_collision);
|
||||||
ret = tti_alloc.alloc_ul_data(user, alloc, needs_pdcch);
|
|
||||||
} else {
|
|
||||||
// allow collisions between Msg3 and PUCCH for 6 PRBs
|
|
||||||
ret = tti_alloc.reserve_ul_prbs(alloc, cc_cfg->nof_prb() != 6);
|
|
||||||
}
|
|
||||||
if (ret != alloc_outcome_t::SUCCESS) {
|
if (ret != alloc_outcome_t::SUCCESS) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul_alloc_t ul_alloc = {};
|
ul_alloc_t ul_alloc = {};
|
||||||
ul_alloc.type = alloc_type;
|
ul_alloc.type = alloc_type;
|
||||||
|
ul_alloc.is_msg3 = is_msg3;
|
||||||
ul_alloc.dci_idx = tti_alloc.get_pdcch_grid().nof_allocs() - 1;
|
ul_alloc.dci_idx = tti_alloc.get_pdcch_grid().nof_allocs() - 1;
|
||||||
ul_alloc.rnti = user->get_rnti();
|
ul_alloc.rnti = user->get_rnti();
|
||||||
ul_alloc.alloc = alloc;
|
ul_alloc.alloc = alloc;
|
||||||
|
@ -656,15 +653,13 @@ alloc_outcome_t sf_sched::alloc_ul_user(sched_ue* user, prb_interval alloc)
|
||||||
bool has_retx = h->has_pending_retx();
|
bool has_retx = h->has_pending_retx();
|
||||||
if (not has_retx) {
|
if (not has_retx) {
|
||||||
alloc_type = ul_alloc_t::NEWTX;
|
alloc_type = ul_alloc_t::NEWTX;
|
||||||
} else if (h->is_msg3()) {
|
|
||||||
alloc_type = ul_alloc_t::MSG3_RETX;
|
|
||||||
} else if (h->retx_requires_pdcch(tti_point{get_tti_tx_ul()}, alloc)) {
|
} else if (h->retx_requires_pdcch(tti_point{get_tti_tx_ul()}, alloc)) {
|
||||||
alloc_type = ul_alloc_t::ADAPT_RETX;
|
alloc_type = ul_alloc_t::ADAPT_RETX;
|
||||||
} else {
|
} else {
|
||||||
alloc_type = ul_alloc_t::NOADAPT_RETX;
|
alloc_type = ul_alloc_t::NOADAPT_RETX;
|
||||||
}
|
}
|
||||||
|
|
||||||
return alloc_ul(user, alloc, alloc_type);
|
return alloc_ul(user, alloc, alloc_type, h->is_msg3());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sf_sched::alloc_phich(sched_ue* user, sched_interface::ul_sched_res_t* ul_sf_result)
|
bool sf_sched::alloc_phich(sched_ue* user, sched_interface::ul_sched_res_t* ul_sf_result)
|
||||||
|
@ -997,7 +992,7 @@ void sf_sched::set_ul_sched_result(const sf_cch_allocator::alloc_result_t& dci_r
|
||||||
fmt::memory_buffer str_buffer;
|
fmt::memory_buffer str_buffer;
|
||||||
fmt::format_to(str_buffer,
|
fmt::format_to(str_buffer,
|
||||||
"SCHED: Error {} {} rnti=0x{:x}, pid={}, dci=({},{}), prb={}, bsr={}",
|
"SCHED: Error {} {} rnti=0x{:x}, pid={}, dci=({},{}), prb={}, bsr={}",
|
||||||
ul_alloc.type == ul_alloc_t::MSG3 ? "Msg3" : "UL",
|
ul_alloc.is_msg3 ? "Msg3" : "UL",
|
||||||
ul_alloc.is_retx() ? "retx" : "tx",
|
ul_alloc.is_retx() ? "retx" : "tx",
|
||||||
user->get_rnti(),
|
user->get_rnti(),
|
||||||
h->get_id(),
|
h->get_id(),
|
||||||
|
@ -1015,7 +1010,7 @@ void sf_sched::set_ul_sched_result(const sf_cch_allocator::alloc_result_t& dci_r
|
||||||
fmt::memory_buffer str_buffer;
|
fmt::memory_buffer str_buffer;
|
||||||
fmt::format_to(str_buffer,
|
fmt::format_to(str_buffer,
|
||||||
"SCHED: {} {} rnti=0x{:x}, cc={}, pid={}, dci=({},{}), prb={}, n_rtx={}, tbs={}, bsr={} ({}-{})",
|
"SCHED: {} {} rnti=0x{:x}, cc={}, pid={}, dci=({},{}), prb={}, n_rtx={}, tbs={}, bsr={} ({}-{})",
|
||||||
ul_alloc.is_msg3() ? "Msg3" : "UL",
|
ul_alloc.is_msg3 ? "Msg3" : "UL",
|
||||||
ul_alloc.is_retx() ? "retx" : "newtx",
|
ul_alloc.is_retx() ? "retx" : "newtx",
|
||||||
user->get_rnti(),
|
user->get_rnti(),
|
||||||
cc_cfg->enb_cc_idx,
|
cc_cfg->enb_cc_idx,
|
||||||
|
@ -1042,7 +1037,7 @@ alloc_outcome_t sf_sched::alloc_msg3(sched_ue* user, const sched_interface::dl_s
|
||||||
// Derive PRBs from allocated RAR grants
|
// Derive PRBs from allocated RAR grants
|
||||||
prb_interval msg3_alloc = prb_interval::riv_to_prbs(rargrant.grant.rba, cc_cfg->nof_prb());
|
prb_interval msg3_alloc = prb_interval::riv_to_prbs(rargrant.grant.rba, cc_cfg->nof_prb());
|
||||||
|
|
||||||
alloc_outcome_t ret = alloc_ul(user, msg3_alloc, sf_sched::ul_alloc_t::MSG3, rargrant.grant.trunc_mcs);
|
alloc_outcome_t ret = alloc_ul(user, msg3_alloc, sf_sched::ul_alloc_t::NEWTX, true, rargrant.grant.trunc_mcs);
|
||||||
if (not ret) {
|
if (not ret) {
|
||||||
fmt::memory_buffer str_buffer;
|
fmt::memory_buffer str_buffer;
|
||||||
fmt::format_to(str_buffer, "{}", msg3_alloc);
|
fmt::format_to(str_buffer, "{}", msg3_alloc);
|
||||||
|
|
|
@ -225,12 +225,19 @@ const ul_harq_proc* get_ul_newtx_harq(sched_ue& user, sf_sched* tti_sched)
|
||||||
|
|
||||||
alloc_outcome_t try_ul_retx_alloc(sf_sched& tti_sched, sched_ue& ue, const ul_harq_proc& h)
|
alloc_outcome_t try_ul_retx_alloc(sf_sched& tti_sched, sched_ue& ue, const ul_harq_proc& h)
|
||||||
{
|
{
|
||||||
// If can schedule the same mask, do it
|
|
||||||
prb_interval alloc = h.get_alloc();
|
prb_interval alloc = h.get_alloc();
|
||||||
|
if (tti_sched.get_cc_cfg()->nof_prb() == 6 and h.is_msg3()) {
|
||||||
|
// We allow collisions with PUCCH for special case of Msg3 and 6 PRBs
|
||||||
|
return tti_sched.alloc_ul_user(&ue, alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If can schedule the same mask as in earlier tx, do it
|
||||||
|
if (not tti_sched.get_ul_mask().any(alloc.start(), alloc.stop())) {
|
||||||
alloc_outcome_t ret = tti_sched.alloc_ul_user(&ue, alloc);
|
alloc_outcome_t ret = tti_sched.alloc_ul_user(&ue, alloc);
|
||||||
if (ret == alloc_outcome_t::SUCCESS or ret == alloc_outcome_t::DCI_COLLISION) {
|
if (ret == alloc_outcome_t::SUCCESS or ret == alloc_outcome_t::DCI_COLLISION) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Avoid measGaps accounting for PDCCH
|
// Avoid measGaps accounting for PDCCH
|
||||||
if (not ue.pusch_enabled(tti_sched.get_tti_rx(), tti_sched.get_enb_cc_idx(), true)) {
|
if (not ue.pusch_enabled(tti_sched.get_tti_rx(), tti_sched.get_enb_cc_idx(), true)) {
|
||||||
|
|
|
@ -77,7 +77,7 @@ struct test_scell_activation_params {
|
||||||
uint32_t pcell_idx = 0;
|
uint32_t pcell_idx = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
int test_scell_activation(test_scell_activation_params params)
|
int test_scell_activation(uint32_t sim_number, test_scell_activation_params params)
|
||||||
{
|
{
|
||||||
std::array<uint32_t, 6> prb_list{6, 15, 25, 50, 75, 100};
|
std::array<uint32_t, 6> prb_list{6, 15, 25, 50, 75, 100};
|
||||||
|
|
||||||
|
@ -228,7 +228,8 @@ int test_scell_activation(test_scell_activation_params params)
|
||||||
TESTASSERT(tot_dl_sched_data > 0);
|
TESTASSERT(tot_dl_sched_data > 0);
|
||||||
TESTASSERT(tot_ul_sched_data > 0);
|
TESTASSERT(tot_ul_sched_data > 0);
|
||||||
|
|
||||||
srslog::fetch_basic_logger("TEST").info("[TESTER] Sim1 finished successfully");
|
srslog::flush();
|
||||||
|
printf("[TESTER] Sim%d finished successfully\n\n", sim_number);
|
||||||
return SRSLTE_SUCCESS;
|
return SRSLTE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,28 +251,27 @@ int main()
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& mac_log = srslog::fetch_basic_logger("MAC");
|
auto& mac_log = srslog::fetch_basic_logger("MAC");
|
||||||
mac_log.set_level(srslog::basic_levels::info);
|
mac_log.set_level(srslog::basic_levels::debug);
|
||||||
auto& test_log = srslog::fetch_basic_logger("TEST", *spy, false);
|
auto& test_log = srslog::fetch_basic_logger("TEST", *spy, false);
|
||||||
test_log.set_level(srslog::basic_levels::info);
|
test_log.set_level(srslog::basic_levels::debug);
|
||||||
|
|
||||||
// Start the log backend.
|
// Start the log backend.
|
||||||
srslog::init();
|
srslog::init();
|
||||||
|
|
||||||
sched_diagnostic_printer printer(*spy);
|
sched_diagnostic_printer printer(*spy);
|
||||||
|
|
||||||
srslte::logmap::set_default_log_level(srslte::LOG_LEVEL_INFO);
|
|
||||||
printf("[TESTER] This is the chosen seed: %u\n", seed);
|
printf("[TESTER] This is the chosen seed: %u\n", seed);
|
||||||
uint32_t N_runs = 20;
|
uint32_t N_runs = 20;
|
||||||
for (uint32_t n = 0; n < N_runs; ++n) {
|
for (uint32_t n = 0; n < N_runs; ++n) {
|
||||||
printf("Sim run number: %u\n", n + 1);
|
printf("[TESTER] Sim run number: %u\n", n);
|
||||||
|
|
||||||
test_scell_activation_params p = {};
|
test_scell_activation_params p = {};
|
||||||
p.pcell_idx = 0;
|
p.pcell_idx = 0;
|
||||||
TESTASSERT(test_scell_activation(p) == SRSLTE_SUCCESS);
|
TESTASSERT(test_scell_activation(n * 2, p) == SRSLTE_SUCCESS);
|
||||||
|
|
||||||
p = {};
|
p = {};
|
||||||
p.pcell_idx = 1;
|
p.pcell_idx = 1;
|
||||||
TESTASSERT(test_scell_activation(p) == SRSLTE_SUCCESS);
|
TESTASSERT(test_scell_activation(n * 2 + 1, p) == SRSLTE_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
srslog::flush();
|
srslog::flush();
|
||||||
|
|
|
@ -194,8 +194,9 @@ int test_ul_sched_result(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t&
|
||||||
} else {
|
} else {
|
||||||
CONDERROR(pusch_ptr->current_tx_nb == 0, "UL retx has to have nof tx > 0");
|
CONDERROR(pusch_ptr->current_tx_nb == 0, "UL retx has to have nof tx > 0");
|
||||||
if (not h.active) {
|
if (not h.active) {
|
||||||
// the HARQ is being resumed
|
// the HARQ is being resumed. PDCCH must be active with the exception of Msg3
|
||||||
CONDERROR(not pusch_ptr->needs_pdcch, "Resumed UL HARQs need to be signalled in PDCCH");
|
CONDERROR(ue.msg4_tti_rx.is_valid() and not pusch_ptr->needs_pdcch,
|
||||||
|
"Resumed UL HARQs need to be signalled in PDCCH");
|
||||||
} else {
|
} else {
|
||||||
if (pusch_ptr->needs_pdcch) {
|
if (pusch_ptr->needs_pdcch) {
|
||||||
CONDERROR(pusch_ptr->dci.type2_alloc.riv == h.riv, "Adaptive retx must change riv");
|
CONDERROR(pusch_ptr->dci.type2_alloc.riv == h.riv, "Adaptive retx must change riv");
|
||||||
|
|
Loading…
Reference in New Issue