RRC multi-cell: adopt cell management in F1 DU configuration update procedures

Migrate F1 DU configuration update procedures in openair2/RRC/NR/rrc_gNB_du.c
to use the cell management API instead of accessing cell data through
setup_req, completing the transition to the new multi-cell architecture.

1. update_cell_info:
   - Refactored to use cell management API: takes RRC instance, old cell_id,
     and new cell info; finds cell via get_cell_by_cell_id() internally.
   - Returns cell container pointer (or NULL on failure) instead of void.
   - When cell_id changes: removes cell from RB tree (old key), updates via
     cp_f1_served_cell_info_to_cell(), re-inserts (new key); validates no
     duplicate cell_id/PCI before allowing change.
   - Frees old MTC when new measurement timing config is present.
   - Encapsulates cell lookup and update logic.

2. rrc_gNB_process_f1_du_configuration_update:
   - Uses update_cell_info() for cell modify; validates cell belongs to DU
     (assoc_id) after update.
   - Duplicate checking via get_cell_by_cell_id() and
     rrc_get_cell_by_pci_for_du()
   - For cells to add: validates duplicate cell_id globally and duplicate
     PCI within DU; rejects with logging.
   - MIB/SIB1 updated in cell container (cell->mib, cell->sib1); uses
     du->gNB_DU_id; passes cell to label_intra_frequency_neighbours();
     removes direct setup_req->cell[0] access.

Backward compatibility: setup_req remains until migration is completed;
cell-specific data is managed in the cell container and accessed via
the cell management API. Improves separation of concerns and enables
proper multi-cell support.
This commit is contained in:
Guido Casati
2026-02-05 11:44:09 +01:00
parent 95d35b9b7d
commit 15c79b0288
+120 -40
View File
@@ -500,6 +500,7 @@ static int invalidate_du_connections(gNB_RRC_INST *rrc, sctp_assoc_t assoc_id)
uint32_t ue_id = UE->rrc_ue_id;
if (UE->ho_context != NULL)
LOG_W(NR_RRC, "DU disconnected while handover for UE %d active\n", ue_id);
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_id);
if (ue_data.du_assoc_id != assoc_id)
continue; /* this UE is on another DU */
@@ -526,26 +527,77 @@ static int invalidate_du_connections(gNB_RRC_INST *rrc, sctp_assoc_t assoc_id)
return count;
}
static void update_cell_info(nr_rrc_du_container_t *du, const f1ap_served_cell_info_t *new_ci)
/** @brief Update cell information from F1AP DU configuration update
* @param[in] rrc RRC instance (needed to get DU and check PCI duplicates)
* @param[in] old_nci The cell_id used to find the cell in the tree
* @param[in] new_ci Pointer to the new cell information from F1AP message
* @return Pointer to the updated cell container, or NULL on failure
* @note RB tree manipulation: The cell tree is keyed by cell_id. If the cell_id changes
* during update, the node must be removed from the tree before updating (using the
* old cell_id) and re-inserted after updating (using the new cell_id). This is
* necessary because RB trees maintain ordering based on the key value. If cell_id
* doesn't change, no tree manipulation is needed. */
static nr_rrc_cell_container_t *update_cell_info(gNB_RRC_INST *rrc, const uint64_t old_nci, const f1ap_served_cell_info_t *new_ci)
{
DevAssert(du != NULL);
DevAssert(rrc != NULL);
DevAssert(new_ci != NULL);
AssertFatal(du->setup_req->num_cells_available == 1, "expected 1 cell for DU, but has %d\n", du->setup_req->num_cells_available);
f1ap_served_cell_info_t *ci = &du->setup_req->cell[0].info;
// make sure no memory is allocated
free_f1ap_cell(ci, NULL);
*ci = copy_f1ap_served_cell_info(new_ci);
NR_MeasurementTimingConfiguration_t *new_mtc =
extract_mtc(new_ci->measurement_timing_config, new_ci->measurement_timing_config_len);
if (new_mtc != NULL) {
ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, du->mtc);
du->mtc = new_mtc;
} else {
LOG_E(NR_RRC, "error decoding MeasurementTimingConfiguration during cell update, ignoring new config\n");
ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, new_mtc);
struct rrc_cell_tree *cells = &rrc->cells;
// Find the cell by old cell_id from the update message
nr_rrc_cell_container_t *cell = get_cell_by_cell_id(cells, old_nci);
if (cell == NULL) {
LOG_W(NR_RRC, "no cell with ID %ld found, ignoring gNB-DU configuration update\n", old_nci);
return NULL;
}
// Check if PCI will change - if so, check for duplicate PCI within the same DU
bool pci_changed = (cell->info.pci != new_ci->nr_pci);
if (pci_changed) {
nr_rrc_du_container_t *du = get_du_by_assoc_id(rrc, cell->assoc_id);
if (du != NULL) {
// Check if new PCI would conflict with another cell in this DU (excluding current cell)
nr_rrc_cell_container_t *existing_pci = rrc_get_cell_by_pci_for_du(&du->cells, new_ci->nr_pci);
if (existing_pci != NULL && existing_pci != cell) {
LOG_E(NR_RRC,
"Cannot change PCI from %d to %d: PCI %d already exists in this DU (cell ID %lu)\n",
cell->info.pci,
new_ci->nr_pci,
new_ci->nr_pci,
existing_pci->info.cell_id);
return NULL;
}
}
}
// Check if cell_id will change - if so, need to update tree structure
bool cell_id_changed = (old_nci != new_ci->nr_cellid);
if (cell_id_changed) {
// Check if new cell_id would create a duplicate (excluding current cell)
nr_rrc_cell_container_t *existing = get_cell_by_cell_id(cells, new_ci->nr_cellid);
if (existing != NULL && existing != cell) {
LOG_E(NR_RRC, "Cannot change cell_id from %ld to %ld: new cell_id already exists\n", old_nci, new_ci->nr_cellid);
return NULL;
}
// Remove from tree before updating cell_id
nr_rrc_cell_container_t *removed = RB_REMOVE(rrc_cell_tree, cells, cell);
AssertFatal(removed == cell, "Failed to remove cell %ld from tree\n", old_nci);
}
// Free old MTC before updating
if (new_ci->measurement_timing_config_len > 0)
ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, cell->mtc);
// Update cell information from the new F1AP served cell info
cp_f1_served_cell_info_to_cell(cell, new_ci);
// If cell_id changed, re-insert into tree with new cell_id
if (cell_id_changed) {
nr_rrc_cell_container_t *existing = RB_INSERT(rrc_cell_tree, cells, cell);
AssertFatal(existing == NULL, "Failed to insert cell %ld into tree\n", cell->info.cell_id);
LOG_I(NR_RRC, "Updated cell_id from %ld to %ld\n", old_nci, cell->info.cell_id);
}
return cell;
}
void rrc_gNB_process_f1_du_configuration_update(f1ap_gnb_du_configuration_update_t *conf_up, sctp_assoc_t assoc_id)
@@ -562,55 +614,83 @@ void rrc_gNB_process_f1_du_configuration_update(f1ap_gnb_du_configuration_update
nr_rrc_du_container_t *du = get_du_by_assoc_id(rrc, assoc_id);
AssertError(du != NULL, return, "no DU found for assoc_id %d\n", assoc_id);
const f1ap_served_cell_info_t *info = &du->setup_req->cell[0].info;
if (conf_up->num_cells_to_add > 0) {
// Here we check if the number of cell limit is respectet, otherwise send failure
LOG_W(NR_RRC, "du_configuration_update->cells_to_add_list is not supported yet");
// Validate each cell to add for uniqueness (cell_id globally; PCI within this DU)
for (uint16_t i = 0; i < conf_up->num_cells_to_add; i++) {
f1ap_served_cell_info_t *cell_info = &conf_up->cell_to_add[i].info;
// Check for duplicate cell_id globally
nr_rrc_cell_container_t *existing = get_cell_by_cell_id(&rrc->cells, cell_info->nr_cellid);
if (existing != NULL) {
nr_rrc_du_container_t *existing_du = get_du_by_assoc_id(rrc, existing->assoc_id);
const char *du_name = existing_du ? existing_du->gNB_DU_name : "unknown";
LOG_E(NR_RRC,
"Cell ID %lu already exists in DU %s (assoc_id %d), rejecting cell addition\n",
cell_info->nr_cellid,
du_name,
existing->assoc_id);
/** @todo send failure message */
return;
}
// Check for duplicate PCI within this DU
nr_rrc_cell_container_t *existing_pci = rrc_get_cell_by_pci_for_du(&du->cells, cell_info->nr_pci);
if (existing_pci != NULL) {
LOG_E(NR_RRC,
"PCI %d already exists in this DU (cell ID %lu), rejecting cell addition (cell ID %lu)\n",
cell_info->nr_pci,
existing_pci->info.cell_id,
cell_info->nr_cellid);
/** @todo send failure message */
return;
}
}
if (conf_up->num_cells_to_modify > 0) {
// here the old nrcgi is used to find the cell information, if it exist then we modify consequently otherwise we fail
AssertFatal(conf_up->num_cells_to_modify == 1, "cannot handle more than one cell!\n");
if (info->nr_cellid != conf_up->cell_to_modify[0].old_nr_cellid) {
LOG_W(NR_RRC, "no cell with ID %ld found, ignoring gNB-DU configuration update\n", conf_up->cell_to_modify[0].old_nr_cellid);
return;
}
const uint64_t old_nci = conf_up->cell_to_modify[0].old_nr_cellid;
const f1ap_served_cell_info_t *new_ci = &conf_up->cell_to_modify[0].info;
// verify the new plmn of the cell
if (!rrc_gNB_plmn_matches(rrc, &conf_up->cell_to_modify[0].info)) {
if (!rrc_gNB_plmn_matches(rrc, new_ci)) {
LOG_W(NR_RRC, "PLMN does not match, ignoring gNB-DU configuration update\n");
return;
}
update_cell_info(du, &conf_up->cell_to_modify[0].info);
nr_rrc_cell_container_t *cell = update_cell_info(rrc, old_nci, new_ci);
if (cell == NULL) {
LOG_W(NR_RRC, "Failed to update cell %ld, ignoring gNB-DU configuration update\n", old_nci);
return;
}
if (cell->assoc_id != du->assoc_id) {
LOG_W(NR_RRC,
"cell %ld belongs to different DU (assoc_id %d vs %d), ignoring gNB-DU configuration update\n",
old_nci,
cell->assoc_id,
du->assoc_id);
return;
}
const f1ap_gnb_du_system_info_t *sys_info = conf_up->cell_to_modify[0].sys_info;
if (sys_info != NULL && sys_info->mib != NULL && !(sys_info->sib1 == NULL && IS_SA_MODE(get_softmodem_params()))) {
// MIB is mandatory, so will be overwritten. SIB1 is optional, so will
// only be overwritten if present in sys_info
ASN_STRUCT_FREE(asn_DEF_NR_MIB, du->mib);
ASN_STRUCT_FREE(asn_DEF_NR_MIB, cell->mib);
if (sys_info->sib1 != NULL) {
ASN_STRUCT_FREE(asn_DEF_NR_SIB1, du->sib1);
du->sib1 = NULL;
ASN_STRUCT_FREE(asn_DEF_NR_SIB1, cell->sib1);
cell->sib1 = NULL;
}
NR_MIB_t *mib = NULL;
if (!extract_sys_info(sys_info, &mib, &du->sib1)) {
LOG_W(NR_RRC, "cannot update sys_info for DU %ld\n", du->setup_req->gNB_DU_id);
if (!extract_sys_info(sys_info, &mib, &cell->sib1)) {
LOG_W(NR_RRC, "cannot update sys_info for DU %ld\n", du->gNB_DU_id);
} else {
DevAssert(mib != NULL);
du->mib = mib;
LOG_I(NR_RRC, "update system information of DU %ld\n", du->setup_req->gNB_DU_id);
cell->mib = mib;
LOG_I(NR_RRC, "update system information of DU %ld\n", du->gNB_DU_id);
}
}
if (du->mib != NULL && du->sib1 != NULL) {
nr_rrc_cell_container_t *cell = get_cell_by_cell_id(&rrc->cells, conf_up->cell_to_modify[0].old_nr_cellid);
if (cell != NULL && cell->assoc_id == du->assoc_id) {
label_intra_frequency_neighbours(rrc, du, cell);
}
if (du->mib != NULL && du->sib1 != NULL && cell != NULL) {
label_intra_frequency_neighbours(rrc, du, cell);
}
}