Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions common/utils/system.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,9 @@ void threadCreate(pthread_t* t, void * (*func)(void*), void * param, char* name,
strncpy(short_name, name, sizeof(short_name) - 1);
short_name[sizeof(short_name) - 1] = '\0';
ret = pthread_setname_np(*t, short_name);
AssertFatal(ret == 0, "Error in pthread_setname_np(): ret: %d, errno: %d\n", ret, errno);

if (ret != 0) {
LOG_E(UTIL, "Error in pthread_setname_np() for %s: ret: %d, errno: %d\n", short_name, ret, errno);
}
if (affinity != -1 ) {
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
Expand Down
35 changes: 32 additions & 3 deletions nfapi/oai_integration/nfapi_pnf.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,13 @@ typedef struct {
uint8_t first_subframe_ind;

// timing information recevied from the vnf
uint8_t timing_window;
uint16_t timing_window;
uint8_t timing_info_mode;
uint8_t timing_info_period;
uint32_t dl_tti_timing_offset;
uint32_t ul_tti_timing_offset;
uint32_t ul_dci_timing_offset;
uint32_t tx_data_timing_offset;

} phy_info;

Expand Down Expand Up @@ -1000,7 +1004,22 @@ int nr_config_request(nfapi_pnf_config_t *config, nfapi_pnf_phy_config_t *phy, n
phy_info->timing_info_mode = 0;
printf("NO timing info mode provided\n");
}
// TODO: Read the P7 message offset values
if (req->nfapi_config.dl_tti_timing_offset.tl.tag == NFAPI_NR_NFAPI_DL_TTI_TIMING_OFFSET) {
phy_info->dl_tti_timing_offset = req->nfapi_config.dl_tti_timing_offset.value;
num_tlv++;
}
if (req->nfapi_config.ul_tti_timing_offset.tl.tag == NFAPI_NR_NFAPI_UL_TTI_TIMING_OFFSET) {
phy_info->ul_tti_timing_offset = req->nfapi_config.ul_tti_timing_offset.value;
num_tlv++;
}
if (req->nfapi_config.ul_dci_timing_offset.tl.tag == NFAPI_NR_NFAPI_UL_DCI_TIMING_OFFSET) {
phy_info->ul_dci_timing_offset = req->nfapi_config.ul_dci_timing_offset.value;
num_tlv++;
}
if (req->nfapi_config.tx_data_timing_offset.tl.tag == NFAPI_NR_NFAPI_TX_DATA_TIMING_OFFSET) {
phy_info->tx_data_timing_offset = req->nfapi_config.tx_data_timing_offset.value;
num_tlv++;
}
if (req->nfapi_config.timing_info_period.tl.tag == NFAPI_NR_NFAPI_TIMING_INFO_PERIOD_TAG) {
printf("timing info period provided value:%d\n", req->nfapi_config.timing_info_period.value);
phy_info->timing_info_period = req->nfapi_config.timing_info_period.value;
Expand Down Expand Up @@ -1663,10 +1682,13 @@ int nr_start_request(nfapi_pnf_config_t *config, nfapi_pnf_phy_config_t *phy, nf
p7_config->subframe_buffer_size = phy_info->timing_window;
p7_config->slot_buffer_size = phy_info->timing_window; // TODO: check if correct for NR
printf("subframe_buffer_size configured using phy_info->timing_window:%d\n", phy_info->timing_window);
// Reset timing info defaults from nfapi_pnf_p7_config_create, use VNF config values instead
p7_config->timing_info_mode_periodic = 0;
p7_config->timing_info_mode_aperiodic = 0;
p7_config->timing_info_period = phy_info->timing_info_period;

if (phy_info->timing_info_mode & 0x1) {
p7_config->timing_info_mode_periodic = 1;
p7_config->timing_info_period = phy_info->timing_info_period;
}

if (phy_info->timing_info_mode & 0x2) {
Expand Down Expand Up @@ -1755,6 +1777,11 @@ int nr_start_request(nfapi_pnf_config_t *config, nfapi_pnf_phy_config_t *phy, nf
DevAssert(scs->tl.tag == NFAPI_NR_CONFIG_SCS_COMMON_TAG);
pnf_p7_t* pnf_p7 = (pnf_p7_t*)(p7_config);
pnf_p7->mu = scs->value;
pnf_p7->timing_window = phy_info->timing_window;
pnf_p7->dl_tti_timing_offset = phy_info->dl_tti_timing_offset;
pnf_p7->ul_tti_timing_offset = phy_info->ul_tti_timing_offset;
pnf_p7->ul_dci_timing_offset = phy_info->ul_dci_timing_offset;
pnf_p7->tx_data_timing_offset = phy_info->tx_data_timing_offset;

// Need to wait for main thread to create RU structures
while (config_sync_var < 0) {
Expand Down Expand Up @@ -2349,8 +2376,10 @@ void handle_nr_slot_ind(uint16_t sfn, uint16_t slot, NR_Sched_Rsp_t *sched_resp)
sfnslot_add_slot(mu, &sfn_tx, &slot_tx, slot_ahead); // modify: do in place

// printf("send slot indication for sfn/slot:%4d.%2d current:%4d.%2d\n", sfn_tx, slot_tx, sfn, slot);
#ifdef ENABLE_WLS
nfapi_nr_slot_indication_scf_t ind = {.sfn = sfn_tx, .slot = slot_tx};
oai_nfapi_nr_slot_indication(&ind);
#endif

// copy data from appropriate p7 slot buffers into channel structures for PHY processing
nfapi_pnf_p7_get_msgs(config,
Expand Down
198 changes: 185 additions & 13 deletions nfapi/oai_integration/nfapi_vnf.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <string.h>
#include <stdarg.h>
#include <pthread.h>
#include <sched.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/socket.h>
Expand Down Expand Up @@ -56,6 +57,8 @@ static nfapi_vnf_config_t *config;
extern RAN_CONTEXT_t RC;
extern UL_RCC_IND_t UL_RCC_INFO;

static int nr_start_resp_received = 0;

nfapi_vnf_config_t * get_config()
{
return config;
Expand Down Expand Up @@ -941,15 +944,15 @@ int phy_nr_slot_indication(nfapi_nr_slot_indication_scf_t *ind)
oai_fapi_send_end_request(ind->sfn, ind->slot);
}
#else
if (sched_response.TX_req.Number_of_PDUs > 0)
oai_nfapi_tx_data_req(&sched_response.TX_req);

if (sched_response.DL_req.dl_tti_request_body.nPDUs > 0)
oai_nfapi_dl_tti_req(&sched_response.DL_req);

if (sched_response.UL_tti_req.n_pdus > 0)
oai_nfapi_ul_tti_req(&sched_response.UL_tti_req);

if (sched_response.TX_req.Number_of_PDUs > 0)
oai_nfapi_tx_data_req(&sched_response.TX_req);

Comment on lines +947 to -952

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to change the message order here?
in SCF222.10.04 Figure 2-21 you can see it is in the order that was defined, and in SCF225.3.0 Section 2.1.3.5 it states that the message order defined in FAPI also applies for nFAPI.

if (sched_response.UL_dci_req.numPdus > 0)
oai_nfapi_ul_dci_req(&sched_response.UL_dci_req);
#endif
Expand All @@ -964,6 +967,153 @@ int phy_nr_slot_indication(nfapi_nr_slot_indication_scf_t *ind)
return 1;
}

#ifndef ENABLE_WLS
static inline void timespec_add_us(struct timespec *t, long us)
{
t->tv_nsec += us * 1000;
if (t->tv_nsec >= 1000000000) {
t->tv_sec += t->tv_nsec / 1000000000;
t->tv_nsec %= 1000000000;
} else if (t->tv_nsec < 0) {
long sec_diff = (-t->tv_nsec / 1000000000) + 1;
t->tv_sec -= sec_diff;
t->tv_nsec += sec_diff * 1000000000;
}
}
#define P7_SYNC_PERIOD_SLOTS_DEFAULT 80
#define P7_SYNC_MAX_CATCHUP_BURST 2
int vnf_nr_build_send_dl_node_sync(vnf_p7_t* vnf_p7, nfapi_vnf_p7_connection_info_t* p7_info);

static inline void p7_sync_init(nfapi_vnf_p7_connection_info_t *p7_info)
{
p7_info->sync_slot_counter = 0;
p7_info->sync_period_slots = P7_SYNC_PERIOD_SLOTS_DEFAULT;
p7_info->consecutive_drift_violations = 0;
p7_info->nr_offset_filtered = 0;
NFAPI_TRACE(NFAPI_TRACE_INFO, "[P7_SYNC] Initialized: period=%u slots\n",
p7_info->sync_period_slots);
}

void *vnf_timing_thread(void *arg)
{
vnf_p7_info *p7_vnf = (vnf_p7_info *)arg;
vnf_p7_t *vnf_p7 = (vnf_p7_t *)p7_vnf->config;

int mu = -1;
nfapi_vnf_p7_connection_info_t *p7_info = NULL;

while (1) {
if (__atomic_load_n(&nr_start_resp_received, __ATOMIC_ACQUIRE)) {
if (vnf_p7->p7_connections) {
p7_info = vnf_p7->p7_connections;
if (RC.nrmac && RC.nrmac[0]) {
nfapi_nr_config_request_scf_t *req = &RC.nrmac[0]->config[0];
const nfapi_uint8_tlv_t *scs = &req->ssb_config.scs_common;
if (scs && scs->tl.tag == NFAPI_NR_CONFIG_SCS_COMMON_TAG) {
mu = scs->value;
}
}
if (mu < 0 && RC.gNB && RC.gNB[0] && RC.gNB[0]->configured && RC.gNB[0]->frame_parms.numerology_index >= 0) {
mu = RC.gNB[0]->frame_parms.numerology_index;
}
if (mu >= 0) {
break;
}
}
}
usleep(1000000);
}
pthread_mutex_lock(&p7_info->mutex);
while (!p7_info->initial_timinginfo_received) {
pthread_cond_wait(&p7_info->initial_timinginfo_cond, &p7_info->mutex);
}
pthread_mutex_unlock(&p7_info->mutex);
DevAssert(mu >= 0 && mu <= 5);
p7_info->mu = mu;
p7_info->slot_duration_us = 1000 >> p7_info->mu;
LOG_I(NFAPI_VNF, "Starting VNF autonomous timing thread: mu = %d, slot duration = %d us\n", mu, p7_info->slot_duration_us);
if (p7_info->initial_timinginfo_received) {
int sfnslot_dec = NFAPI_SFNSLOT2DEC(p7_info->mu, p7_info->sfn, p7_info->slot);
sfnslot_dec = (sfnslot_dec + 1) % NFAPI_MAX_SFNSLOTDEC(p7_info->mu);
p7_info->sfn = NFAPI_SFNSLOTDEC2SFN(p7_info->mu, sfnslot_dec);
p7_info->slot = NFAPI_SFNSLOTDEC2SLOT(p7_info->mu, sfnslot_dec);
}
p7_info->running = 1;
p7_info->thread = pthread_self();
p7_sync_init(p7_info);
clock_gettime(CLOCK_MONOTONIC, &p7_info->next_slot_time);
vnf_p7->slot_start_time_hr = vnf_get_current_time_hr();
vnf_nr_build_send_dl_node_sync(vnf_p7, p7_info);

const int max_sfnslotdec = NFAPI_MAX_SFNSLOTDEC(p7_info->mu);
int last_mac_ind_dec = -1;

int sfnslot_dec = NFAPI_SFNSLOT2DEC(p7_info->mu, p7_info->sfn, p7_info->slot);

while (p7_info->running) {
pthread_mutex_lock(&p7_info->mutex);
if (p7_info->slot_adjustment != 0) {
sfnslot_dec = (sfnslot_dec + p7_info->slot_adjustment + max_sfnslotdec) % max_sfnslotdec;
p7_info->slot_adjustment = 0;
}
int32_t current_pending_us = p7_info->pending_us;
p7_info->pending_us = 0;
pthread_mutex_unlock(&p7_info->mutex);

timespec_add_us(&p7_info->next_slot_time, p7_info->slot_duration_us + current_pending_us);
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
int64_t diff_ns = (p7_info->next_slot_time.tv_sec - now.tv_sec) * 1000000000LL + (p7_info->next_slot_time.tv_nsec - now.tv_nsec);
const int64_t extreme_lag_threshold_ns = (int64_t)p7_info->slot_duration_us * 5 * 1000LL;
if (diff_ns < -extreme_lag_threshold_ns) {
// next_slot_time is in the past by more than 5 slots!
// Yield CPU to prevent starvation of the SCTP/UDP network thread under extreme lag.
sched_yield();
}
if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &p7_info->next_slot_time, NULL) != 0)
continue;
vnf_p7->slot_start_time_hr = vnf_get_current_time_hr();
pthread_mutex_lock(&p7_info->mutex);
p7_info->sfn = NFAPI_SFNSLOTDEC2SFN(p7_info->mu, sfnslot_dec);
p7_info->slot = NFAPI_SFNSLOTDEC2SLOT(p7_info->mu, sfnslot_dec);
pthread_mutex_unlock(&p7_info->mutex);

if (p7_info->sync_slot_counter >= p7_info->sync_period_slots) {
p7_info->sync_slot_counter = 0;
vnf_nr_build_send_dl_node_sync(vnf_p7, p7_info);
} else {
p7_info->sync_slot_counter++;
}

int32_t slot_ahead = __atomic_load_n(&p7_info->slot_ahead, __ATOMIC_RELAXED);
int target_ind_dec = (sfnslot_dec + slot_ahead) % max_sfnslotdec;
if (last_mac_ind_dec == -1) {
last_mac_ind_dec = (target_ind_dec - 1 + max_sfnslotdec) % max_sfnslotdec;
}

int diff_mac = (target_ind_dec - last_mac_ind_dec + max_sfnslotdec) % max_sfnslotdec;
if (diff_mac > 0 && diff_mac < max_sfnslotdec / 2) {
// NEVER skip slots! Skipping slots breaks MAC scheduling (e.g. RACH, HARQ timing assertions) and drops UE.
// Catch up in smooth bursts up to max_burst. If a huge drift happens during iperf CPU starvation,
// generating backlog sequentially is much safer than jumping.
int burst_counter = 0;
const int max_burst = 10 << p7_info->mu;
while (last_mac_ind_dec != target_ind_dec && burst_counter < max_burst) {
last_mac_ind_dec = (last_mac_ind_dec + 1) % max_sfnslotdec;
nfapi_nr_slot_indication_scf_t ind = {0};
ind.sfn = NFAPI_SFNSLOTDEC2SFN(p7_info->mu, last_mac_ind_dec);
ind.slot = NFAPI_SFNSLOTDEC2SLOT(p7_info->mu, last_mac_ind_dec);
ind.header.phy_id = p7_info->phy_id;
phy_nr_slot_indication(&ind);
burst_counter++;
}
}
sfnslot_dec = (sfnslot_dec + 1) % max_sfnslotdec;
}
return NULL;
}
#endif

int phy_nr_srs_indication(nfapi_nr_srs_indication_t *ind)
{
for (int i = 0; i < ind->number_of_pdus; ++i)
Expand Down Expand Up @@ -1299,14 +1449,19 @@ void *configure_nr_p7_vnf(void *ptr)
p7_vnf->config->pack_func = &nfapi_nr_p7_message_pack;
p7_vnf->config->send_p7_msg = &vnf_nr_send_p7_msg;
NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] Creating VNF NFAPI P7 start thread %s\n", __FUNCTION__);
threadCreate(&vnf_p7_start_pthread, &vnf_nr_start_p7_thread, p7_vnf->config, "vnf_p7_thread", -1, OAI_PRIORITY_RT);
threadCreate(&vnf_p7_start_pthread, &vnf_nr_start_p7_thread, p7_vnf->config, "vnf_p7_thread", 14, OAI_PRIORITY_RT);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should avoid pinning to specific cores, if it's really needed, add a new parameter to the config file for the use to be able to set the core to pin to ( default -1 ), similarly to the pinning of the nvIPC thread.

#endif

#ifdef ENABLE_AERIAL
p7_vnf->config->unpack_func = &fapi_nr_p7_message_unpack;
p7_vnf->config->hdr_unpack_func = &fapi_nr_p7_message_header_unpack;
p7_vnf->config->pack_func = &fapi_nr_p7_message_pack;
p7_vnf->config->send_p7_msg = &aerial_nr_send_p7_message;
#endif
#ifndef ENABLE_WLS
// Start VNF autonomous timing thread
pthread_t t;
threadCreate(&t, &vnf_timing_thread, p7_vnf, "vnf_timing", 15, OAI_PRIORITY_RT_MAX);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, don't pin to a specific core.

#endif
return 0;
}
Expand Down Expand Up @@ -1341,7 +1496,7 @@ void *vnf_p7_thread_start(void *ptr) {
p7_vnf->config->codec_config.deallocate = &vnf_deallocate;
p7_vnf->config->allocate_p7_vendor_ext = &phy_allocate_p7_vendor_ext;
p7_vnf->config->deallocate_p7_vendor_ext = &phy_deallocate_p7_vendor_ext;
NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] Creating VNF NFAPI P7 start thread %s\n", __FUNCTION__);
NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] Creating VNF NFAPI start thread %s\n", __FUNCTION__);
pthread_create(&vnf_p7_start_pthread, NULL, &vnf_p7_start_thread, p7_vnf->config);
return 0;
}
Expand All @@ -1356,7 +1511,7 @@ int pnf_nr_start_resp_cb(nfapi_vnf_config_t *config, int p5_idx, nfapi_nr_pnf_st

if(p7_vnf->thread_started == 0) {
pthread_t vnf_p7_thread;
threadCreate(&vnf_p7_thread, &configure_nr_p7_vnf, p7_vnf, "vnf_p7_thread", -1, OAI_PRIORITY_RT);
threadCreate(&vnf_p7_thread, &configure_nr_p7_vnf, p7_vnf, "vnf_p7_thread", 14, OAI_PRIORITY_RT);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above

p7_vnf->thread_started = 1;
} else {
// P7 thread already running.
Expand Down Expand Up @@ -1463,11 +1618,20 @@ int nr_param_resp_cb(nfapi_vnf_config_t *config, int p5_idx, nfapi_nr_param_resp
req->num_tlv++;
}
}
//TODO: Assign tag and value for P7 message offsets
req->nfapi_config.dl_tti_timing_offset.tl.tag = NFAPI_NR_NFAPI_DL_TTI_TIMING_OFFSET;
req->nfapi_config.ul_tti_timing_offset.tl.tag = NFAPI_NR_NFAPI_UL_TTI_TIMING_OFFSET;
req->nfapi_config.ul_dci_timing_offset.tl.tag = NFAPI_NR_NFAPI_UL_DCI_TIMING_OFFSET;
req->nfapi_config.tx_data_timing_offset.tl.tag = NFAPI_NR_NFAPI_TX_DATA_TIMING_OFFSET;
// Assign tag and value for P7 message offsets
req->nfapi_config.dl_tti_timing_offset.tl.tag = NFAPI_NR_NFAPI_DL_TTI_TIMING_OFFSET;
req->nfapi_config.dl_tti_timing_offset.value = p7_vnf->dl_tti_timing_offset;

req->nfapi_config.ul_tti_timing_offset.tl.tag = NFAPI_NR_NFAPI_UL_TTI_TIMING_OFFSET;
req->nfapi_config.ul_tti_timing_offset.value = p7_vnf->ul_tti_timing_offset;

req->nfapi_config.ul_dci_timing_offset.tl.tag = NFAPI_NR_NFAPI_UL_DCI_TIMING_OFFSET;
req->nfapi_config.ul_dci_timing_offset.value = p7_vnf->ul_dci_timing_offset;

req->nfapi_config.tx_data_timing_offset.tl.tag = NFAPI_NR_NFAPI_TX_DATA_TIMING_OFFSET;
req->nfapi_config.tx_data_timing_offset.value = p7_vnf->tx_data_timing_offset;

req->num_tlv += 4;

vendor_ext_tlv_2 ve2;
memset(&ve2, 0, sizeof(ve2));
Expand Down Expand Up @@ -1572,6 +1736,7 @@ int start_resp_cb(nfapi_vnf_config_t *config, int p5_idx, nfapi_start_response_t
int nr_start_resp_cb(nfapi_vnf_config_t *config, int p5_idx, nfapi_nr_start_response_scf_t *resp) {
UNUSED(config);
NFAPI_TRACE(NFAPI_TRACE_INFO, "[VNF] Received NFAPI_START_RESP idx:%d phy_id:%d\n", p5_idx, resp->header.phy_id);
__atomic_store_n(&nr_start_resp_received, 1, __ATOMIC_RELEASE);
return 0;
}

Expand Down Expand Up @@ -1754,8 +1919,12 @@ void configure_nr_nfapi_vnf(eth_params_t params)
#endif
vnf_info *vnf = calloc(1, sizeof(vnf_info));
memset(vnf->p7_vnfs, 0, sizeof(vnf->p7_vnfs));
vnf->p7_vnfs[0].timing_window = 30;
vnf->p7_vnfs[0].periodic_timing_enabled = 0;
vnf->p7_vnfs[0].timing_window = 6500;
vnf->p7_vnfs[0].dl_tti_timing_offset = 0;
vnf->p7_vnfs[0].ul_tti_timing_offset = 0;
vnf->p7_vnfs[0].ul_dci_timing_offset = 0;
vnf->p7_vnfs[0].tx_data_timing_offset = 0;
vnf->p7_vnfs[0].periodic_timing_enabled = 1;
vnf->p7_vnfs[0].aperiodic_timing_enabled = 0;
vnf->p7_vnfs[0].periodic_timing_period = 1;
vnf->p7_vnfs[0].config = nfapi_vnf_p7_config_create();
Expand All @@ -1779,6 +1948,9 @@ void configure_nr_nfapi_vnf(eth_params_t params)
config->vnf_ipv6 = 0;
config->pnf_list = 0;
config->phy_list = 0;
config->timing_window = vnf->p7_vnfs[0].timing_window;
config->timing_info_mode = (vnf->p7_vnfs[0].aperiodic_timing_enabled << 1) | (vnf->p7_vnfs[0].periodic_timing_enabled);
config->timing_info_period = vnf->p7_vnfs[0].periodic_timing_period;

config->pnf_nr_connection_indication = &pnf_nr_connection_indication_cb;
config->pnf_disconnect_indication = &pnf_disconnection_indication_cb;
Expand Down
Loading
Loading