mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
Parallelize UL MAC with DL for TDD case
Assisted-By: Claude Code:Opus 4.6
This commit is contained in:
@@ -180,6 +180,44 @@ void *L1_tx_thread(void *arg) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// L2 UL thread: pre-computes UL scheduling for the upcoming TDD period.
|
||||
/// Runs without sched_lock — safe because during TDD DL-only slots no L1 RX
|
||||
/// indications modify UE state, and per-slot resources (CCE, VRB, beam) are
|
||||
/// indexed by slot so no conflict with the DL thread processing a different slot.
|
||||
void *L2_UL_TDD_thread(void *arg) {
|
||||
PHY_VARS_gNB *gNB = (PHY_VARS_gNB *)arg;
|
||||
gNB_MAC_INST *mac = RC.nrmac[0];
|
||||
const frame_structure_t *fs = &mac->frame_structure;
|
||||
const int period_len = fs->numb_slots_period;
|
||||
const int slots_frame = fs->numb_slots_frame;
|
||||
|
||||
// Bypass NR_SCHED_ENSURE_LOCKED asserts — this thread runs without sched_lock
|
||||
nr_sched_lock_bypassed = true;
|
||||
|
||||
while (oai_exit == 0) {
|
||||
// Wait for trigger from DL thread (first DL slot of TDD period)
|
||||
pthread_mutex_lock(&mac->ul_precomp_mutex);
|
||||
while (!mac->ul_precomp_start_req && oai_exit == 0)
|
||||
pthread_cond_wait(&mac->ul_precomp_start_cond, &mac->ul_precomp_mutex);
|
||||
if (oai_exit) { pthread_mutex_unlock(&mac->ul_precomp_mutex); break; }
|
||||
mac->ul_precomp_start_req = false;
|
||||
const int frame = mac->ul_precomp_frame;
|
||||
const int slot0 = mac->ul_precomp_slot;
|
||||
pthread_mutex_unlock(&mac->ul_precomp_mutex);
|
||||
|
||||
// Pre-compute UL scheduling for all DL slots in this TDD period
|
||||
for (int i = 0; i < period_len; i++) {
|
||||
mac->ul_precomp_dci[i].numPdus = 0;
|
||||
const int f = (frame + (slot0 + i) / slots_frame) % 1024;
|
||||
const int s = (slot0 + i) % slots_frame;
|
||||
if (is_dl_slot(s, fs))
|
||||
nr_schedule_ulsch(0, f, s, &mac->ul_precomp_dci[i]);
|
||||
atomic_store_explicit(&mac->ul_precomp_slots_done, i + 1, memory_order_release);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// L2 TX thread: runs MAC scheduling, dispatches PHY TX to L1_tx_thread
|
||||
void *L2_tx_thread(void *arg) {
|
||||
PHY_VARS_gNB *gNB = (PHY_VARS_gNB*)arg;
|
||||
@@ -409,6 +447,17 @@ void init_gNB_Tpool(int inst)
|
||||
gNB_MAC_INST *mac = RC.nrmac[0];
|
||||
threadCreate(&mac->L2_tx_thread, L2_tx_thread, (void *)gNB, "L2_tx_thread", mac->L2_tx_thread_core, OAI_PRIORITY_RT_MAX);
|
||||
|
||||
// L2 UL thread: TDD UL pre-scheduling on dedicated core
|
||||
if (fp->frame_type == TDD) {
|
||||
int period_len = fp->slots_per_frame / get_nb_periods_per_frame(gNB->gNB_config.tdd_table.tdd_period.value);
|
||||
mac->ul_precomp_dci = calloc(period_len, sizeof(*mac->ul_precomp_dci));
|
||||
AssertFatal(mac->ul_precomp_dci, "ul_precomp_dci alloc failed\n");
|
||||
mac->ul_precomp_period_len = period_len;
|
||||
pthread_mutex_init(&mac->ul_precomp_mutex, NULL);
|
||||
pthread_cond_init(&mac->ul_precomp_start_cond, NULL);
|
||||
threadCreate(&mac->L2_UL_TDD_thread, L2_UL_TDD_thread, (void *)gNB, "L2_UL_TDD_thread", mac->L2_ul_tdd_thread_core, OAI_PRIORITY_RT_MAX);
|
||||
}
|
||||
|
||||
if (!IS_SOFTMODEM_NOSTATS)
|
||||
threadCreate(&proc->L1_stats_thread, nrL1_stats_thread, (void *)gNB, "L1_stats", -1, OAI_PRIORITY_RT_LOW);
|
||||
}
|
||||
@@ -425,6 +474,18 @@ void term_gNB_Tpool(int inst) {
|
||||
abortNotifiedFIFO(&gNB->L1_tx_out);
|
||||
pthread_join(gNB->L1_tx_thread, NULL);
|
||||
|
||||
// Stop UL pre-scheduling thread (TDD only)
|
||||
if (gNB->frame_parms.frame_type == TDD) {
|
||||
pthread_mutex_lock(&mac->ul_precomp_mutex);
|
||||
mac->ul_precomp_start_req = true; // wake up so it sees oai_exit
|
||||
pthread_cond_signal(&mac->ul_precomp_start_cond);
|
||||
pthread_mutex_unlock(&mac->ul_precomp_mutex);
|
||||
pthread_join(mac->L2_UL_TDD_thread, NULL);
|
||||
free(mac->ul_precomp_dci);
|
||||
pthread_mutex_destroy(&mac->ul_precomp_mutex);
|
||||
pthread_cond_destroy(&mac->ul_precomp_start_cond);
|
||||
}
|
||||
|
||||
// Free pool elements (malloced=false so abort didn't free them)
|
||||
for (int i = 0; i < gNB->sched_pool_size; i++)
|
||||
free(gNB->sched_pool[i]);
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
#define MACRLC_PUCCH_RSSI_THRESHOLD "pucch_RSSI_Threshold"
|
||||
#define MACRLC_STATS_MAX_UE "stats_max_ue"
|
||||
#define MACRLC_L2_TX_THREAD_CORE "L2_tx_thread_core"
|
||||
#define MACRLC_L2_UL_TDD_THREAD_CORE "L2_ul_tdd_thread_core"
|
||||
|
||||
#define HLP_MACRLC_UL_PRBBLACK "SNR threshold to decide whether a PRB will be blacklisted or not"
|
||||
#define HLP_MACRLC_DL_BLER_UP "Upper threshold of BLER to decrease DL MCS"
|
||||
@@ -125,6 +126,7 @@
|
||||
0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
|
||||
{MACRLC_STATS_MAX_UE, HLP_MACRLC_STATS_MAX_UE, 0, .iptr=NULL, .defintval=8, TYPE_INT, 0}, \
|
||||
{MACRLC_L2_TX_THREAD_CORE, NULL, 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0}, \
|
||||
{MACRLC_L2_UL_TDD_THREAD_CORE, NULL, 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0}, \
|
||||
}
|
||||
// clang-format off
|
||||
|
||||
@@ -172,6 +174,7 @@
|
||||
{ .s2 = { config_check_intrange, {-1280, 0}} }, /* PUCCH RSSI threshold range */ \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
@@ -1712,7 +1712,9 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg)
|
||||
RC.nrmac[j]->ulsch_max_frame_inactivity = *gpd(params, np, MACRLC_ULSCH_MAX_FRAME_INACTIVITY)->uptr;
|
||||
RC.nrmac[j]->stats_max_ue = *gpd(params, np, MACRLC_STATS_MAX_UE)->iptr;
|
||||
RC.nrmac[j]->L2_tx_thread_core = *gpd(params, np, MACRLC_L2_TX_THREAD_CORE)->iptr;
|
||||
LOG_I(NR_MAC, "L2_tx_thread_core %d\n", RC.nrmac[j]->L2_tx_thread_core);
|
||||
RC.nrmac[j]->L2_ul_tdd_thread_core = *gpd(params, np, MACRLC_L2_UL_TDD_THREAD_CORE)->iptr;
|
||||
LOG_I(NR_MAC, "L2_tx_thread_core %d, L2_ul_tdd_thread_core %d\n",
|
||||
RC.nrmac[j]->L2_tx_thread_core, RC.nrmac[j]->L2_ul_tdd_thread_core);
|
||||
RC.nrmac[j]->print_ue_stats = RC.nrmac[j]->stats_max_ue > 0;
|
||||
NR_bler_options_t *dl_bler_options = &RC.nrmac[j]->dl_bler;
|
||||
dl_bler_options->upper = *gpd(params, np, MACRLC_DL_BLER_TARGET_UPPER)->dblptr;
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
__thread bool nr_sched_lock_bypassed = false;
|
||||
|
||||
uint8_t nr_get_rv(int rel_round)
|
||||
{
|
||||
const uint8_t nr_rv_round_map[4] = {0, 2, 3, 1};
|
||||
@@ -213,7 +215,40 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slo
|
||||
|
||||
// This schedules the DCI for Uplink and subsequently PUSCH
|
||||
start_meas(&gNB->schedule_ulsch);
|
||||
nr_schedule_ulsch(module_idP, frame, slot, &sched_info->UL_dci_req);
|
||||
if (scc->tdd_UL_DL_ConfigurationCommon && gNB->ul_precomp_dci) {
|
||||
// TDD with UL pre-scheduling thread active
|
||||
const frame_structure_t *fs = &gNB->frame_structure;
|
||||
const int slot_in_period = slot % fs->numb_slots_period;
|
||||
int done = atomic_load_explicit(&gNB->ul_precomp_slots_done, memory_order_acquire);
|
||||
if (slot_in_period == 0) {
|
||||
// Verify previous period completed, then reset and trigger new period
|
||||
int prev = done;
|
||||
AssertFatal(prev == 0 || prev >= fs->numb_slots_period,
|
||||
"%d.%d previous UL pre-computation incomplete (%d/%d)\n",
|
||||
frame, slot, prev, fs->numb_slots_period);
|
||||
atomic_store_explicit(&gNB->ul_precomp_slots_done, 0, memory_order_release);
|
||||
pthread_mutex_lock(&gNB->ul_precomp_mutex);
|
||||
gNB->ul_precomp_frame = frame;
|
||||
gNB->ul_precomp_slot = slot;
|
||||
gNB->ul_precomp_start_req = true;
|
||||
pthread_cond_signal(&gNB->ul_precomp_start_cond);
|
||||
pthread_mutex_unlock(&gNB->ul_precomp_mutex);
|
||||
done = 0; // just reset
|
||||
}
|
||||
// At startup the first slot may land mid-period (e.g. ORAN timing sync),
|
||||
// before the UL thread has ever been triggered. Schedule inline until the
|
||||
// first period boundary (slot_in_period == 0) is reached.
|
||||
if (done == 0 && slot_in_period != 0) {
|
||||
nr_schedule_ulsch(module_idP, frame, slot, &sched_info->UL_dci_req);
|
||||
} else {
|
||||
while (atomic_load_explicit(&gNB->ul_precomp_slots_done, memory_order_acquire) <= slot_in_period)
|
||||
;
|
||||
if (gNB->ul_precomp_dci[slot_in_period].numPdus > 0)
|
||||
sched_info->UL_dci_req = gNB->ul_precomp_dci[slot_in_period];
|
||||
}
|
||||
} else {
|
||||
nr_schedule_ulsch(module_idP, frame, slot, &sched_info->UL_dci_req);
|
||||
}
|
||||
stop_meas(&gNB->schedule_ulsch);
|
||||
|
||||
// This schedules the DCI for Downlink and PDSCH
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <string.h>
|
||||
#include <complex.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include "fsn.h"
|
||||
#include "common/utils/ds/seq_arr.h"
|
||||
#include "common/utils/nr/nr_common.h"
|
||||
@@ -34,10 +35,16 @@
|
||||
AssertFatal(rc == 0, "error while locking scheduler mutex, pthread_mutex_unlock() returned %d\n", rc); \
|
||||
} while (0)
|
||||
|
||||
/// Set to true on the UL pre-computation thread so it can call scheduler
|
||||
/// functions without holding sched_lock (safe during TDD DL-only slots).
|
||||
extern __thread bool nr_sched_lock_bypassed;
|
||||
|
||||
#define NR_SCHED_ENSURE_LOCKED(lock)\
|
||||
do {\
|
||||
int rc = pthread_mutex_trylock(lock); \
|
||||
AssertFatal(rc == EBUSY, "this function should be called with the scheduler mutex locked, pthread_mutex_trylock() returned %d\n", rc);\
|
||||
if (!nr_sched_lock_bypassed) {\
|
||||
int rc = pthread_mutex_trylock(lock); \
|
||||
AssertFatal(rc == EBUSY, "this function should be called with the scheduler mutex locked, pthread_mutex_trylock() returned %d\n", rc);\
|
||||
}\
|
||||
} while (0)
|
||||
|
||||
/* Commmon */
|
||||
@@ -1000,6 +1007,21 @@ typedef struct gNB_MAC_INST_s {
|
||||
/// Core affinity for L2 TX (MAC scheduling) thread, -1 = floating
|
||||
int L2_tx_thread_core;
|
||||
|
||||
/// TDD UL pre-scheduling: pre-computed UL DCIs per slot in TDD period.
|
||||
/// Filled by L2_UL_TDD_thread at period start; consumed by L2_tx_thread.
|
||||
nfapi_nr_ul_dci_request_t *ul_precomp_dci;
|
||||
int ul_precomp_period_len;
|
||||
pthread_mutex_t ul_precomp_mutex;
|
||||
pthread_cond_t ul_precomp_start_cond;
|
||||
bool ul_precomp_start_req; ///< DL thread sets, UL thread clears
|
||||
_Atomic int ul_precomp_slots_done; ///< UL thread increments per slot, DL thread reads
|
||||
int ul_precomp_frame; ///< frame of the period being pre-computed
|
||||
int ul_precomp_slot; ///< first slot of the period being pre-computed
|
||||
|
||||
pthread_t L2_UL_TDD_thread; ///< UL pre-scheduling thread (TDD)
|
||||
/// Core affinity for L2 UL pre-scheduling thread, -1 = floating
|
||||
int L2_ul_tdd_thread_core;
|
||||
|
||||
dlul_mac_stats_t mac_stats;
|
||||
uint64_t num_scheduled_prach_rx;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user