Adopt dynamic seq_arr in QoS Flow list (RRC PDU Session)

This commit is contained in:
Guido Casati
2025-09-24 14:38:37 +02:00
parent 7fa0ebb6b9
commit 45d989ec7b
8 changed files with 162 additions and 64 deletions
@@ -67,9 +67,9 @@ bool read_gtp_sm(void * data)
gtp->msg.ngut[i].teidgnb = session->param.n3_outgoing.teid;
gtp->msg.ngut[i].teidupf = session->param.n3_incoming.teid;
// TODO: one PDU session has multiple QoS Flow
int nb_qos_flow = session->param.nb_qos;
if (nb_qos_flow > 0) {
gtp->msg.ngut[i].qfi = session->param.qos[nb_qos_flow - 1].qfi;
DevAssert(seq_arr_size(&session->param.qos) == 1);
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->param.qos) {
gtp->msg.ngut[i].qfi = qos->qos.qfi;
}
break;
}
+6 -2
View File
@@ -103,9 +103,8 @@ typedef struct pdusession_s {
/* Unique pdusession_id for the UE. */
int pdusession_id;
byte_array_t nas_pdu;
uint8_t nb_qos;
/* Quality of service for this pdusession */
pdusession_level_qos_parameter_t qos[QOSFLOW_MAX_VALUE];
seq_arr_t qos;
/* The transport layer address for the IP packets */
pdu_session_type_t pdu_session_type;
// NG-RAN endpoint of the NG-U (N3) transport bearer
@@ -176,6 +175,11 @@ typedef struct nr_redcap_ue_cap {
bool rlc_am_drb_long_sn_redcap_r17;
} nr_redcap_ue_cap_t;
typedef struct {
int drb_id;
pdusession_level_qos_parameter_t qos;
} nr_rrc_qos_t;
/* forward declaration */
typedef struct nr_handover_context_s nr_handover_context_t;
-1
View File
@@ -122,7 +122,6 @@ byte_array_t rrc_gNB_encode_RRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t *
nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t srb_reest_bitmap, bool drb_reestablish);
pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, rrc_pdu_session_param_t *pduSession);
f1ap_qos_flow_param_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param);
void openair_rrc_gNB_configuration(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration);
NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablish);
+13 -22
View File
@@ -733,9 +733,8 @@ void rrc_gNB_modify_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t
}
// Reference TS23501 Table 5.7.4-1: Standardized 5QI to QoS characteristics mapping
for (qos_flow_index = 0; qos_flow_index < session->nb_qos; qos_flow_index++) {
pdusession_level_qos_parameter_t *qos = &session->qos[qos_flow_index];
switch (qos->fiveQI) {
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->qos) {
switch (qos->qos.fiveQI) {
case 1: //100ms
case 2: //150ms
case 3: //50ms
@@ -749,14 +748,14 @@ void rrc_gNB_modify_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t
break;
default:
LOG_E(NR_RRC, "not supported 5qi %lu\n", qos->fiveQI);
LOG_E(NR_RRC, "not supported 5qi %lu\n", qos->qos.fiveQI);
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CauseRadioNetwork_not_supported_5QI_value};
item->status = PDU_SESSION_STATUS_FAILED;
item->xid = params.transaction_id;
item->cause = cause;
continue;
}
LOG_I(NR_RRC, "PDU Session ID %d, QOS flow %d, 5QI %ld \n", session->pdusession_id, qos_flow_index, qos->fiveQI);
LOG_I(NR_RRC, "PDU Session ID %d, QOS flow %d, 5QI %ld \n", session->pdusession_id, qos_flow_index, qos->qos.fiveQI);
}
item->status = PDU_SESSION_STATUS_DONE;
item->xid = params.transaction_id;
@@ -2101,14 +2100,15 @@ void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb
};
bool is_inter_cu_ho = UE->ho_context && UE->ho_context->source && !UE->ho_context->target;
for (int i = 0; i < MAX_DRBS_PER_UE; i++) {
int drb_id = i + 1;
int drb_index = 0;
FOR_EACH_SEQ_ARR(drb_t *, drb, &UE->drbs) {
int drb_id = drb->drb_id;
DRB_nGRAN_to_mod_t drb_to_mod = {0};
if (n > 0) {
/* DRBs to Setup in F1 UE Context Modifcation Response */
if (i == n)
if (drb_index == n)
break;
const f1ap_drb_setup_t *drb_f1 = &drbs[i];
const f1ap_drb_setup_t *drb_f1 = &drbs[drb_index];
drb_id = drb_f1->id;
fill_e1_bearer_modif(&drb_to_mod, drb_f1);
} else if (is_inter_cu_ho) {
@@ -2134,6 +2134,8 @@ void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb
to_mod->sessionId = pdu->param.pdusession_id;
// Fill E1 DRB to Modify item
to_mod->DRBnGRanModList[to_mod->numDRB2Modify++] = drb_to_mod;
drb_index++;
}
DevAssert(req.numPDUSessionsMod > 0);
DevAssert(req.numPDUSessions == 0);
@@ -2448,17 +2450,6 @@ unsigned int mask_flip(unsigned int x) {
return((((x>>8) + (x<<8))&0xffff)>>6);
}
pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, rrc_pdu_session_param_t *pduSession)
{
pdusession_t *pdu = &pduSession->param;
for (int i = 0; i < pdu->nb_qos; i++) {
if (qfi == pdu->qos[i].qfi)
return &pdu->qos[i];
}
AssertFatal(1 == 0, "The pdu session %d does not contain a qos flow with qfi = %d\n", pdu->pdusession_id, qfi);
return NULL;
}
/* \bref return F1AP QoS characteristics based on Qos flow parameters */
f1ap_qos_flow_param_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param)
{
@@ -2516,11 +2507,11 @@ static int fill_drb_to_be_setup_from_e1_resp(const gNB_RRC_INST *rrc,
/* find the QoS characteristics stored at RRC based on QFI returned
* from E1. We expect this to correspond to a QFI we passed to E1
* previously, so we expect it to exist. */
pdusession_level_qos_parameter_t *qos_param = get_qos_characteristics(qfi, pdu);
nr_rrc_qos_t *qos_param = find_qos(&pdu->param.qos, qfi);
DevAssert(qos_param);
f1ap_drb_flows_mapped_t *flow = &drb->nr.flows[j];
flow->qfi = qfi;
flow->param = get_qos_char_from_qos_flow_param(qos_param);
flow->param = get_qos_char_from_qos_flow_param(&qos_param->qos);
}
/* the DRB QoS parameters: we just reuse the ones from the first flow */
drb->nr.drb_qos = drb->nr.flows[0].param;
+71 -29
View File
@@ -172,20 +172,30 @@ static nr_guami_t get_guami(const uint32_t amf_Id, const plmn_id_t plmn)
/** @brief Copy NGAP PDU Session Transfer item to RRC pdusession_t struct */
static void cp_pdusession_transfer_to_pdusession(pdusession_t *dst, const pdusession_transfer_t *src)
{
dst->nb_qos = src->nb_qos;
for (uint8_t i = 0; i < src->nb_qos && i < QOSFLOW_MAX_VALUE; ++i) {
dst->qos[i] = src->qos[i];
}
dst->pdu_session_type = src->pdu_session_type;
dst->n3_incoming = src->n3_incoming;
/* QoS handling */
DevAssert(!dst->qos.data);
DevAssert(src->nb_qos < QOSFLOW_MAX_VALUE);
// Initialise mapped QoS list per PDU Session
seq_arr_init(&dst->qos, sizeof(nr_rrc_qos_t));
// Add QoS flow to list
for (uint8_t i = 0; i < src->nb_qos; ++i) {
if (!add_qos(&dst->qos, &src->qos[i])) {
LOG_E(NR_RRC, "Failed to add QoS flow %d for PDU session %d\n", src->qos[i].qfi, dst->pdusession_id);
continue;
}
}
}
/** @brief Copy NGAP PDU Session Resource item to RRC pdusession_t struct */
/** @brief Copy NGAP PDU Session Resource item to RRC pdusession_t struct to setup */
static void cp_pdusession_resource_item_to_pdusession(pdusession_t *dst, const pdusession_resource_item_t *src)
{
dst->pdusession_id = src->pdusession_id;
dst->nas_pdu = src->nas_pdu;
dst->nssai = src->nssai;
// Use the PDU session transfer function to handle QoS and other PDU session transfer-specific fields
cp_pdusession_transfer_to_pdusession(dst, &src->pdusessionTransfer);
}
@@ -343,11 +353,10 @@ bool trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession
*cellGroup = MCG;
}
drb->numQosFlow2Setup = session->nb_qos;
for (int k=0; k < drb->numQosFlow2Setup; k++) {
qos_flow_to_setup_t *qos_flow = drb->qosFlows + k;
pdusession_level_qos_parameter_t *qos_session = session->qos + k;
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->qos) {
pdusession_level_qos_parameter_t *qos_session = &qos->qos;
DevAssert(drb->numQosFlow2Setup < QOSFLOW_MAX_VALUE);
qos_flow_to_setup_t *qos_flow = &drb->qosFlows[drb->numQosFlow2Setup++];
qos_characteristics_t *qos_char = &qos_flow->qos_params.qos_characteristics;
qos_flow->qfi = qos_session->qfi;
qos_char->qos_type = qos_session->fiveQI_type;
@@ -505,10 +514,11 @@ static pdusession_setup_t fill_ngap_pdusession_setup(pdusession_t *session)
out.pdusession_id = session->pdusession_id;
out.pdu_session_type = session->pdu_session_type;
out.n3_outgoing = cp_gtp_tunnel(session->n3_outgoing);
out.nb_of_qos_flow = session->nb_qos;
for (int q = 0; q < session->nb_qos; q++) {
out.associated_qos_flows[q].qfi = session->qos[q].qfi;
out.associated_qos_flows[q].qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL;
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos_session, &session->qos) {
DevAssert(out.nb_of_qos_flow < QOSFLOW_MAX_VALUE);
pdusession_associate_qosflow_t *q = &out.associated_qos_flows[out.nb_of_qos_flow++];
q->qfi = qos_session->qos.qfi;
q->qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL;
}
char ip_str[INET_ADDRSTRLEN] = {0};
inet_ntop(AF_INET, out.n3_outgoing.addr.buffer, ip_str, sizeof(ip_str));
@@ -868,6 +878,36 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins
}
}
/** @brief Update existing QoS Flow mapped in the UE context */
static void nr_rrc_update_qos(seq_arr_t *list, const int nb_qos, const pdusession_level_qos_parameter_t *in_qos)
{
DevAssert(nb_qos == 1);
DevAssert(nb_qos < QOSFLOW_MAX_VALUE);
for (uint8_t i = 0; i < nb_qos; ++i) {
nr_rrc_qos_t *qos = find_qos(list, in_qos[i].qfi);
if (qos) {
AssertFatal(qos->qos.qfi == in_qos[i].qfi, "QoS Flow to modify must match existing one");
LOG_I(NR_RRC, "Updating QoS for QFI=%d\n", in_qos[i].qfi);
qos->qos = in_qos[i];
} else {
LOG_E(NR_RRC, "Failed to update QoS for QFI=%d: QoS flow not found\n", in_qos[i].qfi);
}
}
}
/** @brief Update stored pdusession_t in list from NGAP PDU Session Resource item */
static void nr_rrc_update_pdusession(pdusession_t *dst, const pdusession_resource_item_t *src)
{
dst->pdusession_id = src->pdusession_id;
dst->nas_pdu = src->nas_pdu;
dst->pdu_session_type = src->pdusessionTransfer.pdu_session_type;
dst->n3_incoming = src->pdusessionTransfer.n3_incoming;
dst->nssai = src->nssai;
DevAssert(dst->qos.data); // QoS list has to be initialized at this stage
// Update QoS flow
nr_rrc_update_qos(&dst->qos, src->pdusessionTransfer.nb_qos, src->pdusessionTransfer.qos);
}
//------------------------------------------------------------------------------
int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(MessageDef *msg_p, instance_t instance)
//------------------------------------------------------------------------------
@@ -900,7 +940,7 @@ int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(MessageDef *msg_p, instance_t ins
all_failed = false;
session->status = PDU_SESSION_STATUS_NEW;
session->cause.type = NGAP_CAUSE_NOTHING;
cp_pdusession_resource_item_to_pdusession(&session->param, sessMod);
nr_rrc_update_pdusession(&session->param, sessMod);
}
}
@@ -969,20 +1009,20 @@ int rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE
session->cause.type = NGAP_CAUSE_NOTHING;
// Fill response
DevAssert(pdu_sessions_done < NGAP_MAX_PDU_SESSION);
resp->pdusessions[pdu_sessions_done].pdusession_id = session->param.pdusession_id;
for (int qos_flow_index = 0; qos_flow_index < session->param.nb_qos; qos_flow_index++) {
resp->pdusessions[pdu_sessions_done].qos[qos_flow_index].qfi = session->param.qos[qos_flow_index].qfi;
pdusession_modify_t *p = &resp->pdusessions[pdu_sessions_done++];
p->pdusession_id = session->param.pdusession_id;
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos_session, &session->param.qos) {
DevAssert(p->nb_of_qos_flow < QOSFLOW_MAX_VALUE);
qos_flow_tobe_modified_t *q = &p->qos[p->nb_of_qos_flow++];
q->qfi = qos_session->qos.qfi;
}
resp->pdusessions[pdu_sessions_done].pdusession_id = session->param.pdusession_id;
resp->pdusessions[pdu_sessions_done].nb_of_qos_flow = session->param.nb_qos;
LOG_I(NR_RRC,
"Modify Resp (msg index %d, status %d, xid %d): nb_of_pduSessions %ld, pdusession_id %d \n ",
pdu_sessions_done,
session->status,
xid,
seq_arr_size(&UE->pduSessions),
resp->pdusessions[pdu_sessions_done].pdusession_id);
pdu_sessions_done++;
p->pdusession_id);
} else if ((session->status == PDU_SESSION_STATUS_NEW) || (session->status == PDU_SESSION_STATUS_ESTABLISHED)) {
LOG_D(NR_RRC, "PDU SESSION is NEW or already ESTABLISHED\n");
} else if (session->status == PDU_SESSION_STATUS_FAILED) {
@@ -1373,10 +1413,11 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(gNB_RRC_INST *rrc, gNB_RRC_U
transfer->gtp_teid = session->param.n3_outgoing.teid;
memcpy(transfer->gNB_addr.buffer, session->param.n3_outgoing.addr.buffer, session->param.n3_outgoing.addr.length);
transfer->gNB_addr.length = session->param.n3_outgoing.addr.length;
transfer->nb_of_qos_flow = session->param.nb_qos;
for (int q = 0; q < transfer->nb_of_qos_flow; q++) {
transfer->qos_setup_list[q].qfi = session->param.qos[q].qfi;
transfer->qos_setup_list[q].qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL;
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->param.qos) {
DevAssert(transfer->nb_of_qos_flow < QOSFLOW_MAX_VALUE);
pdusession_associate_qosflow_t *qos_setup = &transfer->qos_setup_list[transfer->nb_of_qos_flow++];
qos_setup->qfi = qos->qos.qfi;
qos_setup->qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL;
}
}
// Target to Source Transparent Container
@@ -1620,9 +1661,10 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc,
DevAssert(msg.source2target->nb_pdu_session_resource < NGAP_MAX_PDU_SESSION);
pdusession_resource_info_t *pdu_info = &msg.source2target->pdu_session_resource[msg.source2target->nb_pdu_session_resource++];
pdu_info->pdusession_id = session->pdusession_id;
pdu_info->nb_of_qos_flow = session->nb_qos;
for (int j = 0; j < pdu_info->nb_of_qos_flow; ++j) {
pdu_info->qos_flow_info[j].qfi = session->qos[j].qfi;
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->qos) {
DevAssert(pdu_info->nb_of_qos_flow < QOSFLOW_MAX_VALUE);
qosflow_info_t *qos_flow = &pdu_info->qos_flow_info[pdu_info->nb_of_qos_flow++];
qos_flow->qfi = qos->qos.qfi;
}
}
+2 -5
View File
@@ -77,11 +77,8 @@ static int fill_drb_to_be_setup(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, f1ap_
rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(ue, rrc_drb->drb_id);
AssertFatal(pdu != NULL, "no PDU session for DRB ID %d\n", rrc_drb->drb_id);
// for the moment, we only support one QoS flow. Put a reminder in case
// this changes
AssertFatal(pdu->param.nb_qos == 1, "only 1 Qos flow supported\n");
int qfi = rrc_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[0];
pdusession_level_qos_parameter_t *qos_param = get_qos_characteristics(qfi, pdu);
nr_rrc_qos_t *qos_param = find_qos(&pdu->param.qos, qfi);
drb->id = rrc_drb->drb_id;
@@ -91,7 +88,7 @@ static int fill_drb_to_be_setup(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, f1ap_
drb->nr.flows = calloc_or_fail(1, sizeof(*drb->nr.flows));
DevAssert(qfi > 0);
drb->nr.flows[0].qfi = qfi;
drb->nr.flows[0].param = get_qos_char_from_qos_flow_param(qos_param);
drb->nr.flows[0].param = get_qos_char_from_qos_flow_param(&qos_param->qos);
/* the DRB QoS parameters: we just reuse the ones from the first flow */
drb->nr.drb_qos = drb->nr.flows[0].param;
+61 -2
View File
@@ -33,6 +33,61 @@
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_asn1_utils.h"
#include "common/utils/alg/find.h"
static bool eq_qfi(const void *vval, const void *vit)
{
const int id = *(const int *)vval;
const nr_rrc_qos_t *elem = (const nr_rrc_qos_t *)vit;
return elem->qos.qfi == id;
}
/** @brief Retrieves mapped QoS in UE context for the input @param qfi
* @return pointer to the found QoS, NULL if not found */
nr_rrc_qos_t *find_qos(seq_arr_t *seq, int qfi)
{
DevAssert(seq);
elm_arr_t elm = find_if(seq, &qfi, eq_qfi);
if (elm.found)
return (nr_rrc_qos_t *)elm.it;
return NULL;
}
/** @brief Adds a new QoS @param in to UE context the list
* @note A UE can have up to 64 QoS flows which can be multiplexed
* into one or more DRBs. The QFI is the unique ID of the mapping.
* @return pointer to the newly added QoS */
nr_rrc_qos_t *add_qos(seq_arr_t *qos, const pdusession_level_qos_parameter_t *in)
{
DevAssert(qos);
DevAssert(in);
if (seq_arr_size(qos) == QOSFLOW_MAX_VALUE) {
LOG_W(NR_RRC, "Reached maximum number of QoS flows = %ld\n", seq_arr_size(qos));
return NULL;
}
nr_rrc_qos_t item = {.qos = *in};
seq_arr_push_back(qos, &item, sizeof(nr_rrc_qos_t));
// Double check successful add
nr_rrc_qos_t *added = find_qos(qos, in->qfi);
DevAssert(added);
LOG_I(NR_RRC, "Added QoS flow with qfi=%d, total number of QoS flows = %ld\n", in->qfi, seq_arr_size(qos));
// Only one QoS flow is supported
AssertFatal(seq_arr_size(qos) == 1, "only 1 Qos flow supported\n");
return added;
}
/** @brief Free QoS flows list items */
static void free_qos(void *ptr) { /*nothing to do*/ }
/** @brief Free QoS flows list */
static void free_rrc_qos_list(seq_arr_t *seq)
{
seq_arr_free(seq, free_qos);
}
static bool eq_pdu_session_id(const void *vval, const void *vit)
{
const int id = *(const int *)vval;
@@ -44,6 +99,7 @@ static bool eq_pdu_session_id(const void *vval, const void *vit)
void free_pdusession(void *ptr)
{
rrc_pdu_session_param_t *elem = ptr;
free_rrc_qos_list(&elem->param.qos);
free_byte_array(elem->param.nas_pdu);
}
@@ -210,8 +266,11 @@ drb_t *generateDRB(gNB_RRC_UE_t *ue,
est_drb->cnAssociation.sdap_config.sdap_HeaderDL = NR_SDAP_Config__sdap_HeaderDL_absent;
est_drb->cnAssociation.sdap_config.sdap_HeaderUL = NR_SDAP_Config__sdap_HeaderUL_absent;
}
for (int qos_flow_index = 0; qos_flow_index < pduSession->nb_qos; qos_flow_index++) {
est_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[qos_flow_index] = pduSession->qos[qos_flow_index].qfi;
int qos_flow_index = 0;
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos_session, &pduSession->qos) {
qos_session->drb_id = est_drb->drb_id; // Map DRB to QoS
est_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[qos_flow_index++] = qos_session->qos.qfi;
DevAssert(qos_flow_index <= QOSFLOW_MAX_VALUE);
}
/* PDCP Configuration */
set_default_drb_pdcp_config(&est_drb->pdcp_config, do_drb_integrity, do_drb_ciphering, pdcp_config, ue->redcap_cap);
+6
View File
@@ -72,4 +72,10 @@ drb_t *nr_rrc_add_drb(seq_arr_t *drb_ptr, int pdusession_id);
/// @brief Function to free DRB in RRC
void free_drb(void *ptr);
/// @brief retrieve QoS flow associated to @param qfi
nr_rrc_qos_t *find_qos(seq_arr_t *seq, int qfi);
/// @brief Add a new QoS to the list
nr_rrc_qos_t *add_qos(seq_arr_t *qos, const pdusession_level_qos_parameter_t *in);
#endif