diff --git a/openair2/LAYER2/nr_rlc/CMakeLists.txt b/openair2/LAYER2/nr_rlc/CMakeLists.txt index d72ac37d09..953822df10 100644 --- a/openair2/LAYER2/nr_rlc/CMakeLists.txt +++ b/openair2/LAYER2/nr_rlc/CMakeLists.txt @@ -4,7 +4,8 @@ add_library(nr_rlc_core OBJECT nr_rlc_entity_tm.c nr_rlc_entity_um.c nr_rlc_pdu.c - nr_rlc_sdu.c) + nr_rlc_sdu.c + nr_rlc_rx_manager.c) target_include_directories(nr_rlc_core PRIVATE ../../..) target_link_libraries(nr_rlc_core PRIVATE ${T_LIB}) add_library(nr_rlc STATIC diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity.c b/openair2/LAYER2/nr_rlc/nr_rlc_entity.c index 6d35460331..6103a7011a 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_entity.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity.c @@ -144,6 +144,8 @@ nr_rlc_entity_t *new_nr_rlc_entity_am( */ ret->common.txsdu_avg_time_to_tx = time_average_new(100 * 1000, 1024); + ret->rx = nr_rlc_new_rx_manager(1 << (sn_field_length - 1)); + return (nr_rlc_entity_t *)ret; } diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c b/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c index 4d288b48cb..a11f9b3027 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.c @@ -111,19 +111,17 @@ nr_rlc_sdu_segment_t *nr_rlc_tx_sdu_segment_list_add(nr_rlc_entity_am_t *entity, static int segment_already_received(nr_rlc_entity_am_t *entity, int sn, int so, int size) { - nr_rlc_pdu_t *l = entity->rx_list; + nr_rlc_pdu_t *l = nr_rlc_rx_manager_get_pdu_from_sn(entity->rx, sn); int covered; while (l != NULL && size > 0) { - if (l->sn == sn) { - if (l->so <= so && so < l->so + l->size) { - covered = l->size - (so - l->so); - size -= covered; - so += covered; - } else if (l->so <= so+size-1 && so+size-1 < l->so + l->size) { - covered = size - (l->so - so); - size -= covered; - } + if (l->so <= so && so < l->so + l->size) { + covered = l->size - (so - l->so); + size -= covered; + so += covered; + } else if (l->so <= so+size-1 && so+size-1 < l->so + l->size) { + covered = size - (l->so - so); + size -= covered; } l = l->next; } @@ -136,27 +134,20 @@ static int segment_already_received(nr_rlc_entity_am_t *entity, */ static int sdu_full(nr_rlc_entity_am_t *entity, int sn) { - nr_rlc_pdu_t *l = entity->rx_list; - int last_byte; - int new_last_byte; + nr_rlc_pdu_t *l = nr_rlc_rx_manager_get_pdu_from_sn(entity->rx, sn); - last_byte = -1; - while (l != NULL) { - if (l->sn == sn) - break; - l = l->next; - } + int last_byte = -1; /* check if the data has already been processed */ if (l != NULL && l->data == NULL) return 0; - while (l != NULL && l->sn == sn) { + while (l != NULL) { if (l->so > last_byte + 1) return 0; if (l->is_last) return 1; - new_last_byte = l->so + l->size - 1; + int new_last_byte = l->so + l->size - 1; if (new_last_byte > last_byte) last_byte = new_last_byte; l = l->next; @@ -168,14 +159,7 @@ static int sdu_full(nr_rlc_entity_am_t *entity, int sn) /* checks that an SDU has already been delivered */ static int sdu_delivered(nr_rlc_entity_am_t *entity, int sn) { - nr_rlc_pdu_t *l = entity->rx_list; - - while (l != NULL) { - if (l->sn == sn) - break; - l = l->next; - } - + nr_rlc_pdu_t *l = nr_rlc_rx_manager_get_pdu_from_sn(entity->rx, sn); return l != NULL && l->data == NULL; } @@ -185,22 +169,17 @@ static int sdu_delivered(nr_rlc_entity_am_t *entity, int sn) */ static int sdu_has_missing_bytes(nr_rlc_entity_am_t *entity, int sn) { - nr_rlc_pdu_t *l = entity->rx_list; + nr_rlc_pdu_t *l = nr_rlc_rx_manager_get_pdu_from_sn(entity->rx, sn); int last_byte; int new_last_byte; last_byte = -1; - while (l != NULL) { - if (l->sn == sn) - break; - l = l->next; - } /* check if the data has already been processed */ if (l != NULL && l->data == NULL) return 0; /* data already processed: no missing byte */ - while (l != NULL && l->sn == sn) { + while (l != NULL) { if (l->so > last_byte + 1) return 1; new_last_byte = l->so + l->size - 1; @@ -219,13 +198,10 @@ static void reassemble_and_deliver(nr_rlc_entity_am_t *entity, int sn) int so = 0; int bad_sdu = 0; - /* go to first segment of sn */ - pdu = entity->rx_list; - while (pdu->sn != sn) - pdu = pdu->next; + pdu = nr_rlc_rx_manager_get_pdu_from_sn(entity->rx, sn); /* reassemble - free 'data' of each segment after processing */ - while (pdu != NULL && pdu->sn == sn) { + while (pdu != NULL) { int len = pdu->size - (so - pdu->so); if (so + len > NR_SDU_MAX && !bad_sdu) { LOG_E(RLC, "%s:%d:%s: bad SDU, too big, discarding\n", @@ -262,7 +238,6 @@ static void reception_actions(nr_rlc_entity_am_t *entity, nr_rlc_pdu_t *pdu) if (sn_compare_rx(entity, x, entity->rx_next_highest) >= 0) entity->rx_next_highest = (x + 1) % entity->sn_modulus; - /* todo: room for optimization: we can run through rx_list only once */ if (sdu_full(entity, x)) { reassemble_and_deliver(entity, x); @@ -275,21 +250,26 @@ static void reception_actions(nr_rlc_entity_am_t *entity, nr_rlc_pdu_t *pdu) if (x == entity->rx_next) { /* update rx_next and free all delivered SDUs at the head of the - * rx_list + * RX list */ int rx_next = entity->rx_next; - while (entity->rx_list != NULL && entity->rx_list->data == NULL && - entity->rx_list->sn == rx_next) { + int count = 0; + nr_rlc_pdu_t *l = nr_rlc_rx_manager_get_pdu_from_sn(entity->rx, rx_next); + while (l != NULL && l->data == NULL) { /* free all segments of this SDU */ do { - nr_rlc_pdu_t *p = entity->rx_list; - entity->rx_list = p->next; + nr_rlc_pdu_t *p = l; + l = l->next; free(p); - } while (entity->rx_list != NULL && - entity->rx_list->sn == rx_next); + } while (l != NULL); + nr_rlc_rx_manager_clear_pdu(entity->rx, rx_next); + count++; rx_next = (rx_next + 1) % entity->sn_modulus; + l = nr_rlc_rx_manager_get_pdu_from_sn(entity->rx, rx_next); } entity->rx_next = rx_next; + nr_rlc_rx_manager_advance(entity->rx, count); + nr_rlc_rx_manager_set_start(entity->rx, rx_next); } } @@ -833,7 +813,7 @@ void nr_rlc_entity_am_recv_pdu(nr_rlc_entity_t *_entity, /* dicard PDU if rx buffer is full */ if (entity->rx_size + data_size > entity->rx_maxsize) { - LOG_D(RLC, "%s:%d:%s: warning: discard PDU, RX buffer full\n", + LOG_W(RLC, "%s:%d:%s: warning: discard PDU, RX buffer full\n", __FILE__, __LINE__, __FUNCTION__); goto discard; } @@ -864,8 +844,7 @@ void nr_rlc_entity_am_recv_pdu(nr_rlc_entity_t *_entity, entity->rx_size += data_size; pdu = nr_rlc_new_pdu(sn, so, is_first, is_last, buffer + size - data_size, data_size); - entity->rx_list = nr_rlc_pdu_list_add(sn_compare_rx, entity, - entity->rx_list, pdu); + nr_rlc_rx_manager_add_pdu(entity->rx, pdu); /* do reception actions (38.322 5.2.3.2.3) */ LOG_D(RLC, "RLC received PDU sn %d so %d is_first %d is_last %d data_size = %d \n", sn, so, is_first, is_last, data_size); @@ -1037,6 +1016,55 @@ typedef struct { nr_rlc_pdu_t *next; } missing_data_t; +/* returns the first RX PDU with rx_next <= sn <= rx_highest_status + * returns NULL if none is found + */ +static nr_rlc_pdu_t *get_first_rx_pdu(nr_rlc_entity_am_t *entity) +{ + int first_sn = entity->rx_next; + int last_sn = entity->rx_highest_status; + int sn = first_sn; + + while (sn != last_sn) { + nr_rlc_pdu_t *ret = nr_rlc_rx_manager_get_pdu_from_sn(entity->rx, sn); + if (ret) + return ret; + sn = (sn + 1) % entity->sn_modulus; + } + + return nr_rlc_rx_manager_get_pdu_from_sn(entity->rx, sn); +} + +/* find the next RX PDU coming after 'cur' + * two cases: + * - there is a PDU with same SN, return it + * - find PDU with SN <= rx_highest_status if any + * limit search to rx_highest_status + */ +static nr_rlc_pdu_t *get_next_rx_pdu(nr_rlc_entity_am_t *entity, + nr_rlc_pdu_t *cur) +{ + if (cur->next) + return cur->next; + + /* don't process past rx_highest_status */ + if (cur->sn == entity->rx_highest_status) + return NULL; + + int first_sn = (cur->sn + 1) % entity->sn_modulus; + int last_sn = entity->rx_highest_status; + int sn = first_sn; + + while (sn != last_sn) { + nr_rlc_pdu_t *ret = nr_rlc_rx_manager_get_pdu_from_sn(entity->rx, sn); + if (ret) + return ret; + sn = (sn + 1) % entity->sn_modulus; + } + + return nr_rlc_rx_manager_get_pdu_from_sn(entity->rx, sn); +} + /* todo: rewrite this function, too messy */ static missing_data_t next_missing(nr_rlc_entity_am_t *entity, nr_rlc_pdu_t *cur, int check_head) @@ -1076,8 +1104,10 @@ next_pdu: max_so = cur_max_so; last_reached = last_reached | cur->is_last; + nr_rlc_pdu_t *cur_next = get_next_rx_pdu(entity, cur); + /* no next? */ - if (cur->next == NULL) { + if (cur_next == NULL) { /* inform the caller that work is over */ ret.next = NULL; @@ -1118,7 +1148,7 @@ next_pdu: return ret; } - cur = cur->next; + cur = cur_next; /* no discontinuity in data => process to next PDU */ if (cur->sn == sn && max_so >= cur->so - 1) @@ -1374,7 +1404,7 @@ static int generate_status(nr_rlc_entity_am_t *entity, char *buffer, int size) /* first 3 bytes, ack_sn and e1 will be set later */ nr_rlc_pdu_encoder_put_bits(&encoder, 0, 8*3); - cur = entity->rx_list; + cur = get_first_rx_pdu(entity); /* store the position of the e1 bit to be set if * there is a nack following @@ -1538,7 +1568,7 @@ static int status_size(nr_rlc_entity_am_t *entity, int maxsize) /* minimum 3 bytes */ size = 3; - cur = entity->rx_list; + cur = get_first_rx_pdu(entity); while (cur != NULL) { m = next_missing(entity, cur, nack_count == 0); @@ -2020,8 +2050,6 @@ void nr_rlc_entity_am_discard_sdu(nr_rlc_entity_t *_entity, int sdu_id) static void clear_entity(nr_rlc_entity_am_t *entity) { - nr_rlc_pdu_t *cur_rx; - entity->rx_next = 0; entity->rx_next_status_trigger = 0; entity->rx_highest_status = 0; @@ -2045,13 +2073,7 @@ static void clear_entity(nr_rlc_entity_am_t *entity) entity->t_reassembly_start = 0; entity->t_status_prohibit_start = 0; - cur_rx = entity->rx_list; - while (cur_rx != NULL) { - nr_rlc_pdu_t *p = cur_rx; - cur_rx = cur_rx->next; - nr_rlc_free_pdu(p); - } - entity->rx_list = NULL; + nr_rlc_clear_rx_manager(entity->rx); entity->rx_size = 0; nr_rlc_free_sdu_segment_list(entity->tx_list); @@ -2081,6 +2103,7 @@ void nr_rlc_entity_am_delete(nr_rlc_entity_t *_entity) { nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity; clear_entity(entity); + nr_rlc_free_rx_manager(entity->rx); time_average_free(entity->common.txsdu_avg_time_to_tx); free(entity); } diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h b/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h index 4c626c4826..6b61e84a2f 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h +++ b/openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h @@ -25,6 +25,7 @@ #include "nr_rlc_entity.h" #include "nr_rlc_sdu.h" #include "nr_rlc_pdu.h" +#include "nr_rlc_rx_manager.h" typedef struct { nr_rlc_entity_t common; @@ -70,9 +71,9 @@ typedef struct { uint64_t t_status_prohibit_start; /* rx management */ - nr_rlc_pdu_t *rx_list; - int rx_size; - int rx_maxsize; + nr_rlc_rx_manager_t *rx; + int rx_size; + int rx_maxsize; /* tx management */ nr_rlc_sdu_segment_t *tx_list; diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_rx_manager.c b/openair2/LAYER2/nr_rlc/nr_rlc_rx_manager.c new file mode 100644 index 0000000000..eccf74a208 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_rx_manager.c @@ -0,0 +1,114 @@ +/* + * 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 "nr_rlc_rx_manager.h" + +#include "common/utils/utils.h" + +nr_rlc_rx_manager_t *nr_rlc_new_rx_manager(int size) +{ + nr_rlc_rx_manager_t *ret = calloc_or_fail(1, sizeof(*ret)); + + ret->rx_list = calloc_or_fail(size, sizeof(*ret->rx_list)); + ret->size = size; + + return ret; +} + +void nr_rlc_clear_rx_manager(nr_rlc_rx_manager_t *m) +{ + for (int i = 0; i < m->size; i++) { + nr_rlc_pdu_t *cur = m->rx_list[i]; + while (cur) { + nr_rlc_pdu_t *f = cur; + cur = cur->next; + nr_rlc_free_pdu(f); + } + m->rx_list[i] = 0; + } + m->start = 0; + m->sn_start = 0; +} + +void nr_rlc_free_rx_manager(nr_rlc_rx_manager_t *m) +{ + nr_rlc_clear_rx_manager(m); + free(m->rx_list); + free(m); +} + +static inline int get_pos(nr_rlc_rx_manager_t *m, int sn) +{ + return (m->start + (sn - m->sn_start + m->size * 2)) % m->size; +} + +nr_rlc_pdu_t *nr_rlc_rx_manager_get_pdu_from_sn(nr_rlc_rx_manager_t *m, int sn) +{ + int pos = get_pos(m, sn); + return m->rx_list[pos]; +} + +void nr_rlc_rx_manager_clear_pdu(nr_rlc_rx_manager_t *m, int sn) +{ + int pos = get_pos(m, sn); + m->rx_list[pos] = 0; +} + +/* beware: after calling this function 'sn_start' is invalid, caller has + * to call nr_rlc_rx_manager_set_start() just after + */ +void nr_rlc_rx_manager_advance(nr_rlc_rx_manager_t *m, int count) +{ + m->start += count; + m->start %= m->size; +} + +void nr_rlc_rx_manager_set_start(nr_rlc_rx_manager_t *m, int sn_start) +{ + m->sn_start = sn_start; +} + +void nr_rlc_rx_manager_add_pdu(nr_rlc_rx_manager_t *m, nr_rlc_pdu_t *pdu) +{ + int pos = get_pos(m, pdu->sn); + + nr_rlc_pdu_t head; + nr_rlc_pdu_t *cur; + nr_rlc_pdu_t *prev; + + head.next = m->rx_list[pos]; + cur = m->rx_list[pos]; + prev = &head; + + /* order by 'so' */ + while (cur != NULL) { + /* check if 'pdu' is before 'cur' in the list */ + if (cur->so > pdu->so) { + break; + } + prev = cur; + cur = cur->next; + } + prev->next = pdu; + pdu->next = cur; + + m->rx_list[pos] = head.next; +} diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_rx_manager.h b/openair2/LAYER2/nr_rlc/nr_rlc_rx_manager.h new file mode 100644 index 0000000000..7de6984ce7 --- /dev/null +++ b/openair2/LAYER2/nr_rlc/nr_rlc_rx_manager.h @@ -0,0 +1,49 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#ifndef _NR_RLC_RX_MANAGER_H_ +#define _NR_RLC_RX_MANAGER_H_ + +#include "nr_rlc_pdu.h" + +typedef struct { + /* 'rx_list' is managed as a circular buffer + * starts at rx_list[start] where is stored 'sn_start' + * for an RLC entity with an SN of n bits, its size is 2^(n-1) + * corresponding the the window size + */ + nr_rlc_pdu_t **rx_list; + int size; + int start; + int sn_start; +} nr_rlc_rx_manager_t; + +nr_rlc_rx_manager_t *nr_rlc_new_rx_manager(int size); +void nr_rlc_free_rx_manager(nr_rlc_rx_manager_t *m); +void nr_rlc_clear_rx_manager(nr_rlc_rx_manager_t *m); + +nr_rlc_pdu_t *nr_rlc_rx_manager_get_pdu_from_sn(nr_rlc_rx_manager_t *m, int sn); +void nr_rlc_rx_manager_clear_pdu(nr_rlc_rx_manager_t *m, int sn); +void nr_rlc_rx_manager_advance(nr_rlc_rx_manager_t *m, int count); +void nr_rlc_rx_manager_set_start(nr_rlc_rx_manager_t *m, int sn_start); +void nr_rlc_rx_manager_add_pdu(nr_rlc_rx_manager_t *m, nr_rlc_pdu_t *pdu); + +#endif /* _NR_RLC_RX_MANAGER_H_ */ diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_sdu.c b/openair2/LAYER2/nr_rlc/nr_rlc_sdu.c index 60f50d0afd..d9e15b98c9 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_sdu.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_sdu.c @@ -25,6 +25,7 @@ #include #include "LOG/log.h" +#include "common/utils/assertions.h" nr_rlc_sdu_segment_t *nr_rlc_new_sdu( char *buffer, int size, diff --git a/openair2/LAYER2/nr_rlc/tests/Makefile b/openair2/LAYER2/nr_rlc/tests/Makefile index 1e9cd432bf..2673d0955d 100644 --- a/openair2/LAYER2/nr_rlc/tests/Makefile +++ b/openair2/LAYER2/nr_rlc/tests/Makefile @@ -2,7 +2,7 @@ CC=gcc CFLAGS=-Wall -g --coverage -I. -I../../../.. -D_STANDALONE_TESTING_ LIB=nr_rlc_entity.o nr_rlc_entity_am.o nr_rlc_entity_um.o nr_rlc_entity_tm.o \ - nr_rlc_pdu.o nr_rlc_sdu.o time_stat.o + nr_rlc_pdu.o nr_rlc_sdu.o nr_rlc_rx_manager.o time_stat.o tests: @./run_tests.sh