diff --git a/openair2/RRC/NR/rrc_gNB_cuup.c b/openair2/RRC/NR/rrc_gNB_cuup.c index 689670fa85..c065299824 100644 --- a/openair2/RRC/NR/rrc_gNB_cuup.c +++ b/openair2/RRC/NR/rrc_gNB_cuup.c @@ -24,13 +24,17 @@ #include #include #include -#include "PHY/defs_common.h" + +#include "common/utils/alg/find.h" +#include "common/utils/alg/foreach.h" #include "RRC/NR/nr_rrc_proto.h" #include "T.h" #include "as_message.h" #include "assertions.h" #include "common/ran_context.h" #include "common/utils/T/T.h" +#include "rrc_gNB_UE_context.h" +#include "rrc_gNB_du.h" #include "e1ap_messages_types.h" #include "intertask_interface.h" #include "nr_rrc_defs.h" @@ -38,6 +42,7 @@ #include "rrc_messages_types.h" #include "tree.h" #include "e1ap_interface_management.h" +#include "rrc_gNB_NGAP.h" static int cuup_compare(const nr_rrc_cuup_container_t *a, const nr_rrc_cuup_container_t *b) { @@ -168,6 +173,69 @@ static void e1ap_setup_failure(sctp_assoc_t assoc_id, uint64_t transac_id, e1ap_ itti_send_msg_to_task(TASK_CUCP_E1, 0 /*unused by callee*/, msg_p); } +static bool has_assoc_id(const void *vval, const void *vit) +{ + sctp_assoc_t val = *(sctp_assoc_t *)vval; + sctp_assoc_t it = *(sctp_assoc_t *)vit; + return val == it; +} + +/* @brief Remove RRC UE contexts of UE without E1 connection. + * + * 38.463 sec 8.2.3. says that "[E1 Setup] procedure also re-initialises the + * E1AP UE-related contexts (if any) and erases all related signalling + * connections in the two nodes like a Reset procedure would do.". If the CU-UP + * reconnects, it's previous UE contexts are here, but not associated. To + * fulfil the above, we simply free all UE contexts that don't have a CU-UP, + * and send a F1 Reset message to the corresponding DU(s), following the fact + * that (38.473 sec 8.2.1.2.1) "a failure at the gNB-CU [...] has resulted in + * the loss of some or all transaction reference information[...] a RESET + * message shall be sent to the gNB-DU.". This might be overarching in that UEs + * that are in the process of connecting or that are on another DU might be + * affected by that reset procedure as well; we suppose they will connect later + * again. */ +static void remove_unassociated_e1_connections(gNB_RRC_INST *rrc, sctp_assoc_t e1_assoc_id) +{ + seq_arr_t affected_du; + seq_arr_init(&affected_du, sizeof(sctp_assoc_t)); + seq_arr_t ue_context_to_remove; + seq_arr_init(&ue_context_to_remove, sizeof(rrc_gNB_ue_context_t *)); + + rrc_gNB_ue_context_t *ue_context_p = NULL; + RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &rrc->rrc_ue_head) { + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + uint32_t ue_id = UE->rrc_ue_id; + if (ue_associated_to_cuup(rrc, UE)) + continue; + f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_id); + elm_arr_t ret = find_if(&affected_du, &ue_data.du_assoc_id, has_assoc_id); + if (!ret.found) + seq_arr_push_back(&affected_du, &ue_data.du_assoc_id, sizeof(ue_data.du_assoc_id)); + + /* remove UE context: we store the pointer, so pass pointer to ue_context_p */ + seq_arr_push_back(&ue_context_to_remove, &ue_context_p, sizeof(ue_context_p)); + LOG_I(NR_RRC, "remove E1-unassociated UE context ID %d\n", ue_id); + } + + for (int i = 0; i < seq_arr_size(&ue_context_to_remove); ++i) { + /* we retrieve a pointer (=iterator) to the UE context pointer + * (ue_context_p), so dereference once */ + rrc_gNB_ue_context_t *p = *(rrc_gNB_ue_context_t **)seq_arr_at(&ue_context_to_remove, i); + ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_RADIO_CONNECTION_WITH_UE_LOST}; + rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(0, p, cause); + rrc_remove_ue(rrc, p); + } + seq_arr_free(&ue_context_to_remove, NULL); + + for (int i = 0; i < seq_arr_size(&affected_du); ++i) { + void *it = seq_arr_at(&affected_du, i); + sctp_assoc_t du_assoc_id = *(sctp_assoc_t *)it; + LOG_W(NR_RRC, "trigger F1 reset on DU %d due to E1 connection loss\n", du_assoc_id); + trigger_f1_reset(rrc, du_assoc_id); + } + seq_arr_free(&affected_du, NULL); +} + /** * @brief E1AP Setup Request processing on CU-CP */ @@ -216,6 +284,8 @@ int rrc_gNB_process_e1_setup_req(sctp_assoc_t assoc_id, const e1ap_setup_req_t * RB_INSERT(rrc_cuup_tree, &rrc->cuups, cuup); rrc->num_cuups++; + remove_unassociated_e1_connections(rrc, assoc_id); + MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, E1AP_SETUP_RESP); msg_p->ittiMsgHeader.originInstance = assoc_id; e1ap_setup_resp_t *resp = &E1AP_SETUP_RESP(msg_p); @@ -225,6 +295,24 @@ int rrc_gNB_process_e1_setup_req(sctp_assoc_t assoc_id, const e1ap_setup_req_t * return 0; } +/* @brief invalidate (remove) all E1 assoc ID from UE contexts belonging to this CU-UP */ +static void invalidate_cuup_connections(gNB_RRC_INST *rrc, sctp_assoc_t e1_assoc_id) +{ + rrc_gNB_ue_context_t *ue_context_p = NULL; + RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &rrc->rrc_ue_head) { + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + uint32_t ue_id = UE->rrc_ue_id; + f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_id); + if (ue_data.e1_assoc_id != e1_assoc_id) + continue; /* UE is on another CU-UP */ + + ue_data.e1_assoc_id = 0; + bool success = cu_update_f1_ue_data(ue_id, &ue_data); + DevAssert(success); + LOG_W(NR_RRC, "CU-UP %d of UE %d is gone\n", e1_assoc_id, ue_id); + } +} + /** * @brief RRC Processing of the indication of E1 connection loss on CU-CP */ @@ -232,6 +320,7 @@ void rrc_gNB_process_e1_lost_connection(gNB_RRC_INST *rrc, e1ap_lost_connection_ { LOG_I(NR_RRC, "Received E1 connection loss indication on RRC\n"); AssertFatal(assoc_id != 0, "illegal assoc_id == 0: should be -1 (monolithic) or >0 (split)\n"); + invalidate_cuup_connections(rrc, assoc_id); nr_rrc_cuup_container_t e = {.assoc_id = assoc_id}; nr_rrc_cuup_container_t *cuup = RB_FIND(rrc_cuup_tree, &rrc->cuups, &e); if (cuup == NULL) { diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index 802735ee85..402e245dac 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -644,3 +644,14 @@ nr_rrc_du_container_t *find_target_du(gNB_RRC_INST *rrc, sctp_assoc_t source_ass } return target_du; } + +void trigger_f1_reset(gNB_RRC_INST *rrc, sctp_assoc_t du_assoc_id) +{ + f1ap_reset_t reset = { + .transaction_id = F1AP_get_next_transaction_identifier(0, 0), + .cause = F1AP_CAUSE_TRANSPORT, + .cause_value = 1, // F1AP_CauseTransport_transport_resource_unavailable + .reset_type = F1AP_RESET_ALL, // DU does not support partial reset yet + }; + rrc->mac_rrc.f1_reset(du_assoc_id, &reset); +} diff --git a/openair2/RRC/NR/rrc_gNB_du.h b/openair2/RRC/NR/rrc_gNB_du.h index aadac0845a..514416211f 100644 --- a/openair2/RRC/NR/rrc_gNB_du.h +++ b/openair2/RRC/NR/rrc_gNB_du.h @@ -60,4 +60,6 @@ struct nr_rrc_du_container_t *find_target_du(struct gNB_RRC_INST_s *rrc, sctp_as } \ } +void trigger_f1_reset(struct gNB_RRC_INST_s *rrc, sctp_assoc_t du_assoc_id); + #endif /* RRC_GNB_DU_H_ */