mac: MU-MIMO diagnostic instrumentation + ri_bitlen=0 fix
Two changes on top of the initial MU-MIMO patch (commit 766d0a4):
1. CSI-parse fix in evaluate_cqi_report path: when maxMIMO_layers=1
(and thus ri_Restriction has only one bit set, ri_bitlen=0),
evaluate_ri_report was being skipped, leaving r_index=-1 which
trips the DevAssert(ri>=0) inside evaluate_cqi_report. Fix: when
ri_bitlen=0, pick the single allowed RI directly from the
restriction bitmap. Affects all three reportQuantity branches
(cri_RI_PMI_CQI, cri_RI_LI_PMI_CQI, cri_RI_CQI).
2. MU-MIMO scheduler: relaxed-orthogonality flag and broader
partner search:
- OAI_MU_MIMO_RELAXED=1 bypasses the strict (same-x1, x2-diff-2)
orthogonality test for testbed validation on rfsim where the
symmetric channel makes all UEs report identical PMI.
- Partner search now scans the full UE_sched (skipping leader and
already-paired entries) instead of only iterator+1; this finds
candidates regardless of PF position.
- Diagnostic LOG_W messages on partner-search entry and per-skip
reason (capped to 20 events to avoid log flood).
Findings on this w17 build with 6 UEs at 5Mbps DL each:
- Patch compiles, deploys, gNB does not crash, scheduler reaches
partner-search block on a fraction of slots.
- However eligible_others=0 in every observed partner-search entry:
PF puts only one UE in UE_sched per pf_dl call almost always,
even at cell saturation. The other attached UEs are skipped
earlier (likely HARQ pids pending feedback). So the partner
search has nothing to pair.
- Architectural conclusion: forcing concurrent multi-UE scheduling
on OAI's PF + finite-HARQ scheduler likely needs scanning
connected_ue_list (not just UE_sched), and may need broader
refactoring to pre-allocate slot resources for MU-MIMO pairs.
This commit is contained in:
@@ -627,6 +627,18 @@ static bool pmi_is_orthogonal(const NR_UE_info_t *a, const NR_UE_info_t *b)
|
||||
/* Both UEs must report rank-1; other ranks need codebook-specific orthogonality. */
|
||||
if (ra->ri != 0 || rb->ri != 0)
|
||||
return false;
|
||||
/* OAI rfsimulator with a symmetric channel produces identical PMIs across UEs,
|
||||
* so the strict-orthogonality test (same x1, x2 differ by 2) never fires.
|
||||
* OAI_MU_MIMO_RELAXED=1 accepts any rank-1 pair so we can exercise the
|
||||
* co-scheduling path; PHY throughput will not actually improve without true
|
||||
* channel diversity, but the scheduler correctness can be validated. */
|
||||
static int relaxed = -1;
|
||||
if (relaxed < 0) {
|
||||
const char *e = getenv("OAI_MU_MIMO_RELAXED");
|
||||
relaxed = (e && atoi(e) > 0) ? 1 : 0;
|
||||
}
|
||||
if (relaxed)
|
||||
return true;
|
||||
if (ra->pmi_x1 != rb->pmi_x1)
|
||||
return false;
|
||||
/* x2 in {0,1,2,3}; orthogonal pairs are (0,2) and (1,3). */
|
||||
@@ -767,6 +779,12 @@ static void pf_dl(gNB_MAC_INST *mac,
|
||||
|
||||
qsort(UE_sched, numUE, sizeof(UEsched_t), comparator);
|
||||
UEsched_t *iterator = UE_sched;
|
||||
/* nakaolab: diagnostic — log numUE > 1 to understand MU-MIMO opportunity */
|
||||
if (mumimo_enabled() && numUE >= 2) {
|
||||
static int dump_count = 0;
|
||||
if (dump_count++ < 20)
|
||||
LOG_W(NR_MAC, "[nakaolab MU-MIMO] %4d.%2d numUE=%d (PF candidates)\n", frame, slot, numUE);
|
||||
}
|
||||
|
||||
const int min_rbSize = 5;
|
||||
|
||||
@@ -933,13 +951,35 @@ static void pf_dl(gNB_MAC_INST *mac,
|
||||
if (mumimo_enabled() && remainUEs[beam.idx] > 0) {
|
||||
/* For MU-MIMO, the leader UE's DMRS group must be 2 so the second group is
|
||||
* available for the partner. Patch the just-built sched_pdsch in place. */
|
||||
for (UEsched_t *cand = iterator + 1; cand->UE != NULL; cand++) {
|
||||
if (cand->mumimo_paired)
|
||||
static int diag_count = 0;
|
||||
bool log_diag = (diag_count++ < 20);
|
||||
if (log_diag) {
|
||||
int n_cand = 0;
|
||||
for (UEsched_t *c = UE_sched; c->UE != NULL; c++)
|
||||
if (c != iterator && !c->mumimo_paired) n_cand++;
|
||||
LOG_W(NR_MAC, "[mumimo-diag] %4d.%2d ENTER partner-search after RNTI %04x; eligible_others=%d\n",
|
||||
frame, slot, iterator->UE->rnti, n_cand);
|
||||
}
|
||||
/* Scan ALL entries in UE_sched (not just iterator+1) — partner can be at any
|
||||
* position. The leader's PF order doesn't constrain orthogonality. */
|
||||
for (UEsched_t *cand = UE_sched; cand->UE != NULL; cand++) {
|
||||
if (cand == iterator)
|
||||
continue;
|
||||
if (cand->UE->UE_sched_ctrl.available_dl_harq.head < 0)
|
||||
if (cand->mumimo_paired) {
|
||||
if (log_diag) LOG_W(NR_MAC, "[mumimo-diag] %4d.%2d skip RNTI %04x: already paired\n", frame, slot, cand->UE->rnti);
|
||||
continue;
|
||||
if (!pmi_is_orthogonal(iterator->UE, cand->UE))
|
||||
}
|
||||
if (cand->UE->UE_sched_ctrl.available_dl_harq.head < 0) {
|
||||
if (log_diag) LOG_W(NR_MAC, "[mumimo-diag] %4d.%2d skip RNTI %04x: no HARQ\n", frame, slot, cand->UE->rnti);
|
||||
continue;
|
||||
}
|
||||
if (!pmi_is_orthogonal(iterator->UE, cand->UE)) {
|
||||
if (log_diag) LOG_W(NR_MAC, "[mumimo-diag] %4d.%2d skip RNTI %04x: PMI not orthogonal (leader ri=%d, cand ri=%d)\n",
|
||||
frame, slot, cand->UE->rnti,
|
||||
iterator->UE->UE_sched_ctrl.CSI_report.cri_ri_li_pmi_cqi_report.ri,
|
||||
cand->UE->UE_sched_ctrl.CSI_report.cri_ri_li_pmi_cqi_report.ri);
|
||||
continue;
|
||||
}
|
||||
/* Candidate's beam must match ours so the precoding is on the same TRX
|
||||
* resources. (Different beams would also work but our config has a
|
||||
* single beam pool.) */
|
||||
@@ -947,6 +987,7 @@ static void pf_dl(gNB_MAC_INST *mac,
|
||||
cand->UE->UE_beam_index,
|
||||
slots_per_frame);
|
||||
if (pbeam.idx != beam.idx) {
|
||||
if (log_diag) LOG_W(NR_MAC, "[mumimo-diag] %4d.%2d skip RNTI %04x: beam idx %d != leader %d\n", frame, slot, cand->UE->rnti, pbeam.idx, beam.idx);
|
||||
reset_beam_status(&mac->beam_info, frame, slot, cand->UE->UE_beam_index,
|
||||
slots_per_frame, pbeam.new_beam);
|
||||
continue;
|
||||
@@ -960,6 +1001,7 @@ static void pf_dl(gNB_MAC_INST *mac,
|
||||
&cand->UE->UE_sched_ctrl.sched_pdcch,
|
||||
cand->UE->UE_sched_ctrl.pdcch_cl_adjust);
|
||||
if (partner_CCE < 0) {
|
||||
if (log_diag) LOG_W(NR_MAC, "[mumimo-diag] %4d.%2d skip RNTI %04x: no free CCE\n", frame, slot, cand->UE->rnti);
|
||||
reset_beam_status(&mac->beam_info, frame, slot, cand->UE->UE_beam_index,
|
||||
slots_per_frame, pbeam.new_beam);
|
||||
continue;
|
||||
|
||||
@@ -763,8 +763,20 @@ static void extract_pucch_csi_report(NR_CSI_MeasConfig_t *csi_MeasConfig,
|
||||
evaluate_cri_report(payload, cri_bitlen, cumul_bits, sched_ctrl);
|
||||
cumul_bits += cri_bitlen;
|
||||
ri_bitlen = csi_report->csi_meas_bitlen.ri_bitlen;
|
||||
if (ri_bitlen)
|
||||
if (ri_bitlen) {
|
||||
r_index = evaluate_ri_report(payload, ri_bitlen, csi_report->csi_meas_bitlen.ri_restriction, cumul_bits, sched_ctrl);
|
||||
} else {
|
||||
/* nakaolab: ri_bitlen=0 (single RI allowed via ri_restriction).
|
||||
* Pick that RI directly — without this, r_index stays -1 and trips
|
||||
* the DevAssert(ri>=0) in evaluate_cqi_report. */
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if ((csi_report->csi_meas_bitlen.ri_restriction >> i) & 0x01) {
|
||||
r_index = i;
|
||||
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
cumul_bits += ri_bitlen;
|
||||
if (r_index != -1)
|
||||
skip_zero_padding(&cumul_bits, csi_report, r_index, bitlen);
|
||||
@@ -777,8 +789,20 @@ static void extract_pucch_csi_report(NR_CSI_MeasConfig_t *csi_MeasConfig,
|
||||
evaluate_cri_report(payload, cri_bitlen, cumul_bits, sched_ctrl);
|
||||
cumul_bits += cri_bitlen;
|
||||
ri_bitlen = csi_report->csi_meas_bitlen.ri_bitlen;
|
||||
if (ri_bitlen)
|
||||
if (ri_bitlen) {
|
||||
r_index = evaluate_ri_report(payload, ri_bitlen, csi_report->csi_meas_bitlen.ri_restriction, cumul_bits, sched_ctrl);
|
||||
} else {
|
||||
/* nakaolab: ri_bitlen=0 (single RI allowed via ri_restriction).
|
||||
* Pick that RI directly — without this, r_index stays -1 and trips
|
||||
* the DevAssert(ri>=0) in evaluate_cqi_report. */
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if ((csi_report->csi_meas_bitlen.ri_restriction >> i) & 0x01) {
|
||||
r_index = i;
|
||||
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
cumul_bits += ri_bitlen;
|
||||
if (r_index != -1)
|
||||
skip_zero_padding(&cumul_bits, csi_report, r_index, bitlen);
|
||||
@@ -794,8 +818,20 @@ static void extract_pucch_csi_report(NR_CSI_MeasConfig_t *csi_MeasConfig,
|
||||
evaluate_cri_report(payload, cri_bitlen, cumul_bits, sched_ctrl);
|
||||
cumul_bits += cri_bitlen;
|
||||
ri_bitlen = csi_report->csi_meas_bitlen.ri_bitlen;
|
||||
if (ri_bitlen)
|
||||
if (ri_bitlen) {
|
||||
r_index = evaluate_ri_report(payload, ri_bitlen, csi_report->csi_meas_bitlen.ri_restriction, cumul_bits, sched_ctrl);
|
||||
} else {
|
||||
/* nakaolab: ri_bitlen=0 (single RI allowed via ri_restriction).
|
||||
* Pick that RI directly — without this, r_index stays -1 and trips
|
||||
* the DevAssert(ri>=0) in evaluate_cqi_report. */
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if ((csi_report->csi_meas_bitlen.ri_restriction >> i) & 0x01) {
|
||||
r_index = i;
|
||||
sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.ri = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
cumul_bits += ri_bitlen;
|
||||
li_bitlen = evaluate_li_report(payload, csi_report, cumul_bits, r_index, sched_ctrl);
|
||||
cumul_bits += li_bitlen;
|
||||
|
||||
Reference in New Issue
Block a user