Commit b6ccf8aa authored by Robert Schmidt's avatar Robert Schmidt

Handle reestablishment during handover

This commit implements reestablishment during handover when occurring at
the source DU, either as a handover Failure or "normal" reestablishment
(something else went wrong). In this case, the CU will trigger HO
cancel, which in F1, means to cancel on the target DU. Also, undo any
previous ID changes. Retain the old CellGroupConfig (before handover)
for the case of reestablishment at source. If not needed, it is freed
after the reconfiguration complete.

Reestablishment on target cell during handover cannot be handled by the
gNB (yet). It is the same as with a normal reestablishment (not during
handover) on a different cell: that cell has no notion of the old UE, so
we cannot simply indicate the old UE to reestablish; at least, we would
need to cleanup the CellGroupConfig, and I am not sure this works out of
the box, either.
parent c6434c80
......@@ -1072,6 +1072,20 @@ static const char *get_reestab_cause(NR_ReestablishmentCause_t c)
return "UNKNOWN Failure (ASN.1 decoder error?)";
}
static rrc_gNB_ue_context_t *rrc_gNB_get_ue_context_source_cell(gNB_RRC_INST *rrc_instance_pP, long pci, rnti_t rntiP)
{
rrc_gNB_ue_context_t *ue_context_p;
RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &rrc_instance_pP->rrc_ue_head) {
gNB_RRC_UE_t *ue = &ue_context_p->ue_context;
if (!ue->ho_context || !ue->ho_context->source)
continue;
nr_ho_source_cu_t *source_ctx = ue->ho_context->source;
if (source_ctx->old_rnti == rntiP && source_ctx->du->setup_req->cell[0].info.nr_pci == pci)
return ue_context_p;
}
return NULL;
}
static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc,
sctp_assoc_t assoc_id,
const NR_RRCReestablishmentRequest_IEs_t *req,
......@@ -1082,7 +1096,12 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc,
const long physCellId = req->ue_Identity.physCellId;
long ngap_cause = NGAP_CAUSE_RADIO_NETWORK_UNSPECIFIED; /* cause in case of NGAP release req */
rrc_gNB_ue_context_t *ue_context_p = NULL;
LOG_I(NR_RRC, "UE %04x physCellId %ld NR_RRCReestablishmentRequest cause %s\n", msg->crnti, physCellId, scause);
LOG_I(NR_RRC,
"Reestablishment RNTI %04x req C-RNTI %04lx physCellId %ld cause %s\n",
msg->crnti,
req->ue_Identity.c_RNTI,
physCellId,
scause);
const nr_rrc_du_container_t *du = get_du_by_assoc_id(rrc, assoc_id);
if (du == NULL) {
......@@ -1117,13 +1136,47 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc,
rnti_t old_rnti = req->ue_Identity.c_RNTI;
ue_context_p = rrc_gNB_get_ue_context_by_rnti(rrc, assoc_id, old_rnti);
if (ue_context_p == NULL) {
LOG_E(NR_RRC, "NR_RRCReestablishmentRequest without UE context, fallback to RRC setup\n");
goto fallback_rrc_setup;
ue_context_p = rrc_gNB_get_ue_context_source_cell(rrc, physCellId, old_rnti);
if (ue_context_p == NULL) {
LOG_E(NR_RRC, "NR_RRCReestablishmentRequest without UE context, fallback to RRC setup\n");
goto fallback_rrc_setup;
}
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
/* should check phys cell ID to identify the correct cell */
const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
if (physCellId != cell_info->nr_pci) {
const f1ap_served_cell_info_t *previous_cell_info = get_cell_information_by_phycellId(physCellId);
nr_ho_source_cu_t *source_ctx = UE->ho_context ? UE->ho_context->source : NULL;
DevAssert(!source_ctx || source_ctx->du->setup_req->num_cells_available == 1);
bool ho_reestab_on_source = source_ctx && previous_cell_info->nr_cellid == source_ctx->du->setup_req->cell[0].info.nr_cellid;
if (ho_reestab_on_source) {
/* the UE came back on the source DU while doing handover, release at
* target DU and and update the association to the initial DU one */
LOG_W(NR_RRC, "handover for UE %d/RNTI %04x failed, rollback to original cell\n", UE->rrc_ue_id, UE->rnti);
// find the transaction of handover (the corresponding reconfig) and abort it
for (int i = 0; i < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++i) {
if (UE->xids[i] == RRC_DEDICATED_RECONF)
UE->xids[i] = RRC_ACTION_NONE;
}
source_ctx->ho_cancel(rrc, UE);
/* we need the original CellGroupConfig */
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
UE->masterCellGroup = source_ctx->old_cellGroupConfig;
source_ctx->old_cellGroupConfig = NULL;
/* update to old DU assoc id -- RNTI + secondary DU UE ID further below */
f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id);
ue_data.du_assoc_id = source_ctx->du->assoc_id;
cu_remove_f1_ue_data(UE->rrc_ue_id);
cu_add_f1_ue_data(UE->rrc_ue_id, &ue_data);
nr_rrc_finalize_ho(UE);
} else if (physCellId != cell_info->nr_pci) {
/* UE was moving from previous cell so quickly that RRCReestablishment for previous cell was received in this cell */
LOG_I(NR_RRC,
"RRC Reestablishment Request from different physCellId (%ld) than current physCellId (%d), fallback to RRC setup\n",
......
......@@ -115,6 +115,9 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
ho_ctx->source->du = source_du;
ho_ctx->source->du_ue_id = ue_data.secondary_ue;
ho_ctx->source->old_rnti = ue->rnti;
int result = asn_copy(&asn_DEF_NR_CellGroupConfig, (void **)&ho_ctx->source->old_cellGroupConfig, ue->masterCellGroup);
AssertFatal(result == 0, "error during asn_copy() of CellGroupConfig\n");
}
ue->ho_context = ho_ctx;
LOG_A(NR_RRC,
......@@ -289,6 +292,20 @@ static void nr_rrc_f1_ho_complete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
LOG_I(NR_RRC, "UE %d Handover: trigger release on DU assoc_id %d\n", UE->rrc_ue_id, source_ctx->du->assoc_id);
}
static void nr_rrc_cancel_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
{
DevAssert(UE->ho_context != NULL);
nr_ho_target_cu_t *target_ctx = UE->ho_context->target;
DevAssert(target_ctx != NULL);
f1ap_ue_context_release_cmd_t cmd = {
.gNB_CU_ue_id = UE->rrc_ue_id,
.gNB_DU_ue_id = target_ctx->du_ue_id,
.cause = F1AP_CAUSE_RADIO_NETWORK, // better
.cause_value = 5, // 5 = F1AP_CauseRadioNetwork_interaction_with_other_procedure
.srb_id = DCCH,
};
rrc->mac_rrc.ue_context_release_command(target_ctx->du->assoc_id, &cmd);
}
void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_container_t *source_du, nr_rrc_du_container_t *target_du)
{
......@@ -306,12 +323,14 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain
// handover preparation information
ho_req_ack_t ack = nr_rrc_f1_ho_acknowledge;
ho_success_t success = nr_rrc_f1_ho_complete;
ho_cancel_t cancel = NULL;
ho_cancel_t cancel = nr_rrc_cancel_f1_ho;
nr_initiate_handover(rrc, ue, source_du, target_du, buf, size, ack, success, cancel);
}
void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue)
{
if (ue->ho_context->source)
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, ue->ho_context->source->old_cellGroupConfig);
free_ho_ctx(ue->ho_context);
ue->ho_context = NULL;
}
......
......@@ -28,6 +28,8 @@ typedef struct gNB_RRC_INST_s gNB_RRC_INST;
typedef struct gNB_RRC_UE_s gNB_RRC_UE_t;
typedef struct nr_rrc_du_container_t nr_rrc_du_container_t;
typedef struct NR_CellGroupConfig NR_CellGroupConfig_t;
typedef void (*ho_cancel_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
typedef struct nr_ho_source_cu {
/// pointer to the (source) DU structure
......@@ -38,6 +40,9 @@ typedef struct nr_ho_source_cu {
uint32_t du_ue_id;
/// old (source) RNTI (to recognize a UE during reestablishment)
rnti_t old_rnti;
/// old (source) CellGroupConfig (to send the cellGroupConfig in case of
/// reestablishment)
NR_CellGroupConfig_t *old_cellGroupConfig;
/// function pointer to announce the handover cancellation, e.g.,
/// reestablishment
ho_cancel_t ho_cancel;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment