Merge remote-tracking branch 'origin/fixes-ue-handling' into integration_2025_w27 (!3500)

Various fixes for UE handling, HO doc updates

- On release request, search both attach and connected list to free a UE.
- on DU disconnect, only release the correspnonding UE contexts.
- When no AMF for the UE, auto-release the UE (NGAP would also send an
  answer, but better check first)
- implement a "grace period" before the DU/MAC automatically releases a
  UE: this is to avoid cases in which (for whatever reason) the UE
  release request is not honored by the CU. In order to not fill up UE
  contexts unnecessarily, the DU will eventually (after 10s) drop the UE
  context
- some clarification in HO tutorial
This commit is contained in:
Robert Schmidt
2025-07-04 09:41:38 +02:00
9 changed files with 65 additions and 19 deletions
+16 -3
View File
@@ -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 <config> --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
@@ -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
@@ -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);
}
@@ -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)
+1 -1
View File
@@ -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);
@@ -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 = {
+6 -4
View File
@@ -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;
}
+2
View File
@@ -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",
+3 -1
View File
@@ -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;