mirror of https://github.com/PentHertz/srsLTE.git
Implementation of time-domain PF scheduler
- PF scheduling becomes the new default
This commit is contained in:
parent
07d2bc4fe8
commit
d7fae0b7a3
|
@ -0,0 +1,99 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2020 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRSLTE_SCHED_PF_H
|
||||||
|
#define SRSLTE_SCHED_PF_H
|
||||||
|
|
||||||
|
#include "sched.h"
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
|
||||||
|
class sched_dl_pf : public sched::metric_dl
|
||||||
|
{
|
||||||
|
using ue_cit_t = std::map<uint16_t, sched_ue>::const_iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void set_params(const sched_cell_params_t& cell_params_) final;
|
||||||
|
void sched_users(std::map<uint16_t, sched_ue>& ue_db, dl_sf_sched_itf* tti_sched) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const sched_cell_params_t* cc_cfg = nullptr;
|
||||||
|
srslte::log_ref log_h;
|
||||||
|
|
||||||
|
struct ue_ctxt {
|
||||||
|
ue_ctxt(uint16_t rnti_) : rnti(rnti_) {}
|
||||||
|
float avg_rate() const { return nof_samples == 0 ? 0 : rate; }
|
||||||
|
uint32_t count() const { return nof_samples; }
|
||||||
|
void new_tti(const sched_cell_params_t& cell, sched_ue& ue, dl_sf_sched_itf* tti_sched);
|
||||||
|
void save_history(bool alloc, float alpha);
|
||||||
|
|
||||||
|
const uint16_t rnti;
|
||||||
|
|
||||||
|
uint32_t ue_cc_idx = 0;
|
||||||
|
bool is_retx = false;
|
||||||
|
float prio = 0;
|
||||||
|
dl_harq_proc* h = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float rate = 0;
|
||||||
|
uint32_t nof_samples = 0;
|
||||||
|
};
|
||||||
|
std::map<uint16_t, ue_ctxt> ue_history_db;
|
||||||
|
struct ue_prio_compare {
|
||||||
|
bool operator()(const ue_ctxt* lhs, const ue_ctxt* rhs) const;
|
||||||
|
};
|
||||||
|
std::priority_queue<ue_ctxt*, std::vector<ue_ctxt*>, ue_prio_compare> ue_queue;
|
||||||
|
|
||||||
|
bool try_dl_alloc(ue_ctxt& ue_ctxt, sched_ue& ue, dl_sf_sched_itf* tti_sched);
|
||||||
|
};
|
||||||
|
|
||||||
|
class sched_ul_pf : public sched::metric_ul
|
||||||
|
{
|
||||||
|
using ue_cit_t = std::map<uint16_t, sched_ue>::const_iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void set_params(const sched_cell_params_t& cell_params_) final;
|
||||||
|
void sched_users(std::map<uint16_t, sched_ue>& ue_db, ul_sf_sched_itf* tti_sched) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const sched_cell_params_t* cc_cfg = nullptr;
|
||||||
|
srslte::log_ref log_h;
|
||||||
|
struct ue_ctxt {
|
||||||
|
ue_ctxt(uint16_t rnti_) : rnti(rnti_) {}
|
||||||
|
float avg_rate() const { return nof_samples == 0 ? 0 : rate; }
|
||||||
|
uint32_t count() const { return nof_samples; }
|
||||||
|
void new_tti(const sched_cell_params_t& cell, sched_ue& ue, ul_sf_sched_itf* tti_sched);
|
||||||
|
void save_history(bool alloc, float alpha);
|
||||||
|
|
||||||
|
const uint16_t rnti;
|
||||||
|
|
||||||
|
uint32_t ue_cc_idx = 0;
|
||||||
|
float prio = 0;
|
||||||
|
ul_harq_proc* h = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float rate = 0;
|
||||||
|
uint32_t nof_samples = 0;
|
||||||
|
};
|
||||||
|
std::map<uint16_t, ue_ctxt> ue_history_db;
|
||||||
|
struct ue_prio_compare {
|
||||||
|
bool operator()(const ue_ctxt* lhs, const ue_ctxt* rhs) const;
|
||||||
|
};
|
||||||
|
std::priority_queue<ue_ctxt*, std::vector<ue_ctxt*>, ue_prio_compare> ue_queue;
|
||||||
|
|
||||||
|
bool try_ul_alloc(ue_ctxt& ue_ctxt, sched_ue& ue, ul_sf_sched_itf* tti_sched);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace srsenb
|
||||||
|
|
||||||
|
#endif // SRSLTE_SCHED_PF_H
|
|
@ -49,7 +49,7 @@ struct cc_sched_ue {
|
||||||
const sched_cell_params_t* get_cell_cfg() const { return cell_params; }
|
const sched_cell_params_t* get_cell_cfg() const { return cell_params; }
|
||||||
uint32_t get_ue_cc_idx() const { return ue_cc_idx; }
|
uint32_t get_ue_cc_idx() const { return ue_cc_idx; }
|
||||||
void set_dl_cqi(uint32_t tti_tx_dl, uint32_t dl_cqi);
|
void set_dl_cqi(uint32_t tti_tx_dl, uint32_t dl_cqi);
|
||||||
int cqi_to_tbs(uint32_t nof_prb, uint32_t nof_re, bool use_tbs_index_alt, bool is_ul, uint32_t* mcs);
|
int cqi_to_tbs(uint32_t nof_prb, uint32_t nof_re, bool is_ul, uint32_t* mcs);
|
||||||
cc_st cc_state() const { return cc_state_; }
|
cc_st cc_state() const { return cc_state_; }
|
||||||
|
|
||||||
harq_entity harq_ent;
|
harq_entity harq_ent;
|
||||||
|
@ -144,11 +144,13 @@ public:
|
||||||
rbg_interval get_required_dl_rbgs(uint32_t ue_cc_idx);
|
rbg_interval get_required_dl_rbgs(uint32_t ue_cc_idx);
|
||||||
srslte::interval<uint32_t> get_requested_dl_bytes(uint32_t ue_cc_idx);
|
srslte::interval<uint32_t> get_requested_dl_bytes(uint32_t ue_cc_idx);
|
||||||
uint32_t get_pending_dl_rlc_data() const;
|
uint32_t get_pending_dl_rlc_data() const;
|
||||||
|
uint32_t get_expected_dl_bitrate(uint32_t ue_cc_idx) const;
|
||||||
|
|
||||||
uint32_t get_pending_ul_data_total(uint32_t tti, int this_ue_cc_idx);
|
uint32_t get_pending_ul_data_total(uint32_t tti, int this_ue_cc_idx);
|
||||||
uint32_t get_pending_ul_new_data(uint32_t tti, int this_ue_cc_idx);
|
uint32_t get_pending_ul_new_data(uint32_t tti, int this_ue_cc_idx);
|
||||||
uint32_t get_pending_ul_old_data();
|
uint32_t get_pending_ul_old_data();
|
||||||
uint32_t get_pending_ul_old_data(uint32_t cc_idx);
|
uint32_t get_pending_ul_old_data(uint32_t cc_idx);
|
||||||
|
uint32_t get_expected_ul_bitrate(uint32_t ue_cc_idx) const;
|
||||||
|
|
||||||
dl_harq_proc* get_pending_dl_harq(uint32_t tti_tx_dl, uint32_t cc_idx);
|
dl_harq_proc* get_pending_dl_harq(uint32_t tti_tx_dl, uint32_t cc_idx);
|
||||||
dl_harq_proc* get_empty_dl_harq(uint32_t tti_tx_dl, uint32_t cc_idx);
|
dl_harq_proc* get_empty_dl_harq(uint32_t tti_tx_dl, uint32_t cc_idx);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
set(SOURCES mac.cc ue.cc sched.cc sched_carrier.cc sched_grid.cc sched_harq.cc sched_metric.cc sched_ue.cc
|
set(SOURCES mac.cc ue.cc sched.cc sched_carrier.cc sched_grid.cc sched_harq.cc sched_metric.cc sched_ue.cc
|
||||||
sched_lch.cc sched_interface_helpers.cc)
|
sched_lch.cc sched_interface_helpers.cc sched_pf.cc)
|
||||||
add_library(srsenb_mac STATIC ${SOURCES})
|
add_library(srsenb_mac STATIC ${SOURCES})
|
||||||
|
|
||||||
if(ENABLE_5GNR)
|
if(ENABLE_5GNR)
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "srsenb/hdr/stack/mac/sched_carrier.h"
|
#include "srsenb/hdr/stack/mac/sched_carrier.h"
|
||||||
#include "srsenb/hdr/stack/mac/sched_interface_helpers.h"
|
#include "srsenb/hdr/stack/mac/sched_interface_helpers.h"
|
||||||
#include "srsenb/hdr/stack/mac/sched_metric.h"
|
#include "srsenb/hdr/stack/mac/sched_metric.h"
|
||||||
|
#include "srsenb/hdr/stack/mac/sched_pf.h"
|
||||||
#include "srslte/common/log_helper.h"
|
#include "srslte/common/log_helper.h"
|
||||||
#include "srslte/common/logmap.h"
|
#include "srslte/common/logmap.h"
|
||||||
|
|
||||||
|
@ -288,9 +289,13 @@ void sched::carrier_sched::carrier_cfg(const sched_cell_params_t& cell_params_)
|
||||||
ra_sched_ptr.reset(new ra_sched{*cc_cfg, *ue_db});
|
ra_sched_ptr.reset(new ra_sched{*cc_cfg, *ue_db});
|
||||||
|
|
||||||
// Setup data scheduling algorithms
|
// Setup data scheduling algorithms
|
||||||
dl_metric.reset(new srsenb::dl_metric_rr{});
|
// dl_metric.reset(new srsenb::dl_metric_rr{});
|
||||||
|
// dl_metric->set_params(*cc_cfg);
|
||||||
|
// ul_metric.reset(new srsenb::ul_metric_rr{});
|
||||||
|
// ul_metric->set_params(*cc_cfg);
|
||||||
|
dl_metric.reset(new srsenb::sched_dl_pf{});
|
||||||
dl_metric->set_params(*cc_cfg);
|
dl_metric->set_params(*cc_cfg);
|
||||||
ul_metric.reset(new srsenb::ul_metric_rr{});
|
ul_metric.reset(new srsenb::sched_ul_pf{});
|
||||||
ul_metric->set_params(*cc_cfg);
|
ul_metric->set_params(*cc_cfg);
|
||||||
|
|
||||||
// Initiate the tti_scheduler for each TTI
|
// Initiate the tti_scheduler for each TTI
|
||||||
|
|
|
@ -371,21 +371,6 @@ void harq_entity::reset_pending_data(srslte::tti_point tti_rx)
|
||||||
for (auto& h : dl_harqs) {
|
for (auto& h : dl_harqs) {
|
||||||
h.reset_pending_data();
|
h.reset_pending_data();
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete old DL harq procs
|
|
||||||
for (auto& h : dl_harqs) {
|
|
||||||
if (not h.is_empty()) {
|
|
||||||
if (tti_tx_dl > h.get_tti() + 100) {
|
|
||||||
srslte::logmap::get("MAC")->info("SCHED: pid=%d is old. tti_pid=%d, now is %d, resetting\n",
|
|
||||||
h.get_id(),
|
|
||||||
h.get_tti().to_uint(),
|
|
||||||
tti_tx_dl.to_uint());
|
|
||||||
for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) {
|
|
||||||
h.reset(tb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -245,6 +245,10 @@ ul_harq_proc* ul_metric_rr::allocate_user_retx_prbs(sched_ue* user)
|
||||||
|
|
||||||
// if there are procedures and we have space
|
// if there are procedures and we have space
|
||||||
if (h->has_pending_retx()) {
|
if (h->has_pending_retx()) {
|
||||||
|
// Avoid measGaps
|
||||||
|
if (not user->pusch_enabled(tti_rx, cc_cfg->enb_cc_idx, false)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
prb_interval alloc = h->get_alloc();
|
prb_interval alloc = h->get_alloc();
|
||||||
|
|
||||||
// If can schedule the same mask, do it
|
// If can schedule the same mask, do it
|
||||||
|
@ -257,6 +261,10 @@ ul_harq_proc* ul_metric_rr::allocate_user_retx_prbs(sched_ue* user)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Avoid measGaps accounting for PDCCH
|
||||||
|
if (not user->pusch_enabled(tti_rx, cc_cfg->enb_cc_idx, true)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
if (find_allocation(alloc.length(), &alloc)) {
|
if (find_allocation(alloc.length(), &alloc)) {
|
||||||
ret = tti_alloc->alloc_ul_user(user, alloc);
|
ret = tti_alloc->alloc_ul_user(user, alloc);
|
||||||
if (ret == alloc_outcome_t::SUCCESS) {
|
if (ret == alloc_outcome_t::SUCCESS) {
|
||||||
|
|
|
@ -0,0 +1,355 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* \section COPYRIGHT
|
||||||
|
*
|
||||||
|
* Copyright 2013-2020 Software Radio Systems Limited
|
||||||
|
*
|
||||||
|
* By using this file, you agree to the terms and conditions set
|
||||||
|
* forth in the LICENSE file which can be found at the top level of
|
||||||
|
* the distribution.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "srsenb/hdr/stack/mac/sched_pf.h"
|
||||||
|
#include "srsenb/hdr/stack/mac/sched_harq.h"
|
||||||
|
|
||||||
|
namespace srsenb {
|
||||||
|
|
||||||
|
void sched_dl_pf::set_params(const sched_cell_params_t& cell_params_)
|
||||||
|
{
|
||||||
|
cc_cfg = &cell_params_;
|
||||||
|
log_h = srslte::logmap::get("MAC");
|
||||||
|
}
|
||||||
|
|
||||||
|
void sched_dl_pf::sched_users(std::map<uint16_t, sched_ue>& ue_db, dl_sf_sched_itf* tti_sched)
|
||||||
|
{
|
||||||
|
if (ue_db.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// remove deleted users from history
|
||||||
|
for (auto it = ue_history_db.begin(); it != ue_history_db.end();) {
|
||||||
|
if (not ue_db.count(it->first)) {
|
||||||
|
it = ue_history_db.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add new users to history db, and update priority queue
|
||||||
|
for (auto& u : ue_db) {
|
||||||
|
auto it = ue_history_db.find(u.first);
|
||||||
|
if (it == ue_history_db.end()) {
|
||||||
|
it = ue_history_db.insert(std::make_pair(u.first, ue_ctxt{u.first})).first;
|
||||||
|
}
|
||||||
|
it->second.new_tti(*cc_cfg, u.second, tti_sched);
|
||||||
|
ue_queue.push(&it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (not ue_queue.empty()) {
|
||||||
|
ue_ctxt& ue = *ue_queue.top();
|
||||||
|
bool alloc_success = try_dl_alloc(ue, ue_db[ue.rnti], tti_sched);
|
||||||
|
ue.save_history(alloc_success, 0.01);
|
||||||
|
ue_queue.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sched_dl_pf::ue_ctxt::new_tti(const sched_cell_params_t& cell, sched_ue& ue, dl_sf_sched_itf* tti_sched)
|
||||||
|
{
|
||||||
|
h = nullptr;
|
||||||
|
prio = 0;
|
||||||
|
is_retx = false;
|
||||||
|
|
||||||
|
auto p = ue.get_active_cell_index(cell.enb_cc_idx);
|
||||||
|
if (not p.first) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (not ue.pdsch_enabled(srslte::tti_point(tti_sched->get_tti_tx_dl() - TX_ENB_DELAY), cell.enb_cc_idx)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ue_cc_idx = p.second;
|
||||||
|
|
||||||
|
// search for DL HARQ
|
||||||
|
h = ue.get_pending_dl_harq(tti_sched->get_tti_tx_dl(), ue_cc_idx);
|
||||||
|
is_retx = h != nullptr;
|
||||||
|
if (h == nullptr) {
|
||||||
|
h = ue.get_empty_dl_harq(tti_sched->get_tti_tx_dl(), ue_cc_idx);
|
||||||
|
if (h == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate PF priority
|
||||||
|
float r = ue.get_expected_dl_bitrate(ue_cc_idx) / 8;
|
||||||
|
float R = avg_rate();
|
||||||
|
prio = (R != 0) ? r / R : (r == 0 ? 0 : std::numeric_limits<float>::max());
|
||||||
|
}
|
||||||
|
|
||||||
|
void sched_dl_pf::ue_ctxt::save_history(bool alloc, float alpha)
|
||||||
|
{
|
||||||
|
float sample = alloc ? (h->get_tbs(0) + h->get_tbs(1)) : 0;
|
||||||
|
if (nof_samples < 1 / alpha) {
|
||||||
|
// fast start
|
||||||
|
rate = rate + (sample - rate) / (nof_samples + 1);
|
||||||
|
} else {
|
||||||
|
rate = (1 - alpha) * rate + (alpha)*sample;
|
||||||
|
}
|
||||||
|
nof_samples++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool find_allocation(uint32_t min_nof_rbg, uint32_t max_nof_rbg, rbgmask_t* rbgmask, dl_sf_sched_itf* tti_alloc)
|
||||||
|
{
|
||||||
|
if (tti_alloc->get_dl_mask().all()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 1's for free rbgs
|
||||||
|
rbgmask_t localmask = ~(tti_alloc->get_dl_mask());
|
||||||
|
|
||||||
|
uint32_t i = 0, nof_alloc = 0;
|
||||||
|
for (; i < localmask.size() and nof_alloc < max_nof_rbg; ++i) {
|
||||||
|
if (localmask.test(i)) {
|
||||||
|
nof_alloc++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nof_alloc < min_nof_rbg) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
localmask.fill(i, localmask.size(), false);
|
||||||
|
*rbgmask = localmask;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sched_dl_pf::try_dl_alloc(ue_ctxt& ue_ctxt, sched_ue& ue, dl_sf_sched_itf* tti_sched)
|
||||||
|
{
|
||||||
|
if (tti_sched->is_dl_alloc(ue_ctxt.rnti) or ue_ctxt.prio == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
alloc_outcome_t code;
|
||||||
|
if (ue_ctxt.is_retx) {
|
||||||
|
// Try to reuse the same mask
|
||||||
|
rbgmask_t retx_mask = ue_ctxt.h->get_rbgmask();
|
||||||
|
code = tti_sched->alloc_dl_user(&ue, retx_mask, ue_ctxt.h->get_id());
|
||||||
|
if (code == alloc_outcome_t::SUCCESS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (code == alloc_outcome_t::DCI_COLLISION) {
|
||||||
|
// No DCIs available for this user. Move to next
|
||||||
|
log_h->info("SCHED: Couldn't find space in PDCCH for DL retx for rnti=0x%x\n", ue_ctxt.rnti);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If previous mask does not fit, find another with exact same number of rbgs
|
||||||
|
size_t nof_rbg = retx_mask.count();
|
||||||
|
if (find_allocation(nof_rbg, nof_rbg, &retx_mask, tti_sched)) {
|
||||||
|
code = tti_sched->alloc_dl_user(&ue, retx_mask, ue_ctxt.h->get_id());
|
||||||
|
if (code == alloc_outcome_t::SUCCESS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (code == alloc_outcome_t::DCI_COLLISION) {
|
||||||
|
log_h->info("SCHED: Couldn't find space in PDCCH for DL retx for rnti=0x%x\n", ue.get_rnti());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Allocate resources based on pending data
|
||||||
|
rbg_interval req_rbgs = ue.get_required_dl_rbgs(ue_ctxt.ue_cc_idx);
|
||||||
|
if (req_rbgs.stop() > 0) {
|
||||||
|
rbgmask_t newtx_mask(tti_sched->get_dl_mask().size());
|
||||||
|
if (find_allocation(req_rbgs.start(), req_rbgs.stop(), &newtx_mask, tti_sched)) {
|
||||||
|
// some empty spaces were found
|
||||||
|
code = tti_sched->alloc_dl_user(&ue, newtx_mask, ue_ctxt.h->get_id());
|
||||||
|
if (code == alloc_outcome_t::SUCCESS) {
|
||||||
|
return true;
|
||||||
|
} else if (code == alloc_outcome_t::DCI_COLLISION) {
|
||||||
|
log_h->info("SCHED: Couldn't find space in PDCCH for DL tx for rnti=0x%x\n", ue_ctxt.rnti);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sched_dl_pf::ue_prio_compare::operator()(const sched_dl_pf::ue_ctxt* lhs, const sched_dl_pf::ue_ctxt* rhs) const
|
||||||
|
{
|
||||||
|
return (not lhs->is_retx and rhs->is_retx) or (lhs->is_retx == rhs->is_retx and lhs->prio < rhs->prio);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************
|
||||||
|
*
|
||||||
|
* Uplink Metric
|
||||||
|
*
|
||||||
|
*****************************************************************/
|
||||||
|
|
||||||
|
void sched_ul_pf::set_params(const sched_cell_params_t& cell_params_)
|
||||||
|
{
|
||||||
|
cc_cfg = &cell_params_;
|
||||||
|
log_h = srslte::logmap::get("MAC");
|
||||||
|
}
|
||||||
|
|
||||||
|
void sched_ul_pf::sched_users(std::map<uint16_t, sched_ue>& ue_db, ul_sf_sched_itf* tti_sched)
|
||||||
|
{
|
||||||
|
if (ue_db.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// remove deleted users from history
|
||||||
|
for (auto it = ue_history_db.begin(); it != ue_history_db.end();) {
|
||||||
|
if (not ue_db.count(it->first)) {
|
||||||
|
it = ue_history_db.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add new users to history db, and update priority queue
|
||||||
|
for (auto& u : ue_db) {
|
||||||
|
auto it = ue_history_db.find(u.first);
|
||||||
|
if (it == ue_history_db.end()) {
|
||||||
|
it = ue_history_db.insert(std::make_pair(u.first, ue_ctxt{u.first})).first;
|
||||||
|
}
|
||||||
|
it->second.new_tti(*cc_cfg, u.second, tti_sched);
|
||||||
|
ue_queue.push(&it->second);
|
||||||
|
}
|
||||||
|
while (not ue_queue.empty()) {
|
||||||
|
ue_ctxt& ue = *ue_queue.top();
|
||||||
|
bool alloc_success = try_ul_alloc(ue, ue_db[ue.rnti], tti_sched);
|
||||||
|
ue.save_history(alloc_success, 0.01);
|
||||||
|
ue_queue.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a range of L contiguous PRBs that are empty
|
||||||
|
* @param L Size of the requested UL allocation in PRBs
|
||||||
|
* @param alloc Found allocation. It is guaranteed that 0 <= alloc->L <= L
|
||||||
|
* @return true if the requested allocation of size L was strictly met
|
||||||
|
*/
|
||||||
|
bool find_allocation(uint32_t L, prb_interval* alloc, ul_sf_sched_itf* tti_sched)
|
||||||
|
{
|
||||||
|
const prbmask_t* used_rb = &tti_sched->get_ul_mask();
|
||||||
|
*alloc = {};
|
||||||
|
for (uint32_t n = 0; n < used_rb->size() && alloc->length() < L; n++) {
|
||||||
|
if (not used_rb->test(n) && alloc->length() == 0) {
|
||||||
|
alloc->displace_to(n);
|
||||||
|
}
|
||||||
|
if (not used_rb->test(n)) {
|
||||||
|
alloc->resize_by(1);
|
||||||
|
} else if (alloc->length() > 0) {
|
||||||
|
// avoid edges
|
||||||
|
if (n < 3) {
|
||||||
|
*alloc = {};
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (alloc->length() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure L is allowed by SC-FDMA modulation
|
||||||
|
while (!srslte_dft_precoding_valid_prb(alloc->length())) {
|
||||||
|
alloc->resize_by(-1);
|
||||||
|
}
|
||||||
|
return alloc->length() == L;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sched_ul_pf::try_ul_alloc(ue_ctxt& ue_ctxt, sched_ue& ue, ul_sf_sched_itf* tti_sched)
|
||||||
|
{
|
||||||
|
if (ue_ctxt.h == nullptr or tti_sched->is_ul_alloc(ue_ctxt.rnti) or ue_ctxt.prio == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
srslte::tti_point tti_rx{tti_sched->get_tti_tx_ul() - (TX_ENB_DELAY + FDD_HARQ_DELAY_DL_MS)};
|
||||||
|
|
||||||
|
alloc_outcome_t ret;
|
||||||
|
if (ue_ctxt.h->has_pending_retx()) {
|
||||||
|
prb_interval alloc = ue_ctxt.h->get_alloc();
|
||||||
|
|
||||||
|
// If can schedule the same mask, do it
|
||||||
|
ret = tti_sched->alloc_ul_user(&ue, alloc);
|
||||||
|
if (ret == alloc_outcome_t::SUCCESS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (ret == alloc_outcome_t::DCI_COLLISION) {
|
||||||
|
log_h->info("SCHED: Couldn't find space in PDCCH for UL retx of rnti=0x%x\n", ue.get_rnti());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid measGaps accounting for PDCCH
|
||||||
|
if (not ue.pusch_enabled(tti_rx, cc_cfg->enb_cc_idx, true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (find_allocation(alloc.length(), &alloc, tti_sched)) {
|
||||||
|
ret = tti_sched->alloc_ul_user(&ue, alloc);
|
||||||
|
if (ret == alloc_outcome_t::SUCCESS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (ret == alloc_outcome_t::DCI_COLLISION) {
|
||||||
|
log_h->info("SCHED: Couldn't find space in PDCCH for UL retx of rnti=0x%x\n", ue.get_rnti());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Avoid measGaps accounting for PDCCH
|
||||||
|
if (not ue.pusch_enabled(tti_rx, cc_cfg->enb_cc_idx, true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t pending_data = ue.get_pending_ul_new_data(tti_sched->get_tti_tx_ul(), ue_ctxt.ue_cc_idx);
|
||||||
|
// find an empty PID
|
||||||
|
if (ue_ctxt.h->is_empty(0) and pending_data > 0) {
|
||||||
|
uint32_t pending_rb = ue.get_required_prb_ul(ue_ctxt.ue_cc_idx, pending_data);
|
||||||
|
prb_interval alloc{};
|
||||||
|
|
||||||
|
find_allocation(pending_rb, &alloc, tti_sched);
|
||||||
|
if (alloc.length() > 0) { // at least one PRB was scheduled
|
||||||
|
ret = tti_sched->alloc_ul_user(&ue, alloc);
|
||||||
|
if (ret == alloc_outcome_t::SUCCESS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (ret == alloc_outcome_t::DCI_COLLISION) {
|
||||||
|
log_h->info("SCHED: Couldn't find space in PDCCH for UL tx of rnti=0x%x\n", ue.get_rnti());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sched_ul_pf::ue_ctxt::new_tti(const sched_cell_params_t& cell, sched_ue& ue, ul_sf_sched_itf* tti_sched)
|
||||||
|
{
|
||||||
|
srslte::tti_point tti_rx = srslte::tti_point(tti_sched->get_tti_tx_ul() - TX_ENB_DELAY - FDD_HARQ_DELAY_DL_MS);
|
||||||
|
h = nullptr;
|
||||||
|
prio = 0;
|
||||||
|
|
||||||
|
auto p = ue.get_active_cell_index(cell.enb_cc_idx);
|
||||||
|
if (not p.first) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (not ue.pusch_enabled(tti_rx, cell.enb_cc_idx, false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ue_cc_idx = p.second;
|
||||||
|
h = ue.get_ul_harq(tti_sched->get_tti_tx_ul(), ue_cc_idx);
|
||||||
|
|
||||||
|
// calculate PF priority
|
||||||
|
float r = ue.get_expected_ul_bitrate(ue_cc_idx) / 8;
|
||||||
|
float R = avg_rate();
|
||||||
|
prio = (R != 0) ? r / R : (r == 0 ? 0 : std::numeric_limits<float>::max());
|
||||||
|
}
|
||||||
|
|
||||||
|
void sched_ul_pf::ue_ctxt::save_history(bool alloc, float alpha)
|
||||||
|
{
|
||||||
|
float sample = alloc ? h->get_pending_data() : 0;
|
||||||
|
if (nof_samples < 1 / alpha) {
|
||||||
|
// fast start
|
||||||
|
rate = rate + (sample - rate) / (nof_samples + 1);
|
||||||
|
} else {
|
||||||
|
rate = (1 - alpha) * rate + (alpha)*sample;
|
||||||
|
}
|
||||||
|
nof_samples++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sched_ul_pf::ue_prio_compare::operator()(const sched_ul_pf::ue_ctxt* lhs, const sched_ul_pf::ue_ctxt* rhs) const
|
||||||
|
{
|
||||||
|
bool is_retx1 = lhs->h != nullptr and lhs->h->has_pending_retx(),
|
||||||
|
is_retx2 = rhs->h != nullptr and rhs->h->has_pending_retx();
|
||||||
|
return (not is_retx1 and is_retx2) or (is_retx1 == is_retx2 and lhs->prio < rhs->prio);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace srsenb
|
|
@ -1035,6 +1035,34 @@ uint32_t sched_ue::get_pending_dl_rlc_data() const
|
||||||
return pending_data;
|
return pending_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t sched_ue::get_expected_dl_bitrate(uint32_t ue_cc_idx) const
|
||||||
|
{
|
||||||
|
const cc_sched_ue* cc = &carriers[ue_cc_idx];
|
||||||
|
|
||||||
|
auto* cell_cfg = carriers[ue_cc_idx].get_cell_cfg();
|
||||||
|
uint32_t nof_re =
|
||||||
|
srslte_ra_dl_approx_nof_re(&cell_cfg->cfg.cell, cell_cfg->nof_prb(), cell_cfg->sched_cfg->max_nof_ctrl_symbols);
|
||||||
|
float max_coderate = srslte_cqi_to_coderate(std::min(cc->dl_cqi + 1u, 15u), cfg.use_tbs_index_alt);
|
||||||
|
|
||||||
|
// Inverse of srslte_coderate(tbs, nof_re)
|
||||||
|
uint32_t tbs = max_coderate * nof_re - 24;
|
||||||
|
return tbs / tti_duration_ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t sched_ue::get_expected_ul_bitrate(uint32_t ue_cc_idx) const
|
||||||
|
{
|
||||||
|
const cc_sched_ue* cc = &carriers[ue_cc_idx];
|
||||||
|
|
||||||
|
uint32_t N_srs = 0;
|
||||||
|
uint32_t nof_symb = 2 * (SRSLTE_CP_NSYMB(cell.cp) - 1) - N_srs;
|
||||||
|
uint32_t nof_re = nof_symb * cell.nof_prb * SRSLTE_NRE;
|
||||||
|
float max_coderate = srslte_cqi_to_coderate(std::min(cc->ul_cqi + 1u, 15u), false);
|
||||||
|
|
||||||
|
// Inverse of srslte_coderate(tbs, nof_re)
|
||||||
|
uint32_t tbs = max_coderate * nof_re - 24;
|
||||||
|
return tbs / tti_duration_ms;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns nof bytes allocated to active UL HARQs in the carrier cc_idx.
|
/// Returns nof bytes allocated to active UL HARQs in the carrier cc_idx.
|
||||||
/// NOTE: The returned value accounts for the MAC header and payload (RLC headers and actual data)
|
/// NOTE: The returned value accounts for the MAC header and payload (RLC headers and actual data)
|
||||||
uint32_t sched_ue::get_pending_ul_old_data(uint32_t ue_cc_idx)
|
uint32_t sched_ue::get_pending_ul_old_data(uint32_t ue_cc_idx)
|
||||||
|
@ -1249,7 +1277,7 @@ int sched_ue::enb_to_ue_cc_idx(uint32_t enb_cc_idx) const
|
||||||
return it != carriers.end() ? std::distance(carriers.begin(), it) : -1;
|
return it != carriers.end() ? std::distance(carriers.begin(), it) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cc_sched_ue::cqi_to_tbs(uint32_t nof_prb, uint32_t nof_re, bool use_tbs_index_alt, bool is_ul, uint32_t* mcs)
|
int cc_sched_ue::cqi_to_tbs(uint32_t nof_prb, uint32_t nof_re, bool is_ul, uint32_t* mcs)
|
||||||
{
|
{
|
||||||
using ul64qam_cap = sched_interface::ue_cfg_t::ul64qam_cap;
|
using ul64qam_cap = sched_interface::ue_cfg_t::ul64qam_cap;
|
||||||
uint32_t max_Qm;
|
uint32_t max_Qm;
|
||||||
|
@ -1261,17 +1289,17 @@ int cc_sched_ue::cqi_to_tbs(uint32_t nof_prb, uint32_t nof_re, bool use_tbs_inde
|
||||||
max_coderate = srslte_cqi_to_coderate(std::min(ul_cqi + 1u, 15u), false);
|
max_coderate = srslte_cqi_to_coderate(std::min(ul_cqi + 1u, 15u), false);
|
||||||
} else {
|
} else {
|
||||||
max_mcs = max_mcs_dl;
|
max_mcs = max_mcs_dl;
|
||||||
max_Qm = use_tbs_index_alt ? 8 : 6;
|
max_Qm = cfg->use_tbs_index_alt ? 8 : 6;
|
||||||
max_coderate = srslte_cqi_to_coderate(std::min(dl_cqi + 1u, 15u), use_tbs_index_alt);
|
max_coderate = srslte_cqi_to_coderate(std::min(dl_cqi + 1u, 15u), cfg->use_tbs_index_alt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// function with sign-flip at solution
|
// function with sign-flip at solution
|
||||||
auto compute_tbs = [&](int sel_mcs) -> float {
|
auto compute_tbs = [&](int sel_mcs) -> float {
|
||||||
uint32_t tbs_idx = srslte_ra_tbs_idx_from_mcs(sel_mcs, use_tbs_index_alt, is_ul);
|
uint32_t tbs_idx = srslte_ra_tbs_idx_from_mcs(sel_mcs, cfg->use_tbs_index_alt, is_ul);
|
||||||
int tbs = srslte_ra_tbs_from_idx(tbs_idx, nof_prb);
|
int tbs = srslte_ra_tbs_from_idx(tbs_idx, nof_prb);
|
||||||
float coderate = srslte_coderate(tbs, nof_re);
|
float coderate = srslte_coderate(tbs, nof_re);
|
||||||
srslte_mod_t mod =
|
srslte_mod_t mod =
|
||||||
(is_ul) ? srslte_ra_ul_mod_from_mcs(sel_mcs) : srslte_ra_dl_mod_from_mcs(sel_mcs, use_tbs_index_alt);
|
(is_ul) ? srslte_ra_ul_mod_from_mcs(sel_mcs) : srslte_ra_dl_mod_from_mcs(sel_mcs, cfg->use_tbs_index_alt);
|
||||||
uint32_t Qm = std::min(max_Qm, srslte_mod_bits_x_symbol(mod));
|
uint32_t Qm = std::min(max_Qm, srslte_mod_bits_x_symbol(mod));
|
||||||
return coderate - std::min(max_coderate, 0.930f * Qm);
|
return coderate - std::min(max_coderate, 0.930f * Qm);
|
||||||
};
|
};
|
||||||
|
@ -1288,7 +1316,7 @@ int cc_sched_ue::cqi_to_tbs(uint32_t nof_prb, uint32_t nof_re, bool use_tbs_inde
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int chosen_mcs = std::get<0>(ret);
|
int chosen_mcs = std::get<0>(ret);
|
||||||
uint32_t tbs_idx = srslte_ra_tbs_idx_from_mcs(chosen_mcs, use_tbs_index_alt, is_ul);
|
uint32_t tbs_idx = srslte_ra_tbs_idx_from_mcs(chosen_mcs, cfg->use_tbs_index_alt, is_ul);
|
||||||
int chosen_tbs = srslte_ra_tbs_from_idx(tbs_idx, nof_prb);
|
int chosen_tbs = srslte_ra_tbs_from_idx(tbs_idx, nof_prb);
|
||||||
|
|
||||||
if (mcs != nullptr) {
|
if (mcs != nullptr) {
|
||||||
|
@ -1441,7 +1469,7 @@ int cc_sched_ue::alloc_tbs(uint32_t nof_prb, uint32_t nof_re, uint32_t req_bytes
|
||||||
uint32_t sel_mcs = 0;
|
uint32_t sel_mcs = 0;
|
||||||
|
|
||||||
// TODO: Compute real spectral efficiency based on PUSCH-UCI configuration
|
// TODO: Compute real spectral efficiency based on PUSCH-UCI configuration
|
||||||
int tbs_bytes = cqi_to_tbs(nof_prb, nof_re, cfg->use_tbs_index_alt, is_ul, &sel_mcs) / 8;
|
int tbs_bytes = cqi_to_tbs(nof_prb, nof_re, is_ul, &sel_mcs) / 8;
|
||||||
|
|
||||||
/* If less bytes are requested, lower the MCS */
|
/* If less bytes are requested, lower the MCS */
|
||||||
if (tbs_bytes > (int)req_bytes && req_bytes > 0) {
|
if (tbs_bytes > (int)req_bytes && req_bytes > 0) {
|
||||||
|
|
Loading…
Reference in New Issue