Refactor DLSCH/ULSCH scheduler: extract proportional fair policy behind function pointer interface

DL/UL Scheduler Refactor: Staged Pipeline with Pluggable Function Pointers

Motivation

The current pf_dl() is a ~600-line monolithic function that mixes infrastructure concerns (UE iteration, HARQ management, CCE allocation, MAC PDU generation) with the scheduling policy (PF priority, RB allocation, MCS selection). This makes it hard to modify the scheduling strategy, test alternatives, or offload scheduling to a GPU (cuMAC).

This MR refactors the DL and UL schedulers into a clean separation between infrastructure and policy, using function pointers for beam allocation and scheduling decisions. The data structures (nr_dl_candidate_t, nr_dl_sched_params_t) are also designed to map directly to cuMAC's cumacCellGrpUeStatus/cumacSchdSol/CumacCellGrpPrms, to make future integration smooth.

Changes

Goodput tracking fix

dl_thr_ue now tracks actual goodput in bps (EWMA of SDU byte deltas per frame) instead of accumulating raw byte counts per slot, which are not as straightforward to interpret (due to variations in TDD patterns mostly). The new calculation matches closely with the throughput measured with e.g. iperf.

Helper extraction from pf_dl() and post_process_dlsch()

  • find_first_available_rbs() — first-fit contiguous RB allocation
  • setup_dl_harq_process() — HARQ process management
  • generate_dl_mac_pdu() — MAC CE + RLC data + padding
  • fill_dl_tx_request() — FAPI TX_req filling

New scheduling interface

  • nr_dl_candidate_t — per-UE flat struct with all immutable inputs (buffer status, BLER, MCS limits, beam, BWP) and outputs (scheduled, rbStart, rbSize, MCS). Kept relatively minimal for now, but it should be easy to add more input metrics in the future.
  • nr_dl_sched_params_t — per-beam cell-level context (VRB map, available RBs, slot bitmap)

Function pointers (DL)

Pointer Default implementation Role
dl_ri_pmi_select nr_dl_ri_pmi_select_default Rank/PMI selection
dl_beam_select nr_dl_beam_select_default Beam direction assignment
dl_tda_select nr_dl_tda_select_default Time-domain allocation
dl_mcs_select nr_dl_mcs_select_default MCS from BLER/SINR
dl_rb_alloc nr_dl_proportional_fair PRB allocation (PF policy)

Function pointers (UL)

Pointer Default implementation Role
ul_ri_tpmi_select nr_ul_ri_tpmi_select_default Rank/TPMI from SRS feedback
ul_beam_select nr_ul_beam_select_default Beam direction assignment
ul_tda_select nr_ul_tda_select_default Time-domain allocation
ul_mcs_select nr_ul_mcs_select_default MCS from BLER/SINR
ul_rb_alloc nr_ul_proportional_fair PRB allocation (retx first, then PF-sorted new-tx)

All default implementations are in gNB_scheduler_dlsch.c (DL) and gNB_scheduler_ulsch.c (UL).

MCS selection flow

The old get_mcs_from_bler() entangled two concerns: updating the BLER estimate from HARQ feedback and deciding the MCS. These are now split:

  • BLER tracking is infrastructure's job: collect_dl_candidates() calls update_dl_bler_stats() which updates the BLER estimate from HARQ round statistics.
  • MCS selection is the policy's job: the proportional fair policy calls select_mcs_from_bler() internally to adapt MCS based on the BLER value. A different policy could use an entirely different MCS strategy (e.g. cuMAC has its own mcsSelectionLUT + OLLA, one could decide to opportunistically lower the MCS while increasing the PRB allocation for reliability in some cases, etc).

For retransmissions, MCS/number of PRBs are passed as hints so the policy can use them as-is if desired, but we don't enforce it (adaptive HARQ possible too: the standard requires us to maintain TBS but in theory it could be achieved via changing the MCS and number of RBs if we wanted to).

Refactored flow

nr_dl_schedule() (formerly pf_dl()):

collect_dl_candidates()     → build candidate array from UE list
schedule_dl_ues()           → beam alloc + per-beam policy calls
  for each scheduled candidate → CCE/PUCCH/TBS validation + post_process

Beam allocation and scheduling policy are two separate function pointers, allowing each to be developed and tested independently (with the goal in the future to add a parameter in the config file for each, and telnet commands to hotswap).

schedule_dl_ues() wraps both into a single function: it first calls beam allocation to assign candidates to beams, then iterates over beams and calls the scheduling policy for each one. cuMAC performs joint beam + PRB allocation on the GPU, so when integrating later it will replace schedule_dl_ues().

Future work

  • Channel matrix H from SRS on candidates for beam-aware scheduling
  • Per-RB channel magnitude derived from SRS on candidates
  • Config file parameters and telnet commands for hotswapping policies
  • cuMAC integration via schedule_dl_ues() replacement
Edited by Maxime Elkael

Merge request reports

Loading