@ -13,7 +13,7 @@
# include "srsenb/hdr/stack/mac/nr/sched_nr.h"
# include "srsenb/hdr/stack/mac/nr/sched_nr.h"
# include "srsenb/hdr/stack/mac/common/mac_metrics.h"
# include "srsenb/hdr/stack/mac/common/mac_metrics.h"
# include "srsenb/hdr/stack/mac/nr/harq_softbuffer.h"
# include "srsenb/hdr/stack/mac/nr/harq_softbuffer.h"
# include "srsenb/hdr/stack/mac/nr/sched_nr_ cell .h"
# include "srsenb/hdr/stack/mac/nr/sched_nr_ bwp .h"
# include "srsenb/hdr/stack/mac/nr/sched_nr_worker.h"
# include "srsenb/hdr/stack/mac/nr/sched_nr_worker.h"
# include "srsran/common/thread_pool.h"
# include "srsran/common/thread_pool.h"
@ -66,9 +66,100 @@ private:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
sched_nr : : sched_nr ( ) : logger ( & srslog : : fetch_basic_logger ( " MAC-NR " ) ) { }
void sched_nr : : feedback_manager : : enqueue_event ( uint16_t rnti , srsran : : move_callback < void ( ) > ev )
{
std : : lock_guard < std : : mutex > lock ( event_mutex ) ;
next_slot_events . push_back ( ue_event_t { rnti , std : : move ( ev ) } ) ;
}
void sched_nr : : feedback_manager : : get_pending_events ( srsran : : deque < ue_event_t > & current_events )
{
current_events . clear ( ) ;
std : : lock_guard < std : : mutex > ev_lock ( event_mutex ) ;
next_slot_events . swap ( current_events ) ;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class sched_nr : : ue_metrics_manager
{
public :
ue_metrics_manager ( ue_map_t & ues_ ) : ues ( ues_ ) { }
void stop ( )
{
std : : unique_lock < std : : mutex > lock ( mutex ) ;
if ( not stopped ) {
stopped = true ;
// requests during sched::stop may not be fulfilled by sched main thread
save_metrics_nolock ( ) ;
}
}
/// Blocking call that waits for the metrics to be filled
void get_metrics ( mac_metrics_t & requested_metrics )
{
std : : unique_lock < std : : mutex > lock ( mutex ) ;
pending_metrics = & requested_metrics ;
if ( not stopped ) {
cvar . wait ( lock , [ this ] ( ) { return pending_metrics = = nullptr ; } ) ;
} else {
save_metrics_nolock ( ) ;
}
}
/// called from within the scheduler main thread to save metrics
void save_metrics ( )
{
{
std : : unique_lock < std : : mutex > lock ( mutex ) ;
save_metrics_nolock ( ) ;
}
cvar . notify_one ( ) ;
}
private :
void save_metrics_nolock ( )
{
if ( pending_metrics = = nullptr ) {
return ;
}
for ( mac_ue_metrics_t & ue_metric : pending_metrics - > ues ) {
if ( ues . contains ( ue_metric . rnti ) and ues [ ue_metric . rnti ] - > carriers [ 0 ] ! = nullptr ) {
auto & ue_cc = * ues [ ue_metric . rnti ] - > carriers [ 0 ] ;
ue_metric . tx_brate = ue_cc . metrics . tx_brate ;
ue_metric . tx_errors = ue_cc . metrics . tx_errors ;
ue_metric . tx_pkts = ue_cc . metrics . tx_pkts ;
ue_cc . metrics = { } ;
}
}
pending_metrics = nullptr ;
}
ue_map_t & ues ;
std : : mutex mutex ;
std : : condition_variable cvar ;
mac_metrics_t * pending_metrics = nullptr ;
bool stopped = false ;
} ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
sched_nr : : ~ sched_nr ( ) { }
sched_nr : : sched_nr ( ) : logger ( & srslog : : fetch_basic_logger ( " MAC-NR " ) )
{
metrics_handler . reset ( new ue_metrics_manager { ue_db } ) ;
}
sched_nr : : ~ sched_nr ( )
{
stop ( ) ;
}
void sched_nr : : stop ( )
{
metrics_handler - > stop ( ) ;
}
int sched_nr : : config ( const sched_args_t & sched_cfg , srsran : : const_span < cell_cfg_t > cell_list )
int sched_nr : : config ( const sched_args_t & sched_cfg , srsran : : const_span < cell_cfg_t > cell_list )
{
{
@ -81,27 +172,26 @@ int sched_nr::config(const sched_args_t& sched_cfg, srsran::const_span<cell_cfg_
cfg . cells . emplace_back ( cc , cell_list [ cc ] , cfg . sched_cfg ) ;
cfg . cells . emplace_back ( cc , cell_list [ cc ] , cfg . sched_cfg ) ;
}
}
pending_results . reset ( new ul_sched_result_buffer ( cell_list . size ( ) ) ) ;
// Initiate cell-specific schedulers
// Initiate cell-specific schedulers
cells . reserve ( cell_list . size ( ) ) ;
c c_workers. resize ( cfg . cells . size ( ) ) ;
for ( uint32_t cc = 0 ; cc < c ell_li st . size ( ) ; + + cc ) {
for ( uint32_t cc = 0 ; cc < c fg. c ells. size ( ) ; + + cc ) {
c ells. emplace_back ( new serv_cell_manag er{ cfg . cells [ cc ] } ) ;
c c_workers[ cc ] . reset ( new slot_cc_work er{ cfg . cells [ cc ] } ) ;
}
}
pending_results . reset ( new ul_sched_result_buffer ( cell_list . size ( ) ) ) ;
sched_workers . reset ( new sched_nr_impl : : sched_worker_manager ( ue_db , cfg , cells ) ) ;
return SRSRAN_SUCCESS ;
return SRSRAN_SUCCESS ;
}
}
void sched_nr : : ue_cfg ( uint16_t rnti , const ue_cfg_t & uecfg )
void sched_nr : : ue_cfg ( uint16_t rnti , const ue_cfg_t & uecfg )
{
{
srsran_assert ( assert_ue_cfg_valid ( rnti , uecfg ) = = SRSRAN_SUCCESS , " Invalid UE configuration " ) ;
srsran_assert ( assert_ue_cfg_valid ( rnti , uecfg ) = = SRSRAN_SUCCESS , " Invalid UE configuration " ) ;
sched_workers- > enqueue_event ( rnti , [ this , rnti , uecfg ] ( ) { ue_cfg_impl ( rnti , uecfg ) ; } ) ;
pending_feedback. enqueue_event ( rnti , [ this , rnti , uecfg ] ( ) { ue_cfg_impl ( rnti , uecfg ) ; } ) ;
}
}
void sched_nr : : ue_rem ( uint16_t rnti )
void sched_nr : : ue_rem ( uint16_t rnti )
{
{
sched_workers- > enqueue_event ( rnti , [ this , rnti ] ( ) {
pending_feedback. enqueue_event ( rnti , [ this , rnti ] ( ) {
auto ue_it = ue_db . find ( rnti ) ;
auto ue_it = ue_db . find ( rnti ) ;
if ( ue_it = = ue_db . end ( ) ) {
if ( ue_it = = ue_db . end ( ) ) {
logger - > warning ( " SCHED: ue_rem(rnti) called for inexistent rnti=0x%x " , rnti ) ;
logger - > warning ( " SCHED: ue_rem(rnti) called for inexistent rnti=0x%x " , rnti ) ;
@ -130,14 +220,74 @@ void sched_nr::ue_cfg_impl(uint16_t rnti, const ue_cfg_t& uecfg)
}
}
}
}
// NOTE: there is no parallelism in these operations
void sched_nr : : slot_indication ( slot_point slot_tx )
{
srsran_assert ( worker_count . load ( std : : memory_order_relaxed ) = = 0 ,
" Call of sched slot_indication when previous TTI has not been completed " ) ;
// mark the start of slot.
current_slot_tx = slot_tx ;
worker_count . store ( static_cast < int > ( cfg . cells . size ( ) ) , std : : memory_order_relaxed ) ;
// Extract pending feedback events
pending_feedback . get_pending_events ( current_slot_events ) ;
// process non-cc specific feedback if pending (e.g. SRs, buffer state updates, UE config) for CA-enabled UEs
// Note: non-CA UEs are updated later in get_dl_sched, to leverage parallelism
for ( feedback_manager : : ue_event_t & ev : current_slot_events ) {
auto ue_it = ue_db . find ( ev . rnti ) ;
bool contains_ue = ue_it ! = ue_db . end ( ) ;
if ( not contains_ue or ( ue_it - > second - > has_ca ( ) ) ) {
ev . callback ( ) ;
}
}
// prepare CA-enabled UEs internal state for new slot
// Note: non-CA UEs are updated later in get_dl_sched, to leverage parallelism
for ( auto & u : ue_db ) {
if ( u . second - > has_ca ( ) ) {
u . second - > new_slot ( slot_tx ) ;
}
}
// If UE metrics were externally requested, store the current UE state
metrics_handler - > save_metrics ( ) ;
}
/// Generate {pdcch_slot,cc} scheduling decision
/// Generate {pdcch_slot,cc} scheduling decision
int sched_nr : : run_slot ( slot_point slot_dl , uint32_t cc , dl_res_t & result )
int sched_nr : : get_dl_sched( slot_point pdsch_tti , uint32_t cc , dl_res_t & result )
{
{
srsran_assert ( pdsch_tti = = current_slot_tx , " Unexpected pdsch_tti slot received " ) ;
// Copy UL results to intermediate buffer
// Copy UL results to intermediate buffer
ul_res_t & ul_res = pending_results - > add_ul_result ( slot_dl , cc ) ;
ul_res_t & ul_res = pending_results - > add_ul_result ( pdsch_tti , cc ) ;
// process non-cc specific feedback if pending (e.g. SRs, buffer state updates, UE config) for non-CA UEs
for ( feedback_manager : : ue_event_t & ev : current_slot_events ) {
auto ue_it = ue_db . find ( ev . rnti ) ;
bool contains_ue = ue_it ! = ue_db . end ( ) ;
if ( contains_ue and not ue_it - > second - > has_ca ( ) and ue_it - > second - > carriers [ cc ] ! = nullptr ) {
ev . callback ( ) ;
}
}
// prepare non-CA UEs internal state for new slot
for ( auto & u : ue_db ) {
if ( not u . second - > has_ca ( ) and u . second - > carriers [ cc ] ! = nullptr ) {
u . second - > new_slot ( current_slot_tx ) ;
}
}
// Generate {slot_idx,cc} result
// Process pending CC-specific feedback, generate {slot_idx,cc} scheduling decision
sched_workers - > run_slot ( slot_dl , cc , result , ul_res ) ;
cc_workers [ cc ] - > run_slot ( pdsch_tti , ue_db , result , ul_res ) ;
// decrement the number of active workers
int rem_workers = worker_count . fetch_sub ( 1 , std : : memory_order_release ) - 1 ;
srsran_assert ( rem_workers > = 0 , " invalid number of calls to get_dl_sched(slot, cc) " ) ;
if ( rem_workers = = 0 ) {
// Last Worker to finish slot
// TODO: Sync sched results with ue_db state
}
return SRSRAN_SUCCESS ;
return SRSRAN_SUCCESS ;
}
}
@ -158,21 +308,21 @@ int sched_nr::get_ul_sched(slot_point slot_ul, uint32_t cc, ul_res_t& result)
void sched_nr : : get_metrics ( mac_metrics_t & metrics )
void sched_nr : : get_metrics ( mac_metrics_t & metrics )
{
{
sched_workers - > get_metrics ( metrics ) ;
metrics_handler - > get_metrics ( metrics ) ;
}
}
int sched_nr : : dl_rach_info ( uint32_t cc , const rar_info_t & rar_info )
int sched_nr : : dl_rach_info ( uint32_t cc , const rar_info_t & rar_info )
{
{
sched_workers - > enqueue_cc_event ( cc , [ this , cc , rar_info ] ( ) { cells [ cc ] - > bwps [ 0 ] . ra . dl_rach_info ( rar_info ) ; } ) ;
cc_workers [ cc ] - > pending_feedback . enqueue_common_event (
[ this , cc , rar_info ] ( ) { cc_workers [ cc ] - > bwps [ 0 ] . ra . dl_rach_info ( rar_info ) ; } ) ;
return SRSRAN_SUCCESS ;
return SRSRAN_SUCCESS ;
}
}
void sched_nr : : dl_ack_info ( uint16_t rnti , uint32_t cc , uint32_t pid , uint32_t tb_idx , bool ack )
void sched_nr : : dl_ack_info ( uint16_t rnti , uint32_t cc , uint32_t pid , uint32_t tb_idx , bool ack )
{
{
sched_workers- > enqueue_cc_feedback ( rnti , cc , [ this , pid , tb_idx , ack ] ( ue_carrier & ue_cc ) {
cc_workers[ cc ] - > pending_feedback . enqueue_ue_feedback ( rnti , [ this , pid , tb_idx , ack ] ( ue_carrier & ue_cc ) {
int tbs = ue_cc . harq_ent . dl_ack_info ( pid , tb_idx , ack ) ;
int tbs = ue_cc . harq_ent . dl_ack_info ( pid , tb_idx , ack ) ;
if ( tbs > = 0 ) {
if ( tbs > = 0 ) {
std : : lock_guard < std : : mutex > lock ( ue_cc . metrics_mutex ) ;
if ( ack ) {
if ( ack ) {
ue_cc . metrics . tx_brate + = tbs ;
ue_cc . metrics . tx_brate + = tbs ;
} else {
} else {
@ -187,7 +337,7 @@ void sched_nr::dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb
void sched_nr : : ul_crc_info ( uint16_t rnti , uint32_t cc , uint32_t pid , bool crc )
void sched_nr : : ul_crc_info ( uint16_t rnti , uint32_t cc , uint32_t pid , bool crc )
{
{
sched_workers- > enqueue_cc_feedback ( rnti , cc , [ this , pid , crc ] ( ue_carrier & ue_cc ) {
cc_workers[ cc ] - > pending_feedback . enqueue_ue_feedback ( rnti , [ this , pid , crc ] ( ue_carrier & ue_cc ) {
if ( ue_cc . harq_ent . ul_crc_info ( pid , crc ) < 0 ) {
if ( ue_cc . harq_ent . ul_crc_info ( pid , crc ) < 0 ) {
logger - > warning ( " SCHED: rnti=0x%x, received CRC for empty pid=%d " , ue_cc . rnti , pid ) ;
logger - > warning ( " SCHED: rnti=0x%x, received CRC for empty pid=%d " , ue_cc . rnti , pid ) ;
}
}
@ -196,7 +346,7 @@ void sched_nr::ul_crc_info(uint16_t rnti, uint32_t cc, uint32_t pid, bool crc)
void sched_nr : : ul_sr_info ( uint16_t rnti )
void sched_nr : : ul_sr_info ( uint16_t rnti )
{
{
sched_workers- > enqueue_event ( rnti , [ this , rnti ] ( ) {
pending_feedback. enqueue_event ( rnti , [ this , rnti ] ( ) {
if ( ue_db . contains ( rnti ) ) {
if ( ue_db . contains ( rnti ) ) {
ue_db [ rnti ] - > ul_sr_info ( ) ;
ue_db [ rnti ] - > ul_sr_info ( ) ;
} else {
} else {
@ -207,7 +357,7 @@ void sched_nr::ul_sr_info(uint16_t rnti)
void sched_nr : : ul_bsr ( uint16_t rnti , uint32_t lcg_id , uint32_t bsr )
void sched_nr : : ul_bsr ( uint16_t rnti , uint32_t lcg_id , uint32_t bsr )
{
{
sched_workers- > enqueue_event ( rnti , [ this , rnti , lcg_id , bsr ] ( ) {
pending_feedback. enqueue_event ( rnti , [ this , rnti , lcg_id , bsr ] ( ) {
if ( ue_db . contains ( rnti ) ) {
if ( ue_db . contains ( rnti ) ) {
ue_db [ rnti ] - > ul_bsr ( lcg_id , bsr ) ;
ue_db [ rnti ] - > ul_bsr ( lcg_id , bsr ) ;
} else {
} else {
@ -218,7 +368,7 @@ void sched_nr::ul_bsr(uint16_t rnti, uint32_t lcg_id, uint32_t bsr)
void sched_nr : : dl_buffer_state ( uint16_t rnti , uint32_t lcid , uint32_t newtx , uint32_t retx )
void sched_nr : : dl_buffer_state ( uint16_t rnti , uint32_t lcid , uint32_t newtx , uint32_t retx )
{
{
sched_workers- > enqueue_event ( rnti , [ this , rnti , lcid , newtx , retx ] ( ) {
pending_feedback. enqueue_event ( rnti , [ this , rnti , lcid , newtx , retx ] ( ) {
if ( ue_db . contains ( rnti ) ) {
if ( ue_db . contains ( rnti ) ) {
ue_db [ rnti ] - > rlc_buffer_state ( lcid , newtx , retx ) ;
ue_db [ rnti ] - > rlc_buffer_state ( lcid , newtx , retx ) ;
} else {
} else {