Compare commits

...

26 Commits

Author SHA1 Message Date
Teodora
50d33e69f0 Change FlexRIC commit ID 2024-03-30 19:11:11 +01:00
Teodora
1db9ad510c Match (same ssT,any sD) in KPM for exporting data 2024-03-30 19:08:09 +01:00
Chieh-Chun Chen
f5162e263d Refactor ran_func_rc.c: move slicing functions to rc_ctrl_service_style_2.c 2024-03-21 16:59:53 +01:00
Chieh-Chun Chen
61e94b68ac Add cp_ba_to_str 2024-03-21 16:58:31 +01:00
Chieh-Chun Chen
383610ddc2 Add missing guami while handle initial context setup reuqest in RRC 2024-03-21 13:59:07 +01:00
Chieh-Chun Chen
2acb7f14d3 Revert "Fix: save plmn info in RRCSetupComplete"
This reverts commit fa8e88f66fa8778682a620c0301069aea83b596a.
2024-03-21 13:59:07 +01:00
Chieh-Chun Chen
ba461b841d Fix: only compile slice functions in RC ran func when define NGRAN_GNB_DU 2024-03-21 13:59:07 +01:00
Chieh-Chun Chen
fea1344c24 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-03-21 13:57:16 +01:00
Chieh-Chun Chen
3285245a08 Fix: enable association to multiple slices for one UE in RC RAN func 2024-03-21 13:57:16 +01:00
Sakthivel Velumani
da86353fa5 Add support for multiple slices per UE 2024-03-21 13:57:16 +01:00
Chieh-Chun Chen
4c8feea3c9 Add README for nr slicing 2024-03-21 13:57:16 +01:00
Chieh-Chun Chen
b5f5156889 Add new coming UEs to the corresponding slice if slice exists 2024-03-21 13:57:16 +01:00
Chieh-Chun Chen
df88353da7 Fix: save plmn info in RRCSetupComplete 2024-03-21 13:57:16 +01:00
Chieh-Chun Chen
7ee34580a7 Fix: get the correct nssai in ngap 2024-03-21 13:57:16 +01:00
Chieh-Chun Chen
f6662356b6 Add slice in RC ran func 2024-03-21 13:57:16 +01:00
Chieh-Chun Chen
6e52cc7b2e 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-03-21 13:52:00 +01:00
Chieh-Chun Chen
99e8ba4307 Modify the return value of nr_pf_dl to int & remove the unused codes 2024-03-21 13:52:00 +01:00
Chieh-Chun Chen
724b742280 Fix: correct the teidupf in GTP ran func 2024-03-21 13:52:00 +01:00
Robert Schmidt
bfe7d83881 Add 5G preprocessor with NVS slicing 2024-03-21 13:52:00 +01:00
Teodora
88f2ac7c57 E2AP README & FlexRIC commit ID update 2024-03-20 15:14:20 +01:00
Teodora
8a97251ae2 E2SM cmake update
- hardcoded data for E2 Setup Request; fill_rnd_data_e2_setup_req.c dependancy taken
    out
2024-03-20 15:14:20 +01:00
Teodora
fded7890e6 RAN Control cleanup
- use mutual E2SM UE ID file
  - write_ctrl_rc_sm function cleanup
2024-03-20 15:14:20 +01:00
Teodora
aa695207a7 RC Control RAN Function Definition implemented 2024-03-20 15:14:20 +01:00
Teodora
0837edad33 KPM cleanup
- ran_func_kpm.c - use function pointers based on the matching condition,
    and node type
  - ran_func_kpm_subs.h/c - for measurement collection within
    MAC/RLC/PDCP layers; for CU-UP node only PDCP included (otherwise,
we would get undefined reference to MAC/RLC)
  - use mutual E2SM UE ID file
2024-03-20 15:14:10 +01:00
Teodora
a894f52fda KPM RAN Function Definition implemented 2024-03-18 11:39:19 +01:00
Teodora
e606cfa2d3 Add mutual O-RAN E2SM UE ID filling info 2024-03-18 11:39:19 +01:00
33 changed files with 2931 additions and 911 deletions

View File

@@ -1365,6 +1365,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

@@ -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
#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

@@ -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

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

@@ -1,43 +1,51 @@
add_library(e2_ran_func_cuup STATIC
init_ran_func.c
read_setup_ran.c
../flexric/test/rnd/fill_rnd_data_e2_setup_req.c # this is not rnd data; it is used to fill E2 Node Component Configuration Addition List in E2 Setup Request message
CUSTOMIZED/ran_func_gtp.c # current implementation doesn't take split architecture into account, neither CU/DU nor CU-UP/CU-CP
CUSTOMIZED/ran_func_pdcp.c # current implementation doesn't take split architecture into account, neither CU/DU nor CU-UP/CU-CP
O-RAN/ran_e2sm_ue_id.c
O-RAN/ran_func_kpm.c
O-RAN/ran_func_kpm_subs.c
O-RAN/ran_func_rc.c
O-RAN/ran_func_rc_subs.c
CUSTOMIZED/ran_func_gtp.c # GTP SM not yet implemented in CU-UP
CUSTOMIZED/ran_func_pdcp.c
CUSTOMIZED/ran_func_tc.c # currently, not implemented; therefore, filling rnd data
../flexric/test/rnd/fill_rnd_data_tc.c
O-RAN/ran_func_kpm.c # this file should only contain PDCP-U/GTP; to be done in the future
../flexric/test/rnd/fill_rnd_data_kpm.c # this dependancy will be taken out once RAN Function Definition is implemented
O-RAN/ran_func_rc.c # this file should only contain PDCP-U/GTP; to be done in the future
O-RAN/ran_func_rc_subs.c
../flexric/test/rnd/fill_rnd_data_rc.c # this dependancy will be taken out once RAN Function Definition is implemented
)
target_link_libraries(e2_ran_func_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj 3gpp_derived_ie_obj)
target_link_libraries(e2_ran_func_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj 3gpp_derived_ie_obj sm_common_ie_obj)
target_compile_definitions(e2_ran_func_cuup PUBLIC ${E2AP_VERSION} ${KPM_VERSION} NGRAN_GNB_CUUP)
add_library(e2_ran_func_du_cucp_cuup STATIC
init_ran_func.c
read_setup_ran.c
../flexric/test/rnd/fill_rnd_data_e2_setup_req.c # this is not rnd data; it is used to fill E2 Node Component Configuration Addition List in E2 Setup Request message
CUSTOMIZED/ran_func_gtp.c # current implementation doesn't take split architecture into account, neither CU/DU nor CU-UP/CU-CP
CUSTOMIZED/ran_func_pdcp.c # current implementation doesn't take split architecture into account, neither CU/DU nor CU-UP/CU-CP
O-RAN/ran_func_kpm.c # this file should only contain RRC/PDCP-C; to be done in the future
# when nr-softmodem is divided in separate executables
../flexric/test/rnd/fill_rnd_data_kpm.c # this dependancy will be taken out once RAN Function Definition is implemented
O-RAN/ran_func_rc.c # this file should only contain RRC/PDCP-C; to be done in the future
# when nr-softmodem is divided in separate executables
O-RAN/ran_e2sm_ue_id.c
O-RAN/ran_func_kpm.c
O-RAN/ran_func_kpm_subs.c
O-RAN/ran_func_rc.c
O-RAN/ran_func_rc_subs.c
../flexric/test/rnd/fill_rnd_data_rc.c # this dependancy will be taken out once RAN Function Definition is implemented
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
CUSTOMIZED/ran_func_mac.c
CUSTOMIZED/ran_func_rlc.c
CUSTOMIZED/ran_func_slice.c
CUSTOMIZED/ran_func_tc.c
../flexric/test/rnd/fill_rnd_data_tc.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
)
target_link_libraries(e2_ran_func_du_cucp_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj 3gpp_derived_ie_obj e2sm_rc_ir_obj sm_common_ie_obj e2_agent)
target_compile_definitions(e2_ran_func_du_cucp_cuup PUBLIC ${E2AP_VERSION} ${KPM_VERSION} NGRAN_GNB_DU NGRAN_GNB_CUCP NGRAN_GNB_CUUP)
target_link_libraries(e2_ran_func_du_cucp_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj 3gpp_derived_ie_obj e2sm_rc_ir_obj sm_common_ie_obj)
target_compile_definitions(e2_ran_func_du_cucp_cuup PUBLIC ${E2AP_VERSION} ${KPM_VERSION} NGRAN_GNB_DU NGRAN_GNB_CUCP NGRAN_GNB_CUUP)
# Current implementation:
# - we only have one executable nr-softmodem for 4 node types: DU, CU, gNB-mono, CU-CP
# In the future, when nr-softmodem is separated, linking should be done as following:
# - DU: e2_ran_func_du
# - CU-CP : e2_ran_func_cucp
# - CU-UP: e2_ran_func_cuup (this is available at the moment, executable nr-cuup)
# - CU: e2_ran_func_cucp + e2_ran_func_cuup
# - gNB-mono: e2_ran_func_du + e2_ran_func_cucp + e2_ran_func_cuup

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

@@ -0,0 +1,143 @@
/*
* 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
*/
#include "ran_e2sm_ue_id.h"
ue_id_e2sm_t fill_e2sm_gnb_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, __attribute__((unused))const uint32_t rrc_ue_id, __attribute__((unused))const ue_id_t cucp_ue_id)
{
ue_id_e2sm_t ue_id = {0};
ue_id.type = GNB_UE_ID_E2SM;
// 6.2.3.16
// Mandatory
// AMF UE NGAP ID
ue_id.gnb.amf_ue_ngap_id = rrc_ue_context->amf_ue_ngap_id;
// Mandatory
//GUAMI 6.2.3.17
ue_id.gnb.guami.plmn_id = (e2sm_plmn_t) {
.mcc = rrc_ue_context->ue_guami.mcc,
.mnc = rrc_ue_context->ue_guami.mnc,
.mnc_digit_len = rrc_ue_context->ue_guami.mnc_len
};
ue_id.gnb.guami.amf_region_id = rrc_ue_context->ue_guami.amf_region_id;
ue_id.gnb.guami.amf_set_id = rrc_ue_context->ue_guami.amf_set_id;
ue_id.gnb.guami.amf_ptr = rrc_ue_context->ue_guami.amf_pointer;
// RAN UE ID
// Optional
// 6.2.3.25
// OCTET STRING (SIZE (8))
// Defined in TS 38.473 (F1AP)
// clause 9.2.2.1
// UE CONTEXT SETUP REQUEST
ue_id.gnb.ran_ue_id = calloc(1, sizeof(uint64_t));
assert(ue_id.gnb.ran_ue_id != NULL);
*ue_id.gnb.ran_ue_id = rrc_ue_context->rrc_ue_id;
return ue_id;
}
ue_id_e2sm_t fill_e2sm_cu_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id)
{
ue_id_e2sm_t ue_id = fill_e2sm_gnb_ue_id_data(rrc_ue_context, rrc_ue_id, cucp_ue_id);
// gNB-CU UE F1AP ID List
// C-ifCUDUseparated
ue_id.gnb.gnb_cu_ue_f1ap_lst_len = 1;
ue_id.gnb.gnb_cu_ue_f1ap_lst = calloc(ue_id.gnb.gnb_cu_ue_f1ap_lst_len, sizeof(uint32_t));
assert(ue_id.gnb.gnb_cu_ue_f1ap_lst != NULL && "Memory exhausted");
ue_id.gnb.gnb_cu_ue_f1ap_lst[0] = rrc_ue_context->rrc_ue_id;
return ue_id;
}
ue_id_e2sm_t fill_e2sm_du_ue_id_data(__attribute__((unused))const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, __attribute__((unused))const ue_id_t cucp_ue_id)
{
ue_id_e2sm_t ue_id = {0};
ue_id.type = GNB_DU_UE_ID_E2SM;
// 6.2.3.21
// gNB CU UE F1AP
// Mandatory
ue_id.gnb_du.gnb_cu_ue_f1ap = rrc_ue_id;
// 6.2.3.25
// RAN UE ID
// Optional
ue_id.gnb_du.ran_ue_id = calloc(1, sizeof(uint64_t));
assert(ue_id.gnb_du.ran_ue_id != NULL);
*ue_id.gnb_du.ran_ue_id = rrc_ue_id;
return ue_id;
}
ue_id_e2sm_t fill_e2sm_cucp_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id)
{
ue_id_e2sm_t ue_id = fill_e2sm_gnb_ue_id_data(rrc_ue_context, rrc_ue_id, cucp_ue_id);
//gNB-CU-CP UE E1AP ID List
//C-ifCPUPseparated
ue_id.gnb.gnb_cu_cp_ue_e1ap_lst_len = 1;
ue_id.gnb.gnb_cu_cp_ue_e1ap_lst = calloc(ue_id.gnb.gnb_cu_cp_ue_e1ap_lst_len, sizeof(uint32_t));
assert(ue_id.gnb.gnb_cu_cp_ue_e1ap_lst != NULL && "Memory exhausted");
ue_id.gnb.gnb_cu_cp_ue_e1ap_lst[0] = rrc_ue_context->rrc_ue_id;
return ue_id;
}
ue_id_e2sm_t fill_e2sm_cuup_ue_id_data(__attribute__((unused))const gNB_RRC_UE_t *rrc_ue_context, __attribute__((unused))const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id)
{
ue_id_e2sm_t ue_id = {0};
ue_id.type = GNB_CU_UP_UE_ID_E2SM;
// 6.2.3.20
// Mandatory
ue_id.gnb_cu_up.gnb_cu_cp_ue_e1ap = cucp_ue_id; // RAN UE NGAP ID = gNB UE NGAP ID = rrc_ue_id
// 6.2.3.25
// RAN UE ID
// Optional
ue_id.gnb_cu_up.ran_ue_id = calloc(1, sizeof(uint64_t));
assert(ue_id.gnb_cu_up.ran_ue_id != NULL);
*ue_id.gnb_cu_up.ran_ue_id = cucp_ue_id; // RAN UE NGAP ID = gNB UE NGAP ID = rrc_ue_id
return ue_id;
}
get_ue_id fill_ue_id_data[END_NGRAN_NODE_TYPE] =
{
NULL,
NULL,
fill_e2sm_gnb_ue_id_data,
NULL,
NULL,
fill_e2sm_cu_ue_id_data,
NULL,
fill_e2sm_du_ue_id_data,
NULL,
fill_e2sm_cucp_ue_id_data,
fill_e2sm_cuup_ue_id_data,
};

View File

@@ -0,0 +1,42 @@
/*
* 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
*/
#ifndef RAN_E2SM_UE_ID_H
#define RAN_E2SM_UE_ID_H
#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h"
#include "openair2/RRC/NR/rrc_gNB_UE_context.h"
ue_id_e2sm_t fill_e2sm_gnb_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id);
ue_id_e2sm_t fill_e2sm_cu_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id);
ue_id_e2sm_t fill_e2sm_du_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id);
ue_id_e2sm_t fill_e2sm_cucp_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id);
ue_id_e2sm_t fill_e2sm_cuup_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id);
typedef ue_id_e2sm_t (*get_ue_id)(const gNB_RRC_UE_t *rrc_ue_context, const uint32_t rrc_ue_id, const ue_id_t cucp_ue_id);
extern get_ue_id fill_ue_id_data[END_NGRAN_NODE_TYPE];
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,232 @@
/*
* 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
*/
#include "ran_func_kpm_subs.h"
#include <search.h>
/* measurements that need to store values from previous reporting period have a limitation
when it comes to multiple subscriptions to the same UEs; ric_req_id is unique per subscription */
typedef struct uldlcounter {
uint32_t dl;
uint32_t ul;
} uldlcounter_t;
static uldlcounter_t last_pdcp_sdu_total_bytes[MAX_MOBILES_PER_GNB] = {0};
static nr_pdcp_statistics_t get_pdcp_stats_per_drb(const uint32_t rrc_ue_id, const int rb_id)
{
nr_pdcp_statistics_t pdcp = {0};
const int srb_flag = 0;
// Get PDCP stats for specific DRB
const bool rc = nr_pdcp_get_statistics(rrc_ue_id, srb_flag, rb_id, &pdcp);
assert(rc == true && "Cannot get PDCP stats\n");
return pdcp;
}
/* 3GPP TS 28.522 - section 5.1.2.1.1.1
note: this measurement is calculated as per spec */
static meas_record_lst_t fill_DRB_PdcpSduVolumeDL(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
{
meas_record_lst_t meas_record = {0};
// Get PDCP stats per DRB
const int rb_id = 1; // at the moment, only 1 DRB is supported
nr_pdcp_statistics_t pdcp = get_pdcp_stats_per_drb(ue_info.rrc_ue_id, rb_id);
meas_record.value = INTEGER_MEAS_VALUE;
// Get DL data volume delivered to PDCP layer
meas_record.int_val = (pdcp.rxsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].dl)*8/1000; // [kb]
last_pdcp_sdu_total_bytes[ue_idx].dl = pdcp.rxsdu_bytes;
return meas_record;
}
/* 3GPP TS 28.522 - section 5.1.2.1.2.1
note: this measurement is calculated as per spec */
static meas_record_lst_t fill_DRB_PdcpSduVolumeUL(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
{
meas_record_lst_t meas_record = {0};
// Get PDCP stats per DRB
const int rb_id = 1; // at the moment, only 1 DRB is supported
nr_pdcp_statistics_t pdcp = get_pdcp_stats_per_drb(ue_info.rrc_ue_id, rb_id);
meas_record.value = INTEGER_MEAS_VALUE;
// Get UL data volume delivered from PDCP layer
meas_record.int_val = (pdcp.txsdu_bytes - last_pdcp_sdu_total_bytes[ue_idx].ul)*8/1000; // [kb]
last_pdcp_sdu_total_bytes[ue_idx].ul = pdcp.txsdu_bytes;
return meas_record;
}
#if defined (NGRAN_GNB_DU)
static uldlcounter_t last_rlc_pdu_total_bytes[MAX_MOBILES_PER_GNB] = {0};
static uldlcounter_t last_total_prbs[MAX_MOBILES_PER_GNB] = {0};
static nr_rlc_statistics_t get_rlc_stats_per_drb(const rnti_t rnti, const int rb_id)
{
nr_rlc_statistics_t rlc = {0};
const int srb_flag = 0;
// Get RLC stats for specific DRB
const bool rc = nr_rlc_get_statistics(rnti, srb_flag, rb_id, &rlc);
assert(rc == true && "Cannot get RLC stats\n");
// Activate average sojourn time at the RLC buffer for specific DRB
nr_rlc_activate_avg_time_to_tx(rnti, rb_id+3, 1);
return rlc;
}
/* 3GPP TS 28.522 - section 5.1.3.3.3
note: by default this measurement is calculated for previous 100ms (openair2/LAYER2/nr_rlc/nr_rlc_entity.c:118, 173, 213); please, update according to your needs */
static meas_record_lst_t fill_DRB_RlcSduDelayDl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, __attribute__((unused))const size_t ue_idx)
{
meas_record_lst_t meas_record = {0};
// Get RLC stats per DRB
const int rb_id = 1; // at the moment, only 1 DRB is supported
nr_rlc_statistics_t rlc = get_rlc_stats_per_drb(ue_info.ue->rnti, rb_id);
meas_record.value = REAL_MEAS_VALUE;
// Get the value of sojourn time at the RLC buffer
meas_record.real_val = rlc.txsdu_avg_time_to_tx; // [μs]
return meas_record;
}
/* 3GPP TS 28.522 - section 5.1.1.3.1
note: per spec, average UE throughput in DL (taken into consideration values from all UEs, and averaged)
here calculated as: UE specific throughput in DL */
static meas_record_lst_t fill_DRB_UEThpDl(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
{
meas_record_lst_t meas_record = {0};
// Get RLC stats per DRB
const int rb_id = 1; // at the moment, only 1 DRB is supported
nr_rlc_statistics_t rlc = get_rlc_stats_per_drb(ue_info.ue->rnti, rb_id);
meas_record.value = REAL_MEAS_VALUE;
// Calculate DL Thp
meas_record.real_val = (double)(rlc.txpdu_bytes - last_rlc_pdu_total_bytes[ue_idx].dl)*8/gran_period_ms; // [kbps]
last_rlc_pdu_total_bytes[ue_idx].dl = rlc.txpdu_bytes;
return meas_record;
}
/* 3GPP TS 28.522 - section 5.1.1.3.3
note: per spec, average UE throughput in UL (taken into consideration values from all UEs, and averaged)
here calculated as: UE specific throughput in UL */
static meas_record_lst_t fill_DRB_UEThpUl(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
{
meas_record_lst_t meas_record = {0};
// Get RLC stats per DRB
const int rb_id = 1; // at the moment, only 1 DRB is supported
nr_rlc_statistics_t rlc = get_rlc_stats_per_drb(ue_info.ue->rnti, rb_id);
meas_record.value = REAL_MEAS_VALUE;
// Calculate UL Thp
meas_record.real_val = (double)(rlc.rxpdu_bytes - last_rlc_pdu_total_bytes[ue_idx].ul)*8/gran_period_ms; // [kbps]
last_rlc_pdu_total_bytes[ue_idx].ul = rlc.rxpdu_bytes;
return meas_record;
}
/* 3GPP TS 28.522 - section 5.1.1.2.1
note: per spec, DL PRB usage [%] = (total used PRBs for DL traffic / total available PRBs for DL traffic) * 100
here calculated as: aggregated DL PRBs (t) - aggregated DL PRBs (t-gran_period) */
static meas_record_lst_t fill_RRU_PrbTotDl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
{
meas_record_lst_t meas_record = {0};
meas_record.value = INTEGER_MEAS_VALUE;
// Get the number of DL PRBs
meas_record.int_val = ue_info.ue->mac_stats.dl.total_rbs - last_total_prbs[ue_idx].dl; // [PRBs]
last_total_prbs[ue_idx].dl = ue_info.ue->mac_stats.dl.total_rbs;
return meas_record;
}
/* 3GPP TS 28.522 - section 5.1.1.2.2
note: per spec, UL PRB usage [%] = (total used PRBs for UL traffic / total available PRBs for UL traffic) * 100
here calculated as: aggregated UL PRBs (t) - aggregated UL PRBs (t-gran_period) */
static meas_record_lst_t fill_RRU_PrbTotUl(__attribute__((unused))uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
{
meas_record_lst_t meas_record = {0};
meas_record.value = INTEGER_MEAS_VALUE;
// Get the number of UL PRBs
meas_record.int_val = ue_info.ue->mac_stats.ul.total_rbs - last_total_prbs[ue_idx].ul; // [PRBs]
last_total_prbs[ue_idx].ul = ue_info.ue->mac_stats.ul.total_rbs;
return meas_record;
}
#endif
static kv_measure_t lst_measure[] = {
{.key = "DRB.PdcpSduVolumeDL", .value = fill_DRB_PdcpSduVolumeDL },
{.key = "DRB.PdcpSduVolumeUL", .value = fill_DRB_PdcpSduVolumeUL },
#if defined (NGRAN_GNB_DU)
{.key = "DRB.RlcSduDelayDl", .value = fill_DRB_RlcSduDelayDl },
{.key = "DRB.UEThpDl", .value = fill_DRB_UEThpDl },
{.key = "DRB.UEThpUl", .value = fill_DRB_UEThpUl },
{.key = "RRU.PrbTotDl", .value = fill_RRU_PrbTotDl },
{.key = "RRU.PrbTotUl", .value = fill_RRU_PrbTotUl },
#endif
};
ENTRY kv_pair;
void init_kpm_subs_data(void)
{
const size_t ht_len = sizeof(lst_measure) / sizeof(lst_measure[0]);
hcreate(ht_len);
for (size_t i = 0; i < ht_len; i++) {
kv_pair.key = lst_measure[i].key;
kv_pair.data = &lst_measure[i];
hsearch(kv_pair, ENTER);
}
}
meas_record_lst_t get_kpm_meas_value(char* kpm_meas_name, uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
{
assert(kpm_meas_name != NULL);
ENTRY search_entry = {.key = kpm_meas_name};
ENTRY *found_entry = hsearch(search_entry, FIND);
assert(found_entry != NULL && "Unsupported KPM measurement name");
kv_measure_t *kv_found = (kv_measure_t *)found_entry->data;
meas_record_lst_t meas_record = kv_found->value(gran_period_ms, ue_info, ue_idx);
return meas_record;
}

View File

@@ -0,0 +1,49 @@
/*
* 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
*/
#ifndef RAN_FUNC_SM_KPM_SUBSCRIPTION_AGENT_H
#define RAN_FUNC_SM_KPM_SUBSCRIPTION_AGENT_H
#include "openair2/E2AP/flexric/src/sm/kpm_sm/kpm_data_ie_wrapper.h"
#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h"
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h"
#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h"
typedef struct {
uint32_t rrc_ue_id;
NR_UE_info_t* ue;
} cudu_ue_info_pair_t;
typedef meas_record_lst_t (*kpm_meas_fp)(uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx);
typedef struct{
char* key;
kpm_meas_fp value;
} kv_measure_t;
void init_kpm_subs_data(void);
meas_record_lst_t get_kpm_meas_value(char* kpm_meas_name, uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx);
#endif

View File

@@ -22,10 +22,11 @@
#include "ran_func_rc.h"
#include "ran_func_rc_subs.h"
#include "ran_func_rc_extern.h"
#include "../../flexric/test/rnd/fill_rnd_data_rc.h" // this dependancy will be taken out once RAN Function Definition is implemented
#include "ran_e2sm_ue_id.h"
#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>
@@ -35,17 +36,440 @@ static pthread_once_t once_rc_mutex = PTHREAD_ONCE_INIT;
static rc_subs_data_t rc_subs_data = {0};
static pthread_mutex_t rc_mutex = PTHREAD_MUTEX_INITIALIZER;
static ngran_node_t get_e2_node_type(void)
{
ngran_node_t node_type = 0;
#if defined(NGRAN_GNB_DU) && defined(NGRAN_GNB_CUUP) && defined(NGRAN_GNB_CUCP)
node_type = RC.nrrrc[0]->node_type;
#elif defined (NGRAN_GNB_CUUP)
node_type = ngran_gNB_CUUP;
#endif
return node_type;
}
static void init_once_rc(void)
{
init_rc_subs_data(&rc_subs_data);
}
static void fill_rc_ev_trig(ran_func_def_ev_trig_t* ev_trig)
{
// Sequence of EVENT TRIGGER styles
// [1 - 63]
ev_trig->sz_seq_ev_trg_style = 1;
ev_trig->seq_ev_trg_style = calloc(ev_trig->sz_seq_ev_trg_style, sizeof(seq_ev_trg_style_t));
assert(ev_trig->seq_ev_trg_style != NULL && "Memory exhausted");
seq_ev_trg_style_t* ev_trig_style = &ev_trig->seq_ev_trg_style[0];
// RIC Event Trigger Style Type
// Mandatory
// 9.3.3
// 6.2.2.2.
// INTEGER
ev_trig_style->style = 4;
// RIC Event Trigger Style Name
// Mandatory
// 9.3.4
// 6.2.2.3
//PrintableString(SIZE(1..150,...))
const char ev_style_name[] = "UE Information Change";
ev_trig_style->name = cp_str_to_ba(ev_style_name);
// RIC Event Trigger Format Type
// Mandatory
// 9.3.5
// 6.2.2.4.
// INTEGER
ev_trig_style->format = FORMAT_4_E2SM_RC_EV_TRIGGER_FORMAT;
// Sequence of RAN Parameters for L2 Variables
// [0 - 65535]
ev_trig->sz_seq_ran_param_l2_var = 0;
ev_trig->seq_ran_param_l2_var = NULL;
//Sequence of Call Process Types
// [0-65535]
ev_trig->sz_seq_call_proc_type = 0;
ev_trig->seq_call_proc_type = NULL;
// Sequence of RAN Parameters for Identifying UEs
// 0-65535
ev_trig->sz_seq_ran_param_id_ue = 0;
ev_trig->seq_ran_param_id_ue = NULL;
// Sequence of RAN Parameters for Identifying Cells
// 0-65535
ev_trig->sz_seq_ran_param_id_cell = 0;
ev_trig->seq_ran_param_id_cell = NULL;
}
static void fill_rc_report(ran_func_def_report_t* report)
{
// Sequence of REPORT styles
// [1 - 63]
report->sz_seq_report_sty = 1;
report->seq_report_sty = calloc(report->sz_seq_report_sty, sizeof(seq_report_sty_t));
assert(report->seq_report_sty != NULL && "Memory exhausted");
seq_report_sty_t* report_style = &report->seq_report_sty[0];
// RIC Report Style Type
// Mandatory
// 9.3.3
// 6.2.2.2.
// INTEGER
report_style->report_type = 4;
// RIC Report Style Name
// Mandatory
// 9.3.4
// 6.2.2.3.
// 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
// Mandatory
// 9.3.3
// 6.2.2.2.
// INTEGER
report_style->ev_trig_type = FORMAT_4_E2SM_RC_EV_TRIGGER_FORMAT;
// RIC Report Action Format Type
// Mandatory
// 9.3.5
// 6.2.2.4.
// INTEGER
report_style->act_frmt_type = FORMAT_1_E2SM_RC_ACT_DEF;
// RIC Indication Header Format Type
// Mandatory
// 9.3.5
// 6.2.2.4.
// INTEGER
report_style->ind_hdr_type = FORMAT_1_E2SM_RC_IND_HDR;
// RIC Indication Message Format Type
// Mandatory
// 9.3.5
// 6.2.2.4.
// INTEGER
report_style->ind_msg_type = FORMAT_2_E2SM_RC_IND_MSG;
// Sequence of RAN Parameters Supported
// [0 - 65535]
report_style->sz_seq_ran_param = 1;
report_style->ran_param = calloc(report_style->sz_seq_ran_param, sizeof(seq_ran_param_3_t));
assert(report_style->ran_param != NULL && "Memory exhausted");
// RAN Parameter ID
// Mandatory
// 9.3.8
// [1- 4294967295]
report_style->ran_param[0].id = RRC_STATE_CHANGED_TO_E2SM_RC_RAN_PARAM_ID;
// RAN Parameter Name
// Mandatory
// 9.3.9
// [1-150]
const char ran_param_name[] = "RRC State";
report_style->ran_param[0].name = cp_str_to_ba(ran_param_name);
// RAN Parameter Definition
// Optional
// 9.3.51
report_style->ran_param[0].def = NULL;
}
static void fill_rc_control(ran_func_def_ctrl_t* ctrl)
{
// Sequence of CONTROL styles
// [1 - 63]
ctrl->sz_seq_ctrl_style = 1;
ctrl->seq_ctrl_style = calloc(ctrl->sz_seq_ctrl_style, sizeof(seq_ctrl_style_t));
assert(ctrl->seq_ctrl_style != NULL && "Memory exhausted");
seq_ctrl_style_t* ctrl_style = &ctrl->seq_ctrl_style[0];
// RIC Control Style Type
// Mandatory
// 9.3.3
// 6.2.2.2
ctrl_style->style_type = 1;
//RIC Control Style Name
//Mandatory
//9.3.4
// [1 -150]
const char control_name[] = "Radio Bearer Control";
ctrl_style->name = cp_str_to_ba(control_name);
// RIC Control Header Format Type
// Mandatory
// 9.3.5
ctrl_style->hdr = FORMAT_1_E2SM_RC_CTRL_HDR;
// RIC Control Message Format Type
// Mandatory
// 9.3.5
ctrl_style->msg = FORMAT_1_E2SM_RC_CTRL_MSG;
// RIC Call Process ID Format Type
// Optional
ctrl_style->call_proc_id_type = NULL;
// RIC Control Outcome Format Type
// Mandatory
// 9.3.5
ctrl_style->out_frmt = FORMAT_1_E2SM_RC_CTRL_OUT;
// Sequence of Control Actions
// [0-65535]
ctrl_style->sz_seq_ctrl_act = 1;
ctrl_style->seq_ctrl_act = calloc(ctrl_style->sz_seq_ctrl_act, sizeof(seq_ctrl_act_2_t));
assert(ctrl_style->seq_ctrl_act != NULL && "Memory exhausted");
seq_ctrl_act_2_t* ctrl_act = &ctrl_style->seq_ctrl_act[0];
// Control Action ID
// Mandatory
// 9.3.6
// [1-65535]
ctrl_act->id = 2;
// Control Action Name
// Mandatory
// 9.3.7
// [1-150]
const char control_act_name[] = "QoS flow mapping configuration";
ctrl_act->name = cp_str_to_ba(control_act_name);
// Sequence of Associated RAN Parameters
// [0-65535]
ctrl_act->sz_seq_assoc_ran_param = 2;
ctrl_act->assoc_ran_param = calloc(ctrl_act->sz_seq_assoc_ran_param, sizeof(seq_ran_param_3_t));
assert(ctrl_act->assoc_ran_param != NULL && "Memory exhausted");
seq_ran_param_3_t* assoc_ran_param = ctrl_act->assoc_ran_param;
// DRB ID
assoc_ran_param[0].id = 1;
const char ran_param_drb_id[] = "DRB ID";
assoc_ran_param[0].name = cp_str_to_ba(ran_param_drb_id);
assoc_ran_param[0].def = NULL;
// List of QoS Flows to be modified in DRB
assoc_ran_param[1].id = 2;
const char ran_param_list_qos_flow[] = "List of QoS Flows to be modified in DRB";
assoc_ran_param[1].name = cp_str_to_ba(ran_param_list_qos_flow);
assoc_ran_param[1].def = calloc(1, sizeof(ran_param_def_t));
assert(assoc_ran_param[1].def != NULL && "Memory exhausted");
assoc_ran_param[1].def->type = LIST_RAN_PARAMETER_DEF_TYPE;
assoc_ran_param[1].def->lst = calloc(1, sizeof(ran_param_type_t));
assert(assoc_ran_param[1].def->lst != NULL && "Memory exhausted");
ran_param_type_t* lst = assoc_ran_param[1].def->lst;
lst->sz_ran_param = 2;
lst->ran_param = calloc(lst->sz_ran_param, sizeof(ran_param_lst_struct_t));
assert(lst->ran_param != NULL && "Memory exhausted");
// QoS Flow Identifier
lst->ran_param[0].ran_param_id = 4;
const char ran_param_qos_flow_id[] = "QoS Flow Identifier";
lst->ran_param[0].ran_param_name = cp_str_to_ba(ran_param_qos_flow_id);
lst->ran_param[0].ran_param_def = NULL;
// QoS Flow Mapping Indication
lst->ran_param[1].ran_param_id = 5;
const char ran_param_qos_flow_mapping_ind[] = "QoS Flow Mapping Indication";
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
// Parameters for Control Outcome
// [0- 255]
ctrl_style->sz_ran_param_ctrl_out = 0;
ctrl_style->ran_param_ctrl_out = NULL;
}
e2sm_rc_func_def_t fill_rc_ran_def_gnb(void)
{
e2sm_rc_func_def_t def = {0};
// RAN Function Definition for EVENT TRIGGER
// Optional
// 9.2.2.2
def.ev_trig = calloc(1, sizeof(ran_func_def_ev_trig_t));
assert(def.ev_trig != NULL && "Memory exhausted");
fill_rc_ev_trig(def.ev_trig);
// RAN Function Definition for REPORT
// Optional
// 9.2.2.3
def.report = calloc(1, sizeof(ran_func_def_report_t));
assert(def.report != NULL && "Memory exhausted");
fill_rc_report(def.report);
// RAN Function Definition for INSERT
// Optional
// 9.2.2.4
def.insert = NULL;
// RAN Function Definition for CONTROL
// Optional
// 9.2.2.5
def.ctrl = calloc(1, sizeof(ran_func_def_ctrl_t));
assert(def.ctrl != NULL && "Memory exhausted");
fill_rc_control(def.ctrl);
// RAN Function Definition for POLICY
// Optional
// 9.2.2.6
def.policy = NULL;
return def;
}
static e2sm_rc_func_def_t fill_rc_ran_def_cu(void)
{
e2sm_rc_func_def_t def = {0};
// RAN Function Definition for EVENT TRIGGER
// Optional
// 9.2.2.2
def.ev_trig = calloc(1, sizeof(ran_func_def_ev_trig_t));
assert(def.ev_trig != NULL && "Memory exhausted");
fill_rc_ev_trig(def.ev_trig);
// RAN Function Definition for REPORT
// Optional
// 9.2.2.3
def.report = calloc(1, sizeof(ran_func_def_report_t));
assert(def.report != NULL && "Memory exhausted");
fill_rc_report(def.report);
// RAN Function Definition for INSERT
// Optional
// 9.2.2.4
def.insert = NULL;
// RAN Function Definition for CONTROL
// Optional
// 9.2.2.5
def.ctrl = calloc(1, sizeof(ran_func_def_ctrl_t));
assert(def.ctrl != NULL && "Memory exhausted");
fill_rc_control(def.ctrl);
// RAN Function Definition for POLICY
// Optional
// 9.2.2.6
def.policy = NULL;
return def;
}
static e2sm_rc_func_def_t fill_rc_ran_def_null(void)
{
e2sm_rc_func_def_t def = {0};
// RAN Function Definition for EVENT TRIGGER
// Optional
// 9.2.2.2
def.ev_trig = NULL;
// RAN Function Definition for REPORT
// Optional
// 9.2.2.3
def.report = NULL;
// RAN Function Definition for INSERT
// Optional
// 9.2.2.4
def.insert = NULL;
// RAN Function Definition for CONTROL
// Optional
// 9.2.2.5
def.ctrl = NULL;
// RAN Function Definition for POLICY
// Optional
// 9.2.2.6
def.policy = NULL;
return def;
}
static e2sm_rc_func_def_t fill_rc_ran_def_cucp(void)
{
e2sm_rc_func_def_t def = {0};
// RAN Function Definition for EVENT TRIGGER
// Optional
// 9.2.2.2
def.ev_trig = calloc(1, sizeof(ran_func_def_ev_trig_t));
assert(def.ev_trig != NULL && "Memory exhausted");
fill_rc_ev_trig(def.ev_trig);
// RAN Function Definition for REPORT
// Optional
// 9.2.2.3
def.report = calloc(1, sizeof(ran_func_def_report_t));
assert(def.report != NULL && "Memory exhausted");
fill_rc_report(def.report);
// RAN Function Definition for INSERT
// Optional
// 9.2.2.4
def.insert = NULL;
// RAN Function Definition for CONTROL
// Optional
// 9.2.2.5
def.ctrl = NULL;
// RAN Function Definition for POLICY
// Optional
// 9.2.2.6
def.policy = NULL;
return def;
}
typedef e2sm_rc_func_def_t (*fp_rc_func_def)(void);
static const fp_rc_func_def ran_def_rc[END_NGRAN_NODE_TYPE] =
{
NULL,
NULL,
fill_rc_ran_def_gnb,
NULL,
NULL,
fill_rc_ran_def_cu,
NULL,
fill_rc_ran_def_null, // DU - at the moment, no Service is supported
NULL,
fill_rc_ran_def_cucp,
fill_rc_ran_def_null, // CU-UP - at the moment, no Service is supported
};
void read_rc_setup_sm(void* data)
{
assert(data != NULL);
// assert(data->type == RAN_CTRL_V1_3_AGENT_IF_E2_SETUP_ANS_V0);
rc_e2_setup_t* rc = (rc_e2_setup_t*)data;
rc->ran_func_def = fill_rc_ran_func_def();
/* 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();
rc->ran_func_def = ran_def_rc[node_type]();
// E2 Setup Request is sent periodically until the connection is established
// RC subscritpion data should be initialized only once
@@ -56,53 +480,6 @@ void read_rc_setup_sm(void* data)
RB_PROTOTYPE(ric_id_2_param_id_trees, ric_req_id_s, entries, cmp_ric_req_id);
static ue_id_e2sm_t fill_ue_id_data(const gNB_RRC_UE_t *rrc_ue_context)
{
ue_id_e2sm_t ue_id = {0};
// Check the E2 node type
#if defined (NGRAN_GNB_CUUP) && defined (NGRAN_GNB_CUCP)
ue_id.type = GNB_UE_ID_E2SM;
if (RC.nrrrc[0]->node_type == ngran_gNB_CU) {
// gNB-CU UE F1AP ID List
// C-ifCUDUseparated
ue_id.gnb.gnb_cu_ue_f1ap_lst_len = 1;
ue_id.gnb.gnb_cu_ue_f1ap_lst = calloc(ue_id.gnb.gnb_cu_ue_f1ap_lst_len, sizeof(uint32_t));
assert(ue_id.gnb.gnb_cu_ue_f1ap_lst != NULL && "Memory exhausted");
for(size_t i = 0; i < ue_id.gnb.gnb_cu_ue_f1ap_lst_len; i++) {
ue_id.gnb.gnb_cu_ue_f1ap_lst[i] = rrc_ue_context->rrc_ue_id;
}
} else if (RC.nrrrc[0]->node_type == ngran_gNB_CUCP) {
//gNB-CU-CP UE E1AP ID List
//C-ifCPUPseparated
ue_id.gnb.gnb_cu_cp_ue_e1ap_lst_len = 1;
ue_id.gnb.gnb_cu_cp_ue_e1ap_lst = calloc(ue_id.gnb.gnb_cu_cp_ue_e1ap_lst_len, sizeof(uint32_t));
assert(ue_id.gnb.gnb_cu_cp_ue_e1ap_lst != NULL && "Memory exhausted");
for(size_t i = 0; i < ue_id.gnb.gnb_cu_cp_ue_e1ap_lst_len; i++) {
ue_id.gnb.gnb_cu_cp_ue_e1ap_lst[i] = rrc_ue_context->rrc_ue_id;
}
} else if (RC.nrrrc[0]->node_type == ngran_gNB_DU) {
assert(false && "RRC state change cannot be triggered from DU\n");
}
#elif defined (NGRAN_GNB_CUUP)
assert(false && "RRC state change cannot be triggered from CU-UP\n");
#endif
ue_id.gnb.amf_ue_ngap_id = rrc_ue_context->rrc_ue_id;
ue_id.gnb.guami.amf_ptr = rrc_ue_context->ue_guami.amf_pointer;
ue_id.gnb.guami.amf_region_id = rrc_ue_context->ue_guami.amf_region_id;
ue_id.gnb.guami.amf_set_id = rrc_ue_context->ue_guami.amf_set_id;
ue_id.gnb.guami.plmn_id.mcc = rrc_ue_context->ue_guami.mcc;
ue_id.gnb.guami.plmn_id.mnc = rrc_ue_context->ue_guami.mnc;
ue_id.gnb.guami.plmn_id.mnc_digit_len = rrc_ue_context->ue_guami.mnc_len;
return ue_id;
}
static seq_ran_param_t fill_rrc_state_change_seq_ran(const rc_sm_rrc_state_e rrc_state)
{
seq_ran_param_t seq_ran_param = {0};
@@ -112,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;
}
@@ -138,7 +515,8 @@ static rc_ind_data_t* fill_ue_rrc_state_change(const gNB_RRC_UE_t *rrc_ue_contex
// UE ID
// Mandatory
// 9.3.10
rc_ind->msg.frmt_2.seq_ue_id[0].ue_id = fill_ue_id_data(rrc_ue_context);
const ngran_node_t node_type = get_e2_node_type();
rc_ind->msg.frmt_2.seq_ue_id[0].ue_id = fill_ue_id_data[node_type](rrc_ue_context, 0, 0);
// Sequence of
// RAN Parameter
@@ -152,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);
@@ -167,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);
}
@@ -196,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);
}
@@ -214,47 +592,75 @@ 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;
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");
e2sm_rc_ctrl_msg_frmt_1_t const* frmt_1 = &ctrl->msg.frmt_1;
for(size_t i = 0; i < frmt_1->sz_ran_param; ++i){
seq_ran_param_t const* rp = frmt_1->ran_param;
if(rp[i].ran_param_id == 1){
assert(rp[i].ran_param_val.type == ELEMENT_KEY_FLAG_TRUE_RAN_PARAMETER_VAL_TYPE );
printf("DRB ID %ld \n", rp[i].ran_param_val.flag_true->int_ran);
} 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){
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);
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);
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);
int64_t qfi = lrp[j].ran_param_struct.ran_param_struct[0].ran_param_val.flag_true->int_ran;
assert(qfi > -1 && qfi < 65);
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");
if(ctrl->hdr.frmt_1.ric_style_type == 1 && ctrl->hdr.frmt_1.ctrl_act_id == 2) {
printf("QoS flow mapping configuration\n");
assert(lrp[j].ran_param_struct.ran_param_struct[1].ran_param_id == 5);
assert(lrp[j].ran_param_struct.ran_param_struct[1].ran_param_val.type == ELEMENT_KEY_FLAG_FALSE_RAN_PARAMETER_VAL_TYPE);
int64_t dir = lrp[j].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);
}
}
}
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);
// 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);
// 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);
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");
}
sm_ag_if_ans_t ans = {.type = CTRL_OUTCOME_SM_AG_IF_ANS_V0};
ans.ctrl_out.type = RAN_CTRL_V1_3_AGENT_IF_CTRL_ANS_V0;
return ans;

View File

@@ -0,0 +1,240 @@
#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_I(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;
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_nssai.sst, &RC_nssai.sd)) {
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_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);
} 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_nssai.sst, RC_nssai.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;
}

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

@@ -21,10 +21,109 @@
#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 <assert.h>
#include <stdlib.h>
#if defined(E2AP_V2) || defined(E2AP_V3)
// NGAP
static e2ap_node_component_config_add_t fill_ngap_e2ap_node_component_config_add(void)
{
e2ap_node_component_config_add_t dst = {0};
// Mandatory
// 9.2.26
dst.e2_node_comp_interface_type = NG_E2AP_NODE_COMP_INTERFACE_TYPE;
// Bug!! Optional in the standard, mandatory in ASN.1
// 9.2.32
dst.e2_node_comp_id.type = NG_E2AP_NODE_COMP_INTERFACE_TYPE;
const char ng_msg[] = "Dummy message";
dst.e2_node_comp_id.ng_amf_name = cp_str_to_ba(ng_msg);
// Mandatory
// 9.2.27
const char req[] = "NGAP Request Message sent";
const char res[] = "NGAP Response Message reveived";
dst.e2_node_comp_conf.request = cp_str_to_ba(req);
dst.e2_node_comp_conf.response = cp_str_to_ba(res);
return dst;
}
// F1AP
static e2ap_node_component_config_add_t fill_f1ap_e2ap_node_component_config_add(void)
{
e2ap_node_component_config_add_t dst = {0};
// Mandatory
// 9.2.26
dst.e2_node_comp_interface_type = F1_E2AP_NODE_COMP_INTERFACE_TYPE;
// Bug!! Optional in the standard, mandatory in ASN.1
// 9.2.32
dst.e2_node_comp_id.type = F1_E2AP_NODE_COMP_INTERFACE_TYPE;
dst.e2_node_comp_id.f1_gnb_du_id = 1023;
// Mandatory
// 9.2.27
const char req[] = "F1AP Request Message sent";
const char res[] = "F1AP Response Message reveived";
dst.e2_node_comp_conf.request = cp_str_to_ba(req);
dst.e2_node_comp_conf.response = cp_str_to_ba(res);
return dst;
}
// E1AP
static e2ap_node_component_config_add_t fill_e1ap_e2ap_node_component_config_add(void)
{
e2ap_node_component_config_add_t dst = {0};
// Mandatory
// 9.2.26
dst.e2_node_comp_interface_type = E1_E2AP_NODE_COMP_INTERFACE_TYPE;
// Bug!! Optional in the standard, mandatory in ASN.1
// 9.2.32
dst.e2_node_comp_id.type = E1_E2AP_NODE_COMP_INTERFACE_TYPE;
dst.e2_node_comp_id.e1_gnb_cu_up_id = 1025;
// Mandatory
// 9.2.27
const char req[] = "E1AP Request Message sent";
const char res[] = "E1AP Response Message reveived";
dst.e2_node_comp_conf.request = cp_str_to_ba(req);
dst.e2_node_comp_conf.response = cp_str_to_ba(res);
return dst;
}
// S1AP
static e2ap_node_component_config_add_t fill_s1ap_e2ap_node_component_config_add(void)
{
e2ap_node_component_config_add_t dst = {0};
// Mandatory
// 9.2.26
dst.e2_node_comp_interface_type = S1_E2AP_NODE_COMP_INTERFACE_TYPE;
// Bug!! Optional in the standard, mandatory in ASN.1
// 9.2.32
dst.e2_node_comp_id.type = S1_E2AP_NODE_COMP_INTERFACE_TYPE;
const char str[] = "S1 NAME";
dst.e2_node_comp_id.s1_mme_name = cp_str_to_ba(str);
// Mandatory
// 9.2.27
const char req[] = "S1AP Request Message sent";
const char res[] = "S1AP Response Message reveived";
dst.e2_node_comp_conf.request = cp_str_to_ba(req);
dst.e2_node_comp_conf.response = cp_str_to_ba(res);
return dst;
}
#endif
void read_setup_ran(void* data, const ngran_node_t node_type)
{
assert(data != NULL);

View File

@@ -13,6 +13,8 @@ Please see [NOTICE](NOTICE.md) file for third party software that is included in
This tutorial describes the steps of deployment 5G OAI RAN, with integrated E2 agent and a nearRT-RIC using O-RAN compliant FlexRIC.
[[_TOC_]]
# 1. Installation
## 1.1 Install prerequisites
@@ -21,7 +23,7 @@ This tutorial describes the steps of deployment 5G OAI RAN, with integrated E2 a
On Ubuntu, you might want to use [this PPA](https://apt.kitware.com/) to install an up-to-date version.
- SWIG (at least v.4.0).
- SWIG (at least v.4.1).
We use SWIG as an interface generator to enable the multi-language feature (i.e., C/C++ and Python) for the xApps. Please, check your SWIG version (i.e, `swig
-version`) and install it from scratch if necessary as described here: https://swig.org/svn.html or via the code below:
@@ -69,17 +71,11 @@ Currently available versions:
| E2AP v2.03 | Y | Y |
| E2AP v3.01 | Y | Y |
Please note that KPM SM v2.01 is supported only in FlexRIC, but not in OAI.
Please note that KPM SM v2.01 is supported only in FlexRIC, but not in OAI.
```bash
cd cmake_targets/
./build_oai -I -w SIMU --gNB --nrUE --build-e2 --ninja
```
If the openair2/E2AP/flexric folder is empty, try manually the following commands
```bash
git submodule init
git submodule update
```
* `-I` option is to install pre-requisites, you only need it the first time you build the softmodem or when some oai dependencies have changed.
@@ -89,16 +85,22 @@ git submodule update
* `--ninja` is to use the ninja build tool, which speeds up compilation
* `--build-e2` option is to use the E2 agent, integrated within RAN.
If the openair2/E2AP/flexric folder is empty, try manually the following commands:
```bash
git submodule init
git submodule update
```
## 2.2 FlexRIC
- By default, FlexRIC will build the nearRT-RIC with E2AP v2 and KPM v2. If you want a different version, edit the variable E2AP\_VERSION and KPM\_VERSION at FlexRIC's CMakeLists.txt file. Note that OAI's and FlexRIC's E2AP\_VERSION and KPM\_VERSION need to match due to O-RAN incompatibilities among versions.
By default, FlexRIC will build the nearRT-RIC with E2AP v2 and KPM v2. If you want a different version, edit the variable E2AP\_VERSION and KPM\_VERSION at FlexRIC's CMakeLists.txt file. Note that OAI's and FlexRIC's E2AP\_VERSION and KPM\_VERSION need to match due to O-RAN incompatibilities among versions.
### 2.2.1 Clone the FlexRIC repository
```bash
git clone https://gitlab.eurecom.fr/mosaic5g/flexric flexric
cd flexric/
git checkout 29af8408afad3da36824bd7912562202023ae924
git checkout 07e8f8f198b72a0c9b39cc6ed6d0dbce6af133f8
```
### 2.2.2 Build FlexRIC
@@ -113,27 +115,47 @@ sudo make install
By default the service model libraries will be installed in the path `/usr/local/lib/flexric` while the configuration file in `/usr/local/etc/flexric`.
#### 2.2.3.1 Available Service Models
1. KPM v02.03 and KPM v03.00 (`xapp_kpm_moni`)
At the moment, the following measurements are supported in OAI RAN:
* "DRB.PdcpSduVolumeDL"
* "DRB.PdcpSduVolumeUL"
* "DRB.RlcSduDelayDl"
* "DRB.UEThpDl"
* "DRB.UEThpUl"
* "RRU.PrbTotDl"
* "RRU.PrbTotUl"
# 3. Service Models available in OAI RAN
2. RC v01.03
Currently available RC xApps:
* `xapp_kpm_rc` - RAN control function "QoS flow mapping configuration" support, i.e. creating a new DRB.
* `xapp_rc_moni` - aperiodic subscription support for "UE RRC State Change"
## 3.1 O-RAN
3. MAC + RLC + PDCP + GTP (`xapp_gtp_mac_rlc_pdcp_moni`)
We assume that user is familiar with O-RAN WG3 specifications that can be found at https://orandownloadsweb.azurewebsites.net/specifications.
### 3.1.1 E2SM-KPM
As mentioned in section [2.1.2 Build OAI with E2 Agent](#212-build-oai-with-e2-agent), we support KPM v2.03/v3.00. Uses ASN.1 encoding.
Per O-RAN specifications, 5G measurements supported by KPM are specified in 3GPP TS 28.552.
From 3GPP TS 28.552, we support the following list:
* "DRB.PdcpSduVolumeDL"
* "DRB.PdcpSduVolumeUL"
* "DRB.RlcSduDelayDl"
* "DRB.UEThpDl"
* "DRB.UEThpUl"
* "RRU.PrbTotDl"
* "RRU.PrbTotUl"
From O-RAN.WG3.E2SM-KPM-version specification, we implemented:
* REPORT Service Style 4 ("Common condition-based, UE-level" - section 7.4.5) - fetch above measurements per each UE that matches common criteria (e.g. S-NSSAI).
### 3.1.2 E2SM-RC
We support RC v1.03. Uses ASN.1 encoding.
From ORAN.WG3.E2SM-RC-v01.03 specification, we implemented:
* REPORT Service Style 4 ("UE Information" - section 7.4.5) - aperiodic subscription for "UE RRC State Change"
* CONTROL Service Style 1 ("Radio Bearer Control" - section 7.6.2) - "QoS flow mapping configuration" (e.g creating a new DRB)
## 3.2 Custom Service Models
In addition, we support custom Service Models, such are MAC, RLC, PDCP, and GTP. Use plain encoding.
If you are interested in TC and SLICE SMs, please follow the instructions at https://gitlab.eurecom.fr/mosaic5g/flexric.
# 3. Start the process
# 4. Start the process
At this point, we assume the 5G Core Network is already running in backgroud. For more information, please take a look at
In order to configure E2 agent, please, add the following block in OAI's configuration file:
```bash
@@ -142,61 +164,75 @@ e2_agent = {
sm_dir = "/usr/local/lib/flexric/"
}
```
* start E2 Node agents
* start the gNB-mono
```bash
cd oai/cmake_targets/ran_build/build
sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --rfsim --sa -E
```
As per O-RAN.WG3.E2SM-v02.00 specifications, UE ID (section 6.2.2.6) representation in OAI is:
| | gNB-mono | CU | CU-CP | CU-UP | DU |
|:----------------------|:----------------|:----------------|:----------------|:----------------------|:-------------------|
| CHOICE UE ID case | GNB_UE_ID_E2SM | GNB_UE_ID_E2SM | GNB_UE_ID_E2SM | GNB_CU_UP_UE_ID_E2SM | GNB_DU_UE_ID_E2SM |
| AMF UE NGAP ID | amf_ue_ngap_id | amf_ue_ngap_id | amf_ue_ngap_id | | |
| GUAMI | guami | guami | guami | | |
| gNB-CU UE F1AP ID | | rrc_ue_id | | | rrc_ue_id |
| gNB-CU-CP UE E1AP ID | | | rrc_ue_id | rrc_ue_id | |
| RAN UE ID | rrc_ue_id | rrc_ue_id | rrc_ue_id | rrc_ue_id | rrc_ue_id |
* if CU/DU split is used, start the gNB as follows
```bash
sudo ./nr-softmodem -O <path_to_du_conf_file> --rfsim --sa -E
sudo ./nr-softmodem -O <path_to_cu_conf_file> --sa
```
* start the gNB-mono
```bash
cd oai/cmake_targets/ran_build/build
sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --rfsim --sa -E
```
* if CU-CP/CU-UP/DU split is used, start the gNB as follows
```bash
sudo ./nr-softmodem -O <path_to_du_conf_file> --rfsim --sa -E
sudo ./nr-softmodem -O <path_to_cu_cp_conf_file> --sa
sudo ./nr-cuup -O <path_to_cu_up_conf_file> --sa
```
* if CU/DU split is used, start the gNB as follows
```bash
sudo ./nr-softmodem -O <path_to_du_conf_file> --rfsim --sa -E
sudo ./nr-softmodem -O <path_to_cu_conf_file> --sa
```
* if CU-CP/CU-UP/DU split is used, start the gNB as follows
```bash
sudo ./nr-softmodem -O <path_to_du_conf_file> --rfsim --sa -E
sudo ./nr-softmodem -O <path_to_cu_cp_conf_file> --sa
sudo ./nr-cuup -O <path_to_cu_up_conf_file> --sa
```
* start the nrUE
```bash
cd oai/cmake_targets/ran_build/build
sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --rfsim --sa --uicc0.imsi 001010000000001 --rfsimulator.serveraddr 127.0.0.1
```
```bash
cd oai/cmake_targets/ran_build/build
sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --rfsim --sa --uicc0.imsi 001010000000001 --rfsimulator.serveraddr 127.0.0.1
```
* start the nearRT-RIC
```bash
cd flexric
./build/examples/ric/nearRT-RIC
```
```bash
cd flexric
./build/examples/ric/nearRT-RIC
```
* start the KPM monitor xApp
```bash
cd flexric
./build/examples/xApp/c/monitor/xapp_kpm_moni
```
* Start different xApps
* start the RC monitor xApp
```bash
cd flexric
./build/examples/xApp/c/monitor/xapp_rc_moni
```
* start the KPM monitor xApp - measurements stated in [3.1.1 E2SM-KPM](#311-e2sm-kpm) for each UE that matches S-NSSAI common criteria
```bash
cd flexric
./build/examples/xApp/c/monitor/xapp_kpm_moni
```
Note: we assume that each UE has only 1 DRB; CU-UP does not store the slices, therefore "coarse filtering" is used
* start the RC control xApp
```bash
cd flexric
./build/examples/xApp/c/kpm_rc/xapp_kpm_rc
```
* start the RC monitor xApp - aperiodic subscription for "UE RRC State Change"
```bash
cd flexric
./build/examples/xApp/c/monitor/xapp_rc_moni
```
* start the (MAC + RLC + PDCP + GTP) monitor xApp
```bash
cd flexric
./build/examples/xApp/c/monitor/xapp_gtp_mac_rlc_pdcp_moni
```
* start the RC control xApp - RAN control function "QoS flow mapping configuration" (e.g. creating a new DRB)
```bash
cd flexric
./build/examples/xApp/c/kpm_rc/xapp_kpm_rc
```
* start the (MAC + RLC + PDCP + GTP) monitor xApp
```bash
cd flexric
./build/examples/xApp/c/monitor/xapp_gtp_mac_rlc_pdcp_moni
```
The latency that you observe in your monitor xApp is the latency from the E2 Agent to the nearRT-RIC and xApp.
Therefore, FlexRIC is well suited for use cases with ultra low-latency requirements.
Additionally, all the data received in the `xapp_gtp_mac_rlc_pdcp_moni` xApp is also written to /tmp/xapp_db in case that offline data processing is wanted (e.g., Machine Learning/Artificial Intelligence applications). You can browse the data using e.g., sqlitebrowser.

View File

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

@@ -319,8 +319,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
@@ -347,20 +350,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");
}
}
}
@@ -587,13 +592,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;
@@ -654,7 +676,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 */
@@ -807,7 +829,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,
@@ -824,7 +846,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)
{
@@ -876,16 +906,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
@@ -910,7 +941,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,
@@ -928,7 +964,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;
@@ -1266,10 +1305,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;
@@ -1321,6 +1361,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

@@ -58,6 +58,7 @@
#include "common/ran_context.h"
#include "nfapi/oai_integration/vendor_ext.h"
#include "NR_MAC_gNB/slicing/nr_slicing.h"
//#define DEBUG_DCI
@@ -1874,6 +1875,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
*/
@@ -2049,6 +2074,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);
}
@@ -2461,6 +2490,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++) {
@@ -2600,6 +2639,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++)
@@ -3071,6 +3118,17 @@ 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);
}
/* 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,
@@ -179,7 +179,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

@@ -25,6 +25,7 @@
#include "openair2/F1AP/f1ap_ids.h"
#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.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 @@ NR_CellGroupConfig_t *clone_CellGroupConfig(const NR_CellGroupConfig_t *orig)
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];
@@ -360,8 +381,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 */
@@ -465,7 +494,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

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

@@ -110,6 +110,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,
@@ -534,6 +536,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 {
@@ -634,6 +644,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;
@@ -707,6 +724,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 {
@@ -721,6 +740,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);
@@ -728,6 +760,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;
@@ -826,7 +896,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,602 @@
/*
* 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"
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);
for (int l = 0; l < sched_ctrl->dl_lc_num; ++l) {
lcid = sched_ctrl->dl_lc_ids[l];
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);
LOG_D(NR_MAC, "add lcid %ld to slice idx %d\n", lcid, i);
if (!check_nr_list(&new_ue->dl_id, si->s[i]->id)) {
add_nr_list(&new_ue->dl_id, si->s[i]->id);
LOG_D(NR_MAC, "add dl id %d to new UE rnti %x\n", si->s[i]->id, new_ue->rnti);
}
UE_iterator(si->s[i]->UE_list, UE) {
if (UE->rnti == new_ue->rnti)
break;
}
if (UE == NULL) {
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_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_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);
}
}
} else {
LOG_D(NR_MAC, "cannot find matched slice (lcid %ld <sst %d sd %d>, slice idx %d <sst %d sd %d>), do nothing for UE rnti 0x%04x\n",
lcid, sched_ctrl->dl_lc_nssai[lcid].sst, sched_ctrl->dl_lc_nssai[lcid].sd, i, si->s[i]->nssai.sst, si->s[i]->nssai.sd, 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, TYPE_C_RNTI_);
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

@@ -478,6 +478,14 @@ int rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(MessageDef *msg_p, instance_t
PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt, instance, GNB_FLAG_YES, UE->rrc_ue_id, 0, 0);
UE->amf_ue_ngap_id = req->amf_ue_ngap_id;
/* GUAMI */
UE->ue_guami.mcc = req->guami.mcc;
UE->ue_guami.mnc = req->guami.mnc;
UE->ue_guami.mnc_len = req->guami.mnc_len;
UE->ue_guami.amf_region_id = req->guami.amf_region_id;
UE->ue_guami.amf_set_id = req->guami.amf_set_id;
UE->ue_guami.amf_pointer = req->guami.amf_pointer;
/* NAS PDU */
// this is malloced pointers, we pass it for later free()
UE->nas_pdu = req->nas_pdu;

View File

@@ -746,7 +746,7 @@ static int ngap_gNB_handle_initial_context_request(sctp_assoc_t assoc_id, uint32
OCTET_STRING_TO_INT8(&ie->value.choice.GUAMI.aMFRegionID, msg->guami.amf_region_id);
OCTET_STRING_TO_INT16(&ie->value.choice.GUAMI.aMFSetID, msg->guami.amf_set_id);
OCTET_STRING_TO_INT8(&ie->value.choice.GUAMI.aMFPointer, msg->guami.amf_pointer);
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;
@@ -799,6 +799,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 */
@@ -979,6 +980,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);