diff --git a/lib/include/srsran/adt/adt_utils.h b/lib/include/srsran/adt/adt_utils.h deleted file mode 100644 index 1602578f9..000000000 --- a/lib/include/srsran/adt/adt_utils.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsLTE. - * - * srsLTE is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsLTE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#ifndef SRSRAN_ADT_UTILS_H -#define SRSRAN_ADT_UTILS_H - -#ifdef __EXCEPTIONS - -#include - -#define EXCEPTIONS_ENABLED 1 - -namespace srsran { - -class bad_type_access : public std::runtime_error -{ -public: - explicit bad_type_access(const std::string& what_arg) : runtime_error(what_arg) {} - explicit bad_type_access(const char* what_arg) : runtime_error(what_arg) {} -}; - -#define THROW_BAD_ACCESS(msg) throw bad_type_access(msg) - -} // namespace srsran - -#else - -#define EXCEPTIONS_ENABLED 0 - -#include -#include - -namespace srsran { - -#define THROW_BAD_ACCESS(msg) \ - std::fprintf(stderr, "ERROR: exception thrown with %s", msg); \ - std::abort() - -} // namespace srsran - -#endif - -#endif // SRSRAN_ADT_UTILS_H diff --git a/lib/include/srsran/adt/bounded_bitset.h b/lib/include/srsran/adt/bounded_bitset.h index 976b442d9..f0beabed2 100644 --- a/lib/include/srsran/adt/bounded_bitset.h +++ b/lib/include/srsran/adt/bounded_bitset.h @@ -22,7 +22,7 @@ #ifndef SRSRAN_DYN_BITSET_H #define SRSRAN_DYN_BITSET_H -#include "adt_utils.h" +#include "srsran/common/srsran_assert.h" #include "srsran/srslog/bundled/fmt/format.h" #include #include @@ -52,11 +52,7 @@ public: void resize(size_t new_size) { - if (new_size > max_size()) { - std::string msg = - "ERROR: new size=" + std::to_string(new_size) + " exceeds bitset capacity=" + std::to_string(max_size()); - THROW_BAD_ACCESS(msg.c_str()); - } + srsran_assert(new_size <= max_size(), "ERROR: new size=%zd exceeds bitset capacity=%zd", new_size, max_size()); if (new_size == cur_size) { return; } @@ -200,11 +196,10 @@ public: bounded_bitset& operator|=(const bounded_bitset& other) { - if (other.size() != size()) { - std::string msg = "operator|= called for bitsets of different sizes (" + std::to_string(size()) + - "!=" + std::to_string(other.size()) + ")"; - THROW_BAD_ACCESS(msg.c_str()); - } + srsran_assert(other.size() == size(), + "ERROR: operator|= called for bitsets of different sizes (%zd!=%zd)", + size(), + other.size()); for (size_t i = 0; i < nof_words_(); ++i) { buffer[i] |= other.buffer[i]; } @@ -213,11 +208,10 @@ public: bounded_bitset& operator&=(const bounded_bitset& other) { - if (other.size() != size()) { - std::string msg = "operator&= called for bitsets of different sizes (" + std::to_string(size()) + - "!=" + std::to_string(other.size()) + ")"; - THROW_BAD_ACCESS(msg.c_str()); - } + srsran_assert(other.size() == size(), + "ERROR: operator&= called for bitsets of different sizes (%zd!=%zd)", + size(), + other.size()); for (size_t i = 0; i < nof_words_(); ++i) { buffer[i] &= other.buffer[i]; } @@ -254,10 +248,7 @@ public: uint64_t to_uint64() const { - if (nof_words_() > 1) { - std::string msg = "ERROR: cannot convert bitset of size=" + std::to_string(size()) + " to uint64_t"; - THROW_BAD_ACCESS(msg.c_str()); - } + srsran_assert(nof_words_() == 1, "ERROR: cannot convert bitset of size=%zd to uint64_t", size()); return get_word_(0); } @@ -319,11 +310,10 @@ private: void assert_within_bounds_(size_t pos, bool strict) const { - if (pos > size() or (strict and pos == size())) { - std::string msg = - "ERROR: index=" + std::to_string(pos) + "is out of bounds for bitset of size=" + std::to_string(size()); - THROW_BAD_ACCESS(msg.c_str()); - } + srsran_assert(pos < size() or (not strict and pos == size()), + "ERROR: index=%zd is out-of-bounds for bitset of size=%zd", + pos, + size()); } static word_t maskbit(size_t pos) { return (static_cast(1)) << (pos % bits_per_word); } diff --git a/lib/include/srsran/adt/bounded_vector.h b/lib/include/srsran/adt/bounded_vector.h index 5119ba689..1432364cf 100644 --- a/lib/include/srsran/adt/bounded_vector.h +++ b/lib/include/srsran/adt/bounded_vector.h @@ -22,7 +22,8 @@ #ifndef SRSRAN_BOUNDED_VECTOR_H #define SRSRAN_BOUNDED_VECTOR_H -#include +#include "srsran/adt/detail/type_storage.h" +#include "srsran/common/srsran_assert.h" #include #include #include @@ -96,28 +97,28 @@ public: // Element access T& operator[](std::size_t i) { - assert(i < size_ && "Array index is out of bounds."); - return reinterpret_cast(buffer[i]); + srsran_assert(i < size_, "Array index is out of bounds."); + return buffer[i].get(); } const T& operator[](std::size_t i) const { - assert(i < size_ && "Array index is out of bounds."); - return reinterpret_cast(buffer[i]); + srsran_assert(i < size_, "Array index is out of bounds."); + return buffer[i].get(); } T& back() { - assert(size_ > 0 && "Trying to get back of empty array."); + srsran_assert(size_ > 0, "Trying to get back of empty array."); return *(begin() + size_ - 1); } const T& back() const { - assert(size_ > 0 && "Trying to get back of empty array."); + srsran_assert(size_ > 0, "Trying to get back of empty array."); return *(begin() + size_ - 1); } T& front() { return (*this)[0]; } const T& front() const { return (*this)[0]; } - T* data() { return reinterpret_cast(buffer); } - const T* data() const { return reinterpret_cast(buffer); } + T* data() { return reinterpret_cast(buffer.data()); } + const T* data() const { return reinterpret_cast(buffer.data()); } // Iterators iterator begin() { return data(); } @@ -139,8 +140,8 @@ public: } iterator erase(iterator pos) { - assert(pos >= this->begin() && "Iterator to erase is out of bounds."); - assert(pos < this->end() && "Erasing at past-the-end iterator."); + srsran_assert(pos >= this->begin(), "Iterator to erase is out of bounds."); + srsran_assert(pos < this->end(), "Erasing at past-the-end iterator."); iterator ret = pos; std::move(pos + 1, end(), pos); pop_back(); @@ -148,9 +149,9 @@ public: } iterator erase(iterator it_start, iterator it_end) { - assert(it_start >= begin() && "Range to erase is out of bounds."); - assert(it_start <= it_end && "Trying to erase invalid range."); - assert(it_end <= end() && "Trying to erase past the end."); + srsran_assert(it_start >= begin(), "Range to erase is out of bounds."); + srsran_assert(it_start <= it_end, "Trying to erase invalid range."); + srsran_assert(it_end <= end(), "Trying to erase past the end."); iterator ret = it_start; // Shift all elts down. @@ -163,14 +164,14 @@ public: { static_assert(std::is_copy_constructible::value, "T must be copy-constructible"); size_++; - assert(size_ <= MAX_N); + srsran_assert(size_ <= MAX_N, "bounded vector maximum size=%zd was exceeded", MAX_N); new (&back()) T(value); } void push_back(T&& value) { static_assert(std::is_move_constructible::value, "T must be move-constructible"); size_++; - assert(size_ <= MAX_N); + srsran_assert(size_ <= MAX_N, "bounded vector maximum size=%zd was exceeded", MAX_N); new (&back()) T(std::move(value)); } template @@ -178,12 +179,12 @@ public: { static_assert(std::is_constructible::value, "Passed arguments to emplace_back are invalid"); size_++; - assert(size_ <= MAX_N); + srsran_assert(size_ <= MAX_N, "bounded vector maximum size=%zd was exceeded", MAX_N); new (&back()) T(std::forward(args)...); } void pop_back() { - assert(size_ > 0 && "Trying to erase element from empty vector."); + srsran_assert(size_ > 0, "Trying to erase element from empty vector."); back().~T(); size_--; } @@ -219,29 +220,29 @@ private: void append(const_iterator it_begin, const_iterator it_end) { size_type N = std::distance(it_begin, it_end); - assert(N + size_ <= MAX_N); + srsran_assert(N + size_ <= MAX_N, "bounded vector maximum size=%zd was exceeded", MAX_N); std::uninitialized_copy(it_begin, it_end, end()); size_ += N; } void append(size_type N, const T& element) { static_assert(std::is_copy_constructible::value, "T must be copy-constructible"); - assert(N + size_ <= MAX_N); + srsran_assert(N + size_ <= MAX_N, "bounded vector maximum size=%zd was exceeded", MAX_N); std::uninitialized_fill_n(end(), N, element); size_ += N; } void append(size_type N) { static_assert(std::is_default_constructible::value, "T must be default-constructible"); - assert(N + size_ <= MAX_N); + srsran_assert(N + size_ <= MAX_N, "bounded vector maximum size=%zd was exceeded", MAX_N); for (size_type i = size_; i < size_ + N; ++i) { - new (&buffer[i]) T(); + buffer[i].emplace(); } size_ += N; } - std::size_t size_ = 0; - typename std::aligned_storage::type buffer[MAX_N]; + std::size_t size_ = 0; + std::array, MAX_N> buffer; }; } // namespace srsran diff --git a/lib/include/srsran/adt/circular_map.h b/lib/include/srsran/adt/circular_map.h index 671416d01..25d5b5cf8 100644 --- a/lib/include/srsran/adt/circular_map.h +++ b/lib/include/srsran/adt/circular_map.h @@ -88,7 +88,7 @@ public: { public: const_iterator() = default; - const_iterator(static_circular_map* map, size_t idx_) : ptr(map), idx(idx_) {} + const_iterator(const static_circular_map* map, size_t idx_) : ptr(map), idx(idx_) {} const_iterator& operator++() { @@ -150,7 +150,7 @@ public: return *this; } - bool contains(K id) + bool contains(K id) const { size_t idx = id % N; return present[idx] and get_obj_(idx).first == id; @@ -223,12 +223,13 @@ public: size_t size() const { return count; } bool empty() const { return count == 0; } bool full() const { return count == N; } + bool has_space(K id) { return not present[id % N]; } size_t capacity() const { return N; } iterator begin() { return iterator(this, 0); } iterator end() { return iterator(this, N); } const_iterator begin() const { return iterator(this, 0); } - const_iterator end() const { return iterator(this, N); } + const_iterator end() const { return const_iterator(this, N); } iterator find(K id) { @@ -240,7 +241,7 @@ public: const_iterator find(K id) const { if (contains(id)) { - return iterator(this, id % N); + return const_iterator(this, id % N); } return end(); } @@ -254,6 +255,52 @@ private: size_t count = 0; }; +/** + * Operates like a circular map, but automatically assigns the ID/key to inserted objects in a monotonically + * increasing way. The assigned IDs are not necessarily contiguous, as they are selected based on the available slots + * in the circular map + * @tparam K type of ID/key + * @tparam T object being inserted + * @tparam MAX_N maximum size of pool + */ +template +class static_id_obj_pool : private static_circular_map +{ + using base_t = static_circular_map; + +public: + using iterator = typename base_t::iterator; + using const_iterator = typename base_t::const_iterator; + + using base_t::operator[]; + using base_t::begin; + using base_t::contains; + using base_t::empty; + using base_t::end; + using base_t::erase; + using base_t::find; + using base_t::full; + using base_t::size; + + explicit static_id_obj_pool(K first_id = 0) : next_id(first_id) {} + + template + srsran::expected insert(U&& t) + { + if (full()) { + return srsran::default_error_t{}; + } + while (not base_t::has_space(next_id)) { + ++next_id; + } + base_t::insert(next_id, std::forward(t)); + return next_id++; + } + +private: + K next_id = 0; +}; + } // namespace srsran #endif // SRSRAN_ID_MAP_H diff --git a/lib/include/srsran/adt/detail/index_sequence.h b/lib/include/srsran/adt/detail/index_sequence.h index 033db9e51..a5b6057c7 100644 --- a/lib/include/srsran/adt/detail/index_sequence.h +++ b/lib/include/srsran/adt/detail/index_sequence.h @@ -22,6 +22,8 @@ #ifndef CPP_TESTS_INDEX_SEQUENCE_H #define CPP_TESTS_INDEX_SEQUENCE_H +#include + namespace srsran { template diff --git a/lib/include/srsran/adt/detail/type_storage.h b/lib/include/srsran/adt/detail/type_storage.h index d0cd64fc8..35a9a44c0 100644 --- a/lib/include/srsran/adt/detail/type_storage.h +++ b/lib/include/srsran/adt/detail/type_storage.h @@ -22,6 +22,7 @@ #ifndef SRSRAN_TYPE_STORAGE_H #define SRSRAN_TYPE_STORAGE_H +#include #include #include @@ -29,6 +30,17 @@ namespace srsran { namespace detail { +// NOTE: gcc 4.8.5 is missing std::max_align_t. Need to create a struct +union max_alignment_t { + char c; + float f; + uint32_t i; + uint64_t i2; + double d; + long double d2; + uint32_t* ptr; +}; + template struct type_storage { using value_type = T; diff --git a/lib/include/srsran/adt/detail/type_utils.h b/lib/include/srsran/adt/detail/type_utils.h index 885b5a721..7238d8447 100644 --- a/lib/include/srsran/adt/detail/type_utils.h +++ b/lib/include/srsran/adt/detail/type_utils.h @@ -280,19 +280,15 @@ const T* get_if(const TypeContainer& c) template T& get(TypeContainer& c) { - if (c.template is()) { - return c.template get_unchecked(); - } - THROW_BAD_ACCESS("in get"); + srsran_assert(c.template is(), "Bad access via get"); + return c.template get_unchecked(); } template const T& get(const TypeContainer& c) { - if (c.template is()) { - return c.template get_unchecked(); - } - THROW_BAD_ACCESS("in get"); + srsran_assert(c.template is(), "Bad access via get"); + return c.template get_unchecked(); } template #include @@ -30,6 +30,14 @@ namespace srsran { struct default_error_t {}; +template +class expected; + +template +struct is_expected : std::false_type {}; +template +struct is_expected > : std::true_type {}; + template class expected { @@ -38,7 +46,15 @@ class expected public: expected() : has_val(true), val(T{}) {} expected(T&& t) : has_val(true), val(std::forward(t)) {} + expected(const T& t) : has_val(true), val(t) {} expected(E&& e) : has_val(false), unexpected(std::forward(e)) {} + expected(const E& e) : has_val(false), unexpected(e) {} + template < + typename U, + typename std::enable_if::value and not is_expected::type>::value, + int>::type = 0> + explicit expected(U&& u) : has_val(true), val(std::forward(u)) + {} expected(const expected& other) { if (other.has_val) { @@ -112,30 +128,22 @@ public: bool is_error() const { return not has_value(); } const T& value() const { - if (not has_val) { - THROW_BAD_ACCESS("Bad expected value access"); - } + srsran_assert(has_value(), "Bad expected value access"); return val; } T& value() { - if (not has_val) { - THROW_BAD_ACCESS("Bad expected value access"); - } + srsran_assert(has_value(), "Bad expected value access"); return val; } const E& error() const { - if (has_val) { - THROW_BAD_ACCESS("Bad expected error access"); - } + srsran_assert(not has_value(), "Bad expected error access"); return unexpected; } E& error() { - if (has_val) { - THROW_BAD_ACCESS("Bad expected error access"); - } + srsran_assert(not has_value(), "Bad expected error access"); return unexpected; } diff --git a/lib/include/srsran/common/fsm.h b/lib/include/srsran/adt/fsm.h similarity index 98% rename from lib/include/srsran/common/fsm.h rename to lib/include/srsran/adt/fsm.h index ff8a2f18d..f35596873 100644 --- a/lib/include/srsran/common/fsm.h +++ b/lib/include/srsran/adt/fsm.h @@ -27,8 +27,6 @@ #include "srsran/srslog/srslog.h" #include #include -#include -#include #include #include @@ -45,17 +43,17 @@ namespace srsran { -//! Forward declarations +/// Forward declarations template class base_fsm_t; template class composite_fsm_t; -//! Check if type T is an FSM +/// Check if type T is an FSM template using is_fsm = std::is_base_of, T>; -//! Check if type T is a composite FSM +/// Check if type T is a composite FSM template struct is_composite_fsm : public std::false_type {}; template @@ -268,7 +266,7 @@ struct apply_first_guard_pass > { } }; -//! Trigger Event, that will result in a state transition +/// Trigger Event that may result in a state transition template struct trigger_visitor { using event_t = typename std::decay::type; @@ -688,10 +686,8 @@ public: const Result& get_result() const { - if (launch_counter > 0 and base_t::template is_in_state()) { - return last_result; - } - THROW_BAD_ACCESS("in proc_fsm_t::get_result"); + srsran_assert(launch_counter > 0 and base_t::template is_in_state(), "in proc_fsm_t::get_result"); + return last_result; } template diff --git a/lib/include/srsran/adt/interval.h b/lib/include/srsran/adt/interval.h index 891926a0d..914fc356c 100644 --- a/lib/include/srsran/adt/interval.h +++ b/lib/include/srsran/adt/interval.h @@ -22,7 +22,7 @@ #ifndef SRSRAN_INTERVAL_H #define SRSRAN_INTERVAL_H -#include "adt_utils.h" +#include "srsran/common/srsran_assert.h" #include "srsran/srslog/bundled/fmt/format.h" #include #include @@ -52,7 +52,7 @@ public: void set(T start_point, T stop_point) { - assert(stop_point >= start_point); + srsran_assert(stop_point >= start_point, "interval::set called for invalid range points"); start_ = start_point; stop_ = stop_point; } @@ -60,13 +60,13 @@ public: void resize_by(T len) { // Detect length overflows - assert(std::is_unsigned::value or (len >= 0 or length() >= -len)); + srsran_assert(std::is_unsigned::value or (len >= 0 or length() >= -len), "Resulting interval would be invalid"); stop_ += len; } void resize_to(T len) { - assert(std::is_unsigned::value or len >= 0); + srsran_assert(std::is_unsigned::value or len >= 0, "Interval width must be positive"); stop_ = start_ + len; } diff --git a/lib/include/srsran/adt/move_callback.h b/lib/include/srsran/adt/move_callback.h index 510bf8060..bbf7b5d20 100644 --- a/lib/include/srsran/adt/move_callback.h +++ b/lib/include/srsran/adt/move_callback.h @@ -22,6 +22,8 @@ #ifndef SRSRAN_MOVE_CALLBACK_H #define SRSRAN_MOVE_CALLBACK_H +#include "detail/type_storage.h" +#include "srsran/common/srsran_assert.h" #include #include #include @@ -34,23 +36,11 @@ #define THROW_BAD_FUNCTION_CALL(const char* cause) throw std::bad_function_call{}; #else #define THROW_BAD_FUNCTION_CALL(cause) \ - fprintf(stderr, "ERROR: exception thrown due to bad function call (cause: %s)\n", cause); \ - std::abort() + srsran_assert(false, "ERROR: exception thrown due to bad function call (cause: %s)\n", cause); #endif namespace srsran { -// NOTE: gcc 4.8.5 is missing std::max_align_t. Need to create a struct -union max_alignment_t { - char c; - float f; - uint32_t i; - uint64_t i2; - double d; - long double d2; - uint32_t* ptr; -}; - //! Size of the buffer used by "move_callback" to store functors without calling "new" constexpr size_t default_buffer_size = 32; @@ -77,10 +67,13 @@ class empty_table_t : public oper_table_t { public: constexpr empty_table_t() = default; - R call(void* src, Args&&... args) const final { THROW_BAD_FUNCTION_CALL("function ptr is empty"); } - void move(void* src, void* dest) const final {} - void dtor(void* src) const final {} - bool is_in_small_buffer() const final { return true; } + R call(void* src, Args&&... args) const final + { + srsran_terminate("ERROR: bad function call (cause: function ptr is empty)"); + } + void move(void* src, void* dest) const final {} + void dtor(void* src) const final {} + bool is_in_small_buffer() const final { return true; } }; //! specialization of move/call/destroy operations for when the functor is stored in "move_callback" buffer @@ -135,7 +128,7 @@ template class move_callback { static constexpr size_t capacity = Capacity >= sizeof(void*) ? Capacity : sizeof(void*); ///< size of buffer - using storage_t = typename std::aligned_storage::type; + using storage_t = typename std::aligned_storage::type; using oper_table_t = task_details::oper_table_t; static constexpr task_details::empty_table_t empty_table{}; diff --git a/lib/include/srsran/adt/optional.h b/lib/include/srsran/adt/optional.h new file mode 100644 index 000000000..7a00d9dfc --- /dev/null +++ b/lib/include/srsran/adt/optional.h @@ -0,0 +1,135 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_OPTIONAL_H +#define SRSRAN_OPTIONAL_H + +#include "detail/type_storage.h" +#include "srsran/common/srsran_assert.h" + +namespace srsran { + +template +class optional +{ +public: + optional() : has_val_(false) {} + optional(const T& t) : has_val_(true) { storage.emplace(t); } + optional(T&& t) : has_val_(true) { storage.emplace(std::move(t)); } + optional(const optional& other) : has_val_(other.has_value()) + { + if (other.has_value()) { + storage.copy_ctor(other.storage); + } + } + optional(optional&& other) noexcept : has_val_(other.has_value()) + { + if (other.has_value()) { + storage.move_ctor(std::move(other.storage)); + } + } + optional& operator=(const optional& other) + { + if (this == &other) { + return *this; + } + copy_if_present_helper(storage, other.storage, has_value(), other.has_value()); + has_val_ = other.has_value(); + return *this; + } + optional& operator=(optional&& other) noexcept + { + move_if_present_helper(storage, other.storage, has_value(), other.has_value()); + has_val_ = other.has_value(); + return *this; + } + ~optional() + { + if (has_value()) { + storage.destroy(); + } + } + + bool has_value() const { return has_val_; } + explicit operator bool() const { return has_value(); } + + T* operator->() { return &value(); } + const T* operator->() const { return &value(); } + T& operator*() { return value(); } + const T& operator*() const { return value(); } + T& value() + { + srsran_assert(has_val_, "Invalid optional access"); + return storage.get(); + } + const T& value() const + { + srsran_assert(has_val_, "Invalid optional access"); + return storage.get(); + } + + template + void emplace(Args&&... args) + { + if (has_value()) { + storage.destroy(); + } + storage.emplace(std::forward(args)...); + has_val_ = true; + } + + void reset() + { + if (has_value()) { + storage.destroy(); + has_val_ = false; + } + } + +private: + bool has_val_; + detail::type_storage storage; +}; + +template +bool operator==(const optional& lhs, const optional& rhs) +{ + return lhs.has_value() == rhs.has_value() and (not lhs.has_value() or lhs.value() == rhs.value()); +} + +template +bool operator==(const optional& lhs, const T& rhs) +{ + return lhs.has_value() and lhs.value() == rhs; +} + +template +bool operator!=(const optional& lhs, const optional& rhs) +{ + return not(lhs == rhs); +} + +template +bool operator!=(const optional& lhs, const T& rhs) +{ + return not(lhs == rhs); +} + +template +bool operator<(const optional& lhs, const optional& rhs) +{ + return rhs.has_value() and ((lhs.has_value() and lhs.value() < rhs.value()) or (not lhs.has_value())); +} + +} // namespace srsran + +#endif // SRSRAN_OPTIONAL_H diff --git a/lib/include/srsran/adt/pool/fixed_size_pool.h b/lib/include/srsran/adt/pool/fixed_size_pool.h new file mode 100644 index 000000000..09942a809 --- /dev/null +++ b/lib/include/srsran/adt/pool/fixed_size_pool.h @@ -0,0 +1,199 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_FIXED_SIZE_POOL_H +#define SRSRAN_FIXED_SIZE_POOL_H + +#include "memblock_cache.h" +#include "srsran/adt/circular_buffer.h" +#include + +namespace srsran { + +/** + * Concurrent fixed size memory pool made of blocks of equal size + * Each worker keeps a separate thread-local memory block cache that it uses for fast allocation/deallocation. + * When this cache gets depleted, the worker tries to obtain blocks from a central memory block cache. + * When accessing a thread local cache, no locks are required. + * Since there is no stealing of blocks between workers, it is possible that a worker can't allocate while another + * worker still has blocks in its own cache. To minimize the impact of this event, an upper bound is place on a worker + * thread cache size. Once a worker reaches that upper bound, it sends half of its stored blocks to the central cache. + * Note: Taking into account the usage of thread_local, this class is made a singleton + * Note2: No considerations were made regarding false sharing between threads. It is assumed that the blocks are big + * enough to fill a cache line. + * @tparam NofObjects number of objects in the pool + * @tparam ObjSize object size + */ +template +class concurrent_fixed_memory_pool +{ + static_assert(ObjSize > 256, "This pool is particularly designed for large objects."); + using pool_type = concurrent_fixed_memory_pool; + + struct obj_storage_t { + typename std::aligned_storage::type buffer; + }; + + const static size_t batch_steal_size = 16; + + // ctor only accessible from singleton get_instance() + explicit concurrent_fixed_memory_pool(size_t nof_objects_) + { + srsran_assert(nof_objects_ > batch_steal_size, "A positive pool size must be provided"); + + std::lock_guard lock(mutex); + allocated_blocks.resize(nof_objects_); + for (std::unique_ptr& b : allocated_blocks) { + b.reset(new obj_storage_t()); + srsran_assert(b.get() != nullptr, "Failed to instantiate fixed memory pool"); + central_mem_cache.push(static_cast(b.get())); + } + local_growth_thres = allocated_blocks.size() / 16; + local_growth_thres = local_growth_thres < batch_steal_size ? batch_steal_size : local_growth_thres; + } + +public: + const static size_t BLOCK_SIZE = ObjSize; + + concurrent_fixed_memory_pool(const concurrent_fixed_memory_pool&) = delete; + concurrent_fixed_memory_pool(concurrent_fixed_memory_pool&&) = delete; + concurrent_fixed_memory_pool& operator=(const concurrent_fixed_memory_pool&) = delete; + concurrent_fixed_memory_pool& operator=(concurrent_fixed_memory_pool&&) = delete; + + ~concurrent_fixed_memory_pool() + { + std::lock_guard lock(mutex); + allocated_blocks.clear(); + } + + static concurrent_fixed_memory_pool* get_instance(size_t size = 4096) + { + static concurrent_fixed_memory_pool pool(size); + return &pool; + } + + size_t size() { return allocated_blocks.size(); } + + void* allocate_node(size_t sz) + { + srsran_assert(sz <= ObjSize, "Allocated node size=%zd exceeds max object size=%zd", sz, ObjSize); + worker_ctxt* worker_ctxt = get_worker_cache(); + + void* node = worker_ctxt->cache.try_pop(); + if (node == nullptr) { + // fill the thread local cache enough for this and next allocations + std::array popped_blocks; + size_t n = central_mem_cache.try_pop(popped_blocks); + for (size_t i = 0; i < n; ++i) { + new (popped_blocks[i]) obj_storage_t(); + worker_ctxt->cache.push(static_cast(popped_blocks[i])); + } + node = worker_ctxt->cache.try_pop(); + } + +#ifdef SRSRAN_BUFFER_POOL_LOG_ENABLED + if (node == nullptr) { + print_error("Error allocating buffer in pool of ObjSize=%zd", ObjSize); + } +#endif + return node; + } + + void deallocate_node(void* p) + { + srsran_assert(p != nullptr, "Deallocated nodes must have valid address"); + worker_ctxt* worker_ctxt = get_worker_cache(); + obj_storage_t* block_ptr = static_cast(p); + + if (DebugSanitizeAddress) { + std::lock_guard lock(mutex); + srsran_assert(std::any_of(allocated_blocks.begin(), + allocated_blocks.end(), + [block_ptr](const std::unique_ptr& b) { return b.get() == block_ptr; }), + "Error deallocating block with address 0x%lx", + (long unsigned)block_ptr); + } + + // push to local memory block cache + worker_ctxt->cache.push(static_cast(p)); + + if (worker_ctxt->cache.size() >= local_growth_thres) { + // if local cache reached max capacity, send half of the blocks to central cache + central_mem_cache.steal_blocks(worker_ctxt->cache, worker_ctxt->cache.size() / 2); + } + } + + void enable_logger(bool enabled) + { + if (enabled) { + logger = &srslog::fetch_basic_logger("POOL"); + logger->set_level(srslog::basic_levels::debug); + } else { + logger = nullptr; + } + } + + void print_all_buffers() + { + auto* worker = get_worker_cache(); + size_t tot_blocks = 0; + { + std::lock_guard lock(mutex); + tot_blocks = allocated_blocks.size(); + } + printf("There are %zd/%zd buffers in shared block container. This thread contains %zd in its local cache\n", + central_mem_cache.size(), + tot_blocks, + worker->cache.size()); + } + +private: + struct worker_ctxt { + std::thread::id id; + memblock_cache cache; + + worker_ctxt() : id(std::this_thread::get_id()) {} + ~worker_ctxt() + { + mutexed_memblock_cache& central_cache = pool_type::get_instance()->central_mem_cache; + central_cache.steal_blocks(cache, cache.size()); + } + }; + + worker_ctxt* get_worker_cache() + { + thread_local worker_ctxt worker_cache; + return &worker_cache; + } + + /// Formats and prints the input string and arguments into the configured output stream. + template + void print_error(const char* str, Args&&... args) + { + if (logger != nullptr) { + logger->error(str, std::forward(args)...); + } else { + fmt::printf(std::string(str) + "\n", std::forward(args)...); + } + } + + size_t local_growth_thres = 0; + srslog::basic_logger* logger = nullptr; + + mutexed_memblock_cache central_mem_cache; + std::mutex mutex; + std::vector > allocated_blocks; +}; + +} // namespace srsran + +#endif // SRSRAN_FIXED_SIZE_POOL_H diff --git a/lib/include/srsran/adt/mem_pool.h b/lib/include/srsran/adt/pool/mem_pool.h similarity index 67% rename from lib/include/srsran/adt/mem_pool.h rename to lib/include/srsran/adt/pool/mem_pool.h index df2cbb02c..995e8c650 100644 --- a/lib/include/srsran/adt/mem_pool.h +++ b/lib/include/srsran/adt/pool/mem_pool.h @@ -22,6 +22,7 @@ #ifndef SRSRAN_MEM_POOL_H #define SRSRAN_MEM_POOL_H +#include "memblock_cache.h" #include "srsran/common/thread_pool.h" #include #include @@ -30,121 +31,6 @@ namespace srsran { -/// Stores provided mem blocks in a stack in an non-owning manner. Not thread-safe -class memblock_stack -{ - struct node { - node* prev; - explicit node(node* prev_) : prev(prev_) {} - }; - -public: - constexpr static size_t min_memblock_size() { return sizeof(node); } - - memblock_stack() = default; - - memblock_stack(const memblock_stack&) = delete; - - memblock_stack(memblock_stack&& other) noexcept : head(other.head) { other.head = nullptr; } - - memblock_stack& operator=(const memblock_stack&) = delete; - - memblock_stack& operator=(memblock_stack&& other) noexcept - { - head = other.head; - other.head = nullptr; - return *this; - } - - void push(uint8_t* block) noexcept - { - // printf("head: %ld\n", (long)head); - node* next = ::new (block) node(head); - head = next; - count++; - } - - uint8_t* try_pop() noexcept - { - if (is_empty()) { - return nullptr; - } - node* last_head = head; - head = head->prev; - count--; - return (uint8_t*)last_head; - } - - bool is_empty() const { return head == nullptr; } - - size_t size() const { return count; } - - void clear() { head = nullptr; } - -private: - node* head = nullptr; - size_t count = 0; -}; - -/// memblock stack that mutexes pushing/popping -class mutexed_memblock_stack -{ -public: - mutexed_memblock_stack() = default; - - mutexed_memblock_stack(const mutexed_memblock_stack&) = delete; - - mutexed_memblock_stack(mutexed_memblock_stack&& other) noexcept - { - std::unique_lock lk1(other.mutex, std::defer_lock); - std::unique_lock lk2(mutex, std::defer_lock); - std::lock(lk1, lk2); - stack = std::move(other.stack); - } - - mutexed_memblock_stack& operator=(const mutexed_memblock_stack&) = delete; - - mutexed_memblock_stack& operator=(mutexed_memblock_stack&& other) noexcept - { - std::unique_lock lk1(other.mutex, std::defer_lock); - std::unique_lock lk2(mutex, std::defer_lock); - std::lock(lk1, lk2); - stack = std::move(other.stack); - return *this; - } - - void push(uint8_t* block) noexcept - { - std::lock_guard lock(mutex); - stack.push(block); - } - - uint8_t* try_pop() noexcept - { - std::lock_guard lock(mutex); - uint8_t* block = stack.try_pop(); - return block; - } - - bool is_empty() const noexcept { return stack.is_empty(); } - - size_t size() const noexcept - { - std::lock_guard lock(mutex); - return stack.size(); - } - - void clear() - { - std::lock_guard lock(mutex); - stack.clear(); - } - -private: - memblock_stack stack; - mutable std::mutex mutex; -}; - /** * Pool specialized for big objects. Created objects are not contiguous in memory. * Relevant methods: @@ -158,7 +44,7 @@ template class big_obj_pool { // memory stack type derivation (thread safe or not) - using stack_type = typename std::conditional::type; + using stack_type = typename std::conditional::type; // memory stack to cache allocate memory chunks stack_type stack; @@ -170,7 +56,7 @@ public: void* allocate_node(size_t sz) { assert(sz == sizeof(T)); - static const size_t blocksize = std::max(sizeof(T), memblock_stack::min_memblock_size()); + static const size_t blocksize = std::max(sizeof(T), memblock_cache::min_memblock_size()); uint8_t* block = stack.try_pop(); if (block == nullptr) { block = new uint8_t[blocksize]; @@ -188,7 +74,7 @@ public: /// Pre-reserve N memory chunks for future object allocations void reserve(size_t N) { - static const size_t blocksize = std::max(sizeof(T), memblock_stack::min_memblock_size()); + static const size_t blocksize = std::max(sizeof(T), memblock_cache::min_memblock_size()); for (size_t i = 0; i < N; ++i) { stack.push(new uint8_t[blocksize]); } @@ -293,7 +179,7 @@ private: // memory stack to cache allocate memory chunks std::mutex mutex; - memblock_stack obj_cache; + memblock_cache obj_cache; std::vector > batches; }; diff --git a/lib/include/srsran/adt/pool/memblock_cache.h b/lib/include/srsran/adt/pool/memblock_cache.h new file mode 100644 index 000000000..aee7d7dd1 --- /dev/null +++ b/lib/include/srsran/adt/pool/memblock_cache.h @@ -0,0 +1,159 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSRAN_MEMBLOCK_CACHE_H +#define SRSRAN_MEMBLOCK_CACHE_H + +#include + +namespace srsran { + +/// Stores provided mem blocks in a stack in an non-owning manner. Not thread-safe +class memblock_cache +{ + struct node { + node* prev; + explicit node(node* prev_) : prev(prev_) {} + }; + +public: + constexpr static size_t min_memblock_size() { return sizeof(node); } + + memblock_cache() = default; + + memblock_cache(const memblock_cache&) = delete; + + memblock_cache(memblock_cache&& other) noexcept : head(other.head) { other.head = nullptr; } + + memblock_cache& operator=(const memblock_cache&) = delete; + + memblock_cache& operator=(memblock_cache&& other) noexcept + { + head = other.head; + other.head = nullptr; + return *this; + } + + void push(void* block) noexcept + { + // printf("head: %ld\n", (long)head); + node* next = ::new (block) node(head); + head = next; + count++; + } + + uint8_t* try_pop() noexcept + { + if (is_empty()) { + return nullptr; + } + node* last_head = head; + head = head->prev; + count--; + return (uint8_t*)last_head; + } + + bool is_empty() const { return head == nullptr; } + + size_t size() const { return count; } + + void clear() { head = nullptr; } + +private: + node* head = nullptr; + size_t count = 0; +}; + +/// memblock stack that mutexes pushing/popping +class mutexed_memblock_cache +{ +public: + mutexed_memblock_cache() = default; + + mutexed_memblock_cache(const mutexed_memblock_cache&) = delete; + + mutexed_memblock_cache(mutexed_memblock_cache&& other) noexcept + { + std::unique_lock lk1(other.mutex, std::defer_lock); + std::unique_lock lk2(mutex, std::defer_lock); + std::lock(lk1, lk2); + stack = std::move(other.stack); + } + + mutexed_memblock_cache& operator=(const mutexed_memblock_cache&) = delete; + + mutexed_memblock_cache& operator=(mutexed_memblock_cache&& other) noexcept + { + std::unique_lock lk1(other.mutex, std::defer_lock); + std::unique_lock lk2(mutex, std::defer_lock); + std::lock(lk1, lk2); + stack = std::move(other.stack); + return *this; + } + + void push(void* block) noexcept + { + std::lock_guard lock(mutex); + stack.push(block); + } + + void steal_blocks(memblock_cache& other, size_t max_n) noexcept + { + std::lock_guard lock(mutex); + for (size_t i = 0; i < max_n and not other.is_empty(); ++i) { + stack.push(other.try_pop()); + } + } + + uint8_t* try_pop() noexcept + { + std::lock_guard lock(mutex); + uint8_t* block = stack.try_pop(); + return block; + } + + template + size_t try_pop(std::array& result) noexcept + { + std::lock_guard lock(mutex); + size_t i = 0; + for (; i < N; ++i) { + result[i] = stack.try_pop(); + if (result[i] == nullptr) { + break; + } + } + return i; + } + + bool is_empty() const noexcept { return stack.is_empty(); } + + size_t size() const noexcept + { + std::lock_guard lock(mutex); + return stack.size(); + } + + void clear() + { + std::lock_guard lock(mutex); + stack.clear(); + } + +private: + memblock_cache stack; + mutable std::mutex mutex; +}; + +} // namespace srsran + +#endif // SRSRAN_MEMBLOCK_CACHE_H diff --git a/lib/include/srsran/common/singleton.h b/lib/include/srsran/adt/singleton.h similarity index 100% rename from lib/include/srsran/common/singleton.h rename to lib/include/srsran/adt/singleton.h diff --git a/lib/include/srsran/common/buffer_pool.h b/lib/include/srsran/common/buffer_pool.h index 65e1dd732..328aaa904 100644 --- a/lib/include/srsran/common/buffer_pool.h +++ b/lib/include/srsran/common/buffer_pool.h @@ -23,6 +23,7 @@ #define SRSRAN_BUFFER_POOL_H #include "byte_buffer.h" +#include "srsran/adt/bounded_vector.h" #include #include #include @@ -30,10 +31,7 @@ #include #include -/******************************************************************************* - INCLUDES -*******************************************************************************/ - +#include "srsran/adt/pool/fixed_size_pool.h" #include "srsran/common/common.h" #include "srsran/srslog/srslog.h" @@ -174,58 +172,7 @@ private: uint32_t capacity; }; -class byte_buffer_pool -{ - using mem_chunk = typename std::aligned_storage::type; - -public: - // Singleton static methods - static byte_buffer_pool* get_instance(int capacity = -1) - { - static std::unique_ptr instance(new byte_buffer_pool(capacity)); - return instance.get(); - } - byte_buffer_pool(int capacity = -1) : pool(capacity) {} - byte_buffer_pool(const byte_buffer_pool& other) = delete; - byte_buffer_pool(byte_buffer_pool&& other) = delete; - byte_buffer_pool& operator=(const byte_buffer_pool& other) = delete; - byte_buffer_pool& operator=(byte_buffer_pool&& other) = delete; - void* allocate(const char* debug_name = nullptr, bool blocking = false) - { - return pool.allocate(debug_name, blocking); - } - void enable_logger(bool enabled) { print_to_log = enabled; } - void deallocate(void* b) - { - if (!b) { - return; - } - if (!pool.deallocate(static_cast(b))) { -#ifdef SRSRAN_BUFFER_POOL_LOG_ENABLED - print_error("Error deallocating PDU: Addr=0x%p, name=%s not found in pool", (void*)b, b->debug_name); -#else - print_error("Error deallocating PDU: Addr=0x%p", (void*)b); -#endif - } - } - void print_all_buffers() { pool.print_all_buffers(); } - -private: - /// Formats and prints the input string and arguments into the configured output stream. - template - void print_error(const char* str, Args&&... args) - { - if (print_to_log) { - srslog::fetch_basic_logger("POOL", false).error(str, std::forward(args)...); - } else { - fmt::printf(std::string(str) + "\n", std::forward(args)...); - } - } - -private: - bool print_to_log = false; - buffer_pool pool; -}; +using byte_buffer_pool = concurrent_fixed_memory_pool; inline unique_byte_buffer_t make_byte_buffer() noexcept { @@ -246,6 +193,56 @@ inline unique_byte_buffer_t make_byte_buffer(const char* debug_ctxt) noexcept return buffer; } +namespace detail { + +struct byte_buffer_pool_deleter { + void operator()(void* ptr) { byte_buffer_pool::get_instance()->deallocate_node(ptr); } +}; + +} // namespace detail + +/** + * Class to wrap objects of type T which get allocated/deallocated using the byte_buffer_pool + * @tparam T type of the object being allocated + */ +template +struct byte_buffer_pool_ptr { + static_assert(sizeof(T) <= byte_buffer_pool::BLOCK_SIZE, "pool_bounded_vector does not fit buffer pool block size"); + +public: + byte_buffer_pool_ptr() = default; + void reset() { ptr.reset(); } + + T* operator->() { return ptr.get(); } + const T* operator->() const { return ptr.get(); } + T& operator*() { return *ptr; } + const T& operator*() const { return *ptr; } + bool has_value() const { return ptr.get() != nullptr; } + + template + void emplace(CtorArgs&&... args) + { + ptr.reset(make(std::forward(args)...).ptr.release()); + } + + template + static byte_buffer_pool_ptr make(CtorArgs&&... args) + { + void* memblock = byte_buffer_pool::get_instance()->allocate_node(sizeof(T)); + if (memblock == nullptr) { + return byte_buffer_pool_ptr(); + } + new (memblock) T(std::forward(args)...); + byte_buffer_pool_ptr ret; + ret.ptr = std::unique_ptr(static_cast(memblock), + detail::byte_buffer_pool_deleter()); + return ret; + }; + +private: + std::unique_ptr ptr; +}; + } // namespace srsran #endif // SRSRAN_BUFFER_POOL_H diff --git a/lib/include/srsran/common/byte_buffer.h b/lib/include/srsran/common/byte_buffer.h index 57668526f..bb6990289 100644 --- a/lib/include/srsran/common/byte_buffer.h +++ b/lib/include/srsran/common/byte_buffer.h @@ -23,6 +23,7 @@ #define SRSRAN_BYTE_BUFFER_H #include "common.h" +#include "srsran/adt/span.h" #include #include @@ -206,9 +207,6 @@ struct bit_buffer_t { uint32_t get_headroom() { return msg - buffer; } }; -// Create a Managed Life-Time Byte Buffer -class byte_buffer_pool; - using unique_byte_buffer_t = std::unique_ptr; /// diff --git a/lib/include/srsran/common/common.h b/lib/include/srsran/common/common.h index 7df364617..570a8e6fc 100644 --- a/lib/include/srsran/common/common.h +++ b/lib/include/srsran/common/common.h @@ -26,11 +26,11 @@ INCLUDES *******************************************************************************/ -#include "srsran/adt/span.h" #include +#include #include #include -#include +#include #include /******************************************************************************* diff --git a/lib/include/srsran/common/enb_events.h b/lib/include/srsran/common/enb_events.h index 007b3ae10..eac12ca40 100644 --- a/lib/include/srsran/common/enb_events.h +++ b/lib/include/srsran/common/enb_events.h @@ -38,11 +38,12 @@ class event_logger_interface public: virtual ~event_logger_interface() = default; - /// Logs into the underlying log channel the RRC connected event. - virtual void log_rrc_connected(uint32_t enb_cc_idx, const std::string& asn1, unsigned error_code, uint16_t rnti) = 0; - - /// Logs into the underlying log channel the RRC disconnected event. - virtual void log_rrc_disconnect(uint32_t enb_cc_idx, unsigned reason, uint16_t rnti) = 0; + /// Logs into the underlying log channel any RRC event. + virtual void log_rrc_event(uint32_t enb_cc_idx, + const std::string& asn1, + unsigned type, + unsigned additional_info, + uint16_t rnti) = 0; /// Logs into the underlying log channel the S1 context create event. virtual void log_s1_ctx_create(uint32_t enb_cc_idx, uint32_t mme_id, uint32_t enb_id, uint16_t rnti) = 0; diff --git a/lib/include/srsran/common/srsran_assert.h b/lib/include/srsran/common/srsran_assert.h index 1d08982e0..4ba4ceb4b 100644 --- a/lib/include/srsran/common/srsran_assert.h +++ b/lib/include/srsran/common/srsran_assert.h @@ -25,10 +25,14 @@ #include "srsran/srslog/srslog.h" #include -#ifdef ASSERTS_ENABLED - #define srsran_unlikely(expr) __builtin_expect(!!(expr), 0) +#define srsran_terminate(fmt, ...) \ + std::fprintf(stderr, "%s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ + std::abort() + +#ifdef ASSERTS_ENABLED + /** * Macro that asserts condition is true. If false, it logs the remaining parameters, prints the backtrace and closes * the application @@ -36,8 +40,7 @@ #define srsran_assert(condition, fmt, ...) \ do { \ if (srsran_unlikely(not(condition))) { \ - std::fprintf(stderr, "%s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ - std::abort(); \ + srsran_terminate(fmt, ##__VA_ARGS__); \ } \ } while (0) diff --git a/lib/include/srsran/interfaces/enb_gtpu_interfaces.h b/lib/include/srsran/interfaces/enb_gtpu_interfaces.h index c3a1c9fdc..8bbed14a4 100644 --- a/lib/include/srsran/interfaces/enb_gtpu_interfaces.h +++ b/lib/include/srsran/interfaces/enb_gtpu_interfaces.h @@ -22,6 +22,7 @@ #ifndef SRSRAN_ENB_GTPU_INTERFACES_H #define SRSRAN_ENB_GTPU_INTERFACES_H +#include "srsran/adt/expected.h" #include "srsran/common/byte_buffer.h" namespace srsenb { @@ -44,7 +45,7 @@ public: uint32_t flush_before_teidin = 0; }; - virtual uint32_t + virtual srsran::expected add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, const bearer_props* props = nullptr) = 0; virtual void set_tunnel_status(uint32_t teidin, bool dl_active) = 0; virtual void rem_bearer(uint16_t rnti, uint32_t lcid) = 0; diff --git a/lib/include/srsran/interfaces/enb_mac_interfaces.h b/lib/include/srsran/interfaces/enb_mac_interfaces.h index 75983ca32..9e97c1852 100644 --- a/lib/include/srsran/interfaces/enb_mac_interfaces.h +++ b/lib/include/srsran/interfaces/enb_mac_interfaces.h @@ -32,6 +32,7 @@ struct mac_args_t { sched_interface::sched_args_t sched; int nr_tb_size = -1; uint32_t max_nof_ues; + uint32_t max_nof_kos; }; /* Interface PHY -> MAC */ diff --git a/lib/include/srsran/interfaces/enb_rrc_interfaces.h b/lib/include/srsran/interfaces/enb_rrc_interfaces.h index 578fb18c5..becf91451 100644 --- a/lib/include/srsran/interfaces/enb_rrc_interfaces.h +++ b/lib/include/srsran/interfaces/enb_rrc_interfaces.h @@ -32,8 +32,8 @@ namespace srsenb { class rrc_interface_s1ap { public: - virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0; - virtual void release_complete(uint16_t rnti) = 0; + virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0; + virtual void release_ue(uint16_t rnti) = 0; virtual bool setup_ue_ctxt(uint16_t rnti, const asn1::s1ap::init_context_setup_request_s& msg) = 0; virtual bool modify_ue_ctxt(uint16_t rnti, const asn1::s1ap::ue_context_mod_request_s& msg) = 0; virtual bool setup_ue_erabs(uint16_t rnti, const asn1::s1ap::erab_setup_request_s& msg) = 0; @@ -82,7 +82,7 @@ public: /* Radio Link failure */ virtual int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) = 0; virtual void upd_user(uint16_t new_rnti, uint16_t old_rnti) = 0; - virtual void set_activity_user(uint16_t rnti) = 0; + virtual void set_activity_user(uint16_t rnti, bool ack_info) = 0; virtual bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) = 0; ///< Provide packed SIB to MAC (buffer is managed by RRC) diff --git a/lib/include/srsran/interfaces/mac_interface_types.h b/lib/include/srsran/interfaces/mac_interface_types.h index dfd49e906..557b63ca4 100644 --- a/lib/include/srsran/interfaces/mac_interface_types.h +++ b/lib/include/srsran/interfaces/mac_interface_types.h @@ -159,6 +159,17 @@ struct sr_cfg_nr_t { sr_cfg_item_nr_t item[SRSRAN_MAX_MAX_NR_OF_SR_CFG_PER_CELL_GROUP]; }; +struct bsr_cfg_nr_t { + // mandatory BSR config + int periodic_timer; + int retx_timer; + + // SR specific configs for logical channel + bool sr_delay_timer_enabled; + int sr_delay_timer; + bool sr_mask; // Indicates whether SR masking is configured for this logical channel +}; + struct mac_cfg_t { // Default constructor with default values as in 36.331 9.2.2 mac_cfg_t() { set_defaults(); } diff --git a/lib/include/srsran/interfaces/ue_nr_interfaces.h b/lib/include/srsran/interfaces/ue_nr_interfaces.h index 0b73ca7b1..e19cade54 100644 --- a/lib/include/srsran/interfaces/ue_nr_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nr_interfaces.h @@ -108,10 +108,10 @@ class mac_interface_rrc_nr { public: // Config calls that return SRSRAN_SUCCESS or SRSRAN_ERROR - virtual void setup_lcid(const srsran::logical_channel_config_t& config) = 0; - virtual void set_config(const srsran::bsr_cfg_t& bsr_cfg) = 0; - virtual int32_t set_config(const srsran::sr_cfg_nr_t& sr_cfg) = 0; - virtual void set_config(const srsran::rach_nr_cfg_t& rach_cfg) = 0; + virtual int setup_lcid(const srsran::logical_channel_config_t& config) = 0; + virtual int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg) = 0; + virtual int set_config(const srsran::sr_cfg_nr_t& sr_cfg) = 0; + virtual void set_config(const srsran::rach_nr_cfg_t& rach_cfg) = 0; // RRC triggers MAC ra procedure virtual void start_ra_procedure() = 0; diff --git a/lib/include/srsran/interfaces/ue_rrc_interfaces.h b/lib/include/srsran/interfaces/ue_rrc_interfaces.h index 0083a653b..7df5254c2 100644 --- a/lib/include/srsran/interfaces/ue_rrc_interfaces.h +++ b/lib/include/srsran/interfaces/ue_rrc_interfaces.h @@ -30,16 +30,11 @@ namespace srsue { -class rrc_interface_mac_common -{ -public: - virtual void ra_problem() = 0; -}; - -class rrc_interface_mac : public rrc_interface_mac_common +class rrc_interface_mac { public: virtual void ra_completed() = 0; + virtual void ra_problem() = 0; virtual void release_pucch_srs() = 0; }; diff --git a/lib/include/srsran/mac/mac_sch_pdu_nr.h b/lib/include/srsran/mac/mac_sch_pdu_nr.h index 51f72227c..cdd893b3e 100644 --- a/lib/include/srsran/mac/mac_sch_pdu_nr.h +++ b/lib/include/srsran/mac/mac_sch_pdu_nr.h @@ -97,9 +97,10 @@ public: uint32_t write_subpdu(const uint8_t* start_); -private: - uint32_t sizeof_ce(uint32_t lcid, bool is_ul); + // Used by BSR procedure to determine size of BSR types + static uint32_t sizeof_ce(uint32_t lcid, bool is_ul); +private: // protected: uint32_t lcid = 0; int header_length = 0; diff --git a/lib/include/srsran/upper/rlc_am_lte.h b/lib/include/srsran/upper/rlc_am_lte.h index c81b8cefe..69aa72191 100644 --- a/lib/include/srsran/upper/rlc_am_lte.h +++ b/lib/include/srsran/upper/rlc_am_lte.h @@ -85,7 +85,7 @@ struct rlc_ringbuffer_t { rlc_ringbuffer_t() { clear(); } T& add_pdu(size_t sn) { - srsran_expect(not has_sn(sn), "The same SN=%d should not be added twice", sn); + srsran_expect(not has_sn(sn), "The same SN=%zd should not be added twice", sn); window[sn].rlc_sn = sn; active_flag[sn] = true; count++; @@ -93,14 +93,14 @@ struct rlc_ringbuffer_t { } void remove_pdu(size_t sn) { - srsran_expect(has_sn(sn), "The removed SN=%d is not in the window", sn); + srsran_expect(has_sn(sn), "The removed SN=%zd is not in the window", sn); window[sn] = {}; active_flag[sn] = false; count--; } T& operator[](size_t sn) { - srsran_expect(has_sn(sn), "The accessed SN=%d is not in the window", sn); + srsran_expect(has_sn(sn), "The accessed SN=%zd is not in the window", sn); return window[sn]; } size_t size() const { return count; } diff --git a/lib/src/common/byte_buffer.cc b/lib/src/common/byte_buffer.cc index 30554a67d..40e09d836 100644 --- a/lib/src/common/byte_buffer.cc +++ b/lib/src/common/byte_buffer.cc @@ -27,13 +27,13 @@ namespace srsran { void* byte_buffer_t::operator new(size_t sz, const std::nothrow_t& nothrow_value) noexcept { assert(sz == sizeof(byte_buffer_t)); - return byte_buffer_pool::get_instance()->allocate(nullptr, false); + return byte_buffer_pool::get_instance()->allocate_node(sz); } void* byte_buffer_t::operator new(size_t sz) { assert(sz == sizeof(byte_buffer_t)); - void* ptr = byte_buffer_pool::get_instance()->allocate(nullptr, false); + void* ptr = byte_buffer_pool::get_instance()->allocate_node(sz); if (ptr == nullptr) { throw std::bad_alloc(); } @@ -42,7 +42,7 @@ void* byte_buffer_t::operator new(size_t sz) void byte_buffer_t::operator delete(void* ptr) { - byte_buffer_pool::get_instance()->deallocate(ptr); + byte_buffer_pool::get_instance()->deallocate_node(ptr); } } // namespace srsran diff --git a/lib/src/common/enb_events.cc b/lib/src/common/enb_events.cc index ecb03d003..8c0068c2e 100644 --- a/lib/src/common/enb_events.cc +++ b/lib/src/common/enb_events.cc @@ -31,8 +31,12 @@ namespace { class null_event_logger : public event_logger_interface { public: - void log_rrc_connected(uint32_t enb_cc_idx, const std::string& asn1, unsigned error_code, uint16_t rnti) override {} - void log_rrc_disconnect(uint32_t enb_cc_idx, unsigned reason, uint16_t rnti) override {} + void log_rrc_event(uint32_t enb_cc_idx, + const std::string& asn1, + unsigned type, + unsigned additional_info, + uint16_t rnti) override + {} void log_s1_ctx_create(uint32_t enb_cc_idx, uint32_t mme_id, uint32_t enb_id, uint16_t rnti) override {} void log_s1_ctx_delete(uint32_t enb_cc_idx, uint32_t mme_id, uint32_t enb_id, uint16_t rnti) override {} void log_sector_start(uint32_t cc_idx, uint32_t pci, uint32_t cell_id) override {} @@ -72,28 +76,18 @@ DECLARE_METRIC_SET("event_data", mset_sector_event, metric_pci, metric_cell_iden using sector_event_t = srslog:: build_context_type; -/// Context for RRC connect. -DECLARE_METRIC("error_code", metric_error_code, uint32_t, ""); +/// Context for a RRC event. +DECLARE_METRIC("asn1_type", metric_asn1_type, uint32_t, ""); +DECLARE_METRIC("additional", metric_additional, uint32_t, ""); DECLARE_METRIC_SET("event_data", - mset_rrc_connect_event, + mset_rrc_event, metric_rnti, metric_asn1_length, metric_asn1_message, - metric_error_code); -using rrc_connect_event_t = srslog::build_context_type; - -/// Context for RRC disconnect. -DECLARE_METRIC("reason", metric_reason, uint32_t, ""); -DECLARE_METRIC_SET("event_data", mset_rrc_disconnect_event, metric_reason, metric_rnti); -using rrc_disconnect_event_t = srslog::build_context_type; + metric_asn1_type, + metric_additional); +using rrc_event_t = srslog:: + build_context_type; /// Context for S1 context create/delete. DECLARE_METRIC("mme_ue_s1ap_id", metric_ue_mme_id, uint32_t, ""); @@ -121,31 +115,23 @@ class logging_event_logger : public event_logger_interface public: explicit logging_event_logger(srslog::log_channel& c) : event_channel(c) {} - void log_rrc_connected(uint32_t enb_cc_idx, const std::string& asn1, unsigned error_code, uint16_t rnti) override + void log_rrc_event(uint32_t enb_cc_idx, + const std::string& asn1, + unsigned type, + unsigned additional_info, + uint16_t rnti) override { - rrc_connect_event_t ctx(""); + rrc_event_t ctx(""); ctx.write("event"); ctx.write(get_time_stamp()); ctx.write(enb_cc_idx); - ctx.write("rrc_connect"); - ctx.get().write(rnti); - ctx.get().write(asn1.size()); - ctx.get().write(asn1); - ctx.get().write(error_code); - event_channel(ctx); - } - - void log_rrc_disconnect(uint32_t enb_cc_idx, unsigned reason, uint16_t rnti) override - { - rrc_disconnect_event_t ctx(""); - - ctx.write("event"); - ctx.write(get_time_stamp()); - ctx.write(enb_cc_idx); - ctx.write("rrc_disconnect"); - ctx.get().write(reason); - ctx.get().write(rnti); + ctx.write("rrc_log"); + ctx.get().write(rnti); + ctx.get().write(asn1.size()); + ctx.get().write(asn1); + ctx.get().write(type); + ctx.get().write(additional_info); event_channel(ctx); } diff --git a/lib/src/common/mac_pcap_base.cc b/lib/src/common/mac_pcap_base.cc index 66fed75d8..73973ec2e 100644 --- a/lib/src/common/mac_pcap_base.cc +++ b/lib/src/common/mac_pcap_base.cc @@ -54,9 +54,9 @@ void mac_pcap_base::run_thread() } // write remainder of queue - std::lock_guard lock(mutex); pcap_pdu_t pdu = {}; while (queue.try_pop(pdu)) { + std::lock_guard lock(mutex); write_pdu(pdu); } } diff --git a/lib/test/adt/CMakeLists.txt b/lib/test/adt/CMakeLists.txt index 87320d9a4..ed0763cf9 100644 --- a/lib/test/adt/CMakeLists.txt +++ b/lib/test/adt/CMakeLists.txt @@ -61,3 +61,11 @@ add_test(circular_buffer_test circular_buffer_test) add_executable(circular_map_test circular_map_test.cc) target_link_libraries(circular_map_test srsran_common) add_test(circular_map_test circular_map_test) + +add_executable(fsm_test fsm_test.cc) +target_link_libraries(fsm_test srsran_common) +add_test(fsm_test fsm_test) + +add_executable(optional_test optional_test.cc) +target_link_libraries(optional_test srsran_common) +add_test(optional_test optional_test) diff --git a/lib/test/common/fsm_test.cc b/lib/test/adt/fsm_test.cc similarity index 99% rename from lib/test/common/fsm_test.cc rename to lib/test/adt/fsm_test.cc index 48bfdca1b..129c31ca3 100644 --- a/lib/test/common/fsm_test.cc +++ b/lib/test/adt/fsm_test.cc @@ -19,7 +19,7 @@ * */ -#include "srsran/common/fsm.h" +#include "srsran/adt/fsm.h" #include "srsran/common/test_common.h" ///////////////////////////// diff --git a/lib/test/adt/mem_pool_test.cc b/lib/test/adt/mem_pool_test.cc index 806bba06b..465f6b710 100644 --- a/lib/test/adt/mem_pool_test.cc +++ b/lib/test/adt/mem_pool_test.cc @@ -19,7 +19,8 @@ * */ -#include "srsran/adt/mem_pool.h" +#include "srsran/adt/pool/fixed_size_pool.h" +#include "srsran/adt/pool/mem_pool.h" #include "srsran/common/test_common.h" class C @@ -84,9 +85,77 @@ int test_nontrivial_obj_pool() return SRSRAN_SUCCESS; } -int main() +struct BigObj { + C c; + std::array space; + + using pool_t = srsran::concurrent_fixed_memory_pool<512, true>; + + void* operator new(size_t sz) + { + srsran_assert(sz == sizeof(BigObj), "Allocated node size and object size do not match"); + return pool_t::get_instance()->allocate_node(sizeof(BigObj)); + } + void* operator new(size_t sz, const std::nothrow_t& nothrow_value) noexcept + { + srsran_assert(sz == sizeof(BigObj), "Allocated node size and object size do not match"); + return pool_t::get_instance()->allocate_node(sizeof(BigObj)); + } + void operator delete(void* ptr) { pool_t::get_instance()->deallocate_node(ptr); } +}; + +void test_fixedsize_pool() { + size_t pool_size = 1024; + auto* fixed_pool = BigObj::pool_t::get_instance(pool_size); + fixed_pool->print_all_buffers(); + { + std::vector > vec(pool_size); + for (size_t i = 0; i < pool_size; ++i) { + vec[i].reset(new BigObj()); + TESTASSERT(vec[i].get() != nullptr); + } + std::unique_ptr obj(new (std::nothrow) BigObj()); + TESTASSERT(obj == nullptr); + vec.clear(); + obj = std::unique_ptr(new (std::nothrow) BigObj()); + TESTASSERT(obj != nullptr); + obj.reset(); + fixed_pool->print_all_buffers(); + } + fixed_pool->print_all_buffers(); + + // TEST: one thread allocates, and the other deallocates + { + std::unique_ptr obj; + std::atomic stop(false); + srsran::dyn_blocking_queue > queue(pool_size / 2); + std::thread t([&queue, &stop]() { + while (not stop.load(std::memory_order_relaxed)) { + std::unique_ptr obj(new (std::nothrow) BigObj()); + TESTASSERT(obj != nullptr); + queue.try_push(std::move(obj)); + } + }); + + for (size_t i = 0; i < pool_size * 8; ++i) { + obj = queue.pop_blocking(); + TESTASSERT(obj != nullptr); + } + stop.store(true); + fixed_pool->print_all_buffers(); + t.join(); + } + fixed_pool->print_all_buffers(); +} + +int main(int argc, char** argv) +{ + srsran::test_init(argc, argv); + TESTASSERT(test_nontrivial_obj_pool() == SRSRAN_SUCCESS); + test_fixedsize_pool(); + srsran::console("Success\n"); return 0; } \ No newline at end of file diff --git a/lib/test/adt/optional_test.cc b/lib/test/adt/optional_test.cc new file mode 100644 index 000000000..bcfe19063 --- /dev/null +++ b/lib/test/adt/optional_test.cc @@ -0,0 +1,35 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsran/adt/optional.h" +#include "srsran/common/test_common.h" + +using namespace srsran; + +void test_optional_int() +{ + optional opt, opt2(5); + TESTASSERT(not opt.has_value() and opt2.has_value()); + TESTASSERT(not static_cast(opt) and static_cast(opt2)); + TESTASSERT(opt2.value() == 5 and *opt2 == 5); + + opt = 4; + TESTASSERT(opt.has_value()); + TESTASSERT(opt != opt2); + opt2 = 4; + TESTASSERT(opt == opt2); +} + +int main() +{ + test_optional_int(); +} \ No newline at end of file diff --git a/lib/test/common/CMakeLists.txt b/lib/test/common/CMakeLists.txt index e39591de2..eae2514f1 100644 --- a/lib/test/common/CMakeLists.txt +++ b/lib/test/common/CMakeLists.txt @@ -76,10 +76,6 @@ add_executable(tti_point_test tti_point_test.cc) target_link_libraries(tti_point_test srsran_common) add_test(tti_point_test tti_point_test) -add_executable(fsm_test fsm_test.cc) -target_link_libraries(fsm_test srsran_common) -add_test(fsm_test fsm_test) - add_executable(choice_type_test choice_type_test.cc) target_link_libraries(choice_type_test srsran_common) add_test(choice_type_test choice_type_test) diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index e8dcc7b97..f89735a87 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -324,6 +324,7 @@ enable = false #pregenerate_signals = false #tx_amplitude = 0.6 #rrc_inactivity_timer = 30000 +#max_nof_kos = 100 #max_prach_offset_us = 30 #eea_pref_list = EEA0, EEA2, EEA1 #eia_pref_list = EIA2, EIA1, EIA0 diff --git a/srsenb/hdr/enb.h b/srsenb/hdr/enb.h index 2ec9ea2f0..c2a404414 100644 --- a/srsenb/hdr/enb.h +++ b/srsenb/hdr/enb.h @@ -102,6 +102,7 @@ struct general_args_t { std::string tracing_filename; std::string eia_pref_list; std::string eea_pref_list; + uint32_t max_mac_dl_kos; }; struct all_args_t { diff --git a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_harq.h b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_harq.h index e0745c671..e0fab40cd 100644 --- a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_harq.h +++ b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_harq.h @@ -33,9 +33,10 @@ class harq_proc { public: harq_proc(); - void init(uint32_t id); - void reset(uint32_t tb_idx); - uint32_t get_id() const; + void init(uint32_t id); + void reset(uint32_t tb_idx); + + uint32_t get_id() const { return id; } bool is_empty() const; bool is_empty(uint32_t tb_idx) const; @@ -71,6 +72,9 @@ class dl_harq_proc : public harq_proc { public: dl_harq_proc(); + + void new_tti(tti_point tti_tx_dl); + void new_tx(const rbgmask_t& new_mask, uint32_t tb_idx, tti_point tti_tx_dl, @@ -95,6 +99,8 @@ private: class ul_harq_proc : public harq_proc { public: + void new_tti(); + void new_tx(srsran::tti_point tti, int mcs, int tbs, prb_interval alloc, uint32_t max_retx_, bool is_msg3); void new_retx(srsran::tti_point tti_, int* mcs, int* tbs, prb_interval alloc); bool set_ack(uint32_t tb_idx, bool ack); diff --git a/srsenb/hdr/stack/mac/ue.h b/srsenb/hdr/stack/mac/ue.h index 05d42b670..b5e6cf5a8 100644 --- a/srsenb/hdr/stack/mac/ue.h +++ b/srsenb/hdr/stack/mac/ue.h @@ -148,7 +148,7 @@ public: generate_mch_pdu(uint32_t harq_pid, sched_interface::dl_pdu_mch_t sched, uint32_t nof_pdu_elems, uint32_t grant_size); srsran_softbuffer_tx_t* - get_tx_softbuffer(const uint32_t ue_cc_idx, const uint32_t harq_process, const uint32_t tb_idx); + get_tx_softbuffer(const uint32_t ue_cc_idx, const uint32_t harq_process, const uint32_t tb_idx); srsran_softbuffer_rx_t* get_rx_softbuffer(const uint32_t ue_cc_idx, const uint32_t tti); bool process_pdus(); diff --git a/srsenb/hdr/stack/rrc/rrc.h b/srsenb/hdr/stack/rrc/rrc.h index 03fa0ee40..7861cd42e 100644 --- a/srsenb/hdr/stack/rrc/rrc.h +++ b/srsenb/hdr/stack/rrc/rrc.h @@ -76,7 +76,7 @@ public: // rrc_interface_mac int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) override; void upd_user(uint16_t new_rnti, uint16_t old_rnti) override; - void set_activity_user(uint16_t rnti) override; + void set_activity_user(uint16_t rnti, bool ack_info) override; bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) override; uint8_t* read_pdu_bcch_dlsch(const uint8_t cc_idx, const uint32_t sib_index) override; @@ -86,7 +86,7 @@ public: // rrc_interface_s1ap void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) override; - void release_complete(uint16_t rnti) override; + void release_ue(uint16_t rnti) override; bool setup_ue_ctxt(uint16_t rnti, const asn1::s1ap::init_context_setup_request_s& msg) override; bool modify_ue_ctxt(uint16_t rnti, const asn1::s1ap::ue_context_mod_request_s& msg) override; bool setup_ue_erabs(uint16_t rnti, const asn1::s1ap::erab_setup_request_s& msg) override; @@ -190,11 +190,12 @@ private: srsran::unique_byte_buffer_t pdu; } rrc_pdu; - const static uint32_t LCID_EXIT = 0xffff0000; - const static uint32_t LCID_REM_USER = 0xffff0001; - const static uint32_t LCID_REL_USER = 0xffff0002; - const static uint32_t LCID_ACT_USER = 0xffff0004; - const static uint32_t LCID_RTX_USER = 0xffff0005; + const static uint32_t LCID_EXIT = 0xffff0000; + const static uint32_t LCID_REM_USER = 0xffff0001; + const static uint32_t LCID_REL_USER = 0xffff0002; + const static uint32_t LCID_ACT_USER = 0xffff0004; + const static uint32_t LCID_RTX_USER = 0xffff0005; + const static uint32_t LCID_MAC_KO_USER = 0xffff0006; bool running = false; srsran::dyn_blocking_queue rx_pdu_queue; diff --git a/srsenb/hdr/stack/rrc/rrc_bearer_cfg.h b/srsenb/hdr/stack/rrc/rrc_bearer_cfg.h index 97de4b755..87644636c 100644 --- a/srsenb/hdr/stack/rrc/rrc_bearer_cfg.h +++ b/srsenb/hdr/stack/rrc/rrc_bearer_cfg.h @@ -100,13 +100,13 @@ public: const asn1::unbounded_octstring* nas_pdu); // Methods to apply bearer updates - void add_gtpu_bearer(uint32_t erab_id); - uint32_t add_gtpu_bearer(uint32_t erab_id, - uint32_t teid_out, - uint32_t addr, - const gtpu_interface_rrc::bearer_props* props = nullptr); - void rem_gtpu_bearer(uint32_t erab_id); - void fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_s* msg); + void add_gtpu_bearer(uint32_t erab_id); + srsran::expected add_gtpu_bearer(uint32_t erab_id, + uint32_t teid_out, + uint32_t addr, + const gtpu_interface_rrc::bearer_props* props = nullptr); + void rem_gtpu_bearer(uint32_t erab_id); + void fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_s* msg); const std::map& get_erabs() const { return erabs; } const asn1::rrc::drb_to_add_mod_list_l& get_established_drbs() const { return current_drbs; } diff --git a/srsenb/hdr/stack/rrc/rrc_config.h b/srsenb/hdr/stack/rrc/rrc_config.h index 40f1b3a9f..278400281 100644 --- a/srsenb/hdr/stack/rrc/rrc_config.h +++ b/srsenb/hdr/stack/rrc/rrc_config.h @@ -68,6 +68,7 @@ struct rrc_cfg_t { srsran_cell_t cell; cell_list_t cell_list; cell_list_t cell_list_nr; + uint32_t max_mac_dl_kos; }; constexpr uint32_t UE_PCELL_CC_IDX = 0; diff --git a/srsenb/hdr/stack/rrc/rrc_mobility.h b/srsenb/hdr/stack/rrc/rrc_mobility.h index 3d5900d7d..7f87626dc 100644 --- a/srsenb/hdr/stack/rrc/rrc_mobility.h +++ b/srsenb/hdr/stack/rrc/rrc_mobility.h @@ -24,7 +24,7 @@ #include "rrc.h" #include "rrc_ue.h" -#include "srsran/common/fsm.h" +#include "srsran/adt/fsm.h" #include namespace srsenb { diff --git a/srsenb/hdr/stack/rrc/rrc_ue.h b/srsenb/hdr/stack/rrc/rrc_ue.h index 68166d1bd..0153d0985 100644 --- a/srsenb/hdr/stack/rrc/rrc_ue.h +++ b/srsenb/hdr/stack/rrc/rrc_ue.h @@ -24,7 +24,7 @@ #include "mac_controller.h" #include "rrc.h" -#include "srsran/adt/mem_pool.h" +#include "srsran/adt/pool/mem_pool.h" #include "srsran/interfaces/enb_phy_interfaces.h" #include "srsran/interfaces/pdcp_interface_types.h" @@ -42,15 +42,18 @@ public: bool is_idle(); typedef enum { - MSG3_RX_TIMEOUT = 0, ///< Msg3 has its own timeout to quickly remove fake UEs from random PRACHs - UE_INACTIVITY_TIMEOUT, ///< UE inactivity timeout (usually bigger than reestablishment timeout) - UE_REESTABLISH_TIMEOUT, ///< Maximum timeout in which UE reestablishment is expected + MSG3_RX_TIMEOUT = 0, ///< Msg3 has its own timeout to quickly remove fake UEs from random PRACHs + UE_INACTIVITY_TIMEOUT, ///< UE inactivity timeout (usually bigger than reestablishment timeout) nulltype } activity_timeout_type_t; + std::string to_string(const activity_timeout_type_t& type); void set_activity_timeout(const activity_timeout_type_t type); + void set_rlf_timeout(); void set_activity(); - void activity_timer_expired(); + void mac_ko_activity(); + void activity_timer_expired(const activity_timeout_type_t type); + void rlf_timer_expired(); void max_retx_reached(); rrc_state_t get_state(); @@ -59,11 +62,22 @@ public: ///< Helper to access a cell cfg based on ue_cc_idx enb_cell_common* get_ue_cc_cfg(uint32_t ue_cc_idx); + /// List of results a RRC procedure may produce. + enum class procedure_result_code { + none, + activity_timeout, + error_mme_not_connected, + error_unknown_rnti, + radio_conn_with_ue_lost, + msg3_timeout, + unspecified + }; + void send_connection_setup(); void send_connection_reest(uint8_t ncc); - void send_connection_reject(); + void send_connection_reject(procedure_result_code cause); void send_connection_release(); - void send_connection_reest_rej(); + void send_connection_reest_rej(procedure_result_code cause); void send_connection_reconf(srsran::unique_byte_buffer_t sdu = {}, bool phy_cfg_updated = true, const asn1::unbounded_octstring* nas_pdu = nullptr); @@ -73,11 +87,20 @@ public: void parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu); - /// List of results for a connection request. - enum class conn_request_result_t { success, error_mme_not_connected, error_unknown_rnti }; - - /// Possible causes for the RRC to transition to the idle state. - enum class rrc_idle_transition_cause { release, timeout }; + /// List of generated RRC events. + enum class rrc_event_type { + con_request, + con_setup, + con_setup_complete, + con_reconf, + con_reconf_complete, + con_reest_req, + con_reest, + con_reest_complete, + con_reest_reject, + con_reject, + con_release + }; void handle_rrc_con_req(asn1::rrc::rrc_conn_request_s* msg); void handle_rrc_con_setup_complete(asn1::rrc::rrc_conn_setup_complete_s* msg, srsran::unique_byte_buffer_t pdu); @@ -110,9 +133,19 @@ public: bool is_allocated() const; bool is_crnti_set() const { return mac_ctrl.is_crnti_set(); } - void send_dl_ccch(asn1::rrc::dl_ccch_msg_s* dl_ccch_msg); + /** + * Sends the CCCH message to the underlying layer and optionally encodes it as an octet string if a valid string + * pointer is passed. + */ + void send_dl_ccch(asn1::rrc::dl_ccch_msg_s* dl_ccch_msg, std::string* octet_str = nullptr); + + /** + * Sends the DCCH message to the underlying layer and optionally encodes it as an octet string if a valid string + * pointer is passed. + */ bool send_dl_dcch(const asn1::rrc::dl_dcch_msg_s* dl_dcch_msg, - srsran::unique_byte_buffer_t pdu = srsran::unique_byte_buffer_t()); + srsran::unique_byte_buffer_t pdu = srsran::unique_byte_buffer_t(), + std::string* octet_str = nullptr); void save_ul_message(srsran::unique_byte_buffer_t pdu) { last_ul_msg = std::move(pdu); } @@ -135,6 +168,7 @@ public: private: // args srsran::timer_handler::unique_timer activity_timer; + srsran::timer_handler::unique_timer rlf_timer; /// cached ASN1 fields for RRC config update checking, and ease of context transfer during HO ue_var_cfg_t current_ue_cfg; @@ -161,13 +195,19 @@ private: const static uint32_t UE_PCELL_CC_IDX = 0; + uint32_t consecutive_kos = 0; + uint32_t max_mac_dl_retx; + ue_cell_ded_list ue_cell_list; bearer_cfg_handler bearer_list; security_cfg_handler ue_security_cfg; - /// Cached message of the last uplinl message. + /// Cached message of the last uplink message. srsran::unique_byte_buffer_t last_ul_msg; + /// Connection release result. + procedure_result_code con_release_result = procedure_result_code::none; + // controllers mac_controller mac_ctrl; diff --git a/srsenb/hdr/stack/upper/common_enb.h b/srsenb/hdr/stack/upper/common_enb.h index 0b3db91a6..53a2c77b0 100644 --- a/srsenb/hdr/stack/upper/common_enb.h +++ b/srsenb/hdr/stack/upper/common_enb.h @@ -35,6 +35,7 @@ namespace srsenb { #define SRSENB_N_SRB 3 #define SRSENB_N_DRB 8 #define SRSENB_N_RADIO_BEARERS 11 +#define SRSENB_MAX_UES 64 enum rb_id_t { RB_ID_SRB0 = 0, diff --git a/srsenb/hdr/stack/upper/gtpu.h b/srsenb/hdr/stack/upper/gtpu.h index 0a4acc311..491c97071 100644 --- a/srsenb/hdr/stack/upper/gtpu.h +++ b/srsenb/hdr/stack/upper/gtpu.h @@ -21,9 +21,10 @@ #include #include -#include #include "common_enb.h" +#include "srsran/adt/bounded_vector.h" +#include "srsran/adt/circular_map.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/task_scheduler.h" #include "srsran/common/threads.h" @@ -36,15 +37,108 @@ #ifndef SRSENB_GTPU_H #define SRSENB_GTPU_H +namespace srsran { +struct gtpu_header_t; +} + namespace srsenb { class pdcp_interface_gtpu; class stack_interface_gtpu_lte; +class gtpu_tunnel_manager +{ + // Buffer used to store SDUs while PDCP is still getting configured during handover. + // Note: The buffer cannot be too large, otherwise it risks depleting the byte buffer pool. + const static size_t BUFFER_SIZE = 512; + using buffered_sdu_list = srsran::bounded_vector, BUFFER_SIZE>; + + static const uint32_t undefined_pdcp_sn = std::numeric_limits::max(); + +public: + // A UE should have <= 3 DRBs active, and each DRB should have two tunnels active at the same time at most + const static size_t MAX_TUNNELS_PER_UE = 6; + + enum class tunnel_state { pdcp_active, buffering, forward_to, forwarded_from }; + + struct tunnel { + uint16_t rnti = SRSRAN_INVALID_RNTI; + uint32_t lcid = SRSENB_N_RADIO_BEARERS; + uint32_t teid_in = 0; + uint32_t teid_out = 0; + uint32_t spgw_addr = 0; + + tunnel_state state = tunnel_state::pdcp_active; + srsran::unique_timer rx_timer; + srsran::byte_buffer_pool_ptr buffer; + tunnel* fwd_tunnel = nullptr; ///< forward Rx SDUs to this TEID + srsran::move_callback on_removal; + + tunnel() = default; + tunnel(tunnel&&) noexcept = default; + tunnel& operator=(tunnel&&) noexcept = default; + ~tunnel() + { + if (not on_removal.is_empty()) { + on_removal(); + } + } + }; + + struct lcid_tunnel { + uint32_t lcid; + uint32_t teid; + + bool operator<(const lcid_tunnel& other) const + { + return lcid < other.lcid or (lcid == other.lcid and teid < other.teid); + } + bool operator==(const lcid_tunnel& other) const { return lcid == other.lcid and teid == other.teid; } + }; + using ue_lcid_tunnel_list = srsran::bounded_vector; + + explicit gtpu_tunnel_manager(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger); + void init(pdcp_interface_gtpu* pdcp_); + + bool has_teid(uint32_t teid) const { return tunnels.contains(teid); } + const tunnel* find_tunnel(uint32_t teid); + ue_lcid_tunnel_list* find_rnti_tunnels(uint16_t rnti); + srsran::span find_rnti_lcid_tunnels(uint16_t rnti, uint32_t lcid); + + const tunnel* add_tunnel(uint16_t rnti, uint32_t lcid, uint32_t teidout, uint32_t spgw_addr); + bool update_rnti(uint16_t old_rnti, uint16_t new_rnti); + + void activate_tunnel(uint32_t teid); + void suspend_tunnel(uint32_t teid); + void set_tunnel_priority(uint32_t first_teid, uint32_t second_teid); + void handle_rx_pdcp_sdu(uint32_t teid); + void buffer_pdcp_sdu(uint32_t teid, uint32_t pdcp_sn, srsran::unique_byte_buffer_t sdu); + void setup_forwarding(uint32_t rx_teid, uint32_t tx_teid); + + bool remove_tunnel(uint32_t teid); + bool remove_bearer(uint16_t rnti, uint32_t lcid); + bool remove_rnti(uint16_t rnti); + +private: + using tunnel_list_t = srsran::static_id_obj_pool; + using tunnel_ctxt_it = typename tunnel_list_t::iterator; + + srsran::task_sched_handle task_sched; + pdcp_interface_gtpu* pdcp = nullptr; + srslog::basic_logger& logger; + + tunnel_list_t tunnels; + srsran::static_circular_map ue_teidin_db; +}; + +using gtpu_tunnel_state = gtpu_tunnel_manager::tunnel_state; +using gtpu_tunnel = gtpu_tunnel_manager::tunnel; + class gtpu final : public gtpu_interface_rrc, public gtpu_interface_pdcp { public: explicit gtpu(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger); + ~gtpu(); int init(std::string gtp_bind_addr_, std::string mme_addr_, @@ -56,15 +150,15 @@ public: void stop(); // gtpu_interface_rrc - uint32_t add_bearer(uint16_t rnti, - uint32_t lcid, - uint32_t addr, - uint32_t teid_out, - const bearer_props* props = nullptr) override; - void set_tunnel_status(uint32_t teidin, bool dl_active) override; - void rem_bearer(uint16_t rnti, uint32_t lcid) override; - void mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti) override; - void rem_user(uint16_t rnti) override; + srsran::expected add_bearer(uint16_t rnti, + uint32_t lcid, + uint32_t addr, + uint32_t teid_out, + const bearer_props* props = nullptr) override; + void set_tunnel_status(uint32_t teidin, bool dl_active) override; + void rem_bearer(uint16_t rnti, uint32_t lcid) override; + void mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti) override; + void rem_user(uint16_t rnti) override; // gtpu_interface_pdcp void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) override; @@ -113,23 +207,8 @@ private: }; m1u_handler m1u; - const uint32_t undefined_pdcp_sn = std::numeric_limits::max(); - struct tunnel { - bool dl_enabled = true; - bool fwd_teid_in_present = false; - bool prior_teid_in_present = false; - uint16_t rnti = SRSRAN_INVALID_RNTI; - uint32_t lcid = SRSENB_N_RADIO_BEARERS; - uint32_t teid_in = 0; - uint32_t teid_out = 0; - uint32_t spgw_addr = 0; - uint32_t fwd_teid_in = 0; ///< forward Rx SDUs to this TEID - uint32_t prior_teid_in = 0; ///< buffer bearer SDUs until this TEID receives an End Marker - srsran::unique_timer rx_timer; - std::multimap buffer; - }; - std::unordered_map tunnels; - std::map, SRSENB_N_RADIO_BEARERS> > ue_teidin_db; + static const uint32_t undefined_pdcp_sn = std::numeric_limits::max(); + gtpu_tunnel_manager tunnels; // Tx sequence number for signaling messages uint32_t tx_seq = 0; @@ -137,25 +216,23 @@ private: // Socket file descriptor int fd = -1; - void send_pdu_to_tunnel(tunnel& tx_tun, srsran::unique_byte_buffer_t pdu, int pdcp_sn = -1); + void send_pdu_to_tunnel(const gtpu_tunnel& tx_tun, srsran::unique_byte_buffer_t pdu, int pdcp_sn = -1); void echo_response(in_addr_t addr, in_port_t port, uint16_t seq); void error_indication(in_addr_t addr, in_port_t port, uint32_t err_teid); - bool end_marker(uint32_t teidin); + bool send_end_marker(uint32_t teidin); - void handle_end_marker(tunnel& rx_tunnel); + void handle_end_marker(const gtpu_tunnel& rx_tunnel); + void handle_msg_data_pdu(const srsran::gtpu_header_t& header, + const gtpu_tunnel& rx_tunnel, + srsran::unique_byte_buffer_t pdu); int create_dl_fwd_tunnel(uint32_t rx_teid_in, uint32_t tx_teid_in); /**************************************************************************** * TEID to RNIT/LCID helper functions ***************************************************************************/ - uint32_t next_teid_in = 0; - - tunnel* get_tunnel(uint32_t teidin); - srsran::span get_lcid_teids(uint16_t rnti, uint32_t lcid); - - void log_message(tunnel& tun, bool is_rx, srsran::span pdu, int pdcp_sn = -1); + void log_message(const gtpu_tunnel& tun, bool is_rx, srsran::span pdu, int pdcp_sn = -1); }; } // namespace srsenb diff --git a/srsenb/hdr/stack/upper/s1ap.h b/srsenb/hdr/stack/upper/s1ap.h index 094f0926a..0ff53da01 100644 --- a/srsenb/hdr/stack/upper/s1ap.h +++ b/srsenb/hdr/stack/upper/s1ap.h @@ -33,6 +33,7 @@ #include "srsran/interfaces/enb_s1ap_interfaces.h" #include "s1ap_metrics.h" +#include "srsran/adt/optional.h" #include "srsran/asn1/s1ap.h" #include "srsran/common/network_utils.h" #include "srsran/common/stack_procedure.h" @@ -47,12 +48,11 @@ class rrc_interface_s1ap; struct ue_ctxt_t { static const uint32_t invalid_enb_id = std::numeric_limits::max(); - bool mme_ue_s1ap_id_present = false; - uint16_t rnti = SRSRAN_INVALID_RNTI; - uint32_t enb_ue_s1ap_id = invalid_enb_id; - uint32_t mme_ue_s1ap_id = 0; - uint32_t enb_cc_idx = 0; - struct timeval init_timestamp = {}; + uint16_t rnti = SRSRAN_INVALID_RNTI; + uint32_t enb_ue_s1ap_id = invalid_enb_id; + srsran::optional mme_ue_s1ap_id; + uint32_t enb_cc_idx = 0; + struct timeval init_timestamp = {}; }; class s1ap : public s1ap_interface_rrc @@ -99,7 +99,9 @@ public: void send_ho_notify(uint16_t rnti, uint64_t target_eci) override; void send_ho_cancel(uint16_t rnti) override; bool release_erabs(uint16_t rnti, const std::vector& erabs_successfully_released) override; - bool send_error_indication(uint16_t rnti, const asn1::s1ap::cause_c& cause); + bool send_error_indication(const asn1::s1ap::cause_c& cause, + srsran::optional enb_ue_s1ap_id = {}, + srsran::optional mme_ue_s1ap_id = {}); bool send_ue_cap_info_indication(uint16_t rnti, srsran::unique_byte_buffer_t ue_radio_cap) override; // Stack interface @@ -320,7 +322,7 @@ private: s1ap* s1ap_ptr = nullptr; }; - ue* find_s1apmsg_user(uint32_t enb_id, uint32_t mme_id); + ue* handle_s1apmsg_ue_id(uint32_t enb_id, uint32_t mme_id); std::string get_cause(const asn1::s1ap::cause_c& c); void log_s1ap_msg(const asn1::s1ap::s1ap_pdu_c& msg, srsran::const_span sdu, bool is_rx); diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index 84c56a8d5..040e246d4 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -1144,6 +1144,9 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_ // RRC needs eNB id for SIB1 packing rrc_cfg_->enb_id = args_->stack.s1ap.enb_id; + // Set max number of KOs + rrc_cfg_->max_mac_dl_kos = args_->general.max_mac_dl_kos; + // Set sync queue capacity to 1 for ZMQ if (args_->rf.device_name == "zmq") { srslog::fetch_basic_logger("ENB").info("Using sync queue size of one for ZMQ based radio."); diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index 36d51722c..f66d6cedf 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -223,6 +223,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("expert.eea_pref_list", bpo::value(&args->general.eea_pref_list)->default_value("EEA0, EEA2, EEA1"), "Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1).") ("expert.eia_pref_list", bpo::value(&args->general.eia_pref_list)->default_value("EIA2, EIA1, EIA0"), "Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0).") ("expert.max_nof_ues", bpo::value(&args->stack.mac.max_nof_ues)->default_value(64), "Maximum number of connected UEs") + ("expert.max_mac_dl_kos", bpo::value(&args->general.max_mac_dl_kos)->default_value(100), "Maximum number of consecutive KOs before triggering the UE's release") // eMBMS section ("embms.enable", bpo::value(&args->stack.embms.enable)->default_value(false), "Enables MBMS in the eNB") diff --git a/srsenb/src/stack/mac/mac.cc b/srsenb/src/stack/mac/mac.cc index 39eaae952..0abc4fb64 100644 --- a/srsenb/src/stack/mac/mac.cc +++ b/srsenb/src/stack/mac/mac.cc @@ -98,6 +98,8 @@ void mac::stop() { srsran::rwlock_write_guard lock(rwlock); if (started) { + started = false; + ue_db.clear(); for (auto& cc : common_buffers) { for (int i = 0; i < NOF_BCCH_DLSCH_MSG; i++) { @@ -105,8 +107,8 @@ void mac::stop() } srsran_softbuffer_tx_free(&cc.pcch_softbuffer_tx); srsran_softbuffer_tx_free(&cc.rar_softbuffer_tx); - started = false; } + ue_pool.stop(); } } @@ -300,9 +302,11 @@ int mac::ack_info(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, uint32_t if (ack) { if (nof_bytes > 64) { // do not count RLC status messages only - rrc_h->set_activity_user(rnti); + rrc_h->set_activity_user(rnti, true); logger.info("DL activity rnti=0x%x, n_bytes=%d", rnti, nof_bytes); } + } else { + rrc_h->set_activity_user(rnti, false); } return SRSRAN_SUCCESS; } @@ -463,6 +467,10 @@ uint16_t mac::allocate_ue() // Add UE to map { srsran::rwlock_write_guard lock(rwlock); + if (not started) { + logger.info("RACH ignored as eNB is being shutdown"); + return SRSRAN_INVALID_RNTI; + } if (ue_db.size() >= args.max_nof_ues) { logger.warning("Maximum number of connected UEs %zd connected to the eNB. Ignoring PRACH", max_ues); return SRSRAN_INVALID_RNTI; @@ -833,9 +841,9 @@ int mac::get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res int requested_bytes = (mcs_data.tbs / 8 > (int)mch.mtch_sched[mtch_index].lcid_buffer_size) ? (mch.mtch_sched[mtch_index].lcid_buffer_size) : ((mcs_data.tbs / 8) - 2); - int bytes_received = ue_db[SRSRAN_MRNTI]->read_pdu(current_lcid, mtch_payload_buffer, requested_bytes); - mch.pdu[0].lcid = current_lcid; - mch.pdu[0].nbytes = bytes_received; + int bytes_received = ue_db[SRSRAN_MRNTI]->read_pdu(current_lcid, mtch_payload_buffer, requested_bytes); + mch.pdu[0].lcid = current_lcid; + mch.pdu[0].nbytes = bytes_received; mch.mtch_sched[0].mtch_payload = mtch_payload_buffer; dl_sched_res->pdsch[0].dci.rnti = SRSRAN_MRNTI; if (bytes_received) { diff --git a/srsenb/src/stack/mac/sched.cc b/srsenb/src/stack/mac/sched.cc index 215dd65dc..ca29df352 100644 --- a/srsenb/src/stack/mac/sched.cc +++ b/srsenb/src/stack/mac/sched.cc @@ -306,7 +306,7 @@ int sched::dl_sched(uint32_t tti_tx_dl, uint32_t enb_cc_idx, sched_interface::dl return 0; } - tti_point tti_rx = tti_point{tti_tx_dl} - FDD_HARQ_DELAY_UL_MS; + tti_point tti_rx = tti_point{tti_tx_dl} - TX_ENB_DELAY; new_tti(tti_rx); // copy result @@ -327,7 +327,7 @@ int sched::ul_sched(uint32_t tti, uint32_t enb_cc_idx, srsenb::sched_interface:: } // Compute scheduling Result for tti_rx - tti_point tti_rx = tti_point{tti} - FDD_HARQ_DELAY_UL_MS - FDD_HARQ_DELAY_DL_MS; + tti_point tti_rx = tti_point{tti} - TX_ENB_DELAY - FDD_HARQ_DELAY_DL_MS; new_tti(tti_rx); // copy result diff --git a/srsenb/src/stack/mac/sched_grid.cc b/srsenb/src/stack/mac/sched_grid.cc index 684960c8a..b56e418f2 100644 --- a/srsenb/src/stack/mac/sched_grid.cc +++ b/srsenb/src/stack/mac/sched_grid.cc @@ -695,7 +695,8 @@ void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t& // Print Resulting DL Allocation fmt::memory_buffer str_buffer; fmt::format_to(str_buffer, - "SCHED: DL {} rnti=0x{:x}, cc={}, pid={}, mask=0x{:x}, dci=({}, {}), n_rtx={}, tbs={}, buffer={}/{}", + "SCHED: DL {} rnti=0x{:x}, cc={}, pid={}, mask=0x{:x}, dci=({}, {}), n_rtx={}, tbs={}, " + "buffer={}/{}, tti_tx_dl={}", is_newtx ? "tx" : "retx", user->get_rnti(), cc_cfg->enb_cc_idx, @@ -706,7 +707,8 @@ void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t& dl_harq.nof_retx(0) + dl_harq.nof_retx(1), tbs, data_before, - user->get_requested_dl_bytes(cc_cfg->enb_cc_idx).stop()); + user->get_requested_dl_bytes(cc_cfg->enb_cc_idx).stop(), + get_tti_tx_dl()); logger.info("%s", srsran::to_c_str(str_buffer)); } } diff --git a/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc b/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc index 2e8a4d911..a5ba9e1b7 100644 --- a/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc +++ b/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc @@ -54,11 +54,6 @@ void harq_proc::reset(uint32_t tb_idx) tx_cnt[tb_idx] = 0; } -uint32_t harq_proc::get_id() const -{ - return id; -} - bool harq_proc::is_empty() const { for (uint32_t i = 0; i < SRSRAN_MAX_TB; ++i) { @@ -92,14 +87,7 @@ int harq_proc::set_ack_common(uint32_t tb_idx, bool ack_) } ack_state[tb_idx] = ack_; logger->debug("ACK=%d received pid=%d, tb_idx=%d, n_rtx=%d, max_retx=%d", ack_, id, tb_idx, n_rtx[tb_idx], max_retx); - if (!ack_ && (n_rtx[tb_idx] + 1 >= max_retx)) { - logger->info("SCHED: discarding TB=%d pid=%d, tti=%d, maximum number of retx exceeded (%d)", - tb_idx, - id, - tti.to_uint(), - max_retx); - active[tb_idx] = false; - } else if (ack_) { + if (ack_) { active[tb_idx] = false; } return SRSRAN_SUCCESS; @@ -170,6 +158,20 @@ dl_harq_proc::dl_harq_proc() : harq_proc() n_cce = 0; } +void dl_harq_proc::new_tti(tti_point tti_tx_dl) +{ + for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; ++tb) { + if (has_pending_retx(tb, tti_tx_dl) and nof_retx(tb) + 1 >= max_nof_retx()) { + logger->info("SCHED: discarding DL TB=%d pid=%d, tti=%d, maximum number of retx exceeded (%d)", + tb, + get_id(), + tti.to_uint(), + max_retx); + active[tb] = false; + } + } +} + void dl_harq_proc::new_tx(const rbgmask_t& new_mask, uint32_t tb_idx, tti_point tti_tx_dl, @@ -231,9 +233,18 @@ void dl_harq_proc::reset_pending_data() } /****************************************************** - * UE::UL HARQ class * + * UE::UL HARQ class * ******************************************************/ +void ul_harq_proc::new_tti() +{ + if (has_pending_retx() and nof_retx(0) + 1 >= max_nof_retx()) { + logger->info( + "SCHED: discarding UL pid=%d, tti=%d, maximum number of retx exceeded (%d)", get_id(), tti.to_uint(), max_retx); + active[0] = false; + } +} + prb_interval ul_harq_proc::get_alloc() const { return allocation; @@ -338,6 +349,12 @@ void harq_entity::reset() void harq_entity::new_tti(tti_point tti_rx) { last_ttis[tti_rx.to_uint() % last_ttis.size()] = tti_rx; + for (auto& hul : ul_harqs) { + hul.new_tti(); + } + for (auto& hdl : dl_harqs) { + hdl.new_tti(to_tx_dl(tti_rx)); + } } dl_harq_proc* harq_entity::get_empty_dl_harq(tti_point tti_tx_dl) diff --git a/srsenb/src/stack/mac/ue.cc b/srsenb/src/stack/mac/ue.cc index e636a2048..b757b892e 100644 --- a/srsenb/src/stack/mac/ue.cc +++ b/srsenb/src/stack/mac/ue.cc @@ -382,7 +382,7 @@ void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channe // Indicate RRC about successful activity if valid RLC message is received if (mac_msg_ul.get()->get_payload_size() > 64) { // do not count RLC status messages only - rrc->set_activity_user(rnti); + rrc->set_activity_user(rnti, true); logger.debug("UL activity rnti=0x%x, n_bytes=%d", rnti, nof_bytes); } @@ -432,10 +432,8 @@ void ue::deallocate_pdu(uint32_t tti, uint32_t ue_cc_idx) { std::unique_lock lock(rx_buffers_mutex); if (not cc_buffers[ue_cc_idx].get_rx_used_buffers().try_deallocate_pdu(tti_point(tti))) { - logger.warning("UE buffers: Null RX PDU pointer in deallocate_pdu for rnti=0x%x tti=%d cc_idx=%d", - rnti, - tti, - ue_cc_idx); + logger.warning( + "UE buffers: Null RX PDU pointer in deallocate_pdu for rnti=0x%x tti=%d cc_idx=%d", rnti, tti, ue_cc_idx); } } @@ -443,8 +441,7 @@ void ue::push_pdu(uint32_t tti, uint32_t ue_cc_idx, uint32_t len) { std::unique_lock lock(rx_buffers_mutex); if (not cc_buffers[ue_cc_idx].get_rx_used_buffers().push_pdu(tti_point(tti), len)) { - logger.warning( - "UE buffers: Failed to push RX PDU for rnti=0x%x tti=%d cc_idx=%d", rnti, tti, ue_cc_idx); + logger.warning("UE buffers: Failed to push RX PDU for rnti=0x%x tti=%d cc_idx=%d", rnti, tti, ue_cc_idx); } } diff --git a/srsenb/src/stack/rrc/rrc.cc b/srsenb/src/stack/rrc/rrc.cc index f8de1df55..c504a62a6 100644 --- a/srsenb/src/stack/rrc/rrc.cc +++ b/srsenb/src/stack/rrc/rrc.cc @@ -94,6 +94,7 @@ int32_t rrc::init(const rrc_cfg_t& cfg_, "re-establishment procedure."); } logger.info("Inactivity timeout: %d ms", cfg.inactivity_timeout_ms); + logger.info("Max consecutive MAC KOs: %d", cfg.max_mac_dl_kos); running = true; @@ -140,9 +141,15 @@ uint8_t* rrc::read_pdu_bcch_dlsch(const uint8_t cc_idx, const uint32_t sib_index return nullptr; } -void rrc::set_activity_user(uint16_t rnti) +void rrc::set_activity_user(uint16_t rnti, bool ack_info) { - rrc_pdu p = {rnti, LCID_ACT_USER, nullptr}; + rrc_pdu p; + if (ack_info) { + p = {rnti, LCID_ACT_USER, nullptr}; + } else { + p = {rnti, LCID_MAC_KO_USER, nullptr}; + } + if (not rx_pdu_queue.try_push(std::move(p))) { logger.error("Failed to push UE activity command to RRC queue"); } @@ -191,7 +198,6 @@ int rrc::add_user(uint16_t rnti, const sched_interface::ue_cfg_t& sched_ue_cfg) } if (rnti == SRSRAN_MRNTI) { - uint32_t teid_in = 1; for (auto& mbms_item : mcch.msg.c1().mbsfn_area_cfg_r9().pmch_info_list_r9[0].mbms_session_info_list_r9) { uint32_t lcid = mbms_item.lc_ch_id_r9; @@ -199,7 +205,7 @@ int rrc::add_user(uint16_t rnti, const sched_interface::ue_cfg_t& sched_ue_cfg) mac->ue_cfg(SRSRAN_MRNTI, NULL); rlc->add_bearer_mrb(SRSRAN_MRNTI, lcid); pdcp->add_bearer(SRSRAN_MRNTI, lcid, srsran::make_drb_pdcp_config_t(1, false)); - teid_in = gtpu->add_bearer(SRSRAN_MRNTI, lcid, 1, 1); + gtpu->add_bearer(SRSRAN_MRNTI, lcid, 1, 1); } } return SRSRAN_SUCCESS; @@ -297,7 +303,7 @@ void rrc::write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) } } -void rrc::release_complete(uint16_t rnti) +void rrc::release_ue(uint16_t rnti) { rrc_pdu p = {rnti, LCID_REL_USER, nullptr}; if (not rx_pdu_queue.try_push(std::move(p))) { @@ -1029,6 +1035,9 @@ void rrc::tti_clock() case LCID_ACT_USER: user_it->second->set_activity(); break; + case LCID_MAC_KO_USER: + user_it->second->mac_ko_activity(); + break; case LCID_RTX_USER: user_it->second->max_retx_reached(); break; diff --git a/srsenb/src/stack/rrc/rrc_bearer_cfg.cc b/srsenb/src/stack/rrc/rrc_bearer_cfg.cc index 0b529ed32..bfb668d50 100644 --- a/srsenb/src/stack/rrc/rrc_bearer_cfg.cc +++ b/srsenb/src/stack/rrc/rrc_bearer_cfg.cc @@ -318,32 +318,41 @@ bool bearer_cfg_handler::modify_erab(uint8_t void bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id) { auto it = erabs.find(erab_id); - if (it == erabs.end()) { - logger->error("Adding erab_id=%d to GTPU", erab_id); - return; + if (it != erabs.end()) { + srsran::expected teidin = + add_gtpu_bearer(erab_id, it->second.teid_out, it->second.address.to_number(), nullptr); + if (teidin.has_value()) { + it->second.teid_in = teidin.value(); + return; + } } - it->second.teid_in = add_gtpu_bearer(erab_id, it->second.teid_out, it->second.address.to_number(), nullptr); + logger->error("Adding erab_id=%d to GTPU", erab_id); } -uint32_t bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id, - uint32_t teid_out, - uint32_t addr, - const gtpu_interface_rrc::bearer_props* props) +srsran::expected bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id, + uint32_t teid_out, + uint32_t addr, + const gtpu_interface_rrc::bearer_props* props) { auto it = erabs.find(erab_id); if (it == erabs.end()) { logger->error("Adding erab_id=%d to GTPU", erab_id); - return 0; + return srsran::default_error_t(); } // Initialize ERAB tunnel in GTPU right-away. DRBs are only created during RRC setup/reconf erab_t& erab = it->second; erab_t::gtpu_tunnel bearer; - bearer.teid_out = teid_out; - bearer.addr = addr; - bearer.teid_in = gtpu->add_bearer(rnti, erab.id - 2, addr, teid_out, props); + bearer.teid_out = teid_out; + bearer.addr = addr; + srsran::expected teidin = gtpu->add_bearer(rnti, erab.id - 2, addr, teid_out, props); + if (teidin.is_error()) { + logger->error("Adding erab_id=%d to GTPU", erab_id); + return srsran::default_error_t(); + } + bearer.teid_in = teidin.value(); erab.tunnels.push_back(bearer); - return bearer.teid_in; + return teidin; } void bearer_cfg_handler::rem_gtpu_bearer(uint32_t erab_id) diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index c20620084..a47b225f9 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -342,7 +342,7 @@ bool rrc::ue::rrc_mobility::start_ho_preparation(uint32_t target_eci, logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); return false; } - asn1::bit_ref bref(buffer->msg, buffer->get_tailroom()); + asn1::bit_ref bref(buffer->msg, buffer->get_tailroom()); if (rrc_ue->eutra_capabilities.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { logger.error("Failed to pack UE EUTRA Capability"); return false; @@ -385,7 +385,7 @@ bool rrc::ue::rrc_mobility::start_ho_preparation(uint32_t target_eci, logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); return false; } - asn1::bit_ref bref(buffer->msg, buffer->get_tailroom()); + asn1::bit_ref bref(buffer->msg, buffer->get_tailroom()); if (hoprep.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { Error("Failed to pack HO preparation msg"); return false; @@ -791,12 +791,17 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& fwd_erab.dl_forwarding.value == asn1::s1ap::dl_forwarding_opts::dl_forwarding_proposed) { admitted_erab.dl_g_tp_teid_present = true; gtpu_interface_rrc::bearer_props props; - props.flush_before_teidin_present = true; - props.flush_before_teidin = erab.second.teid_in; - uint32_t dl_teid_in = rrc_ue->bearer_list.add_gtpu_bearer( + props.flush_before_teidin_present = true; + props.flush_before_teidin = erab.second.teid_in; + srsran::expected dl_teid_in = rrc_ue->bearer_list.add_gtpu_bearer( erab.second.id, erab.second.teid_out, erab.second.address.to_number(), &props); - fwd_tunnels.push_back(dl_teid_in); - srsran::uint32_to_uint8(dl_teid_in, admitted_erabs.back().dl_g_tp_teid.data()); + if (not dl_teid_in.has_value()) { + logger.error("Failed to allocate GTPU TEID"); + trigger(srsran::failure_ev{}); + return; + } + fwd_tunnels.push_back(dl_teid_in.value()); + srsran::uint32_to_uint8(dl_teid_in.value(), admitted_erabs.back().dl_g_tp_teid.data()); } } } diff --git a/srsenb/src/stack/rrc/rrc_ue.cc b/srsenb/src/stack/rrc/rrc_ue.cc index b39976701..516a08a81 100644 --- a/srsenb/src/stack/rrc/rrc_ue.cc +++ b/srsenb/src/stack/rrc/rrc_ue.cc @@ -23,7 +23,7 @@ #include "srsenb/hdr/stack/rrc/mac_controller.h" #include "srsenb/hdr/stack/rrc/rrc_mobility.h" #include "srsenb/hdr/stack/rrc/ue_rr_cfg.h" -#include "srsran/adt/mem_pool.h" +#include "srsran/adt/pool/mem_pool.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/common/enb_events.h" #include "srsran/common/int_helpers.h" @@ -65,8 +65,10 @@ int rrc::ue::init() // Configure apply_setup_phy_common(parent->cfg.sibs[1].sib2().rr_cfg_common, true); + rlf_timer = parent->task_sched.get_unique_timer(); activity_timer = parent->task_sched.get_unique_timer(); set_activity_timeout(MSG3_RX_TIMEOUT); // next UE response is Msg3 + set_rlf_timeout(); mobility_handler.reset(new rrc_mobility(this)); return SRSRAN_SUCCESS; @@ -112,22 +114,50 @@ void rrc::ue::set_activity() { // re-start activity timer with current timeout value activity_timer.run(); - + rlf_timer.stop(); + consecutive_kos = 0; if (parent) { parent->logger.debug("Activity registered for rnti=0x%x (timeout_value=%dms)", rnti, activity_timer.duration()); } } -void rrc::ue::activity_timer_expired() +void rrc::ue::mac_ko_activity() { + // Count KOs in MAC and trigger release if it goes above a certain value. + // This is done to detect out-of-coverage UEs + if (rlf_timer.is_running()) { + // RLF timer already running, no need to count KOs + return; + } + + consecutive_kos++; + if (consecutive_kos > parent->cfg.max_mac_dl_kos) { + parent->logger.info("Max KOs reached, triggering release rnti=0x%x", rnti); + max_retx_reached(); + } +} + +void rrc::ue::activity_timer_expired(const activity_timeout_type_t type) +{ + rlf_timer.stop(); if (parent) { parent->logger.info("Activity timer for rnti=0x%x expired after %d ms", rnti, activity_timer.time_elapsed()); if (parent->s1ap->user_exists(rnti)) { - parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::user_inactivity); - event_logger::get().log_rrc_disconnect(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, - static_cast(rrc_idle_transition_cause::timeout), - rnti); + if (type == UE_INACTIVITY_TIMEOUT) { + parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::user_inactivity); + con_release_result = procedure_result_code::activity_timeout; + } else if (type == MSG3_RX_TIMEOUT) { + // MSG3 timeout, no need to notify S1AP, just remove UE + parent->rem_user_thread(rnti); + con_release_result = procedure_result_code::msg3_timeout; + } else { + // Unhandled activity timeout, just remove UE and log an error + parent->rem_user_thread(rnti); + con_release_result = procedure_result_code::activity_timeout; + parent->logger.error( + "Unhandled reason for activity timer expiration. rnti=0x%x, cause %d", rnti, static_cast(type)); + } } else { if (rnti != SRSRAN_MRNTI) { parent->rem_user_thread(rnti); @@ -138,18 +168,53 @@ void rrc::ue::activity_timer_expired() state = RRC_STATE_RELEASE_REQUEST; } +void rrc::ue::rlf_timer_expired() +{ + activity_timer.stop(); + if (parent) { + parent->logger.info("RLF timer for rnti=0x%x expired after %d ms", rnti, rlf_timer.time_elapsed()); + + if (parent->s1ap->user_exists(rnti)) { + parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::radio_conn_with_ue_lost); + con_release_result = procedure_result_code::radio_conn_with_ue_lost; + } else { + if (rnti != SRSRAN_MRNTI) { + parent->rem_user(rnti); + } + } + } + + state = RRC_STATE_RELEASE_REQUEST; +} + void rrc::ue::max_retx_reached() { if (parent) { parent->logger.info("Max retx reached for rnti=0x%x", rnti); // Give UE time to start re-establishment - set_activity_timeout(UE_REESTABLISH_TIMEOUT); + rlf_timer.run(); mac_ctrl.handle_max_retx(); } } +void rrc::ue::set_rlf_timeout() +{ + uint32_t deadline_s = 0; + uint32_t deadline_ms = 0; + + deadline_ms = static_cast((get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t310.to_number()) + + (get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t311.to_number()) + + (get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.n310.to_number())); + deadline_s = deadline_ms / 1000; + deadline_ms = deadline_ms % 1000; + + uint32_t deadline = deadline_s * 1e3 + deadline_ms; + rlf_timer.set(deadline, [this](uint32_t tid) { rlf_timer_expired(); }); + parent->logger.debug("Setting RLF timer for rnti=0x%x to %dms", rnti, deadline); +} + void rrc::ue::set_activity_timeout(const activity_timeout_type_t type) { uint32_t deadline_s = 0; @@ -165,19 +230,12 @@ void rrc::ue::set_activity_timeout(const activity_timeout_type_t type) deadline_s = parent->cfg.inactivity_timeout_ms / 1000; deadline_ms = parent->cfg.inactivity_timeout_ms % 1000; break; - case UE_REESTABLISH_TIMEOUT: - deadline_ms = static_cast((get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t310.to_number()) + - (get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t311.to_number()) + - (get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.n310.to_number())); - deadline_s = deadline_ms / 1000; - deadline_ms = deadline_ms % 1000; - break; default: parent->logger.error("Unknown timeout type %d", type); } uint32_t deadline = deadline_s * 1e3 + deadline_ms; - activity_timer.set(deadline, [this](uint32_t tid) { activity_timer_expired(); }); + activity_timer.set(deadline, [this, type](uint32_t tid) { activity_timer_expired(type); }); parent->logger.debug("Setting timer for %s for rnti=0x%x to %dms", to_string(type).c_str(), rnti, deadline); set_activity(); @@ -219,9 +277,11 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) switch (ul_dcch_msg.msg.c1().type()) { case ul_dcch_msg_type_c::c1_c_::types::rrc_conn_setup_complete: + save_ul_message(std::move(original_pdu)); handle_rrc_con_setup_complete(&ul_dcch_msg.msg.c1().rrc_conn_setup_complete(), std::move(pdu)); break; case ul_dcch_msg_type_c::c1_c_::types::rrc_conn_reest_complete: + save_ul_message(std::move(original_pdu)); handle_rrc_con_reest_complete(&ul_dcch_msg.msg.c1().rrc_conn_reest_complete(), std::move(pdu)); break; case ul_dcch_msg_type_c::c1_c_::types::ul_info_transfer: @@ -242,6 +302,7 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) parent->s1ap->write_pdu(rnti, std::move(pdu)); break; case ul_dcch_msg_type_c::c1_c_::types::rrc_conn_recfg_complete: + save_ul_message(std::move(original_pdu)); handle_rrc_reconf_complete(&ul_dcch_msg.msg.c1().rrc_conn_recfg_complete(), std::move(pdu)); srsran::console("User 0x%x connected\n", rnti); state = RRC_STATE_REGISTERED; @@ -261,7 +322,7 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) send_connection_reconf(std::move(pdu)); state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; } else { - send_connection_reject(); + send_connection_reject(procedure_result_code::none); state = RRC_STATE_IDLE; } break; @@ -292,13 +353,16 @@ std::string rrc::ue::to_string(const activity_timeout_type_t& type) */ void rrc::ue::handle_rrc_con_req(rrc_conn_request_s* msg) { + // Log event. + event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, + asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes), + static_cast(rrc_event_type::con_request), + static_cast(procedure_result_code::none), + rnti); + if (not parent->s1ap->is_mme_connected()) { parent->logger.error("MME isn't connected. Sending Connection Reject"); - event_logger::get().log_rrc_connected(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, - asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes), - static_cast(conn_request_result_t::error_mme_not_connected), - rnti); - send_connection_reject(); + send_connection_reject(procedure_result_code::error_mme_not_connected); return; } @@ -352,13 +416,28 @@ void rrc::ue::send_connection_setup() // Configure PHY layer apply_setup_phy_config_dedicated(rr_cfg.phys_cfg_ded); // It assumes SCell has not been set before - send_dl_ccch(&dl_ccch_msg); + std::string octet_str; + send_dl_ccch(&dl_ccch_msg, &octet_str); + + // Log event. + event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, + octet_str, + static_cast(rrc_event_type::con_setup), + static_cast(procedure_result_code::none), + rnti); apply_rr_cfg_ded_diff(current_ue_cfg.rr_cfg, rr_cfg); } void rrc::ue::handle_rrc_con_setup_complete(rrc_conn_setup_complete_s* msg, srsran::unique_byte_buffer_t pdu) { + // Log event. + event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, + asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes), + static_cast(rrc_event_type::con_setup_complete), + static_cast(procedure_result_code::none), + rnti); + // Inform PHY about the configuration completion parent->phy->complete_config(rnti); @@ -385,12 +464,6 @@ void rrc::ue::handle_rrc_con_setup_complete(rrc_conn_setup_complete_s* msg, srsr } state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; - // Log event. - event_logger::get().log_rrc_connected(enb_cc_idx, - asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes), - static_cast(conn_request_result_t::success), - rnti); - // 2> if the UE has radio link failure or handover failure information available if (msg->crit_exts.type().value == c1_or_crit_ext_opts::c1 and msg->crit_exts.c1().type().value == @@ -402,13 +475,22 @@ void rrc::ue::handle_rrc_con_setup_complete(rrc_conn_setup_complete_s* msg, srsr } } -void rrc::ue::send_connection_reject() +void rrc::ue::send_connection_reject(procedure_result_code cause) { mac_ctrl.handle_con_reject(); dl_ccch_msg_s dl_ccch_msg; dl_ccch_msg.msg.set_c1().set_rrc_conn_reject().crit_exts.set_c1().set_rrc_conn_reject_r8().wait_time = 10; - send_dl_ccch(&dl_ccch_msg); + + std::string octet_str; + send_dl_ccch(&dl_ccch_msg, &octet_str); + + // Log event. + event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, + octet_str, + static_cast(rrc_event_type::con_reject), + static_cast(cause), + rnti); } /* @@ -416,13 +498,16 @@ void rrc::ue::send_connection_reject() */ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg) { + // Log event. + event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, + asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes), + static_cast(rrc_event_type::con_reest_req), + static_cast(procedure_result_code::none), + rnti); + if (not parent->s1ap->is_mme_connected()) { parent->logger.error("MME isn't connected. Sending Connection Reject"); - event_logger::get().log_rrc_connected(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, - asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes), - static_cast(conn_request_result_t::error_mme_not_connected), - rnti); - send_connection_reject(); + send_connection_reject(procedure_result_code::error_mme_not_connected); return; } parent->logger.debug("rnti=0x%x, phyid=0x%x, smac=0x%x, cause=%s", @@ -483,11 +568,7 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg) set_activity_timeout(UE_INACTIVITY_TIMEOUT); } else { parent->logger.error("Received ConnectionReestablishment for rnti=0x%x without context", old_rnti); - event_logger::get().log_rrc_connected(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, - asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes), - static_cast(conn_request_result_t::error_unknown_rnti), - rnti); - send_connection_reest_rej(); + send_connection_reest_rej(procedure_result_code::error_unknown_rnti); } } else { parent->logger.error("Received ReestablishmentRequest from an rnti=0x%x not in IDLE", rnti); @@ -519,13 +600,28 @@ void rrc::ue::send_connection_reest(uint8_t ncc) // Configure PHY layer apply_setup_phy_config_dedicated(rr_cfg.phys_cfg_ded); // It assumes SCell has not been set before - send_dl_ccch(&dl_ccch_msg); + std::string octet_str; + send_dl_ccch(&dl_ccch_msg, &octet_str); apply_rr_cfg_ded_diff(current_ue_cfg.rr_cfg, rr_cfg); + + // Log event. + event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, + octet_str, + static_cast(rrc_event_type::con_reest), + static_cast(procedure_result_code::none), + rnti); } void rrc::ue::handle_rrc_con_reest_complete(rrc_conn_reest_complete_s* msg, srsran::unique_byte_buffer_t pdu) { + // Log event. + event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, + asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes), + static_cast(rrc_event_type::con_reest_complete), + static_cast(procedure_result_code::none), + rnti); + // Inform PHY about the configuration completion parent->phy->complete_config(rnti); @@ -554,11 +650,6 @@ void rrc::ue::handle_rrc_con_reest_complete(rrc_conn_reest_complete_s* msg, srsr state = RRC_STATE_REESTABLISHMENT_COMPLETE; - event_logger::get().log_rrc_connected(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, - asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes), - 0, - rnti); - // 2> if the UE has radio link failure or handover failure information available if (msg->crit_exts.type().value == rrc_conn_reest_complete_s::crit_exts_c_::types_opts::rrc_conn_reest_complete_r8) { const auto& complete_r8 = msg->crit_exts.rrc_conn_reest_complete_r8(); @@ -570,13 +661,22 @@ void rrc::ue::handle_rrc_con_reest_complete(rrc_conn_reest_complete_s* msg, srsr send_connection_reconf(std::move(pdu)); } -void rrc::ue::send_connection_reest_rej() +void rrc::ue::send_connection_reest_rej(procedure_result_code cause) { mac_ctrl.handle_con_reject(); dl_ccch_msg_s dl_ccch_msg; dl_ccch_msg.msg.set_c1().set_rrc_conn_reest_reject().crit_exts.set_rrc_conn_reest_reject_r8(); - send_dl_ccch(&dl_ccch_msg); + + std::string octet_str; + send_dl_ccch(&dl_ccch_msg, &octet_str); + + // Log event. + event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, + octet_str, + static_cast(rrc_event_type::con_reest_reject), + static_cast(cause), + rnti); } /* @@ -640,7 +740,15 @@ void rrc::ue::send_connection_reconf(srsran::unique_byte_buffer_t pdu, } // send DL-DCCH message to lower layers - send_dl_dcch(&dl_dcch_msg, std::move(pdu)); + std::string octet_str; + send_dl_dcch(&dl_dcch_msg, std::move(pdu), &octet_str); + + // Log event. + event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, + octet_str, + static_cast(rrc_event_type::con_reconf), + static_cast(procedure_result_code::none), + rnti); state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; } @@ -656,6 +764,13 @@ void rrc::ue::handle_rrc_reconf_complete(rrc_conn_recfg_complete_s* msg, srsran: return; } + // Log event. + event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, + asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes), + static_cast(rrc_event_type::con_reconf_complete), + static_cast(procedure_result_code::none), + rnti); + // Activate SCells and bearers in the MAC scheduler that were advertised in the RRC Reconf message mac_ctrl.handle_con_reconf_complete(); @@ -812,12 +927,17 @@ void rrc::ue::send_connection_release() } } - send_dl_dcch(&dl_dcch_msg); + std::string octet_str; + send_dl_dcch(&dl_dcch_msg, nullptr, &octet_str); // Log rrc release event. - event_logger::get().log_rrc_disconnect(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, - static_cast(rrc_idle_transition_cause::release), - rnti); + event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, + octet_str, + static_cast(rrc_event_type::con_release), + static_cast(con_release_result), + rnti); + // Restore release result. + con_release_result = procedure_result_code::none; } /* @@ -1086,7 +1206,7 @@ void rrc::ue::update_scells() /********************** HELPERS ***************************/ -void rrc::ue::send_dl_ccch(dl_ccch_msg_s* dl_ccch_msg) +void rrc::ue::send_dl_ccch(dl_ccch_msg_s* dl_ccch_msg, std::string* octet_str) { // Allocate a new PDU buffer, pack the message and send to PDCP srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); @@ -1101,13 +1221,19 @@ void rrc::ue::send_dl_ccch(dl_ccch_msg_s* dl_ccch_msg) char buf[32] = {}; sprintf(buf, "SRB0 - rnti=0x%x", rnti); parent->log_rrc_message(buf, Tx, pdu.get(), *dl_ccch_msg, dl_ccch_msg->msg.c1().type().to_string()); + + // Encode the pdu as an octet string if the user passed a valid pointer. + if (octet_str) { + *octet_str = asn1::octstring_to_string(pdu->msg, pdu->N_bytes); + } + parent->rlc->write_sdu(rnti, RB_ID_SRB0, std::move(pdu)); } else { parent->logger.error("Allocating pdu"); } } -bool rrc::ue::send_dl_dcch(const dl_dcch_msg_s* dl_dcch_msg, srsran::unique_byte_buffer_t pdu) +bool rrc::ue::send_dl_dcch(const dl_dcch_msg_s* dl_dcch_msg, srsran::unique_byte_buffer_t pdu, std::string* octet_str) { if (!pdu) { pdu = srsran::make_byte_buffer(); @@ -1130,6 +1256,11 @@ bool rrc::ue::send_dl_dcch(const dl_dcch_msg_s* dl_dcch_msg, srsran::unique_byte sprintf(buf, "SRB%d - rnti=0x%x", lcid, rnti); parent->log_rrc_message(buf, Tx, pdu.get(), *dl_dcch_msg, dl_dcch_msg->msg.c1().type().to_string()); + // Encode the pdu as an octet string if the user passed a valid pointer. + if (octet_str) { + *octet_str = asn1::octstring_to_string(pdu->msg, pdu->N_bytes); + } + parent->pdcp->write_sdu(rnti, lcid, std::move(pdu)); } else { parent->logger.error("Allocating pdu"); diff --git a/srsenb/src/stack/upper/gtpu.cc b/srsenb/src/stack/upper/gtpu.cc index 33d45cc39..23737d71a 100644 --- a/srsenb/src/stack/upper/gtpu.cc +++ b/srsenb/src/stack/upper/gtpu.cc @@ -22,6 +22,7 @@ #include "srsran/upper/gtpu.h" #include "srsenb/hdr/stack/upper/gtpu.h" #include "srsran/common/network_utils.h" +#include "srsran/common/srsran_assert.h" #include "srsran/common/standard_streams.h" #include "srsran/common/string_helpers.h" #include "srsran/interfaces/enb_interfaces.h" @@ -29,17 +30,281 @@ #include #include -#include #include #include using namespace srsran; + namespace srsenb { -gtpu::gtpu(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger) : - m1u(this), task_sched(task_sched_), logger(logger) +// ensure consistent formatting +#define TEID_IN_FMT "TEID In=0x%x" +#define TEID_OUT_FMT "TEID Out=0x%x" + +gtpu_tunnel_manager::gtpu_tunnel_manager(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger) : + logger(logger), task_sched(task_sched_), tunnels(1) {} +void gtpu_tunnel_manager::init(pdcp_interface_gtpu* pdcp_) +{ + pdcp = pdcp_; +} + +const gtpu_tunnel_manager::tunnel* gtpu_tunnel_manager::find_tunnel(uint32_t teid) +{ + auto it = tunnels.find(teid); + return it != tunnels.end() ? &it->second : nullptr; +} + +gtpu_tunnel_manager::ue_lcid_tunnel_list* gtpu_tunnel_manager::find_rnti_tunnels(uint16_t rnti) +{ + if (not ue_teidin_db.contains(rnti)) { + return nullptr; + } + return &ue_teidin_db[rnti]; +} + +srsran::span gtpu_tunnel_manager::find_rnti_lcid_tunnels(uint16_t rnti, uint32_t lcid) +{ + if (lcid < SRSENB_N_SRB or lcid >= SRSENB_N_RADIO_BEARERS) { + logger.warning("Searching for bearer with invalid lcid=%d", lcid); + return {}; + } + auto* ue_ptr = find_rnti_tunnels(rnti); + if (ue_ptr == nullptr) { + return {}; + } + auto lcid_it_begin = std::lower_bound(ue_ptr->begin(), ue_ptr->end(), lcid_tunnel{lcid, 0}); + auto lcid_it_end = std::lower_bound(ue_ptr->begin(), ue_ptr->end(), lcid_tunnel{lcid + 1, 0}); + + return srsran::span(&(*lcid_it_begin), &(*lcid_it_end)); +} + +const gtpu_tunnel* gtpu_tunnel_manager::add_tunnel(uint16_t rnti, uint32_t lcid, uint32_t teidout, uint32_t spgw_addr) +{ + if (lcid < SRSENB_N_SRB or lcid >= SRSENB_N_RADIO_BEARERS) { + logger.warning("Adding TEID with invalid lcid=%d", lcid); + return nullptr; + } + auto ret_pair = tunnels.insert(tunnel()); + if (not ret_pair) { + logger.warning("Unable to create new GTPU TEID In"); + return nullptr; + } + tunnel* tun = &tunnels[ret_pair.value()]; + tun->teid_in = ret_pair.value(); + tun->rnti = rnti; + tun->lcid = lcid; + tun->teid_out = teidout; + tun->spgw_addr = spgw_addr; + + if (not ue_teidin_db.contains(rnti)) { + ue_teidin_db.insert(rnti, ue_lcid_tunnel_list()); + } + auto& ue_tunnels = ue_teidin_db[rnti]; + + if (ue_tunnels.full()) { + logger.error("The number of TEIDs per UE exceeded for rnti=0x%x", rnti); + tunnels.erase(tun->teid_in); + return nullptr; + } + ue_tunnels.push_back(lcid_tunnel{lcid, tun->teid_in}); + std::sort(ue_tunnels.begin(), ue_tunnels.end()); + + fmt::memory_buffer str_buffer; + srsran::gtpu_ntoa(str_buffer, htonl(spgw_addr)); + logger.info("New tunnel created - " TEID_IN_FMT ", " TEID_OUT_FMT ", rnti=0x%x, lcid=%d, remote addr=%s", + tun->teid_in, + teidout, + rnti, + lcid, + srsran::to_c_str(str_buffer)); + + return tun; +} + +bool gtpu_tunnel_manager::update_rnti(uint16_t old_rnti, uint16_t new_rnti) +{ + srsran_assert(find_rnti_tunnels(new_rnti) == nullptr, "New rnti=0x%x already exists", new_rnti); + + auto* old_rnti_ptr = find_rnti_tunnels(old_rnti); + logger.info("Modifying bearer rnti. Old rnti: 0x%x, new rnti: 0x%x", old_rnti, new_rnti); + + // Change RNTI bearers map + ue_teidin_db.insert(new_rnti, std::move(*old_rnti_ptr)); + ue_teidin_db.erase(old_rnti); + + // Change TEID in existing tunnels + auto* new_rnti_ptr = find_rnti_tunnels(new_rnti); + for (lcid_tunnel& bearer : *new_rnti_ptr) { + tunnels[bearer.teid].rnti = new_rnti; + } + + return true; +} + +bool gtpu_tunnel_manager::remove_tunnel(uint32_t teidin) +{ + tunnel& tun = tunnels[teidin]; + + // erase keeping the relative order + auto& ue = ue_teidin_db[tun.rnti]; + auto lcid_it = std::lower_bound(ue.begin(), ue.end(), lcid_tunnel{tun.lcid, tun.teid_in}); + srsran_assert(lcid_it->teid == tun.teid_in and lcid_it->lcid == tun.lcid, "TEID in undefined state"); + ue.erase(lcid_it); + + logger.info("Removed rnti=0x%x,lcid=%d tunnel with " TEID_IN_FMT, tun.rnti, tun.lcid, teidin); + tunnels.erase(teidin); + return true; +} + +bool gtpu_tunnel_manager::remove_bearer(uint16_t rnti, uint32_t lcid) +{ + srsran::span to_rem = find_rnti_lcid_tunnels(rnti, lcid); + if (to_rem.empty()) { + return false; + } + logger.info("Removing rnti=0x%x,lcid=%d", rnti, lcid); + + for (lcid_tunnel& lcid_tun : to_rem) { + srsran_expect(tunnels.erase(lcid_tun.teid) > 0, "Inconsistency detected between two internal data structures"); + } + ue_teidin_db[rnti].erase(to_rem.begin(), to_rem.end()); + return true; +} + +bool gtpu_tunnel_manager::remove_rnti(uint16_t rnti) +{ + if (not ue_teidin_db.contains(rnti)) { + logger.warning("removing rnti. rnti=0x%x not found.", rnti); + return false; + } + logger.info("Removing rnti=0x%x", rnti); + + for (lcid_tunnel& ue_tuns : ue_teidin_db[rnti]) { + srsran_expect(tunnels.erase(ue_tuns.teid) > 0, "Inconsistency detected between two internal data structures"); + } + ue_teidin_db.erase(rnti); + return true; +} + +void gtpu_tunnel_manager::activate_tunnel(uint32_t teid) +{ + tunnel& tun = tunnels[teid]; + if (tun.state == tunnel_state::pdcp_active) { + // nothing happens + return; + } + + logger.info("Activating GTPU tunnel rnti=0x%x, " TEID_IN_FMT ". %d SDUs currently buffered", + tun.rnti, + tun.teid_in, + tun.buffer->size()); + // Forward buffered SDUs to lower layers and delete buffer + auto lower_sn = [](const std::pair& lhs, + const std::pair& rhs) { return lhs.first < rhs.first; }; + std::stable_sort(tun.buffer->begin(), tun.buffer->end(), lower_sn); + + for (auto& sdu_pair : *tun.buffer) { + uint32_t pdcp_sn = sdu_pair.first; + pdcp->write_sdu(tun.rnti, tun.lcid, std::move(sdu_pair.second), pdcp_sn == undefined_pdcp_sn ? -1 : pdcp_sn); + } + tun.buffer.reset(); + tun.state = tunnel_state::pdcp_active; +} + +void gtpu_tunnel_manager::suspend_tunnel(uint32_t teid) +{ + tunnel& tun = tunnels[teid]; + if (tun.state != tunnel_state::pdcp_active) { + logger.error("Invalid TEID transition detected"); + return; + } + // Create a container for buffering SDUs + tun.buffer.emplace(); + tun.state = tunnel_state::buffering; +} + +void gtpu_tunnel_manager::set_tunnel_priority(uint32_t before_teid, uint32_t after_teid) +{ + tunnel& before_tun = tunnels[before_teid]; + tunnel& after_tun = tunnels[after_teid]; + + // GTPU should not forward SDUs from main tunnel until the SeNB-TeNB tunnel has been flushed + suspend_tunnel(after_teid); + + before_tun.on_removal = [this, after_teid]() { + if (tunnels.contains(after_teid)) { + // In Handover, TeNB switches paths, and flushes PDUs that have been buffered + activate_tunnel(after_teid); + } + }; + + // Schedule auto-removal of this indirect tunnel + before_tun.rx_timer = task_sched.get_unique_timer(); + before_tun.rx_timer.set(500, [this, before_teid](uint32_t tid) { + // This will self-destruct the callback object + remove_tunnel(before_teid); + }); + before_tun.rx_timer.run(); +} + +void gtpu_tunnel_manager::handle_rx_pdcp_sdu(uint32_t teid) +{ + tunnel& rx_tun = tunnels[teid]; + + // Reset Rx timer when a PDCP SDU is received + if (rx_tun.rx_timer.is_valid() and rx_tun.rx_timer.is_running()) { + rx_tun.rx_timer.run(); + } +} + +void gtpu_tunnel_manager::buffer_pdcp_sdu(uint32_t teid, uint32_t pdcp_sn, srsran::unique_byte_buffer_t sdu) +{ + tunnel& rx_tun = tunnels[teid]; + + srsran_assert(rx_tun.state == tunnel_state::buffering, "Buffering of PDCP SDUs only enabled when PDCP is not active"); + rx_tun.buffer->push_back(std::make_pair(pdcp_sn, std::move(sdu))); +} + +void gtpu_tunnel_manager::setup_forwarding(uint32_t rx_teid, uint32_t tx_teid) +{ + tunnel& rx_tun = tunnels[rx_teid]; + tunnel& tx_tun = tunnels[tx_teid]; + + rx_tun.state = tunnel_state::forward_to; + rx_tun.fwd_tunnel = &tx_tun; + tx_tun.state = tunnel_state::forwarded_from; + + // Auto-removes indirect tunnel when the main tunnel is removed + rx_tun.on_removal = [this, tx_teid]() { + if (tunnels.contains(tx_teid)) { + remove_tunnel(tx_teid); + } + }; + + fmt::memory_buffer addrbuf; + srsran::gtpu_ntoa(addrbuf, htonl(rx_tun.spgw_addr)); + fmt::format_to(addrbuf, ":0x{:x} > ", rx_tun.teid_out); + srsran::gtpu_ntoa(addrbuf, htonl(tx_tun.spgw_addr)); + fmt::format_to(addrbuf, ":0x{:x}", tx_tun.teid_out); + logger.info( + "Created forwarding tunnel for rnti=0x%x, lcid=%d, %s", rx_tun.rnti, rx_tun.lcid, srsran::to_c_str(addrbuf)); +} + +/******************** + * GTPU class + *******************/ + +gtpu::gtpu(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger) : + m1u(this), task_sched(task_sched_), logger(logger), tunnels(task_sched_, logger) +{} + +gtpu::~gtpu() +{ + stop(); +} + int gtpu::init(std::string gtp_bind_addr_, std::string mme_addr_, std::string m1u_multiaddr_, @@ -53,6 +318,8 @@ int gtpu::init(std::string gtp_bind_addr_, mme_addr = mme_addr_; stack = stack_; + tunnels.init(pdcp); + char errbuf[128] = {}; // Set up socket @@ -98,24 +365,26 @@ int gtpu::init(std::string gtp_bind_addr_, void gtpu::stop() { - if (fd) { + if (fd > 0) { close(fd); + fd = -1; } } // gtpu_interface_pdcp void gtpu::write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) { - srsran::span teids = get_lcid_teids(rnti, lcid); + srsran::span teids = tunnels.find_rnti_lcid_tunnels(rnti, lcid); if (teids.empty()) { + logger.warning("The rnti=0x%x,lcid=%d does not have any pdcp_active tunnel", rnti, lcid); return; } - tunnel& tx_tun = tunnels[teids[0]]; + const gtpu_tunnel& tx_tun = *tunnels.find_tunnel(teids[0].teid); log_message(tx_tun, false, srsran::make_span(pdu)); send_pdu_to_tunnel(tx_tun, std::move(pdu)); } -void gtpu::send_pdu_to_tunnel(tunnel& tx_tun, srsran::unique_byte_buffer_t pdu, int pdcp_sn) +void gtpu::send_pdu_to_tunnel(const gtpu_tunnel& tx_tun, srsran::unique_byte_buffer_t pdu, int pdcp_sn) { // Check valid IP version struct iphdr* ip_pkt = (struct iphdr*)pdu->msg; @@ -154,65 +423,30 @@ void gtpu::send_pdu_to_tunnel(tunnel& tx_tun, srsran::unique_byte_buffer_t pdu, } } -uint32_t gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, const bearer_props* props) +srsran::expected +gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, const bearer_props* props) { // Allocate a TEID for the incoming tunnel - uint32_t teid_in = ++next_teid_in; - auto insert_ret = tunnels.emplace(teid_in, tunnel{}); - tunnel& new_tun = insert_ret.first->second; - new_tun.teid_in = teid_in; - new_tun.rnti = rnti; - new_tun.lcid = lcid; - new_tun.spgw_addr = addr; - new_tun.teid_out = teid_out; - - ue_teidin_db[rnti][lcid].push_back(teid_in); - - fmt::memory_buffer str_buffer; - srsran::gtpu_ntoa(str_buffer, htonl(addr)); - logger.info("New tunnel teid_in=0x%x, teid_out=0x%x, rnti=0x%x, lcid=%d, addr=%s", - teid_in, - teid_out, - rnti, - lcid, - srsran::to_c_str(str_buffer)); + const gtpu_tunnel* new_tun = tunnels.add_tunnel(rnti, lcid, teid_out, addr); + if (new_tun == nullptr) { + return default_error_t(); + } + uint32_t teid_in = new_tun->teid_in; if (props != nullptr) { if (props->flush_before_teidin_present) { // GTPU should wait for the bearer ctxt to arrive before sending SDUs from DL tunnel to PDCP - new_tun.dl_enabled = false; + tunnels.suspend_tunnel(teid_in); + // GTPU should not forward SDUs from main tunnel until the SeNB-TeNB tunnel has been flushed - tunnel& after_tun = tunnels.at(props->flush_before_teidin); - after_tun.dl_enabled = false; - after_tun.prior_teid_in_present = true; - after_tun.prior_teid_in = teid_in; - // Schedule autoremoval of this indirect tunnel - uint32_t after_teidin = after_tun.teid_in; - uint32_t before_teidin = new_tun.teid_in; - new_tun.rx_timer = task_sched.get_unique_timer(); - new_tun.rx_timer.set(500, [this, before_teidin, after_teidin](uint32_t tid) { - auto it = tunnels.find(after_teidin); - if (it != tunnels.end()) { - tunnel& after_tun = it->second; - if (after_tun.prior_teid_in_present) { - after_tun.prior_teid_in_present = false; - set_tunnel_status(after_tun.teid_in, true); - } - // else: indirect tunnel already removed - } else { - logger.info("Callback to automatic indirect tunnel deletion called for non-existent TEID=%d", after_teidin); - } - // This will self-destruct the callback object - rem_tunnel(before_teidin); - }); - new_tun.rx_timer.run(); + tunnels.set_tunnel_priority(teid_in, props->flush_before_teidin); } // Connect tunnels if forwarding is activated if (props->forward_from_teidin_present) { if (create_dl_fwd_tunnel(props->forward_from_teidin, teid_in) != SRSRAN_SUCCESS) { rem_tunnel(teid_in); - return 0; + return default_error_t(); } } } @@ -222,199 +456,111 @@ uint32_t gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t void gtpu::set_tunnel_status(uint32_t teidin, bool dl_active) { - auto tun_it = tunnels.find(teidin); - if (tun_it == tunnels.end()) { - logger.warning("Setting TEID=%d status", teidin); + if (not tunnels.has_teid(teidin)) { + logger.error("Setting status for non-existent " TEID_IN_FMT, teidin); return; } - tun_it->second.dl_enabled = dl_active; + if (dl_active) { - logger.info("Activating GTPU tunnel rnti=0x%x,TEID=%d. %d SDUs currently buffered", - tun_it->second.rnti, - teidin, - tun_it->second.buffer.size()); - for (auto& sdu_it : tun_it->second.buffer) { - pdcp->write_sdu(tun_it->second.rnti, - tun_it->second.lcid, - std::move(sdu_it.second), - sdu_it.first == undefined_pdcp_sn ? -1 : sdu_it.first); - } - tun_it->second.buffer.clear(); + tunnels.activate_tunnel(teidin); + } else { + tunnels.suspend_tunnel(teidin); } } void gtpu::rem_bearer(uint16_t rnti, uint32_t lcid) { - auto ue_it = ue_teidin_db.find(rnti); - if (ue_it == ue_teidin_db.end()) { - logger.warning("Removing bearer rnti=0x%x, lcid=%d", rnti, lcid); + if (tunnels.find_rnti_lcid_tunnels(rnti, lcid).empty()) { + logger.error("Removing non-existent bearer rnti=0x%x,lcid=%d", rnti, lcid); return; } - std::vector& lcid_tuns = ue_it->second[lcid]; - - while (not lcid_tuns.empty()) { - rem_tunnel(lcid_tuns.back()); - } - logger.info("Removing bearer for rnti: 0x%x, lcid: %d", rnti, lcid); - - bool rem_ue = std::all_of( - ue_it->second.begin(), ue_it->second.end(), [](const std::vector& list) { return list.empty(); }); - if (rem_ue) { - ue_teidin_db.erase(ue_it); - } + tunnels.remove_bearer(rnti, lcid); } void gtpu::mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti) { - logger.info("Modifying bearer rnti. Old rnti: 0x%x, new rnti: 0x%x", old_rnti, new_rnti); - - if (ue_teidin_db.count(new_rnti) != 0) { - logger.error("New rnti already exists, aborting."); + auto* old_rnti_ptr = tunnels.find_rnti_tunnels(old_rnti); + if (old_rnti_ptr == nullptr or tunnels.find_rnti_tunnels(new_rnti) != nullptr) { + logger.error("Modifying bearer rnti. Old rnti=0x%x, new rnti=0x%x", old_rnti, new_rnti); return; } - auto old_it = ue_teidin_db.find(old_rnti); - if (old_it == ue_teidin_db.end()) { - logger.error("Old rnti does not exist, aborting."); - return; - } - - // Change RNTI bearers map - ue_teidin_db.insert(std::make_pair(new_rnti, std::move(old_it->second))); - ue_teidin_db.erase(old_it); - - // Change TEID - auto new_it = ue_teidin_db.find(new_rnti); - for (auto& bearer : new_it->second) { - for (uint32_t teid : bearer) { - tunnels[teid].rnti = new_rnti; - } - } + tunnels.update_rnti(old_rnti, new_rnti); } void gtpu::rem_tunnel(uint32_t teidin) { - auto it = tunnels.find(teidin); - if (it == tunnels.end()) { - logger.warning("Removing GTPU tunnel TEID In=0x%x", teidin); + if (not tunnels.has_teid(teidin)) { + logger.warning("Removing tunnel - " TEID_IN_FMT " does not exist", teidin); return; } - auto ue_it = ue_teidin_db.find(it->second.rnti); - std::vector& lcid_tunnels = ue_it->second[it->second.lcid]; - lcid_tunnels.erase(std::remove(lcid_tunnels.begin(), lcid_tunnels.end(), teidin), lcid_tunnels.end()); - logger.debug("TEID In=%d for rnti=0x%x erased", teidin, it->second.rnti); - tunnels.erase(it); + tunnels.remove_tunnel(teidin); } void gtpu::rem_user(uint16_t rnti) { - logger.info("Removing rnti=0x%x", rnti); - auto ue_it = ue_teidin_db.find(rnti); - if (ue_it != ue_teidin_db.end()) { - for (auto& bearer : ue_it->second) { - while (not bearer.empty()) { - rem_tunnel(bearer.back()); - } - } + if (tunnels.find_rnti_tunnels(rnti) == nullptr) { + logger.info("Removing user - rnti=0x%x not found.", rnti); + return; } + tunnels.remove_rnti(rnti); } -void gtpu::handle_end_marker(tunnel& rx_tunnel) +void gtpu::handle_end_marker(const gtpu_tunnel& rx_tunnel) { uint16_t rnti = rx_tunnel.rnti; - logger.info("Received GTPU End Marker for rnti=0x%x.", rnti); + logger.info("Received GTPU End Marker for " TEID_IN_FMT ", rnti=0x%x.", rx_tunnel.teid_in, rnti); - // TS 36.300, Sec 10.1.2.2.1 - Path Switch upon handover - if (rx_tunnel.fwd_teid_in_present) { + if (rx_tunnel.state == gtpu_tunnel_state::forward_to) { + // TS 36.300, Sec 10.1.2.2.1 - Path Switch upon handover // END MARKER should be forwarded to TeNB if forwarding is activated - end_marker(rx_tunnel.fwd_teid_in); - rx_tunnel.fwd_teid_in_present = false; - - rem_tunnel(rx_tunnel.teid_in); - } else { - // TeNB switches paths, and flush PDUs that have been buffered - auto rnti_it = ue_teidin_db.find(rnti); - if (rnti_it == ue_teidin_db.end()) { - logger.error("No rnti=0x%x entry for associated TEID=%d", rnti, rx_tunnel.teid_in); - return; - } - std::vector& bearer_tunnels = rnti_it->second[rx_tunnel.lcid]; - for (uint32_t new_teidin : bearer_tunnels) { - tunnel& new_tun = tunnels.at(new_teidin); - if (new_teidin != rx_tunnel.teid_in and new_tun.prior_teid_in_present and - new_tun.prior_teid_in == rx_tunnel.teid_in) { - rem_tunnel(new_tun.prior_teid_in); - new_tun.prior_teid_in_present = false; - set_tunnel_status(new_tun.teid_in, true); - break; - } - } + send_end_marker(rx_tunnel.fwd_tunnel->teid_in); } + + // Remove tunnel that received End Marker + rem_tunnel(rx_tunnel.teid_in); } void gtpu::handle_gtpu_s1u_rx_packet(srsran::unique_byte_buffer_t pdu, const sockaddr_in& addr) { + srsran_assert(pdu != nullptr, "Called with null PDU"); + logger.debug("Received %d bytes from S1-U interface", pdu->N_bytes); pdu->set_timestamp(); + // Decode GTPU Header gtpu_header_t header; if (not gtpu_read_header(pdu.get(), &header, logger)) { return; } - tunnel* rx_tunnel = nullptr; - if (header.teid != 0) { - auto it = tunnels.find(header.teid); - if (it == tunnels.end()) { - // Received G-PDU for non-existing and non-zero TEID. - // Sending GTP-U error indication - error_indication(addr.sin_addr.s_addr, addr.sin_port, header.teid); - } - rx_tunnel = &it->second; + if (header.message_type == GTPU_MSG_ECHO_REQUEST) { + // Echo request - send response + echo_response(addr.sin_addr.s_addr, addr.sin_port, header.seq_number); + return; + } + if (header.message_type == GTPU_MSG_ERROR_INDICATION) { + logger.warning("Received Error Indication"); + return; + } + if (header.teid == 0) { + logger.warning("Received GTPU S1-U message with " TEID_IN_FMT, header.teid); + } - if (rx_tunnel->rx_timer.is_valid()) { - // Restart Rx timer - rx_tunnel->rx_timer.run(); - } + // Find TEID present in GTPU Header + const gtpu_tunnel* tun_ptr = tunnels.find_tunnel(header.teid); + if (tun_ptr == nullptr) { + // Received G-PDU for non-existing and non-zero TEID. + // Sending GTP-U error indication + error_indication(addr.sin_addr.s_addr, addr.sin_port, header.teid); + return; } switch (header.message_type) { - case GTPU_MSG_ECHO_REQUEST: - // Echo request - send response - echo_response(addr.sin_addr.s_addr, addr.sin_port, header.seq_number); - break; case GTPU_MSG_DATA_PDU: { - auto& rx_tun = tunnels.find(header.teid)->second; - uint16_t rnti = rx_tun.rnti; - uint16_t lcid = rx_tun.lcid; - - log_message(rx_tun, true, srsran::make_span(pdu)); - - if (lcid < SRSENB_N_SRB || lcid >= SRSENB_N_RADIO_BEARERS) { - logger.error("Invalid LCID for DL PDU: %d - dropping packet", lcid); - return; - } - struct iphdr* ip_pkt = (struct iphdr*)pdu->msg; - if (ip_pkt->version != 4 && ip_pkt->version != 6) { - return; - } - - if (rx_tun.fwd_teid_in_present) { - tunnel& tx_tun = tunnels.at(rx_tun.fwd_teid_in); - send_pdu_to_tunnel(tx_tun, std::move(pdu)); - } else { - uint32_t pdcp_sn = undefined_pdcp_sn; - if (header.flags & GTPU_FLAGS_EXTENDED_HDR and header.next_ext_hdr_type == GTPU_EXT_HEADER_PDCP_PDU_NUMBER) { - pdcp_sn = (header.ext_buffer[1] << 8u) + header.ext_buffer[2]; - } - if (not rx_tun.dl_enabled) { - rx_tun.buffer.insert(std::make_pair(pdcp_sn, std::move(pdu))); - } else { - pdcp->write_sdu(rnti, lcid, std::move(pdu), pdcp_sn == undefined_pdcp_sn ? -1 : pdcp_sn); - } - } + handle_msg_data_pdu(header, *tun_ptr, std::move(pdu)); } break; case GTPU_MSG_END_MARKER: - handle_end_marker(*rx_tunnel); + handle_end_marker(*tun_ptr); break; default: logger.warning("Unhandled GTPU message type=%d", header.message_type); @@ -422,6 +568,50 @@ void gtpu::handle_gtpu_s1u_rx_packet(srsran::unique_byte_buffer_t pdu, const soc } } +void gtpu::handle_msg_data_pdu(const gtpu_header_t& header, + const gtpu_tunnel& rx_tunnel, + srsran::unique_byte_buffer_t pdu) +{ + struct iphdr* ip_pkt = (struct iphdr*)pdu->msg; + if (ip_pkt->version != 4 && ip_pkt->version != 6) { + logger.error("Received SDU with invalid IP version=%d", (int)ip_pkt->version); + return; + } + + // Forward SDU to PDCP or buffer it if tunnel is disabled + uint32_t pdcp_sn = undefined_pdcp_sn; + if ((header.flags & GTPU_FLAGS_EXTENDED_HDR) != 0 and header.next_ext_hdr_type == GTPU_EXT_HEADER_PDCP_PDU_NUMBER) { + pdcp_sn = (header.ext_buffer[1] << 8U) + header.ext_buffer[2]; + } + + uint16_t rnti = rx_tunnel.rnti; + uint16_t lcid = rx_tunnel.lcid; + + log_message(rx_tunnel, true, srsran::make_span(pdu)); + + tunnels.handle_rx_pdcp_sdu(rx_tunnel.teid_in); + + switch (rx_tunnel.state) { + case gtpu_tunnel_manager::tunnel_state::forward_to: { + // Forward SDU to direct/indirect tunnel during Handover + send_pdu_to_tunnel(*rx_tunnel.fwd_tunnel, std::move(pdu)); + break; + } + case gtpu_tunnel_manager::tunnel_state::buffering: { + tunnels.buffer_pdcp_sdu(rx_tunnel.teid_in, pdcp_sn, std::move(pdu)); + break; + } + case gtpu_tunnel_manager::tunnel_state::pdcp_active: { + pdcp->write_sdu(rnti, lcid, std::move(pdu), pdcp_sn == undefined_pdcp_sn ? -1 : (int)pdcp_sn); + break; + } + case gtpu_tunnel_manager::tunnel_state::forwarded_from: + default: + logger.error(TEID_IN_FMT " found in invalid state", rx_tunnel.teid_in); + break; + } +} + void gtpu::handle_gtpu_m1u_rx_packet(srsran::unique_byte_buffer_t pdu, const sockaddr_in& addr) { m1u.handle_rx_packet(std::move(pdu), addr); @@ -430,29 +620,21 @@ void gtpu::handle_gtpu_m1u_rx_packet(srsran::unique_byte_buffer_t pdu, const soc /// Connect created tunnel with pre-existing tunnel for data forwarding int gtpu::create_dl_fwd_tunnel(uint32_t rx_teid_in, uint32_t tx_teid_in) { - auto rx_tun_pair = tunnels.find(rx_teid_in); - auto tx_tun_pair = tunnels.find(tx_teid_in); - if (rx_tun_pair == tunnels.end() or tx_tun_pair == tunnels.end()) { + const gtpu_tunnel* rx_tun = tunnels.find_tunnel(rx_teid_in); + const gtpu_tunnel* tx_tun = tunnels.find_tunnel(tx_teid_in); + if (rx_tun == nullptr or tx_tun == nullptr) { logger.error("Failed to create forwarding tunnel between teids 0x%x and 0x%x", rx_teid_in, tx_teid_in); return SRSRAN_ERROR; } - tunnel &rx_tun = rx_tun_pair->second, &tx_tun = tx_tun_pair->second; - rx_tun.fwd_teid_in_present = true; - rx_tun.fwd_teid_in = tx_teid_in; - logger.info("Creating forwarding tunnel for rnti=0x%x, lcid=%d, in={0x%x, 0x%x}->out={0x%x, 0x%x}", - rx_tun.rnti, - rx_tun.lcid, - rx_tun.teid_out, - rx_tun.spgw_addr, - tx_tun.teid_out, - tx_tun.spgw_addr); + tunnels.setup_forwarding(rx_teid_in, tx_teid_in); // Get all buffered PDCP PDUs, and forward them through tx tunnel - std::map pdus = pdcp->get_buffered_pdus(rx_tun.rnti, rx_tun.lcid); + std::map pdus = pdcp->get_buffered_pdus(rx_tun->rnti, rx_tun->lcid); for (auto& pdu_pair : pdus) { - log_message(tx_tun, false, srsran::make_span(pdu_pair.second), pdu_pair.first); - send_pdu_to_tunnel(tx_tun, std::move(pdu_pair.second), pdu_pair.first); + uint32_t pdcp_sn = pdu_pair.first; + log_message(*tx_tun, false, srsran::make_span(pdu_pair.second), pdcp_sn); + send_pdu_to_tunnel(*tx_tun, std::move(pdu_pair.second), pdcp_sn); } return SRSRAN_SUCCESS; @@ -528,15 +710,14 @@ void gtpu::echo_response(in_addr_t addr, in_port_t port, uint16_t seq) /**************************************************************************** * GTP-U END MARKER ***************************************************************************/ -bool gtpu::end_marker(uint32_t teidin) +bool gtpu::send_end_marker(uint32_t teidin) { logger.info("TX GTPU End Marker."); - auto it = tunnels.find(teidin); - if (it == tunnels.end()) { + const gtpu_tunnel* tx_tun = tunnels.find_tunnel(teidin); + if (tx_tun == nullptr) { logger.error("TEID=%d not found to send the end marker to", teidin); return false; } - tunnel& tunnel = it->second; gtpu_header_t header = {}; unique_byte_buffer_t pdu = make_byte_buffer(); @@ -548,14 +729,14 @@ bool gtpu::end_marker(uint32_t teidin) // header header.flags = GTPU_FLAGS_VERSION_V1 | GTPU_FLAGS_GTP_PROTOCOL; header.message_type = GTPU_MSG_END_MARKER; - header.teid = tunnel.teid_out; + header.teid = tx_tun->teid_out; header.length = 0; gtpu_write_header(&header, pdu.get(), logger); struct sockaddr_in servaddr = {}; servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr = htonl(tunnel.spgw_addr); + servaddr.sin_addr.s_addr = htonl(tx_tun->spgw_addr); servaddr.sin_port = htons(GTPU_PORT); sendto(fd, pdu->msg, pdu->N_bytes, MSG_EOR, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in)); @@ -566,28 +747,7 @@ bool gtpu::end_marker(uint32_t teidin) * TEID to RNTI/LCID helper functions ***************************************************************************/ -gtpu::tunnel* gtpu::get_tunnel(uint32_t teidin) -{ - auto it = tunnels.find(teidin); - if (it == tunnels.end()) { - logger.error("TEID=%d In does not exist.", teidin); - return nullptr; - } - return &it->second; -} - -srsran::span gtpu::get_lcid_teids(uint16_t rnti, uint32_t lcid) -{ - auto ue_it = ue_teidin_db.find(rnti); - if (ue_it == ue_teidin_db.end() or lcid < SRSENB_N_SRB or lcid >= SRSENB_N_RADIO_BEARERS or - ue_it->second[lcid].empty()) { - logger.error("Could not find bearer rnti=0x%x, lcid=%d", rnti, lcid); - return {}; - } - return ue_it->second[lcid]; -} - -void gtpu::log_message(tunnel& tun, bool is_rx, srsran::span pdu, int pdcp_sn) +void gtpu::log_message(const gtpu_tunnel& tun, bool is_rx, srsran::span pdu, int pdcp_sn) { struct iphdr* ip_pkt = (struct iphdr*)pdu.data(); if (ip_pkt->version != 4 && ip_pkt->version != 6) { @@ -605,15 +765,22 @@ void gtpu::log_message(tunnel& tun, bool is_rx, srsran::span pdu, int p if (is_rx) { dir = "Rx"; fmt::format_to(strbuf2, "{}:0x{:0x} > ", srsran::to_c_str(addrbuf), tun.teid_in); - if (not tun.dl_enabled) { - fmt::format_to(strbuf2, "DL (buffered), "); - } else if (tun.fwd_teid_in_present) { - tunnel& tx_tun = tunnels.at(tun.fwd_teid_in); - addrbuf.clear(); - srsran::gtpu_ntoa(addrbuf, htonl(tx_tun.spgw_addr)); - fmt::format_to(strbuf2, "{}:0x{:0x} (forwarded), ", srsran::to_c_str(addrbuf), tx_tun.teid_in); - } else { - fmt::format_to(strbuf2, "DL, "); + switch (tun.state) { + case gtpu_tunnel_manager::tunnel_state::buffering: + fmt::format_to(strbuf2, "DL (buffered), "); + break; + case gtpu_tunnel_manager::tunnel_state::forward_to: { + addrbuf.clear(); + srsran::gtpu_ntoa(addrbuf, htonl(tun.fwd_tunnel->spgw_addr)); + fmt::format_to(strbuf2, "{}:0x{:0x} (forwarded), ", srsran::to_c_str(addrbuf), tun.fwd_tunnel->teid_in); + break; + } + case gtpu_tunnel_manager::tunnel_state::pdcp_active: + fmt::format_to(strbuf2, "DL, "); + break; + default: + logger.error(TEID_IN_FMT " found in invalid state: %d", tun.teid_in, (int)tun.state); + break; } } else { if (pdcp_sn >= 0) { diff --git a/srsenb/src/stack/upper/s1ap.cc b/srsenb/src/stack/upper/s1ap.cc index a016bb7c4..090510274 100644 --- a/srsenb/src/stack/upper/s1ap.cc +++ b/srsenb/src/stack/upper/s1ap.cc @@ -32,11 +32,9 @@ #include #include #include -#include #include #include #include -#include //for close(), sleep() using srsran::s1ap_mccmnc_to_plmn; using srsran::uint32_to_uint8; @@ -74,7 +72,7 @@ srsran::proc_outcome_t s1ap::ue::ho_prep_proc_t::init(uint32_t target_eci = target_eci_; target_plmn = target_plmn_; - procInfo("Sending HandoverRequired to MME id=%d", ue_ptr->ctxt.mme_ue_s1ap_id); + procInfo("Sending HandoverRequired to MME id=%d", ue_ptr->ctxt.mme_ue_s1ap_id.value()); if (not ue_ptr->send_ho_required(target_eci, target_plmn, fwd_erabs, std::move(rrc_container_))) { procError("Failed to send HORequired to cell 0x%x", target_eci); return srsran::proc_outcome_t::error; @@ -368,14 +366,14 @@ bool s1ap::user_release(uint16_t rnti, asn1::s1ap::cause_radio_network_e cause_r if (u->was_uectxtrelease_requested()) { logger.warning("UE context for RNTI:0x%x is in zombie state. Releasing...", rnti); users.erase(u); - rrc->release_complete(rnti); + rrc->release_ue(rnti); return false; } cause_c cause; cause.set_radio_network().value = cause_radio.value; - if (u->ctxt.mme_ue_s1ap_id_present) { + if (u->ctxt.mme_ue_s1ap_id.has_value()) { return u->send_uectxtreleaserequest(cause); } return true; @@ -550,7 +548,7 @@ bool s1ap::handle_s1ap_rx_pdu(srsran::byte_buffer_t* pdu) logger.error(pdu->msg, pdu->N_bytes, "Failed to unpack received PDU"); cause_c cause; cause.set_protocol().value = cause_protocol_opts::transfer_syntax_error; - send_error_indication(SRSRAN_INVALID_RNTI, cause); + send_error_indication(cause); return false; } log_s1ap_msg(rx_pdu, srsran::make_span(*pdu), true); @@ -642,7 +640,8 @@ bool s1ap::handle_dlnastransport(const dl_nas_transport_s& msg) if (msg.ext) { logger.warning("Not handling S1AP message extension"); } - ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = + handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } @@ -670,7 +669,8 @@ bool s1ap::handle_initialctxtsetuprequest(const init_context_setup_request_s& ms if (msg.ext) { logger.warning("Not handling S1AP message extension"); } - ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = + handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } @@ -711,7 +711,8 @@ bool s1ap::handle_erabsetuprequest(const erab_setup_request_s& msg) if (msg.ext) { logger.warning("Not handling S1AP message extension"); } - ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = + handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } @@ -728,7 +729,8 @@ bool s1ap::handle_erabmodifyrequest(const erab_modify_request_s& msg) if (msg.ext) { logger.warning("Not handling S1AP message extension"); } - ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = + handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } @@ -760,7 +762,8 @@ bool s1ap::handle_erabreleasecommand(const erab_release_cmd_s& msg) if (msg.ext) { logger.warning("Not handling S1AP message extension"); } - ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = + handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } @@ -779,7 +782,8 @@ bool s1ap::handle_erabreleasecommand(const erab_release_cmd_s& msg) bool s1ap::handle_uecontextmodifyrequest(const ue_context_mod_request_s& msg) { - ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = + handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } @@ -825,7 +829,7 @@ bool s1ap::handle_uectxtreleasecommand(const ue_context_release_cmd_s& msg) if (idpair.ie_exts_present) { logger.warning("Not handling S1AP message iE_Extensions"); } - u = find_s1apmsg_user(idpair.enb_ue_s1ap_id, idpair.mme_ue_s1ap_id); + u = handle_s1apmsg_ue_id(idpair.enb_ue_s1ap_id, idpair.mme_ue_s1ap_id); if (u == nullptr) { return false; } @@ -843,7 +847,7 @@ bool s1ap::handle_uectxtreleasecommand(const ue_context_release_cmd_s& msg) u->send_uectxtreleasecomplete(); users.erase(u); logger.info("UE context for RNTI:0x%x released", rnti); - rrc->release_complete(rnti); + rrc->release_ue(rnti); return true; } @@ -857,7 +861,8 @@ bool s1ap::handle_s1setupfailure(const asn1::s1ap::s1_setup_fail_s& msg) bool s1ap::handle_handover_preparation_failure(const ho_prep_fail_s& msg) { - ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = + handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } @@ -867,7 +872,8 @@ bool s1ap::handle_handover_preparation_failure(const ho_prep_fail_s& msg) bool s1ap::handle_handover_command(const asn1::s1ap::ho_cmd_s& msg) { - ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = + handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } @@ -908,8 +914,7 @@ bool s1ap::handle_handover_request(const asn1::s1ap::ho_request_s& msg) // Create user ctxt object and associated MME context std::unique_ptr ue_ptr{new ue{this}}; - ue_ptr->ctxt.mme_ue_s1ap_id_present = true; - ue_ptr->ctxt.mme_ue_s1ap_id = msg.protocol_ies.mme_ue_s1ap_id.value.value; + ue_ptr->ctxt.mme_ue_s1ap_id = msg.protocol_ies.mme_ue_s1ap_id.value.value; if (users.add_user(std::move(ue_ptr)) == nullptr) { return false; } @@ -1002,7 +1007,8 @@ bool s1ap::send_ho_req_ack(const asn1::s1ap::ho_request_s& msg, bool s1ap::handle_mme_status_transfer(const asn1::s1ap::mme_status_transfer_s& msg) { - ue* u = find_s1apmsg_user(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = + handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } @@ -1025,7 +1031,7 @@ void s1ap::send_ho_notify(uint16_t rnti, uint64_t target_eci) tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_HO_NOTIF); ho_notify_ies_container& container = tx_pdu.init_msg().value.ho_notify().protocol_ies; - container.mme_ue_s1ap_id.value = user_ptr->ctxt.mme_ue_s1ap_id; + container.mme_ue_s1ap_id.value = user_ptr->ctxt.mme_ue_s1ap_id.value(); container.enb_ue_s1ap_id.value = user_ptr->ctxt.enb_ue_s1ap_id; container.eutran_cgi.value = eutran_cgi; @@ -1047,7 +1053,7 @@ void s1ap::send_ho_cancel(uint16_t rnti) tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_HO_CANCEL); ho_cancel_ies_container& container = tx_pdu.init_msg().value.ho_cancel().protocol_ies; - container.mme_ue_s1ap_id.value = user_ptr->ctxt.mme_ue_s1ap_id; + container.mme_ue_s1ap_id.value = user_ptr->ctxt.mme_ue_s1ap_id.value(); container.enb_ue_s1ap_id.value = user_ptr->ctxt.enb_ue_s1ap_id; container.cause.value.set_radio_network().value = cause_radio_network_opts::ho_cancelled; @@ -1072,7 +1078,9 @@ bool s1ap::send_ue_cap_info_indication(uint16_t rnti, srsran::unique_byte_buffer return user_ptr->send_ue_cap_info_indication(std::move(ue_radio_cap)); } -bool s1ap::send_error_indication(uint16_t rnti, const asn1::s1ap::cause_c& cause) +bool s1ap::send_error_indication(const asn1::s1ap::cause_c& cause, + srsran::optional enb_ue_s1ap_id, + srsran::optional mme_ue_s1ap_id) { if (not mme_connected) { return false; @@ -1082,17 +1090,16 @@ bool s1ap::send_error_indication(uint16_t rnti, const asn1::s1ap::cause_c& cause tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_ERROR_IND); auto& container = tx_pdu.init_msg().value.error_ind().protocol_ies; - if (rnti != SRSRAN_INVALID_RNTI) { - ue* user_ptr = users.find_ue_rnti(rnti); - if (user_ptr == nullptr) { - return false; - } - container.enb_ue_s1ap_id_present = true; - container.enb_ue_s1ap_id.value = user_ptr->ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id_present = user_ptr->ctxt.mme_ue_s1ap_id_present; - if (user_ptr->ctxt.mme_ue_s1ap_id_present) { - container.mme_ue_s1ap_id.value = user_ptr->ctxt.mme_ue_s1ap_id; - } + uint16_t rnti = SRSRAN_INVALID_RNTI; + container.enb_ue_s1ap_id_present = enb_ue_s1ap_id.has_value(); + if (enb_ue_s1ap_id.has_value()) { + container.enb_ue_s1ap_id.value = enb_ue_s1ap_id.value(); + ue* user_ptr = users.find_ue_enbid(enb_ue_s1ap_id.value()); + rnti = user_ptr != nullptr ? user_ptr->ctxt.rnti : SRSRAN_INVALID_RNTI; + } + container.mme_ue_s1ap_id_present = mme_ue_s1ap_id.has_value(); + if (mme_ue_s1ap_id.has_value()) { + container.mme_ue_s1ap_id.value = mme_ue_s1ap_id.value(); } container.s_tmsi_present = false; @@ -1156,7 +1163,7 @@ bool s1ap::ue::send_ulnastransport(srsran::unique_byte_buffer_t pdu) s1ap_pdu_c tx_pdu; tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_UL_NAS_TRANSPORT); asn1::s1ap::ul_nas_transport_ies_container& container = tx_pdu.init_msg().value.ul_nas_transport().protocol_ies; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; + container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; // NAS PDU @@ -1178,7 +1185,7 @@ bool s1ap::ue::send_uectxtreleaserequest(const cause_c& cause) return false; } - if (!ctxt.mme_ue_s1ap_id_present) { + if (not ctxt.mme_ue_s1ap_id.has_value()) { logger.error("Cannot send UE context release request without a MME-UE-S1AP-Id allocated."); return false; } @@ -1188,7 +1195,7 @@ bool s1ap::ue::send_uectxtreleaserequest(const cause_c& cause) tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_UE_CONTEXT_RELEASE_REQUEST); ue_context_release_request_ies_container& container = tx_pdu.init_msg().value.ue_context_release_request().protocol_ies; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; + container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; // Cause @@ -1207,10 +1214,10 @@ bool s1ap::ue::send_uectxtreleasecomplete() tx_pdu.set_successful_outcome().load_info_obj(ASN1_S1AP_ID_UE_CONTEXT_RELEASE); auto& container = tx_pdu.successful_outcome().value.ue_context_release_complete().protocol_ies; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; + container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); // Log event. - event_logger::get().log_s1_ctx_delete(ctxt.enb_cc_idx, ctxt.mme_ue_s1ap_id, ctxt.enb_ue_s1ap_id, ctxt.rnti); + event_logger::get().log_s1_ctx_delete(ctxt.enb_cc_idx, ctxt.mme_ue_s1ap_id.value(), ctxt.enb_ue_s1ap_id, ctxt.rnti); return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextReleaseComplete"); } @@ -1229,7 +1236,7 @@ bool s1ap::ue::send_initial_ctxt_setup_response(const asn1::s1ap::init_context_s // Fill in the MME and eNB IDs auto& container = tx_pdu.successful_outcome().value.init_context_setup_resp().protocol_ies; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; + container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; // Fill in the GTP bind address for all bearers @@ -1244,7 +1251,7 @@ bool s1ap::ue::send_initial_ctxt_setup_response(const asn1::s1ap::init_context_s } // Log event. - event_logger::get().log_s1_ctx_create(ctxt.enb_cc_idx, ctxt.mme_ue_s1ap_id, ctxt.enb_ue_s1ap_id, ctxt.rnti); + event_logger::get().log_s1_ctx_create(ctxt.enb_cc_idx, ctxt.mme_ue_s1ap_id.value(), ctxt.enb_ue_s1ap_id, ctxt.rnti); return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "InitialContextSetupResponse"); } @@ -1275,7 +1282,7 @@ bool s1ap::ue::send_erab_setup_response(const erab_setup_resp_s& res_) } // Fill in the MME and eNB IDs - res.protocol_ies.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; + res.protocol_ies.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); res.protocol_ies.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "E_RABSetupResponse"); @@ -1292,7 +1299,7 @@ bool s1ap::ue::send_initial_ctxt_setup_failure() auto& container = tx_pdu.unsuccessful_outcome().value.init_context_setup_fail().protocol_ies; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; + container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); container.cause.value.set_radio_network().value = cause_radio_network_opts::unspecified; return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "InitialContextSetupFailure"); @@ -1309,7 +1316,7 @@ bool s1ap::ue::send_uectxtmodifyresp() auto& container = tx_pdu.successful_outcome().value.ue_context_mod_resp().protocol_ies; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; + container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextModificationResponse"); } @@ -1325,7 +1332,7 @@ bool s1ap::ue::send_uectxtmodifyfailure(const cause_c& cause) auto& container = tx_pdu.unsuccessful_outcome().value.ue_context_mod_fail().protocol_ies; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; + container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); container.cause.value = cause; return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextModificationFailure"); @@ -1350,7 +1357,7 @@ bool s1ap::ue::send_erab_release_response(const std::vector& erabs_suc auto& container = tx_pdu.successful_outcome().value.erab_release_resp().protocol_ies; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; + container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); // Fill in which E-RABs were successfully released if (not erabs_successfully_released.empty()) { @@ -1377,7 +1384,7 @@ bool s1ap::ue::send_erab_release_response(const std::vector& erabs_suc } } - return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "E_RABReleaseResponse"); + return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "E-RABReleaseResponse"); } bool s1ap::ue::send_erab_modify_response(const std::vector& erabs_successfully_modified, @@ -1392,7 +1399,7 @@ bool s1ap::ue::send_erab_modify_response(const std::vector& erabs_succ auto& container = tx_pdu.successful_outcome().value.erab_modify_resp().protocol_ies; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; + container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); // Fill in which E-RABs were successfully released if (not erabs_successfully_modified.empty()) { @@ -1412,13 +1419,14 @@ bool s1ap::ue::send_erab_modify_response(const std::vector& erabs_succ for (uint32_t i = 0; i < container.erab_failed_to_modify_list.value.size(); i++) { container.erab_failed_to_modify_list.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_ITEM); container.erab_failed_to_modify_list.value[i].value.erab_item().erab_id = erabs_failed_to_modify[i]; - container.erab_failed_to_modify_list.value[i].value.erab_item().cause.set(asn1::s1ap::cause_c::types::misc); - container.erab_failed_to_modify_list.value[i].value.erab_item().cause.misc() = - asn1::s1ap::cause_misc_opts::unspecified; + container.erab_failed_to_modify_list.value[i].value.erab_item().cause.set( + asn1::s1ap::cause_c::types_opts::radio_network); + container.erab_failed_to_modify_list.value[i].value.erab_item().cause.radio_network().value = + cause_radio_network_opts::unknown_erab_id; } } - return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "E_RABReleaseResponse"); + return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "E-RABModifyResponse"); } bool s1ap::ue::send_erab_release_indication(const std::vector& erabs_successfully_released) @@ -1436,7 +1444,7 @@ bool s1ap::ue::send_erab_release_indication(const std::vector& erabs_s erab_release_ind_ies_container& container = tx_pdu.init_msg().value.erab_release_ind().protocol_ies; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; + container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); // Fill in which E-RABs were successfully released container.erab_released_list.value.resize(erabs_successfully_released.size()); @@ -1459,7 +1467,7 @@ bool s1ap::ue::send_ue_cap_info_indication(srsran::unique_byte_buffer_t ue_radio ue_cap_info_ind_ies_container& container = tx_pdu.init_msg().value.ue_cap_info_ind().protocol_ies; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; + container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); container.ue_radio_cap.value.resize(ue_radio_cap->N_bytes); memcpy(container.ue_radio_cap.value.data(), ue_radio_cap->msg, ue_radio_cap->N_bytes); @@ -1529,7 +1537,7 @@ s1ap::ue* s1ap::user_list::find_ue_enbid(uint32_t enbid) s1ap::ue* s1ap::user_list::find_ue_mmeid(uint32_t mmeid) { auto it = std::find_if(users.begin(), users.end(), [mmeid](const user_list::pair_type& v) { - return v.second->ctxt.mme_ue_s1ap_id_present and v.second->ctxt.mme_ue_s1ap_id == mmeid; + return v.second->ctxt.mme_ue_s1ap_id == mmeid; }); return it != users.end() ? it->second.get() : nullptr; } @@ -1551,8 +1559,8 @@ s1ap::ue* s1ap::user_list::add_user(std::unique_ptr user) logger.error("The user to be added with enb id=%d already exists", user->ctxt.enb_ue_s1ap_id); return nullptr; } - if (find_ue_mmeid(user->ctxt.mme_ue_s1ap_id) != nullptr) { - logger.error("The user to be added with mme id=%d already exists", user->ctxt.mme_ue_s1ap_id); + if (user->ctxt.mme_ue_s1ap_id.has_value() and find_ue_mmeid(user->ctxt.mme_ue_s1ap_id.value()) != nullptr) { + logger.error("The user to be added with mme id=%d already exists", user->ctxt.mme_ue_s1ap_id.value()); return nullptr; } auto p = users.insert(std::make_pair(user->ctxt.enb_ue_s1ap_id, std::move(user))); @@ -1591,9 +1599,9 @@ bool s1ap::sctp_send_s1ap_pdu(const asn1::s1ap::s1ap_pdu_c& tx_pdu, uint32_t rnt } if (rnti != SRSRAN_INVALID_RNTI) { - logger.info(buf->msg, buf->N_bytes, "Sending %s for rnti=0x%x", procedure_name, rnti); + logger.info(buf->msg, buf->N_bytes, "Tx S1AP SDU, %s, rnti=0x%x", procedure_name, rnti); } else { - logger.info(buf->msg, buf->N_bytes, "Sending %s to MME", procedure_name); + logger.info(buf->msg, buf->N_bytes, "Tx S1AP SDU, %s", procedure_name); } uint16_t streamid = rnti == SRSRAN_INVALID_RNTI ? NONUE_STREAM_ID : users.find_ue_rnti(rnti)->stream_id; @@ -1608,10 +1616,10 @@ bool s1ap::sctp_send_s1ap_pdu(const asn1::s1ap::s1ap_pdu_c& tx_pdu, uint32_t rnt 0, 0); if (n_sent == -1) { - if (rnti > 0) { - logger.error("Failed to send %s for rnti=0x%x", procedure_name, rnti); + if (rnti != SRSRAN_INVALID_RNTI) { + logger.error("Error: Failure at Tx S1AP SDU, %s, rnti=0x%x", procedure_name, rnti); } else { - logger.error("Failed to send %s", procedure_name); + logger.error("Error: Failure at Tx S1AP SDU, %s", procedure_name); } return false; } @@ -1624,28 +1632,51 @@ bool s1ap::sctp_send_s1ap_pdu(const asn1::s1ap::s1ap_pdu_c& tx_pdu, uint32_t rnt * @param mme_id mme_ue_s1ap_id value stored in S1AP message * @return pointer to user if it has been found */ -s1ap::ue* s1ap::find_s1apmsg_user(uint32_t enb_id, uint32_t mme_id) +s1ap::ue* s1ap::handle_s1apmsg_ue_id(uint32_t enb_id, uint32_t mme_id) { - ue* user_ptr = users.find_ue_enbid(enb_id); + ue* user_ptr = users.find_ue_enbid(enb_id); + ue* user_mme_ptr = nullptr; cause_c cause; if (user_ptr != nullptr) { - if (not user_ptr->ctxt.mme_ue_s1ap_id_present) { - user_ptr->ctxt.mme_ue_s1ap_id_present = true; - user_ptr->ctxt.mme_ue_s1ap_id = mme_id; + if (user_ptr->ctxt.mme_ue_s1ap_id == mme_id) { + // No ID inconsistency found return user_ptr; - } else if (user_ptr->ctxt.mme_ue_s1ap_id == mme_id) { - return user_ptr; - } else { - logger.warning("MME UE S1AP ID=%d not found - discarding message", enb_id); - cause.set_radio_network().value = cause_radio_network_opts::unknown_mme_ue_s1ap_id; } + + user_mme_ptr = users.find_ue_mmeid(mme_id); + if (not user_ptr->ctxt.mme_ue_s1ap_id.has_value() and user_mme_ptr == nullptr) { + // First "returned message", no inconsistency found (see 36.413, Section 10.6) + user_ptr->ctxt.mme_ue_s1ap_id = mme_id; + return user_ptr; + } + + // TS 36.413, Sec. 10.6 - If a node receives a first returned message that includes a remote AP ID (...) + + logger.warning("MME UE S1AP ID=%d not found - discarding message", mme_id); + cause.set_radio_network().value = user_mme_ptr != nullptr ? cause_radio_network_opts::unknown_mme_ue_s1ap_id + : cause_radio_network_opts::unknown_pair_ue_s1ap_id; } else { + // TS 36.413, Sec. 10.6 - If a node receives a message (other than the first or first returned messages) that + // includes AP ID(s) identifying (...) + user_mme_ptr = users.find_ue_mmeid(mme_id); + logger.warning("ENB UE S1AP ID=%d not found - discarding message", enb_id); - cause.set_radio_network().value = users.find_ue_mmeid(mme_id) != nullptr - ? cause_radio_network_opts::unknown_enb_ue_s1ap_id - : cause_radio_network_opts::unknown_pair_ue_s1ap_id; + cause.set_radio_network().value = user_mme_ptr != nullptr ? cause_radio_network_opts::unknown_enb_ue_s1ap_id + : cause_radio_network_opts::unknown_pair_ue_s1ap_id; + } + + // the node shall initiate an Error Indication procedure with inclusion of the received AP ID(s) from the peer node + // and an appropriate cause value. + send_error_indication(cause, enb_id, mme_id); + + // Both nodes shall initiate a local release of any established UE-associated logical connection (for the same S1 + // interface) having the erroneous AP ID(s) as local or remote identifier. + if (user_ptr != nullptr) { + rrc->release_ue(user_ptr->ctxt.rnti); + } + if (user_mme_ptr != nullptr and user_mme_ptr != user_ptr) { + rrc->release_ue(user_mme_ptr->ctxt.rnti); } - send_error_indication(SRSRAN_INVALID_RNTI, cause); return nullptr; } @@ -1711,11 +1742,10 @@ bool s1ap::ue::send_ho_required(uint32_t target_eci, /*** fill HO Required message ***/ container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; + container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); container.direct_forwarding_path_availability_present = false; // NOTE: X2 for fwd path not supported container.handov_type.value.value = handov_type_opts::intralte; // NOTE: only intra-LTE HO supported - container.cause.value.set_radio_network().value = cause_radio_network_opts::unspecified; - // LIBLTE_S1AP_CAUSERADIONETWORK_S1_INTRA_SYSTEM_HANDOVER_TRIGGERED; + container.cause.value.set_radio_network().value = cause_radio_network_opts::s1_intra_sys_ho_triggered; /*** set the target eNB ***/ container.csg_id_present = false; // NOTE: CSG/hybrid target cell not supported @@ -1798,7 +1828,7 @@ bool s1ap::ue::send_enb_status_transfer_proc(std::vector& be enb_status_transfer_ies_container& container = tx_pdu.init_msg().value.enb_status_transfer().protocol_ies; container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id; + container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); /* Create StatusTransfer transparent container with all the bearer ctxt to transfer */ auto& list = container.enb_status_transfer_transparent_container.value.bearers_subject_to_status_transfer_list; diff --git a/srsenb/test/common/dummy_classes.h b/srsenb/test/common/dummy_classes.h index 622a33796..4d54ad7dc 100644 --- a/srsenb/test/common/dummy_classes.h +++ b/srsenb/test/common/dummy_classes.h @@ -22,6 +22,7 @@ #ifndef SRSENB_DUMMY_CLASSES_H #define SRSENB_DUMMY_CLASSES_H +#include "srsran/interfaces/enb_gtpu_interfaces.h" #include "srsran/interfaces/enb_interfaces.h" #include "srsran/interfaces/enb_mac_interfaces.h" #include "srsran/interfaces/enb_pdcp_interfaces.h" @@ -154,13 +155,13 @@ public: void complete_config(uint16_t rnti) override{}; }; -class gtpu_dummy : public gtpu_interface_rrc +class gtpu_dummy : public srsenb::gtpu_interface_rrc { public: - uint32_t + srsran::expected add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, const bearer_props* props) override { - return 0; + return 1; } void set_tunnel_status(uint32_t teidin, bool dl_active) override {} void rem_bearer(uint16_t rnti, uint32_t lcid) override {} @@ -168,6 +169,40 @@ public: void rem_user(uint16_t rnti) override {} }; +class rrc_dummy : public rrc_interface_s1ap +{ +public: + void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) override {} + void release_ue(uint16_t rnti) override {} + bool setup_ue_ctxt(uint16_t rnti, const asn1::s1ap::init_context_setup_request_s& msg) override { return true; } + bool modify_ue_ctxt(uint16_t rnti, const asn1::s1ap::ue_context_mod_request_s& msg) override { return true; } + bool setup_ue_erabs(uint16_t rnti, const asn1::s1ap::erab_setup_request_s& msg) override { return true; } + void modify_erabs(uint16_t rnti, + const asn1::s1ap::erab_modify_request_s& msg, + std::vector* erabs_modified, + std::vector* erabs_failed_to_modify) override + {} + bool release_erabs(uint32_t rnti) override { return true; } + void release_erabs(uint32_t rnti, + const asn1::s1ap::erab_release_cmd_s& msg, + std::vector* erabs_released, + std::vector* erabs_failed_to_release) override + {} + void add_paging_id(uint32_t ueid, const asn1::s1ap::ue_paging_id_c& ue_paging_id) override {} + void ho_preparation_complete(uint16_t rnti, + bool is_success, + const asn1::s1ap::ho_cmd_s& msg, + srsran::unique_byte_buffer_t container) override + {} + uint16_t + start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg, + const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container) override + { + return SRSRAN_INVALID_RNTI; + } + void set_erab_status(uint16_t rnti, const asn1::s1ap::bearers_subject_to_status_transfer_list_l& erabs) override {} +}; + } // namespace srsenb #endif // SRSENB_DUMMY_CLASSES_H diff --git a/srsenb/test/mac/sched_common_test_suite.h b/srsenb/test/mac/sched_common_test_suite.h index 0cfa0ac17..b3ad54ccd 100644 --- a/srsenb/test/mac/sched_common_test_suite.h +++ b/srsenb/test/mac/sched_common_test_suite.h @@ -24,6 +24,7 @@ #include "srsenb/hdr/stack/mac/sched_common.h" #include "srsran/adt/bounded_bitset.h" +#include "srsran/adt/span.h" #include "srsran/common/tti_point.h" #include "srsran/interfaces/sched_interface.h" diff --git a/srsenb/test/mac/sched_test_common.h b/srsenb/test/mac/sched_test_common.h index c7a63d723..44a5f7890 100644 --- a/srsenb/test/mac/sched_test_common.h +++ b/srsenb/test/mac/sched_test_common.h @@ -43,7 +43,7 @@ struct rrc_dummy : public rrc_interface_mac { public: int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) { return SRSRAN_SUCCESS; } void upd_user(uint16_t new_rnti, uint16_t old_rnti) {} - void set_activity_user(uint16_t rnti) {} + void set_activity_user(uint16_t rnti, bool ack_info) {} bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) { return false; } uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) { return nullptr; } }; diff --git a/srsenb/test/upper/CMakeLists.txt b/srsenb/test/upper/CMakeLists.txt index 5cfe0ba68..a7150be22 100644 --- a/srsenb/test/upper/CMakeLists.txt +++ b/srsenb/test/upper/CMakeLists.txt @@ -37,6 +37,9 @@ target_link_libraries(rrc_meascfg_test test_helpers) add_executable(gtpu_test gtpu_test.cc) target_link_libraries(gtpu_test srsran_common s1ap_asn1 srsenb_upper srsran_upper ${SCTP_LIBRARIES}) +add_executable(s1ap_test s1ap_test.cc) +target_link_libraries(s1ap_test srsran_common s1ap_asn1 srsenb_upper srsran_upper s1ap_asn1 ${SCTP_LIBRARIES}) + add_test(rrc_mobility_test rrc_mobility_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) add_test(erab_setup_test erab_setup_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) add_test(rrc_meascfg_test rrc_meascfg_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) diff --git a/srsenb/test/upper/gtpu_test.cc b/srsenb/test/upper/gtpu_test.cc index cfa6e0146..c110071c9 100644 --- a/srsenb/test/upper/gtpu_test.cc +++ b/srsenb/test/upper/gtpu_test.cc @@ -37,7 +37,7 @@ static const size_t PDU_HEADER_SIZE = 20; class stack_tester : public stack_interface_gtpu_lte { public: - int s1u_fd; + int s1u_fd = -1; void add_gtpu_s1u_socket_handler(int fd) { s1u_fd = fd; } void add_gtpu_m1u_socket_handler(int fd) {} }; @@ -136,8 +136,68 @@ srsran::unique_byte_buffer_t read_socket(int fd) return pdu; } -int test_gtpu_direct_tunneling() +void test_gtpu_tunnel_manager() { + const char* sgw_addr_str = "127.0.0.1"; + struct sockaddr_in sgw_sockaddr = {}; + srsran::net_utils::set_sockaddr(&sgw_sockaddr, sgw_addr_str, GTPU_PORT); + uint32_t sgw_addr = ntohl(sgw_sockaddr.sin_addr.s_addr); + const uint32_t drb1_lcid = 3; + srsran::task_scheduler task_sched; + + gtpu_tunnel_manager tunnels(&task_sched, srslog::fetch_basic_logger("GTPU")); + TESTASSERT(tunnels.find_tunnel(0) == nullptr); + TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).empty()); + TESTASSERT(tunnels.find_rnti_tunnels(0x46) == nullptr); + + // Creation of tunnels for different users and lcids + const gtpu_tunnel* tun = tunnels.add_tunnel(0x46, drb1_lcid, 5, sgw_addr); + TESTASSERT(tun != nullptr); + TESTASSERT(tunnels.find_tunnel(tun->teid_in) == tun); + const gtpu_tunnel* tun2 = tunnels.add_tunnel(0x47, drb1_lcid, 6, sgw_addr); + TESTASSERT(tun2 != nullptr); + TESTASSERT(tunnels.find_tunnel(tun2->teid_in) == tun2); + tun2 = tunnels.add_tunnel(0x47, drb1_lcid + 1, 7, sgw_addr); + TESTASSERT(tun2 != nullptr); + TESTASSERT(tunnels.find_tunnel(tun2->teid_in) == tun2); + TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 1); + TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x47, drb1_lcid).size() == 1); + TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x47, drb1_lcid + 1).size() == 1); + + // TEST: Creation/Removal of indirect tunnel + const gtpu_tunnel* fwd_tun = tunnels.add_tunnel(0x46, drb1_lcid, 8, sgw_addr); + TESTASSERT(fwd_tun != nullptr); + TESTASSERT(tunnels.find_tunnel(fwd_tun->teid_in) == fwd_tun); + tunnels.setup_forwarding(tun->teid_in, fwd_tun->teid_in); + TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 2); + // Removing a tunnel also clears any associated forwarding tunnel + TESTASSERT(tunnels.remove_tunnel(tun->teid_in)); + TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).empty()); + + // TEST: Prioritization of one TEID over another + const gtpu_tunnel* before_tun = tunnels.add_tunnel(0x46, drb1_lcid, 7, sgw_addr); + const gtpu_tunnel* after_tun = tunnels.add_tunnel(0x46, drb1_lcid, 8, sgw_addr); + TESTASSERT(before_tun != nullptr and after_tun != nullptr); + tunnels.set_tunnel_priority(before_tun->teid_in, after_tun->teid_in); + for (uint32_t i = 0; i < 1000; ++i) { + TESTASSERT(before_tun->state == gtpu_tunnel_manager::tunnel_state::pdcp_active); + TESTASSERT(after_tun->state == gtpu_tunnel_manager::tunnel_state::buffering); + // while Rx packets are received, active forwarding TEID should not be removed + tunnels.handle_rx_pdcp_sdu(before_tun->teid_in); + } + // Removing active TEID, will automatically switch TEID paths + TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 2); + tunnels.remove_tunnel(before_tun->teid_in); + TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 1); + TESTASSERT(after_tun->state == gtpu_tunnel_manager::tunnel_state::pdcp_active); +} + +enum class tunnel_test_event { success, wait_end_marker_timeout }; + +int test_gtpu_direct_tunneling(tunnel_test_event event) +{ + srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST"); + logger.info("\n\n**** Test GTPU Direct Tunneling ****\n"); uint16_t rnti = 0x46, rnti2 = 0x50; uint32_t drb1 = 3; uint32_t sgw_teidout1 = 1, sgw_teidout2 = 2; @@ -165,8 +225,8 @@ int test_gtpu_direct_tunneling() tenb_gtpu.init(tenb_addr_str, sgw_addr_str, "", "", &tenb_pdcp, &tenb_stack, false); // create tunnels MME-SeNB and MME-TeNB - uint32_t senb_teid_in = senb_gtpu.add_bearer(rnti, drb1, sgw_addr, sgw_teidout1); - uint32_t tenb_teid_in = tenb_gtpu.add_bearer(rnti2, drb1, sgw_addr, sgw_teidout2); + uint32_t senb_teid_in = senb_gtpu.add_bearer(rnti, drb1, sgw_addr, sgw_teidout1).value(); + uint32_t tenb_teid_in = tenb_gtpu.add_bearer(rnti2, drb1, sgw_addr, sgw_teidout2).value(); // Buffer PDUs in SeNB PDCP for (size_t sn = 6; sn < 10; ++sn) { @@ -179,7 +239,7 @@ int test_gtpu_direct_tunneling() gtpu::bearer_props props; props.flush_before_teidin_present = true; props.flush_before_teidin = tenb_teid_in; - uint32_t dl_tenb_teid_in = tenb_gtpu.add_bearer(rnti2, drb1, senb_addr, 0, &props); + uint32_t dl_tenb_teid_in = tenb_gtpu.add_bearer(rnti2, drb1, senb_addr, 0, &props).value(); props = {}; props.forward_from_teidin_present = true; props.forward_from_teidin = senb_teid_in; @@ -245,10 +305,19 @@ int test_gtpu_direct_tunneling() TESTASSERT(tenb_pdcp.last_sdu->N_bytes == encoded_data.size() and memcmp(tenb_pdcp.last_sdu->msg, encoded_data.data(), encoded_data.size()) == 0); tenb_pdcp.clear(); - // EndMarker is forwarded via MME->SeNB->TeNB, and TeNB buffered PDUs are flushed - pdu = encode_end_marker(senb_teid_in); - senb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), sgw_sockaddr); - tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr); + + TESTASSERT(tenb_pdcp.last_sdu == nullptr); + if (event == tunnel_test_event::wait_end_marker_timeout) { + // TEST: EndMarker does not reach TeNB, but there is a timeout that will resume the new GTPU tunnel + for (size_t i = 0; i < 1000; ++i) { + task_sched.tic(); + } + } else { + // TEST: EndMarker is forwarded via MME->SeNB->TeNB, and TeNB buffered PDUs are flushed + pdu = encode_end_marker(senb_teid_in); + senb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), sgw_sockaddr); + tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_stack.s1u_fd), senb_sockaddr); + } srsran::span encoded_data2{tenb_pdcp.last_sdu->msg + 20u, tenb_pdcp.last_sdu->msg + 30u}; TESTASSERT(std::all_of(encoded_data2.begin(), encoded_data2.end(), [N_pdus](uint8_t b) { return b == N_pdus - 1; })); @@ -257,7 +326,7 @@ int test_gtpu_direct_tunneling() } // namespace srsenb -int main() +int main(int argc, char** argv) { // Setup logging. auto& logger = srslog::fetch_basic_logger("GTPU", false); @@ -265,9 +334,11 @@ int main() logger.set_hex_dump_max_size(-1); // Start the log backend. - srslog::init(); + srsran::test_init(argc, argv); - TESTASSERT(srsenb::test_gtpu_direct_tunneling() == SRSRAN_SUCCESS); + srsenb::test_gtpu_tunnel_manager(); + TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::success) == SRSRAN_SUCCESS); + TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::wait_end_marker_timeout) == SRSRAN_SUCCESS); srslog::flush(); diff --git a/srsenb/test/upper/s1ap_test.cc b/srsenb/test/upper/s1ap_test.cc new file mode 100644 index 000000000..76106313a --- /dev/null +++ b/srsenb/test/upper/s1ap_test.cc @@ -0,0 +1,269 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsenb/hdr/stack/upper/s1ap.h" +#include "srsenb/test/common/dummy_classes.h" +#include "srsran/common/network_utils.h" +#include "srsran/common/test_common.h" + +using namespace srsenb; + +class stack_dummy : public srsenb::stack_interface_s1ap_lte +{ +public: + void add_mme_socket(int fd) {} + void remove_mme_socket(int fd) {} +}; + +struct mme_dummy { + mme_dummy(const char* addr_str_, int port_) : addr_str(addr_str_), port(port_) + { + srsran::net_utils::set_sockaddr(&mme_sockaddr, addr_str, port); + { + using namespace srsran::net_utils; + fd = open_socket(addr_family::ipv4, socket_type::seqpacket, protocol_type::SCTP); + TESTASSERT(fd > 0); + TESTASSERT(bind_addr(fd, mme_sockaddr)); + } + + srsran_assert(listen(fd, SOMAXCONN) == 0, "Failed to listen to incoming SCTP connections"); + } + + ~mme_dummy() + { + if (fd > 0) { + close(fd); + } + } + + srsran::unique_byte_buffer_t read_msg(sockaddr_in* sockfrom = nullptr) + { + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + sockaddr_in from = {}; + socklen_t fromlen = sizeof(from); + sctp_sndrcvinfo sri = {}; + int flags = 0; + ssize_t n_recv = sctp_recvmsg(fd, pdu->msg, pdu->get_tailroom(), (struct sockaddr*)&from, &fromlen, &sri, &flags); + if (n_recv > 0) { + if (sockfrom != nullptr) { + *sockfrom = from; + } + pdu->N_bytes = n_recv; + } + return pdu; + } + + const char* addr_str; + int port; + struct sockaddr_in mme_sockaddr = {}; + int fd; + srsran::unique_byte_buffer_t last_sdu; +}; + +struct rrc_tester : public rrc_dummy { + void modify_erabs(uint16_t rnti, + const asn1::s1ap::erab_modify_request_s& msg, + std::vector* erabs_modified, + std::vector* erabs_failed_to_modify) override + { + *erabs_modified = next_erabs_modified; + *erabs_failed_to_modify = next_erabs_failed_to_modify; + } + void release_ue(uint16_t rnti) override { last_released_rnti = rnti; } + + uint16_t last_released_rnti = SRSRAN_INVALID_RNTI; + std::vector next_erabs_modified, next_erabs_failed_to_modify; +}; + +void run_s1_setup(s1ap& s1ap_obj, mme_dummy& mme) +{ + asn1::s1ap::s1ap_pdu_c s1ap_pdu; + + // eNB -> MME: S1 Setup Request + srsran::unique_byte_buffer_t sdu = mme.read_msg(); + TESTASSERT(sdu->N_bytes > 0); + asn1::cbit_ref cbref(sdu->msg, sdu->N_bytes); + TESTASSERT(s1ap_pdu.unpack(cbref) == asn1::SRSASN_SUCCESS); + TESTASSERT(s1ap_pdu.type().value == asn1::s1ap::s1ap_pdu_c::types_opts::init_msg); + TESTASSERT(s1ap_pdu.init_msg().proc_code == ASN1_S1AP_ID_S1_SETUP); + + // MME -> eNB: S1 Setup Response + sockaddr_in mme_addr = {}; + sctp_sndrcvinfo rcvinfo = {}; + int flags = 0; + uint8_t s1_setup_resp[] = {0x20, 0x11, 0x00, 0x25, 0x00, 0x00, 0x03, 0x00, 0x3d, 0x40, 0x0a, 0x03, 0x80, 0x73, + 0x72, 0x73, 0x6d, 0x6d, 0x65, 0x30, 0x31, 0x00, 0x69, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0xf1, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1a, 0x00, 0x57, 0x40, 0x01, 0xff}; + memcpy(sdu->msg, s1_setup_resp, sizeof(s1_setup_resp)); + sdu->N_bytes = sizeof(s1_setup_resp); + TESTASSERT(s1ap_obj.handle_mme_rx_msg(std::move(sdu), mme_addr, rcvinfo, flags)); +} + +void add_rnti(s1ap& s1ap_obj, mme_dummy& mme) +{ + asn1::s1ap::s1ap_pdu_c s1ap_pdu; + + // New UE + uint8_t nas_msg[] = {0x00, 0x1a, 0x00, 0x21, 0x20, 0x17, 0x82, 0xa8, 0x64, 0x46, 0x04, 0x07, 0x41, + 0x01, 0x0b, 0xf6, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x1a, 0x5e, 0xa4, 0x54, 0x47, + 0x02, 0xf0, 0x70, 0x00, 0x04, 0x02, 0x01, 0xd0, 0x11, 0x91, 0xe0}; + + srsran::unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + memcpy(sdu->msg, nas_msg, sizeof(nas_msg)); + sdu->N_bytes = sizeof(nas_msg); + s1ap_obj.initial_ue(0x46, 0, asn1::s1ap::rrc_establishment_cause_opts::mo_data, std::move(sdu)); + sdu = mme.read_msg(); + TESTASSERT(sdu->N_bytes > 0); + asn1::cbit_ref cbref{sdu->msg, sdu->N_bytes}; + TESTASSERT(s1ap_pdu.unpack(cbref) == SRSRAN_SUCCESS); + TESTASSERT(s1ap_pdu.type().value == asn1::s1ap::s1ap_pdu_c::types_opts::init_msg); + TESTASSERT(s1ap_pdu.init_msg().proc_code == ASN1_S1AP_ID_INIT_UE_MSG); + + // InitialContextSetupRequest (skip all NAS exchange) + sockaddr_in mme_addr = {}; + sctp_sndrcvinfo rcvinfo = {}; + int flags = 0; + uint8_t icsr_msg[] = { + 0x00, 0x09, 0x00, 0x80, 0xac, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x08, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x42, 0x00, 0x0a, 0x18, 0x3b, 0x9a, 0xca, 0x00, 0x60, 0x3b, 0x9a, 0xca, 0x00, 0x00, 0x18, + 0x00, 0x5e, 0x00, 0x00, 0x34, 0x00, 0x59, 0x45, 0x00, 0x09, 0x3c, 0x0f, 0x80, 0x7f, 0x00, 0x01, 0x64, 0x00, + 0x00, 0x00, 0x01, 0x4a, 0x27, 0x9b, 0x6d, 0xe9, 0x42, 0x01, 0x07, 0x42, 0x01, 0x3e, 0x06, 0x00, 0x00, 0xf1, + 0x10, 0x00, 0x07, 0x00, 0x1d, 0x52, 0x01, 0xc1, 0x01, 0x09, 0x07, 0x06, 0x73, 0x72, 0x73, 0x61, 0x70, 0x6e, + 0x05, 0x01, 0xc0, 0xa8, 0x0a, 0x02, 0x27, 0x08, 0x80, 0x00, 0x0d, 0x04, 0x08, 0x08, 0x08, 0x08, 0x50, 0x0b, + 0xf6, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x1a, 0x32, 0xdd, 0x59, 0x35, 0x13, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x23, + 0x05, 0xf4, 0x32, 0xdd, 0x59, 0x35, 0x00, 0x6b, 0x00, 0x05, 0x18, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x49, 0x00, + 0x20, 0x84, 0xa4, 0xea, 0x15, 0x55, 0xb3, 0xe0, 0xf4, 0x55, 0xbe, 0x1f, 0x41, 0x52, 0x92, 0xfc, 0x04, 0xd8, + 0x02, 0x38, 0x0d, 0xe0, 0x81, 0x29, 0xe1, 0xaa, 0xd7, 0xc4, 0x7b, 0x12, 0x95, 0x72, 0xbe}; + sdu = srsran::make_byte_buffer(); + memcpy(sdu->msg, icsr_msg, sizeof(icsr_msg)); + sdu->N_bytes = sizeof(icsr_msg); + TESTASSERT(s1ap_obj.handle_mme_rx_msg(std::move(sdu), mme_addr, rcvinfo, flags)); + + // InitialContextSetupResponse + uint8_t icsresp[] = {0x20, 0x09, 0x00, 0x22, 0x00, 0x00, 0x03, 0x00, 0x00, 0x40, 0x02, 0x00, 0x01, + 0x00, 0x08, 0x40, 0x02, 0x00, 0x01, 0x00, 0x33, 0x40, 0x0f, 0x00, 0x00, 0x32, + 0x40, 0x0a, 0x0a, 0x1f, 0x7f, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01}; + cbref = asn1::cbit_ref(icsresp, sizeof(icsresp)); + TESTASSERT(s1ap_pdu.unpack(cbref) == SRSRAN_SUCCESS); + s1ap_obj.ue_ctxt_setup_complete(0x46, s1ap_pdu.successful_outcome().value.init_context_setup_resp()); + sdu = mme.read_msg(); + TESTASSERT(sdu->N_bytes > 0); + cbref = asn1::cbit_ref{sdu->msg, sdu->N_bytes}; + TESTASSERT(s1ap_pdu.unpack(cbref) == SRSRAN_SUCCESS); + TESTASSERT(s1ap_pdu.type().value == asn1::s1ap::s1ap_pdu_c::types_opts::successful_outcome); + TESTASSERT(s1ap_pdu.successful_outcome().proc_code == ASN1_S1AP_ID_INIT_CONTEXT_SETUP); +} + +enum class test_event { success, wrong_erabid_mod, wrong_mme_s1ap_id }; + +void test_s1ap_erab_setup(test_event event) +{ + srsran::task_scheduler task_sched; + srslog::basic_logger& logger = srslog::fetch_basic_logger("S1AP"); + s1ap s1ap_obj(&task_sched, logger); + rrc_tester rrc; + stack_dummy stack; + asn1::s1ap::s1ap_pdu_c s1ap_pdu; + srsran::unique_byte_buffer_t sdu; + + const char* mme_addr_str = "127.0.0.1"; + const uint32_t MME_PORT = 36412; + mme_dummy mme(mme_addr_str, MME_PORT); + + s1ap_args_t args = {}; + args.cell_id = 0x01; + args.enb_id = 0x19B; + args.mcc = 907; + args.mnc = 70; + args.s1c_bind_addr = "127.0.0.100"; + args.tac = 7; + args.gtp_bind_addr = "127.0.0.100"; + args.mme_addr = mme_addr_str; + args.enb_name = "srsenb01"; + + TESTASSERT(s1ap_obj.init(args, &rrc, &stack) == SRSRAN_SUCCESS); + + run_s1_setup(s1ap_obj, mme); + add_rnti(s1ap_obj, mme); + + // E-RAB Modify Request + sockaddr_in mme_addr = {}; + sctp_sndrcvinfo rcvinfo = {}; + int flags = 0; + uint8_t mod_req_msg[] = {0x00, 0x06, 0x00, 0x1E, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x08, 0x00, 0x02, 0x00, 0x01, 0x00, 0x1E, 0x00, 0x0B, 0x00, + 0x00, 0x24, 0x00, 0x06, 0x0A, 0x00, 0x09, 0x3C, 0x01, 0x00}; + // 00 06 00 1E 00 00 03 00 00 00 02 00 01 00 08 00 02 00 01 00 1E 00 0B 00 00 24 00 06 0A 00 09 3C 01 00 + if (event == test_event::wrong_erabid_mod) { + mod_req_msg[sizeof(mod_req_msg) - 6] = 0x0C; // E-RAB id = 6 + rrc.next_erabs_failed_to_modify.push_back(6); + } else if (event == test_event::wrong_mme_s1ap_id) { + mod_req_msg[12] = 0x02; // MME-UE-S1AP-ID = 2 + } else { + rrc.next_erabs_modified.push_back(5); + } + sdu = srsran::make_byte_buffer(); + memcpy(sdu->msg, mod_req_msg, sizeof(mod_req_msg)); + sdu->N_bytes = sizeof(mod_req_msg); + TESTASSERT(rrc.last_released_rnti == SRSRAN_INVALID_RNTI); + TESTASSERT(s1ap_obj.handle_mme_rx_msg(std::move(sdu), mme_addr, rcvinfo, flags)); + sdu = mme.read_msg(); + TESTASSERT(sdu->N_bytes > 0); + asn1::cbit_ref cbref{sdu->msg, sdu->N_bytes}; + TESTASSERT(s1ap_pdu.unpack(cbref) == SRSRAN_SUCCESS); + + if (event == test_event::wrong_mme_s1ap_id) { + // See TS 36.413, Section 10.6 - Handling of AP ID + TESTASSERT(s1ap_pdu.type().value == asn1::s1ap::s1ap_pdu_c::types_opts::init_msg); + TESTASSERT(s1ap_pdu.init_msg().proc_code == ASN1_S1AP_ID_ERROR_IND); + auto& protocol_ies = s1ap_pdu.init_msg().value.error_ind().protocol_ies; + TESTASSERT(protocol_ies.mme_ue_s1ap_id_present and protocol_ies.mme_ue_s1ap_id.value.value == 2); + TESTASSERT(protocol_ies.enb_ue_s1ap_id_present and protocol_ies.enb_ue_s1ap_id.value.value == 1); + TESTASSERT(rrc.last_released_rnti == 0x46); + return; + } + + TESTASSERT(s1ap_pdu.type().value == asn1::s1ap::s1ap_pdu_c::types_opts::successful_outcome); + TESTASSERT(s1ap_pdu.successful_outcome().proc_code == ASN1_S1AP_ID_ERAB_MODIFY); + auto& protocol_ies = s1ap_pdu.successful_outcome().value.erab_modify_resp().protocol_ies; + if (event == test_event::wrong_erabid_mod) { + TESTASSERT(not protocol_ies.erab_modify_list_bearer_mod_res_present); + TESTASSERT(protocol_ies.erab_failed_to_modify_list_present); + TESTASSERT(protocol_ies.erab_failed_to_modify_list.value.size() == 1); + auto& erab_item = protocol_ies.erab_failed_to_modify_list.value[0].value.erab_item(); + TESTASSERT(erab_item.erab_id == 6); + TESTASSERT(erab_item.cause.type().value == asn1::s1ap::cause_c::types_opts::radio_network); + TESTASSERT(erab_item.cause.radio_network().value == asn1::s1ap::cause_radio_network_opts::unknown_erab_id); + return; + } + + TESTASSERT(protocol_ies.erab_modify_list_bearer_mod_res_present); + TESTASSERT(not protocol_ies.erab_failed_to_modify_list_present); + TESTASSERT(protocol_ies.erab_modify_list_bearer_mod_res.value.size() == 1); + auto& erab_item = protocol_ies.erab_modify_list_bearer_mod_res.value[0].value.erab_modify_item_bearer_mod_res(); + TESTASSERT(erab_item.erab_id == 5); +} + +int main(int argc, char** argv) +{ + // Setup logging. + auto& logger = srslog::fetch_basic_logger("S1AP"); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(-1); + + // Start the log backend. + srsran::test_init(argc, argv); + + test_s1ap_erab_setup(test_event::success); + test_s1ap_erab_setup(test_event::wrong_erabid_mod); + test_s1ap_erab_setup(test_event::wrong_mme_s1ap_id); +} \ No newline at end of file diff --git a/srsue/hdr/stack/mac/mux.h b/srsue/hdr/stack/mac/mux.h index 85366c1c4..059a78366 100644 --- a/srsue/hdr/stack/mac/mux.h +++ b/srsue/hdr/stack/mac/mux.h @@ -32,11 +32,12 @@ #include "srsran/interfaces/mac_interface_types.h" #include "srsran/mac/pdu.h" #include "srsran/srslog/srslog.h" +#include "srsue/hdr/stack/mac_common/mux_base.h" #include namespace srsue { -class mux +class mux : private mux_base { public: explicit mux(srslog::basic_logger& logger); @@ -65,15 +66,12 @@ public: void print_logical_channel_state(const std::string& info); private: - bool has_logical_channel(const uint32_t& lcid); bool pdu_move_to_msg3(uint32_t pdu_sz); uint32_t allocate_sdu(uint32_t lcid, srsran::sch_pdu* pdu, int max_sdu_sz); bool sched_sdu(srsran::logical_channel_config_t* ch, int* sdu_space, int max_sdu_sz); const static int MAX_NOF_SUBHEADERS = 20; - std::vector logical_channels; - // Mutex for exclusive access std::mutex mutex; diff --git a/srsue/hdr/stack/mac/proc_bsr.h b/srsue/hdr/stack/mac/proc_bsr.h index 17ae221df..358c9a981 100644 --- a/srsue/hdr/stack/mac/proc_bsr.h +++ b/srsue/hdr/stack/mac/proc_bsr.h @@ -28,6 +28,7 @@ #include "proc_sr.h" #include "srsran/common/task_scheduler.h" #include "srsran/srslog/srslog.h" +#include "srsue/hdr/stack/mac_common/mac_common.h" /* Buffer status report procedure */ @@ -97,11 +98,11 @@ private: std::map lcgs[NOF_LCG]; // groups LCID in LCG uint32_t find_max_priority_lcg_with_data(); - typedef enum { NONE, REGULAR, PADDING, PERIODIC } triggered_bsr_type_t; - triggered_bsr_type_t triggered_bsr_type = NONE; + + bsr_trigger_type_t triggered_bsr_type = NONE; void print_state(); - void set_trigger(triggered_bsr_type_t new_trigger); + void set_trigger(bsr_trigger_type_t new_trigger); void update_new_data(); void update_old_buffer(); bool check_highest_channel(); @@ -109,7 +110,7 @@ private: bool check_any_channel(); uint32_t get_buffer_state_lcg(uint32_t lcg); bool generate_bsr(bsr_t* bsr, uint32_t nof_padding_bytes); - char* bsr_type_tostring(triggered_bsr_type_t type); + char* bsr_type_tostring(bsr_trigger_type_t type); char* bsr_format_tostring(bsr_format_t format); srsran::timer_handler::unique_timer timer_periodic; diff --git a/srsue/hdr/stack/mac_common/mac_common.h b/srsue/hdr/stack/mac_common/mac_common.h new file mode 100644 index 000000000..42cd47af7 --- /dev/null +++ b/srsue/hdr/stack/mac_common/mac_common.h @@ -0,0 +1,30 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSUE_MAC_COMMON_H +#define SRSUE_MAC_COMMON_H + +/** + * @brief Common definitions/interfaces between LTE/NR MAC components + * + * @remark: So far only the trigger types are identical. The BSR report type and LCID mapping is implemented in RAT + * specialications. + */ +namespace srsue { + +// BSR trigger are common between LTE and NR +typedef enum { NONE, REGULAR, PADDING, PERIODIC } bsr_trigger_type_t; +char* bsr_trigger_type_tostring(bsr_trigger_type_t type); + +} // namespace srsue + +#endif // SRSUE_MAC_COMMON_H diff --git a/srsue/hdr/stack/mac_common/mux_base.h b/srsue/hdr/stack/mac_common/mux_base.h new file mode 100644 index 000000000..6ddee4928 --- /dev/null +++ b/srsue/hdr/stack/mac_common/mux_base.h @@ -0,0 +1,102 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSUE_MUX_BASE_H +#define SRSUE_MUX_BASE_H + +#include "srsran/interfaces/mac_interface_types.h" + +namespace srsue { + +/** + * @brief Common base class for UE MUX unit for 4G and 5G RAT + * + */ +class mux_base +{ +public: + int setup_lcid(const srsran::logical_channel_config_t& config) + { + if (has_logical_channel(config.lcid)) { + // update settings + for (auto& channel : logical_channels) { + if (channel.lcid == config.lcid) { + channel = config; + break; + } + } + // warn user if there is another LCID with same prio + for (auto& channel : logical_channels) { + if (channel.priority == config.priority && channel.lcid != config.lcid) { + srslog::fetch_basic_logger("MAC").error("LCID %d and %d have same priority.", channel.lcid, config.lcid); + return SRSRAN_ERROR; + } + } + } else { + // add new entry + logical_channels.push_back(config); + } + + // sort according to priority (increasing is lower priority) + std::sort(logical_channels.begin(), logical_channels.end(), priority_compare); + + return SRSRAN_SUCCESS; + } + + void print_logical_channel_state(const std::string& info) + { + std::string logline = info; + + for (auto& channel : logical_channels) { + logline += "\n"; + logline += "- lcid="; + logline += std::to_string(channel.lcid); + logline += ", lcg="; + logline += std::to_string(channel.lcg); + logline += ", prio="; + logline += std::to_string(channel.priority); + logline += ", Bj="; + logline += std::to_string(channel.Bj); + logline += ", PBR="; + logline += std::to_string(channel.PBR); + logline += ", BSD="; + logline += std::to_string(channel.BSD); + logline += ", buffer_len="; + logline += std::to_string(channel.buffer_len); + logline += ", sched_len="; + logline += std::to_string(channel.sched_len); + } + srslog::fetch_basic_logger("MAC").debug("%s", logline.c_str()); + } + +protected: + static bool priority_compare(const srsran::logical_channel_config_t& u1, const srsran::logical_channel_config_t& u2) + { + return u1.priority <= u2.priority; + } + + bool has_logical_channel(const uint32_t& lcid) + { + for (auto& channel : logical_channels) { + if (channel.lcid == lcid) { + return true; + } + } + return false; + } + + std::vector logical_channels; +}; + +} // namespace srsue + +#endif // SRSUE_MUX_BASE_H \ No newline at end of file diff --git a/srsue/hdr/stack/mac_nr/mac_nr.h b/srsue/hdr/stack/mac_nr/mac_nr.h index 75b657e77..2252fc00a 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr.h +++ b/srsue/hdr/stack/mac_nr/mac_nr.h @@ -23,14 +23,13 @@ #define SRSUE_MAC_NR_H #include "mac_nr_interfaces.h" +#include "proc_bsr_nr.h" #include "proc_ra_nr.h" #include "proc_sr_nr.h" #include "srsran/common/block_queue.h" #include "srsran/common/mac_pcap.h" #include "srsran/interfaces/mac_interface_types.h" #include "srsran/interfaces/ue_nr_interfaces.h" -#include "srsran/interfaces/ue_rlc_interfaces.h" -#include "srsran/mac/mac_sch_pdu_nr.h" #include "srsran/srslog/srslog.h" #include "srsue/hdr/stack/mac_nr/mux_nr.h" #include "srsue/hdr/stack/ue_stack_base.h" @@ -42,13 +41,16 @@ class rlc_interface_mac; struct mac_nr_args_t { }; -class mac_nr final : public mac_interface_phy_nr, public mac_interface_rrc_nr, public mac_interface_proc_ra_nr +class mac_nr final : public mac_interface_phy_nr, + public mac_interface_rrc_nr, + public mac_interface_proc_ra_nr, + public mac_interface_mux_nr { public: mac_nr(srsran::ext_task_sched_handle task_sched_); ~mac_nr(); - int init(const mac_nr_args_t& args_, phy_interface_mac_nr* phy, rlc_interface_mac* rlc); + int init(const mac_nr_args_t& args_, phy_interface_mac_nr* phy, rlc_interface_mac* rlc, rrc_interface_mac* rrc_); void stop(); void reset(); @@ -76,18 +78,17 @@ public: void get_metrics(mac_metrics_t* metrics); /// Interface for RRC (RRC -> MAC) - void setup_lcid(const srsran::logical_channel_config_t& config); - void set_config(const srsran::bsr_cfg_t& bsr_cfg); - int32_t set_config(const srsran::sr_cfg_nr_t& sr_cfg); - void set_config(const srsran::rach_nr_cfg_t& rach_cfg); - void set_contention_id(const uint64_t ue_identity); - bool set_crnti(const uint16_t crnti); - void start_ra_procedure(); + int setup_lcid(const srsran::logical_channel_config_t& config); + int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg); + int set_config(const srsran::sr_cfg_nr_t& sr_cfg); + void set_config(const srsran::rach_nr_cfg_t& rach_cfg); + void set_contention_id(const uint64_t ue_identity); + bool set_crnti(const uint16_t crnti); + void start_ra_procedure(); - /// procedure ra nr interface + /// procedure ra nr interface + mux uint64_t get_contention_id(); - uint16_t get_c_rnti(); - void set_c_rnti(uint64_t c_rnti_); + uint16_t get_crnti(); void msg3_flush() { mux.msg3_flush(); } bool msg3_is_transmitted() { return mux.msg3_is_transmitted(); } @@ -116,12 +117,12 @@ private: bool is_paging_opportunity(); bool has_crnti(); - uint16_t get_crnti(); bool is_valid_crnti(const uint16_t crnti); /// Interaction with rest of the stack phy_interface_mac_nr* phy = nullptr; rlc_interface_mac* rlc = nullptr; + rrc_interface_mac* rrc = nullptr; srsran::ext_task_sched_handle task_sched; srsran::mac_pcap* pcap = nullptr; @@ -133,9 +134,6 @@ private: uint16_t c_rnti = SRSRAN_INVALID_RNTI; uint64_t contention_id = 0; - static constexpr uint32_t MIN_RLC_PDU_LEN = - 5; ///< minimum bytes that need to be available in a MAC PDU for attempting to add another RLC SDU - srsran::block_queue pdu_queue; ///< currently only DCH PDUs supported (add BCH, PCH, etc) @@ -145,17 +143,17 @@ private: srsran::mac_sch_pdu_nr rx_pdu; /// Tx buffer - srsran::mac_sch_pdu_nr tx_pdu; - srsran::unique_byte_buffer_t tx_buffer = nullptr; + srsran::unique_byte_buffer_t ul_harq_buffer = nullptr; // store PDU generated from MUX srsran::unique_byte_buffer_t rlc_buffer = nullptr; srsran_softbuffer_tx_t softbuffer_tx = {}; /// UL HARQ (temporal) srsran::task_multiqueue::queue_handle stack_task_dispatch_queue; // MAC Uplink-related procedures - proc_ra_nr proc_ra; - proc_sr_nr proc_sr; - mux_nr mux; + proc_ra_nr proc_ra; + proc_sr_nr proc_sr; + proc_bsr_nr proc_bsr; + mux_nr mux; }; } // namespace srsue diff --git a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h index 25f90d9e7..ec0f8c949 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h +++ b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h @@ -33,8 +33,8 @@ class mac_interface_proc_ra_nr public: // Functions for identity handling, e.g., contention id and c-rnti virtual uint64_t get_contention_id() = 0; - virtual uint16_t get_c_rnti() = 0; - virtual void set_c_rnti(uint64_t c_rnti) = 0; + virtual uint16_t get_crnti() = 0; + virtual bool set_crnti(uint16_t c_rnti) = 0; // Functions for msg3 manipulation which shall be transparent to the procedure virtual bool msg3_is_transmitted() = 0; @@ -43,6 +43,16 @@ public: virtual bool msg3_is_empty() = 0; }; +/** + * @brief Interface from MAC NR parent class to mux ubclass + */ +class mac_interface_mux_nr +{ +public: + // MUX can query MAC for current C-RNTI for Msg3 transmission + virtual uint16_t get_crnti() = 0; +}; + } // namespace srsue #endif // SRSUE_MAC_NR_INTERFACES_H \ No newline at end of file diff --git a/srsue/hdr/stack/mac_nr/mux_nr.h b/srsue/hdr/stack/mac_nr/mux_nr.h index 4ede2d0ae..4c6e55231 100644 --- a/srsue/hdr/stack/mac_nr/mux_nr.h +++ b/srsue/hdr/stack/mac_nr/mux_nr.h @@ -22,19 +22,25 @@ #ifndef SRSUE_MUX_NR_H #define SRSUE_MUX_NR_H +#include "mac_nr_interfaces.h" +#include "proc_bsr_nr.h" #include "srsran/common/byte_buffer.h" #include "srsran/common/common.h" +#include "srsran/mac/mac_sch_pdu_nr.h" #include "srsran/srslog/srslog.h" #include "srsran/srsran.h" +#include "srsue/hdr/stack/mac_common/mux_base.h" +#include namespace srsue { -class mux_nr + +class mux_nr final : mux_base, public mux_interface_bsr_nr { public: - explicit mux_nr(srslog::basic_logger& logger); + explicit mux_nr(mac_interface_mux_nr& mac_, srslog::basic_logger& logger); ~mux_nr(){}; void reset(); - int32_t init(); + int32_t init(rlc_interface_mac* rlc_); void step(); @@ -45,12 +51,39 @@ public: bool msg3_is_pending(); bool msg3_is_empty(); + // MAC interface + int setup_lcid(const srsran::logical_channel_config_t& config); + + // Interface of UL HARQ + srsran::unique_byte_buffer_t get_pdu(uint32_t max_pdu_len); + + // Interface for BSR procedure + void generate_bsr_mac_ce(); + private: + // internal helper methods + + // ctor configured members + mac_interface_mux_nr& mac; + rlc_interface_mac* rlc = nullptr; srslog::basic_logger& logger; + + // Msg3 related srsran::unique_byte_buffer_t msg3_buff = nullptr; typedef enum { none, pending, transmitted } msg3_state_t; msg3_state_t msg3_state = none; + + static constexpr uint32_t MIN_RLC_PDU_LEN = + 5; ///< minimum bytes that need to be available in a MAC PDU for attempting to add another RLC SDU + + srsran::unique_byte_buffer_t rlc_buff = nullptr; + + srsran::mac_sch_pdu_nr tx_pdu; + + // Mutex for exclusive access + std::mutex mutex; }; + } // namespace srsue #endif // SRSUE_MUX_NR_H diff --git a/srsue/hdr/stack/mac_nr/proc_bsr_nr.h b/srsue/hdr/stack/mac_nr/proc_bsr_nr.h new file mode 100644 index 000000000..3ac29661c --- /dev/null +++ b/srsue/hdr/stack/mac_nr/proc_bsr_nr.h @@ -0,0 +1,128 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#ifndef SRSUE_PROC_BSR_NR_H +#define SRSUE_PROC_BSR_NR_H + +#include +#include + +#include "proc_sr_nr.h" +#include "srsran/common/task_scheduler.h" +#include "srsran/srslog/srslog.h" +#include "srsue/hdr/stack/mac_common/mac_common.h" + +/* Buffer status report procedure */ + +namespace srsue { + +class rlc_interface_mac; + +// BSR interface for MUX +class bsr_interface_mux_nr +{ +public: + // TS 38.321 Sec 6.1.3.1 + typedef enum { SHORT_BSR, LONG_BSR, SHORT_TRUNC_BSR, LONG_TRUNC_BSR } bsr_format_nr_t; + + // FIXME: this will be replaced + typedef struct { + bsr_format_nr_t format; + uint32_t buff_size[4]; + } bsr_t; + + /// MUX calls BSR to let it generate a padding BSR if there is space in PDU. + virtual bool generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr) = 0; +}; + +class mux_interface_bsr_nr +{ +public: + /// Inform MUX unit to that a BSR needs to be generated in the next UL transmission. + virtual void generate_bsr_mac_ce() = 0; +}; + +/** + * @brief BSR procedure for NR according to 3GPP TS 38.321 version 15.3.0 + * + * @remark: So far only class scelleton. + */ +class proc_bsr_nr : public srsran::timer_callback, public bsr_interface_mux_nr +{ +public: + explicit proc_bsr_nr(srslog::basic_logger& logger) : logger(logger) {} + int init(proc_sr_nr* sr_proc, + mux_interface_bsr_nr* mux_, + rlc_interface_mac* rlc, + srsran::ext_task_sched_handle* task_sched_); + void step(uint32_t tti); + void reset(); + int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg); + + int setup_lcid(uint32_t lcid, uint32_t lcg, uint32_t priority); + void timer_expired(uint32_t timer_id); + uint32_t get_buffer_state(); + + /// Called by MAC when an UL grant is received + void new_grant_ul(uint32_t grant_size); + + // bool need_to_send_bsr(); + bool generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr); + void update_bsr_tti_end(const bsr_t* bsr); + +private: + const static int QUEUE_STATUS_PERIOD_MS = 1000; + + std::mutex mutex; + + srsran::ext_task_sched_handle* task_sched = nullptr; + srslog::basic_logger& logger; + rlc_interface_mac* rlc = nullptr; + mux_interface_bsr_nr* mux = nullptr; + proc_sr_nr* sr = nullptr; + + srsran::bsr_cfg_nr_t bsr_cfg = {}; + + bool initiated = false; + + const static int MAX_NOF_LCG = 8; + + typedef struct { + int priority; + uint32_t old_buffer; + uint32_t new_buffer; + } lcid_t; + + std::map lcgs[MAX_NOF_LCG]; // groups LCID in LCG + + bsr_trigger_type_t triggered_bsr_type = NONE; + + void print_state(); + void set_trigger(bsr_trigger_type_t new_trigger); + void update_new_data(); + void update_old_buffer(); + bool check_highest_channel(); + bool check_new_data(); + bool check_any_channel(); + uint32_t get_buffer_state_lcg(uint32_t lcg); + bool generate_bsr(bsr_t* bsr, uint32_t nof_padding_bytes); + + uint32_t find_max_priority_lcg_with_data(); + + srsran::timer_handler::unique_timer timer_periodic; + srsran::timer_handler::unique_timer timer_retx; + srsran::timer_handler::unique_timer timer_queue_status_print; +}; + +} // namespace srsue + +#endif // SRSUE_PROC_BSR_NR_H diff --git a/srsue/hdr/stack/mac_nr/proc_ra_nr.h b/srsue/hdr/stack/mac_nr/proc_ra_nr.h index 1f403d387..8a74240d6 100644 --- a/srsue/hdr/stack/mac_nr/proc_ra_nr.h +++ b/srsue/hdr/stack/mac_nr/proc_ra_nr.h @@ -37,10 +37,10 @@ namespace srsue { class proc_ra_nr { public: - proc_ra_nr(srslog::basic_logger& logger_); + proc_ra_nr(mac_interface_proc_ra_nr& mac_, srslog::basic_logger& logger_); ~proc_ra_nr(){}; - void init(phy_interface_mac_nr* phy_h_, mac_interface_proc_ra_nr* mac_, srsran::ext_task_sched_handle* task_sched_); + void init(phy_interface_mac_nr* phy_h_, srsran::ext_task_sched_handle* task_sched_); void set_config(const srsran::rach_nr_cfg_t& rach_cfg); bool is_contention_resolution(); @@ -60,9 +60,9 @@ public: void reset(); private: + mac_interface_proc_ra_nr& mac; srslog::basic_logger& logger; phy_interface_mac_nr* phy = nullptr; - mac_interface_proc_ra_nr* mac = nullptr; srsran::ext_task_sched_handle* task_sched = nullptr; srsran::task_multiqueue::queue_handle task_queue; diff --git a/srsue/hdr/stack/rrc/phy_controller.h b/srsue/hdr/stack/rrc/phy_controller.h index 4b0ee88dc..66d2abd6e 100644 --- a/srsue/hdr/stack/rrc/phy_controller.h +++ b/srsue/hdr/stack/rrc/phy_controller.h @@ -22,8 +22,8 @@ #ifndef SRSRAN_PHY_CONTROLLER_H #define SRSRAN_PHY_CONTROLLER_H +#include "srsran/adt/fsm.h" #include "srsran/adt/observer.h" -#include "srsran/common/fsm.h" #include "srsran/common/task_scheduler.h" #include "srsran/interfaces/ue_phy_interfaces.h" #include "srsran/interfaces/ue_rrc_interfaces.h" diff --git a/srsue/hdr/stack/rrc/rrc_nr.h b/srsue/hdr/stack/rrc/rrc_nr.h index 22f97f087..284698b7c 100644 --- a/srsue/hdr/stack/rrc/rrc_nr.h +++ b/srsue/hdr/stack/rrc/rrc_nr.h @@ -59,6 +59,7 @@ struct rrc_nr_metrics_t {}; class rrc_nr final : public rrc_interface_phy_nr, public rrc_interface_pdcp, public rrc_interface_rlc, + public rrc_interface_mac, public rrc_nr_interface_rrc, public srsran::timer_callback { @@ -109,6 +110,11 @@ public: // RLC interface void max_retx_attempted() final; + // MAC interface + void ra_completed() final; + void ra_problem() final; + void release_pucch_srs() final; + // PDCP interface void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final; void write_pdu_bcch_bch(srsran::unique_byte_buffer_t pdu) final; diff --git a/srsue/src/phy/lte/cc_worker.cc b/srsue/src/phy/lte/cc_worker.cc index 5d5ae4d83..e29cdb39b 100644 --- a/srsue/src/phy/lte/cc_worker.cc +++ b/srsue/src/phy/lte/cc_worker.cc @@ -737,7 +737,7 @@ bool cc_worker::encode_uplink(mac_interface_phy_lte::tb_action_ul_t* action, srs ue_ul_cfg.cc_idx = cc_idx; // Setup input data - if (action) { + if (action != nullptr) { data.ptr = action->tb.payload; ue_ul_cfg.ul_cfg.pusch.softbuffers.tx = action->tb.softbuffer.tx; @@ -749,7 +749,7 @@ bool cc_worker::encode_uplink(mac_interface_phy_lte::tb_action_ul_t* action, srs } // Set UCI data and configuration - if (uci_data) { + if (uci_data != nullptr) { data.uci = uci_data->value; ue_ul_cfg.ul_cfg.pusch.uci_cfg = uci_data->cfg; ue_ul_cfg.ul_cfg.pucch.uci_cfg = uci_data->cfg; @@ -761,6 +761,11 @@ bool cc_worker::encode_uplink(mac_interface_phy_lte::tb_action_ul_t* action, srs // Set UL RNTI ue_ul_cfg.ul_cfg.pucch.rnti = phy->stack->get_ul_sched_rnti(CURRENT_TTI_TX); + // Check if the RNTI is valid. Early return without transmitting any signal if the RNTI is invalid. + if (ue_ul_cfg.ul_cfg.pucch.rnti == SRSRAN_INVALID_RNTI) { + return false; + } + // Encode signal int ret = srsran_ue_ul_encode(&ue_ul, &sf_cfg_ul, &ue_ul_cfg, &data); if (ret < 0) { diff --git a/srsue/src/stack/CMakeLists.txt b/srsue/src/stack/CMakeLists.txt index 099092121..052a8ca5b 100644 --- a/srsue/src/stack/CMakeLists.txt +++ b/srsue/src/stack/CMakeLists.txt @@ -18,6 +18,7 @@ # and at http://www.gnu.org/licenses/. # +add_subdirectory(mac_common) add_subdirectory(mac) add_subdirectory(rrc) add_subdirectory(upper) diff --git a/srsue/src/stack/mac/CMakeLists.txt b/srsue/src/stack/mac/CMakeLists.txt index 59be40744..e4db206c7 100644 --- a/srsue/src/stack/mac/CMakeLists.txt +++ b/srsue/src/stack/mac/CMakeLists.txt @@ -19,4 +19,5 @@ # set(SOURCES demux.cc dl_harq.cc mac.cc mux.cc proc_bsr.cc proc_phr.cc proc_ra.cc proc_sr.cc ul_harq.cc) -add_library(srsue_mac STATIC ${SOURCES}) \ No newline at end of file +add_library(srsue_mac STATIC ${SOURCES}) +target_link_libraries(srsue_mac srsue_mac_common) \ No newline at end of file diff --git a/srsue/src/stack/mac/mux.cc b/srsue/src/stack/mac/mux.cc index b0544b58b..fccd50465 100644 --- a/srsue/src/stack/mac/mux.cc +++ b/srsue/src/stack/mac/mux.cc @@ -79,74 +79,17 @@ bool mux::is_pending_any_sdu() return false; } -bool mux::has_logical_channel(const uint32_t& lcid) -{ - for (auto& channel : logical_channels) { - if (channel.lcid == lcid) { - return true; - } - } - return false; -} - -bool priority_compare(const logical_channel_config_t& u1, const logical_channel_config_t& u2) -{ - return u1.priority <= u2.priority; -} - // This is called by RRC (stack thread) during bearer addition void mux::setup_lcid(const logical_channel_config_t& config) { std::lock_guard lock(mutex); - - if (has_logical_channel(config.lcid)) { - // update settings - for (auto& channel : logical_channels) { - if (channel.lcid == config.lcid) { - channel = config; - break; - } - } - // warn user if there is another LCID with same prio - for (auto& channel : logical_channels) { - if (channel.priority == config.priority && channel.lcid != config.lcid) { - logger.warning("LCID %d and %d have same priority.", channel.lcid, config.lcid); - } - } - } else { - // add new entry - logical_channels.push_back(config); - } - - // sort according to priority (increasing is lower priority) - std::sort(logical_channels.begin(), logical_channels.end(), priority_compare); + mux_base::setup_lcid(config); } // mutex should be hold by caller void mux::print_logical_channel_state(const std::string& info) { - std::string logline = info; - - for (auto& channel : logical_channels) { - logline += "\n"; - logline += "- lcid="; - logline += std::to_string(channel.lcid); - logline += ", lcg="; - logline += std::to_string(channel.lcg); - logline += ", prio="; - logline += std::to_string(channel.priority); - logline += ", Bj="; - logline += std::to_string(channel.Bj); - logline += ", PBR="; - logline += std::to_string(channel.PBR); - logline += ", BSD="; - logline += std::to_string(channel.BSD); - logline += ", buffer_len="; - logline += std::to_string(channel.buffer_len); - logline += ", sched_len="; - logline += std::to_string(channel.sched_len); - } - logger.debug("%s", logline.c_str()); + mux_base::print_logical_channel_state(info); } srsran::ul_sch_lcid bsr_format_convert(bsr_proc::bsr_format_t format) diff --git a/srsue/src/stack/mac/proc_bsr.cc b/srsue/src/stack/mac/proc_bsr.cc index 4e5e76482..07300cbe8 100644 --- a/srsue/src/stack/mac/proc_bsr.cc +++ b/srsue/src/stack/mac/proc_bsr.cc @@ -58,10 +58,11 @@ void bsr_proc::print_state() n = srsran_print_check(str, 128, n, "%d: %d ", iter.first, iter.second.old_buffer); } } - logger.info("BSR: triggered_bsr_type=%s, LCID QUEUE status: %s", bsr_type_tostring(triggered_bsr_type), str); + logger.info( + "BSR: triggered_bsr_type=%s, LCID QUEUE status: %s", bsr_trigger_type_tostring(triggered_bsr_type), str); } -void bsr_proc::set_trigger(srsue::bsr_proc::triggered_bsr_type_t new_trigger) +void bsr_proc::set_trigger(bsr_trigger_type_t new_trigger) { triggered_bsr_type = new_trigger; @@ -309,21 +310,6 @@ void bsr_proc::step(uint32_t tti) update_old_buffer(); } -char* bsr_proc::bsr_type_tostring(triggered_bsr_type_t type) -{ - switch (type) { - case bsr_proc::NONE: - return (char*)"none"; - case bsr_proc::REGULAR: - return (char*)"Regular"; - case bsr_proc::PADDING: - return (char*)"Padding"; - case bsr_proc::PERIODIC: - return (char*)"Periodic"; - } - return (char*)"unknown"; -} - char* bsr_proc::bsr_format_tostring(bsr_format_t format) { switch (format) { diff --git a/srsue/src/stack/mac_common/CMakeLists.txt b/srsue/src/stack/mac_common/CMakeLists.txt new file mode 100644 index 000000000..350278cbd --- /dev/null +++ b/srsue/src/stack/mac_common/CMakeLists.txt @@ -0,0 +1,10 @@ +# +# Copyright 2013-2021 Software Radio Systems Limited +# +# By using this file, you agree to the terms and conditions set +# forth in the LICENSE file which can be found at the top level of +# the distribution. +# + +set(SOURCES mac_common.cc) +add_library(srsue_mac_common STATIC ${SOURCES}) \ No newline at end of file diff --git a/srsue/src/stack/mac_common/mac_common.cc b/srsue/src/stack/mac_common/mac_common.cc new file mode 100644 index 000000000..435bf3707 --- /dev/null +++ b/srsue/src/stack/mac_common/mac_common.cc @@ -0,0 +1,32 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsue/hdr/stack/mac_common/mac_common.h" + +namespace srsue { + +char* bsr_trigger_type_tostring(bsr_trigger_type_t type) +{ + switch (type) { + case bsr_trigger_type_t::NONE: + return (char*)"none"; + case bsr_trigger_type_t::REGULAR: + return (char*)"Regular"; + case bsr_trigger_type_t::PADDING: + return (char*)"Padding"; + case bsr_trigger_type_t::PERIODIC: + return (char*)"Periodic"; + } + return (char*)"unknown"; +} + +} // namespace srsue diff --git a/srsue/src/stack/mac_nr/CMakeLists.txt b/srsue/src/stack/mac_nr/CMakeLists.txt index 0cc0f46b3..cf8d2ea85 100644 --- a/srsue/src/stack/mac_nr/CMakeLists.txt +++ b/srsue/src/stack/mac_nr/CMakeLists.txt @@ -18,6 +18,6 @@ # and at http://www.gnu.org/licenses/. # -set(SOURCES mac_nr.cc proc_ra_nr.cc proc_sr_nr.cc mux_nr.cc) +set(SOURCES mac_nr.cc proc_ra_nr.cc proc_bsr_nr.cc proc_sr_nr.cc mux_nr.cc) add_library(srsue_mac_nr STATIC ${SOURCES}) -target_link_libraries(srsue_mac_nr srsran_mac) \ No newline at end of file +target_link_libraries(srsue_mac_nr srsue_mac_common srsran_mac) \ No newline at end of file diff --git a/srsue/src/stack/mac_nr/mac_nr.cc b/srsue/src/stack/mac_nr/mac_nr.cc index 9e47af068..f936e09f9 100644 --- a/srsue/src/stack/mac_nr/mac_nr.cc +++ b/srsue/src/stack/mac_nr/mac_nr.cc @@ -20,6 +20,7 @@ */ #include "srsue/hdr/stack/mac_nr/mac_nr.h" +#include "srsran/interfaces/ue_rlc_interfaces.h" #include "srsran/mac/mac_rar_pdu_nr.h" #include "srsue/hdr/stack/mac_nr/proc_ra_nr.h" @@ -28,9 +29,10 @@ namespace srsue { mac_nr::mac_nr(srsran::ext_task_sched_handle task_sched_) : task_sched(task_sched_), logger(srslog::fetch_basic_logger("MAC")), - proc_ra(logger), + proc_ra(*this, logger), proc_sr(logger), - mux(logger), + proc_bsr(logger), + mux(*this, logger), pcap(nullptr) {} @@ -39,18 +41,29 @@ mac_nr::~mac_nr() stop(); } -int mac_nr::init(const mac_nr_args_t& args_, phy_interface_mac_nr* phy_, rlc_interface_mac* rlc_) +int mac_nr::init(const mac_nr_args_t& args_, + phy_interface_mac_nr* phy_, + rlc_interface_mac* rlc_, + rrc_interface_mac* rrc_) { args = args_; phy = phy_; rlc = rlc_; + rrc = rrc_; // Create Stack task dispatch queue stack_task_dispatch_queue = task_sched.make_task_queue(); - proc_ra.init(phy, this, &task_sched); + // Init MAC sub procedures + proc_ra.init(phy, &task_sched); + proc_sr.init(&proc_ra, phy, rrc); - if (mux.init() != SRSRAN_SUCCESS) { + if (proc_bsr.init(&proc_sr, &mux, rlc, &task_sched) != SRSRAN_SUCCESS) { + logger.error("Couldn't initialize BSR procedure."); + return SRSRAN_ERROR; + } + + if (mux.init(rlc) != SRSRAN_SUCCESS) { logger.error("Couldn't initialize mux unit."); return SRSRAN_ERROR; } @@ -61,12 +74,8 @@ int mac_nr::init(const mac_nr_args_t& args_, phy_interface_mac_nr* phy_, rlc_int return SRSRAN_ERROR; } - tx_buffer = srsran::make_byte_buffer(); - if (tx_buffer == nullptr) { - return SRSRAN_ERROR; - } - rlc_buffer = srsran::make_byte_buffer(); - if (rlc_buffer == nullptr) { + ul_harq_buffer = srsran::make_byte_buffer(); + if (ul_harq_buffer == nullptr) { return SRSRAN_ERROR; } @@ -233,76 +242,45 @@ void mac_nr::new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant, proc_ra.pdcch_to_crnti(); } + // Let BSR know there is a new grant, might have to send a BSR + proc_bsr.new_grant_ul(grant.tbs); + + // TODO: add proper UL-HARQ + // The code below assumes a single HARQ entity, no retx, every Tx is always a new transmission + ul_harq_buffer = mux.get_pdu(grant.tbs); + // fill TB action (goes into UL harq eventually) - action->tb.payload = tx_buffer.get(); + action->tb.payload = ul_harq_buffer.get(); // pass handle to PDU to PHY action->tb.enabled = true; action->tb.rv = 0; action->tb.softbuffer = &softbuffer_tx; srsran_softbuffer_tx_reset(&softbuffer_tx); - // Pack MAC PDU - get_ul_data(grant, action->tb.payload); + // store PCAP + if (pcap) { + pcap->write_ul_crnti_nr(ul_harq_buffer->msg, ul_harq_buffer->N_bytes, grant.rnti, grant.pid, grant.tti); + } metrics[cc_idx].tx_pkts++; } -void mac_nr::get_ul_data(const mac_nr_grant_ul_t& grant, srsran::byte_buffer_t* phy_tx_pdu) -{ - // initialize MAC PDU - phy_tx_pdu->clear(); - tx_pdu.init_tx(phy_tx_pdu, grant.tbs / 8U, true); - - if (mux.msg3_is_pending()) { - // If message 3 is pending pack message 3 for uplink transmission - // Use the CRNTI which is provided in the RRC reconfiguration (only for DC mode maybe other) - tx_pdu.add_crnti_ce(c_rnti); - srsran::mac_sch_subpdu_nr::lcg_bsr_t sbsr = {}; - sbsr.lcg_id = 0; - sbsr.buffer_size = 1; - tx_pdu.add_sbsr_ce(sbsr); - logger.info("Generated msg3 with RNTI 0x%x", c_rnti); - mux.msg3_transmitted(); - } else { - // Pack normal UL data PDU - while (tx_pdu.get_remaing_len() >= MIN_RLC_PDU_LEN) { - // read RLC PDU - rlc_buffer->clear(); - uint8_t* rd = rlc_buffer->msg; - int pdu_len = 0; - pdu_len = rlc->read_pdu(4, rd, tx_pdu.get_remaing_len() - 2); - - // Add SDU if RLC has something to tx - if (pdu_len > 0) { - rlc_buffer->N_bytes = pdu_len; - logger.info(rlc_buffer->msg, rlc_buffer->N_bytes, "Read %d B from RLC", rlc_buffer->N_bytes); - - // add to MAC PDU and pack - if (tx_pdu.add_sdu(4, rlc_buffer->msg, rlc_buffer->N_bytes) != SRSRAN_SUCCESS) { - logger.error("Error packing MAC PDU"); - } - } else { - break; - } - } - } - - // Pack PDU - tx_pdu.pack(); - - logger.info(phy_tx_pdu->msg, phy_tx_pdu->N_bytes, "Generated MAC PDU (%d B)", phy_tx_pdu->N_bytes); - - if (pcap) { - pcap->write_ul_crnti_nr(phy_tx_pdu->msg, phy_tx_pdu->N_bytes, grant.rnti, grant.pid, grant.tti); - } -} - void mac_nr::timer_expired(uint32_t timer_id) { // not implemented } -void mac_nr::setup_lcid(const srsran::logical_channel_config_t& config) +int mac_nr::setup_lcid(const srsran::logical_channel_config_t& config) { + if (mux.setup_lcid(config) != SRSRAN_SUCCESS) { + logger.error("Couldn't register logical channel at MUX unit."); + return SRSRAN_ERROR; + } + + if (proc_bsr.setup_lcid(config.lcid, config.lcg, config.priority) != SRSRAN_SUCCESS) { + logger.error("Couldn't register logical channel at BSR procedure."); + return SRSRAN_ERROR; + } + logger.info("Logical Channel Setup: LCID=%d, LCG=%d, priority=%d, PBR=%d, BSD=%dms, bucket_size=%d", config.lcid, config.lcg, @@ -310,17 +288,16 @@ void mac_nr::setup_lcid(const srsran::logical_channel_config_t& config) config.PBR, config.BSD, config.bucket_size); - // mux_unit.setup_lcid(config); - // bsr_procedure.setup_lcid(config.lcid, config.lcg, config.priority); + + return SRSRAN_SUCCESS; } -void mac_nr::set_config(const srsran::bsr_cfg_t& bsr_cfg) +int mac_nr::set_config(const srsran::bsr_cfg_nr_t& bsr_cfg) { - logger.info("BSR config periodic timer %d retx timer %d", bsr_cfg.periodic_timer, bsr_cfg.retx_timer); - logger.warning("Not handling BSR config yet"); + return proc_bsr.set_config(bsr_cfg); } -int32_t mac_nr::set_config(const srsran::sr_cfg_nr_t& sr_cfg) +int mac_nr::set_config(const srsran::sr_cfg_nr_t& sr_cfg) { return proc_sr.set_config(sr_cfg); } @@ -387,9 +364,7 @@ void mac_nr::handle_pdu(srsran::unique_byte_buffer_t pdu) subpdu.get_c_rnti(), subpdu.get_lcid(), subpdu.get_sdu_length()); - if (subpdu.get_lcid() == 4) { - rlc->write_pdu(subpdu.get_lcid(), subpdu.get_sdu(), subpdu.get_sdu_length()); - } + rlc->write_pdu(subpdu.get_lcid(), subpdu.get_sdu(), subpdu.get_sdu_length()); } } @@ -398,16 +373,6 @@ uint64_t mac_nr::get_contention_id() return 0xdeadbeef; // TODO when rebased on PR } -uint16_t mac_nr::get_c_rnti() -{ - return c_rnti; -} - -void mac_nr::set_c_rnti(uint64_t c_rnti_) -{ - c_rnti = c_rnti_; -} - // TODO same function as for mac_eutra bool mac_nr::is_in_window(uint32_t tti, int* start, int* len) { diff --git a/srsue/src/stack/mac_nr/mux_nr.cc b/srsue/src/stack/mac_nr/mux_nr.cc index 9585c0b01..32d5ae0e4 100644 --- a/srsue/src/stack/mac_nr/mux_nr.cc +++ b/srsue/src/stack/mac_nr/mux_nr.cc @@ -21,21 +21,90 @@ #include "srsue/hdr/stack/mac_nr/mux_nr.h" #include "srsran/common/buffer_pool.h" - +#include "srsran/interfaces/ue_rlc_interfaces.h" namespace srsue { -mux_nr::mux_nr(srslog::basic_logger& logger_) : logger(logger_){}; +mux_nr::mux_nr(mac_interface_mux_nr& mac_, srslog::basic_logger& logger_) : mac(mac_), logger(logger_) {} -int32_t mux_nr::init() +int32_t mux_nr::init(rlc_interface_mac* rlc_) { + rlc = rlc_; + msg3_buff = srsran::make_byte_buffer(); if (msg3_buff == nullptr) { return SRSRAN_ERROR; } + rlc_buff = srsran::make_byte_buffer(); + if (rlc_buff == nullptr) { + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } +int mux_nr::setup_lcid(const srsran::logical_channel_config_t& config) +{ + std::lock_guard lock(mutex); + return mux_base::setup_lcid(config); +} + +srsran::unique_byte_buffer_t mux_nr::get_pdu(uint32_t max_pdu_len) +{ + // initialize MAC PDU + srsran::unique_byte_buffer_t phy_tx_pdu = srsran::make_byte_buffer(); + if (phy_tx_pdu == nullptr) { + return nullptr; + } + + tx_pdu.init_tx(phy_tx_pdu.get(), max_pdu_len, true); + + if (msg3_is_pending()) { + // If message 3 is pending pack message 3 for uplink transmission + // Use the CRNTI which is provided in the RRC reconfiguration (only for DC mode maybe other) + tx_pdu.add_crnti_ce(mac.get_crnti()); + srsran::mac_sch_subpdu_nr::lcg_bsr_t sbsr = {}; + sbsr.lcg_id = 0; + sbsr.buffer_size = 1; + tx_pdu.add_sbsr_ce(sbsr); + logger.info("Generated msg3 with RNTI 0x%x", mac.get_crnti()); + msg3_transmitted(); + } else { + // Pack normal UL data PDU + + // TODO: Add proper priority handling + for (const auto& lc : logical_channels) { + while (tx_pdu.get_remaing_len() >= MIN_RLC_PDU_LEN) { + // read RLC PDU + rlc_buff->clear(); + uint8_t* rd = rlc_buff->msg; + int pdu_len = 0; + pdu_len = rlc->read_pdu(lc.lcid, rd, tx_pdu.get_remaing_len() - 2); + + // Add SDU if RLC has something to tx + if (pdu_len > 0) { + rlc_buff->N_bytes = pdu_len; + logger.info(rlc_buff->msg, rlc_buff->N_bytes, "Read %d B from RLC", rlc_buff->N_bytes); + + // add to MAC PDU and pack + if (tx_pdu.add_sdu(lc.lcid, rlc_buff->msg, rlc_buff->N_bytes) != SRSRAN_SUCCESS) { + logger.error("Error packing MAC PDU"); + } + } else { + break; + } + } + } + } + + // Pack PDU + tx_pdu.pack(); + + logger.info(phy_tx_pdu->msg, phy_tx_pdu->N_bytes, "Generated MAC PDU (%d B)", phy_tx_pdu->N_bytes); + + return phy_tx_pdu; +} + void mux_nr::msg3_flush() { msg3_buff->clear(); @@ -67,4 +136,6 @@ bool mux_nr::msg3_is_empty() return msg3_buff->N_bytes == 0; } +void mux_nr::generate_bsr_mac_ce() {} + } // namespace srsue diff --git a/srsue/src/stack/mac_nr/proc_bsr_nr.cc b/srsue/src/stack/mac_nr/proc_bsr_nr.cc new file mode 100644 index 000000000..9ebbe3aa2 --- /dev/null +++ b/srsue/src/stack/mac_nr/proc_bsr_nr.cc @@ -0,0 +1,322 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsue/hdr/stack/mac_nr/proc_bsr_nr.h" +#include "srsran/interfaces/ue_rlc_interfaces.h" +#include "srsran/mac/mac_sch_pdu_nr.h" + +namespace srsue { + +int32_t proc_bsr_nr::init(proc_sr_nr* sr_, + mux_interface_bsr_nr* mux_, + rlc_interface_mac* rlc_, + srsran::ext_task_sched_handle* task_sched_) +{ + rlc = rlc_; + mux = mux_; + sr = sr_; + task_sched = task_sched_; + + timer_periodic = task_sched->get_unique_timer(); + timer_retx = task_sched->get_unique_timer(); + timer_queue_status_print = task_sched->get_unique_timer(); + + reset(); + + // Print periodically the LCID queue status + auto queue_status_print_task = [this](uint32_t tid) { + print_state(); + timer_queue_status_print.run(); + }; + timer_queue_status_print.set(QUEUE_STATUS_PERIOD_MS, queue_status_print_task); + timer_queue_status_print.run(); + + initiated = true; + + return SRSRAN_SUCCESS; +} + +void proc_bsr_nr::print_state() +{ + char str[128]; + str[0] = '\0'; + int n = 0; + for (auto& lcg : lcgs) { + for (auto& iter : lcg) { + n = srsran_print_check(str, 128, n, "%d: %d ", iter.first, iter.second.old_buffer); + } + } + logger.info( + "BSR: triggered_bsr_type=%s, LCID QUEUE status: %s", bsr_trigger_type_tostring(triggered_bsr_type), str); +} + +void proc_bsr_nr::set_trigger(bsr_trigger_type_t new_trigger) +{ + triggered_bsr_type = new_trigger; + + // Trigger SR always when Regular BSR is triggered in the current TTI. Will be cancelled if a grant is received + if (triggered_bsr_type == REGULAR) { + logger.debug("BSR: Triggering SR procedure"); + sr->start(); + } +} + +void proc_bsr_nr::reset() +{ + timer_periodic.stop(); + timer_retx.stop(); + + triggered_bsr_type = NONE; +} + +int proc_bsr_nr::set_config(const srsran::bsr_cfg_nr_t& bsr_cfg_) +{ + std::lock_guard lock(mutex); + + bsr_cfg = bsr_cfg_; + + if (bsr_cfg_.periodic_timer > 0) { + timer_periodic.set(bsr_cfg_.periodic_timer, [this](uint32_t tid) { timer_expired(tid); }); + logger.info("BSR: Configured timer periodic %d ms", bsr_cfg_.periodic_timer); + } + if (bsr_cfg_.retx_timer > 0) { + timer_retx.set(bsr_cfg_.retx_timer, [this](uint32_t tid) { timer_expired(tid); }); + logger.info("BSR: Configured timer reTX %d ms", bsr_cfg_.retx_timer); + } + + return SRSRAN_SUCCESS; +} + +/* Process Periodic BSR */ +void proc_bsr_nr::timer_expired(uint32_t timer_id) +{ + std::lock_guard lock(mutex); + + // periodicBSR-Timer + if (timer_id == timer_periodic.id()) { + if (triggered_bsr_type == NONE) { + set_trigger(PERIODIC); + logger.debug("BSR: Triggering Periodic BSR"); + } + // retxBSR-Timer + } else if (timer_id == timer_retx.id()) { + // Enable reTx of SR only if periodic timer is not infinity + logger.debug("BSR: Timer BSR reTX expired, periodic=%d, channel=%d", bsr_cfg.periodic_timer, check_any_channel()); + // Triger Regular BSR if UE has available data for transmission on any channel + if (check_any_channel()) { + set_trigger(REGULAR); + logger.debug("BSR: Triggering BSR reTX"); + } + } +} + +uint32_t proc_bsr_nr::get_buffer_state() +{ + uint32_t buffer = 0; + for (int i = 0; i < MAX_NOF_LCG; i++) { + buffer += get_buffer_state_lcg(i); + } + return buffer; +} + +// Checks if data is available for a channel with higher priority than others +bool proc_bsr_nr::check_highest_channel() +{ + // TODO: move 4G implementation to base class or rewrite + for (int i = 0; i < MAX_NOF_LCG; i++) { + for (std::map::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) { + // If new data available + if (iter->second.new_buffer > iter->second.old_buffer) { + // Check if this LCID has higher priority than any other LCID ("belong to any LCG") for which data is already + // available for transmission + bool is_max_priority = true; + for (int j = 0; j < MAX_NOF_LCG; j++) { + for (std::map::iterator iter2 = lcgs[j].begin(); iter2 != lcgs[j].end(); ++iter2) { + // No max prio LCG if prio isn't higher or LCID already had buffered data + if (iter2->second.priority <= iter->second.priority && (iter2->second.old_buffer > 0)) { + is_max_priority = false; + } + } + } + if (is_max_priority) { + logger.debug("BSR: New data for lcid=%d with maximum priority in lcg=%d", iter->first, i); + return true; + } + } + } + } + return false; +} + +bool proc_bsr_nr::check_any_channel() +{ + // TODO: move 4G implementation to base class or rewrite + for (int i = 0; i < MAX_NOF_LCG; i++) { + if (get_buffer_state_lcg(i)) { + return true; + } + } + return false; +} + +// Checks if only one logical channel has data avaiable for Tx +bool proc_bsr_nr::check_new_data() +{ + // TODO: move 4G implementation to base class or rewrite + for (int i = 0; i < MAX_NOF_LCG; i++) { + // If there was no data available in any LCID belonging to this LCG + if (get_buffer_state_lcg(i) == 0) { + for (std::map::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) { + if (iter->second.new_buffer > 0) { + logger.debug("BSR: New data available for lcid=%d", iter->first); + return true; + } + } + } + } + return false; +} + +void proc_bsr_nr::update_new_data() +{ + // TODO: move 4G implementation to base class or rewrite + for (int i = 0; i < MAX_NOF_LCG; i++) { + for (std::map::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) { + iter->second.new_buffer = rlc->get_buffer_state(iter->first); + } + } +} + +void proc_bsr_nr::update_old_buffer() +{ + // TODO: move 4G implementation to base class or rewrite + for (int i = 0; i < MAX_NOF_LCG; i++) { + for (std::map::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) { + iter->second.old_buffer = iter->second.new_buffer; + } + } +} + +uint32_t proc_bsr_nr::get_buffer_state_lcg(uint32_t lcg) +{ + // TODO: move 4G implementation to base class or rewrite + uint32_t n = 0; + for (std::map::iterator iter = lcgs[lcg].begin(); iter != lcgs[lcg].end(); ++iter) { + n += iter->second.old_buffer; + } + return n; +} + +// Generate BSR +bool proc_bsr_nr::generate_bsr(bsr_t* bsr, uint32_t pdu_space) +{ + // TODO: add BSR generation + bool send_bsr = false; + return send_bsr; +} + +// Called by MAC every TTI +// Checks if Regular BSR must be assembled, as defined in 5.4.5 +// Padding BSR is assembled when called by mux_unit when UL dci is received +// Periodic BSR is triggered by the expiration of the timers +void proc_bsr_nr::step(uint32_t tti) +{ + std::lock_guard lock(mutex); + + if (not initiated) { + return; + } + + update_new_data(); + + // Regular BSR triggered if new data arrives or channel with high priority has new data + if (check_new_data() || check_highest_channel()) { + logger.debug("BSR: Triggering Regular BSR tti=%d", tti); + set_trigger(REGULAR); + } + + update_old_buffer(); +} + +void proc_bsr_nr::new_grant_ul(uint32_t grant_size) +{ + std::lock_guard lock(mutex); + if (triggered_bsr_type != NONE) { + // inform MUX we need to generate a BSR + mux->generate_bsr_mac_ce(); + } + + // TODO: restart retxBSR-Timer +} + +// This function is called by MUX only if Regular BSR has not been triggered before +bool proc_bsr_nr::generate_padding_bsr(uint32_t nof_padding_bytes, bsr_t* bsr) +{ + std::lock_guard lock(mutex); + + // TODO: get correct values from mac_sch_pdu_nr + const uint32_t SBSR_CE_SUBHEADER_LEN = 1; + const uint32_t LBSR_CE_SUBHEADER_LEN = 1; + // if the number of padding bits is equal to or larger than the size of the Short BSR plus its subheader but smaller + // than the size of the Long BSR plus its subheader + if (nof_padding_bytes >= SBSR_CE_SUBHEADER_LEN + srsran::mac_sch_subpdu_nr::sizeof_ce(SHORT_BSR, true) && + nof_padding_bytes <= LBSR_CE_SUBHEADER_LEN + srsran::mac_sch_subpdu_nr::sizeof_ce(LONG_BSR, true)) { + // generate padding BSR + set_trigger(PADDING); + generate_bsr(bsr, nof_padding_bytes); + set_trigger(NONE); + return true; + } + + return false; +} + +int proc_bsr_nr::setup_lcid(uint32_t lcid, uint32_t new_lcg, uint32_t priority) +{ + // TODO: move 4G implementation to base class + if (new_lcg > MAX_NOF_LCG) { + logger.error("BSR: Invalid lcg=%d for lcid=%d", new_lcg, lcid); + return SRSRAN_ERROR; + } + + std::lock_guard lock(mutex); + + // First see if it already exists and eliminate it + for (int i = 0; i < MAX_NOF_LCG; i++) { + if (lcgs[i].count(lcid)) { + lcgs[i].erase(lcid); + } + } + // Now add it + lcgs[new_lcg][lcid].priority = priority; + lcgs[new_lcg][lcid].old_buffer = 0; + + return SRSRAN_SUCCESS; +} + +uint32_t proc_bsr_nr::find_max_priority_lcg_with_data() +{ + // TODO: move 4G implementation to base class or rewrite + int32_t max_prio = 99; + uint32_t max_idx = 0; + for (int i = 0; i < MAX_NOF_LCG; i++) { + for (std::map::iterator iter = lcgs[i].begin(); iter != lcgs[i].end(); ++iter) { + if (iter->second.priority < max_prio && iter->second.old_buffer > 0) { + max_prio = iter->second.priority; + max_idx = i; + } + } + } + return max_idx; +} + +} // namespace srsue diff --git a/srsue/src/stack/mac_nr/proc_ra_nr.cc b/srsue/src/stack/mac_nr/proc_ra_nr.cc index 2a2297365..4cf91935c 100644 --- a/srsue/src/stack/mac_nr/proc_ra_nr.cc +++ b/srsue/src/stack/mac_nr/proc_ra_nr.cc @@ -39,14 +39,11 @@ uint32_t backoff_table_nr[16] = {0, 10, 20, 30, 40, 60, 80, 120, 160, 240, 320, // Table 7.6-1: DELTA_PREAMBLE values long int delta_preamble_db_table_nr[5] = {0, -3, -6, 0}; -proc_ra_nr::proc_ra_nr(srslog::basic_logger& logger_) : logger(logger_) {} +proc_ra_nr::proc_ra_nr(mac_interface_proc_ra_nr& mac_, srslog::basic_logger& logger_) : mac(mac_), logger(logger_) {} -void proc_ra_nr::init(phy_interface_mac_nr* phy_, - mac_interface_proc_ra_nr* mac_, - srsran::ext_task_sched_handle* task_sched_) +void proc_ra_nr::init(phy_interface_mac_nr* phy_, srsran::ext_task_sched_handle* task_sched_) { phy = phy_; - mac = mac_; task_sched = task_sched_; task_queue = task_sched->make_task_queue(); prach_send_timer = task_sched->get_unique_timer(); @@ -153,7 +150,7 @@ void proc_ra_nr::timer_expired(uint32_t timer_id) // 5.1.2 Random Access Resource selection void proc_ra_nr::ra_procedure_initialization() { - mac->msg3_flush(); + mac.msg3_flush(); preamble_power_ramping_step = rach_cfg.powerRampingStep; scaling_factor_bi = 1; preambleTransMax = rach_cfg.preambleTransMax; @@ -215,7 +212,7 @@ void proc_ra_nr::ra_response_reception(const mac_interface_phy_nr::mac_nr_grant_ // reset all parameters that are used before rar rar_rnti = SRSRAN_INVALID_RNTI; - mac->msg3_prepare(); + mac.msg3_prepare(); current_ta = subpdu.get_ta(); } } @@ -269,8 +266,8 @@ void proc_ra_nr::ra_completion() srsran::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_COMPLETION)); return; } - srsran::console("Random Access Complete. c-rnti=0x%x, ta=%d\n", mac->get_c_rnti(), current_ta); - logger.info("Random Access Complete. c-rnti=0x%x, ta=%d", mac->get_c_rnti(), current_ta); + srsran::console("Random Access Complete. c-rnti=0x%x, ta=%d\n", mac.get_crnti(), current_ta); + logger.info("Random Access Complete. c-rnti=0x%x, ta=%d", mac.get_crnti(), current_ta); temp_rnti = SRSRAN_INVALID_RNTI; reset(); } diff --git a/srsue/src/stack/rrc/rrc_nr.cc b/srsue/src/stack/rrc/rrc_nr.cc index f9074311f..65f1a8b51 100644 --- a/srsue/src/stack/rrc/rrc_nr.cc +++ b/srsue/src/stack/rrc/rrc_nr.cc @@ -566,10 +566,12 @@ bool rrc_nr::apply_mac_cell_group(const mac_cell_group_cfg_s& mac_cell_group_cfg if (mac_cell_group_cfg.bsr_cfg_present) { logger.debug("Handling MAC BSR config"); - srsran::bsr_cfg_t bsr_cfg; + srsran::bsr_cfg_nr_t bsr_cfg = {}; bsr_cfg.periodic_timer = mac_cell_group_cfg.bsr_cfg.periodic_bsr_timer.to_number(); bsr_cfg.retx_timer = mac_cell_group_cfg.bsr_cfg.retx_bsr_timer.to_number(); - mac->set_config(bsr_cfg); + if (mac->set_config(bsr_cfg) != SRSRAN_SUCCESS) { + return false; + } } if (mac_cell_group_cfg.tag_cfg_present) { @@ -1244,6 +1246,11 @@ bool rrc_nr::apply_radio_bearer_cfg(const radio_bearer_cfg_s& radio_bearer_cfg) // RLC interface void rrc_nr::max_retx_attempted() {} +// MAC interface +void rrc_nr::ra_completed() {} +void rrc_nr::ra_problem() {} +void rrc_nr::release_pucch_srs() {} + // STACK interface void rrc_nr::cell_search_completed(const rrc_interface_phy_lte::cell_search_ret_t& cs_ret, const phy_cell_t& found_cell) {} diff --git a/srsue/src/stack/ue_stack_lte.cc b/srsue/src/stack/ue_stack_lte.cc index e1772299b..4c2208bb8 100644 --- a/srsue/src/stack/ue_stack_lte.cc +++ b/srsue/src/stack/ue_stack_lte.cc @@ -208,7 +208,7 @@ int ue_stack_lte::init(const stack_args_t& args_) nas.init(usim.get(), &rrc, gw, args.nas); mac_nr_args_t mac_nr_args = {}; - mac_nr.init(mac_nr_args, phy_nr, &rlc); + mac_nr.init(mac_nr_args, phy_nr, &rlc, &rrc_nr); rrc_nr.init(phy_nr, &mac_nr, &rlc, &pdcp, gw, &rrc, usim.get(), task_sched.get_timer_handler(), nullptr, args.rrc_nr); rrc.init(phy, &mac, &rlc, &pdcp, &nas, usim.get(), gw, &rrc_nr, args.rrc); diff --git a/srsue/src/stack/ue_stack_nr.cc b/srsue/src/stack/ue_stack_nr.cc index ed044f579..ab9d08ff3 100644 --- a/srsue/src/stack/ue_stack_nr.cc +++ b/srsue/src/stack/ue_stack_nr.cc @@ -76,7 +76,7 @@ int ue_stack_nr::init(const stack_args_t& args_) pdcp_logger.set_hex_dump_max_size(args.log.pdcp_hex_limit); mac_nr_args_t mac_args = {}; - mac->init(mac_args, phy, rlc.get()); + mac->init(mac_args, phy, rlc.get(), rrc.get()); rlc->init(pdcp.get(), rrc.get(), task_sched.get_timer_handler(), 0 /* RB_ID_SRB0 */); pdcp->init(rlc.get(), rrc.get(), gw); diff --git a/srsue/test/mac_nr/CMakeLists.txt b/srsue/test/mac_nr/CMakeLists.txt index b6e4630f8..c8dd82107 100644 --- a/srsue/test/mac_nr/CMakeLists.txt +++ b/srsue/test/mac_nr/CMakeLists.txt @@ -20,4 +20,8 @@ add_executable(proc_ra_nr_test proc_ra_nr_test.cc) target_link_libraries(proc_ra_nr_test srsue_mac_nr srsran_common) -add_test(proc_ra_nr_test proc_ra_nr_test) \ No newline at end of file +add_test(proc_ra_nr_test proc_ra_nr_test) + +add_executable(mac_nr_test mac_nr_test.cc) +target_link_libraries(mac_nr_test srsue_mac_nr srsran_common) +add_test(mac_nr_test mac_nr_test) \ No newline at end of file diff --git a/srsue/test/mac_nr/mac_nr_test.cc b/srsue/test/mac_nr/mac_nr_test.cc new file mode 100644 index 000000000..55ac58980 --- /dev/null +++ b/srsue/test/mac_nr/mac_nr_test.cc @@ -0,0 +1,251 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/common/test_common.h" +#include "srsran/test/ue_test_interfaces.h" +#include "srsue/hdr/stack/mac_nr/mac_nr.h" + +using namespace srsue; + +#define HAVE_PCAP 0 +#define UE_ID 0 + +static std::unique_ptr pcap_handle = nullptr; + +class dummy_phy : public phy_interface_mac_nr +{ +public: + dummy_phy() {} + void send_prach(const uint32_t prach_occasion_, + const int preamble_index_, + const float preamble_received_target_power_, + const float ta_base_sec_ = 0.0f) override + { + prach_occasion = prach_occasion_; + preamble_index = preamble_index_; + preamble_received_target_power = preamble_received_target_power_; + } + int tx_request(const tx_request_t& request) override { return 0; } + int set_ul_grant(std::array, uint16_t rnti, srsran_rnti_type_t rnti_type) override + { + return 0; + } + + void get_last_send_prach(uint32_t* prach_occasion_, uint32_t* preamble_index_, int* preamble_received_target_power_) + { + *prach_occasion_ = prach_occasion; + *preamble_index_ = preamble_index; + *preamble_received_target_power_ = preamble_received_target_power; + } + void sr_send(uint32_t sr_id) override {} + +private: + uint32_t prach_occasion = 0; + uint32_t preamble_index = 0; + int preamble_received_target_power = 0; +}; + +class rrc_dummy : public rrc_interface_mac +{ +public: + rrc_dummy() {} + virtual void ra_completed() {} + virtual void ra_problem() {} + virtual void release_pucch_srs() {} +}; + +class stack_dummy : public stack_test_dummy +{ +public: + void init(mac_nr* mac_, phy_interface_mac_nr* phy_) + { + mac_h = mac_; + phy_h = phy_; + } + void run_tti(uint32_t tti) + { + mac_h->run_tti(tti); + // flush all events + stack_test_dummy::run_tti(); + } + +private: + phy_interface_mac_nr* phy_h = nullptr; + mac_nr* mac_h = nullptr; +}; + +// TODO: refactor to common test dummy components +class rlc_dummy : public srsue::rlc_dummy_interface +{ +public: + rlc_dummy() : received_bytes(0) {} + bool has_data_locked(const uint32_t lcid) final { return ul_queues[lcid] > 0; } + uint32_t get_buffer_state(const uint32_t lcid) final { return ul_queues[lcid]; } + int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) final + { + if (!read_enable || nof_bytes < read_min) { + return 0; + } + + if (read_len > 0 && read_len < (int32_t)nof_bytes) { + nof_bytes = read_len; + } + + uint32_t len = SRSRAN_MIN(ul_queues[lcid], nof_bytes); + + // set payload bytes to LCID so we can check later if the scheduling was correct + memset(payload, lcid > 0 ? lcid : 0xf, len); + + // remove from UL queue + ul_queues[lcid] -= len; + + return len; + }; + void write_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) final + { + logger.debug(payload, nof_bytes, "Received %d B on LCID %d", nof_bytes, lcid); + received_bytes += nof_bytes; + } + + void write_sdu(uint32_t lcid, uint32_t nof_bytes) { ul_queues[lcid] += nof_bytes; } + uint32_t get_received_bytes() { return received_bytes; } + + void disable_read() { read_enable = false; } + void set_read_len(uint32_t len) { read_len = len; } + void set_read_min(uint32_t len) { read_min = len; } + void reset_queues() + { + for (auto& q : ul_queues) { + q.second = 0; + } + } + +private: + bool read_enable = true; + int32_t read_len = -1; // read all + uint32_t read_min = 0; // minimum "grant size" for read_pdu() to return data + uint32_t received_bytes; + srslog::basic_logger& logger = srslog::fetch_basic_logger("RLC"); + // UL queues where key is LCID and value the queue length + std::map ul_queues; +}; + +// TODO: Add test +int msg3_test() +{ + return SRSRAN_SUCCESS; +} + +// Basic PDU generation test +int mac_nr_ul_logical_channel_prioritization_test1() +{ + // PDU layout (20B in total) + // - 2 B MAC subheader for SCH LCID=4 + // - 10 B sduPDU + // - 1 B subheader padding + // - 7 B padding + const uint8_t tv[] = {0x04, 0x0a, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + // dummy layers + dummy_phy phy; + rlc_dummy rlc; + rrc_dummy rrc; + stack_dummy stack; + + // the actual MAC + mac_nr mac(&stack.task_sched); + + mac_nr_args_t args = {}; + mac.init(args, &phy, &rlc, &rrc); + + stack.init(&mac, &phy); + const uint16_t crnti = 0x1001; + + // generate config (default DRB2 config for EN-DC) + std::vector lcids; + srsran::logical_channel_config_t config = {}; + config.lcid = 4; + config.lcg = 6; + config.PBR = 0; + config.BSD = 1000; // 1000ms + config.priority = 11; + lcids.push_back(config); + + // setup LCIDs in MAC + for (auto& channel : lcids) { + mac.setup_lcid(channel); + } + + srsran::bsr_cfg_nr_t bsr_cfg = {}; + bsr_cfg.periodic_timer = 20; + bsr_cfg.retx_timer = 320; + TESTASSERT(mac.set_config(bsr_cfg) == SRSRAN_SUCCESS); + + // write dummy data to DRB2 + rlc.write_sdu(4, 10); + + // run TTI to setup Bj, BSR should be generated + stack.run_tti(0); + usleep(100); + + // create UL action and grant and read MAC PDU + { + mac_interface_phy_nr::tb_action_ul_t ul_action = {}; + mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {}; + + mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant + mac_grant.pid = 0; + mac_grant.rnti = 0x1001; + mac_grant.tti = 0; + mac_grant.tbs = 20; + int cc_idx = 0; + + // Send grant to MAC and get action for this TB, 0x + mac.new_grant_ul(cc_idx, mac_grant, &ul_action); + + // print generated PDU + srslog::fetch_basic_logger("MAC").info( + ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); +#if HAVE_PCAP + pcap_handle->write_ul_crnti_nr( + ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti); +#endif + + TESTASSERT(memcmp(ul_action.tb.payload->msg, tv, sizeof(tv)) == 0); + } + + // make sure MAC PDU thread picks up before stopping + stack.run_tti(0); + mac.stop(); + + return SRSRAN_SUCCESS; +} + +int main() +{ +#if HAVE_PCAP + pcap_handle = std::unique_ptr(new srsran::mac_pcap()); + pcap_handle->open("mac_test_nr.pcap"); +#endif + + auto& mac_logger = srslog::fetch_basic_logger("MAC"); + mac_logger.set_level(srslog::basic_levels::debug); + mac_logger.set_hex_dump_max_size(-1); + srslog::init(); + + TESTASSERT(msg3_test() == SRSRAN_SUCCESS); + TESTASSERT(mac_nr_ul_logical_channel_prioritization_test1() == SRSRAN_SUCCESS); + + return SRSRAN_SUCCESS; +} diff --git a/srsue/test/mac_nr/proc_ra_nr_test.cc b/srsue/test/mac_nr/proc_ra_nr_test.cc index 4fcd71407..d47a662ea 100644 --- a/srsue/test/mac_nr/proc_ra_nr_test.cc +++ b/srsue/test/mac_nr/proc_ra_nr_test.cc @@ -62,8 +62,12 @@ class dummy_mac : public mac_interface_proc_ra_nr { public: uint64_t get_contention_id() { return 0xdeadbeaf; } - uint16_t get_c_rnti() { return crnti; } - void set_c_rnti(uint64_t c_rnti) { crnti = c_rnti; } + uint16_t get_crnti() { return crnti; } + bool set_crnti(uint16_t c_rnti) + { + crnti = c_rnti; + return true; + } bool msg3_is_transmitted() { return true; } void msg3_flush() {} @@ -88,9 +92,9 @@ int main() srsran::task_scheduler task_sched{5, 2}; srsran::ext_task_sched_handle ext_task_sched_h(&task_sched); - proc_ra_nr proc_ra_nr(mac_logger); + proc_ra_nr proc_ra_nr(dummy_mac, mac_logger); - proc_ra_nr.init(&dummy_phy, &dummy_mac, &ext_task_sched_h); + proc_ra_nr.init(&dummy_phy, &ext_task_sched_h); TESTASSERT(proc_ra_nr.is_rar_opportunity(1) == false); srsran::rach_nr_cfg_t rach_cfg; rach_cfg.powerRampingStep = 4;