diff --git a/doc/handover-tutorial.md b/doc/handover-tutorial.md index a367de868a..5fe08be665 100644 --- a/doc/handover-tutorial.md +++ b/doc/handover-tutorial.md @@ -54,7 +54,11 @@ Start DU0: sudo ./nr-softmodem --rfsim -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band78.106prb.rfsim.pci0.conf --rfsimulator.serveraddr 127.0.0.1 -Start the UE, and let it connect completely: +This will show an error `[HW] connect() to 127.0.0.1:4043 failed, +errno(111)`. _This is expected_, because the RFsim server is at the UE (to be +able to serve two RFsim clients, one DU each; see below for more info). Proceed +by starting the UE, and let it connect completely (this should make the error +go away): sudo ./nr-uesoftmodem -C 3450720000 -r 106 --numerology 1 --ssb 516 -O --rfsim --rfsimulator.serveraddr server @@ -142,7 +146,7 @@ neighbour relation of the DUs at the CU. To do so, proceed as follows: ``` cat nrRRC_stats.log ``` -1. Fill in the `neighbour-config.conf` configuration file as shown below, and +1. Fill in the [`neighbour-config.conf`](../ci-scripts/conf_files/neighbour-config.conf) configuration file as shown below, and `@include` it in the CU file. 1. Start the CU and both DUs. 1. Bring the phone close to one cell, and leave flight mode. It should connect @@ -157,7 +161,7 @@ triggered: - Make sure that both DUs use the same hardware. - Make sure that the UE sees both cells. For instance, you can switch to flight mode, go closer to the other DU, and switch off flight mode -- the UE should - connect to that second UE. + connect to that second DU. - We did not manage handover with every phone yet -- make sure you use one of the list provided above. @@ -246,6 +250,15 @@ nr_measurement_configuration = { }) }; ``` +`@include` this configuration file inside the gNB section of CU file as shown below. + +``` + plmn_list = ({ mcc = 222; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff })}); + + + @include "neighbour-config.conf" + +``` # Handovers triggers and NTN diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_e2sm_ue_id.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_e2sm_ue_id.c index c3f6277f6f..a81a01d394 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_e2sm_ue_id.c +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_e2sm_ue_id.c @@ -30,7 +30,12 @@ ue_id_e2sm_t fill_e2sm_gnb_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, __attr // 6.2.3.16 // Mandatory // AMF UE NGAP ID - ue_id.gnb.amf_ue_ngap_id = rrc_ue_context->amf_ue_ngap_id; + // it seems that certain messages can be triggered before the ue has an AMF + // UE NGAP ID (e.g., RRC setup). In that case, the AMF UE NGAP ID is set to + // an invalid value. Below, check for this, and set 0 in that case (which + // historically used to be in amf_ue_ngap_id). + uint64_t auni = rrc_ue_context->amf_ue_ngap_id; + ue_id.gnb.amf_ue_ngap_id = auni < (1LL << 40) ? auni : 0; // Mandatory //GUAMI 6.2.3.17 diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c index 4468cdf0b9..fd6578b3b2 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c @@ -209,6 +209,9 @@ static arr_ue_id_t filter_ues_by_s_nssai_in_cu(const test_info_lst_t test_info) struct rrc_gNB_ue_context_s* rrc_ue_context = NULL; RB_FOREACH(rrc_ue_context, rrc_nr_ue_tree_s, &RC.nrrrc[0]->rrc_ue_head) { gNB_RRC_UE_t *ue = &rrc_ue_context->ue_context; + /* UE has no AMF UE NGAP ID yet => can't send message */ + if (ue->amf_ue_ngap_id >= (1LL << 40)) + continue; for (int p = 0; p < ue->nb_of_pdusessions; ++p) { pdusession_t *pdu = &ue->pduSession[p].param; if (nssai_matches(pdu->nssai, sst, sd)) { @@ -296,6 +299,9 @@ static arr_ue_id_t filter_ues_by_s_nssai_in_du_or_monolithic(const test_info_lst arr_ue_id.ue_id[arr_ue_id.sz] = fill_ue_id_data[ngran_gNB_DU](NULL, rrc_ue_id.secondary_ue, 0); } else { rrc_gNB_ue_context_t* rrc_ue_context = rrc_gNB_get_ue_context_by_rnti(RC.nrrrc[0], -1, ue->rnti); + /* UE has no AMF UE NGAP ID yet => can't send message */ + if (rrc_ue_context->ue_context.amf_ue_ngap_id >= (1LL << 40)) + continue; arr_ue_id.ue_id[arr_ue_id.sz] = fill_ue_id_data[ngran_gNB](&rrc_ue_context->ue_context, 0, 0); } diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 2363565ef2..36cb53b8f2 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -3271,7 +3271,12 @@ void nr_mac_update_timers(module_id_t module_id, frame_t frame, slot_t slot) } /* check if UL failure and trigger release request if necessary */ - nr_mac_check_ul_failure(mac, UE->rnti, sched_ctrl); + bool released = nr_mac_check_ul_failure(mac, UE->rnti, sched_ctrl); + if (released) { + // go back to examine the next UE, which is at the position the current UE was + UE--; + continue; + } if (nr_timer_tick(&sched_ctrl->transm_interrupt)) { /* expired */ @@ -3481,6 +3486,8 @@ bool nr_mac_check_release(NR_UE_sched_ctrl_t *sched_ctrl, int rnti) return sched_ctrl->release_timer == 0; } +#define UL_FAILURE_REQ_GRACE 10000 +#define UL_FAILURE_TIMEOUT 30000 void nr_mac_trigger_ul_failure(NR_UE_sched_ctrl_t *sched_ctrl, NR_SubcarrierSpacing_t subcarrier_spacing) { if (sched_ctrl->ul_failure) { @@ -3488,8 +3495,9 @@ void nr_mac_trigger_ul_failure(NR_UE_sched_ctrl_t *sched_ctrl, NR_SubcarrierSpac return; } sched_ctrl->ul_failure = true; - // 30 seconds till triggering release request - sched_ctrl->ul_failure_timer = 30000 << subcarrier_spacing; + // UL_FAILURE_TIMEOUT ms till triggering release request + // UL_FAILURE_REQ_GRACE ms till automatically released after request + sched_ctrl->ul_failure_timer = (UL_FAILURE_REQ_GRACE + UL_FAILURE_TIMEOUT) << subcarrier_spacing; } void nr_mac_reset_ul_failure(NR_UE_sched_ctrl_t *sched_ctrl) @@ -3519,20 +3527,27 @@ bool nr_mac_request_release_ue(const gNB_MAC_INST *nrmac, int rnti) return true; } -void nr_mac_check_ul_failure(gNB_MAC_INST *nrmac, int rnti, NR_UE_sched_ctrl_t *sched_ctrl) +bool nr_mac_check_ul_failure(gNB_MAC_INST *nrmac, int rnti, NR_UE_sched_ctrl_t *sched_ctrl) { if (!sched_ctrl->ul_failure) - return; + return false; if (sched_ctrl->ul_failure_timer > 0) sched_ctrl->ul_failure_timer--; - /* to trigger only once: trigger when ul_failure_timer == 1, but timer will - * stop at 0 and we wait for a UE release command from upper layers */ - if (sched_ctrl->ul_failure_timer == 1) { + /* trigger once: trigger when ul_failure_timer == UL_FAILURE_REQ_GRACE( we + * wait for a UE release command from upper layers), and UE is automatically + * released when no answer after UL_FAILURE_REQ_GRACE */ + if (sched_ctrl->ul_failure_timer == UL_FAILURE_REQ_GRACE) { LOG_W(MAC, "UE %04x: request release after UL failure timer expiry\n", rnti); bool requested = nr_mac_request_release_ue(nrmac, rnti); if (!requested) - nr_mac_release_ue(nrmac, rnti); + sched_ctrl->ul_failure_timer = 0; /* force expiry */ } + if (sched_ctrl->ul_failure_timer == 0) { + LOG_W(NR_MAC, "UE %04x: no answer for release request, dropping UE\n", rnti); + nr_mac_release_ue(nrmac, rnti); + return true; + } + return false; } void nr_mac_trigger_reconfiguration(const gNB_MAC_INST *nrmac, const NR_UE_info_t *UE) diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index b87c7e6fb2..c6f9a2f6f9 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -477,7 +477,7 @@ bool nr_mac_request_release_ue(const gNB_MAC_INST *nrmac, int rnti); void nr_mac_trigger_ul_failure(NR_UE_sched_ctrl_t *sched_ctrl, NR_SubcarrierSpacing_t subcarrier_spacing); void nr_mac_reset_ul_failure(NR_UE_sched_ctrl_t *sched_ctrl); -void nr_mac_check_ul_failure(gNB_MAC_INST *nrmac, int rnti, NR_UE_sched_ctrl_t *sched_ctrl); +bool nr_mac_check_ul_failure(gNB_MAC_INST *nrmac, int rnti, NR_UE_sched_ctrl_t *sched_ctrl); void nr_mac_trigger_reconfiguration(const gNB_MAC_INST *nrmac, const NR_UE_info_t *UE); diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c index b594cb5122..30d3999f75 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -868,6 +868,7 @@ void ue_context_release_command(const f1ap_ue_context_rel_cmd_t *cmd) gNB_MAC_INST *mac = RC.nrmac[0]; NR_SCHED_LOCK(&mac->sched_lock); NR_UE_info_t *UE = find_nr_UE(&mac->UE_info, cmd->gNB_DU_ue_id); + UE = UE ? UE : find_ra_UE(&mac->UE_info, cmd->gNB_DU_ue_id); if (UE == NULL) { NR_SCHED_UNLOCK(&mac->sched_lock); f1ap_ue_context_rel_cplt_t complete = { diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index b6327ff00b..a6ba6f8cda 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -2143,12 +2143,14 @@ static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p, sctp_as f1ap_ue_context_rel_req_t *req = &F1AP_UE_CONTEXT_RELEASE_REQ(msg_p); gNB_RRC_INST *rrc = RC.nrrrc[instance]; rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, req->gNB_CU_ue_id); - // TODO what happens if no AMF connected? should also handle, set an_release true int srbid = 1; - if (!ue_context_p) { - LOG_W(RRC, "could not find UE context for CU UE ID %u: auto-generate release command\n", req->gNB_CU_ue_id); + // valid AMF UE NGAP ID range is 0..2^40-1 + if (!ue_context_p || ue_context_p->ue_context.amf_ue_ngap_id >= (1LL << 40)) { + const char *reason = !ue_context_p ? "could not find UE context" : "no AMF"; + LOG_W(RRC, "%s for CU UE ID %u: auto-generate release command\n", reason, req->gNB_CU_ue_id); uint8_t buffer[NR_RRC_BUF_SIZE] = {0}; int size = do_NR_RRCRelease(buffer, NR_RRC_BUF_SIZE, rrc_gNB_get_next_transaction_identifier(0)); + RETURN_IF_INVALID_ASSOC_ID(assoc_id); f1ap_ue_context_rel_cmd_t ue_context_release_cmd = { .gNB_CU_ue_id = req->gNB_CU_ue_id, .gNB_DU_ue_id = req->gNB_DU_ue_id, @@ -2156,7 +2158,7 @@ static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p, sctp_as .cause_value = 10, // 10 = F1AP_CauseRadioNetwork_normal_release .srb_id = &srbid, // C-ifRRCContainer => is added below }; - deliver_ue_ctxt_release_data_t data = {.rrc = rrc, .release_cmd = &ue_context_release_cmd}; + deliver_ue_ctxt_release_data_t data = {.rrc = rrc, .release_cmd = &ue_context_release_cmd, .assoc_id = assoc_id}; nr_pdcp_data_req_srb(req->gNB_CU_ue_id, srbid, rrc_gNB_mui++, size, buffer, rrc_deliver_ue_ctxt_release_cmd, &data); return; } diff --git a/openair2/RRC/NR/rrc_gNB_UE_context.c b/openair2/RRC/NR/rrc_gNB_UE_context.c index b39abd9a30..cb3a04fbce 100644 --- a/openair2/RRC/NR/rrc_gNB_UE_context.c +++ b/openair2/RRC/NR/rrc_gNB_UE_context.c @@ -200,6 +200,8 @@ rrc_gNB_ue_context_t *rrc_gNB_create_ue_context(sctp_assoc_t assoc_id, gNB_RRC_UE_t *ue = &ue_context_p->ue_context; ue->rnti = rnti; ue->random_ue_identity = ue_identityP; + // signal "no AMF UE NGAP ID" because valid range is 0..2^40-1 + ue->amf_ue_ngap_id = INT64_MAX; f1_ue_data_t ue_data = {.secondary_ue = du_ue_id, .du_assoc_id = assoc_id}; AssertFatal(!cu_exists_f1_ue_data(ue->rrc_ue_id), "UE F1 Context for ID %d already exists, logic bug\n", diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index 868a7fb64a..db1a9cd459 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -458,7 +458,9 @@ static int invalidate_du_connections(gNB_RRC_INST *rrc, sctp_assoc_t assoc_id) nr_rrc_finalize_ho(UE); } f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_id); - if (ue_data.du_assoc_id == assoc_id && IS_SA_MODE(get_softmodem_params())) { + if (ue_data.du_assoc_id != assoc_id) + continue; /* this UE is on another DU */ + if (IS_SA_MODE(get_softmodem_params())) { /* this UE belongs to the DU that disconnected, set du_assoc_id to 0, * meaning DU is offline, then trigger release request */ ue_data.du_assoc_id = 0;