Merge branch 'hol-delay' into 'develop'

Add per-LCID head-of-line delay to DL scheduling candidates

See merge request oai/openairinterface5g!4152
This commit is contained in:
Maxime Elkael
2026-07-07 11:42:01 +00:00
10 changed files with 44 additions and 3 deletions

View File

@@ -108,6 +108,7 @@ typedef struct {
sdu_size_t head_sdu_remaining_size_to_send; /*!< \brief remaining size of sdu: could be the total size or the remaining size of sdu_size_t head_sdu_remaining_size_to_send; /*!< \brief remaining size of sdu: could be the total size or the remaining size of
already segmented sdu */ already segmented sdu */
bool head_sdu_is_segmented; /*!< \brief 0 if head SDU has not been segmented, 1 if already segmented */ bool head_sdu_is_segmented; /*!< \brief 0 if head SDU has not been segmented, 1 if already segmented */
uint64_t oldest_sdu_arrival_ms; /*!< \brief Timestamp (ms, RLC time) of oldest SDU in tx/retx queue, 0 if empty */
} mac_rlc_status_resp_t; } mac_rlc_status_resp_t;
#define SDU_CONFIRM_NO false #define SDU_CONFIRM_NO false

View File

@@ -442,6 +442,7 @@ static int collect_dl_candidates(gNB_MAC_INST *mac,
int n = 0; int n = 0;
const frame_structure_t *fs = &mac->frame_structure; const frame_structure_t *fs = &mac->frame_structure;
const float dl_slots_per_s = (float)get_dl_slots_per_period(fs) / fs->numb_slots_period * fs->numb_slots_frame * 100; const float dl_slots_per_s = (float)get_dl_slots_per_period(fs) / fs->numb_slots_period * fs->numb_slots_frame * 100;
const uint64_t now_ms = get_nr_rlc_current_time();
UE_iterator (UE_list, UE) { UE_iterator (UE_list, UE) {
if (n >= max_candidates) if (n >= max_candidates)
@@ -527,6 +528,11 @@ static int collect_dl_candidates(gNB_MAC_INST *mac,
.alloc_beam_idx = 0, .alloc_beam_idx = 0,
.alloc_new_beam = false, .alloc_new_beam = false,
}; };
nr_dl_candidate_t *c = &candidates[n - 1];
FOR_EACH_SEQ_ARR(const nr_lc_config_t *, lc, &sched_ctrl->lc_config) {
const uint64_t ts = sched_ctrl->rlc_status[lc->lcid].oldest_sdu_arrival_ms;
c->hol_delay_ms[lc->lcid] = (ts > 0 && ts <= now_ms) ? (now_ms - ts) : 0;
}
} else { } else {
/* new transmission candidate */ /* new transmission candidate */
if (sched_ctrl->available_dl_harq.head < 0) if (sched_ctrl->available_dl_harq.head < 0)
@@ -572,8 +578,11 @@ static int collect_dl_candidates(gNB_MAC_INST *mac,
.alloc_new_beam = false, .alloc_new_beam = false,
}; };
nr_dl_candidate_t *c = &candidates[n - 1]; nr_dl_candidate_t *c = &candidates[n - 1];
for (int lcid = 0; lcid < NR_MAX_NUM_LCID; lcid++) FOR_EACH_SEQ_ARR(const nr_lc_config_t *, lc, &sched_ctrl->lc_config) {
c->pending_bytes_per_lcid[lcid] = sched_ctrl->rlc_status[lcid].bytes_in_buffer; c->pending_bytes_per_lcid[lc->lcid] = sched_ctrl->rlc_status[lc->lcid].bytes_in_buffer;
const uint64_t ts = sched_ctrl->rlc_status[lc->lcid].oldest_sdu_arrival_ms;
c->hol_delay_ms[lc->lcid] = (ts > 0 && ts <= now_ms) ? (now_ms - ts) : 0;
}
} }
} }

View File

@@ -925,6 +925,7 @@ struct nr_dl_candidate {
int retx_rbSize; ///< RBs needed for retx, 0 for new tx int retx_rbSize; ///< RBs needed for retx, 0 for new tx
uint32_t pending_bytes; ///< total bytes waiting in RLC buffers uint32_t pending_bytes; ///< total bytes waiting in RLC buffers
uint32_t pending_bytes_per_lcid[NR_MAX_NUM_LCID]; ///< per-LCID bytes waiting in RLC buffers uint32_t pending_bytes_per_lcid[NR_MAX_NUM_LCID]; ///< per-LCID bytes waiting in RLC buffers
uint64_t hol_delay_ms[NR_MAX_NUM_LCID]; ///< per-LCID head-of-line delay in milliseconds (age of oldest SDU)
float avg_throughput; ///< EWMA goodput in bps (dl_thr_ue) float avg_throughput; ///< EWMA goodput in bps (dl_thr_ue)
float bler; ///< current BLER estimate float bler; ///< current BLER estimate
int current_mcs; ///< current MCS state (retx: from HARQ, new tx: from BLER tracker) int current_mcs; ///< current MCS state (retx: from HARQ, new tx: from BLER tracker)

View File

@@ -78,6 +78,7 @@ typedef struct {
int status_size; int status_size;
int tx_size; int tx_size;
int retx_size; int retx_size;
uint64_t oldest_sdu_arrival_ms; /* timestamp (ms, from sdu->arrival_ms) of oldest SDU in tx/retx queue, 0 if empty */
} nr_rlc_entity_buffer_status_t; } nr_rlc_entity_buffer_status_t;
typedef struct nr_rlc_entity_s { typedef struct nr_rlc_entity_s {

View File

@@ -1777,6 +1777,12 @@ nr_rlc_entity_buffer_status_t nr_rlc_entity_am_buffer_status(
ret.tx_size = entity->common.bstatus.tx_size; ret.tx_size = entity->common.bstatus.tx_size;
ret.retx_size = entity->common.bstatus.retx_size; ret.retx_size = entity->common.bstatus.retx_size;
ret.oldest_sdu_arrival_ms = 0;
if (entity->retransmit_list && entity->retransmit_list->sdu)
ret.oldest_sdu_arrival_ms = entity->retransmit_list->sdu->arrival_ms;
else if (entity->tx_list && entity->tx_list->sdu)
ret.oldest_sdu_arrival_ms = entity->tx_list->sdu->arrival_ms;
return ret; return ret;
} }
@@ -1851,6 +1857,8 @@ void nr_rlc_entity_am_recv_sdu(nr_rlc_entity_t *_entity,
if (entity->common.avg_time_is_on) if (entity->common.avg_time_is_on)
sdu->sdu->time_of_arrival = time_average_now(); sdu->sdu->time_of_arrival = time_average_now();
sdu->sdu->arrival_ms = entity->t_current;
} }
/*************************************************************************/ /*************************************************************************/

View File

@@ -88,6 +88,10 @@ nr_rlc_entity_buffer_status_t nr_rlc_entity_tm_buffer_status(nr_rlc_entity_t *_e
ret.tx_size = entity->common.bstatus.tx_size; ret.tx_size = entity->common.bstatus.tx_size;
ret.retx_size = 0; ret.retx_size = 0;
ret.oldest_sdu_arrival_ms = 0;
if (entity->tx_list && entity->tx_list->sdu)
ret.oldest_sdu_arrival_ms = entity->tx_list->sdu->arrival_ms;
return ret; return ret;
} }
@@ -147,6 +151,8 @@ void nr_rlc_entity_tm_recv_sdu(nr_rlc_entity_t *_entity,
if (entity->common.avg_time_is_on) if (entity->common.avg_time_is_on)
sdu->sdu->time_of_arrival = time_average_now(); sdu->sdu->time_of_arrival = time_average_now();
sdu->sdu->arrival_ms = entity->t_current;
} }
/*************************************************************************/ /*************************************************************************/

View File

@@ -534,6 +534,10 @@ nr_rlc_entity_buffer_status_t nr_rlc_entity_um_buffer_status(nr_rlc_entity_t *_e
ret.tx_size = entity->common.bstatus.tx_size; ret.tx_size = entity->common.bstatus.tx_size;
ret.retx_size = 0; ret.retx_size = 0;
ret.oldest_sdu_arrival_ms = 0;
if (entity->tx_list && entity->tx_list->sdu)
ret.oldest_sdu_arrival_ms = entity->tx_list->sdu->arrival_ms;
return ret; return ret;
} }
@@ -594,6 +598,8 @@ void nr_rlc_entity_um_recv_sdu(nr_rlc_entity_t *_entity,
if (entity->common.avg_time_is_on) if (entity->common.avg_time_is_on)
sdu->sdu->time_of_arrival = time_average_now(); sdu->sdu->time_of_arrival = time_average_now();
sdu->sdu->arrival_ms = entity->t_current;
} }
/*************************************************************************/ /*************************************************************************/

View File

@@ -47,7 +47,7 @@ void unlock_nr_rlc_current_time(void)
AssertFatal(0, "error locking mutex"); AssertFatal(0, "error locking mutex");
} }
static uint64_t get_nr_rlc_current_time(void) uint64_t get_nr_rlc_current_time(void)
{ {
lock_nr_rlc_current_time(); lock_nr_rlc_current_time();
@@ -267,10 +267,12 @@ static mac_rlc_status_resp_t _nr_rlc_status_ind(nr_rlc_ue_t *ue, frame_t frame,
// Fix me: temproary reduction meanwhile cpu cost of this computation is optimized // Fix me: temproary reduction meanwhile cpu cost of this computation is optimized
buf_stat = rb->buffer_status(rb, 1000 * 1000); buf_stat = rb->buffer_status(rb, 1000 * 1000);
ret.bytes_in_buffer = buf_stat.status_size + buf_stat.retx_size + buf_stat.tx_size; ret.bytes_in_buffer = buf_stat.status_size + buf_stat.retx_size + buf_stat.tx_size;
ret.oldest_sdu_arrival_ms = buf_stat.oldest_sdu_arrival_ms;
} else { } else {
if (!(frame % 128) || channel_idP == 0) //to suppress this warning message if (!(frame % 128) || channel_idP == 0) //to suppress this warning message
LOG_W(RLC, "Radio Bearer (channel ID %d) is NULL for UE %d\n", channel_idP, ue->ue_id); LOG_W(RLC, "Radio Bearer (channel ID %d) is NULL for UE %d\n", channel_idP, ue->ue_id);
ret.bytes_in_buffer = 0; ret.bytes_in_buffer = 0;
ret.oldest_sdu_arrival_ms = 0;
} }
ret.pdus_in_buffer = 0; ret.pdus_in_buffer = 0;

View File

@@ -81,6 +81,8 @@ int nr_rlc_tx_list_occupancy(int ue_id, logical_chan_id_t lcid);
void nr_rlc_activate_avg_time_to_tx(const int ue_id, const logical_chan_id_t channel_id, const bool is_on); void nr_rlc_activate_avg_time_to_tx(const int ue_id, const logical_chan_id_t channel_id, const bool is_on);
uint64_t get_nr_rlc_current_time(void);
void nr_rlc_srb_recv_sdu(const int ue_id, const logical_chan_id_t channel_id, unsigned char *buf, int size); void nr_rlc_srb_recv_sdu(const int ue_id, const logical_chan_id_t channel_id, unsigned char *buf, int size);
bool nr_rlc_activate_srb0(int ue_id, bool nr_rlc_activate_srb0(int ue_id,

View File

@@ -25,6 +25,11 @@ typedef struct nr_rlc_sdu_t {
* transmission is used for statistics * transmission is used for statistics
*/ */
uint64_t time_of_arrival; /* unit microsecond */ uint64_t time_of_arrival; /* unit microsecond */
/* RLC time when the SDU was first received .Used for the HOL-delay
* in MAC. TODO: unify with time_of_arrival (feed E2AP/KPM in us)
*/
uint64_t arrival_ms;
} nr_rlc_sdu_t; } nr_rlc_sdu_t;
typedef struct nr_rlc_sdu_segment_t { typedef struct nr_rlc_sdu_segment_t {