implementation of intrusive list abstract classes to be used in RLC for lists of segments

master
Francisco 4 years ago committed by Francisco Paisana
parent b06f16891d
commit 7f0083a985

@ -16,6 +16,7 @@
#include "srsran/adt/accumulators.h" #include "srsran/adt/accumulators.h"
#include "srsran/adt/circular_array.h" #include "srsran/adt/circular_array.h"
#include "srsran/adt/circular_map.h" #include "srsran/adt/circular_map.h"
#include "srsran/adt/intrusive_list.h"
#include "srsran/common/buffer_pool.h" #include "srsran/common/buffer_pool.h"
#include "srsran/common/common.h" #include "srsran/common/common.h"
#include "srsran/common/srsran_assert.h" #include "srsran/common/srsran_assert.h"
@ -33,94 +34,101 @@ namespace srsran {
#undef RLC_AM_BUFFER_DEBUG #undef RLC_AM_BUFFER_DEBUG
struct pdcp_pdu_segment_list;
struct rlc_pdu_segment_list;
/// Pool that manages the allocation of RLC AM PDU Segments to RLC PDUs and tracking of segments ACK state
struct rlc_am_pdu_segment_pool {
const static size_t MAX_POOL_SIZE = 16384;
using rlc_list_tag = default_intrusive_tag;
struct free_list_tag {};
/// RLC AM PDU Segment, containing the PDCP SN and RLC SN it has been assigned to, and its current ACK state /// RLC AM PDU Segment, containing the PDCP SN and RLC SN it has been assigned to, and its current ACK state
struct rlc_am_pdu_segment { struct segment_resource : public intrusive_forward_list_element<rlc_list_tag>,
public intrusive_forward_list_element<free_list_tag>,
public intrusive_double_linked_list_element<> {
const static uint32_t invalid_sn = std::numeric_limits<uint32_t>::max(); const static uint32_t invalid_sn = std::numeric_limits<uint32_t>::max();
bool empty() const { return rlc_sn() == invalid_sn and pdcp_sn() == invalid_sn; } int id() const;
void set_ack(bool val = true) { acked = val; } void release_pdcp_sn();
bool is_acked() const { return acked; } void release_rlc_sn();
uint32_t rlc_sn() const { return rlc_sn_; } uint32_t rlc_sn() const { return rlc_sn_; }
uint32_t pdcp_sn() const { return pdcp_sn_; } uint32_t pdcp_sn() const { return pdcp_sn_; }
bool empty() const { return rlc_sn_ == invalid_sn and pdcp_sn_ == invalid_sn; }
bool is_acked() const { return rlc_sn() != invalid_sn; }
protected: private:
friend struct rlc_am_pdu_segment_pool;
uint32_t rlc_sn_ = invalid_sn; uint32_t rlc_sn_ = invalid_sn;
uint32_t pdcp_sn_ = invalid_sn; uint32_t pdcp_sn_ = invalid_sn;
bool acked = false;
};
template <bool rlcSnList>
struct pdu_segment_list;
using rlc_pdu_segment_list = pdu_segment_list<true>;
using pdcp_pdu_segment_list = pdu_segment_list<false>;
/// Pool that manages the allocation of RLC AM PDU Segments to RLC SDUs
struct rlc_am_pdu_segment_pool {
const static size_t MAX_POOL_SIZE = 16384;
struct segment_resource : public rlc_am_pdu_segment {
int id() const;
void deallocate();
using rlc_am_pdu_segment::pdcp_sn_;
using rlc_am_pdu_segment::rlc_sn_;
// intrusive same RLC PDU segment list
segment_resource* rlc_next = nullptr;
// intrusive same PDCP PDU segment list
segment_resource* pdcp_next = nullptr;
// intrusive linked lists
segment_resource* next_free = nullptr;
rlc_am_pdu_segment_pool* parent_pool = nullptr; rlc_am_pdu_segment_pool* parent_pool = nullptr;
}; };
rlc_am_pdu_segment_pool(); rlc_am_pdu_segment_pool();
rlc_am_pdu_segment_pool(const rlc_am_pdu_segment_pool&) = delete; rlc_am_pdu_segment_pool(const rlc_am_pdu_segment_pool&) = delete;
rlc_am_pdu_segment_pool(rlc_am_pdu_segment_pool&&) = delete; rlc_am_pdu_segment_pool(rlc_am_pdu_segment_pool&&) = delete;
bool has_segments() const; bool has_segments() const { return not free_list.empty(); }
bool bool
allocate_segment(uint32_t rlc_sn, rlc_pdu_segment_list& rlc_list, uint32_t pdcp_sn, pdcp_pdu_segment_list& pdcp_list); make_segment(uint32_t rlc_sn, uint32_t pdcp_sn, rlc_pdu_segment_list& rlc_list, pdcp_pdu_segment_list& pdcp_list);
private: private:
segment_resource* free_list = nullptr; intrusive_forward_list<segment_resource, free_list_tag> free_list;
std::array<segment_resource, MAX_POOL_SIZE> segments; std::array<segment_resource, MAX_POOL_SIZE> segments;
}; };
template <bool rlcSnList> /// RLC AM PDU Segment, containing the PDCP SN and RLC SN it has been assigned to, and its current ACK state
struct pdu_segment_list { using rlc_am_pdu_segment = rlc_am_pdu_segment_pool::segment_resource;
void push(rlc_am_pdu_segment_pool::segment_resource* obj);
void clear() { head.reset(); } class pdcp_pdu_segment_list
struct iterator : public std::iterator<std::forward_iterator_tag, iterator> {
explicit iterator(rlc_am_pdu_segment_pool::segment_resource* item_ = nullptr) : item(item_) {}
const rlc_am_pdu_segment* operator->() const { return item; }
rlc_am_pdu_segment* operator->() { return item; }
const rlc_am_pdu_segment& operator*() const { return *item; }
rlc_am_pdu_segment& operator*() { return *item; }
iterator& operator++()
{ {
item = (rlcSnList) ? item->rlc_next : item->pdcp_next; using list_type = intrusive_double_linked_list<rlc_am_pdu_segment>;
return *this; list_type list;
}
bool operator==(iterator other) const { return item == other.item; }
bool operator!=(iterator other) const { return item != other.item; }
private: public:
rlc_am_pdu_segment_pool::segment_resource* item; using iterator = typename list_type::iterator;
using const_iterator = typename list_type::const_iterator;
iterator begin() { return list.begin(); }
iterator end() { return list.end(); }
const_iterator begin() const { return list.begin(); }
const_iterator end() const { return list.end(); }
bool empty() const { return list.empty(); }
pdcp_pdu_segment_list() = default;
pdcp_pdu_segment_list(pdcp_pdu_segment_list&&) noexcept = default;
pdcp_pdu_segment_list& operator=(pdcp_pdu_segment_list&&) noexcept = default;
~pdcp_pdu_segment_list() { clear(); }
void push(rlc_am_pdu_segment& segment) { list.push_front(&segment); }
void pop(rlc_am_pdu_segment& segment);
void clear()
{
while (not empty()) {
pop(*begin());
}
}
}; };
using const_iterator = iterator;
iterator begin() { return iterator(head.get()); } struct rlc_pdu_segment_list {
iterator end() { return iterator(nullptr); } using list_type = intrusive_forward_list<rlc_am_pdu_segment>;
const_iterator begin() const { return iterator(head.get()); } using iterator = typename list_type::iterator;
const_iterator end() const { return iterator(nullptr); } using const_iterator = typename list_type::const_iterator;
const_iterator begin() const { return list.begin(); }
const_iterator end() const { return list.end(); }
iterator begin() { return list.begin(); }
iterator end() { return list.end(); }
bool empty() const { return list.empty(); }
rlc_pdu_segment_list() = default;
rlc_pdu_segment_list(rlc_pdu_segment_list&& other) noexcept = default;
rlc_pdu_segment_list(const rlc_pdu_segment_list&) = delete;
rlc_pdu_segment_list& operator=(rlc_pdu_segment_list&& other) noexcept = default;
~rlc_pdu_segment_list() { clear(); }
void push(rlc_am_pdu_segment& segment) { list.push_front(&segment); }
void clear();
private: private:
struct list_deleter { list_type list;
void operator()(rlc_am_pdu_segment_pool::segment_resource* ptr);
};
std::unique_ptr<rlc_am_pdu_segment_pool::segment_resource, list_deleter> head;
}; };
// //
@ -138,7 +146,7 @@ struct rlc_amd_rx_pdu_segments_t {
struct rlc_amd_tx_pdu_t { struct rlc_amd_tx_pdu_t {
rlc_amd_pdu_header_t header; rlc_amd_pdu_header_t header;
unique_byte_buffer_t buf; unique_byte_buffer_t buf;
pdcp_pdu_segment_list pdcp_sn_list; rlc_pdu_segment_list segment_list;
uint32_t retx_count; uint32_t retx_count;
uint32_t rlc_sn = std::numeric_limits<uint32_t>::max(); uint32_t rlc_sn = std::numeric_limits<uint32_t>::max();
bool is_acked = false; bool is_acked = false;
@ -159,9 +167,8 @@ struct rlc_sn_info_t {
struct pdcp_sdu_info_t { struct pdcp_sdu_info_t {
uint32_t sn; uint32_t sn;
bool fully_txed; // Boolean indicating if the SDU is fully transmitted. bool fully_txed; // Boolean indicating if the SDU is fully transmitted.
bool fully_acked; // Boolean indicating if the SDU is fully acked. This is only necessary temporarely to avoid pdcp_pdu_segment_list segment_list; // List of RLC PDUs in transit and whether they have been acked or not.
// duplicate removal from the queue while processing the status report bool fully_acked() const { return segment_list.empty(); }
rlc_pdu_segment_list rlc_segment_list; // List of RLC PDUs in transit and whether they have been acked or not.
}; };
template <class T> template <class T>
@ -223,9 +230,8 @@ public:
return; return;
} }
buffered_pdus[sn_idx].sn = invalid_sn; buffered_pdus[sn_idx].sn = invalid_sn;
buffered_pdus[sn_idx].fully_acked = false;
buffered_pdus[sn_idx].fully_txed = false; buffered_pdus[sn_idx].fully_txed = false;
buffered_pdus[sn_idx].rlc_segment_list.clear(); buffered_pdus[sn_idx].segment_list.clear();
count--; count--;
} }

@ -16,7 +16,6 @@
#include "srsran/interfaces/ue_rrc_interfaces.h" #include "srsran/interfaces/ue_rrc_interfaces.h"
#include "srsran/srslog/event_trace.h" #include "srsran/srslog/event_trace.h"
#include <iostream> #include <iostream>
#include <srsran/upper/rlc_am_lte.h>
#define MOD 1024 #define MOD 1024
#define RX_MOD_BASE(x) (((x)-vr_r) % 1024) #define RX_MOD_BASE(x) (((x)-vr_r) % 1024)
@ -67,75 +66,61 @@ int rlc_am_pdu_segment_pool::segment_resource::id() const
return std::distance(parent_pool->segments.cbegin(), this); return std::distance(parent_pool->segments.cbegin(), this);
} }
void rlc_am_pdu_segment_pool::segment_resource::deallocate() void rlc_am_pdu_segment_pool::segment_resource::release_pdcp_sn()
{ {
acked = false; pdcp_sn_ = invalid_sn;
next_free = parent_pool->free_list; if (empty()) {
parent_pool->free_list = this; parent_pool->free_list.push_front(this);
}
} }
rlc_am_pdu_segment_pool::rlc_am_pdu_segment_pool() void rlc_am_pdu_segment_pool::segment_resource::release_rlc_sn()
{ {
for (segment_resource& s : segments) { rlc_sn_ = invalid_sn;
s.parent_pool = this; if (empty()) {
s.next_free = free_list; parent_pool->free_list.push_front(this);
free_list = &s;
} }
} }
bool rlc_am_pdu_segment_pool::has_segments() const rlc_am_pdu_segment_pool::rlc_am_pdu_segment_pool()
{ {
return free_list != nullptr; for (segment_resource& s : segments) {
s.parent_pool = this;
free_list.push_front(&s);
}
} }
bool rlc_am_pdu_segment_pool::allocate_segment(uint32_t rlc_sn, bool rlc_am_pdu_segment_pool::make_segment(uint32_t rlc_sn,
rlc_pdu_segment_list& rlc_list,
uint32_t pdcp_sn, uint32_t pdcp_sn,
rlc_pdu_segment_list& rlc_list,
pdcp_pdu_segment_list& pdcp_list) pdcp_pdu_segment_list& pdcp_list)
{ {
if (free_list == nullptr) { if (not has_segments()) {
return false; return false;
} }
segment_resource* segment = free_list; segment_resource* segment = free_list.pop_front();
free_list = segment->next_free; rlc_list.push(*segment);
rlc_list.push(segment); pdcp_list.push(*segment);
segment->rlc_sn_ = rlc_sn; segment->rlc_sn_ = rlc_sn;
pdcp_list.push(segment);
segment->pdcp_sn_ = pdcp_sn; segment->pdcp_sn_ = pdcp_sn;
return true; return true;
} }
template <bool rlcSnList> void pdcp_pdu_segment_list::pop(rlc_am_pdu_segment& segment)
void pdu_segment_list<rlcSnList>::push(rlc_am_pdu_segment_pool::segment_resource* obj)
{ {
if (head != nullptr) { // remove from list
if (rlcSnList) { list.pop(&segment);
obj->rlc_next = head.release(); // signal pool that the pdcp handle is released
} else { segment.release_pdcp_sn();
obj->pdcp_next = head.release();
}
}
head.reset(obj);
} }
template <bool rlcSnList> void rlc_pdu_segment_list::clear()
void pdu_segment_list<rlcSnList>::list_deleter::operator()(rlc_am_pdu_segment_pool::segment_resource* node)
{ {
while (node != nullptr) { while (not empty()) {
rlc_am_pdu_segment_pool::segment_resource* next = nullptr; // remove from list
if (rlcSnList) { rlc_am_pdu_segment* segment = list.pop_front();
next = node->rlc_next; // deallocate if also removed from PDCP
node->rlc_next = nullptr; segment->release_rlc_sn();
node->rlc_sn_ = rlc_am_pdu_segment::invalid_sn;
} else {
next = node->pdcp_next;
node->pdcp_next = nullptr;
node->pdcp_sn_ = rlc_am_pdu_segment::invalid_sn;
}
if (node->empty()) {
node->deallocate();
}
node = next;
} }
} }
@ -417,7 +402,7 @@ void rlc_am_lte::rlc_am_lte_tx::check_sn_reached_max_retx(uint32_t sn)
logger.warning("%s Signaling max number of reTx=%d for SN=%d", RB_NAME, tx_window[sn].retx_count, sn); logger.warning("%s Signaling max number of reTx=%d for SN=%d", RB_NAME, tx_window[sn].retx_count, sn);
parent->rrc->max_retx_attempted(); parent->rrc->max_retx_attempted();
srsran::pdcp_sn_vector_t pdcp_sns; srsran::pdcp_sn_vector_t pdcp_sns;
for (const rlc_am_pdu_segment& segment : tx_window[sn].pdcp_sn_list) { for (const rlc_am_pdu_segment& segment : tx_window[sn].segment_list) {
pdcp_sns.push_back(segment.pdcp_sn()); pdcp_sns.push_back(segment.pdcp_sn());
} }
parent->pdcp->notify_failure(parent->lcid, pdcp_sns); parent->pdcp->notify_failure(parent->lcid, pdcp_sns);
@ -1043,7 +1028,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_byt
tx_sdu->msg += to_move; tx_sdu->msg += to_move;
if (undelivered_sdu_info_queue.has_pdcp_sn(tx_sdu->md.pdcp_sn)) { if (undelivered_sdu_info_queue.has_pdcp_sn(tx_sdu->md.pdcp_sn)) {
pdcp_sdu_info_t& pdcp_sdu = undelivered_sdu_info_queue[tx_sdu->md.pdcp_sn]; pdcp_sdu_info_t& pdcp_sdu = undelivered_sdu_info_queue[tx_sdu->md.pdcp_sn];
segment_pool.allocate_segment(header.sn, pdcp_sdu.rlc_segment_list, tx_sdu->md.pdcp_sn, tx_pdu.pdcp_sn_list); segment_pool.make_segment(header.sn, tx_sdu->md.pdcp_sn, tx_pdu.segment_list, pdcp_sdu.segment_list);
if (tx_sdu->N_bytes == 0) { if (tx_sdu->N_bytes == 0) {
pdcp_sdu.fully_txed = true; pdcp_sdu.fully_txed = true;
} }
@ -1115,7 +1100,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_byt
tx_sdu->msg += to_move; tx_sdu->msg += to_move;
if (undelivered_sdu_info_queue.has_pdcp_sn(tx_sdu->md.pdcp_sn)) { if (undelivered_sdu_info_queue.has_pdcp_sn(tx_sdu->md.pdcp_sn)) {
pdcp_sdu_info_t& pdcp_sdu = undelivered_sdu_info_queue[tx_sdu->md.pdcp_sn]; pdcp_sdu_info_t& pdcp_sdu = undelivered_sdu_info_queue[tx_sdu->md.pdcp_sn];
segment_pool.allocate_segment(header.sn, pdcp_sdu.rlc_segment_list, tx_sdu->md.pdcp_sn, tx_pdu.pdcp_sn_list); segment_pool.make_segment(header.sn, tx_sdu->md.pdcp_sn, tx_pdu.segment_list, pdcp_sdu.segment_list);
if (tx_sdu->N_bytes == 0) { if (tx_sdu->N_bytes == 0) {
pdcp_sdu.fully_txed = true; pdcp_sdu.fully_txed = true;
} }
@ -1321,20 +1306,18 @@ void rlc_am_lte::rlc_am_lte_tx::update_notification_ack_info(const rlc_amd_tx_pd
if (not tx_window.has_sn(tx_pdu.header.sn)) { if (not tx_window.has_sn(tx_pdu.header.sn)) {
return; return;
} }
pdcp_pdu_segment_list& pdcp_sns = tx_window[tx_pdu.header.sn].pdcp_sn_list; rlc_pdu_segment_list& pdcp_sns = tx_window[tx_pdu.header.sn].segment_list;
for (rlc_am_pdu_segment& pdcp_segment : pdcp_sns) { // Iterate over all PDCP SNs of the same RLC PDU that were TX'ed
// Iterate over all SNs that were TX'ed for (rlc_am_pdu_segment& acked_segment : pdcp_sns) {
uint32_t pdcp_sn = pdcp_segment.pdcp_sn(); uint32_t pdcp_sn = acked_segment.pdcp_sn();
pdcp_segment.set_ack();
pdcp_sdu_info_t& info = undelivered_sdu_info_queue[pdcp_sn]; pdcp_sdu_info_t& info = undelivered_sdu_info_queue[pdcp_sn];
// Check wether the SDU was fully acked
if (info.fully_txed and not info.fully_acked) { // Remove RLC SN from PDCP PDU undelivered list
info.segment_list.pop(acked_segment);
// Check whether the SDU was fully acked
if (info.fully_txed and info.segment_list.empty()) {
// Check if all SNs were ACK'ed // Check if all SNs were ACK'ed
info.fully_acked = std::all_of(info.rlc_segment_list.begin(),
info.rlc_segment_list.end(),
[](const rlc_am_pdu_segment& elem) { return elem.is_acked(); });
if (info.fully_acked) {
if (not notify_info_vec.full()) { if (not notify_info_vec.full()) {
notify_info_vec.push_back(pdcp_sn); notify_info_vec.push_back(pdcp_sn);
} else { } else {
@ -1344,7 +1327,6 @@ void rlc_am_lte::rlc_am_lte_tx::update_notification_ack_info(const rlc_amd_tx_pd
undelivered_sdu_info_queue.clear_pdcp_sdu(pdcp_sn); undelivered_sdu_info_queue.clear_pdcp_sdu(pdcp_sn);
} }
} }
}
pdcp_sns.clear(); pdcp_sns.clear();
} }
@ -2209,9 +2191,8 @@ void buffered_pdcp_pdu_list::clear()
count = 0; count = 0;
for (auto& b : buffered_pdus) { for (auto& b : buffered_pdus) {
b.sn = invalid_sn; b.sn = invalid_sn;
b.fully_acked = false;
b.fully_txed = false; b.fully_txed = false;
b.rlc_segment_list.clear(); b.segment_list.clear();
} }
} }
@ -2475,9 +2456,9 @@ void rlc_am_undelivered_sdu_info_to_string(fmt::memory_buffer& buffer, const std
{ {
fmt::format_to(buffer, "\n"); fmt::format_to(buffer, "\n");
for (const auto& pdcp_pdu : info_queue) { for (const auto& pdcp_pdu : info_queue) {
fmt::format_to(buffer, "\tPDCP_SN = {}, RLC_SNs = [", pdcp_pdu.sn); fmt::format_to(buffer, "\tPDCP_SN = {}, undelivered RLC SNs = [", pdcp_pdu.sn);
for (const auto& rlc_sn_info : pdcp_pdu.rlc_segment_list) { for (const auto& rlc_sn_info : pdcp_pdu.segment_list) {
fmt::format_to(buffer, "{}ACK={}, ", rlc_sn_info.is_acked() ? "" : "N", rlc_sn_info.rlc_sn()); fmt::format_to(buffer, "{} ", rlc_sn_info.rlc_sn());
} }
fmt::format_to(buffer, "]\n"); fmt::format_to(buffer, "]\n");
} }

Loading…
Cancel
Save