Reset CU-CP E1 UE context after E1 Setup Response
As in deba3ae066 ("Reset CU-UP E1 UE contexts after E1 Setup
Response"), the CU-CP should reset the E1 UE contexts after E1 Setup
Response. 38.463 sec. 8.2.3 says
> This 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.
In the case of CU-CP, this is a bit more complicated. More precisely, we
need to wait for the CU-UP to be available again before informing the DU
about the loss (implemented via an F1 Reset message), because otherwise,
the UE might reconnect immediately, before the CU-UP is online again.
Hence, on connection loss, we mark affected UEs (by setting their E1
association ID to 0); as long as the CU-UP does not connect, nothing
will happen (but UEs don't have user plane). Upon CU-UP connecting
again, we request release of the UEs via NGAP (similarly to what is done
if the DU goes offline, see invalidate_du_connections()), free the UE
contexts locally, and send an F1 Reset message to the DU, because
38.473 sec 8.2.1.2.1 says
> 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.
The core should accept and send an NGAP release command message; since
no UE contexts are to be found (already deleted), the CU-CP will just
send a release complete (together with some warnings).
It might be possible to more gracefully handle the situation (e.g., as
described in 38.401 section 8.9.5 "Change of gNB-CU-UP"), but
- we lack the PDCP SN exchange
- the current procedure has the advantage of being equivalent to
"restart of the entire gNB", with less downtown.
This commit is contained in:
@@ -24,13 +24,17 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
Reference in New Issue
Block a user