From 1a8d81ba57e04532094330d32eddace546068ec4 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Wed, 7 Mar 2018 16:11:36 +0100 Subject: [PATCH 1/4] cleanup MAC PDU generation (plus some general cleanup) The code was very unclear and potentially buggy. This new version is more robust. We can waste up to 2 bytes because the last header in the MAC PDU does not contain a length field and when we request data from RLC we suppose a 3-bytes MAC header. This might be optimized at some point, but the benefit would be low. This commit also contains some general cleanup: - formatting - variables' types: let's use 'int' instead of trying to be clever by using small types that may generate bugs if the value is too big - remove 'tpc_accumulated' which was globally used for all UEs and has no purpose other than logging. We may want to rework a bit the TPC machinery at some point. As the code is today we may repeatedly send TPC over and over without caring about the 3GPP limits, in which case no one knows how the UE is supposed to behave: does it clamp the current max value or does it accumulate over and over and take the clamped value to compute its actual power? If we send a reverse TPC (reduce power instead of increase) does it do it immediately or does it have to decrease n+1 times if we previously ordered it to increase n times?) We do not address the problem of prioritizing LCIDs. As of today there is only one dedicated traffic channel (DTCH), so it's not a problem at this point. What has been tested: - monolithic eNB 5/10/20MHz with one cots UE, TCP/UDP UL/DL. At 20MHz the machine used was not capable of keeping up, generating lots of Us and Ls when the throughput reaches 60Mb/s. USRP B210 was used. --- openair2/LAYER2/MAC/eNB_scheduler_dlsch.c | 273 +++++++++------------- openair2/LAYER2/MAC/proto.h | 18 +- 2 files changed, 125 insertions(+), 166 deletions(-) diff --git a/openair2/LAYER2/MAC/eNB_scheduler_dlsch.c b/openair2/LAYER2/MAC/eNB_scheduler_dlsch.c index aeaa55a79a..0729073eba 100644 --- a/openair2/LAYER2/MAC/eNB_scheduler_dlsch.c +++ b/openair2/LAYER2/MAC/eNB_scheduler_dlsch.c @@ -154,7 +154,7 @@ schedule_next_dlue(module_id_t module_idP, int CC_id, } //------------------------------------------------------------------------------ -unsigned char +int generate_dlsch_header(unsigned char *mac_header, unsigned char num_sdus, unsigned short *sdu_lengths, @@ -344,7 +344,6 @@ generate_dlsch_header(unsigned char *mac_header, //msg("After CEs %d\n",(uint8_t*)mac_header_ptr - mac_header); return ((unsigned char *) mac_header_ptr - mac_header); - } //------------------------------------------------------------------------------ @@ -563,33 +562,28 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, frame_t frameP, sub_frame_t subframeP, int *mbsfn_flag) //------------------------------------------------------------------------------ { - - - uint8_t CC_id; + int CC_id; int UE_id; - unsigned char aggregation; + int aggregation; mac_rlc_status_resp_t rlc_status; - unsigned char header_len_dcch = 0, header_len_dcch_tmp = 0; - unsigned char header_len_dtch = 0, header_len_dtch_tmp = - 0, header_len_dtch_last = 0; - unsigned char ta_len = 0; - unsigned char sdu_lcids[NB_RB_MAX], lcid, offset, num_sdus = 0; - uint16_t nb_rb, nb_rb_temp, nb_available_rb; - uint16_t TBS, j, sdu_lengths[NB_RB_MAX], rnti, padding = - 0, post_padding = 0; + int ta_len = 0; + unsigned char sdu_lcids[NB_RB_MAX]; + int lcid, offset, num_sdus = 0; + int nb_rb, nb_rb_temp, nb_available_rb; + uint16_t sdu_lengths[NB_RB_MAX]; + int TBS, j, rnti, padding = 0, post_padding = 0; unsigned char dlsch_buffer[MAX_DLSCH_PAYLOAD_BYTES]; - unsigned char round = 0; - unsigned char harq_pid = 0; + int round = 0; + int harq_pid = 0; eNB_UE_STATS *eNB_UE_stats = NULL; - uint16_t sdu_length_total = 0; + int sdu_length_total = 0; eNB_MAC_INST *eNB = RC.mac[module_idP]; COMMON_channels_t *cc = eNB->common_channels; UE_list_t *UE_list = &eNB->UE_list; int continue_flag = 0; int32_t normalized_rx_power, target_rx_power; - int32_t tpc = 1; - static int32_t tpc_accumulated = 0; + int tpc = 1; UE_sched_ctrl *ue_sched_ctl; int mcs; int i; @@ -601,6 +595,8 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, nfapi_dl_config_request_pdu_t *dl_config_pdu; int tdd_sfa; int ta_update; + int header_length_last; + int header_length_total; #if 0 if (UE_list->head == -1) { @@ -792,6 +788,7 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, if (UE_list->eNB_UE_stats[CC_id][UE_id].rrc_status < RRC_CONNECTED) continue; + header_length_total = 0; sdu_length_total = 0; num_sdus = 0; @@ -838,8 +835,7 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, if (cc[CC_id].tdd_Config != NULL) { UE_list->UE_template[CC_id][UE_id].DAI++; update_ul_dci(module_idP, CC_id, rnti, - UE_list->UE_template[CC_id][UE_id]. - DAI); + UE_list->UE_template[CC_id][UE_id].DAI); LOG_D(MAC, "DAI update: CC_id %d subframeP %d: UE %d, DAI %d\n", CC_id, subframeP, UE_id, @@ -988,13 +984,12 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, module_idP, frameP, CC_id, UE_id); } } else { /* This is a potentially new SDU opportunity */ - rlc_status.bytes_in_buffer = 0; + // Now check RLC information to compute number of required RBs // get maximum TBS size for RLC request - TBS = - get_TBS_DL(eNB_UE_stats->dlsch_mcs1, nb_available_rb); - // check first for RLC data on DCCH + TBS = get_TBS_DL(eNB_UE_stats->dlsch_mcs1, nb_available_rb); + // add the length for all the control elements (timing adv, drx, etc) : header + payload if (ue_sched_ctl->ta_timer == 0) { @@ -1009,38 +1004,42 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, ta_len = (ta_update != 31) ? 2 : 0; - header_len_dcch = 2; // 2 bytes DCCH SDU subheader - - if (TBS - ta_len - header_len_dcch > 0) { - rlc_status = mac_rlc_status_ind(module_idP, rnti, module_idP, frameP, subframeP, ENB_FLAG_YES, MBMS_FLAG_NO, DCCH, (TBS - ta_len - header_len_dcch)); // transport block set size + // RLC data on DCCH + if (TBS - ta_len - header_length_total - sdu_length_total - 3 > 0) { + rlc_status = mac_rlc_status_ind(module_idP, rnti, module_idP, frameP, subframeP, ENB_FLAG_YES, MBMS_FLAG_NO, DCCH, + TBS - ta_len - header_length_total - sdu_length_total - 3); sdu_lengths[0] = 0; - if (rlc_status.bytes_in_buffer > 0) { // There is DCCH to transmit - LOG_D(MAC, - "[eNB %d] SFN/SF %d.%d, DL-DCCH->DLSCH CC_id %d, Requesting %d bytes from RLC (RRC message)\n", + if (rlc_status.bytes_in_buffer > 0) { + LOG_D(MAC, "[eNB %d] SFN/SF %d.%d, DL-DCCH->DLSCH CC_id %d, Requesting %d bytes from RLC (RRC message)\n", module_idP, frameP, subframeP, CC_id, - TBS - header_len_dcch); - sdu_lengths[0] = mac_rlc_data_req(module_idP, rnti, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_NO, DCCH, TBS, //not used - (char *) - &dlsch_buffer - [0]); + TBS - ta_len - header_length_total - sdu_length_total - 3); + + sdu_lengths[0] = mac_rlc_data_req(module_idP, rnti, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_NO, DCCH, + TBS, //not used + (char *)&dlsch_buffer[0]); T(T_ENB_MAC_UE_DL_SDU, T_INT(module_idP), T_INT(CC_id), T_INT(rnti), T_INT(frameP), T_INT(subframeP), T_INT(harq_pid), T_INT(DCCH), T_INT(sdu_lengths[0])); - LOG_D(MAC, - "[eNB %d][DCCH] CC_id %d Got %d bytes from RLC\n", + LOG_D(MAC, "[eNB %d][DCCH] CC_id %d Got %d bytes from RLC\n", module_idP, CC_id, sdu_lengths[0]); + sdu_length_total = sdu_lengths[0]; sdu_lcids[0] = DCCH; UE_list->eNB_UE_stats[CC_id][UE_id].lcid_sdu[0] = DCCH; UE_list->eNB_UE_stats[CC_id][UE_id].sdu_length_tx[DCCH] = sdu_lengths[0]; UE_list->eNB_UE_stats[CC_id][UE_id].num_pdu_tx[DCCH] += 1; UE_list->eNB_UE_stats[CC_id][UE_id].num_bytes_tx[DCCH] += sdu_lengths[0]; + + header_length_last = 1 + 1 + (sdu_lengths[0] >= 128); + header_length_total += header_length_last; + num_sdus = 1; + #ifdef DEBUG_eNB_SCHEDULER LOG_T(MAC, "[eNB %d][DCCH] CC_id %d Got %d bytes :", @@ -1052,26 +1051,25 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, LOG_T(MAC, "\n"); #endif - } else { - header_len_dcch = 0; - sdu_length_total = 0; } } - // check for DCCH1 and update header information (assume 2 byte sub-header) - if (TBS - ta_len - header_len_dcch - sdu_length_total > 0) { - rlc_status = mac_rlc_status_ind(module_idP, rnti, module_idP, frameP, subframeP, ENB_FLAG_YES, MBMS_FLAG_NO, DCCH + 1, (TBS - ta_len - header_len_dcch - sdu_length_total)); // transport block set size less allocations for timing advance and + + // RLC data on DCCH1 + if (TBS - ta_len - header_length_total - sdu_length_total - 3 > 0) { + rlc_status = mac_rlc_status_ind(module_idP, rnti, module_idP, frameP, subframeP, ENB_FLAG_YES, MBMS_FLAG_NO, DCCH + 1, + TBS - ta_len - header_length_total - sdu_length_total - 3); + // DCCH SDU sdu_lengths[num_sdus] = 0; if (rlc_status.bytes_in_buffer > 0) { - LOG_D(MAC, - "[eNB %d], Frame %d, DCCH1->DLSCH, CC_id %d, Requesting %d bytes from RLC (RRC message)\n", + LOG_D(MAC, "[eNB %d], Frame %d, DCCH1->DLSCH, CC_id %d, Requesting %d bytes from RLC (RRC message)\n", module_idP, frameP, CC_id, - TBS - header_len_dcch - sdu_length_total); - sdu_lengths[num_sdus] += mac_rlc_data_req(module_idP, rnti, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_NO, DCCH + 1, TBS, //not used - (char *) - &dlsch_buffer - [sdu_length_total]); + TBS - ta_len - header_length_total - sdu_length_total - 3); + + sdu_lengths[num_sdus] += mac_rlc_data_req(module_idP, rnti, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_NO, DCCH + 1, + TBS, //not used + (char *)&dlsch_buffer[sdu_length_total]); T(T_ENB_MAC_UE_DL_SDU, T_INT(module_idP), T_INT(CC_id), T_INT(rnti), T_INT(frameP), @@ -1080,13 +1078,16 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, sdu_lcids[num_sdus] = DCCH1; sdu_length_total += sdu_lengths[num_sdus]; - header_len_dcch += 2; UE_list->eNB_UE_stats[CC_id][UE_id].lcid_sdu[num_sdus] = DCCH1; UE_list->eNB_UE_stats[CC_id][UE_id].sdu_length_tx[DCCH1] = sdu_lengths[num_sdus]; - UE_list->eNB_UE_stats[CC_id][UE_id]. - num_pdu_tx[DCCH1] += 1; + UE_list->eNB_UE_stats[CC_id][UE_id].num_pdu_tx[DCCH1] += 1; UE_list->eNB_UE_stats[CC_id][UE_id].num_bytes_tx[DCCH1] += sdu_lengths[num_sdus]; + + header_length_last = 1 + 1 + (sdu_lengths[num_sdus] >= 128); + header_length_total += header_length_last; + num_sdus++; + #ifdef DEBUG_eNB_SCHEDULER LOG_T(MAC, "[eNB %d][DCCH1] CC_id %d Got %d bytes :", @@ -1098,25 +1099,18 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, LOG_T(MAC, "\n"); #endif - } } - // assume the max dtch header size, and adjust it later - header_len_dtch = 0; - header_len_dtch_last = 0; // the header length of the last mac sdu - // lcid has to be sorted before the actual allocation (similar struct as ue_list). + + // TODO: lcid has to be sorted before the actual allocation (similar struct as ue_list). for (lcid = NB_RB_MAX - 1; lcid >= DTCH; lcid--) { - // TBD: check if the lcid is active + // TODO: check if the lcid is active - header_len_dtch += 3; - header_len_dtch_last = 3; - LOG_D(MAC, - "[eNB %d], Frame %d, DTCH%d->DLSCH, Checking RLC status (tbs %d, len %d)\n", + LOG_D(MAC, "[eNB %d], Frame %d, DTCH%d->DLSCH, Checking RLC status (tbs %d, len %d)\n", module_idP, frameP, lcid, TBS, - TBS - ta_len - header_len_dcch - - sdu_length_total - header_len_dtch); + TBS - ta_len - header_length_total - sdu_length_total - 3); - if (TBS - ta_len - header_len_dcch - sdu_length_total - header_len_dtch > 0) { // NN: > 2 ? + if (TBS - ta_len - header_length_total - sdu_length_total - 3 > 0) { rlc_status = mac_rlc_status_ind(module_idP, rnti, module_idP, @@ -1125,25 +1119,22 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, ENB_FLAG_YES, MBMS_FLAG_NO, lcid, - TBS - ta_len - - header_len_dcch - - sdu_length_total - - header_len_dtch); + TBS - ta_len - header_length_total - sdu_length_total - 3); + if (rlc_status.bytes_in_buffer > 0) { - LOG_D(MAC, "[eNB %d][USER-PLANE DEFAULT DRB] Frame %d : DTCH->DLSCH, Requesting %d bytes from RLC (lcid %d total hdr len %d)\n", module_idP, frameP, - TBS - header_len_dcch - - sdu_length_total - header_len_dtch, lcid, - header_len_dtch); - sdu_lengths[num_sdus] = mac_rlc_data_req(module_idP, rnti, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_NO, lcid, TBS, //not used - (char - *) - &dlsch_buffer - [sdu_length_total]); + TBS - ta_len - header_length_total - sdu_length_total - 3, + lcid, + header_length_total); + + sdu_lengths[num_sdus] = mac_rlc_data_req(module_idP, rnti, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_NO, lcid, + TBS, //not used + (char *)&dlsch_buffer[sdu_length_total]); + T(T_ENB_MAC_UE_DL_SDU, T_INT(module_idP), T_INT(CC_id), T_INT(rnti), T_INT(frameP), T_INT(subframeP), T_INT(harq_pid), @@ -1152,50 +1143,39 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, LOG_D(MAC, "[eNB %d][USER-PLANE DEFAULT DRB] Got %d bytes for DTCH %d \n", module_idP, sdu_lengths[num_sdus], lcid); + sdu_lcids[num_sdus] = lcid; sdu_length_total += sdu_lengths[num_sdus]; - UE_list-> - eNB_UE_stats[CC_id][UE_id].num_pdu_tx[lcid] - += 1; + UE_list->eNB_UE_stats[CC_id][UE_id].num_pdu_tx[lcid]++; UE_list->eNB_UE_stats[CC_id][UE_id].lcid_sdu[num_sdus] = lcid; UE_list->eNB_UE_stats[CC_id][UE_id].sdu_length_tx[lcid] = sdu_lengths[num_sdus]; - UE_list-> - eNB_UE_stats[CC_id][UE_id].num_bytes_tx - [lcid] += sdu_lengths[num_sdus]; - if (sdu_lengths[num_sdus] < 128) { - header_len_dtch--; - header_len_dtch_last--; - } + UE_list->eNB_UE_stats[CC_id][UE_id].num_bytes_tx[lcid] += sdu_lengths[num_sdus]; + + header_length_last = 1 + 1 + (sdu_lengths[num_sdus] >= 128); + header_length_total += header_length_last; + num_sdus++; + UE_list->UE_sched_ctrl[UE_id].uplane_inactivity_timer = 0; - } // no data for this LCID - else { - header_len_dtch -= 3; } - } // no TBS left - else { - header_len_dtch -= 3; + } else { + // no TBS left break; } } - if (header_len_dtch == 0) - header_len_dtch_last = 0; - // there is at least one SDU + + /* last header does not have length field */ + if (header_length_total) { + header_length_total -= header_length_last; + header_length_total++; + } + + // there is at least one SDU or TA command // if (num_sdus > 0 ){ - if ((sdu_length_total + header_len_dcch + - header_len_dtch) > 0) { + if (ta_len + sdu_length_total + header_length_total > 0) { // Now compute number of required RBs for total sdu length // Assume RAH format 2 - // adjust header lengths - header_len_dcch_tmp = header_len_dcch; - header_len_dtch_tmp = header_len_dtch; - if (header_len_dtch == 0) { - header_len_dcch = (header_len_dcch > 0) ? 1 : 0; //header_len_dcch; // remove length field - } else { - header_len_dtch_last -= 1; // now use it to find how many bytes has to be removed for the last MAC SDU - header_len_dtch = (header_len_dtch > 0) ? header_len_dtch - header_len_dtch_last : header_len_dtch; // remove length field for the last SDU - } mcs = eNB_UE_stats->dlsch_mcs1; @@ -1207,16 +1187,12 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, TBS = get_TBS_DL(mcs, nb_rb); - while (TBS < - (sdu_length_total + header_len_dcch + - header_len_dtch + ta_len)) { + while (TBS < sdu_length_total + header_length_total + ta_len) { nb_rb += min_rb_unit[CC_id]; // if (nb_rb > nb_available_rb) { // if we've gone beyond the maximum number of RBs // (can happen if N_RB_DL is odd) - TBS = - get_TBS_DL(eNB_UE_stats->dlsch_mcs1, - nb_available_rb); + TBS = get_TBS_DL(eNB_UE_stats->dlsch_mcs1, nb_available_rb); nb_rb = nb_available_rb; break; } @@ -1250,13 +1226,13 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, } // decrease mcs until TBS falls below required length - while ((TBS > (sdu_length_total + header_len_dcch + header_len_dtch + ta_len)) && (mcs > 0)) { + while ((TBS > sdu_length_total + header_length_total + ta_len) && (mcs > 0)) { mcs--; TBS = get_TBS_DL(mcs, nb_rb); } // if we have decreased too much or we don't have enough RBs, increase MCS - while ((TBS < (sdu_length_total + header_len_dcch + header_len_dtch + ta_len)) + while ((TBS < sdu_length_total + header_length_total + ta_len) && (((ue_sched_ctl->dl_pow_off[CC_id] > 0) && (mcs < 28)) || ((ue_sched_ctl->dl_pow_off[CC_id] == 0) @@ -1277,23 +1253,14 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, // TBS, sdu_length_total, offset, TBS-sdu_length_total-offset); #endif - if ((TBS - header_len_dcch - header_len_dtch - sdu_length_total - ta_len) <= 2) { - padding = (TBS - header_len_dcch - header_len_dtch - sdu_length_total - ta_len); + if (TBS - header_length_total - sdu_length_total - ta_len <= 2) { + padding = TBS - header_length_total - sdu_length_total - ta_len; post_padding = 0; } else { padding = 0; - - // adjust the header len - if (header_len_dtch == 0) { - header_len_dcch = header_len_dcch_tmp; - } else { //if (( header_len_dcch==0)&&((header_len_dtch==1)||(header_len_dtch==2))) - header_len_dtch = header_len_dtch_tmp; - } - - post_padding = TBS - sdu_length_total - header_len_dcch - header_len_dtch - ta_len; // 1 is for the postpadding header + post_padding = 1; } - offset = generate_dlsch_header((unsigned char *) UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0], num_sdus, //num_sdus sdu_lengths, // sdu_lcids, 255, // no drx @@ -1304,12 +1271,12 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, //#ifdef DEBUG_eNB_SCHEDULER if (ta_update != 31) { LOG_D(MAC, - "[eNB %d][DLSCH] Frame %d Generate header for UE_id %d on CC_id %d: sdu_length_total %d, num_sdus %d, sdu_lengths[0] %d, sdu_lcids[0] %d => payload offset %d,timing advance value : %d, padding %d,post_padding %d,(mcs %d, TBS %d, nb_rb %d),header_dcch %d, header_dtch %d\n", + "[eNB %d][DLSCH] Frame %d Generate header for UE_id %d on CC_id %d: sdu_length_total %d, num_sdus %d, sdu_lengths[0] %d, sdu_lcids[0] %d => payload offset %d,timing advance value : %d, padding %d,post_padding %d,(mcs %d, TBS %d, nb_rb %d),header_length %d\n", module_idP, frameP, UE_id, CC_id, sdu_length_total, num_sdus, sdu_lengths[0], sdu_lcids[0], offset, ta_update, padding, post_padding, mcs, TBS, nb_rb, - header_len_dcch, header_len_dtch); + header_length_total); } //#endif #ifdef DEBUG_eNB_SCHEDULER @@ -1327,11 +1294,16 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, dlsch_buffer, sdu_length_total); // memcpy(RC.mac[0].DLSCH_pdu[0][0].payload[0][offset],dcch_buffer,sdu_lengths[0]); +#if 0 // fill remainder of DLSCH with random data for (j = 0; j < (TBS - sdu_length_total - offset); j++) { UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0][offset + sdu_length_total + j] = (char) (taus() & 0xff); } - +#endif + // fill remainder of DLSCH with 0 + for (j = 0; j < (TBS - sdu_length_total - offset); j++) { + UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0][offset + sdu_length_total + j] = 0; + } if (opt_enabled == 1) { trace_pdu(1, (uint8_t *) @@ -1348,8 +1320,7 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, T(T_ENB_MAC_UE_DL_PDU_WITH_DATA, T_INT(module_idP), T_INT(CC_id), T_INT(rnti), T_INT(frameP), T_INT(subframeP), T_INT(harq_pid), - T_BUFFER(UE_list->DLSCH_pdu[CC_id][0][UE_id]. - payload[0], TBS)); + T_BUFFER(UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0], TBS)); UE_list->UE_template[CC_id][UE_id].nb_rb[harq_pid] = nb_rb; @@ -1378,6 +1349,7 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, UE_list->UE_template[CC_id][UE_id]. DAI); } + // do PUCCH power control // this is the normalized RX power eNB_UE_stats = &UE_list->eNB_UE_stats[CC_id][UE_id]; @@ -1399,18 +1371,15 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, if (normalized_rx_power > (target_rx_power + 4)) { tpc = 0; //-1 - tpc_accumulated--; } else if (normalized_rx_power < (target_rx_power - 4)) { tpc = 2; //+1 - tpc_accumulated++; } else { tpc = 1; //0 } LOG_D(MAC, - "[eNB %d] DLSCH scheduler: frame %d, subframe %d, harq_pid %d, tpc %d, accumulated %d, normalized/target rx power %d/%d\n", - module_idP, frameP, subframeP, harq_pid, - tpc, tpc_accumulated, + "[eNB %d] DLSCH scheduler: frame %d, subframe %d, harq_pid %d, tpc %d, normalized/target rx power %d/%d\n", + module_idP, frameP, subframeP, harq_pid, tpc, normalized_rx_power, target_rx_power); } // Po_PUCCH has been updated @@ -1456,13 +1425,13 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, module_idP, CC_id, harq_pid, mcs); } + LOG_D(MAC, "Checking feasibility pdu %d (new sdu)\n", dl_req->number_pdu); + if (!CCE_allocation_infeasible(module_idP, CC_id, 1, subframeP, dl_config_pdu->dci_dl_pdu. dci_dl_pdu_rel8.aggregation_level, rnti)) { - - ue_sched_ctl->round[CC_id][harq_pid] = 0; dl_req->number_dci++; dl_req->number_pdu++; @@ -1482,8 +1451,7 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, UE_list->UE_template[CC_id][UE_id].oldNDI[harq_pid] = 1 - UE_list->UE_template[CC_id][UE_id].oldNDI[harq_pid]; UE_list->UE_template[CC_id][UE_id].oldmcs1[harq_pid] = mcs; UE_list->UE_template[CC_id][UE_id].oldmcs2[harq_pid] = 0; - AssertFatal(UE_list->UE_template[CC_id][UE_id].physicalConfigDedicated != - NULL, + AssertFatal(UE_list->UE_template[CC_id][UE_id].physicalConfigDedicated != NULL, "physicalConfigDedicated is NULL\n"); AssertFatal(UE_list->UE_template[CC_id][UE_id].physicalConfigDedicated->pdsch_ConfigDedicated != NULL, "physicalConfigDedicated->pdsch_ConfigDedicated is NULL\n"); @@ -1506,15 +1474,10 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, 0, //number of PRBs treated as one subband, not used here 0 // number of beamforming vectors, not used here ); - eNB->TX_req[CC_id].sfn_sf = fill_nfapi_tx_req(&eNB-> - TX_req - [CC_id].tx_request_body, + eNB->TX_req[CC_id].sfn_sf = fill_nfapi_tx_req(&eNB->TX_req[CC_id].tx_request_body, (frameP * 10) + subframeP, TBS, eNB->pdu_index[CC_id], - eNB-> - UE_list.DLSCH_pdu[CC_id][0][(unsigned char) - UE_id].payload - [0]); + eNB->UE_list.DLSCH_pdu[CC_id][0][UE_id].payload[0]); LOG_D(MAC, "Filled NFAPI configuration for DCI/DLSCH/TXREQ %d, new SDU\n", @@ -1523,10 +1486,7 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, eNB->pdu_index[CC_id]++; program_dlsch_acknak(module_idP, CC_id, UE_id, frameP, subframeP, - dl_config_pdu-> - dci_dl_pdu.dci_dl_pdu_rel8. - cce_idx); - + dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.cce_idx); } else { LOG_W(MAC, "Frame %d, Subframe %d: Dropping DLSCH allocation for UE %d/%x, infeasible CCE allocations\n", @@ -1550,7 +1510,6 @@ schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP, stop_meas(&eNB->schedule_dlsch); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_SCHEDULE_DLSCH, VCD_FUNCTION_OUT); - } //------------------------------------------------------------------------------ diff --git a/openair2/LAYER2/MAC/proto.h b/openair2/LAYER2/MAC/proto.h index 07159f9c24..2586c94f43 100644 --- a/openair2/LAYER2/MAC/proto.h +++ b/openair2/LAYER2/MAC/proto.h @@ -855,15 +855,15 @@ in the DLSCH buffer. @param post_padding number of bytes for padding at the end of MAC PDU @returns Number of bytes used for header */ -unsigned char generate_dlsch_header(unsigned char *mac_header, - unsigned char num_sdus, - unsigned short *sdu_lengths, - unsigned char *sdu_lcids, - unsigned char drx_cmd, - unsigned short timing_advance_cmd, - unsigned char *ue_cont_res_id, - unsigned char short_padding, - unsigned short post_padding); +int generate_dlsch_header(unsigned char *mac_header, + unsigned char num_sdus, + unsigned short *sdu_lengths, + unsigned char *sdu_lcids, + unsigned char drx_cmd, + unsigned short timing_advance_cmd, + unsigned char *ue_cont_res_id, + unsigned char short_padding, + unsigned short post_padding); /** \brief RRC eNB Configuration primitive for PHY/MAC. Allows configuration of PHY/MAC resources based on System Information (SI), RRCConnectionSetup and RRCConnectionReconfiguration messages. @param Mod_id Instance ID of eNB From 5a61f9944c93bc33157361330dbdc5165427330c Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Thu, 8 Mar 2018 09:58:13 +0100 Subject: [PATCH 2/4] cleanup - remove unnecessary calls to get_cpu_freq_GHz The one in lte-enb.c disrupts the realtime. Using a B200mini with 20MHz bandwidth leads to the UE unable to connect for it seesms like the UL and DL are not properly time synched because of this sleep of one second that happens after the USRP streaming has started. We see some random access attempts but the decoded preamble is wrong. This may be dependant on the setup. I had sporadic errors with a B210, where sometimes the UE could connect and sometimes not. --- targets/RT/USER/lte-enb.c | 7 ------- targets/RT/USER/lte-softmodem.c | 6 ++---- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/targets/RT/USER/lte-enb.c b/targets/RT/USER/lte-enb.c index 01a2c68bf3..e1c5d10de5 100644 --- a/targets/RT/USER/lte-enb.c +++ b/targets/RT/USER/lte-enb.c @@ -163,13 +163,6 @@ extern void add_subframe(uint16_t *frameP, uint16_t *subframeP, int offset); static inline int rxtx(PHY_VARS_eNB *eNB,eNB_rxtx_proc_t *proc, char *thread_name) { - - static double cpu_freq_GHz = 0.0; - - if (cpu_freq_GHz == 0.0) - cpu_freq_GHz = get_cpu_freq_GHz(); - - start_meas(&softmodem_stats_rxtx_sf); // ******************************************************************* diff --git a/targets/RT/USER/lte-softmodem.c b/targets/RT/USER/lte-softmodem.c index f6789086b4..0564049c70 100644 --- a/targets/RT/USER/lte-softmodem.c +++ b/targets/RT/USER/lte-softmodem.c @@ -1249,10 +1249,8 @@ int main( int argc, char **argv ) printf("Runtime table\n"); fill_modeled_runtime_table(runtime_phy_rx,runtime_phy_tx); - cpuf=get_cpu_freq_GHz(); - - - + + #ifndef DEADLINE_SCHEDULER printf("NO deadline scheduler\n"); From 963805820c9a0b0deff845d44fe116cda5013cc3 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Thu, 8 Mar 2018 10:17:16 +0100 Subject: [PATCH 3/4] add a stop_rf function in the RU When the program exits it has to stop the streaming of the USRP. The function exit_fun is supposed to do that. When quitting with control+c (very common case) this function is not called. The code is very unclear there, so let's add a stop_rf in the RU, as there is already a start_rf. If we don't call trx_end_func, then at the next run the USRP device may be in an unstable state and behave improperly. If the program crashes then the USRP device may be in an unstable state. The only solution to this problem is to reset the USRP device. Maybe there is a way to clean the state of the device when we open it, before we start using it. Sort of a cleanup before use. That could be a better solution to "bad state after program crash". What has been tested: - monolithic eNB only --- openair1/PHY/defs.h | 2 ++ targets/RT/USER/lte-ru.c | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/openair1/PHY/defs.h b/openair1/PHY/defs.h index 46f37e9431..a0f37dd885 100644 --- a/openair1/PHY/defs.h +++ b/openair1/PHY/defs.h @@ -738,6 +738,8 @@ typedef struct RU_t_s{ void (*fh_south_asynch_in)(struct RU_t_s *ru,int *frame, int *subframe); /// function pointer to initialization function for radio interface int (*start_rf)(struct RU_t_s *ru); + /// function pointer to release function for radio interface + int (*stop_rf)(struct RU_t_s *ru); /// function pointer to initialization function for radio interface int (*start_if)(struct RU_t_s *ru,struct PHY_VARS_eNB_s *eNB); /// function pointer to RX front-end processing routine (DFTs/prefix removal or NULL) diff --git a/targets/RT/USER/lte-ru.c b/targets/RT/USER/lte-ru.c index d909da627f..d9bda6a037 100644 --- a/targets/RT/USER/lte-ru.c +++ b/targets/RT/USER/lte-ru.c @@ -1523,7 +1523,13 @@ static void* ru_thread( void* param ) { printf( "Exiting ru_thread \n"); - + + if (ru->stop_rf != NULL) { + if (ru->stop_rf(ru) != 0) + LOG_E(HW,"Could not stop the RF device\n"); + else LOG_I(PHY,"RU %d rf device stopped\n",ru->idx); + } + ru_thread_status = 0; return &ru_thread_status; @@ -1617,6 +1623,12 @@ int start_rf(RU_t *ru) { return(ru->rfdevice.trx_start_func(&ru->rfdevice)); } +int stop_rf(RU_t *ru) +{ + ru->rfdevice.trx_end_func(&ru->rfdevice); + return 0; +} + extern void fep_full(RU_t *ru); extern void ru_fep_full_2thread(RU_t *ru); extern void feptx_ofdm(RU_t *ru); @@ -2082,6 +2094,7 @@ void set_function_spec_param(RU_t *ru) ru->fh_south_in = rx_rf; // local synchronous RF RX ru->fh_south_out = tx_rf; // local synchronous RF TX ru->start_rf = start_rf; // need to start the local RF interface + ru->stop_rf = stop_rf; printf("configuring ru_id %d (start_rf %p)\n", ru->idx, start_rf); /* if (ru->function == eNodeB_3GPP) { // configure RF parameters only for 3GPP eNodeB, we need to get them from RAU otherwise @@ -2113,6 +2126,7 @@ void set_function_spec_param(RU_t *ru) ru->fh_south_asynch_in = NULL; // no asynchronous UL } ru->start_rf = NULL; // no local RF + ru->stop_rf = NULL; ru->start_if = start_if; // need to start if interface for IF5 ru->ifdevice.host_type = RAU_HOST; ru->ifdevice.eth_params = &ru->eth_params; @@ -2137,6 +2151,7 @@ void set_function_spec_param(RU_t *ru) ru->fh_north_out = NULL; ru->fh_north_asynch_in = NULL; ru->start_rf = NULL; // no local RF + ru->stop_rf = NULL; ru->start_if = start_if; // need to start if interface for IF4p5 ru->ifdevice.host_type = RAU_HOST; ru->ifdevice.eth_params = &ru->eth_params; From 71e7f9713a9d410a249ad3097caa53128e1dc2d3 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Fri, 9 Mar 2018 10:11:24 +0100 Subject: [PATCH 4/4] hack: try to avoid "buffer full" problem when pushing DL traffic This is hack-level development. With this commit you can do UDP DL traffic of say 100Mb/s over a 5MHz link with one connected UE and the eNB should not crash because of memory exhaustion. Of course on the receiver side you won't get 100Mb/s and many many lost packets. But the system should not crash. 1Gb/s does not work. So in any case try to remain within some reasonable limits. There is no reason to push more than twice the maximum achievable throughput of the link. This work is based on a patch proposed by Francesco Gringoli. --- common/utils/itti/intertask_interface.c | 97 +++++++++++++++++++++++++ common/utils/itti/intertask_interface.h | 12 +++ openair2/LAYER2/RLC/rlc.c | 9 +++ openair3/UDP/udp_eNB_task.c | 13 +++- 4 files changed, 130 insertions(+), 1 deletion(-) diff --git a/common/utils/itti/intertask_interface.c b/common/utils/itti/intertask_interface.c index 554f50a2a1..85b5fef509 100644 --- a/common/utils/itti/intertask_interface.c +++ b/common/utils/itti/intertask_interface.c @@ -391,6 +391,103 @@ int itti_send_msg_to_task(task_id_t destination_task_id, instance_t instance, Me return 0; } +/* same as itti_send_msg_to_task but returns -1 in case of failure instead of crashing */ +/* TODO: this is a hack - the whole logic needs a proper rework. */ +/* look for HACK_RLC_UM_LIMIT for others places related to the hack. Please do not remove this comment. */ +int itti_try_send_msg_to_task(task_id_t destination_task_id, instance_t instance, MessageDef *message) +{ + thread_id_t destination_thread_id; + task_id_t origin_task_id; + message_list_t *new; + uint32_t priority; + message_number_t message_number; + uint32_t message_id; + + AssertFatal (message != NULL, "Message is NULL!\n"); + AssertFatal (destination_task_id < itti_desc.task_max, "Destination task id (%d) is out of range (%d)\n", destination_task_id, itti_desc.task_max); + + destination_thread_id = TASK_GET_THREAD_ID(destination_task_id); + message->ittiMsgHeader.destinationTaskId = destination_task_id; + message->ittiMsgHeader.instance = instance; + message->ittiMsgHeader.lte_time.frame = itti_desc.lte_time.frame; + message->ittiMsgHeader.lte_time.slot = itti_desc.lte_time.slot; + message_id = message->ittiMsgHeader.messageId; + AssertFatal (message_id < itti_desc.messages_id_max, "Message id (%d) is out of range (%d)!\n", message_id, itti_desc.messages_id_max); + + origin_task_id = ITTI_MSG_ORIGIN_ID(message); + + priority = itti_get_message_priority (message_id); + + /* Increment the global message number */ + message_number = itti_increment_message_number (); + + itti_dump_queue_message (origin_task_id, message_number, message, itti_desc.messages_info[message_id].name, + sizeof(MessageHeader) + message->ittiMsgHeader.ittiMsgSize); + + if (destination_task_id != TASK_UNKNOWN) { + + if (itti_desc.threads[destination_thread_id].task_state == TASK_STATE_ENDED) { + ITTI_DEBUG(ITTI_DEBUG_ISSUES, " Message %s, number %lu with priority %d can not be sent from %s to queue (%u:%s), ended destination task!\n", + itti_desc.messages_info[message_id].name, + message_number, + priority, + itti_get_task_name(origin_task_id), + destination_task_id, + itti_get_task_name(destination_task_id)); + } else { + /* We cannot send a message if the task is not running */ + AssertFatal (itti_desc.threads[destination_thread_id].task_state == TASK_STATE_READY, + "Task %s Cannot send message %s (%d) to thread %d, it is not in ready state (%d)!\n", + itti_get_task_name(origin_task_id), + itti_desc.messages_info[message_id].name, + message_id, + destination_thread_id, + itti_desc.threads[destination_thread_id].task_state); + + /* Allocate new list element */ + new = (message_list_t *) itti_malloc (origin_task_id, destination_task_id, sizeof(struct message_list_s)); + + /* Fill in members */ + new->msg = message; + new->message_number = message_number; + new->message_priority = priority; + + /* Enqueue message in destination task queue */ + if (lfds611_queue_enqueue(itti_desc.tasks[destination_task_id].message_queue, new) == 0) { + itti_free(origin_task_id, new); + return -1; + } + + { + /* Only use event fd for tasks, subtasks will pool the queue */ + if (TASK_GET_PARENT_TASK_ID(destination_task_id) == TASK_UNKNOWN) { + ssize_t write_ret; + eventfd_t sem_counter = 1; + + /* Call to write for an event fd must be of 8 bytes */ + write_ret = write (itti_desc.threads[destination_thread_id].task_event_fd, &sem_counter, sizeof(sem_counter)); + AssertFatal (write_ret == sizeof(sem_counter), "Write to task message FD (%d) failed (%d/%d)\n", + destination_thread_id, (int) write_ret, (int) sizeof(sem_counter)); + } + } + + ITTI_DEBUG(ITTI_DEBUG_SEND, " Message %s, number %lu with priority %d successfully sent from %s to queue (%u:%s)\n", + itti_desc.messages_info[message_id].name, + message_number, + priority, + itti_get_task_name(origin_task_id), + destination_task_id, + itti_get_task_name(destination_task_id)); + } + } else { + /* This is a debug message to TASK_UNKNOWN, we can release safely release it */ + int result = itti_free(origin_task_id, message); + AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result); + } + + return 0; +} + void itti_subscribe_event_fd(task_id_t task_id, int fd) { thread_id_t thread_id; diff --git a/common/utils/itti/intertask_interface.h b/common/utils/itti/intertask_interface.h index 606c851d1d..ada34ce1b7 100644 --- a/common/utils/itti/intertask_interface.h +++ b/common/utils/itti/intertask_interface.h @@ -108,6 +108,18 @@ int itti_send_broadcast_message(MessageDef *message_p); **/ int itti_send_msg_to_task(task_id_t task_id, instance_t instance, MessageDef *message); +/* TODO: this is a hack. Almost no caller of itti_send_msg_to_task checks + * the return value so it has been changed to crash the program in case + * of failure instead of returning -1 as the documentation above says. + * The RLC UM code may receive too much data when doing UDP at a higher + * throughput than the link allows and so for this specific case we need + * a version that actually returns -1 on failure. + * + * This needs to be cleaned at some point. + */ +/* look for HACK_RLC_UM_LIMIT for others places related to the hack. Please do not remove this comment. */ +int itti_try_send_msg_to_task(task_id_t task_id, instance_t instance, MessageDef *message); + /** \brief Add a new fd to monitor. * NOTE: it is up to the user to read data associated with the fd * \param task_id Task ID of the receiving task diff --git a/openair2/LAYER2/RLC/rlc.c b/openair2/LAYER2/RLC/rlc.c index d27d46ff42..783441bd58 100644 --- a/openair2/LAYER2/RLC/rlc.c +++ b/openair2/LAYER2/RLC/rlc.c @@ -437,6 +437,15 @@ rlc_op_status_t rlc_data_req (const protocol_ctxt_t* const ctxt_pP, break; case RLC_MODE_UM: + /* TODO: this is a hack, needs better solution. Let's not use too + * much memory and store at maximum 5 millions bytes. + */ + /* look for HACK_RLC_UM_LIMIT for others places related to the hack. Please do not remove this comment. */ + if (rlc_um_get_buffer_occupancy(&rlc_union_p->rlc.um) > 5000000) { + free_mem_block(sdu_pP, __func__); + return RLC_OP_STATUS_OUT_OF_RESSOURCES; + } + new_sdu_p = get_free_mem_block (sdu_sizeP + sizeof (struct rlc_um_data_req_alloc), __func__); if (new_sdu_p != NULL) { diff --git a/openair3/UDP/udp_eNB_task.c b/openair3/UDP/udp_eNB_task.c index 527715eb7a..4d6bd6e033 100644 --- a/openair3/UDP/udp_eNB_task.c +++ b/openair3/UDP/udp_eNB_task.c @@ -270,10 +270,21 @@ void udp_eNB_receiver(struct udp_socket_desc_s *udp_sock_pP) n, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); #endif - if (itti_send_msg_to_task(udp_sock_pP->task_id, INSTANCE_DEFAULT, message_p) < 0) { + /* TODO: this is a hack. Let's accept failures and do nothing when + * it happens. Since itti_send_msg_to_task crashes when the message + * queue is full we wrote itti_try_send_msg_to_task that returns -1 + * if the queue is full. + */ + /* look for HACK_RLC_UM_LIMIT for others places related to the hack. Please do not remove this comment. */ + //if (itti_send_msg_to_task(udp_sock_pP->task_id, INSTANCE_DEFAULT, message_p) < 0) { + if (itti_try_send_msg_to_task(udp_sock_pP->task_id, INSTANCE_DEFAULT, message_p) < 0) { +#if 0 LOG_I(UDP_, "Failed to send message %d to task %d\n", UDP_DATA_IND, udp_sock_pP->task_id); +#endif + itti_free(TASK_UDP, message_p); + itti_free(TASK_UDP, forwarded_buffer); return; } }