Compare commits

...

21 Commits

Author SHA1 Message Date
Chieh-Chun Chen
d47ded101e Fix: avoid using id 0 for slice 2024-01-12 20:20:35 +01:00
Chieh-Chun Chen
f326c15d67 Add default slice sst 0 sd 0 for SRB
Note: assume default slice nssai is sst 0 and sd 0, which is reserved for SRB. it needs to be assigned to sched_ctrl before gnb created slice from RC control message in order to be able associated to the corresponding slice
2024-01-12 20:20:35 +01:00
Chieh-Chun Chen
88daeb39db Fix: remove released UE from slice before MAC removed 2024-01-12 20:20:35 +01:00
Chieh-Chun Chen
e2f35a2dcd Fix: add default slice while initializing slice 2024-01-12 20:20:35 +01:00
Chieh-Chun Chen
bdfea1d001 Fix RC ran func 2024-01-12 20:20:35 +01:00
Sakthivel Velumani
8954a140fd Remove UE from slice list(to be squashed) 2024-01-11 17:50:36 +01:00
Sakthivel Velumani
289c87d238 Add support for multiple slices per UE 2024-01-09 14:20:16 +01:00
Teodora
5bb186229e Fix warnings, read functions in SMs should return bool 2024-01-09 14:19:37 +01:00
Teodora
dbb08a9c85 Made it compile with FlexRIC release v2 2024-01-09 14:19:37 +01:00
Chieh-Chun Chen
3faaf888e7 Fix: correct the function pointer of preprocessor function for nr dlsim 2023-12-18 11:36:27 +01:00
Chieh-Chun Chen
3bc87342c6 Add README for nr slicing 2023-12-15 12:03:50 +01:00
Chieh-Chun Chen
3544c35c7a Fix: correct bytes_last_round to be the instantaneous transmission rate for each slice 2023-12-13 15:33:41 +01:00
Chieh-Chun Chen
b5177eb796 Fix: use min prb ratio as parameter for NVS algo 2023-12-13 14:43:30 +01:00
Chieh-Chun Chen
8eb350c5c8 Add new coming UEs to the corresponding slice if slice exists 2023-12-13 14:43:30 +01:00
Chieh-Chun Chen
d0b00aa53d Fix: save plmn info in RRCSetupComplete 2023-12-13 14:43:30 +01:00
Chieh-Chun Chen
477510cb53 Fix: get the correct nssai in ngap 2023-12-13 14:42:42 +01:00
Chieh-Chun Chen
3c8a95e767 Add slice in RC ran func 2023-12-13 14:42:42 +01:00
Chieh-Chun Chen
f02f3af722 Fixes for NR slicing
- correct return value of slice idx
- add/move UE in UE_list
- use rnti_t instead uint16_t
2023-12-13 14:42:42 +01:00
Chieh-Chun Chen
8f544acf20 Modify the return value of nr_pf_dl to int & remove the unused codes 2023-12-13 14:42:08 +01:00
Chieh-Chun Chen
09a2643518 Fix: correct the teidupf in GTP ran func 2023-12-13 14:42:08 +01:00
Robert Schmidt
2350bf56dd Add 5G preprocessor with NVS slicing 2023-12-13 14:41:53 +01:00
40 changed files with 1535 additions and 93 deletions

View File

@@ -300,8 +300,8 @@ set(E2AP_VERSION "E2AP_V2" CACHE STRING "E2AP version")
set_property(CACHE E2AP_VERSION PROPERTY STRINGS "E2AP_V1" "E2AP_V2" "E2AP_V3")
message(STATUS "Selected E2AP_VERSION: ${E2AP_VERSION}")
set(KPM_VERSION "KPM_V2" CACHE STRING "The KPM SM version to use")
set_property(CACHE KPM_VERSION PROPERTY STRINGS "KPM_V2" "KPM_V3")
set(KPM_VERSION "KPM_V2_03" CACHE STRING "The KPM SM version to use")
set_property(CACHE KPM_VERSION PROPERTY STRINGS "KPM_V2_03" "KPM_V3_00")
message(STATUS "Selected KPM Version: ${KPM_VERSION}")
@@ -1366,6 +1366,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

@@ -44,6 +44,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
typedef enum {
nr_FR1 = 0,

View File

@@ -274,7 +274,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

@@ -739,7 +739,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

@@ -33,7 +33,7 @@
static
const int mod_id = 0;
void read_gtp_sm(void * data)
bool read_gtp_sm(void * data)
{
assert(data != NULL);
@@ -66,7 +66,7 @@ void 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,6 +80,7 @@ void read_gtp_sm(void * data)
i++;
}
return (num_ues > 0);
}
void read_gtp_setup_sm(void* data)

View File

@@ -24,7 +24,7 @@
#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h"
void read_gtp_sm(void*);
bool read_gtp_sm(void*);
void read_gtp_setup_sm(void*);

View File

@@ -27,7 +27,7 @@ static
const int mod_id = 0;
void read_mac_sm(void* data)
bool read_mac_sm(void* data)
{
assert(data != NULL);
@@ -110,6 +110,8 @@ void read_mac_sm(void* data)
++i;
}
return (num_ues > 0);
}
void read_mac_setup_sm(void* data)

View File

@@ -28,7 +28,7 @@
#include "openair2/E2AP/flexric/src/util/time_now_us.h"
void read_mac_sm(void*);
bool read_mac_sm(void*);
void read_mac_setup_sm(void*);

View File

@@ -55,7 +55,7 @@ static
return act_rb;
}
void read_pdcp_sm(void* data)
bool read_pdcp_sm(void* data)
{
assert(data != NULL);
//assert(data->type == PDCP_STATS_V0);
@@ -123,6 +123,9 @@ void read_pdcp_sm(void* data)
++i;
}
}
return (act_rb > 0);
} else {
//assert(0!=0 && "Calling PDCP");
@@ -176,6 +179,8 @@ void read_pdcp_sm(void* data)
++i;
}
}
return (act_rb > 0);
}
}

View File

@@ -24,7 +24,7 @@
#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h"
void read_pdcp_sm(void*);
bool read_pdcp_sm(void*);
void read_pdcp_setup_sm(void* data);

View File

@@ -67,7 +67,7 @@ void active_avg_to_tx(NR_UEs_t* const UE_info)
}
}
void read_rlc_sm(void* data)
bool read_rlc_sm(void* data)
{
assert(data != NULL);
// assert(data->type == RLC_STATS_V0);
@@ -161,6 +161,8 @@ void read_rlc_sm(void* data)
++i;
}
}
return (act_rb > 0);
}
void read_rlc_setup_sm(void* data)

View File

@@ -24,7 +24,7 @@
#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h"
void read_rlc_sm(void*);
bool read_rlc_sm(void*);
void read_rlc_setup_sm(void* data);

View File

@@ -24,13 +24,15 @@
#include <assert.h>
#include <stdio.h>
void read_slice_sm(void* data)
bool read_slice_sm(void* data)
{
assert(data != NULL);
// assert(data->type == SLICE_STATS_V0);
slice_ind_data_t* slice = (slice_ind_data_t*)data;
fill_slice_ind_data(slice);
return true;
}
void read_slice_setup_sm(void* data)

View File

@@ -24,7 +24,7 @@
#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h"
void read_slice_sm(void*);
bool read_slice_sm(void*);
void read_slice_setup_sm(void* data);

View File

@@ -23,13 +23,15 @@
#include "../../flexric/test/rnd/fill_rnd_data_tc.h"
#include <assert.h>
void read_tc_sm(void* data)
bool read_tc_sm(void* data)
{
assert(data != NULL);
//assert(data->type == TC_STATS_V0);
tc_ind_data_t* tc = (tc_ind_data_t*)data;
fill_tc_ind_data(tc);
return true;
}
void read_tc_setup_sm(void* data)

View File

@@ -24,7 +24,7 @@
#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h"
void read_tc_sm(void*);
bool read_tc_sm(void*);
void read_tc_setup_sm(void* data);

View File

@@ -2,8 +2,10 @@ add_library(e2_ran_func_oran STATIC
ran_func_kpm.c
ran_func_rc.c
# For testing purposes
../../flexric/test/rnd/fill_rnd_data_kpm.c
../../flexric/test/rnd/fill_rnd_data_rc.c
../../flexric/test/rnd/fill_rnd_data_kpm.c
../../flexric/test/rnd/fill_rnd_data_rc.c
# For slice
../../../LAYER2/NR_MAC_gNB/slicing/nr_slicing.c
)
target_link_libraries(e2_ran_func_oran PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs)

View File

@@ -73,7 +73,7 @@ static size_t filter_ues_by_s_nssai_in_du_or_monolithic(test_cond_e const condit
}
matches->num_ues = i;
assert(matches->num_ues >= 1 && "The number of filtered UEs must be at least equal to 1");
return i;
}
@@ -613,9 +613,9 @@ kpm_ric_ind_hdr_format_1_t kpm_ind_hdr_frm_1(void)
kpm_ric_ind_hdr_format_1_t hdr_frm_1 = {0};
int64_t const t = time_now_us();
#if defined KPM_V2
#if defined KPM_V2_03
hdr_frm_1.collectStartTime = t/1000000; // seconds
#elif defined KPM_V3
#elif defined KPM_V3_00
hdr_frm_1.collectStartTime = t; // microseconds
#else
static_assert(0!=0, "Undefined KPM SM Version");
@@ -690,31 +690,33 @@ static void capture_sst_sd(test_info_lst_t* test, uint8_t *sst, uint32_t **sd)
// we made a mistake in the past: NSSAI is supposed to be an OCTET_STRING,
// but earlier version of the RAN function and the xApp used integer, so
// handle this gracefully by accepting integer as well
switch (*test->test_cond_value) {
switch (test->test_cond_value->type) {
case INTEGER_TEST_COND_VALUE:
AssertFatal(*test->int_value <= 0xff, "illegal SST %ld\n", *test->int_value);
*sst = *test->int_value;
printf("test cond value before = %ld\n", *test->test_cond_value->int_value);
AssertFatal(*test->test_cond_value->int_value <= 0xff, "illegal SST %ld\n", *test->test_cond_value->int_value);
*sst = *test->test_cond_value->int_value;
printf("test cond value after = %d\n", *sst);
*sd = NULL;
break;
case OCTET_STRING_TEST_COND_VALUE:
if (test->octet_string_value->len == 1) {
*sst = test->octet_string_value->buf[0];
if (test->test_cond_value->octet_string_value->len == 1) {
*sst = test->test_cond_value->octet_string_value->buf[0];
*sd = NULL;
} else {
DevAssert(test->octet_string_value->len == 4);
uint8_t *buf = test->octet_string_value->buf;
DevAssert(test->test_cond_value->octet_string_value->len == 4);
uint8_t *buf = test->test_cond_value->octet_string_value->buf;
*sst = buf[0];
*sd = malloc(**sd);
**sd = buf[1] << 16 | buf[2] << 8 | buf[3];
}
break;
default:
AssertFatal(false, "test condition value %d impossible\n", *test->test_cond_value);
AssertFatal(false, "test condition value %d impossible\n", test->test_cond_value->type);
break;
}
}
void read_kpm_sm(void* data)
bool read_kpm_sm(void* data)
{
assert(data != NULL);
// assert(data->type == KPM_STATS_V3_0);
@@ -768,7 +770,7 @@ void read_kpm_sm(void* data)
case CQI_TEST_COND_TYPE: {
// This is a bad idea. Done only to check FlexRIC xAPP
printf("CQI not implemented!. Randomly filling the data \n");
// printf("CQI not implemented!. Randomly filling the data \n");
goto rnd_data_label;
break;
}
@@ -786,7 +788,7 @@ void read_kpm_sm(void* data)
case S_NSSAI_TEST_COND_TYPE: {
assert(frm_4->matching_cond_lst[i].test_info_lst.S_NSSAI == TRUE_TEST_COND_TYPE && "Must be true");
assert(frm_4->matching_cond_lst[i].test_info_lst.test_cond != NULL && "Even though is optional..");
assert(frm_4->matching_cond_lst[i].test_info_lst.int_value != NULL && "Even though is optional..");
assert(frm_4->matching_cond_lst[i].test_info_lst.test_cond_value != NULL && "Even though is optional..");
test_cond_e const test_cond = *frm_4->matching_cond_lst[i].test_info_lst.test_cond;
uint8_t sst = 0;
@@ -796,14 +798,17 @@ void read_kpm_sm(void* data)
if (NODE_IS_DU(RC.nrrrc[0]->node_type)) {
matched_ues_mac_t matched_ues = {0};
matched_ues.num_ues = filter_ues_by_s_nssai_in_du_or_monolithic(test_cond, sst, sd, &matched_ues);
if (matched_ues.num_ues == 0) return false;
kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_du(&matched_ues, &frm_4->action_def_format_1);
} else if (NODE_IS_CU(RC.nrrrc[0]->node_type)) {
matched_ues_rrc_t matched_ues = {0};
matched_ues.num_ues = filter_ues_by_s_nssai_in_cu(test_cond, sst, sd, &matched_ues);
if (matched_ues.num_ues == 0) return false;
kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_cu(&matched_ues, &frm_4->action_def_format_1);
} else if (NODE_IS_MONOLITHIC(RC.nrrrc[0]->node_type)) {
matched_ues_mac_t matched_ues = {0};
matched_ues.num_ues = filter_ues_by_s_nssai_in_du_or_monolithic(test_cond, sst, sd, &matched_ues);
if (matched_ues.num_ues == 0) return false;
kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_monolithic(&matched_ues, &frm_4->action_def_format_1);
} else {
assert(false && "NG-RAN Type not implemented");
@@ -823,12 +828,17 @@ void read_kpm_sm(void* data)
default: {
rnd_data_label:
return false;
// Only for testing new xApps, but not existing ones in FlexRIC
kpm->ind.hdr = fill_rnd_kpm_ind_hdr();
kpm->ind.msg = fill_rnd_kpm_ind_msg();
printf("[E2 Agent]: Filling random KPM Indication Message\n");
break;
}
}
return true;
}
void read_kpm_setup_sm(void* e2ap)

View File

@@ -24,7 +24,7 @@
#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h"
void read_kpm_sm(void*);
bool read_kpm_sm(void*);
void read_kpm_setup_sm(void*);

View File

@@ -24,13 +24,19 @@
#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 "common/ran_context.h"
#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"
#include <assert.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void read_rc_sm(void* data)
bool read_rc_sm(void* data)
{
assert(data != NULL);
// assert(data->type == RAN_CTRL_STATS_V1_03);
@@ -45,12 +51,278 @@ void read_rc_setup_sm(void* data)
rc->ran_func_def = fill_rc_ran_func_def();
}
static int add_mod_dl_slice(int mod_id,
slice_algorithm_e current_algo,
int id,
uint8_t sst,
uint32_t sd,
char* label,
int64_t pct_reserved)
{
nr_pp_impl_param_dl_t *dl = &RC.nrmac[mod_id]->pre_processor_dl;
void *params = NULL;
char *slice_algo = NULL;
if (current_algo == NVS_SLICING) {
params = malloc(sizeof(nvs_nr_slice_param_t));
if (!params) return -1;
// use SLICE_SM_NVS_V0_CAPACITY by default
slice_algo = strdup("NVS_CAPACITY");
((nvs_nr_slice_param_t *)params)->type = NVS_RES;
float remain_pct = 1-((nvs_nr_slice_param_t *)dl->slices->s[0]->algo_data)->pct_reserved;
((nvs_nr_slice_param_t *)params)->pct_reserved = 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);
nssai_t nssai = {.sst = sst, .sd = sd};
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 int find_dl_slice(nr_slice_info_t *si, uint32_t id)
{
for (int i = 0; i < si->num; ++i) {
if (si->s[i]->id == id)
return i;
}
return -1;
}
static char* copy_bytearr_to_str(const byte_array_t* ba)
{
if (ba->len < 1)
return NULL;
char* str = calloc(ba->len + 1, sizeof(char));
assert(str != NULL && "memory exhausted\n");
memcpy(str, ba->buf, ba->len);
str[ba->len] = '\0';
return str;
}
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;
}
}
static 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 = copy_bytearr_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 = copy_bytearr_to_str(&SST->ran_param_val.flag_false->octet_str_ran);
uint8_t RC_sst = atoi(rc_sst_str);
char* rc_sd_str = copy_bytearr_to_str(&SD->ran_param_val.flag_false->octet_str_ran);
uint32_t RC_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_sst, RC_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 nvs_cap = Min_PRB_Policy_Ratio->ran_param_val.flag_false->int_ran;
LOG_I(NR_MAC, "configure slice %ld, label %s, Min_PRB_Policy_Ratio %ld\n", i, label_nssai, nvs_cap);
// TODO: could be extended to support max prb ratio and dedicated prb ratio in the MAC scheduling algorithm
//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);
//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 /////
const int rc = add_mod_dl_slice(mod_id, current_algo, i+1, RC_sst, RC_sd, label_nssai, (float)nvs_cap);
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;
}
/// 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 matched_ue = 0;
long lcid = 0;
for (int l = 0; l < sched_ctrl->dl_lc_num; ++l) {
lcid = sched_ctrl->dl_lc_ids[l];
LOG_D(NR_MAC, "l %d, lcid %ld, sst %d, sd %d\n", l, lcid, sched_ctrl->dl_lc_nssai[lcid].sst, sched_ctrl->dl_lc_nssai[lcid].sd);
if (nssai_matches(sched_ctrl->dl_lc_nssai[lcid], RC_sst, &RC_sd)) {
matched_ue = 1; // assume UE only associates to one slice
break;
}
}
if (!matched_ue)
continue;
// TODO: need to fix nssai info in RRCSetupComplete
rrc_gNB_ue_context_t* rrc_ue_context_list = rrc_gNB_get_ue_context_by_rnti_any_du(RC.nrrrc[mod_id], rnti);
uint16_t UE_mcc = rrc_ue_context_list->ue_context.ue_guami.mcc;
uint16_t UE_mnc = rrc_ue_context_list->ue_context.ue_guami.mnc;
uint8_t UE_sst = sched_ctrl->dl_lc_nssai[lcid].sst;
uint32_t UE_sd = sched_ctrl->dl_lc_nssai[lcid].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_sst, RC_sd);
if (UE_mcc == RC_mcc && UE_mnc == RC_mnc && UE_sst == RC_sst && UE_sd == RC_sd) {
dl->add_UE(dl->slices, UE);
} else {
LOG_W(NR_MAC, "cannot find specified PLMN (mcc %d mnc %d) NSSAI (sst %d sd %d) from the existing UE PLMN (mcc %d mnc %d) NSSAI (sst %d sd %d) \n",
RC_mcc, RC_mnc, RC_sst, RC_sd, UE_mcc, UE_mnc, UE_sst, UE_sd);
}
}
}
pthread_mutex_unlock(&nrmac->UE_info.mutex);
LOG_D(NR_MAC, "All slices add/mod successfully!\n");
return true;
}
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);
if(ctrl->hdr.format == FORMAT_1_E2SM_RC_CTRL_HDR){
if(ctrl->hdr.frmt_1.ric_style_type == 1 && ctrl->hdr.frmt_1.ctrl_act_id == 2){
printf("QoS flow mapping configuration \n");
@@ -63,11 +335,11 @@ sm_ag_if_ans_t write_ctrl_rc_sm(void const* data)
} else if(rp[i].ran_param_id == 2){
assert(rp[i].ran_param_val.type == LIST_RAN_PARAMETER_VAL_TYPE);
printf("List of QoS Flows to be modified \n");
for(size_t j = 0; j < ctrl->msg.frmt_1.ran_param[i].ran_param_val.lst->sz_lst_ran_param; ++j){
for(size_t j = 0; j < ctrl->msg.frmt_1.ran_param[i].ran_param_val.lst->sz_lst_ran_param; ++j){
lst_ran_param_t const* lrp = rp[i].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[j].ran_param_id == 3);
// check src/sm/rc_sm/enc/rc_enc_asn.c:1085 and src/sm/rc_sm/enc/rc_enc_asn.c:984
// assert(lrp[j].ran_param_id == 3);
assert(lrp[j].ran_param_struct.ran_param_struct[0].ran_param_id == 4) ;
assert(lrp[j].ran_param_struct.ran_param_struct[0].ran_param_val.type == ELEMENT_KEY_FLAG_TRUE_RAN_PARAMETER_VAL_TYPE);
@@ -80,7 +352,23 @@ sm_ag_if_ans_t write_ctrl_rc_sm(void const* data)
assert(dir == 0 || dir == 1);
printf("qfi = %ld dir %ld \n", qfi, dir);
}
}
}
}
} 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");
}
}
}
@@ -90,8 +378,7 @@ sm_ag_if_ans_t write_ctrl_rc_sm(void const* data)
return ans;
}
static
void* emulate_rrc_msg(void* ptr)
static void* emulate_rrc_msg(void* ptr)
{
uint32_t* ric_id = (uint32_t*)ptr;
for(size_t i = 0; i < 5; ++i){
@@ -107,8 +394,7 @@ void* emulate_rrc_msg(void* ptr)
return NULL;
}
static
pthread_t t_ran_ctrl;
static pthread_t t_ran_ctrl;
sm_ag_if_ans_t write_subs_rc_sm(void const* src)
{

View File

@@ -24,7 +24,7 @@
#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h"
void read_rc_sm(void *);
bool read_rc_sm(void *);
void read_rc_setup_sm(void* data);

View File

@@ -0,0 +1,28 @@
#ifndef OPENAIRINTERFACE_RC_CTRL_SERVICE_STYLE_2_H
#define OPENAIRINTERFACE_RC_CTRL_SERVICE_STYLE_2_H
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

@@ -22,6 +22,7 @@
#include "read_setup_ran.h"
#include "../../E2AP/flexric/src/lib/e2ap/e2ap_node_component_config_add_wrapper.h"
#include "../../E2AP/flexric/test/rnd/fill_rnd_data_e2_setup_req.h"
#include "../../../openair2/GNB_APP/gnb_config.h"
#include <assert.h>
#include <stdlib.h>
@@ -31,7 +32,60 @@ void read_setup_ran(void* data)
#ifdef E2AP_V1
#elif defined(E2AP_V2) || defined(E2AP_V3)
*((e2ap_node_component_config_add_t*)data) = fill_e2ap_node_component_config_add();
arr_node_component_config_add_t* dst = (arr_node_component_config_add_t*)data;
// Value assigned by CMakeLists.txt
const ngran_node_t node_type = get_node_type();
if(node_type == ngran_gNB){
dst->len_cca = 1;
dst->cca = calloc(1, sizeof(e2ap_node_component_config_add_t));
assert(dst->cca != NULL);
// NGAP
dst->cca[0] = fill_ngap_e2ap_node_component_config_add();
} else if(node_type == ngran_gNB_CU){
dst->len_cca = 2;
dst->cca = calloc(2, sizeof(e2ap_node_component_config_add_t));
assert(dst->cca != NULL);
// NGAP
dst->cca[0] = fill_ngap_e2ap_node_component_config_add();
// F1AP
dst->cca[1] = fill_f1ap_e2ap_node_component_config_add();
} else if(node_type == ngran_gNB_DU){
dst->len_cca = 1;
dst->cca = calloc(1, sizeof(e2ap_node_component_config_add_t));
assert(dst->cca != NULL);
// F1AP
dst->cca[0] = fill_f1ap_e2ap_node_component_config_add();
} else if(node_type == ngran_gNB_CUCP){
dst->len_cca = 3;
dst->cca = calloc(3, sizeof(e2ap_node_component_config_add_t));
assert(dst->cca != NULL);
// NGAP
dst->cca[0] = fill_ngap_e2ap_node_component_config_add();
// F1AP
dst->cca[1] = fill_f1ap_e2ap_node_component_config_add();
// E1AP
dst->cca[2] = fill_e1ap_e2ap_node_component_config_add();
} else if(node_type == ngran_gNB_CUUP){
dst->len_cca = 3;
dst->cca = calloc(3, sizeof(e2ap_node_component_config_add_t));
assert(dst->cca != NULL);
// NGAP
dst->cca[0] = fill_ngap_e2ap_node_component_config_add();
// F1AP
dst->cca[1] = fill_f1ap_e2ap_node_component_config_add();
// E1AP
dst->cca[2] = fill_e1ap_e2ap_node_component_config_add();
} else if(node_type == ngran_eNB){
dst->len_cca = 1;
dst->cca = calloc(1, sizeof(e2ap_node_component_config_add_t));
assert(dst->cca != NULL);
// S1AP
dst->cca[0] = fill_s1ap_e2ap_node_component_config_add();
} else {
assert(0 != 0 && "Not implemented");
}
#else
static_assert(0!=0, "Unknown E2AP version");
#endif

View File

@@ -89,7 +89,7 @@ git submodule update
```bash
git clone https://gitlab.eurecom.fr/mosaic5g/flexric flexric
cd flexric/
git checkout 93961d2480b2c7a5721e03435cb34cf9446d1ad1
git checkout 1f04cc558ebc8da9de6a620762bc02f5db4ecb4a
```
### 2.2.2 Build FlexRIC

View File

@@ -505,7 +505,7 @@ void nr_mac_config_scc(gNB_MAC_INST *nrmac, NR_ServingCellConfigCommon_t *scc, c
}
if (get_softmodem_params()->phy_test) {
nrmac->pre_processor_dl = nr_preprocessor_phytest;
nrmac->pre_processor_dl.dl = nr_preprocessor_phytest;
nrmac->pre_processor_ul = nr_ul_preprocessor_phytest;
} else {
nrmac->pre_processor_dl = nr_init_fr1_dlsch_preprocessor(0);

View File

@@ -320,8 +320,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 */
sched_ctrl->last_sched_slice = 0;
sched_ctrl->sliceInfo[sched_ctrl->last_sched_slice].num_total_bytes = 0;
sched_ctrl->sliceInfo[sched_ctrl->last_sched_slice].dl_pdus_total = 0;
reset_nr_list(&sched_ctrl->sliceInfo[sched_ctrl->last_sched_slice].lcid);
/* loop over all activated logical channels */
// Note: DL_SCH_LCID_DCCH, DL_SCH_LCID_DCCH1, DL_SCH_LCID_DTCH
@@ -348,20 +351,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[sched_ctrl->last_sched_slice].dl_pdus_total += sched_ctrl->rlc_status[lcid].pdus_in_buffer;
sched_ctrl->sliceInfo[sched_ctrl->last_sched_slice].num_total_bytes += sched_ctrl->rlc_status[lcid].bytes_in_buffer;
add_nr_list(&sched_ctrl->sliceInfo[sched_ctrl->last_sched_slice].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[sched_ctrl->last_sched_slice].num_total_bytes,
sched_ctrl->sliceInfo[sched_ctrl->last_sched_slice].dl_pdus_total,
sched_ctrl->ta_apply ? "send" : "do not send");
}
}
}
@@ -582,13 +587,30 @@ 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,
uint16_t *rballoc_mask)
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,
uint16_t *rballoc_mask,
void *data)
{
gNB_MAC_INST *mac = RC.nrmac[module_id];
NR_ServingCellConfigCommon_t *scc=mac->common_channels[0].ServingCellConfigCommon;
@@ -649,7 +671,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 + 10) % 1024)
if (sched_ctrl->sliceInfo[sched_ctrl->last_sched_slice].num_total_bytes == 0 && frame != (sched_ctrl->ta_frame + 10) % 1024)
continue;
/* Calculate coeff */
@@ -796,7 +818,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,
@@ -813,7 +835,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)
{
@@ -864,16 +894,17 @@ static void nr_fr1_dlsch_preprocessor(module_id_t module_id, frame_t frame, sub_
int max_sched_ues = bw / (average_agg_level * NR_NB_REG_PER_CCE);
/* proportional fair scheduling algorithm */
pf_dl(module_id,
frame,
slot,
UE_info->list,
max_sched_ues,
n_rb_sched,
rballoc_mask);
RC.nrmac[module_id]->pre_processor_dl.dl_algo.run(module_id,
frame,
slot,
UE_info->list,
max_sched_ues,
n_rb_sched,
rballoc_mask,
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
@@ -898,7 +929,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,
@@ -916,7 +952,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;
@@ -1243,10 +1282,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 < sched_ctrl->dl_lc_num; ++i) {
const int lcid = sched_ctrl->dl_lc_ids[i];
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;
@@ -1298,6 +1338,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

@@ -57,6 +57,7 @@
#define DEBUG_gNB_SCHEDULER 1
#include "common/ran_context.h"
#include "NR_MAC_gNB/slicing/nr_slicing.h"
//#define DEBUG_DCI
@@ -1820,6 +1821,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
*/
@@ -1996,6 +2021,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);
}
@@ -2358,6 +2387,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++) {
@@ -2497,6 +2536,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++)
@@ -2977,6 +3024,21 @@ void prepare_initial_ul_rrc_message(gNB_MAC_INST *mac, NR_UE_info_t *UE)
UE->CellGroup = cellGroupConfig;
process_CellGroup(cellGroupConfig, UE);
/* Assign SRB1 to default slice */
const long lcid = 1;
nr_pp_impl_param_dl_t *dl = &mac->pre_processor_dl;
if (dl->slices) {
nssai_t *default_nssai = &dl->slices->s[0]->nssai;
UE->UE_sched_ctrl.dl_lc_nssai[lcid] = *default_nssai;
LOG_I(NR_MAC, "Setting NSSAI sst: %d, sd: %d for SRB: %ld\n", default_nssai->sst, default_nssai->sd, lcid);
dl->add_UE(dl->slices, UE);
} else {
nssai_t nssai = {.sst = 0, .sd = 0};
UE->UE_sched_ctrl.dl_lc_nssai[lcid] = nssai;
LOG_I(NR_MAC, "Setting NSSAI sst: %d, sd: %d for SRB: %ld\n", UE->UE_sched_ctrl.dl_lc_nssai[lcid].sst, UE->UE_sched_ctrl.dl_lc_nssai[lcid].sd, lcid);
}
/* activate SRB0 */
nr_rlc_activate_srb0(UE->rnti, UE, send_initial_ul_rrc_message);

View File

@@ -76,7 +76,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,
@@ -181,7 +181,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);
@@ -298,6 +298,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

@@ -26,6 +26,7 @@
#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h"
#include "openair2/RRC/NR/MESSAGES/asn1_msg.h"
#include "F1AP_CauseRadioNetwork.h"
#include "NR_MAC_gNB/slicing/nr_slicing.h"
#include "uper_decoder.h"
#include "uper_encoder.h"
@@ -237,8 +238,28 @@ static NR_CellGroupConfig_t *clone_CellGroupConfig(const NR_CellGroupConfig_t *o
return cloned;
}
static void set_nssaiConfig(const int drb_len, const f1ap_drb_to_be_setup_t *req_drbs, NR_UE_sched_ctrl_t *sched_ctrl)
static void set_nssaiConfig(const int srb_len,
const f1ap_srb_to_be_setup_t *req_srbs,
const int drb_len,
const f1ap_drb_to_be_setup_t *req_drbs,
NR_UE_sched_ctrl_t *sched_ctrl)
{
gNB_MAC_INST *mac = RC.nrmac[0];
for (int i = 0; i < srb_len; i++) {
const f1ap_srb_to_be_setup_t *srb = &req_srbs[i];
const long lcid = get_lcid_from_srbid(srb->srb_id);
/* consider first slice as default slice and assign it for SRBs */
nr_pp_impl_param_dl_t *dl = &mac->pre_processor_dl;
if (dl->slices) {
nssai_t *default_nssai = &dl->slices->s[0]->nssai;
sched_ctrl->dl_lc_nssai[lcid] = *default_nssai;
} else {
nssai_t nssai = {.sst = 0, .sd = 0};
sched_ctrl->dl_lc_nssai[lcid] = nssai;
}
LOG_I(NR_MAC, "Setting NSSAI sst: %d, sd: %d for SRB: %ld\n", sched_ctrl->dl_lc_nssai[lcid].sst, sched_ctrl->dl_lc_nssai[lcid].sd, srb->srb_id);
}
for (int i = 0; i < drb_len; i++) {
const f1ap_drb_to_be_setup_t *drb = &req_drbs[i];
@@ -361,8 +382,16 @@ void ue_context_setup_request(const f1ap_ue_context_setup_t *req)
set_QoSConfig(req, &UE->UE_sched_ctrl);
/* Set NSSAI config in MAC for each active DRB */
set_nssaiConfig(req->drbs_to_be_setup_length, req->drbs_to_be_setup, &UE->UE_sched_ctrl);
set_nssaiConfig(req->srbs_to_be_setup_length,
req->srbs_to_be_setup,
req->drbs_to_be_setup_length,
req->drbs_to_be_setup,
&UE->UE_sched_ctrl);
/* 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 */
@@ -464,7 +493,16 @@ void ue_context_modification_request(const f1ap_ue_context_modif_req_t *req)
set_QoSConfig(req, &UE->UE_sched_ctrl);
/* Set NSSAI config in MAC for each active DRB */
set_nssaiConfig(req->drbs_to_be_setup_length, req->drbs_to_be_setup, &UE->UE_sched_ctrl);
set_nssaiConfig(req->srbs_to_be_setup_length,
req->srbs_to_be_setup,
req->drbs_to_be_setup_length,
req->drbs_to_be_setup,
&UE->UE_sched_ctrl);
/* 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

@@ -254,7 +254,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

@@ -111,6 +111,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 {
RA_IDLE = 0,
Msg2 = 1,
@@ -541,6 +543,14 @@ typedef struct NR_QoS_config_s {
uint64_t priority;
} NR_QoS_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 {
@@ -641,6 +651,13 @@ typedef struct {
nr_srs_feedback_t srs_feedback;
nssai_t dl_lc_nssai[NR_MAX_NUM_LCID];
/// 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];
// Information about the QoS configuration for each LCID/DRB
NR_QoS_config_t qos_config[NR_MAX_NUM_LCID - 4][NR_MAX_NUM_QFI]; // 0 -CCCH and 1- 3 SRBs(0,1,2)
} NR_UE_sched_ctrl_t;
@@ -714,6 +731,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 {
@@ -728,6 +747,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, uint16_t *, 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);
@@ -735,6 +767,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;
@@ -833,7 +903,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,589 @@
/*
* 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 "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h"
#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h"
#include "nr_slicing.h"
#include "nr_slicing_internal.h"
#include "common/ran_context.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;
long lcid = 0;
reset_nr_list(&new_ue->dl_id);
for (int i = 0; i < si->num; ++i) {
reset_nr_list(&new_ue->UE_sched_ctrl.sliceInfo[i].lcid);
bool matched_ue = false;
for (int l = 0; l < sched_ctrl->dl_lc_num; ++l) {
lcid = sched_ctrl->dl_lc_ids[l];
LOG_D(NR_MAC, "l %d, lcid %ld, sst %d, sd %d\n", l, lcid, sched_ctrl->dl_lc_nssai[lcid].sst, sched_ctrl->dl_lc_nssai[lcid].sd);
LOG_D(NR_MAC, "slice %d, sst %d, sd %d\n", i, si->s[i]->nssai.sst, si->s[i]->nssai.sd);
if (nssai_matches(sched_ctrl->dl_lc_nssai[lcid], si->s[i]->nssai.sst, &si->s[i]->nssai.sd)) {
add_nr_list(&new_ue->UE_sched_ctrl.sliceInfo[i].lcid, lcid);
matched_ue = true;
}
}
if (matched_ue) {
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;
} 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);
}
LOG_W(NR_MAC, "Matched slice, 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_W(NR_MAC, "cannot find matched slice, do nothing for UE rnti 0x%04x\n", new_ue->rnti);
}
}
}
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 && sched_ctrl->rrc_processing_timer > 0) {
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)
{
gNB_MAC_INST *nrmac = RC.nrmac[mod_id];
NR_UEs_t *UE_info = &nrmac->UE_info;
if (UE_info->list[0] == NULL) /* no UEs at all -> don't bother */
return;
/* check if we are supposed to schedule something */
NR_ServingCellConfigCommon_t *scc = RC.nrmac[mod_id]->common_channels[0].ServingCellConfigCommon;
const int CC_id = 0;
/* Get bwpSize and TDAfrom the first UE */
/* This is temporary and it assumes all UEs have the same BWP and TDA*/
NR_UE_info_t *first_UE=UE_info->list[0];
NR_UE_sched_ctrl_t *sched_ctrl = &first_UE->UE_sched_ctrl;
NR_UE_DL_BWP_t *current_BWP = &first_UE->current_DL_BWP;
const int tda = get_dl_tda(RC.nrmac[mod_id], scc, slot);
int startSymbolIndex, nrOfSymbols;
const int coresetid = sched_ctrl->coreset->controlResourceSetId;
const struct NR_PDSCH_TimeDomainResourceAllocationList *tdaList = get_dl_tdalist(current_BWP, coresetid, sched_ctrl->search_space->searchSpaceType->present, NR_RNTI_C);
AssertFatal(tda < tdaList->list.count, "time_domain_allocation %d>=%d\n", tda, tdaList->list.count);
const int startSymbolAndLength = tdaList->list.array[tda]->startSymbolAndLength;
SLIV2SL(startSymbolAndLength, &startSymbolIndex, &nrOfSymbols);
const uint16_t bwpSize = current_BWP->BWPSize;
const uint16_t BWPStart = current_BWP->BWPStart;
const uint16_t slbitmap = SL_to_bitmap(startSymbolIndex, nrOfSymbols);
uint16_t *vrb_map = RC.nrmac[mod_id]->common_channels[CC_id].vrb_map;
uint16_t rballoc_mask[bwpSize];
int n_rb_sched = 0;
for (int i = 0; i < bwpSize; i++) {
// calculate mask: init with "NOT" vrb_map:
// if any RB in vrb_map is blocked (1), the current RBG will be 0
rballoc_mask[i] = (~vrb_map[i+BWPStart])&0x3fff; //bitwise not and 14 symbols
// if all the pdsch symbols are free
if ((rballoc_mask[i]&slbitmap) == slbitmap) {
n_rb_sched++;
}
}
/* Retrieve amount of data to send */
nr_store_dlsch_buffer(mod_id, frame, slot);
int bw = scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth;
int average_agg_level = 4; // TODO find a better estimation
int max_sched_ues = bw / (average_agg_level * NR_NB_REG_PER_CCE);
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;
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 = n_rb_sched;
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,
n_rb_sched,
rballoc_mask,
si->s[maxidx]->dl_algo.data);
LOG_D(NR_MAC, "%4d.%2d schedule %d RBs at slice idx %d ID %d \n", frame, slot, n_rb_sched - rb_rem, maxidx, si->s[maxidx]->id);
if (rb_rem == n_rb_sched) // 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

@@ -1553,6 +1553,12 @@ static int handle_rrcSetupComplete(const protocol_ctxt_t *const ctxt_pP,
}
}
// TODO: need to handle multiple PLMN in RRC Connection Setup Complete
UE->ue_guami.mcc = RC.nrrrc[0]->configuration.mcc[0];
UE->ue_guami.mnc = RC.nrrrc[0]->configuration.mnc[0];
UE->ue_guami.mnc_len = RC.nrrrc[0]->configuration.mnc_digit_length[0];
// LOG_W(NR_RRC, "mcc %d, mnc %d, mnc_len %d\n", UE->ue_guami.mcc, UE->ue_guami.mnc, UE->ue_guami.mnc_len);
rrc_gNB_process_RRCSetupComplete(ctxt_pP, ue_context_p, setup_complete->criticalExtensions.choice.rrcSetupComplete);
LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT " UE State = NR_RRC_CONNECTED \n", PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP));
return 0;

View File

@@ -209,6 +209,9 @@ rrc_gNB_send_NGAP_NAS_FIRST_REQ(
/* selected_plmn_identity: IE is 1-based, convert to 0-based (C array) */
int selected_plmn_identity = rrcSetupComplete->selectedPLMN_Identity - 1;
req->selected_plmn_identity = selected_plmn_identity;
req->ue_identity.guami.mcc = UE->ue_guami.mcc;
req->ue_identity.guami.mnc = UE->ue_guami.mnc;
req->ue_identity.guami.mnc_len = UE->ue_guami.mnc_len;
if (rrcSetupComplete->registeredAMF != NULL) {
NR_RegisteredAMF_t *r_amf = rrcSetupComplete->registeredAMF;

View File

@@ -784,10 +784,12 @@ static int ngap_gNB_handle_initial_context_request(sctp_assoc_t assoc_id, uint32
OCTET_STRING_TO_INT8(&allow_nssai_item_p->s_NSSAI.sST, msg->allowed_nssai[i].sst);
if(allow_nssai_item_p->s_NSSAI.sD != NULL) {
memcpy(&msg->allowed_nssai[i].sd, allow_nssai_item_p->s_NSSAI.sD, 3);
msg->allowed_nssai[i].sd = 0;
BUFFER_TO_INT24((uint8_t *)allow_nssai_item_p->s_NSSAI.sD->buf, msg->allowed_nssai[i].sd);
} 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 */
@@ -964,13 +966,11 @@ static int ngap_gNB_handle_pdusession_setup_request(sctp_assoc_t assoc_id, uint3
// S-NSSAI
OCTET_STRING_TO_INT8(&item_p->s_NSSAI.sST, msg->pdusession_setup_params[i].nssai.sst);
if (item_p->s_NSSAI.sD != NULL) {
uint8_t *sd_p = (uint8_t *)&msg->pdusession_setup_params[i].nssai.sd;
sd_p[0] = item_p->s_NSSAI.sD->buf[0];
sd_p[1] = item_p->s_NSSAI.sD->buf[1];
sd_p[2] = item_p->s_NSSAI.sD->buf[2];
BUFFER_TO_INT24((uint8_t *)item_p->s_NSSAI.sD->buf, msg->pdusession_setup_params[i].nssai.sd);
} 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);