Running TCP DL traffic with one connected UE showed a lot of fluctuations in throughput. After analysis it was found that sometimes the RLC UM PDU was not correct. It contained one byte more than it should. On the receiver side, the TCP packet contained in the RLC packet seems to be rejected by the TCP stack of the UE (it has one byte more than it should), leading to a brutal reduction of the throughput, probably due to some congestion detection in the TCP implementation. Or something. This hotfix seems to solve the problem. Using iperf in downlink with a 5MHz eNB, we see no more fluctuations, the traffic is very steady at 16.8Mb/s, as reported by the iperf server running on the phone. (17.5 in the PHY plot of the T tracer.) A rewrite of both the MAC and RLC UM packet generation is needed. The code is way too complex for what it does and may contain several similar problems that only trigger in specific rare conditions.
807 lines
32 KiB
C
807 lines
32 KiB
C
/*
|
|
* 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
|
|
*/
|
|
|
|
#define RLC_UM_MODULE 1
|
|
#define RLC_UM_SEGMENT_C 1
|
|
//-----------------------------------------------------------------------------
|
|
#include "platform_types.h"
|
|
#include "platform_constants.h"
|
|
//-----------------------------------------------------------------------------
|
|
#include <assert.h>
|
|
#include "assertions.h"
|
|
#include "msc.h"
|
|
#include "list.h"
|
|
#include "rlc_um.h"
|
|
#include "rlc_primitives.h"
|
|
#include "UTIL/LOG/log.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void
|
|
rlc_um_segment_10 (const protocol_ctxt_t* const ctxt_pP, rlc_um_entity_t *rlc_pP)
|
|
{
|
|
list_t pdus;
|
|
signed int pdu_remaining_size;
|
|
signed int test_pdu_remaining_size;
|
|
|
|
int nb_bytes_to_transmit = rlc_pP->nb_bytes_requested_by_mac;
|
|
rlc_um_pdu_sn_10_t *pdu_p;
|
|
struct mac_tb_req *pdu_tb_req_p;
|
|
mem_block_t *pdu_mem_p;
|
|
char *data;
|
|
char *data_sdu_p;
|
|
rlc_um_e_li_t *e_li_p;
|
|
struct rlc_um_tx_sdu_management *sdu_mngt_p;
|
|
unsigned int li_length_in_bytes;
|
|
unsigned int test_li_length_in_bytes;
|
|
unsigned int test_remaining_size_to_substract;
|
|
unsigned int test_remaining_num_li_to_substract;
|
|
unsigned int continue_fill_pdu_with_sdu;
|
|
unsigned int num_fill_sdu;
|
|
unsigned int test_num_li;
|
|
unsigned int fill_num_li;
|
|
mem_block_t *sdu_in_buffer = NULL;
|
|
unsigned int data_pdu_size;
|
|
|
|
unsigned int fi_first_byte_pdu_is_first_byte_sdu;
|
|
unsigned int fi_last_byte_pdu_is_last_byte_sdu;
|
|
unsigned int fi;
|
|
unsigned int max_li_overhead;
|
|
|
|
if (nb_bytes_to_transmit < 3) {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" NO SEGMENTATION nb_bytes to transmit = %d\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
nb_bytes_to_transmit);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
list_init (&pdus, NULL); // param string identifying the list is NULL
|
|
pdu_mem_p = NULL;
|
|
|
|
// not fine locking
|
|
RLC_UM_MUTEX_LOCK(&rlc_pP->lock_input_sdus, ctxt_pP, rlc_pP);
|
|
|
|
while ((list_get_head(&rlc_pP->input_sdus)) && (nb_bytes_to_transmit > 0)) {
|
|
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" nb_bytes_to_transmit %d BO %d\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
nb_bytes_to_transmit,
|
|
rlc_pP->buffer_occupancy);
|
|
#endif
|
|
|
|
// pdu_p management
|
|
if (!pdu_mem_p) {
|
|
if (rlc_pP->input_sdus.nb_elements <= 1) {
|
|
max_li_overhead = 0;
|
|
} else {
|
|
max_li_overhead = (((rlc_pP->input_sdus.nb_elements - 1) * 3) / 2) + ((rlc_pP->input_sdus.nb_elements - 1) % 2);
|
|
}
|
|
|
|
if (nb_bytes_to_transmit >= (rlc_pP->buffer_occupancy + rlc_pP->tx_header_min_length_in_bytes + max_li_overhead)) {
|
|
data_pdu_size = rlc_pP->buffer_occupancy + rlc_pP->tx_header_min_length_in_bytes + max_li_overhead;
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" alloc PDU size %d bytes to contain not all bytes requested by MAC but all BO of RLC@1\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
data_pdu_size);
|
|
#endif
|
|
} else {
|
|
data_pdu_size = nb_bytes_to_transmit;
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" alloc PDU size %d bytes to contain all bytes requested by MAC@1\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
data_pdu_size);
|
|
#endif
|
|
}
|
|
|
|
if (!(pdu_mem_p = get_free_mem_block (data_pdu_size + sizeof(struct mac_tb_req), __func__))) {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_E(RLC, PROTOCOL_RLC_UM_CTXT_FMT" ERROR COULD NOT GET NEW PDU, EXIT\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP));
|
|
#endif
|
|
RLC_UM_MUTEX_UNLOCK(&rlc_pP->lock_input_sdus);
|
|
return;
|
|
}
|
|
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" get new PDU %d bytes\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
data_pdu_size);
|
|
#endif
|
|
pdu_remaining_size = data_pdu_size - 2;
|
|
pdu_p = (rlc_um_pdu_sn_10_t*) (&pdu_mem_p->data[sizeof(struct mac_tb_req)]);
|
|
pdu_tb_req_p = (struct mac_tb_req*) (pdu_mem_p->data);
|
|
|
|
memset (pdu_mem_p->data, 0, sizeof (rlc_um_pdu_sn_10_t)+sizeof(struct mac_tb_req));
|
|
li_length_in_bytes = 1;
|
|
}
|
|
|
|
//----------------------------------------
|
|
// compute how many SDUS can fill the PDU
|
|
//----------------------------------------
|
|
continue_fill_pdu_with_sdu = 1;
|
|
num_fill_sdu = 0;
|
|
test_num_li = 0;
|
|
sdu_in_buffer = list_get_head(&rlc_pP->input_sdus);
|
|
test_pdu_remaining_size = pdu_remaining_size;
|
|
test_li_length_in_bytes = 1;
|
|
test_remaining_size_to_substract = 0;
|
|
test_remaining_num_li_to_substract = 0;
|
|
|
|
|
|
while ((sdu_in_buffer) && (continue_fill_pdu_with_sdu > 0)) {
|
|
sdu_mngt_p = ((struct rlc_um_tx_sdu_management *) (sdu_in_buffer->data));
|
|
|
|
if (sdu_mngt_p->sdu_remaining_size > test_pdu_remaining_size) {
|
|
// no LI
|
|
continue_fill_pdu_with_sdu = 0;
|
|
num_fill_sdu += 1;
|
|
test_pdu_remaining_size = 0;
|
|
test_remaining_size_to_substract = 0;
|
|
test_remaining_num_li_to_substract = 0;
|
|
} else if (sdu_mngt_p->sdu_remaining_size == test_pdu_remaining_size) {
|
|
// fi will indicate end of PDU is end of SDU, no need for LI
|
|
continue_fill_pdu_with_sdu = 0;
|
|
num_fill_sdu += 1;
|
|
test_pdu_remaining_size = 0;
|
|
test_remaining_size_to_substract = 0;
|
|
test_remaining_num_li_to_substract = 0;
|
|
} else if ((sdu_mngt_p->sdu_remaining_size + (test_li_length_in_bytes ^ 3)) == test_pdu_remaining_size ) {
|
|
// no LI
|
|
continue_fill_pdu_with_sdu = 0;
|
|
num_fill_sdu += 1;
|
|
test_pdu_remaining_size = 0;
|
|
test_remaining_size_to_substract = 0;
|
|
test_remaining_num_li_to_substract = 0;
|
|
//pdu_remaining_size = pdu_remaining_size - (test_li_length_in_bytes ^ 3);
|
|
} else if ((sdu_mngt_p->sdu_remaining_size + (test_li_length_in_bytes ^ 3)) < test_pdu_remaining_size ) {
|
|
test_num_li += 1;
|
|
num_fill_sdu += 1;
|
|
test_pdu_remaining_size = test_pdu_remaining_size - (sdu_mngt_p->sdu_remaining_size + (test_li_length_in_bytes ^ 3));
|
|
test_remaining_size_to_substract = test_li_length_in_bytes ^ 3;
|
|
test_remaining_num_li_to_substract = 1;
|
|
test_li_length_in_bytes = test_li_length_in_bytes ^ 3;
|
|
} else {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" sdu_mngt_p->sdu_remaining_size=%d test_pdu_remaining_size=%d test_li_length_in_bytes=%d\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
sdu_mngt_p->sdu_remaining_size,
|
|
test_pdu_remaining_size,
|
|
test_li_length_in_bytes ^ 3);
|
|
#endif
|
|
// reduce the size of the PDU
|
|
continue_fill_pdu_with_sdu = 0;
|
|
num_fill_sdu += 1;
|
|
test_pdu_remaining_size = 0;
|
|
test_remaining_size_to_substract = 0;
|
|
test_remaining_num_li_to_substract = 0;
|
|
pdu_remaining_size = pdu_remaining_size - 1;
|
|
}
|
|
|
|
sdu_in_buffer = sdu_in_buffer->next;
|
|
}
|
|
|
|
if (test_remaining_num_li_to_substract > 0) {
|
|
// there is a LI that is not necessary
|
|
test_num_li = test_num_li - 1;
|
|
pdu_remaining_size = pdu_remaining_size - test_remaining_size_to_substract;
|
|
}
|
|
|
|
//----------------------------------------
|
|
// Do the real filling of the pdu_p
|
|
//----------------------------------------
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" data shift %d Bytes num_li %d\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
((test_num_li*3) +1) >> 1,
|
|
test_num_li);
|
|
#endif
|
|
|
|
data = ((char*)(&pdu_p->data[((test_num_li*3) +1) >> 1]));
|
|
e_li_p = (rlc_um_e_li_t*)(pdu_p->data);
|
|
continue_fill_pdu_with_sdu = 1;
|
|
li_length_in_bytes = 1;
|
|
fill_num_li = 0;
|
|
fi_first_byte_pdu_is_first_byte_sdu = 0;
|
|
fi_last_byte_pdu_is_last_byte_sdu = 0;
|
|
|
|
sdu_in_buffer = list_get_head(&rlc_pP->input_sdus);
|
|
|
|
if (
|
|
((struct rlc_um_tx_sdu_management *) (sdu_in_buffer->data))->sdu_remaining_size ==
|
|
((struct rlc_um_tx_sdu_management *) (sdu_in_buffer->data))->sdu_size) {
|
|
fi_first_byte_pdu_is_first_byte_sdu = 1;
|
|
}
|
|
|
|
while ((sdu_in_buffer) && (continue_fill_pdu_with_sdu > 0)) {
|
|
sdu_mngt_p = ((struct rlc_um_tx_sdu_management *) (sdu_in_buffer->data));
|
|
|
|
if (sdu_mngt_p->sdu_segmented_size == 0) {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" GET NEW SDU %p AVAILABLE SIZE %d Bytes\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
sdu_mngt_p,
|
|
sdu_mngt_p->sdu_remaining_size);
|
|
#endif
|
|
} else {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" GET AGAIN SDU %p REMAINING AVAILABLE SIZE %d Bytes / %d Bytes \n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
sdu_mngt_p,
|
|
sdu_mngt_p->sdu_remaining_size,
|
|
sdu_mngt_p->sdu_size);
|
|
#endif
|
|
}
|
|
|
|
data_sdu_p = (char *) &(sdu_in_buffer->data[sizeof (struct rlc_um_tx_sdu_management) + sdu_mngt_p->sdu_segmented_size]);
|
|
|
|
if (sdu_mngt_p->sdu_remaining_size > pdu_remaining_size) {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" Filling all remaining PDU with %d bytes\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
pdu_remaining_size);
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" pdu_mem_p %p pdu_mem_p->data %p pdu_p %p pdu_p->data %p data %p data_sdu_p %p pdu_remaining_size %d\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
pdu_mem_p,
|
|
pdu_mem_p->data,
|
|
pdu_p,
|
|
pdu_p->data,
|
|
data,
|
|
data_sdu_p,
|
|
pdu_remaining_size);
|
|
#endif
|
|
|
|
memcpy(data, data_sdu_p, pdu_remaining_size);
|
|
sdu_mngt_p->sdu_remaining_size = sdu_mngt_p->sdu_remaining_size - pdu_remaining_size;
|
|
sdu_mngt_p->sdu_segmented_size = sdu_mngt_p->sdu_segmented_size + pdu_remaining_size;
|
|
fi_last_byte_pdu_is_last_byte_sdu = 0;
|
|
// no LI
|
|
rlc_pP->buffer_occupancy -= pdu_remaining_size;
|
|
continue_fill_pdu_with_sdu = 0;
|
|
pdu_remaining_size = 0;
|
|
} else if (sdu_mngt_p->sdu_remaining_size == pdu_remaining_size) {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" Exactly Filling remaining PDU with %d remaining bytes of SDU\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
pdu_remaining_size);
|
|
#endif
|
|
memcpy(data, data_sdu_p, pdu_remaining_size);
|
|
|
|
// free SDU
|
|
rlc_pP->buffer_occupancy -= sdu_mngt_p->sdu_remaining_size;
|
|
sdu_in_buffer = list_remove_head(&rlc_pP->input_sdus);
|
|
free_mem_block (sdu_in_buffer, __func__);
|
|
sdu_in_buffer = list_get_head(&rlc_pP->input_sdus);
|
|
sdu_mngt_p = NULL;
|
|
|
|
|
|
fi_last_byte_pdu_is_last_byte_sdu = 1;
|
|
// fi will indicate end of PDU is end of SDU, no need for LI
|
|
continue_fill_pdu_with_sdu = 0;
|
|
pdu_remaining_size = 0;
|
|
} else if ((sdu_mngt_p->sdu_remaining_size + (li_length_in_bytes ^ 3)) < pdu_remaining_size ) {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" Filling PDU with %d all remaining bytes of SDU\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
sdu_mngt_p->sdu_remaining_size);
|
|
#endif
|
|
memcpy(data, data_sdu_p, sdu_mngt_p->sdu_remaining_size);
|
|
data = &data[sdu_mngt_p->sdu_remaining_size];
|
|
li_length_in_bytes = li_length_in_bytes ^ 3;
|
|
fill_num_li += 1;
|
|
|
|
if (li_length_in_bytes == 2) {
|
|
if (fill_num_li == test_num_li) {
|
|
//e_li_p->e1 = 0;
|
|
e_li_p->b1 = 0;
|
|
} else {
|
|
//e_li_p->e1 = 1;
|
|
e_li_p->b1 = 0x80;
|
|
}
|
|
|
|
//e_li_p->li1 = sdu_mngt_p->sdu_remaining_size;
|
|
e_li_p->b1 = e_li_p->b1 | (sdu_mngt_p->sdu_remaining_size >> 4);
|
|
e_li_p->b2 = sdu_mngt_p->sdu_remaining_size << 4;
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" set e_li_p->b1=%02X set e_li_p->b2=%02X fill_num_li=%d test_num_li=%d\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
e_li_p->b1,
|
|
e_li_p->b2,
|
|
fill_num_li,
|
|
test_num_li);
|
|
#endif
|
|
} else {
|
|
if (fill_num_li != test_num_li) {
|
|
//e_li_p->e2 = 1;
|
|
e_li_p->b2 = e_li_p->b2 | 0x08;
|
|
}
|
|
|
|
//e_li_p->li2 = sdu_mngt_p->sdu_remaining_size;
|
|
e_li_p->b2 = e_li_p->b2 | (sdu_mngt_p->sdu_remaining_size >> 8);
|
|
e_li_p->b3 = sdu_mngt_p->sdu_remaining_size & 0xFF;
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" set e_li_p->b2=%02X set e_li_p->b3=%02X fill_num_li=%d test_num_li=%d\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
e_li_p->b2,
|
|
e_li_p->b3,
|
|
fill_num_li,
|
|
test_num_li);
|
|
#endif
|
|
e_li_p++;
|
|
}
|
|
|
|
pdu_remaining_size = pdu_remaining_size - (sdu_mngt_p->sdu_remaining_size + li_length_in_bytes);
|
|
|
|
// free SDU
|
|
rlc_pP->buffer_occupancy -= sdu_mngt_p->sdu_remaining_size;
|
|
sdu_in_buffer = list_remove_head(&rlc_pP->input_sdus);
|
|
free_mem_block (sdu_in_buffer, __func__);
|
|
sdu_in_buffer = list_get_head(&rlc_pP->input_sdus);
|
|
sdu_mngt_p = NULL;
|
|
|
|
} else {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" Filling PDU with %d all remaining bytes of SDU and reduce TB size by %d bytes\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
sdu_mngt_p->sdu_remaining_size,
|
|
pdu_remaining_size - sdu_mngt_p->sdu_remaining_size);
|
|
#endif
|
|
//#if !EXMIMO
|
|
// assert(1!=1);
|
|
//#endif
|
|
memcpy(data, data_sdu_p, sdu_mngt_p->sdu_remaining_size);
|
|
// reduce the size of the PDU
|
|
continue_fill_pdu_with_sdu = 0;
|
|
fi_last_byte_pdu_is_last_byte_sdu = 1;
|
|
pdu_remaining_size = pdu_remaining_size - sdu_mngt_p->sdu_remaining_size;
|
|
// free SDU
|
|
rlc_pP->buffer_occupancy -= sdu_mngt_p->sdu_remaining_size;
|
|
sdu_in_buffer = list_remove_head(&rlc_pP->input_sdus);
|
|
free_mem_block (sdu_in_buffer, __func__);
|
|
sdu_in_buffer = list_get_head(&rlc_pP->input_sdus);
|
|
sdu_mngt_p = NULL;
|
|
|
|
}
|
|
}
|
|
|
|
// set framing info
|
|
if (fi_first_byte_pdu_is_first_byte_sdu) {
|
|
fi = 0;
|
|
} else {
|
|
fi = 2;
|
|
}
|
|
|
|
if (!fi_last_byte_pdu_is_last_byte_sdu) {
|
|
fi = fi + 1;
|
|
}
|
|
|
|
pdu_p->b1 = (fi << 3); //pdu_p->b1 |
|
|
|
|
// set fist e bit
|
|
if (fill_num_li > 0) {
|
|
pdu_p->b1 = pdu_p->b1 | 0x04;
|
|
}
|
|
|
|
pdu_p->b1 = pdu_p->b1 | ((rlc_pP->vt_us >> 8) & 0x03);
|
|
pdu_p->b2 = rlc_pP->vt_us & 0xFF;
|
|
rlc_pP->vt_us = rlc_pP->vt_us+1;
|
|
|
|
pdu_tb_req_p->data_ptr = (unsigned char*)pdu_p;
|
|
pdu_tb_req_p->tb_size = data_pdu_size - pdu_remaining_size;
|
|
list_add_tail_eurecom (pdu_mem_p, &rlc_pP->pdus_to_mac_layer);
|
|
#if TRACE_RLC_PAYLOAD
|
|
rlc_util_print_hex_octets(RLC, pdu_mem_p->data, data_pdu_size);
|
|
#endif
|
|
AssertFatal( pdu_tb_req_p->tb_size > 0 , "SEGMENT10: FINAL RLC UM PDU LENGTH %d", pdu_tb_req_p->tb_size);
|
|
pdu_p = NULL;
|
|
pdu_mem_p = NULL;
|
|
|
|
//nb_bytes_to_transmit = nb_bytes_to_transmit - data_pdu_size;
|
|
nb_bytes_to_transmit = 0; // 1 PDU only
|
|
}
|
|
|
|
RLC_UM_MUTEX_UNLOCK(&rlc_pP->lock_input_sdus);
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
void
|
|
rlc_um_segment_5 (const protocol_ctxt_t* const ctxt_pP, rlc_um_entity_t *rlc_pP)
|
|
{
|
|
list_t pdus;
|
|
signed int pdu_remaining_size = 0;
|
|
signed int test_pdu_remaining_size = 0;
|
|
|
|
int nb_bytes_to_transmit = rlc_pP->nb_bytes_requested_by_mac;
|
|
rlc_um_pdu_sn_5_t *pdu_p = NULL;
|
|
struct mac_tb_req *pdu_tb_req_p = NULL;
|
|
mem_block_t *pdu_mem_p = NULL;
|
|
char *data = NULL;
|
|
char *data_sdu_p = NULL;
|
|
rlc_um_e_li_t *e_li_p = NULL;
|
|
struct rlc_um_tx_sdu_management *sdu_mngt_p = NULL;
|
|
unsigned int li_length_in_bytes = 0;
|
|
unsigned int test_li_length_in_bytes = 0;
|
|
unsigned int test_remaining_size_to_substract = 0;
|
|
unsigned int test_remaining_num_li_to_substract = 0;
|
|
unsigned int continue_fill_pdu_with_sdu = 0;
|
|
unsigned int num_fill_sdu = 0;
|
|
unsigned int test_num_li = 0;
|
|
unsigned int fill_num_li = 0;
|
|
mem_block_t *sdu_in_buffer = NULL;
|
|
unsigned int data_pdu_size = 0;
|
|
|
|
unsigned int fi_first_byte_pdu_is_first_byte_sdu = 0;
|
|
unsigned int fi_last_byte_pdu_is_last_byte_sdu = 0;
|
|
unsigned int fi = 0;
|
|
unsigned int max_li_overhead = 0;
|
|
|
|
if (nb_bytes_to_transmit < 2) {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" NO SEGMENTATION5 nb_bytes to transmit = %d\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
nb_bytes_to_transmit);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
list_init (&pdus, NULL); // param string identifying the list is NULL
|
|
pdu_mem_p = NULL;
|
|
|
|
RLC_UM_MUTEX_LOCK(&rlc_pP->lock_input_sdus, ctxt_pP, rlc_pP);
|
|
|
|
while ((list_get_head(&rlc_pP->input_sdus)) && (nb_bytes_to_transmit > 0)) {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT"nb_bytes_to_transmit %d BO %d\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
nb_bytes_to_transmit,
|
|
rlc_pP->buffer_occupancy);
|
|
#endif
|
|
|
|
// pdu_p management
|
|
if (!pdu_mem_p) {
|
|
if (rlc_pP->input_sdus.nb_elements <= 1) {
|
|
max_li_overhead = 0;
|
|
} else {
|
|
max_li_overhead = (((rlc_pP->input_sdus.nb_elements - 1) * 3) / 2) + ((rlc_pP->input_sdus.nb_elements - 1) % 2);
|
|
}
|
|
|
|
if (nb_bytes_to_transmit >= (rlc_pP->buffer_occupancy + rlc_pP->tx_header_min_length_in_bytes + max_li_overhead)) {
|
|
data_pdu_size = rlc_pP->buffer_occupancy + rlc_pP->tx_header_min_length_in_bytes + max_li_overhead;
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" alloc PDU size %d bytes to contain not all bytes requested by MAC but all BO of RLC@1\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
data_pdu_size);
|
|
#endif
|
|
} else {
|
|
data_pdu_size = nb_bytes_to_transmit;
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" alloc PDU size %d bytes to contain all bytes requested by MAC@1\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
data_pdu_size);
|
|
#endif
|
|
}
|
|
|
|
if (!(pdu_mem_p = get_free_mem_block (data_pdu_size + sizeof(struct mac_tb_req), __func__))) {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_E(RLC, PROTOCOL_RLC_UM_CTXT_FMT" ERROR COULD NOT GET NEW PDU, EXIT\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP));
|
|
#endif
|
|
RLC_UM_MUTEX_UNLOCK(&rlc_pP->lock_input_sdus);
|
|
return;
|
|
}
|
|
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" get new PDU %d bytes\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
data_pdu_size);
|
|
#endif
|
|
pdu_remaining_size = data_pdu_size - 1;
|
|
pdu_p = (rlc_um_pdu_sn_5_t*) (&pdu_mem_p->data[sizeof(struct mac_tb_req)]);
|
|
pdu_tb_req_p = (struct mac_tb_req*) (pdu_mem_p->data);
|
|
|
|
memset (pdu_mem_p->data, 0, sizeof (rlc_um_pdu_sn_5_t)+sizeof(struct mac_tb_req));
|
|
li_length_in_bytes = 1;
|
|
}
|
|
|
|
//----------------------------------------
|
|
// compute how many SDUS can fill the PDU
|
|
//----------------------------------------
|
|
continue_fill_pdu_with_sdu = 1;
|
|
num_fill_sdu = 0;
|
|
test_num_li = 0;
|
|
sdu_in_buffer = list_get_head(&rlc_pP->input_sdus);
|
|
test_pdu_remaining_size = pdu_remaining_size;
|
|
test_li_length_in_bytes = 1;
|
|
test_remaining_size_to_substract = 0;
|
|
test_remaining_num_li_to_substract = 0;
|
|
|
|
|
|
while ((sdu_in_buffer) && (continue_fill_pdu_with_sdu > 0)) {
|
|
sdu_mngt_p = ((struct rlc_um_tx_sdu_management *) (sdu_in_buffer->data));
|
|
|
|
if (sdu_mngt_p->sdu_remaining_size > test_pdu_remaining_size) {
|
|
// no LI
|
|
continue_fill_pdu_with_sdu = 0;
|
|
num_fill_sdu += 1;
|
|
test_pdu_remaining_size = 0;
|
|
test_remaining_size_to_substract = 0;
|
|
test_remaining_num_li_to_substract = 0;
|
|
} else if (sdu_mngt_p->sdu_remaining_size == test_pdu_remaining_size) {
|
|
// fi will indicate end of PDU is end of SDU, no need for LI
|
|
continue_fill_pdu_with_sdu = 0;
|
|
num_fill_sdu += 1;
|
|
test_pdu_remaining_size = 0;
|
|
test_remaining_size_to_substract = 0;
|
|
test_remaining_num_li_to_substract = 0;
|
|
} else if ((sdu_mngt_p->sdu_remaining_size + (test_li_length_in_bytes ^ 3)) == test_pdu_remaining_size ) {
|
|
// no LI
|
|
continue_fill_pdu_with_sdu = 0;
|
|
num_fill_sdu += 1;
|
|
test_pdu_remaining_size = 0;
|
|
test_remaining_size_to_substract = 0;
|
|
test_remaining_num_li_to_substract = 0;
|
|
pdu_remaining_size = pdu_remaining_size - (test_li_length_in_bytes ^ 3);
|
|
data_pdu_size -= (test_li_length_in_bytes ^ 3);//modifier pour duy
|
|
} else if ((sdu_mngt_p->sdu_remaining_size + (test_li_length_in_bytes ^ 3)) < test_pdu_remaining_size ) {
|
|
test_num_li += 1;
|
|
num_fill_sdu += 1;
|
|
test_pdu_remaining_size = test_pdu_remaining_size - (sdu_mngt_p->sdu_remaining_size + (test_li_length_in_bytes ^ 3));
|
|
test_remaining_size_to_substract = test_li_length_in_bytes ^ 3;
|
|
test_remaining_num_li_to_substract = 1;
|
|
test_li_length_in_bytes = test_li_length_in_bytes ^ 3;
|
|
} else {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" sdu_mngt_p->sdu_remaining_size=%d test_pdu_remaining_size=%d test_li_length_in_bytes=%d\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
sdu_mngt_p->sdu_remaining_size,
|
|
test_pdu_remaining_size,
|
|
test_li_length_in_bytes ^ 3);
|
|
#endif
|
|
// reduce the size of the PDU
|
|
continue_fill_pdu_with_sdu = 0;
|
|
num_fill_sdu += 1;
|
|
test_pdu_remaining_size = 0;
|
|
test_remaining_size_to_substract = 0;
|
|
test_remaining_num_li_to_substract = 0;
|
|
pdu_remaining_size = pdu_remaining_size - 1;
|
|
data_pdu_size -= 1;//modifier pour duy
|
|
}
|
|
|
|
sdu_in_buffer = sdu_in_buffer->next;
|
|
}
|
|
|
|
if (test_remaining_num_li_to_substract > 0) {
|
|
// there is a LI that is not necessary
|
|
test_num_li = test_num_li - 1;
|
|
pdu_remaining_size = pdu_remaining_size - test_remaining_size_to_substract;
|
|
}
|
|
|
|
//----------------------------------------
|
|
// Do the real filling of the pdu_p
|
|
//----------------------------------------
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" data shift %d Bytes num_li %d\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
((test_num_li*3) +1) >> 1,
|
|
test_num_li);
|
|
#endif
|
|
|
|
data = ((char*)(&pdu_p->data[((test_num_li*3) +1) >> 1]));
|
|
e_li_p = (rlc_um_e_li_t*)(pdu_p->data);
|
|
continue_fill_pdu_with_sdu = 1;
|
|
li_length_in_bytes = 1;
|
|
fill_num_li = 0;
|
|
fi_first_byte_pdu_is_first_byte_sdu = 0;
|
|
fi_last_byte_pdu_is_last_byte_sdu = 0;
|
|
|
|
sdu_in_buffer = list_get_head(&rlc_pP->input_sdus);
|
|
|
|
if (
|
|
((struct rlc_um_tx_sdu_management *) (sdu_in_buffer->data))->sdu_remaining_size ==
|
|
((struct rlc_um_tx_sdu_management *) (sdu_in_buffer->data))->sdu_size) {
|
|
fi_first_byte_pdu_is_first_byte_sdu = 1;
|
|
}
|
|
|
|
while ((sdu_in_buffer) && (continue_fill_pdu_with_sdu > 0)) {
|
|
sdu_mngt_p = ((struct rlc_um_tx_sdu_management *) (sdu_in_buffer->data));
|
|
|
|
if (sdu_mngt_p->sdu_segmented_size == 0) {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" GET NEW SDU %p AVAILABLE SIZE %d Bytes\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
sdu_mngt_p,
|
|
sdu_mngt_p->sdu_remaining_size);
|
|
#endif
|
|
} else {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" GET AGAIN SDU %p REMAINING AVAILABLE SIZE %d Bytes / %d Bytes \n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
sdu_mngt_p,
|
|
sdu_mngt_p->sdu_remaining_size,
|
|
sdu_mngt_p->sdu_size);
|
|
#endif
|
|
}
|
|
|
|
data_sdu_p = (char*) &(sdu_in_buffer->data[sizeof (struct rlc_um_tx_sdu_management) + sdu_mngt_p->sdu_segmented_size]);
|
|
|
|
if (sdu_mngt_p->sdu_remaining_size > pdu_remaining_size) {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" Filling all remaining PDU with %d bytes\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
pdu_remaining_size);
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" pdu_mem_p %p pdu_p %p pdu_p->data %p data %p data_sdu_p %p pdu_remaining_size %d\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
pdu_mem_p,
|
|
pdu_p,
|
|
pdu_p->data,
|
|
data,
|
|
data_sdu_p,
|
|
pdu_remaining_size);
|
|
#endif
|
|
|
|
memcpy(data, data_sdu_p, pdu_remaining_size);
|
|
sdu_mngt_p->sdu_remaining_size = sdu_mngt_p->sdu_remaining_size - pdu_remaining_size;
|
|
sdu_mngt_p->sdu_segmented_size = sdu_mngt_p->sdu_segmented_size + pdu_remaining_size;
|
|
fi_last_byte_pdu_is_last_byte_sdu = 0;
|
|
// no LI
|
|
rlc_pP->buffer_occupancy -= pdu_remaining_size;
|
|
continue_fill_pdu_with_sdu = 0;
|
|
pdu_remaining_size = 0;
|
|
} else if (sdu_mngt_p->sdu_remaining_size == pdu_remaining_size) {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" Exactly Filling remaining PDU with %d remaining bytes of SDU\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
pdu_remaining_size);
|
|
#endif
|
|
memcpy(data, data_sdu_p, pdu_remaining_size);
|
|
// free SDU
|
|
rlc_pP->buffer_occupancy -= sdu_mngt_p->sdu_remaining_size;
|
|
sdu_in_buffer = list_remove_head(&rlc_pP->input_sdus);
|
|
free_mem_block (sdu_in_buffer, __func__);
|
|
sdu_in_buffer = list_get_head(&rlc_pP->input_sdus);
|
|
sdu_mngt_p = NULL;
|
|
|
|
fi_last_byte_pdu_is_last_byte_sdu = 1;
|
|
// fi will indicate end of PDU is end of SDU, no need for LI
|
|
continue_fill_pdu_with_sdu = 0;
|
|
pdu_remaining_size = 0;
|
|
} else if ((sdu_mngt_p->sdu_remaining_size + (li_length_in_bytes ^ 3)) < pdu_remaining_size ) {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" Filling PDU with %d all remaining bytes of SDU\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
sdu_mngt_p->sdu_remaining_size);
|
|
#endif
|
|
memcpy(data, data_sdu_p, sdu_mngt_p->sdu_remaining_size);
|
|
data = &data[sdu_mngt_p->sdu_remaining_size];
|
|
li_length_in_bytes = li_length_in_bytes ^ 3;
|
|
fill_num_li += 1;
|
|
|
|
if (li_length_in_bytes == 2) {
|
|
if (fill_num_li == test_num_li) {
|
|
//e_li_p->e1 = 0;
|
|
e_li_p->b1 = 0;
|
|
} else {
|
|
//e_li_p->e1 = 1;
|
|
e_li_p->b1 = 0x80;
|
|
}
|
|
|
|
//e_li_p->li1 = sdu_mngt_p->sdu_remaining_size;
|
|
e_li_p->b1 = e_li_p->b1 | (sdu_mngt_p->sdu_remaining_size >> 4);
|
|
e_li_p->b2 = sdu_mngt_p->sdu_remaining_size << 4;
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" set e_li_p->b1=%02X set e_li_p->b2=%02X fill_num_li=%d test_num_li=%d\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
e_li_p->b1,
|
|
e_li_p->b2,
|
|
fill_num_li,
|
|
test_num_li);
|
|
#endif
|
|
} else {
|
|
if (fill_num_li != test_num_li) {
|
|
//e_li_p->e2 = 1;
|
|
e_li_p->b2 = e_li_p->b2 | 0x08;
|
|
}
|
|
|
|
//e_li_p->li2 = sdu_mngt_p->sdu_remaining_size;
|
|
e_li_p->b2 = e_li_p->b2 | (sdu_mngt_p->sdu_remaining_size >> 8);
|
|
e_li_p->b3 = sdu_mngt_p->sdu_remaining_size & 0xFF;
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" set e_li_p->b2=%02X set e_li_p->b3=%02X fill_num_li=%d test_num_li=%d\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
e_li_p->b2,
|
|
e_li_p->b3,
|
|
fill_num_li,
|
|
test_num_li);
|
|
#endif
|
|
e_li_p++;
|
|
}
|
|
|
|
pdu_remaining_size = pdu_remaining_size - (sdu_mngt_p->sdu_remaining_size + li_length_in_bytes);
|
|
|
|
// free SDU
|
|
rlc_pP->buffer_occupancy -= sdu_mngt_p->sdu_remaining_size;
|
|
sdu_in_buffer = list_remove_head(&rlc_pP->input_sdus);
|
|
free_mem_block (sdu_in_buffer, __func__);
|
|
sdu_in_buffer = list_get_head(&rlc_pP->input_sdus);
|
|
sdu_mngt_p = NULL;
|
|
|
|
} else {
|
|
#if TRACE_RLC_UM_SEGMENT
|
|
LOG_D(RLC, PROTOCOL_RLC_UM_CTXT_FMT" Filling PDU with %d all remaining bytes of SDU and reduce TB size by %d bytes\n",
|
|
PROTOCOL_RLC_UM_CTXT_ARGS(ctxt_pP,rlc_pP),
|
|
sdu_mngt_p->sdu_remaining_size,
|
|
pdu_remaining_size - sdu_mngt_p->sdu_remaining_size);
|
|
#endif
|
|
assert(1!=1);
|
|
memcpy(data, data_sdu_p, sdu_mngt_p->sdu_remaining_size);
|
|
// reduce the size of the PDU
|
|
continue_fill_pdu_with_sdu = 0;
|
|
fi_last_byte_pdu_is_last_byte_sdu = 1;
|
|
pdu_remaining_size = pdu_remaining_size - sdu_mngt_p->sdu_remaining_size;
|
|
// free SDU
|
|
rlc_pP->buffer_occupancy -= sdu_mngt_p->sdu_remaining_size;
|
|
sdu_in_buffer = list_remove_head(&rlc_pP->input_sdus);
|
|
free_mem_block (sdu_in_buffer, __func__);
|
|
sdu_in_buffer = list_get_head(&rlc_pP->input_sdus);
|
|
sdu_mngt_p = NULL;
|
|
}
|
|
}
|
|
|
|
// set framing info
|
|
if (fi_first_byte_pdu_is_first_byte_sdu) {
|
|
fi = 0;
|
|
} else {
|
|
fi = 2;
|
|
}
|
|
|
|
if (!fi_last_byte_pdu_is_last_byte_sdu) {
|
|
fi = fi + 1;
|
|
}
|
|
|
|
pdu_p->b1 = (fi << 6); //pdu_p->b1 |
|
|
|
|
// set fist e bit
|
|
if (fill_num_li > 0) {
|
|
pdu_p->b1 = pdu_p->b1 | 0x20;
|
|
}
|
|
|
|
pdu_p->b1 = pdu_p->b1 | (rlc_pP->vt_us & 0x1F);
|
|
rlc_pP->vt_us = rlc_pP->vt_us+1;
|
|
|
|
pdu_tb_req_p->data_ptr = (unsigned char*)pdu_p;
|
|
pdu_tb_req_p->tb_size = data_pdu_size - pdu_remaining_size;
|
|
list_add_tail_eurecom (pdu_mem_p, &rlc_pP->pdus_to_mac_layer);
|
|
#if TRACE_RLC_PAYLOAD
|
|
rlc_util_print_hex_octets(RLC, (unsigned char*)pdu_mem_p->data, data_pdu_size);
|
|
#endif
|
|
AssertFatal( pdu_tb_req_p->tb_size > 0 , "SEGMENT5: FINAL RLC UM PDU LENGTH %d", pdu_tb_req_p->tb_size);
|
|
|
|
pdu_p = NULL;
|
|
pdu_mem_p = NULL;
|
|
|
|
//nb_bytes_to_transmit = nb_bytes_to_transmit - data_pdu_size;
|
|
nb_bytes_to_transmit = 0; // 1 PDU only
|
|
}
|
|
|
|
RLC_UM_MUTEX_UNLOCK(&rlc_pP->lock_input_sdus);
|
|
}
|
|
|