Compare commits

...

15 Commits

Author SHA1 Message Date
Chieh-Chun Chen
d630781741 Refactor ran_func_rc.c: move slicing functions to rc_ctrl_service_style_2.c 2024-11-22 18:19:50 -05:00
Chieh-Chun Chen
4d79ed4e36 Add cp_ba_to_str 2024-11-22 18:19:50 -05:00
Chieh-Chun Chen
d05c5ce6ec Add missing guami while handle initial context setup reuqest in RRC 2024-11-22 18:19:50 -05:00
Chieh-Chun Chen
f466c56fae Fix: only compile slice functions in RC ran func when define NGRAN_GNB_DU 2024-11-22 18:19:50 -05:00
Chieh-Chun Chen
b95efbc743 Fix: add default slice (sst 0 sd 0) for SRB while initializing slice & add new UE before checking it is in the list or not 2024-11-22 18:19:16 -05:00
Chieh-Chun Chen
3bcd846172 Fix: enable association to multiple slices for one UE in RC RAN func 2024-11-21 16:32:14 -05:00
Sakthivel Velumani
6750c4e77a Add support for multiple slices per UE 2024-11-21 16:32:11 -05:00
Chieh-Chun Chen
89db9cb849 Add README for nr slicing 2024-11-21 15:07:16 -05:00
Chieh-Chun Chen
ae6c0f5a52 Add new coming UEs to the corresponding slice if slice exists 2024-11-21 15:07:14 -05:00
Chieh-Chun Chen
b93c0d2795 Fix: get the correct nssai in ngap 2024-11-21 14:18:25 -05:00
Chieh-Chun Chen
aaded488c5 Add slice in RC ran func 2024-11-21 14:18:25 -05:00
Chieh-Chun Chen
784c920155 Fixes for NR slicing
- correct return value of slice idx
- add/move UE in UE_list
- use rnti_t instead uint16_t
- correct bytes_last_round to be the instantaneous transmission rate for each slice
2024-11-21 14:18:25 -05:00
Chieh-Chun Chen
6fc2d53748 Modify the return value of nr_pf_dl to int & remove the unused codes 2024-11-21 14:18:23 -05:00
Chieh-Chun Chen
f71ef3f8e4 Fix: correct the teidupf in GTP ran func 2024-11-21 13:57:52 -05:00
Robert Schmidt
2c0653b62e Add 5G preprocessor with NVS slicing 2024-11-21 13:57:50 -05:00
23 changed files with 1398 additions and 81 deletions

View File

@@ -1348,6 +1348,7 @@ set (MAC_NR_SRC
${NR_GNB_MAC_DIR}/mac_rrc_dl_handler.c
${NR_GNB_MAC_DIR}/mac_rrc_ul_direct.c
${NR_GNB_MAC_DIR}/mac_rrc_ul_f1ap.c
${NR_GNB_MAC_DIR}/slicing/nr_slicing.c
)

View File

@@ -72,3 +72,17 @@ byte_array_t cp_str_to_ba(const char* str)
return dst;
}
char* cp_ba_to_str(const byte_array_t ba)
{
assert(ba.len > 0);
const size_t sz = ba.len;
char* str = calloc(sz+1, sizeof(char));
assert(str != NULL && "Memory exhausted");
memcpy(str, ba.buf, sz);
str[sz] = '\0';
return str;
}

View File

@@ -42,5 +42,5 @@ void free_byte_array(byte_array_t ba);
bool eq_byte_array(const byte_array_t* m0, const byte_array_t* m1);
byte_array_t cp_str_to_ba(const char* str);
char* cp_ba_to_str(const byte_array_t ba);
#endif

View File

@@ -47,6 +47,7 @@
#define NR_NB_SC_PER_RB 12
#define NR_MAX_NUM_LCID 32
#define NR_MAX_NUM_QFI 64
#define NR_MAX_NUM_SLICES 10
#define RNTI_NAMES /* see 38.321 Table 7.1-2 RNTI usage */ \
R(TYPE_C_RNTI_) /* Cell RNTI */ \
R(TYPE_CS_RNTI_) /* Configured Scheduling RNTI */ \

View File

@@ -268,7 +268,7 @@ nr_preprocessor_phytest()], multiple users in FR1
2) Checks the quantity of waiting data in RLC
3) Either set up resource allocation directly (e.g., for a single UE,
phytest), or call into a function to perform actual resource allocation.
Currently, this is done using pf_dl() which implements a basic
Currently, this is done using nr_pf_dl() which implements a basic
proportional fair scheduler:
* for every UE, check for retransmission and allocate as necessary
* Calculate the PF coefficient and put eligible UEs into a list

View File

@@ -741,7 +741,7 @@ int main(int argc, char **argv)
nr_mac_add_test_ue(RC.nrmac[0], secondaryCellGroup->spCellConfig->reconfigurationWithSync->newUE_Identity, secondaryCellGroup);
// reset preprocessor to the one of DLSIM after it has been set during
// nr_mac_config_scc()
gNB_mac->pre_processor_dl = nr_dlsim_preprocessor;
gNB_mac->pre_processor_dl.dl = nr_dlsim_preprocessor;
phy_init_nr_gNB(gNB);
N_RB_DL = gNB->frame_parms.N_RB_DL;
NR_UE_info_t *UE_info = RC.nrmac[0]->UE_info.list[0];

View File

@@ -24,6 +24,7 @@ add_library(e2_ran_func_du_cucp_cuup STATIC
O-RAN/ran_func_kpm_subs.c
O-RAN/ran_func_rc.c
O-RAN/ran_func_rc_subs.c
O-RAN/rc_ctrl_service_style_2.c
../flexric/src/sm/rc_sm/ie/rc_data_ie.c
CUSTOMIZED/ran_func_gtp.c
CUSTOMIZED/ran_func_pdcp.c
@@ -31,6 +32,7 @@ add_library(e2_ran_func_du_cucp_cuup STATIC
CUSTOMIZED/ran_func_rlc.c
CUSTOMIZED/ran_func_slice.c # currently, not implemented; therefore, filling rnd data
../flexric/test/rnd/fill_rnd_data_slice.c
../../LAYER2/NR_MAC_gNB/slicing/nr_slicing.c
CUSTOMIZED/ran_func_tc.c # currently, not implemented; therefore, filling rnd data
../flexric/test/rnd/fill_rnd_data_tc.c
)

View File

@@ -44,7 +44,7 @@ bool read_gtp_sm(void * data)
uint64_t ue_id_list[MAX_MOBILES_PER_GNB];
size_t num_ues = nr_pdcp_get_num_ues(ue_id_list, MAX_MOBILES_PER_GNB);
gtp->msg.len = num_ues;
if(gtp->msg.len > 0){
gtp->msg.ngut = calloc(gtp->msg.len, sizeof(gtp_ngu_t_stats_t) );
@@ -66,7 +66,7 @@ bool read_gtp_sm(void * data)
if (nb_pdu_session > 0) {
int nb_pdu_idx = nb_pdu_session - 1;
gtp->msg.ngut[i].teidgnb = ue_context_p->ue_context.pduSession[nb_pdu_idx].param.gNB_teid_N3;
gtp->msg.ngut[i].teidupf = ue_context_p->ue_context.pduSession[nb_pdu_idx].param.UPF_teid_N3;
gtp->msg.ngut[i].teidupf = ue_context_p->ue_context.pduSession[nb_pdu_idx].param.gtp_teid;
// TODO: one PDU session has multiple QoS Flow
int nb_qos_flow = ue_context_p->ue_context.pduSession[nb_pdu_idx].param.nb_qos;
if (nb_qos_flow > 0) {
@@ -80,7 +80,7 @@ bool read_gtp_sm(void * data)
#elif defined (NGRAN_GNB_CUUP)
// For the moment, CU-UP doesn't store PDU session information
printf("GTP SM not yet implemented in CU-UP\n");
return false;
return false;
#endif
}

View File

@@ -26,6 +26,7 @@
#include "../../flexric/src/sm/rc_sm/ie/ir/lst_ran_param.h"
#include "../../flexric/src/sm/rc_sm/ie/ir/ran_param_list.h"
#include "../../flexric/src/agent/e2_agent_api.h"
#include "rc_ctrl_service_style_2.h"
#include <stdio.h>
#include <unistd.h>
@@ -127,11 +128,11 @@ static void fill_rc_report(ran_func_def_report_t* report)
// Mandatory
// 9.3.4
// 6.2.2.3.
// PrintableString(SIZE(1..150,...))
// PrintableString(SIZE(1..150,...))
const char report_name[] = "UE Information";
report_style->name = cp_str_to_ba(report_name);
// Supported RIC Event Trigger Style Type
// Supported RIC Event Trigger Style Type
// Mandatory
// 9.3.3
// 6.2.2.2.
@@ -174,7 +175,7 @@ static void fill_rc_report(ran_func_def_report_t* report)
// RAN Parameter Name
// Mandatory
// 9.3.9
// [1-150]
// [1-150]
const char ran_param_name[] = "RRC State";
report_style->ran_param[0].name = cp_str_to_ba(ran_param_name);
@@ -289,7 +290,7 @@ static void fill_rc_control(ran_func_def_ctrl_t* ctrl)
lst->ran_param[1].ran_param_name = cp_str_to_ba(ran_param_qos_flow_mapping_ind);
lst->ran_param[1].ran_param_def = NULL;
// Sequence of Associated RAN
// Sequence of Associated RAN
// Parameters for Control Outcome
// [0- 255]
ctrl_style->sz_ran_param_ctrl_out = 0;
@@ -464,7 +465,7 @@ void read_rc_setup_sm(void* data)
rc_e2_setup_t* rc = (rc_e2_setup_t*)data;
/* Fill the RAN Function Definition with currently supported measurements */
// RAN Function Name is already filled in fill_ran_function_name() in rc_sm_agent.c
const ngran_node_t node_type = get_e2_node_type();
@@ -488,7 +489,7 @@ static seq_ran_param_t fill_rrc_state_change_seq_ran(const rc_sm_rrc_state_e rrc
seq_ran_param.ran_param_val.flag_false = calloc(1, sizeof(ran_parameter_value_t));
assert(seq_ran_param.ran_param_val.flag_false != NULL && "Memory exhausted");
seq_ran_param.ran_param_val.flag_false->type = INTEGER_RAN_PARAMETER_VALUE;
seq_ran_param.ran_param_val.flag_false->int_ran = rrc_state;
seq_ran_param.ran_param_val.flag_false->int_ran = rrc_state;
return seq_ran_param;
}
@@ -529,13 +530,13 @@ static rc_ind_data_t* fill_ue_rrc_state_change(const gNB_RRC_UE_t *rrc_ue_contex
}
void signal_rrc_state_changed_to(const gNB_RRC_UE_t *rrc_ue_context, const rc_sm_rrc_state_e rrc_state)
{
{
pthread_mutex_lock(&rc_mutex);
if (rc_subs_data.rb[RRC_STATE_CHANGED_TO_E2SM_RC_RAN_PARAM_ID].rbh_root == NULL) {
pthread_mutex_unlock(&rc_mutex);
return;
}
struct ric_req_id_s *node;
RB_FOREACH(node, ric_id_2_param_id_trees, &rc_subs_data.rb[RRC_STATE_CHANGED_TO_E2SM_RC_RAN_PARAM_ID]) {
rc_ind_data_t* rc_ind_data = fill_ue_rrc_state_change(rrc_ue_context, rrc_state);
@@ -544,7 +545,7 @@ void signal_rrc_state_changed_to(const gNB_RRC_UE_t *rrc_ue_context, const rc_sm
async_event_agent_api(node->ric_req_id, rc_ind_data);
printf( "Event for RIC Req ID %u generated\n", node->ric_req_id);
}
pthread_mutex_unlock(&rc_mutex);
}
@@ -573,13 +574,13 @@ sm_ag_if_ans_t write_subs_rc_sm(void const* src)
arr_ran_param_id->ran_param_id = calloc(arr_ran_param_id->len, sizeof(ran_param_id_e));
const size_t sz = arr_ran_param_id->len;
for(size_t i = 0; i < sz; i++) {
for(size_t i = 0; i < sz; i++) {
arr_ran_param_id->ran_param_id[i] = wr_rc->rc.ad->frmt_1.param_report_def[i].ran_param_id;
}
insert_rc_subs_data(&rc_subs_data, ric_req_id, arr_ran_param_id);
break;
}
default:
AssertFatal(wr_rc->rc.ad->format == FORMAT_1_E2SM_RC_ACT_DEF, "Action Definition Format %d not yet implemented", wr_rc->rc.ad->format);
}
@@ -591,51 +592,73 @@ sm_ag_if_ans_t write_subs_rc_sm(void const* src)
return ans;
}
sm_ag_if_ans_t write_ctrl_rc_sm(void const* data)
{
assert(data != NULL);
// assert(data->type == RAN_CONTROL_CTRL_V1_03 );
rc_ctrl_req_data_t const* ctrl = (rc_ctrl_req_data_t const*)data;
LOG_I(NR_MAC, "[E2-Agent]: RC CONTROL rx, RIC Style Type %d, Action ID %d\n", ctrl->hdr.frmt_1.ric_style_type, ctrl->hdr.frmt_1.ctrl_act_id);
assert(ctrl->hdr.format == FORMAT_1_E2SM_RC_CTRL_HDR && "Indication Header Format received not valid");
assert(ctrl->msg.format == FORMAT_1_E2SM_RC_CTRL_MSG && "Indication Message Format received not valid");
assert(ctrl->hdr.frmt_1.ctrl_act_id == 2 && "Currently only QoS flow mapping configuration supported");
//assert(ctrl->hdr.frmt_1.ctrl_act_id == 2 && "Currently only QoS flow mapping configuration supported");
if(ctrl->hdr.frmt_1.ric_style_type == 1 && ctrl->hdr.frmt_1.ctrl_act_id == 2) {
printf("QoS flow mapping configuration\n");
printf("QoS flow mapping configuration\n");
const seq_ran_param_t* ran_param = ctrl->msg.frmt_1.ran_param;
const seq_ran_param_t* ran_param = ctrl->msg.frmt_1.ran_param;
// DRB ID
assert(ran_param[0].ran_param_id == 1 && "First RAN Parameter ID has to be DRB ID");
assert(ran_param[0].ran_param_val.type == ELEMENT_KEY_FLAG_TRUE_RAN_PARAMETER_VAL_TYPE);
printf("DRB ID %ld \n", ran_param[0].ran_param_val.flag_true->int_ran);
// DRB ID
assert(ran_param[0].ran_param_id == 1 && "First RAN Parameter ID has to be DRB ID");
assert(ran_param[0].ran_param_val.type == ELEMENT_KEY_FLAG_TRUE_RAN_PARAMETER_VAL_TYPE);
printf("DRB ID %ld \n", ran_param[0].ran_param_val.flag_true->int_ran);
// List of QoS Flows to be modified in DRB
assert(ran_param[1].ran_param_id == 2 && "Second RAN Parameter ID has to be List of QoS Flows");
assert(ran_param[1].ran_param_val.type == LIST_RAN_PARAMETER_VAL_TYPE);
printf("List of QoS Flows to be modified in DRB\n");
const lst_ran_param_t* lrp = ran_param[1].ran_param_val.lst->lst_ran_param;
// List of QoS Flows to be modified in DRB
assert(ran_param[1].ran_param_id == 2 && "Second RAN Parameter ID has to be List of QoS Flows");
assert(ran_param[1].ran_param_val.type == LIST_RAN_PARAMETER_VAL_TYPE);
printf("List of QoS Flows to be modified in DRB\n");
const lst_ran_param_t* lrp = ran_param[1].ran_param_val.lst->lst_ran_param;
// The following assertion should be true, but there is a bug in the std
// check src/sm/rc_sm/enc/rc_enc_asn.c:1085 and src/sm/rc_sm/enc/rc_enc_asn.c:984
// assert(lrp->ran_param_struct.ran_param_struct[0].ran_param_id == 3);
// The following assertion should be true, but there is a bug in the std
// check src/sm/rc_sm/enc/rc_enc_asn.c:1085 and src/sm/rc_sm/enc/rc_enc_asn.c:984
// assert(lrp->ran_param_struct.ran_param_struct[0].ran_param_id == 3);
// QoS Flow Identifier
assert(lrp->ran_param_struct.ran_param_struct[0].ran_param_id == 4);
assert(lrp->ran_param_struct.ran_param_struct[0].ran_param_val.type == ELEMENT_KEY_FLAG_TRUE_RAN_PARAMETER_VAL_TYPE);
int64_t qfi = lrp->ran_param_struct.ran_param_struct[0].ran_param_val.flag_true->int_ran;
assert(qfi > -1 && qfi < 65);
// QoS Flow Identifier
assert(lrp->ran_param_struct.ran_param_struct[0].ran_param_id == 4);
assert(lrp->ran_param_struct.ran_param_struct[0].ran_param_val.type == ELEMENT_KEY_FLAG_TRUE_RAN_PARAMETER_VAL_TYPE);
int64_t qfi = lrp->ran_param_struct.ran_param_struct[0].ran_param_val.flag_true->int_ran;
assert(qfi > -1 && qfi < 65);
// QoS Flow Mapping Indication
assert(lrp->ran_param_struct.ran_param_struct[1].ran_param_id == 5);
assert(lrp->ran_param_struct.ran_param_struct[1].ran_param_val.type == ELEMENT_KEY_FLAG_FALSE_RAN_PARAMETER_VAL_TYPE);
int64_t dir = lrp->ran_param_struct.ran_param_struct[1].ran_param_val.flag_false->int_ran;
assert(dir == 0 || dir == 1);
// QoS Flow Mapping Indication
assert(lrp->ran_param_struct.ran_param_struct[1].ran_param_id == 5);
assert(lrp->ran_param_struct.ran_param_struct[1].ran_param_val.type == ELEMENT_KEY_FLAG_FALSE_RAN_PARAMETER_VAL_TYPE);
int64_t dir = lrp->ran_param_struct.ran_param_struct[1].ran_param_val.flag_false->int_ran;
assert(dir == 0 || dir == 1);
printf("qfi = %ld, dir %ld \n", qfi, dir);
#ifdef NGRAN_GNB_DU
} else if (ctrl->hdr.frmt_1.ric_style_type == 2 && ctrl->hdr.frmt_1.ctrl_act_id == Slice_level_PRB_quotal_7_6_3_1) {
/// ADD/MOD SLICE ///
e2sm_rc_ctrl_msg_frmt_1_t const* msg = &ctrl->msg.frmt_1;
assert(msg->sz_ran_param == 1 && "not support msg->sz_ran_param != 1");
seq_ran_param_t* RRM_Policy_Ratio_List = &msg->ran_param[0];
assert(RRM_Policy_Ratio_List->ran_param_id == RRM_Policy_Ratio_List_8_4_3_6 && "wrong RRM_Policy_Ratio_List id");
assert(RRM_Policy_Ratio_List->ran_param_val.type == LIST_RAN_PARAMETER_VAL_TYPE && "wrong RRM_Policy_Ratio_List type");
if (RRM_Policy_Ratio_List->ran_param_val.lst) {
size_t slices_len = RRM_Policy_Ratio_List->ran_param_val.lst->sz_lst_ran_param;
const int mod_id = 0;
bool rc = add_mod_rc_slice(mod_id, slices_len, RRM_Policy_Ratio_List->ran_param_val.lst);
if (!rc)
LOG_E(NR_MAC, "failed add/mod slices\n");
} else {
LOG_I(NR_MAC, "RRM_Policy_Ratio_List->ran_param_val.lst is NULL\n");
}
#endif
} else {
assert(0!=0 && "unknown ric_style_type and ctrl_act_id\n");
}
printf("qfi = %ld, dir %ld \n", qfi, dir);
sm_ag_if_ans_t ans = {.type = CTRL_OUTCOME_SM_AG_IF_ANS_V0};

View File

@@ -0,0 +1,244 @@
#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h"
#include "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h"
#include "openair2/LAYER2/NR_MAC_gNB/slicing/nr_slicing.h"
#include "openair2/RRC/NR/rrc_gNB_UE_context.h"
#include "rc_ctrl_service_style_2.h"
static int add_mod_dl_slice(int mod_id,
slice_algorithm_e current_algo,
int id,
nssai_t nssai,
char* label,
void* params)
{
nr_pp_impl_param_dl_t *dl = &RC.nrmac[mod_id]->pre_processor_dl;
char *slice_algo = NULL;
if (current_algo == NVS_SLICING) {
nvs_nr_slice_param_t * nvs_params = (nvs_nr_slice_param_t *)params;
if (!nvs_params) return -1;
slice_algo = strdup("NVS_CAPACITY");
float remain_pct = 1-((nvs_nr_slice_param_t *)dl->slices->s[0]->algo_data)->pct_reserved;
nvs_params->pct_reserved = nvs_params->pct_reserved/100.0*remain_pct;
} else {
assert(0 != 0 && "Unknow current_algo");
}
void *algo = &dl->dl_algo;
char *l = NULL;
if (label)
l = strdup(label);
LOG_W(NR_MAC, "add DL slice id %d, label %s, slice sched algo %s, pct_reserved %.2f, ue sched algo %s\n", id, l, slice_algo, ((nvs_nr_slice_param_t *)params)->pct_reserved, dl->dl_algo.name);
return dl->addmod_slice(dl->slices, id, nssai, l, algo, params);
}
static void set_new_dl_slice_algo(int mod_id, int algo)
{
gNB_MAC_INST *nrmac = RC.nrmac[mod_id];
assert(nrmac);
nr_pp_impl_param_dl_t dl = nrmac->pre_processor_dl;
switch (algo) {
case NVS_SLICING:
nrmac->pre_processor_dl = nvs_nr_dl_init(mod_id);
break;
default:
nrmac->pre_processor_dl.algorithm = 0;
nrmac->pre_processor_dl = nr_init_fr1_dlsch_preprocessor(0); // assume CC_id = 0
nrmac->pre_processor_dl.slices = NULL;
break;
}
if (dl.slices)
dl.destroy(&dl.slices);
if (dl.dl_algo.data)
dl.dl_algo.unset(&dl.dl_algo.data);
}
static bool nssai_matches(nssai_t a_nssai, uint8_t b_sst, const uint32_t *b_sd)
{
AssertFatal(b_sd == NULL || *b_sd <= 0xffffff, "illegal SD %d\n", *b_sd);
if (b_sd == NULL) {
return a_nssai.sst == b_sst && a_nssai.sd == 0xffffff;
} else {
return a_nssai.sst == b_sst && a_nssai.sd == *b_sd;
}
}
bool add_mod_rc_slice(int mod_id, size_t slices_len, ran_param_list_t* lst)
{
gNB_MAC_INST *nrmac = RC.nrmac[mod_id];
assert(nrmac);
int current_algo = nrmac->pre_processor_dl.algorithm;
// use NVS algorithm by default
int new_algo = NVS_SLICING;
pthread_mutex_lock(&nrmac->UE_info.mutex);
if (current_algo != new_algo) {
set_new_dl_slice_algo(mod_id, new_algo);
current_algo = new_algo;
if (new_algo > 0)
LOG_D(NR_MAC, "set new algorithm %d\n", current_algo);
else
LOG_W(NR_MAC, "reset slicing algorithm as NONE\n");
}
for (size_t i = 0; i < slices_len; ++i) {
lst_ran_param_t* RRM_Policy_Ratio_Group = &lst->lst_ran_param[i];
//Bug in rc_enc_asn.c:1003, asn didn't define ran_param_id for lst_ran_param_t...
//assert(RRM_Policy_Ratio_Group->ran_param_id == RRM_Policy_Ratio_Group_8_4_3_6 && "wrong RRM_Policy_Ratio_Group id");
assert(RRM_Policy_Ratio_Group->ran_param_struct.sz_ran_param_struct == 4 && "wrong RRM_Policy_Ratio_Group->ran_param_struct.sz_ran_param_struct");
assert(RRM_Policy_Ratio_Group->ran_param_struct.ran_param_struct != NULL && "NULL RRM_Policy_Ratio_Group->ran_param_struct.ran_param_struct");
seq_ran_param_t* RRM_Policy = &RRM_Policy_Ratio_Group->ran_param_struct.ran_param_struct[0];
assert(RRM_Policy->ran_param_id == RRM_Policy_8_4_3_6 && "wrong RRM_Policy id");
assert(RRM_Policy->ran_param_val.type == STRUCTURE_RAN_PARAMETER_VAL_TYPE && "wrong RRM_Policy type");
assert(RRM_Policy->ran_param_val.strct != NULL && "NULL RRM_Policy->ran_param_val.strct");
assert(RRM_Policy->ran_param_val.strct->sz_ran_param_struct == 1 && "wrong RRM_Policy->ran_param_val.strct->sz_ran_param_struct");
assert(RRM_Policy->ran_param_val.strct->ran_param_struct != NULL && "NULL RRM_Policy->ran_param_val.strct->ran_param_struct");
seq_ran_param_t* RRM_Policy_Member_List = &RRM_Policy->ran_param_val.strct->ran_param_struct[0];
assert(RRM_Policy_Member_List->ran_param_id == RRM_Policy_Member_List_8_4_3_6 && "wrong RRM_Policy_Member_List id");
assert(RRM_Policy_Member_List->ran_param_val.type == LIST_RAN_PARAMETER_VAL_TYPE && "wrong RRM_Policy_Member_List type");
assert(RRM_Policy_Member_List->ran_param_val.lst != NULL && "NULL RRM_Policy_Member_List->ran_param_val.lst");
assert(RRM_Policy_Member_List->ran_param_val.lst->sz_lst_ran_param == 1 && "wrong RRM_Policy_Member_List->ran_param_val.lst->sz_lst_ran_param");
assert(RRM_Policy_Member_List->ran_param_val.lst->lst_ran_param != NULL && "NULL RRM_Policy_Member_List->ran_param_val.lst->lst_ran_param");
lst_ran_param_t* RRM_Policy_Member = &RRM_Policy_Member_List->ran_param_val.lst->lst_ran_param[0];
//Bug in rc_enc_asn.c:1003, asn didn't define ran_param_id for lst_ran_param_t ...
//assert(RRM_Policy_Member->ran_param_id == RRM_Policy_Member_8_4_3_6 && "wrong RRM_Policy_Member id");
assert(RRM_Policy_Member->ran_param_struct.sz_ran_param_struct == 2 && "wrong RRM_Policy_Member->ran_param_struct.sz_ran_param_struct");
assert(RRM_Policy_Member->ran_param_struct.ran_param_struct != NULL && "NULL RRM_Policy_Member->ran_param_struct.ran_param_struct");
seq_ran_param_t* PLMN_Identity = &RRM_Policy_Member->ran_param_struct.ran_param_struct[0];
assert(PLMN_Identity->ran_param_id == PLMN_Identity_8_4_3_6 && "wrong PLMN_Identity id");
assert(PLMN_Identity->ran_param_val.type == ELEMENT_KEY_FLAG_FALSE_RAN_PARAMETER_VAL_TYPE && "wrong PLMN_Identity type");
assert(PLMN_Identity->ran_param_val.flag_false != NULL && "NULL PLMN_Identity->ran_param_val.flag_false");
assert(PLMN_Identity->ran_param_val.flag_false->type == OCTET_STRING_RAN_PARAMETER_VALUE && "wrong PLMN_Identity->ran_param_val.flag_false->type");
///// GET RC PLMN ////
char* plmn_str = cp_ba_to_str(PLMN_Identity->ran_param_val.flag_false->octet_str_ran);
int RC_mnc, RC_mcc = 0;
if (strlen(plmn_str) == 6)
sscanf(plmn_str, "%3d%2d", &RC_mcc, &RC_mnc);
else
sscanf(plmn_str, "%3d%3d", &RC_mcc, &RC_mnc);
LOG_D(NR_MAC, "RC PLMN %s, MCC %d, MNC %d\n", plmn_str, RC_mcc, RC_mnc);
free(plmn_str);
seq_ran_param_t* S_NSSAI = &RRM_Policy_Member->ran_param_struct.ran_param_struct[1];
assert(S_NSSAI->ran_param_id == S_NSSAI_8_4_3_6 && "wrong S_NSSAI id");
assert(S_NSSAI->ran_param_val.type == STRUCTURE_RAN_PARAMETER_VAL_TYPE && "wrong S_NSSAI type");
assert(S_NSSAI->ran_param_val.strct != NULL && "NULL S_NSSAI->ran_param_val.strct");
assert(S_NSSAI->ran_param_val.strct->sz_ran_param_struct == 2 && "wrong S_NSSAI->ran_param_val.strct->sz_ran_param_struct");
assert(S_NSSAI->ran_param_val.strct->ran_param_struct != NULL && "NULL S_NSSAI->ran_param_val.strct->ran_param_struct");
seq_ran_param_t* SST = &S_NSSAI->ran_param_val.strct->ran_param_struct[0];
assert(SST->ran_param_id == SST_8_4_3_6 && "wrong SST id");
assert(SST->ran_param_val.type == ELEMENT_KEY_FLAG_FALSE_RAN_PARAMETER_VAL_TYPE && "wrong SST type");
assert(SST->ran_param_val.flag_false != NULL && "NULL SST->ran_param_val.flag_false");
assert(SST->ran_param_val.flag_false->type == OCTET_STRING_RAN_PARAMETER_VALUE && "wrong SST->ran_param_val.flag_false type");
seq_ran_param_t* SD = &S_NSSAI->ran_param_val.strct->ran_param_struct[1];
assert(SD->ran_param_id == SD_8_4_3_6 && "wrong SD id");
assert(SD->ran_param_val.type == ELEMENT_KEY_FLAG_FALSE_RAN_PARAMETER_VAL_TYPE && "wrong SD type");
assert(SD->ran_param_val.flag_false != NULL && "NULL SD->ran_param_val.flag_false");
assert(SD->ran_param_val.flag_false->type == OCTET_STRING_RAN_PARAMETER_VALUE && "wrong SD->ran_param_val.flag_false type");
///// GET RC NSSAI ////
char* rc_sst_str = cp_ba_to_str(SST->ran_param_val.flag_false->octet_str_ran);
char* rc_sd_str = cp_ba_to_str(SD->ran_param_val.flag_false->octet_str_ran);
nssai_t RC_nssai = {.sst = atoi(rc_sst_str), .sd = atoi(rc_sd_str)};
LOG_D(NR_MAC, "RC (oct) SST %s, SD %s -> (uint) SST %d, SD %d\n", rc_sst_str, rc_sd_str, RC_nssai.sst, RC_nssai.sd);
///// SLICE LABEL NAME /////
char* sst_str = "SST";
char* sd_str = "SD";
size_t label_nssai_len = strlen(sst_str) + strlen(rc_sst_str) + strlen(sd_str) + strlen(rc_sd_str) + 1;
char* label_nssai = (char*)malloc(label_nssai_len);
assert(label_nssai != NULL && "Memory exhausted");
sprintf(label_nssai, "%s%s%s%s", sst_str, rc_sst_str, sd_str, rc_sd_str);
free(rc_sst_str);
free(rc_sd_str);
///// SLICE NVS CAP /////
seq_ran_param_t* Min_PRB_Policy_Ratio = &RRM_Policy_Ratio_Group->ran_param_struct.ran_param_struct[1];
assert(Min_PRB_Policy_Ratio->ran_param_id == Min_PRB_Policy_Ratio_8_4_3_6 && "wrong Min_PRB_Policy_Ratio id");
assert(Min_PRB_Policy_Ratio->ran_param_val.type == ELEMENT_KEY_FLAG_FALSE_RAN_PARAMETER_VAL_TYPE && "wrong Min_PRB_Policy_Ratio type");
assert(Min_PRB_Policy_Ratio->ran_param_val.flag_false != NULL && "NULL Min_PRB_Policy_Ratio->ran_param_val.flag_false");
assert(Min_PRB_Policy_Ratio->ran_param_val.flag_false->type == INTEGER_RAN_PARAMETER_VALUE && "wrong Min_PRB_Policy_Ratio->ran_param_val.flag_false type");
int64_t min_prb_ratio = Min_PRB_Policy_Ratio->ran_param_val.flag_false->int_ran;
LOG_D(NR_MAC, "configure slice %ld, label %s, Min_PRB_Policy_Ratio %ld\n", i, label_nssai, min_prb_ratio);
seq_ran_param_t* Dedicated_PRB_Policy_Ratio = &RRM_Policy_Ratio_Group->ran_param_struct.ran_param_struct[3];
assert(Dedicated_PRB_Policy_Ratio->ran_param_id == Dedicated_PRB_Policy_Ratio_8_4_3_6 && "wrong Dedicated_PRB_Policy_Ratio id");
assert(Dedicated_PRB_Policy_Ratio->ran_param_val.type == ELEMENT_KEY_FLAG_FALSE_RAN_PARAMETER_VAL_TYPE && "wrong Dedicated_PRB_Policy_Ratio type");
assert(Dedicated_PRB_Policy_Ratio->ran_param_val.flag_false != NULL && "NULL Dedicated_PRB_Policy_Ratio->ran_param_val.flag_false");
assert(Dedicated_PRB_Policy_Ratio->ran_param_val.flag_false->type == INTEGER_RAN_PARAMETER_VALUE && "wrong Dedicated_PRB_Policy_Ratio->ran_param_val.flag_false type");
int64_t dedicated_prb_ratio = Dedicated_PRB_Policy_Ratio->ran_param_val.flag_false->int_ran;
LOG_I(NR_MAC, "configure slice %ld, label %s, Dedicated_PRB_Policy_Ratio %ld\n", i, label_nssai, dedicated_prb_ratio);
// TODO: could be extended to support max prb ratio in the MAC scheduling algorithm
//seq_ran_param_t* Max_PRB_Policy_Ratio = &RRM_Policy_Ratio_Group->ran_param_struct.ran_param_struct[2];
//assert(Max_PRB_Policy_Ratio->ran_param_id == Max_PRB_Policy_Ratio_8_4_3_6 && "wrong Max_PRB_Policy_Ratio id");
//assert(Max_PRB_Policy_Ratio->ran_param_val.type == ELEMENT_KEY_FLAG_FALSE_RAN_PARAMETER_VAL_TYPE && "wrong Max_PRB_Policy_Ratio type");
//assert(Max_PRB_Policy_Ratio->ran_param_val.flag_false != NULL && "NULL Max_PRB_Policy_Ratio->ran_param_val.flag_false");
//assert(Max_PRB_Policy_Ratio->ran_param_val.flag_false->type == INTEGER_RAN_PARAMETER_VALUE && "wrong Max_PRB_Policy_Ratio->ran_param_val.flag_false type");
//int64_t max_prb_ratio = Max_PRB_Policy_Ratio->ran_param_val.flag_false->int_ran;
//LOG_I(NR_MAC, "configure slice %ld, label %s, Max_PRB_Policy_Ratio %ld\n", i, label_nssai, max_prb_ratio);
///// ADD SLICE /////
/* Resource-based */
void *params = malloc(sizeof(nvs_nr_slice_param_t));
((nvs_nr_slice_param_t *)params)->type = NVS_RES;
((nvs_nr_slice_param_t *)params)->pct_reserved = dedicated_prb_ratio;
const int rc = add_mod_dl_slice(mod_id, current_algo, i+1, RC_nssai, label_nssai, params);
free(label_nssai);
if (rc < 0) {
pthread_mutex_unlock(&nrmac->UE_info.mutex);
LOG_E(NR_MAC, "error code %d while updating slices\n", rc);
return false;
}
/* TODO: Bandwidth-based */
/// ASSOC SLICE ///
if (nrmac->pre_processor_dl.algorithm <= 0)
LOG_E(NR_MAC, "current slice algo is NONE, no UE can be associated\n");
if (nrmac->UE_info.list[0] == NULL)
LOG_E(NR_MAC, "no UE connected\n");
nr_pp_impl_param_dl_t *dl = &RC.nrmac[mod_id]->pre_processor_dl;
NR_UEs_t *UE_info = &RC.nrmac[mod_id]->UE_info;
UE_iterator(UE_info->list, UE) {
rnti_t rnti = UE->rnti;
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
bool assoc_ue = 0;
for (unsigned int l = 0; l < seq_arr_size(&sched_ctrl->lc_config); ++l) {
const nr_lc_config_t *c = seq_arr_at(&sched_ctrl->lc_config, l);
const long lcid = c->lcid;
LOG_D(NR_MAC, "l %d, lcid %ld, sst %d, sd %d\n", l, lcid, c->nssai.sst, c->nssai.sd);
if (nssai_matches(c->nssai, RC_nssai.sst, &RC_nssai.sd)) {
const rrc_gNB_ue_context_t* rrc_ue_context_list = rrc_gNB_get_ue_context_by_rnti_any_du(RC.nrrrc[mod_id], rnti);
const uint16_t UE_mcc = rrc_ue_context_list->ue_context.ue_guami.mcc;
const uint16_t UE_mnc = rrc_ue_context_list->ue_context.ue_guami.mnc;
const uint8_t UE_sst = c->nssai.sst;
const uint32_t UE_sd = c->nssai.sd;
LOG_D(NR_MAC, "UE: mcc %d mnc %d, sst %d sd %d, RC: mcc %d mnc %d, sst %d sd %d\n",
UE_mcc, UE_mnc, UE_sst, UE_sd, RC_mcc, RC_mnc, RC_nssai.sst, RC_nssai.sd);
if (UE_mcc == RC_mcc && UE_mnc == RC_mnc && UE_sst == RC_nssai.sst && UE_sd == RC_nssai.sd) {
dl->add_UE(dl->slices, UE);
assoc_ue = true;
} else {
LOG_E(NR_MAC, "Failed adding UE (PLMN: mcc %d mnc %d, NSSAI: sst %d sd %d) to slice (PLMN: mcc %d mnc %d, NSSAI: sst %d sd %d)\n",
UE_mcc, UE_mnc, UE_sst, UE_sd, RC_mcc, RC_mnc, RC_nssai.sst, RC_nssai.sd);
}
}
}
if (!assoc_ue)
LOG_E(NR_MAC, "Failed matching UE rnti %x with current slice (sst %d, sd %d), might lost user plane data\n", rnti, RC_nssai.sst, RC_nssai.sd);
}
}
pthread_mutex_unlock(&nrmac->UE_info.mutex);
LOG_D(NR_MAC, "All slices add/mod successfully!\n");
return true;
}

View File

@@ -0,0 +1,36 @@
#ifndef OPENAIRINTERFACE_RC_CTRL_SERVICE_STYLE_2_H
#define OPENAIRINTERFACE_RC_CTRL_SERVICE_STYLE_2_H
#include "openair2/E2AP/flexric/src/sm/rc_sm/ie/ir/lst_ran_param.h"
#include "openair2/E2AP/flexric/src/sm/rc_sm/ie/ir/ran_param_list.h"
#include "openair2/E2AP/flexric/src/sm/rc_sm/ie/ir/ran_parameter_value.h"
#include "openair2/E2AP/flexric/src/sm/rc_sm/ie/ir/ran_parameter_value_type.h"
#include "openair2/E2AP/flexric/src/sm/slice_sm/ie/slice_data_ie.h"
bool add_mod_rc_slice(int mod_id, size_t slices_len, ran_param_list_t* lst);
typedef enum{
DRX_parameter_configuration_7_6_3_1 = 1,
SR_periodicity_configuration_7_6_3_1 = 2,
SPS_parameters_configuration_7_6_3_1 = 3,
Configured_grant_control_7_6_3_1 = 4,
CQI_table_configuration_7_6_3_1 = 5,
Slice_level_PRB_quotal_7_6_3_1 = 6,
} rc_ctrl_service_style_2_act_id_e;
typedef enum {
RRM_Policy_Ratio_List_8_4_3_6 = 1,
RRM_Policy_Ratio_Group_8_4_3_6 = 2,
RRM_Policy_8_4_3_6 = 3,
RRM_Policy_Member_List_8_4_3_6 = 4,
RRM_Policy_Member_8_4_3_6 = 5,
PLMN_Identity_8_4_3_6 = 6,
S_NSSAI_8_4_3_6 = 7,
SST_8_4_3_6 = 8,
SD_8_4_3_6 = 9,
Min_PRB_Policy_Ratio_8_4_3_6 = 10,
Max_PRB_Policy_Ratio_8_4_3_6 = 11,
Dedicated_PRB_Policy_Ratio_8_4_3_6 = 12,
} slice_level_PRB_quota_param_id_e;
#endif // OPENAIRINTERFACE_RC_CTRL_SERVICE_STYLE_2_H

View File

@@ -324,8 +324,11 @@ static void nr_store_dlsch_buffer(module_id_t module_id, frame_t frame, sub_fram
{
UE_iterator(RC.nrmac[module_id]->UE_info.list, UE) {
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
sched_ctrl->num_total_bytes = 0;
sched_ctrl->dl_pdus_total = 0;
/* add all LCID to fist slice. This is to have a common post-processor function for both slicing and non-slicing scheduler */
const int slice_id = sched_ctrl->last_sched_slice = 0;
sched_ctrl->sliceInfo[slice_id].num_total_bytes = 0;
sched_ctrl->sliceInfo[slice_id].dl_pdus_total = 0;
reset_nr_list(&sched_ctrl->sliceInfo[slice_id].lcid);
/* loop over all activated logical channels */
// Note: DL_SCH_LCID_DCCH, DL_SCH_LCID_DCCH1, DL_SCH_LCID_DTCH
@@ -352,20 +355,22 @@ static void nr_store_dlsch_buffer(module_id_t module_id, frame_t frame, sub_fram
if (sched_ctrl->rlc_status[lcid].bytes_in_buffer == 0)
continue;
sched_ctrl->dl_pdus_total += sched_ctrl->rlc_status[lcid].pdus_in_buffer;
sched_ctrl->num_total_bytes += sched_ctrl->rlc_status[lcid].bytes_in_buffer;
sched_ctrl->sliceInfo[slice_id].dl_pdus_total += sched_ctrl->rlc_status[lcid].pdus_in_buffer;
sched_ctrl->sliceInfo[slice_id].num_total_bytes += sched_ctrl->rlc_status[lcid].bytes_in_buffer;
add_nr_list(&sched_ctrl->sliceInfo[slice_id].lcid, lcid);
LOG_D(MAC,
"[gNB %d][%4d.%2d] %s%d->DLSCH, RLC status for UE %d: %d bytes in buffer, total DL buffer size = %d bytes, %d total PDU bytes, %s TA command\n",
"[gNB %d][%4d.%2d] %s%d->DLSCH, RLC status for UE %d: %d bytes in buffer, total DL buffer size = %d bytes, %d total "
"PDU bytes, %s TA command\n",
module_id,
frame,
slot,
lcid < 4 ? "DCCH":"DTCH",
lcid < 4 ? "DCCH" : "DTCH",
lcid,
UE->rnti,
sched_ctrl->rlc_status[lcid].bytes_in_buffer,
sched_ctrl->num_total_bytes,
sched_ctrl->dl_pdus_total,
sched_ctrl->ta_apply ? "send":"do not send");
sched_ctrl->sliceInfo[slice_id].num_total_bytes,
sched_ctrl->sliceInfo[slice_id].dl_pdus_total,
sched_ctrl->ta_apply ? "send" : "do not send");
}
}
}
@@ -603,12 +608,29 @@ static int comparator(const void *p, const void *q) {
return ((UEsched_t*)p)->coef < ((UEsched_t*)q)->coef;
}
static void pf_dl(module_id_t module_id,
frame_t frame,
sub_frame_t slot,
NR_UE_info_t **UE_list,
int max_num_ue,
int n_rb_sched)
static void *nr_pf_dl_setup(void)
{
void *data = malloc(MAX_MOBILES_PER_GNB * sizeof(float));
AssertFatal(data, "%s(): could not allocate data\n", __func__);
for (int i = 0; i < MAX_MOBILES_PER_ENB; i++)
*(float *) data = 0.0f;
return data;
}
static void nr_pf_dl_unset(void **data)
{
DevAssert(data);
if (*data)
free(*data);
*data = NULL;
}
static int nr_pf_dl(module_id_t module_id,
frame_t frame,
sub_frame_t slot,
NR_UE_info_t **UE_list,
int max_num_ue,
int n_rb_sched,
void *data)
{
gNB_MAC_INST *mac = RC.nrmac[module_id];
NR_ServingCellConfigCommon_t *scc=mac->common_channels[0].ServingCellConfigCommon;
@@ -669,7 +691,7 @@ static void pf_dl(module_id_t module_id,
}
/* Check DL buffer and skip this UE if no bytes and no TA necessary */
if (sched_ctrl->num_total_bytes == 0 && frame != (sched_ctrl->ta_frame + 100) % 1024)
if (sched_ctrl->sliceInfo[sched_ctrl->last_sched_slice].num_total_bytes == 0 && frame != (sched_ctrl->ta_frame + 10) % 1024)
continue;
/* Calculate coeff */
@@ -842,7 +864,7 @@ static void pf_dl(module_id_t module_id,
sched_pdsch->nrOfLayers,
tda_info->nrOfSymbols,
sched_pdsch->dmrs_parms.N_PRB_DMRS * sched_pdsch->dmrs_parms.N_DMRS_SLOT,
sched_ctrl->num_total_bytes + oh,
sched_ctrl->sliceInfo[sched_ctrl->last_sched_slice].num_total_bytes + oh,
min_rbSize,
max_rbSize,
&TBS,
@@ -859,7 +881,15 @@ static void pf_dl(module_id_t module_id,
remainUEs--;
iterator++;
}
return n_rb_sched;
}
nr_dl_sched_algo_t nr_proportional_fair_wbcqi_dl = {
.name = "nr_proportional_fair_wbcqi_dl",
.setup = nr_pf_dl_setup,
.unset = nr_pf_dl_unset,
.run = nr_pf_dl,
.data = NULL
};
static void nr_fr1_dlsch_preprocessor(module_id_t module_id, frame_t frame, sub_frame_t slot)
{
@@ -881,15 +911,16 @@ static void nr_fr1_dlsch_preprocessor(module_id_t module_id, frame_t frame, sub_
max_sched_ues = min(max_sched_ues, MAX_DCI_CORESET);
/* proportional fair scheduling algorithm */
pf_dl(module_id,
frame,
slot,
UE_info->list,
max_sched_ues,
bw); // we set the whole BW as max number
RC.nrmac[module_id]->pre_processor_dl.dl_algo.run(module_id,
frame,
slot,
UE_info->list,
max_sched_ues,
bw,
RC.nrmac[module_id]->pre_processor_dl.dl_algo.data);
}
nr_pp_impl_dl nr_init_fr1_dlsch_preprocessor(int CC_id) {
nr_pp_impl_param_dl_t nr_init_fr1_dlsch_preprocessor(int CC_id) {
/* during initialization: no mutex needed */
/* in the PF algorithm, we have to use the TBsize to compute the coefficient.
* This would include the number of DMRS symbols, which in turn depends on
@@ -914,7 +945,12 @@ nr_pp_impl_dl nr_init_fr1_dlsch_preprocessor(int CC_id) {
}
}
return nr_fr1_dlsch_preprocessor;
nr_pp_impl_param_dl_t impl;
memset(&impl, 0, sizeof(impl));
impl.dl = nr_fr1_dlsch_preprocessor;
impl.dl_algo = nr_proportional_fair_wbcqi_dl;
impl.dl_algo.data = impl.dl_algo.setup();
return impl;
}
void nr_schedule_ue_spec(module_id_t module_id,
@@ -932,7 +968,10 @@ void nr_schedule_ue_spec(module_id_t module_id,
return;
/* PREPROCESSOR */
gNB_mac->pre_processor_dl(module_id, frame, slot);
pthread_mutex_lock(&gNB_mac->UE_info.mutex);
gNB_mac->pre_processor_dl.dl(module_id, frame, slot);
pthread_mutex_unlock(&gNB_mac->UE_info.mutex);
const int CC_id = 0;
NR_ServingCellConfigCommon_t *scc = gNB_mac->common_channels[CC_id].ServingCellConfigCommon;
NR_UEs_t *UE_info = &gNB_mac->UE_info;
@@ -1269,11 +1308,11 @@ void nr_schedule_ue_spec(module_id_t module_id,
start_meas(&gNB_mac->rlc_data_req);
int sdus = 0;
if (sched_ctrl->num_total_bytes > 0) {
/* loop over all activated logical channels */
for (int i = 0; i < seq_arr_size(&sched_ctrl->lc_config); ++i) {
const nr_lc_config_t *c = seq_arr_at(&sched_ctrl->lc_config, i);
const int lcid = c->lcid;
if (sched_ctrl->sliceInfo[sched_ctrl->last_sched_slice].num_total_bytes > 0) {
/* loop over all activated logical channels in this slice */
NR_List_Iterator(&sched_ctrl->sliceInfo[sched_ctrl->last_sched_slice].lcid, lcidP)
{
const int lcid = *lcidP;
if (sched_ctrl->rlc_status[lcid].bytes_in_buffer == 0)
continue; // no data for this LC tbs_size_t len = 0;
@@ -1325,6 +1364,7 @@ void nr_schedule_ue_spec(module_id_t module_id,
UE->mac_stats.dl.lc_bytes[lcid] += lcid_bytes;
}
sched_ctrl->harq_slice_map[sched_ctrl->sched_pdsch.dl_harq_pid] = sched_ctrl->last_sched_slice;
} else if (get_softmodem_params()->phy_test || get_softmodem_params()->do_ra) {
/* we will need the large header, phy-test typically allocates all
* resources and fills to the last byte below */

View File

@@ -60,6 +60,7 @@
#include "common/ran_context.h"
#include "nfapi/oai_integration/vendor_ext.h"
#include "NR_MAC_gNB/slicing/nr_slicing.h"
#include "common/utils/alg/find.h"
@@ -1950,6 +1951,30 @@ void create_nr_list(NR_list_t *list, int len)
list->len = len;
}
/*
* Reset NR_list
*/
void reset_nr_list(NR_list_t *list)
{
list->head = -1;
memset(list->next, -1, list->len);
list->tail = -1;
}
/*
* Check if id is in the list
*/
bool check_nr_list(const NR_list_t *listP, int id)
{
const int *cur = &listP->head;
while (*cur >= 0) {
if (*cur == id)
return true;
cur = &listP->next[*cur];
}
return false;
}
/*
* Resize an NR_list
*/
@@ -2089,6 +2114,10 @@ void delete_nr_ue_data(NR_UE_info_t *UE, NR_COMMON_channels_t *ccPtr, uid_alloca
destroy_nr_list(&sched_ctrl->retrans_ul_harq);
free_sched_pucch_list(sched_ctrl);
uid_linear_allocator_free(uia, UE->uid);
for (int slice = 0; slice < NR_MAX_NUM_SLICES; slice++) {
destroy_nr_list(&sched_ctrl->sliceInfo[slice].lcid);
}
destroy_nr_list(&UE->dl_id);
LOG_I(NR_MAC, "Remove NR rnti 0x%04x\n", UE->rnti);
free(UE);
}
@@ -2504,6 +2533,16 @@ NR_UE_info_t *add_new_nr_ue(gNB_MAC_INST *nr_mac, rnti_t rntiP, NR_CellGroupConf
reset_srs_stats(UE);
/* prepare LC list for all slices in this UE */
for (int slice = 0; slice < NR_MAX_NUM_SLICES; slice++) {
create_nr_list(&sched_ctrl->sliceInfo[slice].lcid, NR_MAX_NUM_LCID);
}
create_nr_list(&UE->dl_id, NR_MAX_NUM_SLICES);
// associate UEs to the first slice if slice exists (there is no DRB setup in this stage)
nr_pp_impl_param_dl_t *dl = &RC.nrmac[0]->pre_processor_dl;
if (dl->slices)
dl->add_UE(dl->slices, UE);
NR_SCHED_LOCK(&UE_info->mutex);
int i;
for(i=0; i<MAX_MOBILES_PER_GNB; i++) {
@@ -2667,6 +2706,14 @@ void mac_remove_nr_ue(gNB_MAC_INST *nr_mac, rnti_t rnti)
return;
}
/* Dissociate UE from all corresponding slice*/
nr_pp_impl_param_dl_t *dl = &nr_mac->pre_processor_dl;
if (dl->slices) {
for (int i = 0; i < dl->slices->num; i++) {
dl->remove_UE(dl->slices, UE, i);
}
}
NR_UE_info_t * newUEs[MAX_MOBILES_PER_GNB+1]={0};
int newListIdx=0;
for (int i=0; i<MAX_MOBILES_PER_GNB; i++)
@@ -3269,7 +3316,15 @@ bool prepare_initial_ul_rrc_message(gNB_MAC_INST *mac, NR_UE_info_t *UE)
int priority = bearer->mac_LogicalChannelConfig->ul_SpecificParameters->priority;
nr_lc_config_t c = {.lcid = bearer->logicalChannelIdentity, .priority = priority};
nr_pp_impl_param_dl_t *dl = &mac->pre_processor_dl;
/* for UEs added before RIC configures slices */
const nssai_t default_slice_nssai = {.sst = 0, .sd = 0};
/* if slices are configured, first slice is default slice for SRBs */
c.nssai = (dl->slices) ? dl->slices->s[0]->nssai : default_slice_nssai;
nr_mac_add_lcid(&UE->UE_sched_ctrl, &c);
if (dl->slices) {
dl->add_UE(dl->slices, UE);
}
return true;
}

View File

@@ -83,7 +83,7 @@ void nr_schedule_ue_spec(module_id_t module_id,
nfapi_nr_tx_data_request_t *TX_req);
/* \brief default FR1 DL preprocessor init routine, returns preprocessor to call */
nr_pp_impl_dl nr_init_fr1_dlsch_preprocessor(int CC_id);
nr_pp_impl_param_dl_t nr_init_fr1_dlsch_preprocessor(int CC_id);
void schedule_nr_sib1(module_id_t module_idP,
frame_t frameP,
@@ -197,7 +197,7 @@ int nr_acknack_scheduling(gNB_MAC_INST *mac,
int get_pdsch_to_harq_feedback(NR_PUCCH_Config_t *pucch_Config,
nr_dci_format_t dci_format,
uint8_t *pdsch_to_harq_feedback);
int nr_get_pucch_resource(NR_ControlResourceSet_t *coreset,
NR_PUCCH_Config_t *pucch_Config,
int CCEIndex);
@@ -318,6 +318,8 @@ void remove_nr_list(NR_list_t *listP, int id);
void add_tail_nr_list(NR_list_t *listP, int id);
void add_front_nr_list(NR_list_t *listP, int id);
void remove_front_nr_list(NR_list_t *listP);
void reset_nr_list(NR_list_t *listP);
bool check_nr_list(const NR_list_t *listP, int id);
NR_UE_info_t * find_nr_UE(NR_UEs_t* UEs, rnti_t rntiP);

View File

@@ -32,6 +32,7 @@
#include "lib/f1ap_interface_management.h"
#include "executables/softmodem-common.h"
#include "NR_MAC_gNB/slicing/nr_slicing.h"
#include "uper_decoder.h"
#include "uper_encoder.h"
@@ -220,6 +221,7 @@ static int handle_ue_context_srbs_setup(NR_UE_info_t *UE,
f1ap_srb_to_be_setup_t **resp_srbs,
NR_CellGroupConfig_t *cellGroupConfig)
{
gNB_MAC_INST *mac = RC.nrmac[0];
DevAssert(req_srbs != NULL && resp_srbs != NULL && cellGroupConfig != NULL);
*resp_srbs = calloc(srbs_len, sizeof(**resp_srbs));
@@ -230,7 +232,12 @@ static int handle_ue_context_srbs_setup(NR_UE_info_t *UE,
nr_rlc_add_srb(UE->rnti, srb->srb_id, rlc_BearerConfig);
int priority = rlc_BearerConfig->mac_LogicalChannelConfig->ul_SpecificParameters->priority;
/* for UEs added before RIC configures slices */
const nssai_t default_slice_nssai = {.sst = 0, .sd = 0};
nr_lc_config_t c = {.lcid = rlc_BearerConfig->logicalChannelIdentity, .priority = priority};
/* if slices are configured, first slice is default slice for SRBs */
nr_pp_impl_param_dl_t *dl = &mac->pre_processor_dl;
c.nssai = (dl->slices) ? dl->slices->s[0]->nssai : default_slice_nssai;
nr_mac_add_lcid(&UE->UE_sched_ctrl, &c);
(*resp_srbs)[i].srb_id = srb->srb_id;
@@ -588,6 +595,11 @@ void ue_context_setup_request(const f1ap_ue_context_setup_t *req)
nr_mac_prepare_cellgroup_update(mac, UE, new_CellGroup);
/* Associate UE to the corresponding slice */
nr_pp_impl_param_dl_t *dl = &mac->pre_processor_dl;
if (dl->slices)
dl->add_UE(dl->slices, UE);
NR_SCHED_UNLOCK(&mac->sched_lock);
/* some sanity checks, since we use the same type for request and response */
@@ -686,6 +698,12 @@ void ue_context_modification_request(const f1ap_ue_context_modif_req_t *req)
resp.du_to_cu_rrc_information->cellGroupConfig_length = (enc_rval.encoded + 7) >> 3;
nr_mac_prepare_cellgroup_update(mac, UE, new_CellGroup);
/* Associate UE to the corresponding slice*/
nr_pp_impl_param_dl_t *dl = &mac->pre_processor_dl;
if (dl->slices)
dl->add_UE(dl->slices, UE);
} else {
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, new_CellGroup); // we actually don't need it
}

View File

@@ -261,7 +261,7 @@ void mac_top_init_gNB(ngran_node_t node_type,
uid_linear_allocator_init(&RC.nrmac[i]->UE_info.uid_allocator);
if (get_softmodem_params()->phy_test) {
RC.nrmac[i]->pre_processor_dl = nr_preprocessor_phytest;
RC.nrmac[i]->pre_processor_dl.dl = nr_preprocessor_phytest;
RC.nrmac[i]->pre_processor_ul = nr_ul_preprocessor_phytest;
} else {
RC.nrmac[i]->pre_processor_dl = nr_init_fr1_dlsch_preprocessor(0);

View File

@@ -113,6 +113,8 @@ typedef struct {
int len;
} NR_list_t;
#define NR_List_Iterator(BaSe, CuR) for (int *CuR = &(BaSe)->head, *nxt = (BaSe)->next; *CuR >= 0; CuR = &nxt[*CuR])
typedef enum {
nrRA_gNB_IDLE,
nrRA_Msg2,
@@ -573,6 +575,14 @@ typedef struct nr_lc_config {
NR_QoS_config_t qos_config[NR_MAX_NUM_QFI];
} nr_lc_config_t;
typedef struct {
/// LCs in this slice
NR_list_t lcid;
/// total amount of data awaiting for this UE
uint32_t num_total_bytes;
uint16_t dl_pdus_total;
} NR_UE_slice_info_t;
/*! \brief scheduling control information set through an API */
#define MAX_CSI_REPORTS 48
typedef struct {
@@ -673,6 +683,12 @@ typedef struct {
/// per-LC configuration
seq_arr_t lc_config;
/// last scheduled slice index
int last_sched_slice;
/// hold information of slices
NR_UE_slice_info_t sliceInfo[NR_MAX_NUM_SLICES];
/// DL harq to slice map
int harq_slice_map[NR_MAX_HARQ_PROCESSES];
} NR_UE_sched_ctrl_t;
typedef struct {
@@ -767,6 +783,8 @@ typedef struct {
float ul_thr_ue;
float dl_thr_ue;
long pdsch_HARQ_ACK_Codebook;
/// Assoc slice
NR_list_t dl_id;
} NR_UE_info_t;
typedef struct {
@@ -789,6 +807,19 @@ typedef struct {
#define UE_iterator(BaSe, VaR) NR_UE_info_t ** VaR##pptr=BaSe, *VaR; while ((VaR=*(VaR##pptr++)))
/**
* definition of a scheduling algorithm implementation used in the
* default DL scheduler
*/
typedef struct {
char *name;
void *(*setup)(void);
void (*unset)(void **);
int (*run)(
module_id_t, frame_t, sub_frame_t, NR_UE_info_t **, int, int, void *);
void *data;
} nr_dl_sched_algo_t;
typedef void (*nr_pp_impl_dl)(module_id_t mod_id,
frame_t frame,
sub_frame_t slot);
@@ -796,6 +827,44 @@ typedef bool (*nr_pp_impl_ul)(module_id_t mod_id,
frame_t frame,
sub_frame_t slot);
struct nr_slice_info_s;
struct nr_slice_s;
typedef struct {
int algorithm;
/// inform the slice algorithm about a new UE
void (*add_UE)(struct nr_slice_info_s *s, NR_UE_info_t *new_ue);
/// inform the slice algorithm about a UE that disconnected
void (*remove_UE)(struct nr_slice_info_s *s, NR_UE_info_t* rm_ue, int idx);
/// move a UE to a slice in DL/UL, -1 means don't move (no-op).
void (*move_UE)(struct nr_slice_info_s *s, NR_UE_info_t* assoc_ue, int old_idx, int new_idx);
/// get UE associated slice's index
int (*get_UE_slice_idx)(struct nr_slice_info_s *s, rnti_t rnti);
/// get UE's index from the slice
int (*get_UE_idx)(struct nr_slice_s *si, rnti_t rnti);
/// Adds a new slice through admission control. slice_params are
/// algorithm-specific parameters. sched is either a default_sched_ul_algo_t
/// or default_sched_dl_algo_t, depending on whether this implementation
/// handles UL/DL. If slice at index exists, updates existing
/// slice. Returns index of new slice or -1 on failure.
int (*addmod_slice)(struct nr_slice_info_s *s,
int id,
nssai_t nssai,
char *label,
void *sched,
void *slice_params);
/// Returns slice through slice_idx. 1 if successful, 0 if not.
int (*remove_slice)(struct nr_slice_info_s *s, uint8_t slice_idx);
nr_pp_impl_dl dl;
nr_dl_sched_algo_t dl_algo;
void (*destroy)(struct nr_slice_info_s **s);
struct nr_slice_info_s *slices;
} nr_pp_impl_param_dl_t;
typedef struct f1_config_t {
f1ap_setup_req_t *setup_req;
f1ap_setup_resp_t *setup_resp;
@@ -903,7 +972,7 @@ typedef struct gNB_MAC_INST_s {
uint32_t ulsch_max_frame_inactivity;
/// DL preprocessor for differentiated scheduling
nr_pp_impl_dl pre_processor_dl;
nr_pp_impl_param_dl_t pre_processor_dl;
/// UL preprocessor for differentiated scheduling
nr_pp_impl_ul pre_processor_ul;

View File

@@ -0,0 +1,112 @@
# Slicing in NR MAC Scheduler
`nr_schedule_ue_spec()` is divided into two parts:
1. **Pre-processor** makes the scheduling decisions by **slice-** and **user-** levels scheduling [1] [2]
2. **Post-processor** fills the scheduling decisions into nFAPI structures to indicate to the PHY
The pre-processor is defined by the structure `nr_pp_impl_param_dl_t` and
it is initialized by `nr_init_fr1_dlsch_preprocessor()` in `mac_top_init_gNB()`:
```c
RC.nrmac[i]->pre_processor_dl = nr_init_fr1_dlsch_preprocessor(0);
```
In addition, the algorithm for slice-level scheduler
is defined by the structure `nr_pp_impl_dl` and
is assigned by the function pointer `nr_fr1_dlsch_preprocessor()` by default, which is _none_ slicing algorithm (means there is no slices in the system to be scheduled).
On the other hand, the algorithm for user-level scheduler
is defined by the structure `nr_dl_sched_algo_t` and
is assigned by the structure pointer `nr_proportional_fair_wbcqi_dl` by default, which is adopting _proportional fair_ algorithm (`nr_pf_dl()`) to schedule the UEs.
Both schedulers are initialized in `nr_init_fr1_dlsch_preprocessor()`:
```c
nr_pp_impl_param_dl_t impl;
memset(&impl, 0, sizeof(impl));
impl.dl = nr_fr1_dlsch_preprocessor;
impl.dl_algo = nr_proportional_fair_wbcqi_dl;
```
## Slice-level Scheduler
The slice-level scheduler in pre-preprocessor is enabled by calling
`gNB_mac->pre_processor_dl.dl()` in `nr_schedule_ue_spec()`.
Through Near-Real-Time RIC, xApp and RAN Control (RC) service model,
the slice-level scheduler can be customized to other algorithm on-the-fly.
> For an example,NVS [3] algorithm is implemented in the `nr_slicing.c`,
and it is initialized through `set_new_dl_slice_algo()` in the RC RAN function `E2AP/RAN_FUNCTION/rc_ran_func.c`:
> ```c
> switch (algo) {
> case NVS_SLICING:
> nrmac->pre_processor_dl = nvs_nr_dl_init(mod_id);
> break;
> default:
> nrmac->pre_processor_dl.algorithm = 0;
> nrmac->pre_processor_dl = nr_init_fr1_dlsch_preprocessor(0);
> nrmac->pre_processor_dl.slices = NULL;
> break;
> }
> ```
> In `nvs_nr_dl_init()`, the slice-level scheduler is changed to point to the function `nvs_nr_dl()`,
which will be called by `gNB_mac->pre_processor_dl.dl()` in the pre-processor to schedule the slices based on the NVS algorithm.
To implement different slice-level scheduling algorithm,
you can start from `nr_slicing.h` by adding the parameters related to your scheduling algorithm
and init function (e.g., `nvs_nr_dl_init()`).
Afterwards, you can start create the corresponding functions which are used in your init function,
> take an example in `nvs_nr_dl_init()`, these are the required functions need to be implemented:
> - `nvs_nr_dl()` is the function for your slice-level scheduling algorithm (i.e, select a slice, or sort slices)
> - `addmod_nvs_nr_slice_dl()` is the function to add or modify the setting/configuration of existing slices (i.e., increase the capacity, change user-level scheduling algorithm)
> - `remove_nvs_nr_slice_dl()` is the function to remove the slice from the existing slices
> - `nvs_nr_destroy()` is the function to delete all the slices in the system
## User-level Scheduler
The user-level scheduler in pre-preprocessor is enabled by calling
`RC.nrmac[module_id]->pre_processor_dl.dl_algo.run()` in `nr_fr1_dlsch_preprocessor()` by default.
> Once the NVS algorithm is applied to the slice-level scheduler, the user-level scheduler is enabled by calling
> `si->s[maxidx]->dl_algo.run()` in `nvs_nr_dl()`.
Note that the algorithm of user-level scheduler also can be customized for each slice, for an example,
the user-level scheduling algorithm `nr_proportional_fair_wbcqi_dl` is defined in `gNB_scheduler_dlsch.c`:
```c
nr_dl_sched_algo_t nr_proportional_fair_wbcqi_dl = {
.name = "nr_proportional_fair_wbcqi_dl",
.setup = nr_pf_dl_setup,
.unset = nr_pf_dl_unset,
.run = nr_pf_dl,
.data = NULL
};
```
By following above structure `nr_dl_sched_algo_t`,
the other user-level scheduling algorithm can be implemented and be assigned to different slices,
> for an example, in `addmod_nvs_nr_slice_dl()`:
> ```c
> s->dl_algo = *(nr_dl_sched_algo_t*) algo;
> ```
## Examples
### Enable 3GPP slices (NSSAI-based) by using NVS algorithm to achieve minimum PRB Ratio
The NVS scheduling algorithm, as detailed in the referenced paper [3],
operates by determining the maximum weight for slices in each time slot.
The weight represents the marginal utility of each slice with respect to the achieved cumulative fraction of resources.
At each time slot,
the algorithm selects the slice with the highest weight,
and allocate the resource to the UEs associated to the selected slice.
> For instance, if there are two slices
and both are set to use 50% of the resources from system (assuming there are 8 downlink slots in 5 sub-frames),
every time slot will utilize 100% of Resource Blocks (RBs) for the chosen slice.
To be more concrete, the selected slots for two slices could be as follows:
> - slice 1 occupies 0, 2, 4, 6th slot
> - slice 2 occupies 1, 3, 5, 7th slot
Based on the _RRMPolicyRatio_ defined by 3GPP TS 28.541 v16.6, as shown in below figure,
the prioritized resource can be achieved by using type `NVS_RES` and parameter `pct_reserved` from NVS algorithm.
These resources are preferentially used by the associated slice, and
are guaranteed for use by the associated slice when it needs to use them.
When not used, these resources may be used by other slices.
Therefore, in the RC RAN function, `pct_reserved` is assigned by `Min_PRB_Policy_Ratio` which is given by xApp via Near-Real-Time RIC,
to configure the slices.
![TS28_541_structure_of_RRMPolicyRatio](TS28_541_structure_of_RRMPolicyRatio.png)
### References
- [1] [SLICING IN HETEROGENEOUS SOFTWARE-DEFINED RADIO ACCESS NETWORKS](https://www.eurecom.fr/en/publication/6635)
- [2] [FLEXSLICE: FLEXIBLE AND REAL-TIME PROGRAMMABLE RAN SLICING FRAMEWORK](https://www.eurecom.fr/en/publication/7416)
- [3] [NVS: A Substrate for Virtualizing Wireless Resources in Cellular Networks](https://ieeexplore.ieee.org/document/6117098)

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,575 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*!
* \file nr_slicing.c
* \brief Generic NR Slicing helper functions and Static Slicing Implementation
* \author Robert Schmidt
* \date 2021
* \email robert.schmidt@eurecom.fr
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <dlfcn.h>
#include "assertions.h"
#include "common/utils/LOG/log.h"
#include "common/utils/nr/nr_common.h"
#include "NR_MAC_COMMON/nr_mac_extern.h"
#include "NR_MAC_COMMON/nr_mac.h"
#include "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h"
#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h"
#include "openair2/LAYER2/RLC/rlc.h"
#include "nr_slicing.h"
#include "nr_slicing_internal.h"
#include "executables/softmodem-common.h"
#include "common/utils/alg/find.h"
extern RAN_CONTEXT_t RC;
#define RET_FAIL(ret, x...) do { LOG_E(MAC, x); return ret; } while (0)
int nr_slicing_get_UE_slice_idx(nr_slice_info_t *si, rnti_t rnti)
{
for (int s_len = 0; s_len < si->num; s_len++) {
for (int i = 0; i < MAX_MOBILES_PER_GNB; i++) {
if (si->s[s_len]->UE_list[i] != NULL) {
if (si->s[s_len]->UE_list[i]->rnti == rnti) {
return s_len;
}
}
}
}
LOG_E(NR_MAC, "cannot find slice idx for UE rnti 0x%04x\n", rnti);
return -99;
}
int nr_slicing_get_UE_idx(nr_slice_t *si, rnti_t rnti)
{
for (int i = 0; i < MAX_MOBILES_PER_GNB; i++) {
if (si->UE_list[i] != NULL) {
LOG_D(NR_MAC, "nr_slicing_get_UE_idx: si->UE_list[%d]->rnti %x map to rnti %x\n", i, si->UE_list[i]->rnti, rnti);
if (si->UE_list[i]->rnti == rnti)
return i;
}
}
LOG_E(NR_MAC, "cannot find ue idx for UE rnti 0x%04x\n", rnti);
return -99;
}
static bool nssai_matches(nssai_t a_nssai, uint8_t b_sst, const uint32_t *b_sd)
{
AssertFatal(b_sd == NULL || *b_sd <= 0xffffff, "illegal SD %d\n", *b_sd);
if (b_sd == NULL) {
return a_nssai.sst == b_sst && a_nssai.sd == 0xffffff;
} else {
return a_nssai.sst == b_sst && a_nssai.sd == *b_sd;
}
}
void nr_slicing_add_UE(nr_slice_info_t *si, NR_UE_info_t *new_ue)
{
AssertFatal(si->num > 0 && si->s != NULL, "no slices exists, cannot add UEs\n");
NR_UE_sched_ctrl_t *sched_ctrl = &new_ue->UE_sched_ctrl;
reset_nr_list(&new_ue->dl_id);
for (int i = 0; i < si->num; ++i) {
reset_nr_list(&sched_ctrl->sliceInfo[i].lcid);
bool matched_ue = false;
for (int l = 0; l < seq_arr_size(&sched_ctrl->lc_config); l++) {
const nr_lc_config_t *c = seq_arr_at(&sched_ctrl->lc_config, l);
const long lcid = c->lcid;
if (nssai_matches(c->nssai, si->s[i]->nssai.sst, &si->s[i]->nssai.sd)) {
/* add this LCID to slice's LCID list */
add_nr_list(&sched_ctrl->sliceInfo[i].lcid, lcid);
matched_ue = true;
}
}
if (matched_ue) {
/* check if incoming UE is already in slice list */
UE_iterator(si->s[i]->UE_list, UE) {
if (UE->rnti == new_ue->rnti)
break;
}
if (UE)
continue; // go to next slice
/* add this slice id to this UE's slice list */
add_nr_list(&new_ue->dl_id, si->s[i]->id);
int num_UEs = si->s[i]->num_UEs;
if (si->s[i]->UE_list[num_UEs] == NULL) {
si->s[i]->UE_list[num_UEs] = new_ue;
si->s[i]->num_UEs += 1;
LOG_I(NR_MAC,
"Add UE rnti 0x%04x to slice idx %d, sst %d, sd %d\n",
new_ue->rnti,
i,
si->s[i]->nssai.sst,
si->s[i]->nssai.sd);
} else {
LOG_E(NR_MAC, "cannot add new UE rnti 0x%04x to slice idx %d, num_UEs %d\n", new_ue->rnti, i, si->s[i]->num_UEs);
}
}
}
}
void nr_slicing_remove_UE(nr_slice_info_t *si, NR_UE_info_t* rm_ue, int idx)
{
for (int i = 0; i < MAX_MOBILES_PER_GNB; i++) {
if(si->s[idx]->UE_list[i] != NULL) {
if (si->s[idx]->UE_list[i]->rnti == rm_ue->rnti) {
si->s[idx]->UE_list[i] = NULL;
si->s[idx]->num_UEs -= 1;
remove_nr_list(&rm_ue->dl_id, si->s[idx]->id);
break;
}
}
}
}
void nr_slicing_move_UE(nr_slice_info_t *si, NR_UE_info_t* assoc_ue, int old_idx, int new_idx)
{
DevAssert(new_idx >= -1 && new_idx < si->num);
DevAssert(old_idx >= -1 && old_idx < si->num);
// add UE to new slice
remove_nr_list(&assoc_ue->dl_id, si->s[old_idx]->id);
add_nr_list(&assoc_ue->dl_id, si->s[new_idx]->id);
int cur_idx = si->s[new_idx]->num_UEs;
si->s[new_idx]->UE_list[cur_idx] = assoc_ue;
si->s[new_idx]->num_UEs += 1;
// remove from old slice
for (int i = 0; i < MAX_MOBILES_PER_GNB; i++) {
if(si->s[old_idx]->UE_list[i] != NULL) {
if (si->s[old_idx]->UE_list[i]->rnti == assoc_ue->rnti) {
si->s[old_idx]->UE_list[i] = NULL;
si->s[old_idx]->num_UEs -= 1;
break;
}
}
}
// reorder UE_list
int n, m = 0;
for (n = 0; n < MAX_MOBILES_PER_GNB; n++) {
if (si->s[old_idx]->UE_list[n] != NULL) {
si->s[old_idx]->UE_list[m++] = si->s[old_idx]->UE_list[n];
}
}
while (m < MAX_MOBILES_PER_GNB) {
si->s[old_idx]->UE_list[m++] = NULL;
}
}
int _nr_exists_slice(uint8_t n, nr_slice_t **s, int id)
{
for (int i = 0; i < n; ++i) {
LOG_D(NR_MAC, "_nr_exists_slice(): n %d, s[%d]->id %d, id %d\n", n ,i, s[i]->id, id);
if (s[i]->id == id)
return i;
}
return -1;
}
nr_slice_t *_nr_add_slice(uint8_t *n, nr_slice_t **s)
{
s[*n] = calloc(1, sizeof(nr_slice_t));
if (!s[*n])
return NULL;
*n += 1;
return s[*n - 1];
}
nr_slice_t *_nr_remove_slice(uint8_t *n, nr_slice_t **s, int idx)
{
if (idx >= *n)
return NULL;
nr_slice_t *sr = s[idx];
for (int i = idx + 1; i < *n; ++i)
s[i - 1] = s[i];
*n -= 1;
s[*n] = NULL;
if (sr->label)
free(sr->label);
return sr;
}
/************************* NVS Slicing Implementation **************************/
typedef struct {
float exp; // exponential weight. mov. avg for weight calc
int rb; // number of RBs this slice has been scheduled in last round
float eff; // effective rate for rate slices
float beta_eff; // averaging coeff so we average over roughly one second
int active; // activity state for rate slices
} _nvs_int_t;
int _nvs_nr_admission_control(const nr_slice_info_t *si,
const nvs_nr_slice_param_t *p,
int idx)
{
if (p->type != NVS_RATE && p->type != NVS_RES)
RET_FAIL(-1, "%s(): invalid slice type %d\n", __func__, p->type);
if (p->type == NVS_RATE && p->Mbps_reserved > p->Mbps_reference)
RET_FAIL(-1,
"%s(): a rate slice cannot reserve more than the reference rate\n",
__func__);
if (p->type == NVS_RES && p->pct_reserved > 1.0f)
RET_FAIL(-1, "%s(): cannot reserve more than 1.0\n", __func__);
float sum_req = 0.0f;
for (int i = 0; i < si->num; ++i) {
const nvs_nr_slice_param_t *sp = i == idx ? p : si->s[i]->algo_data;
if (sp->type == NVS_RATE) {
sum_req += sp->Mbps_reserved / sp->Mbps_reference;
} else {
sum_req += sp->pct_reserved;
}
LOG_D(NR_MAC, "slice idx %d, sum_req %.2f\n", i, sum_req);
}
if (idx < 0) { /* not an existing slice */
if (p->type == NVS_RATE)
sum_req += p->Mbps_reserved / p->Mbps_reference;
else
sum_req += p->pct_reserved;
}
LOG_D(NR_MAC, "slice idx %u, pct_reserved %.2f, sum_req %.2f\n", idx, p->pct_reserved, sum_req);
if (sum_req > 1.0)
RET_FAIL(-3,
"%s(): admission control failed: sum of resources is %f > 1.0\n",
__func__, sum_req);
return 0;
}
int addmod_nvs_nr_slice_dl(nr_slice_info_t *si,
int id,
nssai_t nssai,
char *label,
void *algo,
void *slice_params_dl)
{
nvs_nr_slice_param_t *dl = slice_params_dl;
int index = _nr_exists_slice(si->num, si->s, id);
if (index < 0 && si->num >= MAX_NVS_SLICES)
RET_FAIL(-2, "%s(): cannot handle more than %d slices\n", __func__, MAX_NVS_SLICES);
if (index < 0 && !dl)
RET_FAIL(-100, "%s(): no parameters for new slice %d, aborting\n", __func__, id);
if (dl) {
int rc = _nvs_nr_admission_control(si, dl, index);
if (rc < 0)
return rc;
}
nr_slice_t *s = NULL;
if (index >= 0) {
s = si->s[index];
if (label) {
if (s->label) free(s->label);
s->label = label;
}
if (algo && s->dl_algo.run != ((nr_dl_sched_algo_t*)algo)->run) {
s->dl_algo.unset(&s->dl_algo.data);
s->dl_algo = *(nr_dl_sched_algo_t*) algo;
if (!s->dl_algo.data)
s->dl_algo.data = s->dl_algo.setup();
}
if (dl) {
free(s->algo_data);
s->algo_data = dl;
} else { /* we have no parameters: we are done */
return index;
}
s->nssai = nssai;
} else {
if (!algo)
RET_FAIL(-14, "%s(): no scheduler algorithm provided\n", __func__);
s = _nr_add_slice(&si->num, si->s);
if (!s)
RET_FAIL(-4, "%s(): cannot allocate memory for slice\n", __func__);
s->int_data = malloc(sizeof(_nvs_int_t));
if (!s->int_data)
RET_FAIL(-5, "%s(): cannot allocate memory for slice internal data\n", __func__);
s->id = id;
s->label = label;
s->dl_algo = *(nr_dl_sched_algo_t*) algo;
if (!s->dl_algo.data)
s->dl_algo.data = s->dl_algo.setup();
s->algo_data = dl;
s->nssai = nssai;
}
_nvs_int_t *nvs_p = s->int_data;
/* reset all slice-internal parameters */
nvs_p->rb = 0;
nvs_p->active = 0;
if (dl->type == NVS_RATE) {
nvs_p->exp = dl->Mbps_reserved / dl->Mbps_reference;
nvs_p->eff = dl->Mbps_reference;
} else {
nvs_p->exp = dl->pct_reserved;
nvs_p->eff = 0; // not used
}
// scale beta so we (roughly) average the eff rate over 1s
nvs_p->beta_eff = BETA / nvs_p->exp;
return index < 0 ? si->num - 1 : index;
}
int remove_nvs_nr_slice_dl(nr_slice_info_t *si, uint8_t slice_idx)
{
if (slice_idx == 0 && si->num <= 1)
return 0;
UE_iterator(si->s[slice_idx]->UE_list, rm_ue) {
nr_slicing_remove_UE(si, rm_ue, slice_idx);
remove_nr_list(&rm_ue->dl_id, si->s[slice_idx]->id);
LOG_D(NR_MAC, "%s(), move UE rnti 0x%04x in slice ID %d idx %d to slice ID %d idx %d\n",
__func__, rm_ue->rnti, si->s[slice_idx]->id, slice_idx, si->s[0]->id, 0);
for (int i = 0; i < MAX_MOBILES_PER_GNB; i++) {
if (si->s[0]->UE_list[i] == NULL) {
si->s[0]->UE_list[i] = rm_ue;
break;
}
}
}
nr_slice_t *sr = _nr_remove_slice(&si->num, si->s, slice_idx);
if (!sr)
return 0;
free(sr->algo_data);
free(sr->int_data);
sr->dl_algo.unset(&sr->dl_algo.data);
free(sr);
return 1;
}
static void nr_store_dlsch_buffer(module_id_t module_id, frame_t frame, sub_frame_t slot)
{
nr_slice_info_t *si = RC.nrmac[module_id]->pre_processor_dl.slices;
for (int s = 0; s < si->num; s++) {
UE_iterator (si->s[s]->UE_list, UE) {
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
sched_ctrl->sliceInfo[s].num_total_bytes = 0;
sched_ctrl->sliceInfo[s].dl_pdus_total = 0;
NR_List_Iterator(&UE->UE_sched_ctrl.sliceInfo[s].lcid, lcidP)
{
const int lcid = *lcidP;
const uint16_t rnti = UE->rnti;
LOG_D(NR_MAC, "In %s: UE %x: LCID %d\n", __FUNCTION__, rnti, lcid);
if (lcid == DL_SCH_LCID_DTCH && nr_timer_is_active(&sched_ctrl->transm_interrupt)) {
continue;
}
start_meas(&RC.nrmac[module_id]->rlc_status_ind);
sched_ctrl->rlc_status[lcid] =
mac_rlc_status_ind(module_id, rnti, module_id, frame, slot, ENB_FLAG_YES, MBMS_FLAG_NO, lcid, 0, 0);
stop_meas(&RC.nrmac[module_id]->rlc_status_ind);
if (sched_ctrl->rlc_status[lcid].bytes_in_buffer == 0)
continue;
sched_ctrl->sliceInfo[s].dl_pdus_total += sched_ctrl->rlc_status[lcid].pdus_in_buffer;
sched_ctrl->sliceInfo[s].num_total_bytes += sched_ctrl->rlc_status[lcid].bytes_in_buffer;
LOG_D(MAC,
"[gNB %d][%4d.%2d] %s%d->DLSCH, RLC status for UE %d, slice %d: %d bytes in buffer, total DL buffer size = %d bytes, "
"%d total PDU bytes, %s TA command\n",
module_id,
frame,
slot,
lcid < 4 ? "DCCH" : "DTCH",
lcid,
UE->rnti,
s,
sched_ctrl->rlc_status[lcid].bytes_in_buffer,
sched_ctrl->sliceInfo[s].num_total_bytes,
sched_ctrl->sliceInfo[s].dl_pdus_total,
sched_ctrl->ta_apply ? "send" : "do not send");
}
}
}
}
void nvs_nr_dl(module_id_t mod_id,
frame_t frame,
sub_frame_t slot)
{
NR_UEs_t *UE_info = &RC.nrmac[mod_id]->UE_info;
if (UE_info->list[0] == NULL)
return;
NR_ServingCellConfigCommon_t *scc = RC.nrmac[mod_id]->common_channels[0].ServingCellConfigCommon;
int bw = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth;
/* Retrieve amount of data to send */
nr_store_dlsch_buffer(mod_id, frame, slot);
int average_agg_level = 4; // TODO find a better estimation
int max_sched_ues = bw / (average_agg_level * NR_NB_REG_PER_CCE);
// FAPI cannot handle more than MAX_DCI_CORESET DCIs
max_sched_ues = min(max_sched_ues, MAX_DCI_CORESET);
nr_slice_info_t *si = RC.nrmac[mod_id]->pre_processor_dl.slices;
int bytes_last_round[MAX_NVS_SLICES] = {0};
for (int s_idx = 0; s_idx < si->num; ++s_idx) {
UE_iterator (si->s[s_idx]->UE_list, UE) {
const NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
bytes_last_round[s_idx] += UE->mac_stats.dl.current_bytes;
/* if UE has data or retransmission, mark respective slice as active */
const int retx_pid = sched_ctrl->retrans_dl_harq.head;
const int retx_slice = sched_ctrl->harq_slice_map[retx_pid];
const bool active = sched_ctrl->sliceInfo[s_idx].num_total_bytes > 0 || retx_slice == s_idx;
((_nvs_int_t *)si->s[s_idx]->int_data)->active |= active;
}
}
float maxw = 0.0f;
int maxidx = -1;
const int bwpSize = bw; // we assume for now all UEs in the same BWP that occupies entire carrier BW
for (int i = 0; i < si->num; ++i) {
nr_slice_t *s = si->s[i];
nvs_nr_slice_param_t *p = s->algo_data;
_nvs_int_t *ip = s->int_data;
float w = 0.0f;
if (p->type == NVS_RATE) {
/* if this slice has been marked as inactive, disable to prevent that
* it's exp rate is uselessly driven down */
if (!ip->active)
continue;
float inst = 0.0f;
if (ip->rb > 0) { /* it was scheduled last round */
/* inst rate: B in last round * 8(bit) / 1000000 (Mbps) * 1000 (1ms) */
inst = (float) bytes_last_round[i] * 8 / 1000;
ip->eff = (1.0f - ip->beta_eff) * ip->eff + ip->beta_eff * inst;
//LOG_W(NR_MAC, "i %d slice %d ip->rb %d inst %f ip->eff %f\n", i, s->id, ip->rb, inst, ip->eff);
ip->rb = 0;
}
ip->exp = (1 - BETA) * ip->exp + BETA * inst;
const float rsv = p->Mbps_reserved * min(1.0f, ip->eff / p->Mbps_reference);
w = rsv / ip->exp;
} else {
float inst = (float)ip->rb / bwpSize;
ip->exp = (1.0f - BETA) * ip->exp + BETA * inst;
w = p->pct_reserved / ip->exp;
}
//LOG_I(NR_MAC, "i %d slice %d type %d ip->exp %f w %f\n", i, s->id, p->type, ip->exp, w);
ip->rb = 0;
if (w > maxw + 0.001f) {
maxw = w;
maxidx = i;
}
}
if (maxidx < 0)
return;
((_nvs_int_t *)si->s[maxidx]->int_data)->rb = bwpSize;
UE_iterator (si->s[maxidx]->UE_list, UE) {
UE->UE_sched_ctrl.last_sched_slice = maxidx;
}
/* proportional fair scheduling algorithm */
int rb_rem = si->s[maxidx]->dl_algo.run(mod_id,
frame,
slot,
si->s[maxidx]->UE_list,
max_sched_ues,
bwpSize,
si->s[maxidx]->dl_algo.data);
LOG_D(NR_MAC, "%4d.%2d scheduled %d RBs for slice idx %d ID %d \n", frame, slot, bwpSize - rb_rem, maxidx, si->s[maxidx]->id);
if (rb_rem == bwpSize) // if no RBs have been used mark as inactive
((_nvs_int_t *)si->s[maxidx]->int_data)->active = 0;
}
void nvs_nr_destroy(nr_slice_info_t **si)
{
const int n_dl = (*si)->num;
(*si)->num = 0;
for (int i = 0; i < n_dl; ++i) {
nr_slice_t *s = (*si)->s[i];
if (s->label)
free(s->label);
free(s->algo_data);
free(s->int_data);
free(s);
}
free((*si)->s);
}
nr_pp_impl_param_dl_t nvs_nr_dl_init(module_id_t mod_id)
{
nr_slice_info_t *si = calloc(1, sizeof(nr_slice_info_t));
DevAssert(si);
si->num = 0;
si->s = calloc(MAX_NVS_SLICES, sizeof(*si->s));
DevAssert(si->s);
for (int i = 0; i < MAX_MOBILES_PER_GNB; ++i)
si->UE_assoc_slice[i] = -1;
/* insert default slice, all resources */
nvs_nr_slice_param_t *dlp = malloc(sizeof(nvs_nr_slice_param_t));
DevAssert(dlp);
dlp->type = NVS_RES;
// we reserved 5% resource for RRC connection while UE is connecting before created slice or
// PDU setup while UE is trying to connect after created slice
dlp->pct_reserved = 0.05f;
nr_dl_sched_algo_t *algo = &RC.nrmac[mod_id]->pre_processor_dl.dl_algo;
algo->data = NULL;
// default slice: sst = 1, sd = 0x000000, id = 999, label = default
nssai_t nssai = {.sst = 0, .sd = 0};
const int rc = addmod_nvs_nr_slice_dl(si, 99, nssai, strdup("default"), algo, dlp);
LOG_W(NR_MAC, "Add default DL slice id 99, label default, sst %d, sd %d, slice sched algo NVS_CAPACITY, pct_reserved %.2f, ue sched algo %s\n", nssai.sst, nssai.sd, dlp->pct_reserved, algo->name);
DevAssert(0 == rc);
nr_pp_impl_param_dl_t nvs;
nvs.algorithm = NVS_SLICING;
nvs.add_UE = nr_slicing_add_UE;
nvs.remove_UE = nr_slicing_remove_UE;
nvs.move_UE = nr_slicing_move_UE;
nvs.get_UE_slice_idx = nr_slicing_get_UE_slice_idx;
nvs.get_UE_idx = nr_slicing_get_UE_idx;
nvs.addmod_slice = addmod_nvs_nr_slice_dl;
nvs.remove_slice = remove_nvs_nr_slice_dl;
nvs.dl = nvs_nr_dl;
// current DL algo becomes default scheduler
nvs.dl_algo = *algo;
nvs.destroy = nvs_nr_destroy;
nvs.slices = si;
return nvs;
}

View File

@@ -0,0 +1,75 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*!
* \file nr_slicing.h
* \brief General NR slice definition and helper parameters
* \author Robert Schmidt
* \date 2021
* \email robert.schmidt@eurecom.fr
*/
#ifndef NR_SLICING_H__
#define NR_SLICING_H__
#include "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h"
typedef struct nr_slice_s {
/// Arbitrary ID
slice_id_t id;
/// Arbitrary label
char *label;
nr_dl_sched_algo_t dl_algo;
/// A specific algorithm's implementation parameters
void *algo_data;
/// Internal data that might be kept alongside a slice's params
void *int_data;
// list of users in this slice
int num_UEs;
NR_UE_info_t *UE_list[MAX_MOBILES_PER_GNB+1];
nssai_t nssai;
} nr_slice_t;
typedef struct nr_slice_info_s {
uint8_t num;
nr_slice_t **s;
uint8_t UE_assoc_slice[MAX_MOBILES_PER_GNB+1];
} nr_slice_info_t;
#define NVS_SLICING 20
/* arbitrary upper limit, increase if you want to instantiate more slices */
#define MAX_NVS_SLICES NR_MAX_NUM_SLICES
/* window for slice weight averaging -> 1s for fine granularity */
#define BETA 0.001f
typedef struct {
enum nvs_type {NVS_RATE, NVS_RES} type;
union {
struct { float Mbps_reserved; float Mbps_reference; };
struct { float pct_reserved; };
};
} nvs_nr_slice_param_t;
nr_pp_impl_param_dl_t nvs_nr_dl_init(module_id_t mod_id);
#endif /* NR_SLICING_H__ */

View File

@@ -0,0 +1,48 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*!
* \file nr_slicing_internal.h
* \brief Internal NR slice helper functions
* \author Robert Schmidt
* \date 2021
* \email robert.schmidt@eurecom.fr
*/
#ifndef NR_SLICING_INTERNAL_H__
#define NR_SLICING_INTERNAL_H__
#include "nr_slicing.h"
void nr_slicing_add_UE(nr_slice_info_t *si, NR_UE_info_t *new_ue);
void nr_slicing_remove_UE(nr_slice_info_t *si, NR_UE_info_t* rm_ue, int idx);
void nr_slicing_move_UE(nr_slice_info_t *si, NR_UE_info_t* assoc_ue, int old_idx, int new_idx);
int nr_slicing_get_UE_slice_idx(nr_slice_info_t *si, rnti_t rnti);
int nr_slicing_get_UE_idx(nr_slice_t *si, rnti_t rnti);
nr_slice_t *_nr_add_slice(uint8_t *n, nr_slice_t **s);
nr_slice_t *_nr_remove_slice(uint8_t *n, nr_slice_t **s, int idx);
#endif /* NR_SLICING_INTERNAL_H__ */

View File

@@ -748,7 +748,7 @@ static int ngap_gNB_handle_initial_context_request(sctp_assoc_t assoc_id, uint32
msg->guami.amf_set_id = BIT_STRING_to_uint16(&ie->value.choice.GUAMI.aMFSetID);
msg->guami.amf_pointer = BIT_STRING_to_uint8(&ie->value.choice.GUAMI.aMFPointer);
NGAP_INFO("Initial Context Request, GUAMI mcc %d mnc %d mnc_len %d\n", msg->guami.mcc, msg->guami.mnc, msg->guami.mnc_len);
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_InitialContextSetupRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_PDUSessionResourceSetupListCxtReq, false);
if (ie != NULL) {
msg->nb_of_pdusessions = ie->value.choice.PDUSessionResourceSetupListCxtReq.list.count;
@@ -801,6 +801,7 @@ static int ngap_gNB_handle_initial_context_request(sctp_assoc_t assoc_id, uint32
} else {
msg->allowed_nssai[i].sd = 0xffffff;
}
NGAP_INFO("Initial Context Request, allowed_nssai ST %d SD 0x%06x\n", msg->allowed_nssai[i].sst, msg->allowed_nssai[i].sd);
}
/* id-UESecurityCapabilities */
@@ -981,6 +982,7 @@ static int ngap_gNB_handle_pdusession_setup_request(sctp_assoc_t assoc_id, uint3
} else {
msg->pdusession_setup_params[i].nssai.sd = 0xffffff;
}
NGAP_INFO("Handle Pdusession Setup Request, nssai ST %d SD 0x%06x\n", msg->pdusession_setup_params[i].nssai.sst, msg->pdusession_setup_params[i].nssai.sd);
allocCopy(&msg->pdusession_setup_params[i].nas_pdu, *item_p->pDUSessionNAS_PDU);
allocCopy(&msg->pdusession_setup_params[i].pdusessionTransfer, item_p->pDUSessionResourceSetupRequestTransfer);