From f8aeba44f485b08e1ccc5a4070c8912ab5e90046 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 15 Oct 2025 09:28:33 +0200 Subject: [PATCH 1/7] RLC status ind: make only one call per UE --- openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c | 25 +++++++------- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 26 ++++++++------- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 33 +++++++++++-------- .../LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c | 4 +-- openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c | 18 ++++++---- openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h | 2 +- 6 files changed, 62 insertions(+), 46 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index fcc977d9e5..51e69a76eb 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -1452,23 +1452,24 @@ static void nr_update_sr(NR_UE_MAC_INST_t *mac, bool BSRsent) static void nr_update_rlc_buffers_status(NR_UE_MAC_INST_t *mac, frame_t frameP, slot_t slotP, uint8_t gNB_index) { + logical_chan_id_t ch[NR_MAX_NUM_LCID] = {0}; + int n = 0; for (int i = 0; i < mac->lc_ordered_list.count; i++) { nr_lcordered_info_t *lc_info = mac->lc_ordered_list.array[i]; if (lc_info->rb_suspended) continue; - int lcid = lc_info->lcid; + ch[n++] = lc_info->lcid; + } + + mac_rlc_status_resp_t ret[NR_MAX_NUM_LCID] = {0}; + nr_mac_rlc_status_ind(mac->ue_id, frameP, n, ch, ret); + for (int i = 0; i < n; ++i) { + const logical_chan_id_t lcid = ch[i]; + const rlc_buffer_occupancy_t b = ret[i].bytes_in_buffer; NR_LC_SCHEDULING_INFO *lc_sched_info = get_scheduling_info_from_lcid(mac, lcid); - mac_rlc_status_resp_t rlc_status = nr_mac_rlc_status_ind(mac->ue_id, frameP, lcid); - if (rlc_status.bytes_in_buffer > 0) { - LOG_D(NR_MAC, - "[UE %d] LCID %d has %d bytes to transmit at sfn %d.%d\n", - mac->ue_id, - lcid, - rlc_status.bytes_in_buffer, - frameP, - slotP); - } - lc_sched_info->LCID_buffer_remain = rlc_status.bytes_in_buffer; + if (b > 0) + LOG_D(NR_MAC, "[UE %d] LCID %d has %d bytes to transmit at sfn %d.%d\n", mac->ue_id, lcid, b, frameP, slotP); + lc_sched_info->LCID_buffer_remain = b; } } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 1657a492fe..d2c6250070 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -1701,21 +1701,23 @@ static void nr_generate_Msg4_MsgB(module_id_t module_idP, /* get the PID of a HARQ process awaiting retrnasmission, or -1 otherwise */ int current_harq_pid = sched_ctrl->retrans_dl_harq.head; - logical_chan_id_t lcid = DL_SCH_LCID_CCCH; + logical_chan_id_t lcid = 0; if (current_harq_pid < 0) { - // Check for data on SRB0 (RRCSetup) - mac_rlc_status_resp_t srb_status = nr_mac_rlc_status_ind(UE->rnti, frameP, lcid); + const logical_chan_id_t lcid_l[2] = {DL_SCH_LCID_CCCH, DL_SCH_LCID_DCCH}; + mac_rlc_status_resp_t stat[2]; + nr_mac_rlc_status_ind(UE->rnti, frameP, 2, lcid_l, stat); - if (srb_status.bytes_in_buffer == 0) { - lcid = DL_SCH_LCID_DCCH; - // Check for data on SRB1 (RRCReestablishment, RRCReconfiguration) - srb_status = nr_mac_rlc_status_ind(UE->rnti, frameP, lcid); + if (stat[0].bytes_in_buffer > 0) { + // some data in SRB0 (likely RRCSetup) + mac_sdu_length = stat[0].bytes_in_buffer; + lcid = lcid_l[0]; + } else if (stat[1].bytes_in_buffer > 0) { + // some data in SRB1 (likely RRCReestablishmnt/RRCReconfiguration) + mac_sdu_length = stat[1].bytes_in_buffer; + lcid = lcid_l[1]; + } else { + return; // no data to forward } - - // Need to wait until data for Msg4 is ready - if (srb_status.bytes_in_buffer == 0) - return; - mac_sdu_length = srb_status.bytes_in_buffer; } const int n_slots_frame = nr_mac->frame_structure.numb_slots_frame; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 921003d110..075e46dd52 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -326,31 +326,38 @@ static uint32_t update_dlsch_buffer(frame_t frame, slot_t slot, NR_UE_info_t *UE sched_ctrl->num_total_bytes = 0; sched_ctrl->dl_pdus_total = 0; + logical_chan_id_t ch[NR_MAX_NUM_LCID] = {0}; + int n = 0; /* loop over all activated logical channels */ - for (int i = 0; i < seq_arr_size(&sched_ctrl->lc_config); ++i) { - const nr_lc_config_t *c = seq_arr_at(&sched_ctrl->lc_config, i); - const int lcid = c->lcid; - const uint16_t rnti = UE->rnti; - LOG_D(NR_MAC, "UE %x: LCID %d\n", rnti, lcid); - memset(&sched_ctrl->rlc_status[lcid], 0, sizeof(sched_ctrl->rlc_status[lcid])); - if (c->suspended) + FOR_EACH_SEQ_ARR(const nr_lc_config_t *, c, &sched_ctrl->lc_config ) { + logical_chan_id_t lcid = c->lcid; + if (c->suspended || (lcid == DL_SCH_LCID_DTCH && nr_timer_is_active(&sched_ctrl->transm_interrupt))) { + memset(&sched_ctrl->rlc_status[lcid], 0, sizeof(sched_ctrl->rlc_status[lcid])); continue; - if (lcid == DL_SCH_LCID_DTCH && nr_timer_is_active(&sched_ctrl->transm_interrupt)) - continue; - sched_ctrl->rlc_status[lcid] = nr_mac_rlc_status_ind(rnti, frame, lcid); + } + ch[n++] = lcid; + } - if (sched_ctrl->rlc_status[lcid].bytes_in_buffer == 0) + mac_rlc_status_resp_t ret[NR_MAX_NUM_LCID] = {0}; + nr_mac_rlc_status_ind(UE->rnti, frame, n, ch, ret); + + for (int i = 0; i < n; ++i) { + logical_chan_id_t lcid = ch[i]; + + if (ret[i].bytes_in_buffer == 0) continue; + sched_ctrl->rlc_status[lcid] = ret[i]; sched_ctrl->dl_pdus_total += sched_ctrl->rlc_status[lcid].pdus_in_buffer; sched_ctrl->num_total_bytes += sched_ctrl->rlc_status[lcid].bytes_in_buffer; LOG_D(MAC, - "%4d.%2d UE %04x LCID %d status: %d bytes, total buffer %d bytes %d PDUs\n", + "%4d.%2d UE %04x LCID %d status: %d bytes, %d PDUs, total buffer %d bytes %d PDUs\n", frame, slot, UE->rnti, lcid, - sched_ctrl->rlc_status[lcid].bytes_in_buffer, + ret[i].bytes_in_buffer, + ret[i].pdus_in_buffer, sched_ctrl->num_total_bytes, sched_ctrl->dl_pdus_total); } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c index f23dfe8b30..d78ae9280f 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c @@ -144,11 +144,11 @@ void nr_preprocessor_phytest(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pdsch) sched_ctrl->num_total_bytes = 0; DevAssert(seq_arr_size(&sched_ctrl->lc_config) == 1); const nr_lc_config_t *c = seq_arr_at(&sched_ctrl->lc_config, 0); - const int lcid = c->lcid; + const logical_chan_id_t lcid = c->lcid; const uint16_t rnti = UE->rnti; /* update sched_ctrl->num_total_bytes so that postprocessor schedules data, * if available */ - sched_ctrl->rlc_status[lcid] = nr_mac_rlc_status_ind(rnti, frame, lcid); + nr_mac_rlc_status_ind(rnti, frame, 1, &lcid, &sched_ctrl->rlc_status[lcid]); sched_ctrl->num_total_bytes += sched_ctrl->rlc_status[lcid].bytes_in_buffer; int CCEIndex = get_cce_index(mac, diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c index 126f9fb51c..4597b96478 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c @@ -223,12 +223,10 @@ tbs_size_t nr_mac_rlc_data_req(const module_id_t module_idP, return ret; } -mac_rlc_status_resp_t nr_mac_rlc_status_ind(const uint16_t ue_id, const frame_t frame, const logical_chan_id_t channel_idP) +static mac_rlc_status_resp_t _nr_rlc_status_ind(nr_rlc_ue_t *ue, frame_t frame, logical_chan_id_t channel_idP) { mac_rlc_status_resp_t ret; - nr_rlc_manager_lock(nr_rlc_ue_manager); - nr_rlc_ue_t *ue = nr_rlc_manager_get_ue(nr_rlc_ue_manager, ue_id); nr_rlc_entity_t *rb = get_rlc_entity_from_lcid(ue, channel_idP); if (rb != NULL) { @@ -243,12 +241,10 @@ mac_rlc_status_resp_t nr_mac_rlc_status_ind(const uint16_t ue_id, const frame_t ret.bytes_in_buffer = buf_stat.status_size + buf_stat.retx_size + buf_stat.tx_size; } else { 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_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; } - nr_rlc_manager_unlock(nr_rlc_ue_manager); - ret.pdus_in_buffer = 0; /* TODO: creation time may be important (unit: frame, as it seems) */ ret.head_sdu_creation_time = 0; @@ -257,6 +253,16 @@ mac_rlc_status_resp_t nr_mac_rlc_status_ind(const uint16_t ue_id, const frame_t return ret; } +void nr_mac_rlc_status_ind(uint16_t ue_id, frame_t frame, int n_ch, const logical_chan_id_t *ch, mac_rlc_status_resp_t *ret) +{ + nr_rlc_manager_lock(nr_rlc_ue_manager); + nr_rlc_ue_t *ue = nr_rlc_manager_get_ue(nr_rlc_ue_manager, ue_id); + for (int i = 0; i < n_ch; ++i) { + ret[i] = _nr_rlc_status_ind(ue, frame, ch[i]); + } + nr_rlc_manager_unlock(nr_rlc_ue_manager); +} + rlc_op_status_t nr_rlc_data_req(const protocol_ctxt_t *const ctxt_pP, const srb_flag_t srb_flagP, const rb_id_t rb_idP, diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h index 454a59b928..c426aa8d6e 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h +++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h @@ -59,7 +59,7 @@ rlc_op_status_t nr_rlc_data_req(const protocol_ctxt_t *const ctxt_pP, const mui_t muiP, sdu_size_t sdu_sizeP, uint8_t *sdu_pP); -mac_rlc_status_resp_t nr_mac_rlc_status_ind(const uint16_t ue_id, const frame_t frame, const logical_chan_id_t channel_idP); +void nr_mac_rlc_status_ind(uint16_t ue_id, frame_t frame, int n_ch, const logical_chan_id_t *ch, mac_rlc_status_resp_t *ret); void nr_rlc_add_srb(int ue_id, int srb_id, const NR_RLC_BearerConfig_t *rlc_BearerConfig); void nr_rlc_add_drb(int ue_id, int drb_id, const NR_RLC_BearerConfig_t *rlc_BearerConfig); From d0a6be27daa618efcc0bc0b917ae3d42887ed4b4 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 28 Jan 2026 08:15:29 +0100 Subject: [PATCH 2/7] RLC data ind: change API to handle multiple TB, but use only one --- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 6 +++-- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 9 ++++--- openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c | 27 ++++++++++--------- openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h | 10 ++++--- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index 672bc3c3a2..ffe1c99823 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -3971,7 +3971,8 @@ static void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_ if (mac_len > 0) { LOG_DDUMP(NR_MAC, (void *)pduP, mac_subheader_len + mac_len, LOG_DUMP_CHAR, "DL_SCH_LCID_CCCH (e.g. RRCSetup) payload: "); - nr_mac_rlc_data_ind(mac->ue_id, mac->ue_id, false, rx_lcid, (char *)(pduP + mac_subheader_len), mac_len); + nr_rlc_data_ind_t ind = {.ch = rx_lcid, .buf = pduP + mac_subheader_len, .len = mac_len}; + nr_mac_rlc_data_ind(mac->ue_id, mac->ue_id, false, &ind, 1); } break; case DL_SCH_LCID_TCI_STATE_ACT_UE_SPEC_PDSCH: @@ -4091,7 +4092,8 @@ static void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_ break; } LOG_D(NR_MAC, "%4d.%2d : DLSCH -> LCID %d %d bytes\n", frameP, slot, rx_lcid, mac_len); - nr_mac_rlc_data_ind(mac->ue_id, mac->ue_id, false, rx_lcid, (char *)(pduP + mac_subheader_len), mac_len); + nr_rlc_data_ind_t ind = {.ch = rx_lcid, .buf = pduP + mac_subheader_len, .len = mac_len}; + nr_mac_rlc_data_ind(mac->ue_id, mac->ue_id, false, &ind, 1); break; default: LOG_W(MAC, "unknown lcid %02x\n", rx_lcid); diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index 3282c4a483..ca46f52e9a 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -456,7 +456,8 @@ static int nr_process_mac_pdu(instance_t module_idP, } if (prepare_initial_ul_rrc_message(RC.nrmac[module_idP], UE)) { - nr_mac_rlc_data_ind(module_idP, UE->rnti, true, 0, (char *)(pduP + mac_subheader_len), mac_len); + nr_rlc_data_ind_t ind = {.ch = 0, .buf = pduP + mac_subheader_len, .len = mac_len}; + nr_mac_rlc_data_ind(module_idP, UE->rnti, true, &ind, 1); } else { LOG_E(NR_MAC, "prepare_initial_ul_rrc_message() returned false, cannot forward CCCH message\n"); } @@ -475,7 +476,8 @@ static int nr_process_mac_pdu(instance_t module_idP, if (!srbc || srbc->suspended) { LOG_I(NR_MAC, "RNTI %04x LCID %d: ignoring %d bytes\n", UE->rnti, lcid, mac_len); } else { - nr_mac_rlc_data_ind(module_idP, UE->rnti, true, lcid, (char *)(pduP + mac_subheader_len), mac_len); + nr_rlc_data_ind_t ind = {.ch = lcid, .buf = pduP + mac_subheader_len, .len = mac_len}; + nr_mac_rlc_data_ind(module_idP, UE->rnti, true, &ind, 1); UE->mac_stats.ul.total_sdu_bytes += mac_len; UE->mac_stats.ul.lc_bytes[lcid] += mac_len; @@ -499,7 +501,8 @@ static int nr_process_mac_pdu(instance_t module_idP, } else { UE->mac_stats.ul.lc_bytes[lcid] += mac_len; - nr_mac_rlc_data_ind(module_idP, UE->rnti, true, lcid, (char *)(pduP + mac_subheader_len), mac_len); + nr_rlc_data_ind_t ind = {.ch = lcid, .buf = pduP + mac_subheader_len, .len = mac_len}; + nr_mac_rlc_data_ind(module_idP, UE->rnti, true, &ind, 1); sdus += 1; /* Updated estimated buffer when receiving data */ diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c index 4597b96478..9710df661e 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c @@ -163,12 +163,12 @@ void nr_rlc_release_entity(int ue_id, logical_chan_id_t channel_id) void nr_mac_rlc_data_ind(const module_id_t module_idP, const uint16_t ue_id, const bool gnb_flagP, - const logical_chan_id_t channel_idP, - char *buffer_pP, - const tb_size_t tb_sizeP) + const nr_rlc_data_ind_t *data, + int num_data) { if (gnb_flagP) - T(T_ENB_RLC_MAC_UL, T_INT(module_idP), T_INT(ue_id), T_INT(channel_idP), T_INT(tb_sizeP)); + for (int i = 0; i < num_data; ++i) + T(T_ENB_RLC_MAC_UL, T_INT(module_idP), T_INT(ue_id), T_INT(data[i].ch), T_INT(data[i].len)); nr_rlc_manager_lock(nr_rlc_ue_manager); nr_rlc_ue_t *ue = nr_rlc_manager_get_ue(nr_rlc_ue_manager, ue_id); @@ -176,15 +176,16 @@ void nr_mac_rlc_data_ind(const module_id_t module_idP, if(ue == NULL) LOG_I(RLC, "RLC instance for the given UE was not found \n"); - nr_rlc_entity_t *rb = get_rlc_entity_from_lcid(ue, channel_idP); - - if (rb != NULL) { - LOG_D(RLC, "RB found! (channel ID %d) \n", channel_idP); - rb->set_time(rb, get_nr_rlc_current_time()); - rb->recv_pdu(rb, buffer_pP, tb_sizeP); - } else { - LOG_E(RLC, "Fatal: no RB found (channel ID %d UE ID %d)\n", channel_idP, ue_id); - // exit(1); + for (int i = 0; i < num_data; ++i) { + logical_chan_id_t ch = data[i].ch; + nr_rlc_entity_t *rb = get_rlc_entity_from_lcid(ue, ch); + if (rb != NULL) { + LOG_D(RLC, "RB found! (channel ID %d) \n", ch); + rb->set_time(rb, get_nr_rlc_current_time()); + rb->recv_pdu(rb, (char *)data[i].buf, data[i].len); + } else { + LOG_W(RLC, "no RB found (channel ID %d UE ID %d)\n", ch, ue_id); + } } nr_rlc_manager_unlock(nr_rlc_ue_manager); diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h index c426aa8d6e..93759ef161 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h +++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h @@ -41,12 +41,16 @@ struct NR_RLC_Config; struct NR_LogicalChannelConfig; int nr_rlc_module_init(nr_rlc_op_mode_t mode); +typedef struct nr_rlc_data_ind { + logical_chan_id_t ch; + uint8_t *buf; + tb_size_t len; +} nr_rlc_data_ind_t; void nr_mac_rlc_data_ind(const module_id_t module_idP, const uint16_t ue_id, const bool gnb_flagP, - const logical_chan_id_t channel_idP, - char *buffer_pP, - const tb_size_t tb_sizeP); + const nr_rlc_data_ind_t *data, + int num_data); tbs_size_t nr_mac_rlc_data_req(const module_id_t module_idP, const uint16_t ue_id, const bool gnb_flagP, From 2e0493632d35d05555ca094f984326ba9100b584 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 28 Jan 2026 09:00:11 +0100 Subject: [PATCH 3/7] RLC data ind: single call for complete TB at gNB --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index ca46f52e9a..ac6a0d6495 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -37,6 +37,7 @@ #include "LAYER2/nr_rlc/nr_rlc_oai_api.h" //#define SRS_IND_DEBUG +#define MAX_NUM_DATA_IND 1024 /* \brief Get the number of UL TDAs that could be used in slot, reachable * via specific k2. The output parameter first_idx is a pointer to the first @@ -395,6 +396,8 @@ static int nr_process_mac_pdu(instance_t module_idP, log_dump(NR_MAC, pduP, pdu_len, LOG_DUMP_CHAR, "\n"); #endif + nr_rlc_data_ind_t data_ind[MAX_NUM_DATA_IND] = {0}; + int num_data_ind = 0; while (pdu_len > 0) { uint16_t mac_len = 0; uint8_t lcid = 0; @@ -416,7 +419,7 @@ static int nr_process_mac_pdu(instance_t module_idP, for (int i = 0; i < print_len; i++) printf("%02x ", pduP[i]); printf("\n"); - return 0; + break; } LOG_D(NR_MAC, @@ -457,7 +460,8 @@ static int nr_process_mac_pdu(instance_t module_idP, if (prepare_initial_ul_rrc_message(RC.nrmac[module_idP], UE)) { nr_rlc_data_ind_t ind = {.ch = 0, .buf = pduP + mac_subheader_len, .len = mac_len}; - nr_mac_rlc_data_ind(module_idP, UE->rnti, true, &ind, 1); + data_ind[num_data_ind++] = ind; + DevAssert(num_data_ind < MAX_NUM_DATA_IND); } else { LOG_E(NR_MAC, "prepare_initial_ul_rrc_message() returned false, cannot forward CCCH message\n"); } @@ -477,7 +481,8 @@ static int nr_process_mac_pdu(instance_t module_idP, LOG_I(NR_MAC, "RNTI %04x LCID %d: ignoring %d bytes\n", UE->rnti, lcid, mac_len); } else { nr_rlc_data_ind_t ind = {.ch = lcid, .buf = pduP + mac_subheader_len, .len = mac_len}; - nr_mac_rlc_data_ind(module_idP, UE->rnti, true, &ind, 1); + data_ind[num_data_ind++] = ind; + DevAssert(num_data_ind < MAX_NUM_DATA_IND); UE->mac_stats.ul.total_sdu_bytes += mac_len; UE->mac_stats.ul.lc_bytes[lcid] += mac_len; @@ -502,7 +507,8 @@ static int nr_process_mac_pdu(instance_t module_idP, UE->mac_stats.ul.lc_bytes[lcid] += mac_len; nr_rlc_data_ind_t ind = {.ch = lcid, .buf = pduP + mac_subheader_len, .len = mac_len}; - nr_mac_rlc_data_ind(module_idP, UE->rnti, true, &ind, 1); + data_ind[num_data_ind++] = ind; + DevAssert(num_data_ind < MAX_NUM_DATA_IND); sdus += 1; /* Updated estimated buffer when receiving data */ @@ -533,7 +539,7 @@ static int nr_process_mac_pdu(instance_t module_idP, case UL_SCH_LCID_SINGLE_ENTRY_PHR: if (harq_pid < 0) { LOG_E(NR_MAC, "Invalid HARQ PID %d\n", harq_pid); - return 0; + continue; } NR_sched_pusch_t *sched_pusch = &sched_ctrl->ul_harq_processes[harq_pid].sched_pusch; @@ -608,14 +614,18 @@ static int nr_process_mac_pdu(instance_t module_idP, case UL_SCH_LCID_PADDING: // End of MAC PDU, can ignore the rest. - return 0; + // 38.321 Sec 6.1.5: "Presence and length of padding is implicit based + // on TB size, size of MAC subPDU(s)." + mac_len = pdu_len - mac_subheader_len; // will make pdu_len go to 0 + break; default: LOG_E(NR_MAC, "RNTI %0x [%d.%d], received unknown MAC header (LCID = 0x%02x)\n", UE->rnti, frameP, slot, lcid); - return -1; + mac_len = pdu_len - mac_subheader_len; // will make pdu_len go to 0 break; } + #ifdef ENABLE_MAC_PAYLOAD_DEBUG if (lcid < 45 || lcid == 52 || lcid == 63) { LOG_I(NR_MAC, "In %s: dumping UL MAC SDU sub-header with length %d (LCID = 0x%02x):\n", __func__, mac_subheader_len, lcid); @@ -632,6 +642,8 @@ static int nr_process_mac_pdu(instance_t module_idP, pdu_len -= (mac_subheader_len + mac_len); } + nr_mac_rlc_data_ind(module_idP, UE->rnti, true, data_ind, num_data_ind); + UE->mac_stats.ul.num_mac_sdu += sdus; return 0; From c0a78e6c4793cf5168bd2c21fb09f8ea697b49b7 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 9 Mar 2026 10:14:46 +0100 Subject: [PATCH 4/7] RLC data ind: single call for complete TB at UE --- openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c | 24 ++++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index ffe1c99823..4b548f0d78 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -3876,6 +3876,8 @@ static int nr_ue_validate_successrar(uint8_t *pduP, return n; } +#define MAX_NUM_DATA_IND 1024 + /////////////////////////////////// // brief: nr_ue_process_mac_pdu // function: parsing DL PDU header @@ -3941,6 +3943,9 @@ static void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_ pdu_len -= n; } + nr_rlc_data_ind_t data_ind[MAX_NUM_DATA_IND] = {0}; + int num_data_ind = 0; + while (!done && pdu_len > 0){ uint16_t mac_len = 0x0000; uint16_t mac_subheader_len = 0x0001; // default to fixed-length subheader = 1-oct @@ -3972,7 +3977,8 @@ static void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_ if (mac_len > 0) { LOG_DDUMP(NR_MAC, (void *)pduP, mac_subheader_len + mac_len, LOG_DUMP_CHAR, "DL_SCH_LCID_CCCH (e.g. RRCSetup) payload: "); nr_rlc_data_ind_t ind = {.ch = rx_lcid, .buf = pduP + mac_subheader_len, .len = mac_len}; - nr_mac_rlc_data_ind(mac->ue_id, mac->ue_id, false, &ind, 1); + data_ind[num_data_ind++] = ind; + DevAssert(num_data_ind < MAX_NUM_DATA_IND); } break; case DL_SCH_LCID_TCI_STATE_ACT_UE_SPEC_PDSCH: @@ -4084,8 +4090,11 @@ static void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_ // MAC SDU // From values 1 to 32 it equals to the identity of the logical channel case 1 ... 32: - if (!get_mac_len(pduP, pdu_len, &mac_len, &mac_subheader_len)) - return; + if (!get_mac_len(pduP, pdu_len, &mac_len, &mac_subheader_len)) { + LOG_E(MAC, "get_mac_len(): invalid pdu_len %d (mac_len %d mac_subheader_len %d)\n", pdu_len, mac_len, mac_subheader_len); + done = 1; + break; + } // discard the received subPDU if RB is suspended if (is_lcid_suspended(mac, rx_lcid)) { LOG_W(NR_MAC, "Received PDU for a suspended RB, corresponding to LCID %d. Dropping it.\n", rx_lcid); @@ -4093,7 +4102,8 @@ static void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_ } LOG_D(NR_MAC, "%4d.%2d : DLSCH -> LCID %d %d bytes\n", frameP, slot, rx_lcid, mac_len); nr_rlc_data_ind_t ind = {.ch = rx_lcid, .buf = pduP + mac_subheader_len, .len = mac_len}; - nr_mac_rlc_data_ind(mac->ue_id, mac->ue_id, false, &ind, 1); + data_ind[num_data_ind++] = ind; + DevAssert(num_data_ind < MAX_NUM_DATA_IND); break; default: LOG_W(MAC, "unknown lcid %02x\n", rx_lcid); @@ -4101,9 +4111,13 @@ static void nr_ue_process_mac_pdu(NR_UE_MAC_INST_t *mac, nr_downlink_indication_ } pduP += (mac_subheader_len + mac_len); pdu_len -= (mac_subheader_len + mac_len); - if (pdu_len < 0) + if (pdu_len < 0) { LOG_E(MAC, "[UE %d][%d.%d] nr_ue_process_mac_pdu, residual mac pdu length %d < 0!\n", mac->ue_id, frameP, slot, pdu_len); + done = 1; + } } + + nr_mac_rlc_data_ind(mac->ue_id, mac->ue_id, false, data_ind, num_data_ind); } /** From 4426fb1a2e53765d855f63df146db0fba6dd721b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 29 Jan 2026 17:26:41 +0100 Subject: [PATCH 5/7] RLC data req: Add API to fill multiple RLC PDUs in one TB, use at gNB Adds the API, which will always prepend a 3 byte header before writing a PDU. This can be used to call less often into RLC, and avoid potential locking. The API is used at the gNB DLSCH scheduler. Also, this removes the "overhead" calculation: since we fill the entire TB at once, RLC will leave space for headers as necessary. --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c | 50 ++++++------------- openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c | 44 ++++++++++++++++ openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h | 16 ++++++ 3 files changed, 75 insertions(+), 35 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 075e46dd52..313260c225 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -50,6 +50,8 @@ #define WORD 32 //#define SIZE_OF_POINTER sizeof (void *) +#define MAX_NUM_DATA_REQ 1024 + int get_dl_tda(const gNB_MAC_INST *nrmac, int slot) { /* we assume that this function is mutex-protected from outside */ @@ -1298,47 +1300,25 @@ void post_process_dlsch(gNB_MAC_INST *nr_mac, post_process_pdsch_t *pdsch, NR_UE if (sched_ctrl->rlc_status[lcid].bytes_in_buffer == 0) continue; // no data for this LC tbs_size_t len = 0; - int lcid_bytes=0; - while (bufEnd-buf > sizeof(NR_MAC_SUBHEADER_LONG) + 1 ) { - // we do not know how much data we will get from RLC, i.e., whether it - // will be longer than 256B or not. Therefore, reserve space for long header, then - // fetch data, then fill real length + + tb_size_t pdu_siz[MAX_NUM_DATA_REQ]; + int num = nr_mac_rlc_multi_data_req(module_id, rnti, true, lcid, bufEnd - buf, (char *)buf, pdu_siz, sizeofArray(pdu_siz)); + DevAssert(num <= sizeofArray(pdu_siz)); + + sdus += num; + int lcid_bytes = 0; + for (int j = 0; j < num; ++j) { NR_MAC_SUBHEADER_LONG *header = (NR_MAC_SUBHEADER_LONG *) buf; - /* limit requested number of bytes to what preprocessor specified, or - * such that TBS is full */ - const rlc_buffer_occupancy_t ndata = min(sched_ctrl->rlc_status[lcid].bytes_in_buffer, - bufEnd-buf-sizeof(NR_MAC_SUBHEADER_LONG)); - tbs_size_t len = nr_mac_rlc_data_req(module_id, - rnti, - true, - lcid, - ndata, - (char *)buf+sizeof(NR_MAC_SUBHEADER_LONG)); - LOG_D(NR_MAC, - "%4d.%2d RNTI %04x: %d bytes from %s %d (ndata %d, remaining size %ld)\n", - frame, - slot, - rnti, - len, - lcid < 4 ? "DCCH" : "DTCH", - lcid, - ndata, - bufEnd-buf-sizeof(NR_MAC_SUBHEADER_LONG)); - - if (len == 0) - break; - - T(T_GNB_MAC_LCID_DL, T_INT(rnti), T_INT(frame), T_INT(slot), T_INT(lcid), T_INT(len * 8), T_INT(nr_rlc_tx_list_occupancy(rnti, lcid))); header->R = 0; header->F = 1; header->LCID = lcid; - header->L = htons(len); - buf += len+sizeof(NR_MAC_SUBHEADER_LONG); - dlsch_total_bytes += len; - lcid_bytes += len; - sdus += 1; + header->L = htons(pdu_siz[j]); + buf += pdu_siz[j] + sizeof(NR_MAC_SUBHEADER_LONG); + dlsch_total_bytes += pdu_siz[j]; + lcid_bytes += pdu_siz[j]; } + T(T_GNB_MAC_LCID_DL, T_INT(rnti), T_INT(frame), T_INT(slot), T_INT(lcid), T_INT(lcid_bytes), T_INT(nr_rlc_tx_list_occupancy(rnti, lcid))); UE->mac_stats.dl.lc_bytes[lcid] += lcid_bytes; } } else if (get_softmodem_params()->phy_test || get_softmodem_params()->do_ra) { diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c index 9710df661e..08741b100b 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c @@ -191,6 +191,50 @@ void nr_mac_rlc_data_ind(const module_id_t module_idP, nr_rlc_manager_unlock(nr_rlc_ue_manager); } +int nr_mac_rlc_multi_data_req(const module_id_t module_idP, + const uint16_t ue_id, + const bool gnb_flagP, + const logical_chan_id_t channel_idP, + tb_size_t tb_sizeP, + char *buffer_pP, + tb_size_t *pdu_siz, + int pdu_siz_len) +{ + int ret = 0; + + nr_rlc_manager_lock(nr_rlc_ue_manager); + nr_rlc_ue_t *ue = nr_rlc_manager_get_ue(nr_rlc_ue_manager, ue_id); + nr_rlc_entity_t *rb = get_rlc_entity_from_lcid(ue, channel_idP); + + if (rb != NULL) { + rb->set_time(rb, get_nr_rlc_current_time()); + while (tb_sizeP - 3 > 0 && pdu_siz_len > 0) { + // fill PDU in buffer, store size of PDU + pdu_siz[0] = rb->generate_pdu(rb, buffer_pP + 3, tb_sizeP - 3); + if (pdu_siz[0] == 0) + break; + LOG_D(RLC, "MAC PDU created: channel_idP %d len %d\n", channel_idP, pdu_siz[0]); + // reserve header + tb_sizeP -= 3 + pdu_siz[0]; + buffer_pP += 3 + pdu_siz[0]; + pdu_siz++; + pdu_siz_len--; + ret++; + } + } else { + LOG_D(RLC, "MAC PDU failed to get created for channel_idP:%d \n", channel_idP); + ret = 0; + } + + nr_rlc_manager_unlock(nr_rlc_ue_manager); + + if (gnb_flagP) + T(T_ENB_RLC_MAC_DL, T_INT(module_idP), T_INT(ue_id), + T_INT(channel_idP), T_INT(ret)); + + return ret; +} + tbs_size_t nr_mac_rlc_data_req(const module_id_t module_idP, const uint16_t ue_id, const bool gnb_flagP, diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h index 93759ef161..cf5d088834 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h +++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h @@ -51,6 +51,22 @@ void nr_mac_rlc_data_ind(const module_id_t module_idP, const bool gnb_flagP, const nr_rlc_data_ind_t *data, int num_data); +/* \brief Fill up to pdu_siz_len RLC PDUs into buffer pointed to by buffer_pP, + * or until tb_sizeP is reached. Each PDU is preceded by 3 bytes (nothing is + * written) that have to be filled by the consumer (with a MAC sub-PDU + * subheader). This function fills up to an entire TB in a single call, unlike + * nr_mac_rlc_data_req() that fills only one RLC PDU in one step. */ +int nr_mac_rlc_multi_data_req(const module_id_t module_idP, + const uint16_t ue_id, + const bool gnb_flagP, + const logical_chan_id_t channel_idP, + const tb_size_t tb_sizeP, + char *buffer_pP, + tb_size_t *pdu_siz, + int pdu_siz_len); +/* \brief Fill one RLC PDU into buffer pointed to by buffer_pP, or until + * tb_sizeP is reached. Note that NO header is prepended (for a MAC sub-PDU + * subheader), and the user of the API has to fill the necessary data. */ tbs_size_t nr_mac_rlc_data_req(const module_id_t module_idP, const uint16_t ue_id, const bool gnb_flagP, From 4c9b7ec679ad73604814b2294cfe0bf7a6f5b09b Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 30 Jan 2026 12:57:26 +0100 Subject: [PATCH 6/7] increase priority of Aerial thread --- nfapi/oai_integration/aerial/fapi_nvIPC.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nfapi/oai_integration/aerial/fapi_nvIPC.c b/nfapi/oai_integration/aerial/fapi_nvIPC.c index bbb0d66900..53717ffdaf 100644 --- a/nfapi/oai_integration/aerial/fapi_nvIPC.c +++ b/nfapi/oai_integration/aerial/fapi_nvIPC.c @@ -355,7 +355,7 @@ void *epoll_recv_task(void *arg) void create_recv_thread(int8_t affinity) { pthread_t thread_id; - threadCreate(&thread_id, epoll_recv_task, NULL, "vnf_nvipc_aerial", affinity, OAI_PRIORITY_RT); + threadCreate(&thread_id, epoll_recv_task, NULL, "vnf_nvipc_aerial", affinity, OAI_PRIORITY_RT_MAX); } int load_hard_code_config(nv_ipc_config_t *config, int module_type, nv_ipc_transport_t _transport) From 17d1e5674ceee26228c94e38892c8500415c2b43 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 30 Jan 2026 16:25:05 +0100 Subject: [PATCH 7/7] reduce max buffer status size --- openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c index 08741b100b..10ff001c49 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c @@ -1074,7 +1074,7 @@ bool nr_rlc_get_statistics(int ue_id, int srb_flag, int rb_id, nr_rlc_statistics // Patch buffer status using OAI results (no need to change anything in the RB) // rb->set_time(rb, get_nr_rlc_current_time()); - nr_rlc_entity_buffer_status_t oai_stat = rb->buffer_status(rb, 1000*1000); + nr_rlc_entity_buffer_status_t oai_stat = rb->buffer_status(rb, 256 * 1000); out->rxbuf_occ_bytes = oai_stat.status_size; out->txbuf_occ_bytes = oai_stat.tx_size + oai_stat.retx_size; } else {