Commit ba003cc6 authored by Teodora's avatar Teodora

KPM cleanup

  - RAN Function Definition implemented
  - 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
parent fc4c4fc5
......@@ -8,7 +8,7 @@ add_library(e2_ran_func_cuup STATIC
../flexric/test/rnd/fill_rnd_data_tc.c
O-RAN/ran_e2sm_ue_id.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_kpm_subs.c
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
......@@ -27,7 +27,7 @@ add_library(e2_ran_func_du_cucp_cuup STATIC
O-RAN/ran_e2sm_ue_id.c
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_kpm_subs.c
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_func_rc_subs.c
......
......@@ -20,141 +20,70 @@
*/
#include "ran_func_kpm.h"
#include "openair2/E2AP/flexric/test/rnd/fill_rnd_data_kpm.h" // this dependancy will be taken out once RAN Function Definition is implemented
#include "openair2/E2AP/flexric/src/util/time_now_us.h"
#include "common/utils/assertions.h"
#if defined (NGRAN_GNB_DU)
#include "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h"
#include "openair2/LAYER2/nr_rlc/nr_rlc_entity.h"
#include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h"
#endif
#if defined (NGRAN_GNB_CUCP)
#include "openair2/RRC/NR/rrc_gNB_UE_context.h"
#include "openair3/NGAP/ngap_gNB_ue_context.h"
#endif
#include "ran_func_kpm_subs.h"
#include "ran_e2sm_ue_id.h"
#if defined (NGRAN_GNB_CUUP)
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_entity.h"
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h"
#include "openair2/E1AP/e1ap_common.h"
#endif
#include "openair2/E2AP/flexric/src/util/time_now_us.h"
#include "openair2/F1AP/f1ap_ids.h"
#include <assert.h>
#include <stdio.h>
/* Please note that only one executable nr-softmodem exists for DU, CU, CU-CP and gNB-mono nodes;
the layer separation should be done better in the future */
#if defined (NGRAN_GNB_CUUP)
typedef struct uldlcounter {
uint32_t dl;
uint32_t ul;
} uldlcounter_t;
static pthread_once_t once_kpm_mutex = PTHREAD_ONCE_INIT;
static bool nssai_matches(nssai_t a_nssai, uint8_t b_sst, const uint32_t *b_sd)
static void init_once_kpm(void)
{
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;
}
init_kpm_subs_data();
}
static uldlcounter_t last_pdcp_sdu_total_bytes[MAX_MOBILES_PER_GNB] = {0};
static gnb_cu_up_e2sm_t fill_gnb_cu_up_data(const ue_id_t cucp_ue_id)
static ngran_node_t get_e2_node_type(void)
{
gnb_cu_up_e2sm_t gnb_cu_up = {0};
ngran_node_t node_type = 0;
// 6.2.3.20
// Mandatory
gnb_cu_up.gnb_cu_cp_ue_e1ap = cucp_ue_id;
// 6.2.3.25
// RAN UE ID
// Optional
gnb_cu_up.ran_ue_id = NULL;
#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 gnb_cu_up;
return node_type;
}
typedef struct {
ue_id_t pdcp_ue_id_list[MAX_MOBILES_PER_GNB]; // list of matched UEs on PDCP-U level
size_t num_ues;
size_t sz;
ue_id_e2sm_t* ue_id;
} matched_ues_pdcp_t;
// Optional
// only used to retreive MAC/RLC stats
NR_UE_info_t* ue_info_list;
}arr_ue_id_t;
__attribute__((unused))
static size_t filter_ues_by_s_nssai_in_cuup(test_cond_e const condition, uint8_t sst, const uint32_t *sd, matched_ues_pdcp_t* matches)
static meas_data_lst_t fill_kpm_meas_data_item(const meas_info_format_1_lst_t* meas_info_lst, const size_t len, const uint32_t gran_period_ms, cudu_ue_info_pair_t ue_info, const size_t ue_idx)
{
DevAssert(matches != NULL);
AssertFatal(condition == EQUAL_TEST_COND, "Condition %d not yet implemented\n", condition);
meas_data_lst_t data_item = {0};
// Get NSSAI info from E1 context
const instance_t CUuniqInstance = 0;
const e1ap_upcp_inst_t *e1inst = getCxtE1(CUuniqInstance);
// Measurement Record
data_item.meas_record_len = len; // record data list length corresponds to info list length from action definition
// currently the CU-UP does not store the slices, so we can only do this "coarse filtering"
for(int s = 0; s < e1inst->cuup.setupReq.plmn[0].supported_slices; s++) {
if (nssai_matches(e1inst->cuup.setupReq.plmn[0].slice[s], sst, sd)) {
matches->num_ues = nr_pdcp_get_num_ues(matches->pdcp_ue_id_list, MAX_MOBILES_PER_GNB);
}
}
return matches->num_ues;
}
data_item.meas_record_lst = calloc(len, sizeof(meas_record_lst_t));
assert(data_item.meas_record_lst != NULL && "Memory exhausted");
static nr_pdcp_statistics_t get_pdcp_stats_per_drb(const uint32_t rrc_ue_id)
{
nr_pdcp_statistics_t pdcp = {0};
const int srb_flag = 0;
const int rb_id = 1; // at the moment, only 1 DRB is supported
for (size_t i = 0; i < len; i++) {
// Measurement Type as requested in Action Definition
assert(meas_info_lst[i].meas_type.type == NAME_MEAS_TYPE && "Only NAME supported");
// 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");
char* meas_info_name_str = cp_ba_to_str(meas_info_lst[i].meas_type.name);
return pdcp;
}
data_item.meas_record_lst[i] = get_kpm_meas_value(meas_info_name_str, gran_period_ms, ue_info, ue_idx);
static meas_record_lst_t fill_ue_pdcp_data(const uint32_t rrc_ue_id, const size_t ue_idx, const char * meas_info_name_str)
{
meas_record_lst_t meas_record = {0};
// Get PDCP stats per DRB
nr_pdcp_statistics_t pdcp = get_pdcp_stats_per_drb(rrc_ue_id);
// Measurement Type as requested in Action Definition
if (strcmp(meas_info_name_str, "DRB.PdcpSduVolumeDL") == 0) { // 3GPP TS 28.522 - section 5.1.2.1.1.1
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;
/* note: this measurement is calculated as per spec */
} else if (strcmp(meas_info_name_str, "DRB.PdcpSduVolumeUL") == 0) { // 3GPP TS 28.522 - section 5.1.2.1.2.1
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;
/* note: this measurement is calculated as per spec */
} else {
assert(false && "Measurement Name not yet implemented");
free(meas_info_name_str);
}
return meas_record;
return data_item;
}
static kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1_in_cu(const uint32_t rrc_ue_id, const size_t ue_idx, const kpm_act_def_format_1_t * act_def_fr_1)
static kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1(cudu_ue_info_pair_t ue_info, const size_t ue_idx, const kpm_act_def_format_1_t* act_def_fr_1)
{
kpm_ind_msg_format_1_t msg_frm_1 = {0};
// Measurement Data contains a set of Meas Records, each collected at each granularity period
msg_frm_1.meas_data_lst_len = 1; /* this value is equal to (kpm_ric_event_trigger_format_1.report_period_ms/act_def_fr_1->gran_period_ms)
please, check their values in xApp */
......@@ -162,32 +91,11 @@ static kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1_in_cu(const uint32_t rrc_ue
msg_frm_1.meas_data_lst = calloc(msg_frm_1.meas_data_lst_len, sizeof(*msg_frm_1.meas_data_lst));
assert(msg_frm_1.meas_data_lst != NULL && "Memory exhausted" );
for (size_t i = 0; i<msg_frm_1.meas_data_lst_len; i++) {
// Measurement Record
msg_frm_1.meas_data_lst[i].meas_record_len = act_def_fr_1->meas_info_lst_len; // record data list length corresponds to info list length from action definition
msg_frm_1.meas_data_lst[i].meas_record_lst = calloc(msg_frm_1.meas_data_lst[i].meas_record_len, sizeof(meas_record_lst_t));
assert(msg_frm_1.meas_data_lst[i].meas_record_lst != NULL && "Memory exhausted");
for (size_t j = 0; j < msg_frm_1.meas_data_lst[i].meas_record_len; j++) { // each meas record corresponds to one meas type
// Measurement Type as requested in Action Definition
switch (act_def_fr_1->meas_info_lst[j].meas_type.type) {
case NAME_MEAS_TYPE: {
char meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len + 1];
memcpy(meas_info_name_str, act_def_fr_1->meas_info_lst[j].meas_type.name.buf, act_def_fr_1->meas_info_lst[j].meas_type.name.len);
meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len] = '\0';
msg_frm_1.meas_data_lst[i].meas_record_lst[j] = fill_ue_pdcp_data(rrc_ue_id, ue_idx, meas_info_name_str);
break;
}
case ID_MEAS_TYPE:
assert(false && "ID Measurement Type not yet implemented");
default:
assert(false && "Measurement Type not recognized");
}
}
}
msg_frm_1.meas_data_lst[0] = fill_kpm_meas_data_item(act_def_fr_1->meas_info_lst,
act_def_fr_1->meas_info_lst_len,
act_def_fr_1->gran_period_ms,
ue_info,
ue_idx);
// Measurement Information - OPTIONAL
msg_frm_1.meas_info_lst_len = act_def_fr_1->meas_info_lst_len;
......@@ -200,416 +108,241 @@ static kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1_in_cu(const uint32_t rrc_ue
return msg_frm_1;
}
__attribute__((unused))
static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3_in_cuup(const matched_ues_pdcp_t* matched_ues,
const kpm_act_def_format_1_t* act_def_fr_1)
static cudu_ue_info_pair_t fill_ue_related_info(arr_ue_id_t* arr_ue_id, const size_t ue_idx)
{
cudu_ue_info_pair_t ue_info = {0};
if (arr_ue_id->ue_id[ue_idx].type == GNB_UE_ID_E2SM) {
ue_info.rrc_ue_id = *arr_ue_id->ue_id[ue_idx].gnb.ran_ue_id; // rrc_ue_id
ue_info.ue = &arr_ue_id->ue_info_list[ue_idx];
} else if (arr_ue_id->ue_id[ue_idx].type == GNB_CU_UP_UE_ID_E2SM) {
/* in OAI implementation, CU-UP ue id = CU-CP ue id
=> CU-UP ue id = rrc_ue_id, but it should not be the case by the spec */
ue_info.rrc_ue_id = *arr_ue_id->ue_id[ue_idx].gnb_cu_up.ran_ue_id; // cucp_ue_id = rrc_ue_id
} else if (arr_ue_id->ue_id[ue_idx].type == GNB_DU_UE_ID_E2SM) {
ue_info.rrc_ue_id = *arr_ue_id->ue_id[ue_idx].gnb_du.ran_ue_id; // rrc_ue_id
ue_info.ue = &arr_ue_id->ue_info_list[ue_idx];
}
return ue_info;
}
static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3(arr_ue_id_t* arr_ue_id, const kpm_act_def_format_1_t* act_def_fr_1)
{
assert(act_def_fr_1 != NULL);
kpm_ind_msg_format_3_t msg_frm_3 = {0};
// Fill UE Measurement Reports
msg_frm_3.ue_meas_report_lst_len = matched_ues->num_ues;
msg_frm_3.ue_meas_report_lst_len = arr_ue_id->sz;
msg_frm_3.meas_report_per_ue = calloc(msg_frm_3.ue_meas_report_lst_len, sizeof(meas_report_per_ue_t));
assert(msg_frm_3.meas_report_per_ue != NULL && "Memory exhausted");
for (size_t i = 0; i<msg_frm_3.ue_meas_report_lst_len; i++) {
// Fill UE ID data
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.type = GNB_CU_UP_UE_ID_E2SM;
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.gnb_cu_up = fill_gnb_cu_up_data(matched_ues->pdcp_ue_id_list[i]);
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst = cp_ue_id_e2sm(&arr_ue_id->ue_id[i]);
// Fill UE related info
msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 =
fill_kpm_ind_msg_frm_1_in_cu(matched_ues->pdcp_ue_id_list[i], i, act_def_fr_1);
}
return msg_frm_3;
}
#endif
cudu_ue_info_pair_t ue_info = fill_ue_related_info(arr_ue_id, i);
#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 gnb_e2sm_t fill_gnb_data(rrc_gNB_ue_context_t * ue_context_p)
{
gnb_e2sm_t gnb = {0};
// 6.2.3.16
// Mandatory
// AMF UE NGAP ID
gnb.amf_ue_ngap_id = ue_context_p->ue_context.rrc_ue_id;
// Mandatory
//GUAMI 6.2.3.17
gnb.guami.plmn_id = (e2sm_plmn_t) {
.mcc = ue_context_p->ue_context.ue_guami.mcc,
.mnc = ue_context_p->ue_context.ue_guami.mnc,
.mnc_digit_len = ue_context_p->ue_context.ue_guami.mnc_len
};
gnb.guami.amf_region_id = ue_context_p->ue_context.ue_guami.amf_region_id;
gnb.guami.amf_set_id = ue_context_p->ue_context.ue_guami.amf_set_id;
gnb.guami.amf_ptr = ue_context_p->ue_context.ue_guami.amf_pointer;
// gNB-CU UE F1AP ID List
// C-ifCUDUseparated
if (NODE_IS_CU(RC.nrrrc[0]->node_type)) {
gnb.gnb_cu_ue_f1ap_lst_len = 1;
gnb.gnb_cu_ue_f1ap_lst = calloc(gnb.gnb_cu_ue_f1ap_lst_len, sizeof(uint32_t));
assert(gnb.gnb_cu_ue_f1ap_lst != NULL);
for (size_t i = 0; i < gnb.gnb_cu_ue_f1ap_lst_len; i++) {
gnb.gnb_cu_ue_f1ap_lst[i] = ue_context_p->ue_context.rrc_ue_id;
}
// Fill UE measurements
msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 = fill_kpm_ind_msg_frm_1(ue_info, i, act_def_fr_1);
}
return gnb;
return msg_frm_3;
}
static gnb_du_e2sm_t fill_gnb_du_data(const f1_ue_data_t * rrc_ue_id)
{
gnb_du_e2sm_t gnb_du = {0};
// 6.2.3.21
// gNB CU UE F1AP
// Mandatory
gnb_du.gnb_cu_ue_f1ap = rrc_ue_id->secondary_ue;
// 6.2.3.25
// RAN UE ID
// Optional
gnb_du.ran_ue_id = NULL;
return gnb_du;
}
typedef struct {
NR_UE_info_t* ue_list[MAX_MOBILES_PER_GNB];
size_t num_ues;
} matched_ues_mac_t;
static size_t filter_ues_by_s_nssai_in_du_or_monolithic(test_cond_e const condition,
uint8_t sst,
const uint32_t *sd,
matched_ues_mac_t* matches)
static void capture_sst_sd(test_cond_value_t* test_cond_value, uint8_t *sst, uint32_t **sd)
{
DevAssert(matches != NULL);
AssertFatal(condition == EQUAL_TEST_COND, "Condition %d not yet implemented", condition);
DevAssert(sst != NULL);
DevAssert(sd != NULL);
// Take MAC info
size_t i = 0;
UE_iterator (RC.nrmac[0]->UE_info.list, ue) {
NR_UE_sched_ctrl_t *sched_ctrl = &ue->UE_sched_ctrl;
// UE matches if any of its DRBs matches
for (int l = 0; l < sched_ctrl->dl_lc_num; ++l) {
long lcid = sched_ctrl->dl_lc_ids[l];
if (nssai_matches(sched_ctrl->dl_lc_nssai[lcid], sst, sd)) {
matches->ue_list[i++] = ue;
break;
// S-NSSAI is an OCTET_STRING, as defined by spec
switch (test_cond_value->type) {
case OCTET_STRING_TEST_COND_VALUE: {
if (test_cond_value->octet_string_value->len == 1) {
*sst = test_cond_value->octet_string_value->buf[0];
*sd = NULL;
} else {
DevAssert(test_cond_value->octet_string_value->len == 4);
uint8_t *buf = test_cond_value->octet_string_value->buf;
*sst = buf[0];
*sd = malloc(**sd);
**sd = buf[1] << 16 | buf[2] << 8 | buf[3];
}
break;
}
AssertFatal(i < MAX_MOBILES_PER_GNB, "cannot have more UEs than global UE number maximum\n");
default:
AssertFatal(false, "test condition value %d impossible\n", test_cond_value->type);
}
matches->num_ues = i;
return i;
}
static nr_rlc_statistics_t get_rlc_stats_per_drb(NR_UE_info_t const * UE)
{
assert(UE != NULL);
nr_rlc_statistics_t rlc = {0};
const int srb_flag = 0;
const int rb_id = 1; // at the moment, only 1 DRB is supported
// Get RLC stats for specific DRB
const bool rc = nr_rlc_get_statistics(UE->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(UE->rnti, rb_id+3, 1);
return rlc;
}
static meas_record_lst_t fill_ue_mac_rlc_data(const NR_UE_info_t* UE, const size_t ue_idx, const char * meas_info_name_str, const uint32_t gran_period_ms)
static bool nssai_matches(nssai_t a_nssai, uint8_t b_sst, const uint32_t *b_sd)
{
assert(UE != NULL);
meas_record_lst_t meas_record = {0};
// Get RLC stats per DRB
nr_rlc_statistics_t rlc = get_rlc_stats_per_drb(UE);
// Measurement Type as requested in Action Definition
if (strcmp(meas_info_name_str, "DRB.UEThpDl") == 0) { // 3GPP TS 28.522 - section 5.1.1.3.1
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;
/* 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 */
} else if (strcmp(meas_info_name_str, "DRB.UEThpUl") == 0) { // 3GPP TS 28.522 - section 5.1.1.3.3
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;
/* 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 */
} else if (strcmp(meas_info_name_str, "DRB.RlcSduDelayDl") == 0) { // 3GPP TS 28.522 - section 5.1.3.3.3
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]
/* 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 */
} else if (strcmp(meas_info_name_str, "RRU.PrbTotDl") == 0) { // 3GPP TS 28.522 - section 5.1.1.2.1
meas_record.value = INTEGER_MEAS_VALUE;
// Get the number of DL PRBs
meas_record.int_val = UE->mac_stats.dl.total_rbs - last_total_prbs[ue_idx].dl; // [PRBs]
last_total_prbs[ue_idx].dl = UE->mac_stats.dl.total_rbs;
/* 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) */
} else if (strcmp(meas_info_name_str, "RRU.PrbTotUl") == 0) { // 3GPP TS 28.522 - section 5.1.1.2.2
meas_record.value = INTEGER_MEAS_VALUE;
// Get the number of UL PRBs
meas_record.int_val = UE->mac_stats.ul.total_rbs - last_total_prbs[ue_idx].ul; // [PRBs]
last_total_prbs[ue_idx].ul = UE->mac_stats.ul.total_rbs;
/* 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) */
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 {
assert(false && "Measurement Name not yet implemented");
return a_nssai.sst == b_sst && a_nssai.sd == *b_sd;
}
return meas_record;
}
static kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1_in_du(const NR_UE_info_t* UE, const size_t ue_idx, const kpm_act_def_format_1_t * act_def_fr_1)
static arr_ue_id_t filter_ues_by_s_nssai_in_cu(const test_info_lst_t test_info)
{
kpm_ind_msg_format_1_t msg_frm_1 = {0};
// Measurement Data contains a set of Meas Records, each collected at each granularity period
msg_frm_1.meas_data_lst_len = 1; /* this value is equal to (kpm_ric_event_trigger_format_1.report_period_ms/act_def_fr_1->gran_period_ms)
please, check their values in xApp */
assert(test_info.S_NSSAI == TRUE_TEST_COND_TYPE && "Must be true");
assert(test_info.test_cond != NULL && "Must be filled, even though it's optional by the specification");
assert(*test_info.test_cond == EQUAL_TEST_COND && "Currently support EQUAL test condition");
assert(test_info.test_cond_value != NULL && "Must be filled, even though it's optional by the specification");
msg_frm_1.meas_data_lst = calloc(msg_frm_1.meas_data_lst_len, sizeof(*msg_frm_1.meas_data_lst));
assert(msg_frm_1.meas_data_lst != NULL && "Memory exhausted" );
for (size_t i = 0; i<msg_frm_1.meas_data_lst_len; i++) {
// Measurement Record
msg_frm_1.meas_data_lst[i].meas_record_len = act_def_fr_1->meas_info_lst_len; // record data list length corresponds to info list length from action definition
uint8_t sst = 0;
uint32_t *sd = NULL;
capture_sst_sd(test_info.test_cond_value, &sst, &sd);
msg_frm_1.meas_data_lst[i].meas_record_lst = calloc(msg_frm_1.meas_data_lst[i].meas_record_len, sizeof(meas_record_lst_t));
assert(msg_frm_1.meas_data_lst[i].meas_record_lst != NULL && "Memory exhausted");
arr_ue_id_t arr_ue_id = {.sz = 0};
arr_ue_id.ue_id = calloc(MAX_MOBILES_PER_GNB, sizeof(ue_id_e2sm_t));
assert(arr_ue_id.ue_id != NULL);
for (size_t j = 0; j < msg_frm_1.meas_data_lst[i].meas_record_len; j++) { // each meas record corresponds to one meas type
// Measurement Type as requested in Action Definition
switch (act_def_fr_1->meas_info_lst[j].meas_type.type) {
case NAME_MEAS_TYPE: {
char meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len + 1];
memcpy(meas_info_name_str, act_def_fr_1->meas_info_lst[j].meas_type.name.buf, act_def_fr_1->meas_info_lst[j].meas_type.name.len);
meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len] = '\0';
struct rrc_gNB_ue_context_s* rrc_ue_context = NULL;
RB_FOREACH(rrc_ue_context, rrc_nr_ue_tree_s, &RC.nrrrc[0]->rrc_ue_head) {
gNB_RRC_UE_t *ue = &rrc_ue_context->ue_context;
for (int p = 0; p < ue->nb_of_pdusessions; ++p) {
pdusession_t *pdu = &ue->pduSession[p].param;
if (nssai_matches(pdu->nssai, sst, sd)) {
arr_ue_id.ue_id[arr_ue_id.sz] = fill_ue_id_data[ngran_gNB_CU](ue, 0, 0);
msg_frm_1.meas_data_lst[i].meas_record_lst[j] = fill_ue_mac_rlc_data(UE, ue_idx, meas_info_name_str, act_def_fr_1->gran_period_ms);
arr_ue_id.sz++;
break;
}
case ID_MEAS_TYPE:
assert(false && "ID Measurement Type not yet implemented");
default:
assert(false && "Measurement Type not recognized");
}
}
AssertFatal(arr_ue_id.sz < MAX_MOBILES_PER_GNB, "cannot have more UEs than global UE number maximum\n");
}
// Measurement Information - OPTIONAL
msg_frm_1.meas_info_lst_len = act_def_fr_1->meas_info_lst_len;
msg_frm_1.meas_info_lst = calloc(msg_frm_1.meas_info_lst_len, sizeof(meas_info_format_1_lst_t));
assert(msg_frm_1.meas_info_lst != NULL && "Memory exhausted");
for (size_t i = 0; i < msg_frm_1.meas_info_lst_len; i++) {
msg_frm_1.meas_info_lst[i] = cp_meas_info_format_1_lst(&act_def_fr_1->meas_info_lst[i]);
}
free(sd); // if NULL, nothing happens
return msg_frm_1;
return arr_ue_id;
}
static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3_in_du(const matched_ues_mac_t* matched_ues,
const kpm_act_def_format_1_t* act_def_fr_1)
static arr_ue_id_t filter_ues_by_s_nssai_in_cuup(const test_info_lst_t test_info)
{
assert(act_def_fr_1 != NULL);
kpm_ind_msg_format_3_t msg_frm_3 = {0};
// Fill UE Measurement Reports
msg_frm_3.ue_meas_report_lst_len = matched_ues->num_ues;
msg_frm_3.meas_report_per_ue = calloc(msg_frm_3.ue_meas_report_lst_len, sizeof(meas_report_per_ue_t));
assert(msg_frm_3.meas_report_per_ue != NULL && "Memory exhausted");
for (size_t i = 0; i<msg_frm_3.ue_meas_report_lst_len; i++) {
// Fill UE ID data
f1_ue_data_t rrc_ue_id = du_get_f1_ue_data(matched_ues->ue_list[i]->rnti); // get gNB CU UE ID as rrc_ue_id
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.type = GNB_DU_UE_ID_E2SM;
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.gnb_du = fill_gnb_du_data(&rrc_ue_id);
// Fill UE related info
msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 = fill_kpm_ind_msg_frm_1_in_du(matched_ues->ue_list[i], i, act_def_fr_1);
}
return msg_frm_3;
}
static kpm_ind_msg_format_1_t fill_kpm_ind_msg_frm_1_in_monolithic(const NR_UE_info_t* UE, const size_t ue_idx, const uint32_t rrc_ue_id, const kpm_act_def_format_1_t * act_def_fr_1)
{
kpm_ind_msg_format_1_t msg_frm_1 = {0};
// Measurement Data contains a set of Meas Records, each collected at each granularity period
msg_frm_1.meas_data_lst_len = 1; /* this value is equal to (kpm_ric_event_trigger_format_1.report_period_ms/act_def_fr_1->gran_period_ms)
please, check their values in xApp */
assert(test_info.S_NSSAI == TRUE_TEST_COND_TYPE && "Must be true");
assert(test_info.test_cond != NULL && "Must be filled, even though it's optional by the specification");
assert(*test_info.test_cond == EQUAL_TEST_COND && "Currently support EQUAL test condition");
assert(test_info.test_cond_value != NULL && "Must be filled, even though it's optional by the specification");
msg_frm_1.meas_data_lst = calloc(msg_frm_1.meas_data_lst_len, sizeof(*msg_frm_1.meas_data_lst));
assert(msg_frm_1.meas_data_lst != NULL && "Memory exhausted" );
for (size_t i = 0; i<msg_frm_1.meas_data_lst_len; i++) {
// Measurement Record
msg_frm_1.meas_data_lst[i].meas_record_len = act_def_fr_1->meas_info_lst_len; // record data list length corresponds to info list length from action definition
uint8_t sst = 0;
uint32_t *sd = NULL;
capture_sst_sd(test_info.test_cond_value, &sst, &sd);
msg_frm_1.meas_data_lst[i].meas_record_lst = calloc(msg_frm_1.meas_data_lst[i].meas_record_len, sizeof(meas_record_lst_t));
assert(msg_frm_1.meas_data_lst[i].meas_record_lst != NULL && "Memory exhausted");
arr_ue_id_t arr_ue_id = {.sz = 0};
arr_ue_id.ue_id = calloc(MAX_MOBILES_PER_GNB, sizeof(ue_id_e2sm_t));
assert(arr_ue_id.ue_id != NULL);
for (size_t j = 0; j < msg_frm_1.meas_data_lst[i].meas_record_len; j++) { // each meas record corresponds to one meas type
// Measurement Type as requested in Action Definition
switch (act_def_fr_1->meas_info_lst[j].meas_type.type) {
case NAME_MEAS_TYPE: {
char meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len + 1];
memcpy(meas_info_name_str, act_def_fr_1->meas_info_lst[j].meas_type.name.buf, act_def_fr_1->meas_info_lst[j].meas_type.name.len);
meas_info_name_str[act_def_fr_1->meas_info_lst[j].meas_type.name.len] = '\0';
// Get NSSAI info from E1 context
const instance_t e1_inst = 0;
const e1ap_upcp_inst_t *e1inst = getCxtE1(e1_inst);
if (strcmp(meas_info_name_str, "DRB.PdcpSduVolumeDL") == 0 || strcmp(meas_info_name_str, "DRB.PdcpSduVolumeUL") == 0) {
msg_frm_1.meas_data_lst[i].meas_record_lst[j] = fill_ue_pdcp_data(rrc_ue_id, ue_idx, meas_info_name_str);
} else {
msg_frm_1.meas_data_lst[i].meas_record_lst[j] = fill_ue_mac_rlc_data(UE, ue_idx, meas_info_name_str, act_def_fr_1->gran_period_ms);
}
break;
}
case ID_MEAS_TYPE:
assert(false && "ID Measurement Type not yet implemented");
// currently the CU-UP does not store the slices, so we can only do this "coarse filtering"
for(int s = 0; s < e1inst->cuup.setupReq.plmn[0].supported_slices; s++) {
if (nssai_matches(e1inst->cuup.setupReq.plmn[0].slice[s], sst, sd)) {
ue_id_t *pdcp_ue_id_list = calloc(MAX_MOBILES_PER_GNB, sizeof(ue_id_t));
assert(pdcp_ue_id_list != NULL);
default:
assert(false && "Measurement Type not recognized");
arr_ue_id.sz = nr_pdcp_get_num_ues(pdcp_ue_id_list, MAX_MOBILES_PER_GNB);
for (size_t i = 0; i < arr_ue_id.sz; i++) {
arr_ue_id.ue_id[i] = fill_ue_id_data[ngran_gNB_CUUP](NULL, 0, pdcp_ue_id_list[i]);
}
free(pdcp_ue_id_list);
}
}
// Measurement Information - OPTIONAL
msg_frm_1.meas_info_lst_len = act_def_fr_1->meas_info_lst_len;
msg_frm_1.meas_info_lst = calloc(msg_frm_1.meas_info_lst_len, sizeof(meas_info_format_1_lst_t));
assert(msg_frm_1.meas_info_lst != NULL && "Memory exhausted");
for (size_t i = 0; i < msg_frm_1.meas_info_lst_len; i++) {
msg_frm_1.meas_info_lst[i] = cp_meas_info_format_1_lst(&act_def_fr_1->meas_info_lst[i]);
}
free(sd); // if NULL, nothing happens
return msg_frm_1;
return arr_ue_id;
}
static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3_in_monolithic(const matched_ues_mac_t* matched_ues,
const kpm_act_def_format_1_t* act_def_fr_1)
static arr_ue_id_t filter_ues_by_s_nssai_in_du_or_monolithic(const test_info_lst_t test_info)
{
assert(act_def_fr_1 != NULL);
kpm_ind_msg_format_3_t msg_frm_3 = {0};
// Fill UE Measurement Reports
msg_frm_3.ue_meas_report_lst_len = matched_ues->num_ues;
msg_frm_3.meas_report_per_ue = calloc(msg_frm_3.ue_meas_report_lst_len, sizeof(meas_report_per_ue_t));
assert(msg_frm_3.meas_report_per_ue != NULL && "Memory exhausted");
assert(test_info.S_NSSAI == TRUE_TEST_COND_TYPE && "Must be true");
assert(test_info.test_cond != NULL && "Must be filled, even though it's optional by the specification");
assert(*test_info.test_cond == EQUAL_TEST_COND && "Currently support EQUAL test condition");
assert(test_info.test_cond_value != NULL && "Must be filled, even though it's optional by the specification");
uint8_t sst = 0;
uint32_t *sd = NULL;
capture_sst_sd(test_info.test_cond_value, &sst, &sd);
for (size_t i = 0; i<msg_frm_3.ue_meas_report_lst_len; i++) {
// Fill UE ID data
rrc_gNB_ue_context_t* rrc_ue_context_list = rrc_gNB_get_ue_context_by_rnti_any_du(RC.nrrrc[0], matched_ues->ue_list[i]->rnti);
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.type = GNB_UE_ID_E2SM;
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.gnb = fill_gnb_data(rrc_ue_context_list);
// Fill UE related info
msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 =
fill_kpm_ind_msg_frm_1_in_monolithic(matched_ues->ue_list[i], i, rrc_ue_context_list->ue_context.rrc_ue_id, act_def_fr_1);
}
arr_ue_id_t arr_ue_id = {.sz = 0};
arr_ue_id.ue_id = calloc(MAX_MOBILES_PER_GNB, sizeof(ue_id_e2sm_t));
assert(arr_ue_id.ue_id != NULL);
return msg_frm_3;
}
#endif
arr_ue_id.ue_info_list = calloc(MAX_MOBILES_PER_GNB, sizeof(NR_UE_info_t));
assert(arr_ue_id.ue_info_list != NULL);
#if defined (NGRAN_GNB_CUCP)
typedef struct {
uint32_t rrc_ue_id_list[MAX_MOBILES_PER_GNB]; // list of matched UEs on RRC level containing only rrc_ue_id (gNB CU UE ID)
size_t num_ues;
const ngran_node_t node_type = get_e2_node_type();
} matched_ues_rrc_t;
// Take MAC info
UE_iterator (RC.nrmac[0]->UE_info.list, ue) {
NR_UE_sched_ctrl_t *sched_ctrl = &ue->UE_sched_ctrl;
// UE matches if any of its DRBs matches
for (int l = 0; l < sched_ctrl->dl_lc_num; ++l) {
long lcid = sched_ctrl->dl_lc_ids[l];
if (nssai_matches(sched_ctrl->dl_lc_nssai[lcid], sst, sd)) {
if (node_type == ngran_gNB_DU) {
f1_ue_data_t rrc_ue_id = du_get_f1_ue_data(ue->rnti); // gNB CU UE ID = rrc_ue_id
arr_ue_id.ue_id[arr_ue_id.sz] = fill_ue_id_data[ngran_gNB_DU](NULL, rrc_ue_id.secondary_ue, 0);
} else {
rrc_gNB_ue_context_t* rrc_ue_context = rrc_gNB_get_ue_context_by_rnti(RC.nrrrc[0], -1, ue->rnti);
arr_ue_id.ue_id[arr_ue_id.sz] = fill_ue_id_data[ngran_gNB](&rrc_ue_context->ue_context, 0, 0);
}
static size_t filter_ues_by_s_nssai_in_cu(test_cond_e const condition, uint8_t sst, const uint32_t *sd, matched_ues_rrc_t* matches)
{
DevAssert(matches != NULL);
AssertFatal(condition == EQUAL_TEST_COND, "Condition %d not yet implemented\n", condition);
// store NR_UE_info_t
arr_ue_id.ue_info_list[arr_ue_id.sz] = *ue;
struct rrc_gNB_ue_context_s *ue_context_p1 = NULL;
size_t i = 0;
RB_FOREACH(ue_context_p1, rrc_nr_ue_tree_s, &RC.nrrrc[0]->rrc_ue_head) {
gNB_RRC_UE_t *ue = &ue_context_p1->ue_context;
for (int p = 0; p < ue->nb_of_pdusessions; ++p) {
pdusession_t *pdu = &ue->pduSession[0].param;
if (nssai_matches(pdu->nssai, sst, sd)) {
matches->rrc_ue_id_list[i++] = ue_context_p1->ue_context.rrc_ue_id;
arr_ue_id.sz++;
break;
}
}
AssertFatal(i < MAX_MOBILES_PER_GNB, "cannot have more UEs than global UE number maximum\n");
AssertFatal(arr_ue_id.sz < MAX_MOBILES_PER_GNB, "cannot have more UEs than global UE number maximum\n");
}
matches->num_ues = i;
return i;
}
static kpm_ind_msg_format_3_t fill_kpm_ind_msg_frm_3_in_cu(const matched_ues_rrc_t* matched_ues,
const kpm_act_def_format_1_t* act_def_fr_1)
{
assert(act_def_fr_1 != NULL);
kpm_ind_msg_format_3_t msg_frm_3 = {0};
// Fill UE Measurement Reports
free(sd); // if NULL, nothing happens
msg_frm_3.ue_meas_report_lst_len = matched_ues->num_ues;
msg_frm_3.meas_report_per_ue = calloc(msg_frm_3.ue_meas_report_lst_len, sizeof(meas_report_per_ue_t));
assert(msg_frm_3.meas_report_per_ue != NULL && "Memory exhausted");
return arr_ue_id;
}
for (size_t i = 0; i<msg_frm_3.ue_meas_report_lst_len; i++) {
// Fill UE ID data
rrc_gNB_ue_context_t* rrc_ue_context_list = rrc_gNB_get_ue_context(RC.nrrrc[0], matched_ues->rrc_ue_id_list[i]);
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.type = GNB_UE_ID_E2SM;
msg_frm_3.meas_report_per_ue[i].ue_meas_report_lst.gnb = fill_gnb_data(rrc_ue_context_list);
typedef arr_ue_id_t (*ue_type_matcher)(const test_info_lst_t test_info);
// Fill UE related info
msg_frm_3.meas_report_per_ue[i].ind_msg_format_1 =
fill_kpm_ind_msg_frm_1_in_cu(matched_ues->rrc_ue_id_list[i], i, act_def_fr_1);
}
static ue_type_matcher match_s_nssai_test_cond_type[END_NGRAN_NODE_TYPE] =
{
NULL,
NULL,
filter_ues_by_s_nssai_in_du_or_monolithic,
NULL,
NULL,
filter_ues_by_s_nssai_in_cu,
NULL,
filter_ues_by_s_nssai_in_du_or_monolithic,
NULL,
NULL, // for CU-CP we don't use this
filter_ues_by_s_nssai_in_cuup,
};
typedef ue_type_matcher (*filter_ues)[END_NGRAN_NODE_TYPE];
static filter_ues match_cond_arr[END_TEST_COND_TYPE_KPM_V2_01] = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&match_s_nssai_test_cond_type,
};
return msg_frm_3;
}
#endif
static kpm_ric_ind_hdr_format_1_t kpm_ind_hdr_frm_1(void)
{
......@@ -627,60 +360,10 @@ static kpm_ric_ind_hdr_format_1_t kpm_ind_hdr_frm_1(void)
hdr_frm_1.fileformat_version = NULL;
// Check E2 Node NG-RAN Type
#if defined (NGRAN_GNB_DU)
if (RC.nrrrc[0]->node_type == ngran_gNB_DU) {
hdr_frm_1.sender_name = calloc(1, sizeof(byte_array_t));
hdr_frm_1.sender_name->buf = calloc(strlen("OAI-DU") + 1, sizeof(char));
memcpy(hdr_frm_1.sender_name->buf, "OAI-DU", strlen("OAI-DU"));
hdr_frm_1.sender_name->len = strlen("OAI-DU");
hdr_frm_1.sender_type = calloc(1, sizeof(byte_array_t));
hdr_frm_1.sender_type->buf = calloc(strlen("DU") + 1, sizeof(char));
memcpy(hdr_frm_1.sender_type->buf, "DU", strlen("DU"));
hdr_frm_1.sender_type->len = strlen("DU");
} else if (RC.nrrrc[0]->node_type == ngran_gNB_CU) {
hdr_frm_1.sender_name = calloc(1, sizeof(byte_array_t));
hdr_frm_1.sender_name->buf = calloc(strlen("OAI-CU") + 1, sizeof(char));
memcpy(hdr_frm_1.sender_name->buf, "OAI-CU", strlen("OAI-CU"));
hdr_frm_1.sender_name->len = strlen("OAI-CU");
hdr_frm_1.sender_type = calloc(1, sizeof(byte_array_t));
hdr_frm_1.sender_type->buf = calloc(strlen("CU") + 1, sizeof(char));
memcpy(hdr_frm_1.sender_type->buf, "CU", strlen("CU"));
hdr_frm_1.sender_type->len = strlen("CU");
} else if (RC.nrrrc[0]->node_type == ngran_gNB) {
hdr_frm_1.sender_name = calloc(1, sizeof(byte_array_t));
hdr_frm_1.sender_name->buf = calloc(strlen("OAI-MONO") + 1, sizeof(char));
memcpy(hdr_frm_1.sender_name->buf, "OAI-MONO", strlen("OAI-MONO"));
hdr_frm_1.sender_name->len = strlen("OAI-MONO");
hdr_frm_1.sender_type = calloc(1, sizeof(byte_array_t));
hdr_frm_1.sender_type->buf = calloc(strlen("MONO") + 1, sizeof(char));
memcpy(hdr_frm_1.sender_type->buf, "MONO", strlen("MONO"));
hdr_frm_1.sender_type->len = strlen("MONO");
} else if (RC.nrrrc[0]->node_type == ngran_gNB_CUCP) {
return hdr_frm_1; // nothing to fill
} else {
assert(0!=0 && "Unknown node type");
}
#endif
#if defined (NGRAN_GNB_CUUP)
const ngran_node_t node_type = get_e2_node_type();
const char* sender_name = get_ngran_name(node_type);
hdr_frm_1.sender_name = calloc(1, sizeof(byte_array_t));
hdr_frm_1.sender_name->buf = calloc(strlen("OAI-CU-UP") + 1, sizeof(char));
memcpy(hdr_frm_1.sender_name->buf, "OAI-CU-UP", strlen("OAI-CU-UP"));
hdr_frm_1.sender_name->len = strlen("OAI-CU-UP");
hdr_frm_1.sender_type = calloc(1, sizeof(byte_array_t));
hdr_frm_1.sender_type->buf = calloc(strlen("CU-UP") + 1, sizeof(char));
memcpy(hdr_frm_1.sender_type->buf, "CU-UP", strlen("CU-UP"));
hdr_frm_1.sender_type->len = strlen("CU-UP");
#endif
hdr_frm_1.vendor_name = calloc(1, sizeof(byte_array_t));
hdr_frm_1.vendor_name->buf = calloc(strlen("OAI") + 1, sizeof(char));
memcpy(hdr_frm_1.vendor_name->buf, "OAI", strlen("OAI"));
hdr_frm_1.vendor_name->len = strlen("OAI");
*hdr_frm_1.sender_name = cp_str_to_ba(sender_name);
return hdr_frm_1;
}
......@@ -695,40 +378,6 @@ kpm_ind_hdr_t kpm_ind_hdr(void)
return hdr;
}
static void capture_sst_sd(test_info_lst_t* test, uint8_t *sst, uint32_t **sd)
{
DevAssert(test != NULL && test->test_cond_value != NULL);
DevAssert(sst != NULL);
DevAssert(sd != NULL);
// we made a mistake in the past: NSSAI is supposed to be an OCTET_STRING,
// but earlier version of the RAN function and the xApp used integer, so
// handle this gracefully by accepting integer as well
switch (test->test_cond_value->type) {
case INTEGER_TEST_COND_VALUE: {
AssertFatal(*test->test_cond_value->int_value <= 0xff, "illegal SST %ld\n", *test->test_cond_value->int_value);
*sst = *test->test_cond_value->int_value;
*sd = NULL;
break;
}
case OCTET_STRING_TEST_COND_VALUE: {
if (test->test_cond_value->octet_string_value->len == 1) {
*sst = test->test_cond_value->octet_string_value->buf[0];
*sd = NULL;
} else {
DevAssert(test->test_cond_value->octet_string_value->len == 4);
uint8_t *buf = test->test_cond_value->octet_string_value->buf;
*sst = buf[0];
*sd = malloc(**sd);
**sd = buf[1] << 16 | buf[2] << 8 | buf[3];
}
break;
}
default:
AssertFatal(false, "test condition value %d impossible\n", test->test_cond_value->type);
}
}
bool read_kpm_sm(void* data)
{
assert(data != NULL);
......@@ -748,113 +397,17 @@ bool read_kpm_sm(void* data)
// Filter the UE by the test condition criteria
kpm_act_def_format_4_t const* frm_4 = &kpm->act_def->frm_4; // 8.2.1.2.4
for (size_t i = 0; i < frm_4->matching_cond_lst_len; i++) {
switch (frm_4->matching_cond_lst[i].test_info_lst.test_cond_type) {
case GBR_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case AMBR_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case IsStat_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case IsCatM_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case DL_RSRP_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case DL_RSRQ_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case UL_RSRP_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case CQI_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case fiveQI_TEST_COND_TYPE: {
assert(0 != 0 && "Not implemented");
break;
}
case QCI_TEST_COND_TYPE: {
;
assert(0 != 0 && "Not implemented");
break;
}
case S_NSSAI_TEST_COND_TYPE: {
assert(frm_4->matching_cond_lst[i].test_info_lst.S_NSSAI == TRUE_TEST_COND_TYPE && "Must be true");
assert(frm_4->matching_cond_lst[i].test_info_lst.test_cond != NULL && "Even though is optional..");
assert(frm_4->matching_cond_lst[i].test_info_lst.test_cond_value != NULL && "Even though is optional..");
test_cond_e const test_cond = *frm_4->matching_cond_lst[i].test_info_lst.test_cond;
uint8_t sst = 0;
uint32_t *sd = NULL;
capture_sst_sd(&frm_4->matching_cond_lst[i].test_info_lst, &sst, &sd);
// Check E2 Node NG-RAN Type
/* 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 */
#if defined (NGRAN_GNB_DU)
if (RC.nrrrc[0]->node_type == ngran_gNB_DU) {
matched_ues_mac_t matched_ues = {0};
matched_ues.num_ues = filter_ues_by_s_nssai_in_du_or_monolithic(test_cond, sst, sd, &matched_ues);
if (matched_ues.num_ues == 0) return false;
kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_du(&matched_ues, &frm_4->action_def_format_1);
} else if (RC.nrrrc[0]->node_type == ngran_gNB_CU) {
matched_ues_rrc_t matched_ues = {0};
matched_ues.num_ues = filter_ues_by_s_nssai_in_cu(test_cond, sst, sd, &matched_ues);
if (matched_ues.num_ues == 0) return false;
kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_cu(&matched_ues, &frm_4->action_def_format_1);
} else if (RC.nrrrc[0]->node_type == ngran_gNB) {
matched_ues_mac_t matched_ues = {0};
matched_ues.num_ues = filter_ues_by_s_nssai_in_du_or_monolithic(test_cond, sst, sd, &matched_ues);
if (matched_ues.num_ues == 0) return false;
kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_monolithic(&matched_ues, &frm_4->action_def_format_1);
} else if (RC.nrrrc[0]->node_type == ngran_gNB_CUCP) {
return false; // at the moment, measurements regarding CU-CP node are not yet implemented
} else {
assert(false && "NG-RAN Type not implemented");
}
#elif defined (NGRAN_GNB_CUUP)
matched_ues_pdcp_t matched_ues = {0};
matched_ues.num_ues = filter_ues_by_s_nssai_in_cuup(test_cond, sst, sd, &matched_ues);
if (matched_ues.num_ues == 0) return false;
kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3_in_cuup(&matched_ues, &frm_4->action_def_format_1);
#endif
free(sd); // if NULL, nothing happens
break;
}
default:
assert(false && "Unknown Test condition");
}
}
const test_info_lst_t test_info = frm_4->matching_cond_lst[i].test_info_lst;
const ngran_node_t node_type = get_e2_node_type();
/* Based on the node type (gNB-mono, CU, DU,...) and matching condition (S-NSSAI, DL RSRP, GBR,...)
get array of E2SM UE IDs with optionally their MAC info */
ue_type_matcher fp_match_cond_type = (*match_cond_arr[test_info.test_cond_type])[node_type];
arr_ue_id_t arr_ue_id = fp_match_cond_type(test_info);
if (arr_ue_id.sz == 0) return false;
kpm->ind.msg.frm_3 = fill_kpm_ind_msg_frm_3(&arr_ue_id, &frm_4->action_def_format_1);
}
break;
}
......@@ -866,13 +419,117 @@ bool read_kpm_sm(void* data)
return true;
}
static const char* kpm_meas_du[] = {
"DRB.RlcSduDelayDl",
"DRB.UEThpDl",
"DRB.UEThpUl",
"RRU.PrbTotDl",
"RRU.PrbTotUl",
};
static const char* kpm_meas_gnb[] = {
"DRB.PdcpSduVolumeDL",
"DRB.PdcpSduVolumeUL",
"DRB.RlcSduDelayDl",
"DRB.UEThpDl",
"DRB.UEThpUl",
"RRU.PrbTotDl",
"RRU.PrbTotUl",
};
static const char* kpm_meas_cuup[] = {
"DRB.PdcpSduVolumeDL",
"DRB.PdcpSduVolumeUL",
};
typedef const char** meas_list;
static const meas_list ran_def_kpm[END_NGRAN_NODE_TYPE] = {
NULL,
NULL,
kpm_meas_gnb,
NULL,
NULL,
kpm_meas_cuup, // at the moment, for CU, we use the same function as for CU-UP
NULL,
kpm_meas_du,
NULL,
NULL, // at the moment, no measurement is implemented in CU-CP
kpm_meas_cuup,
};
void read_kpm_setup_sm(void* e2ap)
{
assert(e2ap != NULL);
// assert(e2ap->type == KPM_V3_0_AGENT_IF_E2_SETUP_ANS_V0);
kpm_e2_setup_t* kpm = (kpm_e2_setup_t*)(e2ap);
kpm->ran_func_def = fill_rnd_kpm_ran_func_def();
/* Fill the RAN Function Definition with currently supported measurements */
// RAN Function Name is already filled in fill_ran_function_name() in kpm_sm_agent.c
// Fill supported measurements depending on the E2 node
// [1, 65535]
// 3GPP TS 28.552
const ngran_node_t node_type = get_e2_node_type();
const char** kpm_meas = ran_def_kpm[node_type];
if (kpm_meas == NULL) return; // e.g. CU-CP node, doesn't support any measurements
size_t sz = 0;
while (kpm_meas[sz] != NULL) {
sz++;
}
// Sequence of Report styles
const size_t sz_report = 1;
kpm->ran_func_def.sz_ric_report_style_list = sz_report;
kpm->ran_func_def.ric_report_style_list = calloc(sz_report, sizeof(ric_report_style_item_t));
assert(kpm->ran_func_def.ric_report_style_list != NULL && "Memory exhausted");
ric_report_style_item_t* report_item = &kpm->ran_func_def.ric_report_style_list[0];
report_item->report_style_type = STYLE_4_RIC_SERVICE_REPORT;
const char report_style_name[] = "Common Condition-based, UE-level Measurement";
report_item->report_style_name = cp_str_to_ba(report_style_name);
report_item->act_def_format_type = FORMAT_4_ACTION_DEFINITION;
report_item->meas_info_for_action_lst_len = sz;
report_item->meas_info_for_action_lst = calloc(sz, sizeof(meas_info_for_action_lst_t));
assert(report_item->meas_info_for_action_lst != NULL && "Memory exhausted");
for(size_t i = 0; i < sz; i++) {
meas_info_for_action_lst_t* meas_item = &report_item->meas_info_for_action_lst[i];
// 8.3.9
meas_item->name = cp_str_to_ba(kpm_meas[i]);
// 8.3.10 - OPTIONAL
meas_item->id = NULL;
// 8.3.26 - OPTIONAL
meas_item->bin_range_def = NULL;
}
// Sequence of Event Trigger styles
const size_t sz_ev_tr = 1;
kpm->ran_func_def.sz_ric_event_trigger_style_list = sz_ev_tr;
kpm->ran_func_def.ric_event_trigger_style_list = calloc(sz_ev_tr, sizeof(ric_event_trigger_style_item_t));
assert(kpm->ran_func_def.ric_event_trigger_style_list != NULL && "Memory exhausted");
ric_event_trigger_style_item_t* ev_tr_item = &kpm->ran_func_def.ric_event_trigger_style_list[0];
ev_tr_item->style_type = STYLE_1_RIC_EVENT_TRIGGER;
const char ev_style_name[] = "Periodic Report";
ev_tr_item->style_name = cp_str_to_ba(ev_style_name);
ev_tr_item->format_type = FORMAT_1_RIC_EVENT_TRIGGER;
// Supported RIC Indication Formats
report_item->ind_hdr_format_type = FORMAT_1_INDICATION_HEADER; // 8.3.5
report_item->ind_msg_format_type = FORMAT_3_INDICATION_MESSAGE; // 8.3.5
// E2 Setup Request is sent periodically until the connection is established
// RC subscritpion data should be initialized only once
const int ret = pthread_once(&once_kpm_mutex, init_once_kpm);
DevAssert(ret == 0);
}
sm_ag_if_ans_t write_ctrl_kpm_sm(void const* src)
......
/*
* 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
};
void init_kpm_subs_data(void)
{
const size_t ht_len = sizeof(lst_measure) / sizeof(lst_measure[0]);
hcreate(ht_len);
ENTRY kv_pair;
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;
}
/*
* 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment